diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml index befb6280e..2d467d5e5 100644 --- a/.github/DISCUSSION_TEMPLATE/questions.yml +++ b/.github/DISCUSSION_TEMPLATE/questions.yml @@ -1,6 +1,3 @@ -name: Question -description: Ask about anything NewPipe-related -labels: [question] body: - type: markdown attributes: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03b04b7c4..0d76e1645 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,8 @@ jobs: contents: read steps: - - uses: actions/checkout@v3 - - uses: gradle/wrapper-validation-action@v1 + - uses: actions/checkout@v4 + - uses: gradle/wrapper-validation-action@v2 - name: create and checkout branch # push events already checked out the branch @@ -47,7 +47,7 @@ jobs: run: git checkout -B "$BRANCH" - name: set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: "temurin" @@ -57,7 +57,7 @@ jobs: run: ./gradlew assembleDebug lintDebug testDebugUnitTest --stacktrace -DskipFormatKtlint - name: Upload APK - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: app path: app/build/outputs/apk/debug/*.apk @@ -80,10 +80,10 @@ jobs: contents: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: "temurin" @@ -98,7 +98,7 @@ jobs: script: ./gradlew connectedCheck --stacktrace - name: Upload test report when tests fail # because the printed out stacktrace (console) is too short, see also #7553 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: android-test-report-api${{ matrix.api-level }} @@ -111,19 +111,19 @@ jobs: contents: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: "temurin" cache: 'gradle' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar diff --git a/.github/workflows/image-minimizer.yml b/.github/workflows/image-minimizer.yml index 56c6eb37f..d9241c33b 100644 --- a/.github/workflows/image-minimizer.yml +++ b/.github/workflows/image-minimizer.yml @@ -17,9 +17,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 16 @@ -27,7 +27,7 @@ jobs: run: npm i probe-image-size@7.2.3 --ignore-scripts - name: Minimize simple images - uses: actions/github-script@v6 + uses: actions/github-script@v7 timeout-minutes: 3 with: script: | diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index e0dba3111..a18daca3a 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -1,5 +1,5 @@ name: "PR size labeler" -on: [pull_request] +on: [pull_request_target] permissions: contents: read pull-requests: write diff --git a/README.md b/README.md index 9fabb5ac7..4e22f9260 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ - +


ScreenshotsSupported ServicesDescriptionFeaturesInstallation and updatesContributionDonateLicense

@@ -22,9 +22,10 @@ *Read this document in other languages: [Deutsch](doc/README.de.md), [English](README.md), [Español](doc/README.es.md), [Français](doc/README.fr.md), [हिन्दी](doc/README.hi.md), [Italiano](doc/README.it.md), [한국어](doc/README.ko.md), [Português Brasil](doc/README.pt_BR.md), [Polski](doc/README.pl.md), [ਪੰਜਾਬੀ ](doc/README.pa.md), [日本語](doc/README.ja.md), [Română](doc/README.ro.md), [Soomaali](doc/README.so.md), [Türkçe](doc/README.tr.md), [正體中文](doc/README.zh_TW.md), [অসমীয়া](doc/README.asm.md), [Српски](doc/README.sr.md)* -WARNING: THIS APP IS IN BETA, SO YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE IN OUR GITHUB REPOSITORY BY FILLING OUT THE ISSUE TEMPLATE. - -PUTTING NEWPIPE, OR ANY FORK OF IT, INTO THE GOOGLE PLAY STORE VIOLATES THEIR TERMS AND CONDITIONS. +> [!warning] +> THIS APP IS IN BETA, SO YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE IN OUR GITHUB REPOSITORY BY FILLING OUT THE ISSUE TEMPLATE. +> +> PUTTING NEWPIPE, OR ANY FORK OF IT, INTO THE GOOGLE PLAY STORE VIOLATES THEIR TERMS AND CONDITIONS. ## Screenshots diff --git a/app/build.gradle b/app/build.gradle index 9d64e59d4..1f3d1f759 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ plugins { } android { - compileSdk 33 + compileSdk 34 namespace 'org.schabi.newpipe' defaultConfig { @@ -20,8 +20,8 @@ android { resValue "string", "app_name", "NewPipe" minSdk 21 targetSdk 33 - versionCode 996 - versionName "0.26.1" + versionCode 997 + versionName "0.27.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -98,7 +98,9 @@ android { resources { // remove two files which belong to jsoup // no idea how they ended up in the META-INF dir... - excludes += ['META-INF/README.md', 'META-INF/CHANGES'] + excludes += ['META-INF/README.md', 'META-INF/CHANGES', + // 'COPYRIGHT' belongs to RxJava... + 'META-INF/COPYRIGHT'] } } } @@ -106,9 +108,9 @@ android { ext { checkstyleVersion = '10.12.1' - androidxLifecycleVersion = '2.5.1' - androidxRoomVersion = '2.5.2' - androidxWorkVersion = '2.7.1' + androidxLifecycleVersion = '2.6.2' + androidxRoomVersion = '2.6.1' + androidxWorkVersion = '2.8.1' icepickVersion = '3.2.0' exoPlayerVersion = '2.18.7' @@ -118,7 +120,6 @@ ext { leakCanaryVersion = '2.12' stethoVersion = '1.6.0' - mockitoVersion = '4.0.0' } configurations { @@ -133,7 +134,7 @@ checkstyle { toolVersion = checkstyleVersion } -task runCheckstyle(type: Checkstyle) { +tasks.register('runCheckstyle', Checkstyle) { source 'src' include '**/*.java' exclude '**/gen/**' @@ -154,7 +155,7 @@ task runCheckstyle(type: Checkstyle) { def outputDir = "${project.buildDir}/reports/ktlint/" def inputFiles = project.fileTree(dir: "src", include: "**/*.kt") -task runKtlint(type: JavaExec) { +tasks.register('runKtlint', JavaExec) { inputs.files(inputFiles) outputs.dir(outputDir) getMainClass().set("com.pinterest.ktlint.Main") @@ -163,7 +164,7 @@ task runKtlint(type: JavaExec) { jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED") } -task formatKtlint(type: JavaExec) { +tasks.register('formatKtlint', JavaExec) { inputs.files(inputFiles) outputs.dir(outputDir) getMainClass().set("com.pinterest.ktlint.Main") @@ -189,7 +190,7 @@ sonar { dependencies { /** Desugaring **/ - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.3' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.4' /** NewPipe libraries **/ // You can use a local version by uncommenting a few lines in settings.gradle @@ -197,7 +198,7 @@ dependencies { // name and the commit hash with the commit hash of the (pushed) commit you want to test // This works thanks to JitPack: https://jitpack.io/ implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751' - implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.23.1' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:fbe9e6223aceac8d6f6b352afaed4cb61aed1c79' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ @@ -208,28 +209,28 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" /** AndroidX **/ - implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.core:core-ktx:1.10.0' + implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.documentfile:documentfile:1.0.1' - implementation 'androidx.fragment:fragment-ktx:1.4.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' implementation "androidx.lifecycle:lifecycle-livedata-ktx:${androidxLifecycleVersion}" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:${androidxLifecycleVersion}" implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0' - implementation 'androidx.media:media:1.6.0' - implementation 'androidx.preference:preference:1.2.0' - implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation 'androidx.media:media:1.7.0' + implementation 'androidx.preference:preference:1.2.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation "androidx.room:room-runtime:${androidxRoomVersion}" implementation "androidx.room:room-rxjava3:${androidxRoomVersion}" kapt "androidx.room:room-compiler:${androidxRoomVersion}" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' // Newer version specified to prevent accessibility regressions with RecyclerView, see: // https://developer.android.com/jetpack/androidx/releases/viewpager2#1.1.0-alpha01 - implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' + implementation 'androidx.viewpager2:viewpager2:1.1.0-beta02' implementation "androidx.work:work-runtime-ktx:${androidxWorkVersion}" implementation "androidx.work:work-rxjava3:${androidxWorkVersion}" - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.11.0' /** Third-party libraries **/ // Instance state boilerplate elimination @@ -237,13 +238,10 @@ dependencies { kapt "frankiesardo:icepick-processor:${icepickVersion}" // HTML parser - implementation "org.jsoup:jsoup:1.16.1" + implementation "org.jsoup:jsoup:1.17.2" // HTTP client - implementation "com.squareup.okhttp3:okhttp:4.11.0" - // okhttp3:4.11.0 introduces a vulnerability from com.squareup.okio:okio@3.3.0, - // remove com.squareup.okio:okio when updating okhttp - implementation "com.squareup.okio:okio:3.4.0" + implementation "com.squareup.okhttp3:okhttp:4.12.0" // Media player implementation "com.google.android.exoplayer:exoplayer-core:${exoPlayerVersion}" @@ -272,19 +270,19 @@ dependencies { implementation "io.noties.markwon:linkify:${markwonVersion}" // Crash reporting - implementation "ch.acra:acra-core:5.10.1" + implementation "ch.acra:acra-core:5.11.3" // Properly restarting implementation 'com.jakewharton:process-phoenix:2.1.2' // Reactive extensions for Java VM - implementation "io.reactivex.rxjava3:rxjava:3.1.6" + implementation "io.reactivex.rxjava3:rxjava:3.1.8" implementation "io.reactivex.rxjava3:rxandroid:3.0.2" // RxJava binding APIs for Android UI widgets implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0" // Date and time formatting - implementation "org.ocpsoft.prettytime:prettytime:5.0.6.Final" + implementation "org.ocpsoft.prettytime:prettytime:5.0.7.Final" /** Debugging **/ // Memory leak detection @@ -297,13 +295,12 @@ dependencies { /** Testing **/ testImplementation 'junit:junit:4.13.2' - testImplementation "org.mockito:mockito-core:${mockitoVersion}" - testImplementation "org.mockito:mockito-inline:${mockitoVersion}" + testImplementation 'org.mockito:mockito-core:5.6.0' androidTestImplementation "androidx.test.ext:junit:1.1.5" androidTestImplementation "androidx.test:runner:1.5.2" androidTestImplementation "androidx.room:room-testing:${androidxRoomVersion}" - androidTestImplementation "org.assertj:assertj-core:3.23.1" + androidTestImplementation "org.assertj:assertj-core:3.24.2" } static String getGitWorkingBranch() { diff --git a/app/schemas/org.schabi.newpipe.database.AppDatabase/8.json b/app/schemas/org.schabi.newpipe.database.AppDatabase/8.json new file mode 100644 index 000000000..d4a89567b --- /dev/null +++ b/app/schemas/org.schabi.newpipe.database.AppDatabase/8.json @@ -0,0 +1,737 @@ +{ + "formatVersion": 1, + "database": { + "version": 8, + "identityHash": "012fc8e7ad3333f1597347f34e76a513", + "entities": [ + { + "tableName": "subscriptions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT, `name` TEXT, `avatar_url` TEXT, `subscriber_count` INTEGER, `description` TEXT, `notification_mode` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatar_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subscriberCount", + "columnName": "subscriber_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notificationMode", + "columnName": "notification_mode", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_subscriptions_service_id_url", + "unique": true, + "columnNames": [ + "service_id", + "url" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_subscriptions_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "search_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`creation_date` INTEGER, `service_id` INTEGER NOT NULL, `search` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "creationDate", + "columnName": "creation_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "search", + "columnName": "search", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_search_history_search", + "unique": false, + "columnNames": [ + "search" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_search_history_search` ON `${TABLE_NAME}` (`search`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "streams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `stream_type` TEXT NOT NULL, `duration` INTEGER NOT NULL, `uploader` TEXT NOT NULL, `uploader_url` TEXT, `thumbnail_url` TEXT, `view_count` INTEGER, `textual_upload_date` TEXT, `upload_date` INTEGER, `is_upload_date_approximation` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "streamType", + "columnName": "stream_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uploader", + "columnName": "uploader", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uploaderUrl", + "columnName": "uploader_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnail_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "viewCount", + "columnName": "view_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "textualUploadDate", + "columnName": "textual_upload_date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadDate", + "columnName": "upload_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isUploadDateApproximation", + "columnName": "is_upload_date_approximation", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_streams_service_id_url", + "unique": true, + "columnNames": [ + "service_id", + "url" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_streams_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "stream_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `access_date` INTEGER NOT NULL, `repeat_count` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `access_date`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "streamUid", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accessDate", + "columnName": "access_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatCount", + "columnName": "repeat_count", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "stream_id", + "access_date" + ] + }, + "indices": [ + { + "name": "index_stream_history_stream_id", + "unique": false, + "columnNames": [ + "stream_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_stream_history_stream_id` ON `${TABLE_NAME}` (`stream_id`)" + } + ], + "foreignKeys": [ + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "stream_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `progress_time` INTEGER NOT NULL, PRIMARY KEY(`stream_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "streamUid", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "progressMillis", + "columnName": "progress_time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "stream_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "playlists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `is_thumbnail_permanent` INTEGER NOT NULL, `thumbnail_stream_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isThumbnailPermanent", + "columnName": "is_thumbnail_permanent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thumbnailStreamId", + "columnName": "thumbnail_stream_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_playlists_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_playlists_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "playlist_stream_join", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER NOT NULL, `stream_id` INTEGER NOT NULL, `join_index` INTEGER NOT NULL, PRIMARY KEY(`playlist_id`, `join_index`), FOREIGN KEY(`playlist_id`) REFERENCES `playlists`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "playlistUid", + "columnName": "playlist_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "streamUid", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "index", + "columnName": "join_index", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "playlist_id", + "join_index" + ] + }, + "indices": [ + { + "name": "index_playlist_stream_join_playlist_id_join_index", + "unique": true, + "columnNames": [ + "playlist_id", + "join_index" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_playlist_stream_join_playlist_id_join_index` ON `${TABLE_NAME}` (`playlist_id`, `join_index`)" + }, + { + "name": "index_playlist_stream_join_stream_id", + "unique": false, + "columnNames": [ + "stream_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_playlist_stream_join_stream_id` ON `${TABLE_NAME}` (`stream_id`)" + } + ], + "foreignKeys": [ + { + "table": "playlists", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "playlist_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "remote_playlists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, `thumbnail_url` TEXT, `uploader` TEXT, `stream_count` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnail_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploader", + "columnName": "uploader", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "streamCount", + "columnName": "stream_count", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_remote_playlists_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_remote_playlists_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_remote_playlists_service_id_url", + "unique": true, + "columnNames": [ + "service_id", + "url" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_remote_playlists_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "feed", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `subscription_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "streamId", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subscriptionId", + "columnName": "subscription_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "stream_id", + "subscription_id" + ] + }, + "indices": [ + { + "name": "index_feed_subscription_id", + "unique": false, + "columnNames": [ + "subscription_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)" + } + ], + "foreignKeys": [ + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "subscriptions", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "subscription_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "feed_group", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon_id` INTEGER NOT NULL, `sort_order` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sortOrder", + "columnName": "sort_order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_feed_group_sort_order", + "unique": false, + "columnNames": [ + "sort_order" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_sort_order` ON `${TABLE_NAME}` (`sort_order`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "feed_group_subscription_join", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`group_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`group_id`, `subscription_id`), FOREIGN KEY(`group_id`) REFERENCES `feed_group`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "feedGroupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subscriptionId", + "columnName": "subscription_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "group_id", + "subscription_id" + ] + }, + "indices": [ + { + "name": "index_feed_group_subscription_join_subscription_id", + "unique": false, + "columnNames": [ + "subscription_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_subscription_join_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)" + } + ], + "foreignKeys": [ + { + "table": "feed_group", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "subscriptions", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "subscription_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "feed_last_updated", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`subscription_id` INTEGER NOT NULL, `last_updated` INTEGER, PRIMARY KEY(`subscription_id`), FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "subscriptionId", + "columnName": "subscription_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUpdated", + "columnName": "last_updated", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "subscription_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "subscriptions", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "subscription_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '012fc8e7ad3333f1597347f34e76a513')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/org.schabi.newpipe.database.AppDatabase/9.json b/app/schemas/org.schabi.newpipe.database.AppDatabase/9.json new file mode 100644 index 000000000..aced06c0a --- /dev/null +++ b/app/schemas/org.schabi.newpipe.database.AppDatabase/9.json @@ -0,0 +1,730 @@ +{ + "formatVersion": 1, + "database": { + "version": 9, + "identityHash": "7591e8039faa74d8c0517dc867af9d3e", + "entities": [ + { + "tableName": "subscriptions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT, `name` TEXT, `avatar_url` TEXT, `subscriber_count` INTEGER, `description` TEXT, `notification_mode` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatar_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subscriberCount", + "columnName": "subscriber_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notificationMode", + "columnName": "notification_mode", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_subscriptions_service_id_url", + "unique": true, + "columnNames": [ + "service_id", + "url" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_subscriptions_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "search_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`creation_date` INTEGER, `service_id` INTEGER NOT NULL, `search` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "creationDate", + "columnName": "creation_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "search", + "columnName": "search", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_search_history_search", + "unique": false, + "columnNames": [ + "search" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_search_history_search` ON `${TABLE_NAME}` (`search`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "streams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `stream_type` TEXT NOT NULL, `duration` INTEGER NOT NULL, `uploader` TEXT NOT NULL, `uploader_url` TEXT, `thumbnail_url` TEXT, `view_count` INTEGER, `textual_upload_date` TEXT, `upload_date` INTEGER, `is_upload_date_approximation` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "streamType", + "columnName": "stream_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uploader", + "columnName": "uploader", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uploaderUrl", + "columnName": "uploader_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnail_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "viewCount", + "columnName": "view_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "textualUploadDate", + "columnName": "textual_upload_date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadDate", + "columnName": "upload_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isUploadDateApproximation", + "columnName": "is_upload_date_approximation", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_streams_service_id_url", + "unique": true, + "columnNames": [ + "service_id", + "url" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_streams_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "stream_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `access_date` INTEGER NOT NULL, `repeat_count` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `access_date`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "streamUid", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accessDate", + "columnName": "access_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatCount", + "columnName": "repeat_count", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "stream_id", + "access_date" + ] + }, + "indices": [ + { + "name": "index_stream_history_stream_id", + "unique": false, + "columnNames": [ + "stream_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_stream_history_stream_id` ON `${TABLE_NAME}` (`stream_id`)" + } + ], + "foreignKeys": [ + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "stream_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `progress_time` INTEGER NOT NULL, PRIMARY KEY(`stream_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "streamUid", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "progressMillis", + "columnName": "progress_time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "stream_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "playlists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `is_thumbnail_permanent` INTEGER NOT NULL, `thumbnail_stream_id` INTEGER NOT NULL, `display_index` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isThumbnailPermanent", + "columnName": "is_thumbnail_permanent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thumbnailStreamId", + "columnName": "thumbnail_stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayIndex", + "columnName": "display_index", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "playlist_stream_join", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER NOT NULL, `stream_id` INTEGER NOT NULL, `join_index` INTEGER NOT NULL, PRIMARY KEY(`playlist_id`, `join_index`), FOREIGN KEY(`playlist_id`) REFERENCES `playlists`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "playlistUid", + "columnName": "playlist_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "streamUid", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "index", + "columnName": "join_index", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "playlist_id", + "join_index" + ] + }, + "indices": [ + { + "name": "index_playlist_stream_join_playlist_id_join_index", + "unique": true, + "columnNames": [ + "playlist_id", + "join_index" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_playlist_stream_join_playlist_id_join_index` ON `${TABLE_NAME}` (`playlist_id`, `join_index`)" + }, + { + "name": "index_playlist_stream_join_stream_id", + "unique": false, + "columnNames": [ + "stream_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_playlist_stream_join_stream_id` ON `${TABLE_NAME}` (`stream_id`)" + } + ], + "foreignKeys": [ + { + "table": "playlists", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "playlist_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "remote_playlists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, `thumbnail_url` TEXT, `uploader` TEXT, `display_index` INTEGER NOT NULL, `stream_count` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serviceId", + "columnName": "service_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnail_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploader", + "columnName": "uploader", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayIndex", + "columnName": "display_index", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "streamCount", + "columnName": "stream_count", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_remote_playlists_service_id_url", + "unique": true, + "columnNames": [ + "service_id", + "url" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_remote_playlists_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "feed", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `subscription_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "streamId", + "columnName": "stream_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subscriptionId", + "columnName": "subscription_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "stream_id", + "subscription_id" + ] + }, + "indices": [ + { + "name": "index_feed_subscription_id", + "unique": false, + "columnNames": [ + "subscription_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)" + } + ], + "foreignKeys": [ + { + "table": "streams", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "stream_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "subscriptions", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "subscription_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "feed_group", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon_id` INTEGER NOT NULL, `sort_order` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sortOrder", + "columnName": "sort_order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_feed_group_sort_order", + "unique": false, + "columnNames": [ + "sort_order" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_sort_order` ON `${TABLE_NAME}` (`sort_order`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "feed_group_subscription_join", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`group_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`group_id`, `subscription_id`), FOREIGN KEY(`group_id`) REFERENCES `feed_group`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "feedGroupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subscriptionId", + "columnName": "subscription_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "group_id", + "subscription_id" + ] + }, + "indices": [ + { + "name": "index_feed_group_subscription_join_subscription_id", + "unique": false, + "columnNames": [ + "subscription_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_subscription_join_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)" + } + ], + "foreignKeys": [ + { + "table": "feed_group", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "subscriptions", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "subscription_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "feed_last_updated", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`subscription_id` INTEGER NOT NULL, `last_updated` INTEGER, PRIMARY KEY(`subscription_id`), FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "fields": [ + { + "fieldPath": "subscriptionId", + "columnName": "subscription_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUpdated", + "columnName": "last_updated", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "subscription_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "subscriptions", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "subscription_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7591e8039faa74d8c0517dc867af9d3e')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt index 88e737205..a34cfece6 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -8,10 +8,14 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNull import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.schabi.newpipe.database.playlist.model.PlaylistEntity +import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity +import org.schabi.newpipe.extractor.ServiceList import org.schabi.newpipe.extractor.stream.StreamType @RunWith(AndroidJUnit4::class) @@ -20,13 +24,17 @@ class DatabaseMigrationTest { private const val DEFAULT_SERVICE_ID = 0 private const val DEFAULT_URL = "https://www.youtube.com/watch?v=cDphUib5iG4" private const val DEFAULT_TITLE = "Test Title" + private const val DEFAULT_NAME = "Test Name" private val DEFAULT_TYPE = StreamType.VIDEO_STREAM private const val DEFAULT_DURATION = 480L private const val DEFAULT_UPLOADER_NAME = "Uploader Test" private const val DEFAULT_THUMBNAIL = "https://example.com/example.jpg" - private const val DEFAULT_SECOND_SERVICE_ID = 0 + private const val DEFAULT_SECOND_SERVICE_ID = 1 private const val DEFAULT_SECOND_URL = "https://www.youtube.com/watch?v=ncQU6iBn5Fc" + + private const val DEFAULT_THIRD_SERVICE_ID = 2 + private const val DEFAULT_THIRD_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" } @get:Rule @@ -106,6 +114,20 @@ class DatabaseMigrationTest { Migrations.MIGRATION_6_7 ) + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, + Migrations.DB_VER_8, + true, + Migrations.MIGRATION_7_8 + ) + + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, + Migrations.DB_VER_9, + true, + Migrations.MIGRATION_8_9 + ) + val migratedDatabaseV3 = getMigratedDatabase() val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst() @@ -140,6 +162,157 @@ class DatabaseMigrationTest { assertNull(secondStreamFromMigratedDatabase.isUploadDateApproximation) } + @Test + fun migrateDatabaseFrom7to8() { + val databaseInV7 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_7) + + val defaultSearch1 = " abc " + val defaultSearch2 = " abc" + + val serviceId = DEFAULT_SERVICE_ID // YouTube + // Use id different to YouTube because two searches with the same query + // but different service are considered not equal. + val otherServiceId = ServiceList.SoundCloud.serviceId + + databaseInV7.run { + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", serviceId) + put("search", defaultSearch1) + } + ) + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", serviceId) + put("search", defaultSearch2) + } + ) + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", otherServiceId) + put("search", defaultSearch1) + } + ) + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", otherServiceId) + put("search", defaultSearch2) + } + ) + close() + } + + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, Migrations.DB_VER_8, + true, Migrations.MIGRATION_7_8 + ) + + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, Migrations.DB_VER_9, + true, Migrations.MIGRATION_8_9 + ) + + val migratedDatabaseV8 = getMigratedDatabase() + val listFromDB = migratedDatabaseV8.searchHistoryDAO().all.blockingFirst() + + assertEquals(2, listFromDB.size) + assertEquals("abc", listFromDB[0].search) + assertEquals("abc", listFromDB[1].search) + assertNotEquals(listFromDB[0].serviceId, listFromDB[1].serviceId) + } + + @Test + fun migrateDatabaseFrom8to9() { + val databaseInV8 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_8) + + val localUid1: Long + val localUid2: Long + val remoteUid1: Long + val remoteUid2: Long + databaseInV8.run { + localUid1 = insert( + "playlists", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("name", DEFAULT_NAME + "1") + put("is_thumbnail_permanent", false) + put("thumbnail_stream_id", -1) + } + ) + localUid2 = insert( + "playlists", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("name", DEFAULT_NAME + "2") + put("is_thumbnail_permanent", false) + put("thumbnail_stream_id", -1) + } + ) + delete( + "playlists", "uid = ?", + Array(1) { localUid1 } + ) + remoteUid1 = insert( + "remote_playlists", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", DEFAULT_SERVICE_ID) + put("url", DEFAULT_URL) + } + ) + remoteUid2 = insert( + "remote_playlists", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", DEFAULT_SECOND_SERVICE_ID) + put("url", DEFAULT_SECOND_URL) + } + ) + delete( + "remote_playlists", "uid = ?", + Array(1) { remoteUid2 } + ) + close() + } + + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, + Migrations.DB_VER_9, + true, + Migrations.MIGRATION_8_9 + ) + + val migratedDatabaseV9 = getMigratedDatabase() + var localListFromDB = migratedDatabaseV9.playlistDAO().all.blockingFirst() + var remoteListFromDB = migratedDatabaseV9.playlistRemoteDAO().all.blockingFirst() + + assertEquals(1, localListFromDB.size) + assertEquals(localUid2, localListFromDB[0].uid) + assertEquals(-1, localListFromDB[0].displayIndex) + assertEquals(1, remoteListFromDB.size) + assertEquals(remoteUid1, remoteListFromDB[0].uid) + assertEquals(-1, remoteListFromDB[0].displayIndex) + + val localUid3 = migratedDatabaseV9.playlistDAO().insert( + PlaylistEntity(DEFAULT_NAME + "3", false, -1, -1) + ) + val remoteUid3 = migratedDatabaseV9.playlistRemoteDAO().insert( + PlaylistRemoteEntity( + DEFAULT_THIRD_SERVICE_ID, DEFAULT_NAME, DEFAULT_THIRD_URL, + DEFAULT_THUMBNAIL, DEFAULT_UPLOADER_NAME, -1, 10 + ) + ) + + localListFromDB = migratedDatabaseV9.playlistDAO().all.blockingFirst() + remoteListFromDB = migratedDatabaseV9.playlistRemoteDAO().all.blockingFirst() + assertEquals(2, localListFromDB.size) + assertEquals(localUid3, localListFromDB[1].uid) + assertEquals(-1, localListFromDB[1].displayIndex) + assertEquals(2, remoteListFromDB.size) + assertEquals(remoteUid3, remoteListFromDB[1].uid) + assertEquals(-1, remoteListFromDB[1].displayIndex) + } + private fun getMigratedDatabase(): AppDatabase { val database: AppDatabase = Room.databaseBuilder( ApplicationProvider.getApplicationContext(), diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt index 9fa87c31b..893ae82b7 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt @@ -85,7 +85,13 @@ class FeedDAOTest { private fun assertEqual(streams: List?, allowedStreams: List) { assertNotNull(streams) - assertEquals(allowedStreams, streams!!.stream().map { it.stream }.toList().sortedBy { it.uid }) + assertEquals( + allowedStreams, + streams!! + .map { it.stream } + .sortedBy { it.uid } + .toList() + ) } private fun setupUnlinkDelete(time: String) { diff --git a/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java b/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java index 8d87e90bd..8d03a1486 100644 --- a/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java +++ b/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java @@ -25,6 +25,7 @@ import android.view.ViewGroup; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.os.BundleCompat; import androidx.lifecycle.Lifecycle; import androidx.viewpager.widget.PagerAdapter; @@ -284,7 +285,7 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt Bundle state = null; if (!mSavedState.isEmpty()) { state = new Bundle(); - state.putParcelableArray("states", mSavedState.toArray(new Fragment.SavedState[0])); + state.putParcelableArrayList("states", mSavedState); } for (int i = 0; i < mFragments.size(); i++) { final Fragment f = mFragments.get(i); @@ -311,13 +312,12 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt if (state != null) { final Bundle bundle = (Bundle) state; bundle.setClassLoader(loader); - final Parcelable[] fss = bundle.getParcelableArray("states"); + final var states = BundleCompat.getParcelableArrayList(bundle, "states", + Fragment.SavedState.class); mSavedState.clear(); mFragments.clear(); - if (fss != null) { - for (final Parcelable parcelable : fss) { - mSavedState.add((Fragment.SavedState) parcelable); - } + if (states != null) { + mSavedState.addAll(states); } final Iterable keys = bundle.keySet(); for (final String key : keys) { diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index ee352ae4a..d92425d20 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -60,6 +60,8 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; public class App extends Application { public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; private static final String TAG = App.class.toString(); + + private boolean isFirstRun = false; private static App app; @NonNull @@ -85,7 +87,13 @@ public class App extends Application { return; } - // Initialize settings first because others inits can use its values + // check if the last used preference version is set + // to determine whether this is the first app run + final int lastUsedPrefVersion = PreferenceManager.getDefaultSharedPreferences(this) + .getInt(getString(R.string.last_used_preferences_version), -1); + isFirstRun = lastUsedPrefVersion == -1; + + // Initialize settings first because other initializations can use its values NewPipeSettings.initSettings(this); NewPipe.init(getDownloader(), @@ -255,4 +263,7 @@ public class App extends Application { return false; } + public boolean isFirstRun() { + return isFirstRun; + } } diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index ea047ce61..175694125 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -44,6 +44,7 @@ import android.widget.FrameLayout; import android.widget.Spinner; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; @@ -51,6 +52,7 @@ import androidx.core.app.ActivityCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentContainerView; import androidx.fragment.app.FragmentManager; import androidx.preference.PreferenceManager; @@ -64,17 +66,20 @@ import org.schabi.newpipe.databinding.ToolbarLayoutBinding; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance; import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; +import org.schabi.newpipe.fragments.list.comments.CommentRepliesFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.local.feed.notifications.NotificationWorker; import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; +import org.schabi.newpipe.settings.UpdateSettingsFragment; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KioskTranslator; @@ -82,6 +87,7 @@ import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PeertubeHelper; import org.schabi.newpipe.util.PermissionHelper; +import org.schabi.newpipe.util.ReleaseVersionUtil; import org.schabi.newpipe.util.SerializedCache; import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.StateSaver; @@ -163,6 +169,11 @@ public class MainActivity extends AppCompatActivity { // if this is enabled by the user. NotificationWorker.initialize(this); } + if (!UpdateSettingsFragment.wasUserAskedForConsent(this) + && !App.getApp().isFirstRun() + && ReleaseVersionUtil.INSTANCE.isReleaseApk()) { + UpdateSettingsFragment.askForConsentToUpdateChecks(this); + } } @Override @@ -172,7 +183,8 @@ public class MainActivity extends AppCompatActivity { final App app = App.getApp(); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app); - if (prefs.getBoolean(app.getString(R.string.update_app_key), true)) { + if (prefs.getBoolean(app.getString(R.string.update_app_key), false) + && prefs.getBoolean(app.getString(R.string.update_check_consent_key), false)) { // Start the worker which is checking all conditions // and eventually searching for a new version. NewVersionWorker.enqueueNewVersionCheckingWork(app, false); @@ -546,14 +558,21 @@ public class MainActivity extends AppCompatActivity { // interacts with a fragment inside fragment_holder so all back presses should be // handled by it if (bottomSheetHiddenOrCollapsed()) { - final Fragment fragment = getSupportFragmentManager() - .findFragmentById(R.id.fragment_holder); + final FragmentManager fm = getSupportFragmentManager(); + final Fragment fragment = fm.findFragmentById(R.id.fragment_holder); // If current fragment implements BackPressable (i.e. can/wanna handle back press) // delegate the back press to it if (fragment instanceof BackPressable) { if (((BackPressable) fragment).onBackPressed()) { return; } + } else if (fragment instanceof CommentRepliesFragment) { + // expand DetailsFragment if CommentRepliesFragment was opened + // to show the top level comments again + // Expand DetailsFragment if CommentRepliesFragment was opened + // and no other CommentRepliesFragments are on top of the back stack + // to show the top level comments again. + openDetailFragmentFromCommentReplies(fm, false); } } else { @@ -629,10 +648,17 @@ public class MainActivity extends AppCompatActivity { * */ private void onHomeButtonPressed() { - // If search fragment wasn't found in the backstack... - if (!NavigationHelper.tryGotoSearchFragment(getSupportFragmentManager())) { - // ...go to the main fragment - NavigationHelper.gotoMainFragment(getSupportFragmentManager()); + final FragmentManager fm = getSupportFragmentManager(); + final Fragment fragment = fm.findFragmentById(R.id.fragment_holder); + + if (fragment instanceof CommentRepliesFragment) { + // Expand DetailsFragment if CommentRepliesFragment was opened + // and no other CommentRepliesFragments are on top of the back stack + // to show the top level comments again. + openDetailFragmentFromCommentReplies(fm, true); + } else if (!NavigationHelper.tryGotoSearchFragment(fm)) { + // If search fragment wasn't found in the backstack go to the main fragment + NavigationHelper.gotoMainFragment(fm); } } @@ -828,6 +854,68 @@ public class MainActivity extends AppCompatActivity { } } + private void openDetailFragmentFromCommentReplies( + @NonNull final FragmentManager fm, + final boolean popBackStack + ) { + // obtain the name of the fragment under the replies fragment that's going to be popped + @Nullable final String fragmentUnderEntryName; + if (fm.getBackStackEntryCount() < 2) { + fragmentUnderEntryName = null; + } else { + fragmentUnderEntryName = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 2) + .getName(); + } + + // the root comment is the comment for which the user opened the replies page + @Nullable final CommentRepliesFragment repliesFragment = + (CommentRepliesFragment) fm.findFragmentByTag(CommentRepliesFragment.TAG); + @Nullable final CommentsInfoItem rootComment = + repliesFragment == null ? null : repliesFragment.getCommentsInfoItem(); + + // sometimes this function pops the backstack, other times it's handled by the system + if (popBackStack) { + fm.popBackStackImmediate(); + } + + // only expand the bottom sheet back if there are no more nested comment replies fragments + // stacked under the one that is currently being popped + if (CommentRepliesFragment.TAG.equals(fragmentUnderEntryName)) { + return; + } + + final BottomSheetBehavior behavior = BottomSheetBehavior + .from(mainBinding.fragmentPlayerHolder); + // do not return to the comment if the details fragment was closed + if (behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) { + return; + } + + // scroll to the root comment once the bottom sheet expansion animation is finished + behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { + @Override + public void onStateChanged(@NonNull final View bottomSheet, + final int newState) { + if (newState == BottomSheetBehavior.STATE_EXPANDED) { + final Fragment detailFragment = fm.findFragmentById( + R.id.fragment_player_holder); + if (detailFragment instanceof VideoDetailFragment && rootComment != null) { + // should always be the case + ((VideoDetailFragment) detailFragment).scrollToComment(rootComment); + } + behavior.removeBottomSheetCallback(this); + } + } + + @Override + public void onSlide(@NonNull final View bottomSheet, final float slideOffset) { + // not needed, listener is removed once the sheet is expanded + } + }); + + behavior.setState(BottomSheetBehavior.STATE_EXPANDED); + } + private boolean bottomSheetHiddenOrCollapsed() { final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder); diff --git a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java index 856fbff8b..21c5354f4 100644 --- a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java +++ b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java @@ -7,6 +7,8 @@ import static org.schabi.newpipe.database.Migrations.MIGRATION_3_4; import static org.schabi.newpipe.database.Migrations.MIGRATION_4_5; import static org.schabi.newpipe.database.Migrations.MIGRATION_5_6; import static org.schabi.newpipe.database.Migrations.MIGRATION_6_7; +import static org.schabi.newpipe.database.Migrations.MIGRATION_7_8; +import static org.schabi.newpipe.database.Migrations.MIGRATION_8_9; import android.content.Context; import android.database.Cursor; @@ -27,7 +29,7 @@ public final class NewPipeDatabase { return Room .databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, - MIGRATION_5_6, MIGRATION_6_7) + MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9) .build(); } diff --git a/app/src/main/java/org/schabi/newpipe/NewVersionWorker.kt b/app/src/main/java/org/schabi/newpipe/NewVersionWorker.kt index 39d8e90dc..000b83953 100644 --- a/app/src/main/java/org/schabi/newpipe/NewVersionWorker.kt +++ b/app/src/main/java/org/schabi/newpipe/NewVersionWorker.kt @@ -20,9 +20,7 @@ import com.grack.nanojson.JsonParser import com.grack.nanojson.JsonParserException import org.schabi.newpipe.extractor.downloader.Response import org.schabi.newpipe.extractor.exceptions.ReCaptchaException -import org.schabi.newpipe.util.ReleaseVersionUtil.coerceUpdateCheckExpiry -import org.schabi.newpipe.util.ReleaseVersionUtil.isLastUpdateCheckExpired -import org.schabi.newpipe.util.ReleaseVersionUtil.isReleaseApk +import org.schabi.newpipe.util.ReleaseVersionUtil import java.io.IOException class NewVersionWorker( @@ -84,7 +82,7 @@ class NewVersionWorker( @Throws(IOException::class, ReCaptchaException::class) private fun checkNewVersion() { // Check if the current apk is a github one or not. - if (!isReleaseApk()) { + if (!ReleaseVersionUtil.isReleaseApk) { return } @@ -93,7 +91,7 @@ class NewVersionWorker( // Check if the last request has happened a certain time ago // to reduce the number of API requests. val expiry = prefs.getLong(applicationContext.getString(R.string.update_expiry_key), 0) - if (!isLastUpdateCheckExpired(expiry)) { + if (!ReleaseVersionUtil.isLastUpdateCheckExpired(expiry)) { return } } @@ -108,7 +106,7 @@ class NewVersionWorker( try { // Store a timestamp which needs to be exceeded, // before a new request to the API is made. - val newExpiry = coerceUpdateCheckExpiry(response.getHeader("expires")) + val newExpiry = ReleaseVersionUtil.coerceUpdateCheckExpiry(response.getHeader("expires")) prefs.edit { putLong(applicationContext.getString(R.string.update_expiry_key), newExpiry) } diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt index f1d4c26df..7f148e9b5 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt @@ -116,7 +116,7 @@ class AboutActivity : AppCompatActivity() { /** * List of all software components. */ - private val SOFTWARE_COMPONENTS = arrayOf( + private val SOFTWARE_COMPONENTS = arrayListOf( SoftwareComponent( "ACRA", "2013", "Kevin Gaudin", "https://github.com/ACRA/acra", StandardLicenses.APACHE2 diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt index a5cf2924a..9f5ad2a7a 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt @@ -18,6 +18,7 @@ import org.schabi.newpipe.BuildConfig import org.schabi.newpipe.R import org.schabi.newpipe.databinding.FragmentLicensesBinding import org.schabi.newpipe.databinding.ItemSoftwareComponentBinding +import org.schabi.newpipe.ktx.parcelableArrayList import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.external_communication.ShareUtils @@ -25,16 +26,15 @@ import org.schabi.newpipe.util.external_communication.ShareUtils * Fragment containing the software licenses. */ class LicenseFragment : Fragment() { - private lateinit var softwareComponents: Array + private lateinit var softwareComponents: List private var activeSoftwareComponent: SoftwareComponent? = null private val compositeDisposable = CompositeDisposable() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - softwareComponents = arguments?.getParcelableArray(ARG_COMPONENTS) as Array + softwareComponents = arguments?.parcelableArrayList(ARG_COMPONENTS)!! + .sortedBy { it.name } // Sort components by name activeSoftwareComponent = savedInstanceState?.getSerializable(SOFTWARE_COMPONENT_KEY) as? SoftwareComponent - // Sort components by name - softwareComponents.sortBy { it.name } } override fun onDestroy() { @@ -130,7 +130,8 @@ class LicenseFragment : Fragment() { StandardLicenses.GPL3, BuildConfig.VERSION_NAME ) - fun newInstance(softwareComponents: Array): LicenseFragment { + + fun newInstance(softwareComponents: ArrayList): LicenseFragment { val fragment = LicenseFragment() fragment.arguments = bundleOf(ARG_COMPONENTS to softwareComponents) return fragment diff --git a/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java b/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java index 03e39cd43..04d93a238 100644 --- a/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java +++ b/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java @@ -1,6 +1,6 @@ package org.schabi.newpipe.database; -import static org.schabi.newpipe.database.Migrations.DB_VER_7; +import static org.schabi.newpipe.database.Migrations.DB_VER_9; import androidx.room.Database; import androidx.room.RoomDatabase; @@ -38,7 +38,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity; FeedEntity.class, FeedGroupEntity.class, FeedGroupSubscriptionEntity.class, FeedLastUpdatedEntity.class }, - version = DB_VER_7 + version = DB_VER_9 ) public abstract class AppDatabase extends RoomDatabase { public static final String DATABASE_NAME = "newpipe.db"; diff --git a/app/src/main/java/org/schabi/newpipe/database/Converters.kt b/app/src/main/java/org/schabi/newpipe/database/Converters.kt index 0eafcede1..ec097cc1b 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Converters.kt +++ b/app/src/main/java/org/schabi/newpipe/database/Converters.kt @@ -7,7 +7,7 @@ import java.time.Instant import java.time.OffsetDateTime import java.time.ZoneOffset -object Converters { +class Converters { /** * Convert a long value to a [OffsetDateTime]. * @@ -47,6 +47,6 @@ object Converters { @TypeConverter fun feedGroupIconOf(id: Int): FeedGroupIcon { - return FeedGroupIcon.values().first { it.id == id } + return FeedGroupIcon.entries.first { it.id == id } } } diff --git a/app/src/main/java/org/schabi/newpipe/database/Migrations.java b/app/src/main/java/org/schabi/newpipe/database/Migrations.java index 1886b87c2..c9f630869 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -25,6 +25,8 @@ public final class Migrations { public static final int DB_VER_5 = 5; public static final int DB_VER_6 = 6; public static final int DB_VER_7 = 7; + public static final int DB_VER_8 = 8; + public static final int DB_VER_9 = 9; private static final String TAG = Migrations.class.getName(); public static final boolean DEBUG = MainActivity.DEBUG; @@ -186,7 +188,7 @@ public final class Migrations { @Override public void migrate(@NonNull final SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE `subscriptions` ADD COLUMN `notification_mode` " - + "INTEGER NOT NULL DEFAULT 0"); + + "INTEGER NOT NULL DEFAULT 0"); } }; @@ -235,6 +237,71 @@ public final class Migrations { } }; + public static final Migration MIGRATION_7_8 = new Migration(DB_VER_7, DB_VER_8) { + @Override + public void migrate(@NonNull final SupportSQLiteDatabase database) { + database.execSQL("DELETE FROM search_history WHERE id NOT IN (SELECT id FROM (SELECT " + + "MIN(id) as id FROM search_history GROUP BY trim(search), service_id ) tmp)"); + database.execSQL("UPDATE search_history SET search = trim(search)"); + } + }; + + public static final Migration MIGRATION_8_9 = new Migration(DB_VER_8, DB_VER_9) { + @Override + public void migrate(@NonNull final SupportSQLiteDatabase database) { + try { + database.beginTransaction(); + + // Update playlists. + // Create a temp table to initialize display_index. + database.execSQL("CREATE TABLE `playlists_tmp` " + + "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`name` TEXT, `is_thumbnail_permanent` INTEGER NOT NULL, " + + "`thumbnail_stream_id` INTEGER NOT NULL, " + + "`display_index` INTEGER NOT NULL)"); + database.execSQL("INSERT INTO `playlists_tmp` " + + "(`uid`, `name`, `is_thumbnail_permanent`, `thumbnail_stream_id`, " + + "`display_index`) " + + "SELECT `uid`, `name`, `is_thumbnail_permanent`, `thumbnail_stream_id`, " + + "-1 " + + "FROM `playlists`"); + + // Replace the old table, note that this also removes the index on the name which + // we don't need anymore. + database.execSQL("DROP TABLE `playlists`"); + database.execSQL("ALTER TABLE `playlists_tmp` RENAME TO `playlists`"); + + + // Update remote_playlists. + // Create a temp table to initialize display_index. + database.execSQL("CREATE TABLE `remote_playlists_tmp` " + + "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`service_id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, " + + "`thumbnail_url` TEXT, `uploader` TEXT, " + + "`display_index` INTEGER NOT NULL," + + "`stream_count` INTEGER)"); + database.execSQL("INSERT INTO `remote_playlists_tmp` (`uid`, `service_id`, " + + "`name`, `url`, `thumbnail_url`, `uploader`, `display_index`, " + + "`stream_count`)" + + "SELECT `uid`, `service_id`, `name`, `url`, `thumbnail_url`, `uploader`, " + + "-1, `stream_count` FROM `remote_playlists`"); + + // Replace the old table, note that this also removes the index on the name which + // we don't need anymore. + database.execSQL("DROP TABLE `remote_playlists`"); + database.execSQL("ALTER TABLE `remote_playlists_tmp` RENAME TO `remote_playlists`"); + + // Create index on the new table. + database.execSQL("CREATE UNIQUE INDEX `index_remote_playlists_service_id_url` " + + "ON `remote_playlists` (`service_id`, `url`)"); + + database.setTransactionSuccessful(); + } finally { + database.endTransaction(); + } + } + }; + private Migrations() { } } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistDuplicatesEntry.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistDuplicatesEntry.java index 0fcb4ced4..3be85e6e1 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistDuplicatesEntry.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistDuplicatesEntry.java @@ -13,12 +13,17 @@ public class PlaylistDuplicatesEntry extends PlaylistMetadataEntry { @ColumnInfo(name = PLAYLIST_TIMES_STREAM_IS_CONTAINED) public final long timesStreamIsContained; + @SuppressWarnings("checkstyle:ParameterNumber") public PlaylistDuplicatesEntry(final long uid, final String name, final String thumbnailUrl, + final boolean isThumbnailPermanent, + final long thumbnailStreamId, + final long displayIndex, final long streamCount, final long timesStreamIsContained) { - super(uid, name, thumbnailUrl, streamCount); + super(uid, name, thumbnailUrl, isThumbnailPermanent, thumbnailStreamId, displayIndex, + streamCount); this.timesStreamIsContained = timesStreamIsContained; } } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java index 695f9ec5a..072c49e2c 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java @@ -1,22 +1,13 @@ package org.schabi.newpipe.database.playlist; import org.schabi.newpipe.database.LocalItem; -import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; - -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; public interface PlaylistLocalItem extends LocalItem { String getOrderingName(); - static List merge( - final List localPlaylists, - final List remotePlaylists) { - return Stream.concat(localPlaylists.stream(), remotePlaylists.stream()) - .sorted(Comparator.comparing(PlaylistLocalItem::getOrderingName, - Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))) - .collect(Collectors.toList()); - } + long getDisplayIndex(); + + long getUid(); + + void setDisplayIndex(long displayIndex); } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java index a13894030..03a1e1e30 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java @@ -2,27 +2,40 @@ package org.schabi.newpipe.database.playlist; import androidx.room.ColumnInfo; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_DISPLAY_INDEX; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_PERMANENT; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_STREAM_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL; public class PlaylistMetadataEntry implements PlaylistLocalItem { public static final String PLAYLIST_STREAM_COUNT = "streamCount"; @ColumnInfo(name = PLAYLIST_ID) - public final long uid; + private final long uid; @ColumnInfo(name = PLAYLIST_NAME) public final String name; + @ColumnInfo(name = PLAYLIST_THUMBNAIL_PERMANENT) + private final boolean isThumbnailPermanent; + @ColumnInfo(name = PLAYLIST_THUMBNAIL_STREAM_ID) + private final long thumbnailStreamId; @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) public final String thumbnailUrl; + @ColumnInfo(name = PLAYLIST_DISPLAY_INDEX) + private long displayIndex; @ColumnInfo(name = PLAYLIST_STREAM_COUNT) public final long streamCount; public PlaylistMetadataEntry(final long uid, final String name, final String thumbnailUrl, - final long streamCount) { + final boolean isThumbnailPermanent, final long thumbnailStreamId, + final long displayIndex, final long streamCount) { this.uid = uid; this.name = name; this.thumbnailUrl = thumbnailUrl; + this.isThumbnailPermanent = isThumbnailPermanent; + this.thumbnailStreamId = thumbnailStreamId; + this.displayIndex = displayIndex; this.streamCount = streamCount; } @@ -35,4 +48,27 @@ public class PlaylistMetadataEntry implements PlaylistLocalItem { public String getOrderingName() { return name; } + + public boolean isThumbnailPermanent() { + return isThumbnailPermanent; + } + + public long getThumbnailStreamId() { + return thumbnailStreamId; + } + + @Override + public long getDisplayIndex() { + return displayIndex; + } + + @Override + public long getUid() { + return uid; + } + + @Override + public void setDisplayIndex(final long displayIndex) { + this.displayIndex = displayIndex; + } } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java index 70aaa3b2d..d8071e0af 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java @@ -2,6 +2,7 @@ package org.schabi.newpipe.database.playlist.dao; import androidx.room.Dao; import androidx.room.Query; +import androidx.room.Transaction; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistEntity; @@ -36,4 +37,17 @@ public interface PlaylistDAO extends BasicDAO { @Query("SELECT COUNT(*) FROM " + PLAYLIST_TABLE) Flowable getCount(); + + @Transaction + default long upsertPlaylist(final PlaylistEntity playlist) { + final long playlistId = playlist.getUid(); + + if (playlistId == -1) { + // This situation is probably impossible. + return insert(playlist); + } else { + update(playlist); + return playlistId; + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java index 6bb849428..8ab8a2afd 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java @@ -11,6 +11,7 @@ import java.util.List; import io.reactivex.rxjava3.core.Flowable; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_DISPLAY_INDEX; import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE; @@ -31,10 +32,18 @@ public interface PlaylistRemoteDAO extends BasicDAO { + " WHERE " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId") Flowable> listByService(int serviceId); + @Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE " + + REMOTE_PLAYLIST_ID + " = :playlistId") + Flowable> getPlaylist(long playlistId); + @Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE " + REMOTE_PLAYLIST_URL + " = :url AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId") Flowable> getPlaylist(long serviceId, String url); + @Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + + " ORDER BY " + REMOTE_PLAYLIST_DISPLAY_INDEX) + Flowable> getPlaylists(); + @Query("SELECT " + REMOTE_PLAYLIST_ID + " FROM " + REMOTE_PLAYLIST_TABLE + " WHERE " + REMOTE_PLAYLIST_URL + " = :url " + "AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId") diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java index 8aa1a0c8c..85b891770 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java @@ -18,10 +18,12 @@ import io.reactivex.rxjava3.core.Flowable; import static org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry.PLAYLIST_TIMES_STREAM_IS_CONTAINED; import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_DISPLAY_INDEX; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.DEFAULT_THUMBNAIL; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_PERMANENT; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_STREAM_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL; import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX; @@ -91,7 +93,9 @@ public interface PlaylistStreamDAO extends BasicDAO { Flowable> getOrderedStreamsOf(long playlistId); @Transaction - @Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + "," + @Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + ", " + + PLAYLIST_THUMBNAIL_PERMANENT + ", " + PLAYLIST_THUMBNAIL_STREAM_ID + ", " + + PLAYLIST_DISPLAY_INDEX + ", " + " CASE WHEN " + PLAYLIST_THUMBNAIL_STREAM_ID + " = " + PlaylistEntity.DEFAULT_THUMBNAIL_ID + " THEN " + "'" + DEFAULT_THUMBNAIL + "'" @@ -105,7 +109,7 @@ public interface PlaylistStreamDAO extends BasicDAO { + " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE + " ON " + PLAYLIST_TABLE + "." + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID + " GROUP BY " + PLAYLIST_ID - + " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC") + + " ORDER BY " + PLAYLIST_DISPLAY_INDEX) Flowable> getPlaylistMetadata(); @RewriteQueriesToDropUnusedColumns @@ -126,8 +130,9 @@ public interface PlaylistStreamDAO extends BasicDAO { Flowable> getStreamsWithoutDuplicates(long playlistId); @Transaction - @Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", " - + PLAYLIST_NAME + ", " + @Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", " + PLAYLIST_NAME + ", " + + PLAYLIST_THUMBNAIL_PERMANENT + ", " + PLAYLIST_THUMBNAIL_STREAM_ID + ", " + + PLAYLIST_DISPLAY_INDEX + ", " + " CASE WHEN " + PLAYLIST_THUMBNAIL_STREAM_ID + " = " + PlaylistEntity.DEFAULT_THUMBNAIL_ID + " THEN " + "'" + DEFAULT_THUMBNAIL + "'" @@ -149,6 +154,6 @@ public interface PlaylistStreamDAO extends BasicDAO { + " AND :streamUrl = :streamUrl" + " GROUP BY " + JOIN_PLAYLIST_ID - + " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC") + + " ORDER BY " + PLAYLIST_DISPLAY_INDEX) Flowable> getPlaylistDuplicatesMetadata(String streamUrl); } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java index efb7278fd..e0c1a06b7 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java @@ -2,16 +2,15 @@ package org.schabi.newpipe.database.playlist.model; import androidx.room.ColumnInfo; import androidx.room.Entity; -import androidx.room.Index; +import androidx.room.Ignore; import androidx.room.PrimaryKey; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; import org.schabi.newpipe.R; +import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; -@Entity(tableName = PLAYLIST_TABLE, - indices = {@Index(value = {PLAYLIST_NAME})}) +@Entity(tableName = PLAYLIST_TABLE) public class PlaylistEntity { public static final String DEFAULT_THUMBNAIL = "drawable://" @@ -22,6 +21,7 @@ public class PlaylistEntity { public static final String PLAYLIST_ID = "uid"; public static final String PLAYLIST_NAME = "name"; public static final String PLAYLIST_THUMBNAIL_URL = "thumbnail_url"; + public static final String PLAYLIST_DISPLAY_INDEX = "display_index"; public static final String PLAYLIST_THUMBNAIL_PERMANENT = "is_thumbnail_permanent"; public static final String PLAYLIST_THUMBNAIL_STREAM_ID = "thumbnail_stream_id"; @@ -38,11 +38,24 @@ public class PlaylistEntity { @ColumnInfo(name = PLAYLIST_THUMBNAIL_STREAM_ID) private long thumbnailStreamId; + @ColumnInfo(name = PLAYLIST_DISPLAY_INDEX) + private long displayIndex; + public PlaylistEntity(final String name, final boolean isThumbnailPermanent, - final long thumbnailStreamId) { + final long thumbnailStreamId, final long displayIndex) { this.name = name; this.isThumbnailPermanent = isThumbnailPermanent; this.thumbnailStreamId = thumbnailStreamId; + this.displayIndex = displayIndex; + } + + @Ignore + public PlaylistEntity(final PlaylistMetadataEntry item) { + this.uid = item.getUid(); + this.name = item.name; + this.isThumbnailPermanent = item.isThumbnailPermanent(); + this.thumbnailStreamId = item.getThumbnailStreamId(); + this.displayIndex = item.getDisplayIndex(); } public long getUid() { @@ -77,4 +90,11 @@ public class PlaylistEntity { this.isThumbnailPermanent = isThumbnailSet; } + public long getDisplayIndex() { + return displayIndex; + } + + public void setDisplayIndex(final long displayIndex) { + this.displayIndex = displayIndex; + } } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java index 7c6b4a8b0..60027a057 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java @@ -21,7 +21,6 @@ import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.RE @Entity(tableName = REMOTE_PLAYLIST_TABLE, indices = { - @Index(value = {REMOTE_PLAYLIST_NAME}), @Index(value = {REMOTE_PLAYLIST_SERVICE_ID, REMOTE_PLAYLIST_URL}, unique = true) }) public class PlaylistRemoteEntity implements PlaylistLocalItem { @@ -32,6 +31,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { public static final String REMOTE_PLAYLIST_URL = "url"; public static final String REMOTE_PLAYLIST_THUMBNAIL_URL = "thumbnail_url"; public static final String REMOTE_PLAYLIST_UPLOADER_NAME = "uploader"; + public static final String REMOTE_PLAYLIST_DISPLAY_INDEX = "display_index"; public static final String REMOTE_PLAYLIST_STREAM_COUNT = "stream_count"; @PrimaryKey(autoGenerate = true) @@ -53,6 +53,9 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { @ColumnInfo(name = REMOTE_PLAYLIST_UPLOADER_NAME) private String uploader; + @ColumnInfo(name = REMOTE_PLAYLIST_DISPLAY_INDEX) + private long displayIndex = -1; // Make sure the new item is on the top + @ColumnInfo(name = REMOTE_PLAYLIST_STREAM_COUNT) private Long streamCount; @@ -67,6 +70,19 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { this.streamCount = streamCount; } + @Ignore + public PlaylistRemoteEntity(final int serviceId, final String name, final String url, + final String thumbnailUrl, final String uploader, + final long displayIndex, final Long streamCount) { + this.serviceId = serviceId; + this.name = name; + this.url = url; + this.thumbnailUrl = thumbnailUrl; + this.uploader = uploader; + this.displayIndex = displayIndex; + this.streamCount = streamCount; + } + @Ignore public PlaylistRemoteEntity(final PlaylistInfo info) { this(info.getServiceId(), info.getName(), info.getUrl(), @@ -93,6 +109,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { && TextUtils.equals(getUploader(), info.getUploaderName()); } + @Override public long getUid() { return uid; } @@ -141,6 +158,16 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { this.uploader = uploader; } + @Override + public long getDisplayIndex() { + return displayIndex; + } + + @Override + public void setDisplayIndex(final long displayIndex) { + this.displayIndex = displayIndex; + } + public Long getStreamCount() { return streamCount; } diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java index a61a22a84..df5a3067a 100644 --- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java @@ -1,6 +1,7 @@ package org.schabi.newpipe.database.subscription; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.Ignore; @@ -95,11 +96,12 @@ public class SubscriptionEntity { this.name = name; } + @Nullable public String getAvatarUrl() { return avatarUrl; } - public void setAvatarUrl(final String avatarUrl) { + public void setAvatarUrl(@Nullable final String avatarUrl) { this.avatarUrl = avatarUrl; } diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 2e0a421da..db2066b27 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -7,8 +7,6 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import android.app.Activity; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; @@ -16,6 +14,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; +import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -74,6 +73,7 @@ import org.schabi.newpipe.util.ThemeHelper; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -111,14 +111,11 @@ public class DownloadDialog extends DialogFragment @State int selectedSubtitleIndex = 0; // default to the first item - @Nullable - private OnDismissListener onDismissListener = null; - private StoredDirectoryHelper mainStorageAudio = null; private StoredDirectoryHelper mainStorageVideo = null; private DownloadManager downloadManager = null; private ActionMenuItemView okButton = null; - private Context context; + private Context context = null; private boolean askForSavePath; private AudioTrackAdapter audioTrackAdapter; @@ -146,7 +143,6 @@ public class DownloadDialog extends DialogFragment registerForActivityResult( new StartActivityForResult(), this::requestDownloadPickVideoFolderResult); - /*////////////////////////////////////////////////////////////////////////// // Instance creation //////////////////////////////////////////////////////////////////////////*/ @@ -194,13 +190,6 @@ public class DownloadDialog extends DialogFragment this.selectedVideoIndex = ListHelper.getDefaultResolutionIndex(context, videoStreams); } - /** - * @param onDismissListener the listener to call in {@link #onDismiss(DialogInterface)} - */ - public void setOnDismissListener(@Nullable final OnDismissListener onDismissListener) { - this.onDismissListener = onDismissListener; - } - /*////////////////////////////////////////////////////////////////////////// // Android lifecycle @@ -220,6 +209,8 @@ public class DownloadDialog extends DialogFragment return; } + // context will remain null if dismiss() was called above, allowing to check whether the + // dialog is being dismissed in onViewCreated() context = getContext(); setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context)); @@ -304,6 +295,9 @@ public class DownloadDialog extends DialogFragment @Nullable final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); dialogBinding = DownloadDialogBinding.bind(view); + if (context == null) { + return; // the dialog is being dismissed, see the call to dismiss() in onCreate() + } dialogBinding.fileName.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName())); @@ -363,14 +357,6 @@ public class DownloadDialog extends DialogFragment }); } - @Override - public void onDismiss(@NonNull final DialogInterface dialog) { - super.onDismiss(dialog); - if (onDismissListener != null) { - onDismissListener.onDismiss(dialog); - } - } - @Override public void onDestroy() { super.onDestroy(); @@ -564,7 +550,6 @@ public class DownloadDialog extends DialogFragment } } - /*////////////////////////////////////////////////////////////////////////// // Listeners //////////////////////////////////////////////////////////////////////////*/ @@ -783,6 +768,7 @@ public class DownloadDialog extends DialogFragment final StoredDirectoryHelper mainStorage; final MediaFormat format; final String selectedMediaType; + final long size; // first, build the filename and get the output folder (if possible) // later, run a very very very large file checking logic @@ -794,6 +780,7 @@ public class DownloadDialog extends DialogFragment selectedMediaType = getString(R.string.last_download_type_audio_key); mainStorage = mainStorageAudio; format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat(); + size = getWrappedAudioStreams().getSizeInBytes(selectedAudioIndex); if (format == MediaFormat.WEBMA_OPUS) { mimeTmp = "audio/ogg"; filenameTmp += "opus"; @@ -806,6 +793,7 @@ public class DownloadDialog extends DialogFragment selectedMediaType = getString(R.string.last_download_type_video_key); mainStorage = mainStorageVideo; format = videoStreamsAdapter.getItem(selectedVideoIndex).getFormat(); + size = wrappedVideoStreams.getSizeInBytes(selectedVideoIndex); if (format != null) { mimeTmp = format.mimeType; filenameTmp += format.getSuffix(); @@ -815,6 +803,7 @@ public class DownloadDialog extends DialogFragment selectedMediaType = getString(R.string.last_download_type_subtitle_key); mainStorage = mainStorageVideo; // subtitle & video files go together format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat(); + size = wrappedSubtitleStreams.getSizeInBytes(selectedSubtitleIndex); if (format != null) { mimeTmp = format.mimeType; } @@ -870,6 +859,21 @@ public class DownloadDialog extends DialogFragment return; } + // Check for free storage space + final long freeSpace = mainStorage.getFreeStorageSpace(); + if (freeSpace <= size) { + Toast.makeText(context, getString(R. + string.error_insufficient_storage), Toast.LENGTH_LONG).show(); + // move the user to storage setting tab + final Intent storageSettingsIntent = new Intent(Settings. + ACTION_INTERNAL_STORAGE_SETTINGS); + if (storageSettingsIntent.resolveActivity(context.getPackageManager()) + != null) { + startActivity(storageSettingsIntent); + } + return; + } + // check for existing file with the same name checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), filenameTmp, mimeTmp); @@ -1052,7 +1056,7 @@ public class DownloadDialog extends DialogFragment final char kind; int threads = dialogBinding.threads.getProgress() + 1; final String[] urls; - final MissionRecoveryInfo[] recoveryInfo; + final List recoveryInfo; String psName = null; String[] psArgs = null; long nearLength = 0; @@ -1117,9 +1121,7 @@ public class DownloadDialog extends DialogFragment urls = new String[] { selectedStream.getContent() }; - recoveryInfo = new MissionRecoveryInfo[] { - new MissionRecoveryInfo(selectedStream) - }; + recoveryInfo = List.of(new MissionRecoveryInfo(selectedStream)); } else { if (secondaryStream.getDeliveryMethod() != PROGRESSIVE_HTTP) { throw new IllegalArgumentException("Unsupported stream delivery format" @@ -1129,12 +1131,14 @@ public class DownloadDialog extends DialogFragment urls = new String[] { selectedStream.getContent(), secondaryStream.getContent() }; - recoveryInfo = new MissionRecoveryInfo[] {new MissionRecoveryInfo(selectedStream), - new MissionRecoveryInfo(secondaryStream)}; + recoveryInfo = List.of( + new MissionRecoveryInfo(selectedStream), + new MissionRecoveryInfo(secondaryStream) + ); } DownloadManagerService.startMission(context, urls, storage, kind, threads, - currentInfo.getUrl(), psName, psArgs, nearLength, recoveryInfo); + currentInfo.getUrl(), psName, psArgs, nearLength, new ArrayList<>(recoveryInfo)); Toast.makeText(context, getString(R.string.download_has_started), Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java index 6b34c8e90..831a8cc4b 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java @@ -17,6 +17,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.IntentCompat; import com.grack.nanojson.JsonWriter; @@ -105,7 +106,7 @@ public class ErrorActivity extends AppCompatActivity { actionBar.setDisplayShowTitleEnabled(true); } - errorInfo = intent.getParcelableExtra(ERROR_INFO); + errorInfo = IntentCompat.getParcelableExtra(intent, ERROR_INFO, ErrorInfo.class); // important add guru meditation addGuruMeditation(); diff --git a/app/src/main/java/org/schabi/newpipe/error/UserAction.java b/app/src/main/java/org/schabi/newpipe/error/UserAction.java index 976173373..c8701cd77 100644 --- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java @@ -19,6 +19,7 @@ public enum UserAction { REQUESTED_PLAYLIST("requested playlist"), REQUESTED_KIOSK("requested kiosk"), REQUESTED_COMMENTS("requested comments"), + REQUESTED_COMMENT_REPLIES("requested comment replies"), REQUESTED_FEED("requested feed"), REQUESTED_BOOKMARK("bookmark"), DELETE_FROM_HISTORY("delete from history"), diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index d50f0b0d8..52a41d38f 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -220,7 +220,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte public void commitPlaylistTabs() { pagerAdapter.getLocalPlaylistFragments() .stream() - .forEach(LocalPlaylistFragment::commitChanges); + .forEach(LocalPlaylistFragment::saveImmediate); } private void updateTabLayoutPosition() { @@ -282,7 +282,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte * Keep reference to LocalPlaylistFragments, because their data can be modified by the user * during runtime and changes are not committed immediately. However, in some cases, * the changes need to be committed immediately by calling - * {@link LocalPlaylistFragment#commitChanges()}. + * {@link LocalPlaylistFragment#saveImmediate()}. * The fragments are removed when {@link LocalPlaylistFragment#onDestroy()} is called. */ private final List localPlaylistFragments = new ArrayList<>(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java index e3bdc6592..4789b02e6 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java @@ -64,7 +64,7 @@ public abstract class BaseDescriptionFragment extends BaseFragment { /** * Get the description to display. - * @return description object + * @return description object, if available */ @Nullable protected abstract Description getDescription(); @@ -73,7 +73,7 @@ public abstract class BaseDescriptionFragment extends BaseFragment { * Get the streaming service. Used for generating description links. * @return streaming service */ - @Nullable + @NonNull protected abstract StreamingService getService(); /** @@ -93,7 +93,7 @@ public abstract class BaseDescriptionFragment extends BaseFragment { * Get the list of tags to display below the description. * @return tag list */ - @Nullable + @NonNull public abstract List getTags(); /** @@ -158,7 +158,7 @@ public abstract class BaseDescriptionFragment extends BaseFragment { final LinearLayout layout, final boolean linkifyContent, @StringRes final int type, - @Nullable final String content) { + @NonNull final String content) { if (isBlank(content)) { return; } @@ -221,16 +221,12 @@ public abstract class BaseDescriptionFragment extends BaseFragment { urls.append(imageSizeToText(image.getWidth())); } else { switch (image.getEstimatedResolutionLevel()) { - case LOW: - urls.append(getString(R.string.image_quality_low)); - break; - default: // unreachable, Image.ResolutionLevel.UNKNOWN is already filtered out - case MEDIUM: - urls.append(getString(R.string.image_quality_medium)); - break; - case HIGH: - urls.append(getString(R.string.image_quality_high)); - break; + case LOW -> urls.append(getString(R.string.image_quality_low)); + case MEDIUM -> urls.append(getString(R.string.image_quality_medium)); + case HIGH -> urls.append(getString(R.string.image_quality_high)); + default -> { + // unreachable, Image.ResolutionLevel.UNKNOWN is already filtered out + } } } @@ -255,7 +251,7 @@ public abstract class BaseDescriptionFragment extends BaseFragment { private void addTagsMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { final List tags = getTags(); - if (tags != null && !tags.isEmpty()) { + if (!tags.isEmpty()) { final var itemBinding = ItemMetadataTagsBinding.inflate(inflater, layout, false); tags.stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(tag -> { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java index ada12fc8e..581e54156 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java @@ -7,6 +7,7 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -23,56 +24,43 @@ import icepick.State; public class DescriptionFragment extends BaseDescriptionFragment { @State - StreamInfo streamInfo = null; - - public DescriptionFragment() { - } + StreamInfo streamInfo; public DescriptionFragment(final StreamInfo streamInfo) { this.streamInfo = streamInfo; } - @Nullable - @Override - protected Description getDescription() { - if (streamInfo == null) { - return null; - } - return streamInfo.getDescription(); + public DescriptionFragment() { + // keep empty constructor for IcePick when resuming fragment from memory } + @Nullable @Override + protected Description getDescription() { + return streamInfo.getDescription(); + } + + @NonNull + @Override protected StreamingService getService() { - if (streamInfo == null) { - return null; - } return streamInfo.getService(); } @Override protected int getServiceId() { - if (streamInfo == null) { - return -1; - } return streamInfo.getServiceId(); } - @Nullable + @NonNull @Override protected String getStreamUrl() { - if (streamInfo == null) { - return null; - } return streamInfo.getUrl(); } - @Nullable + @NonNull @Override public List getTags() { - if (streamInfo == null) { - return null; - } return streamInfo.getTags(); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 7db5e0251..95b54f65a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -72,8 +72,8 @@ import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.ReCaptchaActivity; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.Image; -import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream.AudioStream; @@ -106,16 +106,17 @@ import org.schabi.newpipe.player.ui.VideoPlayerUi; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.InfoCache; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PermissionHelper; -import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.PlayButtonHelper; import org.schabi.newpipe.util.StreamTypeUtil; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.external_communication.ShareUtils; -import org.schabi.newpipe.util.PlayButtonHelper; +import org.schabi.newpipe.util.image.PicassoHelper; import java.util.ArrayList; import java.util.Iterator; @@ -481,7 +482,7 @@ public final class VideoDetailFragment // commit previous pending changes to database if (fragment instanceof LocalPlaylistFragment) { - ((LocalPlaylistFragment) fragment).commitChanges(); + ((LocalPlaylistFragment) fragment).saveImmediate(); } else if (fragment instanceof MainFragment) { ((MainFragment) fragment).commitPlaylistTabs(); } @@ -1012,6 +1013,20 @@ public final class VideoDetailFragment updateTabLayoutVisibility(); } + public void scrollToComment(final CommentsInfoItem comment) { + final int commentsTabPos = pageAdapter.getItemPositionByTitle(COMMENTS_TAB_TAG); + final Fragment fragment = pageAdapter.getItem(commentsTabPos); + if (!(fragment instanceof CommentsFragment)) { + return; + } + + // unexpand the app bar only if scrolling to the comment succeeded + if (((CommentsFragment) fragment).scrollToComment(comment)) { + binding.appBarLayout.setExpanded(false, false); + binding.viewPager.setCurrentItem(commentsTabPos, false); + } + } + /*////////////////////////////////////////////////////////////////////////// // Play Utils //////////////////////////////////////////////////////////////////////////*/ @@ -1430,7 +1445,7 @@ public final class VideoDetailFragment super.showLoading(); //if data is already cached, transition from VISIBLE -> INVISIBLE -> VISIBLE is not required - if (!ExtractorHelper.isCached(serviceId, url, InfoItem.InfoType.STREAM)) { + if (!ExtractorHelper.isCached(serviceId, url, InfoCache.Type.STREAM)) { binding.detailContentRootHiding.setVisibility(View.INVISIBLE); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index e7e9f5aad..dd5eb6c8a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -231,6 +231,8 @@ public abstract class BaseListInfoFragment getTags() { - if (channelInfo == null) { - return null; - } return channelInfo.getTags(); } @@ -93,10 +79,11 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { return; } - final Context context = getContext(); if (channelInfo.getSubscriberCount() != UNKNOWN_SUBSCRIBER_COUNT) { addMetadataItem(inflater, layout, false, R.string.metadata_subscribers, - Localization.localizeNumber(context, channelInfo.getSubscriberCount())); + Localization.localizeNumber( + requireContext(), + channelInfo.getSubscriberCount())); } addImagesMetadataItem(inflater, layout, R.string.metadata_avatars, diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index b16f40a4a..7e83d9958 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -474,7 +474,7 @@ public class ChannelFragment extends BaseStateFragment if (ChannelTabHelper.showChannelTab( context, preferences, R.string.show_channel_tabs_about)) { tabAdapter.addFragment( - ChannelAboutFragment.getInstance(currentInfo), + new ChannelAboutFragment(currentInfo), context.getString(R.string.channel_tab_about)); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java new file mode 100644 index 000000000..304eaf55a --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java @@ -0,0 +1,170 @@ +package org.schabi.newpipe.fragments.list.comments; + +import static org.schabi.newpipe.util.ServiceHelper.getServiceById; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.text.HtmlCompat; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.databinding.CommentRepliesHeaderBinding; +import org.schabi.newpipe.error.UserAction; +import org.schabi.newpipe.extractor.ListExtractor; +import org.schabi.newpipe.extractor.comments.CommentsInfoItem; +import org.schabi.newpipe.fragments.list.BaseListInfoFragment; +import org.schabi.newpipe.info_list.ItemViewMode; +import org.schabi.newpipe.util.DeviceUtils; +import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.image.ImageStrategy; +import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.text.TextLinkifier; + +import java.util.Queue; +import java.util.function.Supplier; + +import icepick.State; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; + +public final class CommentRepliesFragment + extends BaseListInfoFragment { + + public static final String TAG = CommentRepliesFragment.class.getSimpleName(); + + @State + CommentsInfoItem commentsInfoItem; // the comment to show replies of + private final CompositeDisposable disposables = new CompositeDisposable(); + + + /*////////////////////////////////////////////////////////////////////////// + // Constructors and lifecycle + //////////////////////////////////////////////////////////////////////////*/ + + // only called by the Android framework, after which readFrom is called and restores all data + public CommentRepliesFragment() { + super(UserAction.REQUESTED_COMMENT_REPLIES); + } + + public CommentRepliesFragment(@NonNull final CommentsInfoItem commentsInfoItem) { + this(); + this.commentsInfoItem = commentsInfoItem; + // setting "" as title since the title will be properly set right after + setInitialData(commentsInfoItem.getServiceId(), commentsInfoItem.getUrl(), ""); + } + + @Nullable + @Override + public View onCreateView(@NonNull final LayoutInflater inflater, + @Nullable final ViewGroup container, + @Nullable final Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_comments, container, false); + } + + @Override + public void onDestroyView() { + disposables.clear(); + super.onDestroyView(); + } + + @Override + protected Supplier getListHeaderSupplier() { + return () -> { + final CommentRepliesHeaderBinding binding = CommentRepliesHeaderBinding + .inflate(activity.getLayoutInflater(), itemsList, false); + final CommentsInfoItem item = commentsInfoItem; + + // load the author avatar + PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(binding.authorAvatar); + binding.authorAvatar.setVisibility(ImageStrategy.shouldLoadImages() + ? View.VISIBLE : View.GONE); + + // setup author name and comment date + binding.authorName.setText(item.getUploaderName()); + binding.uploadDate.setText(Localization.relativeTimeOrTextual( + getContext(), item.getUploadDate(), item.getTextualUploadDate())); + binding.authorTouchArea.setOnClickListener( + v -> NavigationHelper.openCommentAuthorIfPresent(requireActivity(), item)); + + // setup like count, hearted and pinned + binding.thumbsUpCount.setText( + Localization.likeCount(requireContext(), item.getLikeCount())); + // for heartImage goneMarginEnd was used, but there is no way to tell ConstraintLayout + // not to use a different margin only when both the next two views are gone + ((ConstraintLayout.LayoutParams) binding.thumbsUpCount.getLayoutParams()) + .setMarginEnd(DeviceUtils.dpToPx( + (item.isHeartedByUploader() || item.isPinned() ? 8 : 16), + requireContext())); + binding.heartImage.setVisibility(item.isHeartedByUploader() ? View.VISIBLE : View.GONE); + binding.pinnedImage.setVisibility(item.isPinned() ? View.VISIBLE : View.GONE); + + // setup comment content + TextLinkifier.fromDescription(binding.commentContent, item.getCommentText(), + HtmlCompat.FROM_HTML_MODE_LEGACY, getServiceById(item.getServiceId()), + item.getUrl(), disposables, null); + + return binding.getRoot(); + }; + } + + + /*////////////////////////////////////////////////////////////////////////// + // State saving + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void writeTo(final Queue objectsToSave) { + super.writeTo(objectsToSave); + objectsToSave.add(commentsInfoItem); + } + + @Override + public void readFrom(@NonNull final Queue savedObjects) throws Exception { + super.readFrom(savedObjects); + commentsInfoItem = (CommentsInfoItem) savedObjects.poll(); + } + + + /*////////////////////////////////////////////////////////////////////////// + // Data loading + //////////////////////////////////////////////////////////////////////////*/ + + @Override + protected Single loadResult(final boolean forceLoad) { + return Single.fromCallable(() -> new CommentRepliesInfo(commentsInfoItem, + // the reply count string will be shown as the activity title + Localization.replyCount(requireContext(), commentsInfoItem.getReplyCount()))); + } + + @Override + protected Single> loadMoreItemsLogic() { + // commentsInfoItem.getUrl() should contain the url of the original + // ListInfo, which should be the stream url + return ExtractorHelper.getMoreCommentItems( + serviceId, commentsInfoItem.getUrl(), currentNextPage); + } + + + /*////////////////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////////////////*/ + + @Override + protected ItemViewMode getItemViewMode() { + return ItemViewMode.LIST; + } + + /** + * @return the comment to which the replies are shown + */ + public CommentsInfoItem getCommentsInfoItem() { + return commentsInfoItem; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesInfo.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesInfo.java new file mode 100644 index 000000000..cc160c395 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesInfo.java @@ -0,0 +1,22 @@ +package org.schabi.newpipe.fragments.list.comments; + +import org.schabi.newpipe.extractor.ListInfo; +import org.schabi.newpipe.extractor.comments.CommentsInfoItem; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; + +import java.util.Collections; + +public final class CommentRepliesInfo extends ListInfo { + /** + * This class is used to wrap the comment replies page into a ListInfo object. + * + * @param comment the comment from which to get replies + * @param name will be shown as the fragment title + */ + public CommentRepliesInfo(final CommentsInfoItem comment, final String name) { + super(comment.getServiceId(), + new ListLinkHandler("", "", "", Collections.emptyList(), null), name); + setNextPage(comment.getReplies()); + setRelatedItems(Collections.emptyList()); // since it must be non-null + } +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java index 5a5f84968..e25e02794 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java @@ -110,4 +110,14 @@ public class CommentsFragment extends BaseListInfoFragment + headerBinding.playlistDescriptionReadMore.setText( + Boolean.TRUE.equals(isEllipsized) ? R.string.show_more : R.string.show_less + )); + ellipsizer.setOnContentChanged(canBeEllipsized -> { + headerBinding.playlistDescriptionReadMore.setVisibility( + Boolean.TRUE.equals(canBeEllipsized) ? View.VISIBLE : View.GONE); + if (Boolean.TRUE.equals(canBeEllipsized)) { + ellipsizer.ellipsize(); + } + }); + ellipsizer.setContent(description); + headerBinding.playlistDescriptionReadMore.setOnClickListener(v -> ellipsizer.toggle()); + } else { + headerBinding.playlistDescription.setVisibility(View.GONE); + headerBinding.playlistDescriptionReadMore.setVisibility(View.GONE); + } if (!result.getErrors().isEmpty()) { showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.REQUESTED_PLAYLIST, @@ -459,4 +495,20 @@ public class PlaylistFragment extends BaseListInfoFragment list, + final boolean isDurationComplete) { + if (activity != null && headerBinding != null) { + playlistOverallDurationSeconds += list.stream() + .mapToLong(x -> x.getDuration()) + .sum(); + headerBinding.playlistStreamCount.setText( + Localization.concatenateStrings( + Localization.localizeStreamCount(activity, streamCount), + Localization.getDurationString(playlistOverallDurationSeconds, + isDurationComplete, true)) + ); + } + } + } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 4bae6f1ca..eef3455ae 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -1,6 +1,7 @@ package org.schabi.newpipe.fragments.list.search; import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; +import static org.schabi.newpipe.extractor.utils.Utils.isBlank; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; import static java.util.Arrays.asList; @@ -389,7 +390,7 @@ public class SearchFragment extends BaseListFragment suggestionPublisher - .onNext(searchEditText.getText().toString()), + .onNext(getSearchEditString()), throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY, "Deleting item failed"))); @@ -723,9 +726,9 @@ public class SearchFragment extends BaseListFragment - searchHistoryEntries.stream() - .map(entry -> new SuggestionItem(true, entry)) - .collect(Collectors.toList())); + searchHistoryEntries.stream() + .map(entry -> new SuggestionItem(true, entry)) + .collect(Collectors.toList())); } private Observable> getRemoteSuggestionsObservable(final String query) { @@ -792,12 +795,12 @@ public class SearchFragment extends BaseListFragment showSnackBarError(new ErrorInfo( - throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId))); + throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId))); } @Override @@ -805,7 +808,13 @@ public class SearchFragment extends BaseListFragment NavigationHelper.getIntentByLink(activity, - streamingService, theSearchString)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(intent -> { - getFM().popBackStackImmediate(); - activity.startActivity(intent); - }, throwable -> showTextError(getString(R.string.unsupported_url)))); - return; - } + showLoading(); + disposables.add(Observable + .fromCallable(() -> NavigationHelper.getIntentByLink(activity, + streamingService, theSearchString)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(intent -> { + getFM().popBackStackImmediate(); + activity.startActivity(intent); + }, throwable -> showTextError(getString(R.string.unsupported_url)))); + return; } catch (final Exception ignored) { // Exception occurred, it's not a url } + // prepare search lastSearchedString = this.searchString; this.searchString = theSearchString; infoListAdapter.clearStreamItemList(); @@ -842,13 +852,17 @@ public class SearchFragment extends BaseListFragment { }, + ignored -> { + }, throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.SEARCHED, theSearchString, serviceId)) )); + + // load search results suggestionPublisher.onNext(theSearchString); startLoading(false); } @@ -938,6 +952,14 @@ public class SearchFragment extends BaseListFragment cannot be bundled without creating some containers @@ -1080,7 +1105,7 @@ public class SearchFragment extends BaseListFragment suggestionPublisher - .onNext(searchEditText.getText().toString()), + .onNext(getSearchEditString()), throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY, "Deleting item failed"))); disposables.add(onDelete); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java index 65664e0e3..e46937ede 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java @@ -21,18 +21,17 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.info_list.ItemViewMode; import org.schabi.newpipe.ktx.ViewUtils; -import org.schabi.newpipe.util.RelatedItemInfo; import java.io.Serializable; import java.util.function.Supplier; import io.reactivex.rxjava3.core.Single; -public class RelatedItemsFragment extends BaseListInfoFragment +public class RelatedItemsFragment extends BaseListInfoFragment implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String INFO_KEY = "related_info_key"; - private RelatedItemInfo relatedItemInfo; + private RelatedItemsInfo relatedItemsInfo; /*////////////////////////////////////////////////////////////////////////// // Views @@ -69,7 +68,7 @@ public class RelatedItemsFragment extends BaseListInfoFragment getListHeaderSupplier() { - if (relatedItemInfo == null || relatedItemInfo.getRelatedItems() == null) { + if (relatedItemsInfo == null || relatedItemsInfo.getRelatedItems() == null) { return null; } @@ -97,8 +96,8 @@ public class RelatedItemsFragment extends BaseListInfoFragment loadResult(final boolean forceLoad) { - return Single.fromCallable(() -> relatedItemInfo); + protected Single loadResult(final boolean forceLoad) { + return Single.fromCallable(() -> relatedItemsInfo); } @Override @@ -110,7 +109,7 @@ public class RelatedItemsFragment extends BaseListInfoFragment { + /** + * This class is used to wrap the related items of a StreamInfo into a ListInfo object. + * + * @param info the stream info from which to get related items + */ + public RelatedItemsInfo(final StreamInfo info) { + super(info.getServiceId(), new ListLinkHandler(info.getOriginalUrl(), info.getUrl(), + info.getId(), Collections.emptyList(), null), info.getName()); + setRelatedItems(new ArrayList<>(info.getRelatedItems())); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java index 68f19ee97..d959c6327 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java @@ -13,8 +13,7 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder; import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder; -import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder; -import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder; +import org.schabi.newpipe.info_list.holder.CommentInfoItemHolder; import org.schabi.newpipe.info_list.holder.InfoItemHolder; import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder; import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder; @@ -87,8 +86,7 @@ public class InfoItemBuilder { return useMiniVariant ? new PlaylistMiniInfoItemHolder(this, parent) : new PlaylistInfoItemHolder(this, parent); case COMMENT: - return useMiniVariant ? new CommentsMiniInfoItemHolder(this, parent) - : new CommentsInfoItemHolder(this, parent); + return new CommentInfoItemHolder(this, parent); default: throw new RuntimeException("InfoType not expected = " + infoType.name()); } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java index a13f0e5aa..575568c00 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java @@ -21,8 +21,7 @@ import org.schabi.newpipe.info_list.holder.ChannelCardInfoItemHolder; import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder; import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder; import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder; -import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder; -import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder; +import org.schabi.newpipe.info_list.holder.CommentInfoItemHolder; import org.schabi.newpipe.info_list.holder.InfoItemHolder; import org.schabi.newpipe.info_list.holder.PlaylistCardInfoItemHolder; import org.schabi.newpipe.info_list.holder.PlaylistGridInfoItemHolder; @@ -79,8 +78,7 @@ public class InfoListAdapter extends RecyclerView.Adapter { + if (Boolean.TRUE.equals(isEllipsized)) { + denyLinkFocus(); + } else { + determineMovementMethod(); + } + }); + } + + @Override + public void updateFromItem(final InfoItem infoItem, + final HistoryRecordManager historyRecordManager) { + if (!(infoItem instanceof CommentsInfoItem)) { + return; + } + final CommentsInfoItem item = (CommentsInfoItem) infoItem; + + + // load the author avatar + PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView); + if (ImageStrategy.shouldLoadImages()) { + itemThumbnailView.setVisibility(View.VISIBLE); + itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding, + commentVerticalPadding, commentVerticalPadding); + } else { + itemThumbnailView.setVisibility(View.GONE); + itemRoot.setPadding(commentHorizontalPadding, commentVerticalPadding, + commentHorizontalPadding, commentVerticalPadding); + } + itemThumbnailView.setOnClickListener(view -> openCommentAuthor(item)); + + + // setup the top row, with pinned icon, author name and comment date + itemPinnedView.setVisibility(item.isPinned() ? View.VISIBLE : View.GONE); + itemTitleView.setText(Localization.concatenateStrings(item.getUploaderName(), + Localization.relativeTimeOrTextual(itemBuilder.getContext(), item.getUploadDate(), + item.getTextualUploadDate()))); + + + // setup bottom row, with likes, heart and replies button + itemLikesCountView.setText( + Localization.likeCount(itemBuilder.getContext(), item.getLikeCount())); + + itemHeartView.setVisibility(item.isHeartedByUploader() ? View.VISIBLE : View.GONE); + + final boolean hasReplies = item.getReplies() != null; + repliesButton.setOnClickListener(hasReplies ? v -> openCommentReplies(item) : null); + repliesButton.setVisibility(hasReplies ? View.VISIBLE : View.GONE); + repliesButton.setText(hasReplies + ? Localization.replyCount(itemBuilder.getContext(), item.getReplyCount()) : ""); + ((RelativeLayout.LayoutParams) itemThumbsUpView.getLayoutParams()).topMargin = + hasReplies ? 0 : DeviceUtils.dpToPx(6, itemBuilder.getContext()); + + + // setup comment content and click listeners to expand/ellipsize it + textEllipsizer.setStreamingService(getServiceById(item.getServiceId())); + textEllipsizer.setStreamUrl(item.getUrl()); + textEllipsizer.setContent(item.getCommentText()); + textEllipsizer.ellipsize(); + + //noinspection ClickableViewAccessibility + itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); + + itemView.setOnClickListener(view -> { + textEllipsizer.toggle(); + if (itemBuilder.getOnCommentsSelectedListener() != null) { + itemBuilder.getOnCommentsSelectedListener().selected(item); + } + }); + + itemView.setOnLongClickListener(view -> { + if (DeviceUtils.isTv(itemBuilder.getContext())) { + openCommentAuthor(item); + } else { + final CharSequence text = itemContentView.getText(); + if (text != null) { + ShareUtils.copyToClipboard(itemBuilder.getContext(), text.toString()); + } + } + return true; + }); + } + + private void openCommentAuthor(@NonNull final CommentsInfoItem item) { + NavigationHelper.openCommentAuthorIfPresent((FragmentActivity) itemBuilder.getContext(), + item); + } + + private void openCommentReplies(@NonNull final CommentsInfoItem item) { + NavigationHelper.openCommentRepliesFragment((FragmentActivity) itemBuilder.getContext(), + item); + } + + private void allowLinkFocus() { + itemContentView.setMovementMethod(LinkMovementMethod.getInstance()); + } + + private void denyLinkFocus() { + itemContentView.setMovementMethod(null); + } + + private boolean shouldFocusLinks() { + if (itemView.isInTouchMode()) { + return false; + } + + final URLSpan[] urls = itemContentView.getUrls(); + + return urls != null && urls.length != 0; + } + + private void determineMovementMethod() { + if (shouldFocusLinks()) { + allowLinkFocus(); + } else { + denyLinkFocus(); + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsInfoItemHolder.java deleted file mode 100644 index 4fc2d9f84..000000000 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsInfoItemHolder.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.schabi.newpipe.info_list.holder; - -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.comments.CommentsInfoItem; -import org.schabi.newpipe.info_list.InfoItemBuilder; -import org.schabi.newpipe.local.history.HistoryRecordManager; - -/* - * Created by Christian Schabesberger on 12.02.17. - * - * Copyright (C) Christian Schabesberger 2016 - * ChannelInfoItemHolder .java is part of NewPipe. - * - * NewPipe is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * NewPipe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with NewPipe. If not, see . - */ - -public class CommentsInfoItemHolder extends CommentsMiniInfoItemHolder { - public final TextView itemTitleView; - private final ImageView itemHeartView; - private final ImageView itemPinnedView; - - 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); - itemPinnedView = itemView.findViewById(R.id.detail_pinned_view); - } - - @Override - public void updateFromItem(final InfoItem infoItem, - final HistoryRecordManager historyRecordManager) { - super.updateFromItem(infoItem, historyRecordManager); - - if (!(infoItem instanceof CommentsInfoItem)) { - return; - } - final CommentsInfoItem item = (CommentsInfoItem) infoItem; - - itemTitleView.setText(item.getUploaderName()); - - itemHeartView.setVisibility(item.isHeartedByUploader() ? View.VISIBLE : View.GONE); - - itemPinnedView.setVisibility(item.isPinned() ? View.VISIBLE : View.GONE); - } -} diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java deleted file mode 100644 index d6a08e6cb..000000000 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java +++ /dev/null @@ -1,280 +0,0 @@ -package org.schabi.newpipe.info_list.holder; - -import static android.text.TextUtils.isEmpty; - -import android.graphics.Paint; -import android.text.Layout; -import android.text.method.LinkMovementMethod; -import android.text.style.URLSpan; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.text.HtmlCompat; - -import org.schabi.newpipe.R; -import org.schabi.newpipe.error.ErrorUtil; -import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.ServiceList; -import org.schabi.newpipe.extractor.StreamingService; -import org.schabi.newpipe.extractor.comments.CommentsInfoItem; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.stream.Description; -import org.schabi.newpipe.info_list.InfoItemBuilder; -import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.image.ImageStrategy; -import org.schabi.newpipe.util.image.PicassoHelper; -import org.schabi.newpipe.util.external_communication.ShareUtils; -import org.schabi.newpipe.util.text.CommentTextOnTouchListener; -import org.schabi.newpipe.util.text.TextLinkifier; - -import java.util.function.Consumer; - -import io.reactivex.rxjava3.disposables.CompositeDisposable; - -public class CommentsMiniInfoItemHolder extends InfoItemHolder { - private static final String TAG = "CommentsMiniIIHolder"; - private static final String ELLIPSIS = "…"; - - private static final int COMMENT_DEFAULT_LINES = 2; - private static final int COMMENT_EXPANDED_LINES = 1000; - - private final int commentHorizontalPadding; - private final int commentVerticalPadding; - - private final Paint paintAtContentSize; - private final float ellipsisWidthPx; - - private final RelativeLayout itemRoot; - private final ImageView itemThumbnailView; - private final TextView itemContentView; - private final TextView itemLikesCountView; - private final TextView itemPublishedTime; - - private final CompositeDisposable disposables = new CompositeDisposable(); - @Nullable private Description commentText; - @Nullable private StreamingService streamService; - @Nullable private String streamUrl; - - CommentsMiniInfoItemHolder(final InfoItemBuilder infoItemBuilder, final int layoutId, - final ViewGroup parent) { - super(infoItemBuilder, layoutId, parent); - - itemRoot = itemView.findViewById(R.id.itemRoot); - itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView); - itemLikesCountView = itemView.findViewById(R.id.detail_thumbs_up_count_view); - itemPublishedTime = itemView.findViewById(R.id.itemPublishedTime); - itemContentView = itemView.findViewById(R.id.itemCommentContentView); - - commentHorizontalPadding = (int) infoItemBuilder.getContext() - .getResources().getDimension(R.dimen.comments_horizontal_padding); - commentVerticalPadding = (int) infoItemBuilder.getContext() - .getResources().getDimension(R.dimen.comments_vertical_padding); - - paintAtContentSize = new Paint(); - paintAtContentSize.setTextSize(itemContentView.getTextSize()); - ellipsisWidthPx = paintAtContentSize.measureText(ELLIPSIS); - } - - public CommentsMiniInfoItemHolder(final InfoItemBuilder infoItemBuilder, - final ViewGroup parent) { - this(infoItemBuilder, R.layout.list_comments_mini_item, parent); - } - - @Override - public void updateFromItem(final InfoItem infoItem, - final HistoryRecordManager historyRecordManager) { - if (!(infoItem instanceof CommentsInfoItem)) { - return; - } - final CommentsInfoItem item = (CommentsInfoItem) infoItem; - - PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView); - if (ImageStrategy.shouldLoadImages()) { - itemThumbnailView.setVisibility(View.VISIBLE); - itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding, - commentVerticalPadding, commentVerticalPadding); - } else { - itemThumbnailView.setVisibility(View.GONE); - itemRoot.setPadding(commentHorizontalPadding, commentVerticalPadding, - commentHorizontalPadding, commentVerticalPadding); - } - - - itemThumbnailView.setOnClickListener(view -> openCommentAuthor(item)); - - try { - streamService = NewPipe.getService(item.getServiceId()); - } catch (final ExtractionException e) { - // should never happen - ErrorUtil.showUiErrorSnackbar(itemBuilder.getContext(), "Getting StreamingService", e); - Log.w(TAG, "Cannot obtain service from comment service id, defaulting to YouTube", e); - streamService = ServiceList.YouTube; - } - streamUrl = item.getUrl(); - commentText = item.getCommentText(); - ellipsize(); - - //noinspection ClickableViewAccessibility - itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); - - if (item.getLikeCount() >= 0) { - itemLikesCountView.setText( - Localization.shortCount( - itemBuilder.getContext(), - item.getLikeCount())); - } else { - itemLikesCountView.setText("-"); - } - - if (item.getUploadDate() != null) { - itemPublishedTime.setText(Localization.relativeTime(item.getUploadDate() - .offsetDateTime())); - } else { - itemPublishedTime.setText(item.getTextualUploadDate()); - } - - itemView.setOnClickListener(view -> { - toggleEllipsize(); - if (itemBuilder.getOnCommentsSelectedListener() != null) { - itemBuilder.getOnCommentsSelectedListener().selected(item); - } - }); - - - itemView.setOnLongClickListener(view -> { - if (DeviceUtils.isTv(itemBuilder.getContext())) { - openCommentAuthor(item); - } else { - final CharSequence text = itemContentView.getText(); - if (text != null) { - ShareUtils.copyToClipboard(itemBuilder.getContext(), text.toString()); - } - } - return true; - }); - } - - private void openCommentAuthor(final CommentsInfoItem item) { - if (isEmpty(item.getUploaderUrl())) { - return; - } - final AppCompatActivity activity = (AppCompatActivity) itemBuilder.getContext(); - try { - NavigationHelper.openChannelFragment( - activity.getSupportFragmentManager(), - item.getServiceId(), - item.getUploaderUrl(), - item.getUploaderName()); - } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(activity, "Opening channel fragment", e); - } - } - - private void allowLinkFocus() { - itemContentView.setMovementMethod(LinkMovementMethod.getInstance()); - } - - private void denyLinkFocus() { - itemContentView.setMovementMethod(null); - } - - private boolean shouldFocusLinks() { - if (itemView.isInTouchMode()) { - return false; - } - - final URLSpan[] urls = itemContentView.getUrls(); - - return urls != null && urls.length != 0; - } - - private void determineMovementMethod() { - if (shouldFocusLinks()) { - allowLinkFocus(); - } else { - denyLinkFocus(); - } - } - - private void ellipsize() { - itemContentView.setMaxLines(COMMENT_EXPANDED_LINES); - linkifyCommentContentView(v -> { - boolean hasEllipsis = false; - - final CharSequence charSeqText = itemContentView.getText(); - if (charSeqText != null && itemContentView.getLineCount() > COMMENT_DEFAULT_LINES) { - // Note that converting to String removes spans (i.e. links), but that's something - // we actually want since when the text is ellipsized we want all clicks on the - // comment to expand the comment, not to open links. - final String text = charSeqText.toString(); - - final Layout layout = itemContentView.getLayout(); - final float lineWidth = layout.getLineWidth(COMMENT_DEFAULT_LINES - 1); - final float layoutWidth = layout.getWidth(); - final int lineStart = layout.getLineStart(COMMENT_DEFAULT_LINES - 1); - final int lineEnd = layout.getLineEnd(COMMENT_DEFAULT_LINES - 1); - - // remove characters up until there is enough space for the ellipsis - // (also summing 2 more pixels, just to be sure to avoid float rounding errors) - int end = lineEnd; - float removedCharactersWidth = 0.0f; - while (lineWidth - removedCharactersWidth + ellipsisWidthPx + 2.0f > layoutWidth - && end >= lineStart) { - end -= 1; - // recalculate each time to account for ligatures or other similar things - removedCharactersWidth = paintAtContentSize.measureText( - text.substring(end, lineEnd)); - } - - // remove trailing spaces and newlines - while (end > 0 && Character.isWhitespace(text.charAt(end - 1))) { - end -= 1; - } - - final String newVal = text.substring(0, end) + ELLIPSIS; - itemContentView.setText(newVal); - hasEllipsis = true; - } - - itemContentView.setMaxLines(COMMENT_DEFAULT_LINES); - if (hasEllipsis) { - denyLinkFocus(); - } else { - determineMovementMethod(); - } - }); - } - - private void toggleEllipsize() { - final CharSequence text = itemContentView.getText(); - if (!isEmpty(text) && text.charAt(text.length() - 1) == ELLIPSIS.charAt(0)) { - expand(); - } else if (itemContentView.getLineCount() > COMMENT_DEFAULT_LINES) { - ellipsize(); - } - } - - private void expand() { - itemContentView.setMaxLines(COMMENT_EXPANDED_LINES); - linkifyCommentContentView(v -> determineMovementMethod()); - } - - private void linkifyCommentContentView(@Nullable final Consumer onCompletion) { - disposables.clear(); - if (commentText != null) { - TextLinkifier.fromDescription(itemContentView, commentText, - HtmlCompat.FROM_HTML_MODE_LEGACY, streamService, streamUrl, disposables, - onCompletion); - } - } -} diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java index a84c98404..80f62eed3 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java @@ -12,10 +12,6 @@ import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.Localization; -import androidx.preference.PreferenceManager; - -import static org.schabi.newpipe.MainActivity.DEBUG; - /* * Created by Christian Schabesberger on 01.08.16. *

@@ -81,7 +77,9 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder { } } - final String uploadDate = getFormattedRelativeUploadDate(infoItem); + final String uploadDate = Localization.relativeTimeOrTextual(itemBuilder.getContext(), + infoItem.getUploadDate(), + infoItem.getTextualUploadDate()); if (!TextUtils.isEmpty(uploadDate)) { if (viewsAndDate.isEmpty()) { return uploadDate; @@ -92,20 +90,4 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder { return viewsAndDate; } - - private String getFormattedRelativeUploadDate(final StreamInfoItem infoItem) { - if (infoItem.getUploadDate() != null) { - String formattedRelativeTime = Localization - .relativeTime(infoItem.getUploadDate().offsetDateTime()); - - if (DEBUG && PreferenceManager.getDefaultSharedPreferences(itemBuilder.getContext()) - .getBoolean(itemBuilder.getContext() - .getString(R.string.show_original_time_ago_key), false)) { - formattedRelativeTime += " (" + infoItem.getTextualUploadDate() + ")"; - } - return formattedRelativeTime; - } else { - return infoItem.getTextualUploadDate(); - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt b/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt new file mode 100644 index 000000000..61721d546 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt @@ -0,0 +1,9 @@ +package org.schabi.newpipe.ktx + +import android.os.Bundle +import android.os.Parcelable +import androidx.core.os.BundleCompat + +inline fun Bundle.parcelableArrayList(key: String?): ArrayList? { + return BundleCompat.getParcelableArrayList(this, key, T::class.java) +} diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java index b9409cb9d..b33619dea 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -14,6 +14,7 @@ import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.info_list.ItemViewMode; import org.schabi.newpipe.local.history.HistoryRecordManager; +import org.schabi.newpipe.local.holder.LocalBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.holder.LocalItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistCardItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder; @@ -24,6 +25,7 @@ import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder; import org.schabi.newpipe.local.holder.LocalStatisticStreamCardItemHolder; import org.schabi.newpipe.local.holder.LocalStatisticStreamGridItemHolder; import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder; +import org.schabi.newpipe.local.holder.RemoteBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistCardItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder; @@ -73,10 +75,12 @@ public class LocalItemListAdapter extends RecyclerView.Adapter localItems; @@ -87,6 +91,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter, Void> { +public final class BookmarkFragment extends BaseLocalListFragment, Void> + implements DebounceSavable { + + private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State - protected Parcelable itemsListState; + Parcelable itemsListState; private Subscription databaseSubscription; private CompositeDisposable disposables = new CompositeDisposable(); private LocalPlaylistManager localPlaylistManager; private RemotePlaylistManager remotePlaylistManager; + private ItemTouchHelper itemTouchHelper; + + /* Have the bookmarked playlists been fully loaded from db */ + private AtomicBoolean isLoadingComplete; + + /* Gives enough time to avoid interrupting user sorting operations */ + @Nullable + private DebounceSaver debounceSaver; + + private List> deletedItems; /////////////////////////////////////////////////////////////////////////// // Fragment LifeCycle - Creation @@ -65,6 +86,11 @@ public final class BookmarkFragment extends BaseLocalListFragment(); } @Nullable @@ -91,10 +117,20 @@ public final class BookmarkFragment extends BaseLocalListFragment() { @Override public void selected(final LocalItem selectedItem) { @@ -102,7 +138,7 @@ public final class BookmarkFragment extends BaseLocalListFragment> getPlaylistsSubscriber() { - return new Subscriber>() { + return new Subscriber<>() { @Override public void onSubscribe(final Subscription s) { showLoading(); + isLoadingComplete.set(false); + if (databaseSubscription != null) { databaseSubscription.cancel(); } @@ -196,7 +258,10 @@ public final class BookmarkFragment extends BaseLocalListFragment subscriptions) { - handleResult(subscriptions); + if (debounceSaver == null || !debounceSaver.getIsModified()) { + handleResult(subscriptions); + isLoadingComplete.set(true); + } if (databaseSubscription != null) { databaseSubscription.request(1); } @@ -209,7 +274,8 @@ public final class BookmarkFragment extends BaseLocalListFragment { /*Do nothing on success*/ }, throwable -> showError( + new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, + "Changing playlist name"))); + disposables.add(disposable); + } + + private void deleteItem(final PlaylistLocalItem item) { + if (itemListAdapter == null) { + return; + } + itemListAdapter.removeItem(item); + + if (item instanceof PlaylistMetadataEntry) { + deletedItems.add(new Pair<>(item.getUid(), + LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM)); + } else if (item instanceof PlaylistRemoteEntity) { + deletedItems.add(new Pair<>(item.getUid(), + LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM)); + } + + if (debounceSaver != null) { + debounceSaver.setHasChangesToSave(); + saveImmediate(); + } + } + + @Override + public void saveImmediate() { + if (itemListAdapter == null) { + return; + } + + // List must be loaded and modified in order to save + if (isLoadingComplete == null || debounceSaver == null + || !isLoadingComplete.get() || !debounceSaver.getIsModified()) { + return; + } + + final List items = itemListAdapter.getItemsList(); + final List localItemsUpdate = new ArrayList<>(); + final List localItemsDeleteUid = new ArrayList<>(); + final List remoteItemsUpdate = new ArrayList<>(); + final List remoteItemsDeleteUid = new ArrayList<>(); + + // Calculate display index + for (int i = 0; i < items.size(); i++) { + final LocalItem item = items.get(i); + + if (item instanceof PlaylistMetadataEntry + && ((PlaylistMetadataEntry) item).getDisplayIndex() != i) { + ((PlaylistMetadataEntry) item).setDisplayIndex(i); + localItemsUpdate.add((PlaylistMetadataEntry) item); + } else if (item instanceof PlaylistRemoteEntity + && ((PlaylistRemoteEntity) item).getDisplayIndex() != i) { + ((PlaylistRemoteEntity) item).setDisplayIndex(i); + remoteItemsUpdate.add((PlaylistRemoteEntity) item); + } + } + + // Find deleted items + for (final Pair item : deletedItems) { + if (item.second.equals(LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM)) { + localItemsDeleteUid.add(item.first); + } else if (item.second.equals(LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM)) { + remoteItemsDeleteUid.add(item.first); + } + } + + deletedItems.clear(); + + // 1. Update local playlists + // 2. Update remote playlists + // 3. Set NoChangesToSave + disposables.add(localPlaylistManager.updatePlaylists(localItemsUpdate, localItemsDeleteUid) + .mergeWith(remotePlaylistManager.updatePlaylists( + remoteItemsUpdate, remoteItemsDeleteUid)) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> { + if (debounceSaver != null) { + debounceSaver.setNoChangesToSave(); + } + }, + throwable -> showError(new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, "Saving playlist")) + )); + + } + + private ItemTouchHelper.SimpleCallback getItemTouchCallback() { + // if adding grid layout, also include ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT + // with an `if (shouldUseGridLayout()) ...` + return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, + ItemTouchHelper.ACTION_STATE_IDLE) { + @Override + public int interpolateOutOfBoundsScroll(@NonNull final RecyclerView recyclerView, + final int viewSize, + final int viewSizeOutOfBounds, + final int totalSize, + final long msSinceStartScroll) { + final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, + viewSize, viewSizeOutOfBounds, totalSize, msSinceStartScroll); + final int minimumAbsVelocity = Math.max(MINIMUM_INITIAL_DRAG_VELOCITY, + Math.abs(standardSpeed)); + return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds); + } + + @Override + public boolean onMove(@NonNull final RecyclerView recyclerView, + @NonNull final RecyclerView.ViewHolder source, + @NonNull final RecyclerView.ViewHolder target) { + + // Allow swap LocalBookmarkPlaylistItemHolder and RemoteBookmarkPlaylistItemHolder. + if (itemListAdapter == null + || source.getItemViewType() != target.getItemViewType() + && !( + ( + (source instanceof LocalBookmarkPlaylistItemHolder) + || (source instanceof RemoteBookmarkPlaylistItemHolder) + ) + && ( + (target instanceof LocalBookmarkPlaylistItemHolder) + || (target instanceof RemoteBookmarkPlaylistItemHolder) + )) + ) { + return false; + } + + final int sourceIndex = source.getBindingAdapterPosition(); + final int targetIndex = target.getBindingAdapterPosition(); + final boolean isSwapped = itemListAdapter.swapItems(sourceIndex, targetIndex); + if (isSwapped && debounceSaver != null) { + debounceSaver.setHasChangesToSave(); + } + return isSwapped; + } + + @Override + public boolean isLongPressDragEnabled() { + return false; + } + + @Override + public boolean isItemViewSwipeEnabled() { + return false; + } + + @Override + public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, + final int swipeDir) { + // Do nothing. + } + }; + } + /////////////////////////////////////////////////////////////////////////// // Utils /////////////////////////////////////////////////////////////////////////// private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) { - showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid())); + showDeleteDialog(item.getName(), item); } private void showLocalDialog(final PlaylistMetadataEntry selectedItem) { @@ -257,7 +494,7 @@ public final class BookmarkFragment extends BaseLocalListFragment items = new ArrayList<>(); items.add(rename); @@ -270,13 +507,12 @@ public final class BookmarkFragment extends BaseLocalListFragment changeLocalPlaylistName( - selectedItem.uid, + selectedItem.getUid(), dialogBinding.dialogEditText.getText().toString())) .setNegativeButton(R.string.cancel, null) .show(); } - private void showDeleteDialog(final String name, final Single deleteReactor) { + private void showDeleteDialog(final String name, final PlaylistLocalItem item) { if (activity == null || disposables == null) { return; } @@ -313,35 +549,8 @@ public final class BookmarkFragment extends BaseLocalListFragment - disposables.add(deleteReactor - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(ignored -> { /*Do nothing on success*/ }, throwable -> - showError(new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Deleting playlist"))))) + .setPositiveButton(R.string.delete, (dialog, i) -> deleteItem(item)) .setNegativeButton(R.string.cancel, null) .show(); } - - private void changeLocalPlaylistName(final long id, final String name) { - if (localPlaylistManager == null) { - return; - } - - if (DEBUG) { - Log.d(TAG, "Updating playlist id=[" + id + "] " - + "with new name=[" + name + "] items"); - } - - localPlaylistManager.renamePlaylist(id, name); - final Disposable disposable = localPlaylistManager.renamePlaylist(id, name) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(longs -> { /*Do nothing on success*/ }, throwable -> showError( - new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Changing playlist name"))); - disposables.add(disposable); - } } - diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/MergedPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/MergedPlaylistManager.java new file mode 100644 index 000000000..25eb2f652 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/MergedPlaylistManager.java @@ -0,0 +1,95 @@ +package org.schabi.newpipe.local.bookmark; + +import org.schabi.newpipe.database.playlist.PlaylistLocalItem; +import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; +import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; +import org.schabi.newpipe.local.playlist.LocalPlaylistManager; +import org.schabi.newpipe.local.playlist.RemotePlaylistManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import io.reactivex.rxjava3.core.Flowable; + +/** + * Takes care of remote and local playlists at once, hence "merged". + */ +public final class MergedPlaylistManager { + + private MergedPlaylistManager() { + } + + public static Flowable> getMergedOrderedPlaylists( + final LocalPlaylistManager localPlaylistManager, + final RemotePlaylistManager remotePlaylistManager) { + return Flowable.combineLatest( + localPlaylistManager.getPlaylists(), + remotePlaylistManager.getPlaylists(), + MergedPlaylistManager::merge + ); + } + + /** + * Merge localPlaylists and remotePlaylists by the display index. + * If two items have the same display index, sort them in {@code CASE_INSENSITIVE_ORDER}. + * + * @param localPlaylists local playlists, already sorted by display index + * @param remotePlaylists remote playlists, already sorted by display index + * @return merged playlists + */ + public static List merge( + final List localPlaylists, + final List remotePlaylists) { + + // This algorithm is similar to the merge operation in merge sort. + final List result = new ArrayList<>( + localPlaylists.size() + remotePlaylists.size()); + final List itemsWithSameIndex = new ArrayList<>(); + + int i = 0; + int j = 0; + while (i < localPlaylists.size()) { + while (j < remotePlaylists.size()) { + if (remotePlaylists.get(j).getDisplayIndex() + <= localPlaylists.get(i).getDisplayIndex()) { + addItem(result, remotePlaylists.get(j), itemsWithSameIndex); + j++; + } else { + break; + } + } + addItem(result, localPlaylists.get(i), itemsWithSameIndex); + i++; + } + while (j < remotePlaylists.size()) { + addItem(result, remotePlaylists.get(j), itemsWithSameIndex); + j++; + } + addItemsWithSameIndex(result, itemsWithSameIndex); + + return result; + } + + private static void addItem(final List result, + final PlaylistLocalItem item, + final List itemsWithSameIndex) { + if (!itemsWithSameIndex.isEmpty() + && itemsWithSameIndex.get(0).getDisplayIndex() != item.getDisplayIndex()) { + // The new item has a different display index, add previous items with same + // index to the result. + addItemsWithSameIndex(result, itemsWithSameIndex); + itemsWithSameIndex.clear(); + } + itemsWithSameIndex.add(item); + } + + private static void addItemsWithSameIndex(final List result, + final List itemsWithSameIndex) { + Collections.sort(itemsWithSameIndex, + Comparator.comparing(PlaylistLocalItem::getOrderingName, + Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))); + result.addAll(itemsWithSameIndex); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java index b503c4e05..e7f73079f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java +++ b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java @@ -155,14 +155,14 @@ public final class PlaylistAppendDialog extends PlaylistDialog { final Toast successToast = Toast.makeText(getContext(), toastText, Toast.LENGTH_SHORT); - playlistDisposables.add(manager.appendToPlaylist(playlist.uid, streams) + playlistDisposables.add(manager.appendToPlaylist(playlist.getUid(), streams) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignored -> { successToast.show(); if (playlist.thumbnailUrl.equals(PlaylistEntity.DEFAULT_THUMBNAIL)) { playlistDisposables.add(manager - .changePlaylistThumbnail(playlist.uid, streams.get(0).getUid(), + .changePlaylistThumbnail(playlist.getUid(), streams.get(0).getUid(), false) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignore -> successToast.show())); diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt index de640dbbb..a40bf35dc 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt @@ -137,7 +137,7 @@ class NotificationWorker( .enqueueUniquePeriodicWork( WORK_TAG, if (force) { - ExistingPeriodicWorkPolicy.REPLACE + ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE } else { ExistingPeriodicWorkPolicy.KEEP }, diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedEventManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedEventManager.kt index 3d19de9c6..1c2826e7a 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedEventManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedEventManager.kt @@ -26,7 +26,7 @@ object FeedEventManager { } sealed class Event { - object IdleEvent : Event() + data object IdleEvent : Event() data class ProgressEvent(val currentProgress: Int = -1, val maxProgress: Int = -1, @StringRes val progressMessage: Int = 0) : Event() { constructor(@StringRes progressMessage: Int) : this(-1, -1, progressMessage) } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt index 84cd8ed59..b44eec353 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt @@ -18,7 +18,7 @@ data class FeedUpdateInfo( @NotificationMode val notificationMode: Int, val name: String, - val avatarUrl: String, + val avatarUrl: String?, val url: String, val serviceId: Int, // description and subscriberCount are null if the constructor info is from the fast feed method diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java new file mode 100644 index 000000000..16130009b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java @@ -0,0 +1,54 @@ +package org.schabi.newpipe.local.holder; + +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.database.LocalItem; +import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; +import org.schabi.newpipe.local.LocalItemBuilder; +import org.schabi.newpipe.local.history.HistoryRecordManager; + +import java.time.format.DateTimeFormatter; + +public class LocalBookmarkPlaylistItemHolder extends LocalPlaylistItemHolder { + private final View itemHandleView; + + public LocalBookmarkPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, + final ViewGroup parent) { + this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); + } + + LocalBookmarkPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, + final ViewGroup parent) { + super(infoItemBuilder, layoutId, parent); + itemHandleView = itemView.findViewById(R.id.itemHandle); + } + + @Override + public void updateFromItem(final LocalItem localItem, + final HistoryRecordManager historyRecordManager, + final DateTimeFormatter dateTimeFormatter) { + if (!(localItem instanceof PlaylistMetadataEntry)) { + return; + } + final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem; + + itemHandleView.setOnTouchListener(getOnTouchListener(item)); + + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); + } + + private View.OnTouchListener getOnTouchListener(final PlaylistMetadataEntry item) { + return (view, motionEvent) -> { + view.performClick(); + if (itemBuilder != null && itemBuilder.getOnItemSelectedListener() != null + && motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemBuilder.getOnItemSelectedListener().drag(item, + LocalBookmarkPlaylistItemHolder.this); + } + return false; + }; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java new file mode 100644 index 000000000..6d61d1e08 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java @@ -0,0 +1,54 @@ +package org.schabi.newpipe.local.holder; + +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.database.LocalItem; +import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; +import org.schabi.newpipe.local.LocalItemBuilder; +import org.schabi.newpipe.local.history.HistoryRecordManager; + +import java.time.format.DateTimeFormatter; + +public class RemoteBookmarkPlaylistItemHolder extends RemotePlaylistItemHolder { + private final View itemHandleView; + + public RemoteBookmarkPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, + final ViewGroup parent) { + this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); + } + + RemoteBookmarkPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, + final ViewGroup parent) { + super(infoItemBuilder, layoutId, parent); + itemHandleView = itemView.findViewById(R.id.itemHandle); + } + + @Override + public void updateFromItem(final LocalItem localItem, + final HistoryRecordManager historyRecordManager, + final DateTimeFormatter dateTimeFormatter) { + if (!(localItem instanceof PlaylistRemoteEntity)) { + return; + } + final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem; + + itemHandleView.setOnTouchListener(getOnTouchListener(item)); + + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); + } + + private View.OnTouchListener getOnTouchListener(final PlaylistRemoteEntity item) { + return (view, motionEvent) -> { + view.performClick(); + if (itemBuilder != null && itemBuilder.getOnItemSelectedListener() != null + && motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemBuilder.getOnItemSelectedListener().drag(item, + RemoteBookmarkPlaylistItemHolder.this); + } + return false; + }; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index d14c1a231..765732063 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -14,6 +14,7 @@ import org.schabi.newpipe.util.ServiceHelper; import java.time.format.DateTimeFormatter; public class RemotePlaylistItemHolder extends PlaylistItemHolder { + public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { super(infoItemBuilder, parent); diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 51da52ae0..d5ae431fa 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -49,6 +49,8 @@ import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; +import org.schabi.newpipe.util.debounce.DebounceSavable; +import org.schabi.newpipe.util.debounce.DebounceSaver; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; @@ -58,7 +60,6 @@ import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -68,12 +69,10 @@ import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; public class LocalPlaylistFragment extends BaseLocalListFragment, Void> - implements PlaylistControlViewHolder { - /** Save the list 10 seconds after the last change occurred. */ - private static final long SAVE_DEBOUNCE_MILLIS = 10000; + implements PlaylistControlViewHolder, DebounceSavable { + private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State protected Long playlistId; @@ -90,13 +89,12 @@ public class LocalPlaylistFragment extends BaseLocalListFragment debouncedSaveSignal; private CompositeDisposable disposables; /** Whether the playlist has been fully loaded from db. */ private AtomicBoolean isLoadingComplete; - /** Whether the playlist has been modified (e.g. items reordered or deleted) */ - private AtomicBoolean isModified; + /** Used to debounce saving playlist edits to disk. */ + private DebounceSaver debounceSaver; /** Flag to prevent simultaneous rewrites of the playlist. */ private boolean isRewritingPlaylist = false; @@ -121,12 +119,11 @@ public class LocalPlaylistFragment extends BaseLocalListFragmentCommit changes immediately if the playlist has been modified.

- * Delete operations and other modifications will be committed to ensure that the database - * is up to date, e.g. when the user adds the just deleted stream from another fragment. - */ - public void commitChanges() { - if (isModified != null && isModified.get()) { - saveImmediate(); - } - } - @Override protected void initListeners() { super.initListeners(); @@ -243,10 +229,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment streams) { // Skip handling the result after it has been modified - if (isModified == null || !isModified.get()) { + if (debounceSaver == null || !debounceSaver.getIsModified()) { handleResult(streams); isLoadingComplete.set(true); } @@ -495,14 +483,14 @@ public class LocalPlaylistFragment extends BaseLocalListFragment { itemListAdapter.clearStreamItemList(); itemListAdapter.addItems(itemsToKeep); - setVideoCount(itemListAdapter.getItemsList().size()); - saveChanges(); + setStreamCountAndOverallDuration(itemListAdapter.getItemsList()); + debounceSaver.setHasChangesToSave(); hideLoading(); isRewritingPlaylist = false; @@ -684,42 +672,24 @@ public class LocalPlaylistFragment extends BaseLocalListFragment saveImmediate(), throwable -> - showError(new ErrorInfo(throwable, UserAction.SOMETHING_ELSE, - "Debounced saver"))); - } - - private void saveImmediate() { + /** + *

Commit changes immediately if the playlist has been modified.

+ * Delete operations and other modifications will be committed to ensure that the database + * is up to date, e.g. when the user adds the just deleted stream from another fragment. + */ + @Override + public void saveImmediate() { if (playlistManager == null || itemListAdapter == null) { return; } // List must be loaded and modified in order to save - if (isLoadingComplete == null || isModified == null - || !isLoadingComplete.get() || !isModified.get()) { - Log.w(TAG, "Attempting to save playlist when local playlist " - + "is not loaded or not modified: playlist id=[" + playlistId + "]"); + if (isLoadingComplete == null || debounceSaver == null + || !isLoadingComplete.get() || !debounceSaver.getIsModified()) { return; } @@ -740,8 +710,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment { - if (isModified != null) { - isModified.set(false); + if (debounceSaver != null) { + debounceSaver.setNoChangesToSave(); } }, throwable -> showError(new ErrorInfo(throwable, @@ -784,7 +754,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment itemsList) { if (activity != null && headerBinding != null) { - headerBinding.playlistStreamCount.setText(Localization - .localizeStreamCount(activity, count)); + final long streamCount = itemsList.size(); + final long playlistOverallDurationSeconds = itemsList.stream() + .filter(PlaylistStreamEntry.class::isInstance) + .map(PlaylistStreamEntry.class::cast) + .map(PlaylistStreamEntry::getStreamEntity) + .mapToLong(StreamEntity::getDuration) + .sum(); + headerBinding.playlistStreamCount.setText( + Localization.concatenateStrings( + Localization.localizeStreamCount(activity, streamCount), + Localization.getDurationString(playlistOverallDurationSeconds, + true, true)) + ); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java index 27c148561..dd9307675 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java @@ -19,7 +19,6 @@ import java.util.List; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Maybe; -import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; public class LocalPlaylistManager { @@ -43,10 +42,13 @@ public class LocalPlaylistManager { return Maybe.empty(); } + // Save to the database directly. + // Make sure the new playlist is always on the top of bookmark. + // The index will be reassigned to non-negative number in BookmarkFragment. return Maybe.fromCallable(() -> database.runInTransaction(() -> { final List streamIds = streamTable.upsertAll(streams); final PlaylistEntity newPlaylist = new PlaylistEntity(name, false, - streamIds.get(0)); + streamIds.get(0), -1); return insertJoinEntities(playlistTable.insert(newPlaylist), streamIds, 0); @@ -89,8 +91,20 @@ public class LocalPlaylistManager { })).subscribeOn(Schedulers.io()); } - public Flowable> getPlaylists() { - return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io()); + public Completable updatePlaylists(final List updateItems, + final List deletedItems) { + final List items = new ArrayList<>(updateItems.size()); + for (final PlaylistMetadataEntry item : updateItems) { + items.add(new PlaylistEntity(item)); + } + return Completable.fromRunnable(() -> database.runInTransaction(() -> { + for (final Long uid : deletedItems) { + playlistTable.deletePlaylist(uid); + } + for (final PlaylistEntity item : items) { + playlistTable.upsertPlaylist(item); + } + })).subscribeOn(Schedulers.io()); } public Flowable> getDistinctPlaylistStreams(final long playlistId) { @@ -110,13 +124,12 @@ public class LocalPlaylistManager { .subscribeOn(Schedulers.io()); } - public Flowable> getPlaylistStreams(final long playlistId) { - return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io()); + public Flowable> getPlaylists() { + return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io()); } - public Single deletePlaylist(final long playlistId) { - return Single.fromCallable(() -> playlistTable.deletePlaylist(playlistId)) - .subscribeOn(Schedulers.io()); + public Flowable> getPlaylistStreams(final long playlistId) { + return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io()); } public Maybe renamePlaylist(final long playlistId, final String name) { diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java index 5221139e3..4cc51f752 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java @@ -7,20 +7,23 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import java.util.List; +import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; public class RemotePlaylistManager { + private final AppDatabase database; private final PlaylistRemoteDAO playlistRemoteTable; public RemotePlaylistManager(final AppDatabase db) { + database = db; playlistRemoteTable = db.playlistRemoteDAO(); } public Flowable> getPlaylists() { - return playlistRemoteTable.getAll().subscribeOn(Schedulers.io()); + return playlistRemoteTable.getPlaylists().subscribeOn(Schedulers.io()); } public Flowable> getPlaylist(final PlaylistInfo info) { @@ -33,6 +36,18 @@ public class RemotePlaylistManager { .subscribeOn(Schedulers.io()); } + public Completable updatePlaylists(final List updateItems, + final List deletedItems) { + return Completable.fromRunnable(() -> database.runInTransaction(() -> { + for (final Long uid: deletedItems) { + playlistRemoteTable.deletePlaylist(uid); + } + for (final PlaylistRemoteEntity item: updateItems) { + playlistRemoteTable.upsert(item); + } + })).subscribeOn(Schedulers.io()); + } + public Single onBookmark(final PlaylistInfo playlistInfo) { return Single.fromCallable(() -> { final PlaylistRemoteEntity playlist = new PlaylistRemoteEntity(playlistInfo); diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt index 488d8b3d2..474add4f4 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt @@ -100,7 +100,9 @@ class SubscriptionManager(context: Context) { val subscriptionEntity = subscriptionTable.getSubscription(info.uid) subscriptionEntity.name = info.name - subscriptionEntity.avatarUrl = info.avatarUrl + + // some services do not provide an avatar URL + info.avatarUrl?.let { subscriptionEntity.avatarUrl = it } // these two fields are null if the feed info was fetched using the fast feed method info.description?.let { subscriptionEntity.description = it } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt index 19c581c08..41761fb01 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt @@ -55,10 +55,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable { private var groupSortOrder: Long = -1 sealed class ScreenState : Serializable { - object InitialScreen : ScreenState() - object IconPickerScreen : ScreenState() - object SubscriptionsPickerScreen : ScreenState() - object DeleteScreen : ScreenState() + data object InitialScreen : ScreenState() + data object IconPickerScreen : ScreenState() + data object SubscriptionsPickerScreen : ScreenState() + data object DeleteScreen : ScreenState() } @State @JvmField var selectedIcon: FeedGroupIcon? = null @@ -370,7 +370,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable { private fun setupIconPicker() { val groupAdapter = GroupieAdapter() - groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(it) }) + groupAdapter.addAll(FeedGroupIcon.entries.map { PickerIconItem(it) }) feedGroupCreateBinding.iconSelector.apply { layoutManager = GridLayoutManager(requireContext(), 7, RecyclerView.VERTICAL, false) diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt index eff1a4400..292bda394 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt @@ -110,8 +110,8 @@ class FeedGroupDialogViewModel( } sealed class DialogEvent { - object ProcessingEvent : DialogEvent() - object SuccessEvent : DialogEvent() + data object ProcessingEvent : DialogEvent() + data object SuccessEvent : DialogEvent() } data class Filter(val query: String, val showOnlyUngrouped: Boolean) diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java index d56d16f3c..54809068a 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.net.Uri; import android.util.Log; +import androidx.core.content.IntentCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.reactivestreams.Subscriber; @@ -65,7 +66,7 @@ public class SubscriptionsExportService extends BaseImportExportService { return START_NOT_STICKY; } - final Uri path = intent.getParcelableExtra(KEY_FILE_PATH); + final Uri path = IntentCompat.getParcelableExtra(intent, KEY_FILE_PATH, Uri.class); if (path == null) { stopAndReportError(new IllegalStateException( "Exporting to a file, but the path is null"), diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java index d624e1038..442c7fddb 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java @@ -30,6 +30,7 @@ import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.IntentCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.reactivestreams.Subscriber; @@ -108,7 +109,7 @@ public class SubscriptionsImportService extends BaseImportExportService { if (currentMode == CHANNEL_URL_MODE) { channelUrl = intent.getStringExtra(KEY_VALUE); } else { - final Uri uri = intent.getParcelableExtra(KEY_VALUE); + final Uri uri = IntentCompat.getParcelableExtra(intent, KEY_VALUE, Uri.class); if (uri == null) { stopAndReportError(new IllegalStateException( "Importing from input stream, but file path is null"), diff --git a/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt index 8acd70413..ff0bb269d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt +++ b/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt @@ -160,13 +160,12 @@ class MainPlayerGestureListener( } override fun onScroll( - initialEvent: MotionEvent, + initialEvent: MotionEvent?, movingEvent: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { - - if (!playerUi.isFullscreen) { + if (initialEvent == null || !playerUi.isFullscreen) { return false } diff --git a/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt index 23edcaeb8..0b94bf364 100644 --- a/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt +++ b/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt @@ -167,7 +167,7 @@ class PopupPlayerGestureListener( } override fun onFling( - e1: MotionEvent, + e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float @@ -218,11 +218,14 @@ class PopupPlayerGestureListener( } override fun onScroll( - initialEvent: MotionEvent, + initialEvent: MotionEvent?, movingEvent: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { + if (initialEvent == null) { + return false + } if (isResizing) { return super.onScroll(initialEvent, movingEvent, distanceX, distanceY) diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java index 6f76a91d1..737ebc5dd 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java @@ -1,10 +1,12 @@ package org.schabi.newpipe.player.mediasession; import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; +import android.os.Build; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; import android.util.Log; @@ -14,15 +16,23 @@ import androidx.annotation.Nullable; import androidx.media.session.MediaButtonReceiver; import com.google.android.exoplayer2.ForwardingPlayer; +import com.google.android.exoplayer2.Player.RepeatMode; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.Player; +import org.schabi.newpipe.player.notification.NotificationActionData; +import org.schabi.newpipe.player.notification.NotificationConstants; import org.schabi.newpipe.player.ui.PlayerUi; import org.schabi.newpipe.player.ui.VideoPlayerUi; import org.schabi.newpipe.util.StreamTypeUtil; +import java.util.List; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public class MediaSessionPlayerUi extends PlayerUi implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -34,6 +44,10 @@ public class MediaSessionPlayerUi extends PlayerUi private final String ignoreHardwareMediaButtonsKey; private boolean shouldIgnoreHardwareMediaButtons = false; + // used to check whether any notification action changed, before sending costly updates + private List prevNotificationActions = List.of(); + + public MediaSessionPlayerUi(@NonNull final Player player) { super(player); ignoreHardwareMediaButtonsKey = @@ -63,6 +77,10 @@ public class MediaSessionPlayerUi extends PlayerUi sessionConnector.setMetadataDeduplicationEnabled(true); sessionConnector.setMediaMetadataProvider(exoPlayer -> buildMediaMetadata()); + + // force updating media session actions by resetting the previous ones + prevNotificationActions = List.of(); + updateMediaSessionActions(); } @Override @@ -80,6 +98,7 @@ public class MediaSessionPlayerUi extends PlayerUi mediaSession.release(); mediaSession = null; } + prevNotificationActions = List.of(); } @Override @@ -163,4 +182,109 @@ public class MediaSessionPlayerUi extends PlayerUi return builder.build(); } + + + private void updateMediaSessionActions() { + // On Android 13+ (or Android T or API 33+) the actions in the player notification can't be + // controlled directly anymore, but are instead derived from custom media session actions. + // However the system allows customizing only two of these actions, since the other three + // are fixed to play-pause-buffering, previous, next. + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + // Although setting media session actions on older android versions doesn't seem to + // cause any trouble, it also doesn't seem to do anything, so we don't do anything to + // save battery. Check out NotificationUtil.updateActions() to see what happens on + // older android versions. + return; + } + + // only use the fourth and fifth actions (the settings page also shows only the last 2 on + // Android 13+) + final List newNotificationActions = IntStream.of(3, 4) + .map(i -> player.getPrefs().getInt( + player.getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), + NotificationConstants.SLOT_DEFAULTS[i])) + .mapToObj(action -> NotificationActionData + .fromNotificationActionEnum(player, action)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + // avoid costly notification actions update, if nothing changed from last time + if (!newNotificationActions.equals(prevNotificationActions)) { + prevNotificationActions = newNotificationActions; + sessionConnector.setCustomActionProviders( + newNotificationActions.stream() + .map(data -> new SessionConnectorActionProvider(data, context)) + .toArray(SessionConnectorActionProvider[]::new)); + } + } + + @Override + public void onBlocked() { + super.onBlocked(); + updateMediaSessionActions(); + } + + @Override + public void onPlaying() { + super.onPlaying(); + updateMediaSessionActions(); + } + + @Override + public void onBuffering() { + super.onBuffering(); + updateMediaSessionActions(); + } + + @Override + public void onPaused() { + super.onPaused(); + updateMediaSessionActions(); + } + + @Override + public void onPausedSeek() { + super.onPausedSeek(); + updateMediaSessionActions(); + } + + @Override + public void onCompleted() { + super.onCompleted(); + updateMediaSessionActions(); + } + + @Override + public void onRepeatModeChanged(@RepeatMode final int repeatMode) { + super.onRepeatModeChanged(repeatMode); + updateMediaSessionActions(); + } + + @Override + public void onShuffleModeEnabledChanged(final boolean shuffleModeEnabled) { + super.onShuffleModeEnabledChanged(shuffleModeEnabled); + updateMediaSessionActions(); + } + + @Override + public void onBroadcastReceived(final Intent intent) { + super.onBroadcastReceived(intent); + if (ACTION_RECREATE_NOTIFICATION.equals(intent.getAction())) { + // the notification actions changed + updateMediaSessionActions(); + } + } + + @Override + public void onMetadataChanged(@NonNull final StreamInfo info) { + super.onMetadataChanged(info); + updateMediaSessionActions(); + } + + @Override + public void onPlayQueueEdited() { + super.onPlayQueueEdited(); + updateMediaSessionActions(); + } } diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/SessionConnectorActionProvider.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/SessionConnectorActionProvider.java new file mode 100644 index 000000000..a5c9fccc9 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/SessionConnectorActionProvider.java @@ -0,0 +1,47 @@ +package org.schabi.newpipe.player.mediasession; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.media.session.PlaybackStateCompat; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; + +import org.schabi.newpipe.player.notification.NotificationActionData; + +import java.lang.ref.WeakReference; + +public class SessionConnectorActionProvider implements MediaSessionConnector.CustomActionProvider { + + private final NotificationActionData data; + @NonNull + private final WeakReference context; + + public SessionConnectorActionProvider(final NotificationActionData notificationActionData, + @NonNull final Context context) { + this.data = notificationActionData; + this.context = new WeakReference<>(context); + } + + @Override + public void onCustomAction(@NonNull final Player player, + @NonNull final String action, + @Nullable final Bundle extras) { + final Context actualContext = context.get(); + if (actualContext != null) { + actualContext.sendBroadcast(new Intent(action)); + } + } + + @Nullable + @Override + public PlaybackStateCompat.CustomAction getCustomAction(@NonNull final Player player) { + return new PlaybackStateCompat.CustomAction.Builder( + data.action(), data.name(), data.icon() + ).build(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java new file mode 100644 index 000000000..b3abcd0b5 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java @@ -0,0 +1,187 @@ +package org.schabi.newpipe.player.notification; + +import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; +import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_FORWARD; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_REWIND; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_NEXT; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PAUSE; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PREVIOUS; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_REPEAT; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_SHUFFLE; + +import android.annotation.SuppressLint; +import android.content.Context; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.player.Player; + +import java.util.Objects; + +public final class NotificationActionData { + + @NonNull + private final String action; + @NonNull + private final String name; + @DrawableRes + private final int icon; + + + public NotificationActionData(@NonNull final String action, @NonNull final String name, + @DrawableRes final int icon) { + this.action = action; + this.name = name; + this.icon = icon; + } + + @NonNull + public String action() { + return action; + } + + @NonNull + public String name() { + return name; + } + + @DrawableRes + public int icon() { + return icon; + } + + + @SuppressLint("PrivateResource") // we currently use Exoplayer's internal strings and icons + @Nullable + public static NotificationActionData fromNotificationActionEnum( + @NonNull final Player player, + @NotificationConstants.Action final int selectedAction + ) { + + final int baseActionIcon = NotificationConstants.ACTION_ICONS[selectedAction]; + final Context ctx = player.getContext(); + + switch (selectedAction) { + case NotificationConstants.PREVIOUS: + return new NotificationActionData(ACTION_PLAY_PREVIOUS, + ctx.getString(R.string.exo_controls_previous_description), baseActionIcon); + + case NotificationConstants.NEXT: + return new NotificationActionData(ACTION_PLAY_NEXT, + ctx.getString(R.string.exo_controls_next_description), baseActionIcon); + + case NotificationConstants.REWIND: + return new NotificationActionData(ACTION_FAST_REWIND, + ctx.getString(R.string.exo_controls_rewind_description), baseActionIcon); + + case NotificationConstants.FORWARD: + return new NotificationActionData(ACTION_FAST_FORWARD, + ctx.getString(R.string.exo_controls_fastforward_description), + baseActionIcon); + + case NotificationConstants.SMART_REWIND_PREVIOUS: + if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { + return new NotificationActionData(ACTION_PLAY_PREVIOUS, + ctx.getString(R.string.exo_controls_previous_description), + R.drawable.exo_notification_previous); + } else { + return new NotificationActionData(ACTION_FAST_REWIND, + ctx.getString(R.string.exo_controls_rewind_description), + R.drawable.exo_controls_rewind); + } + + case NotificationConstants.SMART_FORWARD_NEXT: + if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { + return new NotificationActionData(ACTION_PLAY_NEXT, + ctx.getString(R.string.exo_controls_next_description), + R.drawable.exo_notification_next); + } else { + return new NotificationActionData(ACTION_FAST_FORWARD, + ctx.getString(R.string.exo_controls_fastforward_description), + R.drawable.exo_controls_fastforward); + } + + case NotificationConstants.PLAY_PAUSE_BUFFERING: + if (player.getCurrentState() == Player.STATE_PREFLIGHT + || player.getCurrentState() == Player.STATE_BLOCKED + || player.getCurrentState() == Player.STATE_BUFFERING) { + return new NotificationActionData(ACTION_PLAY_PAUSE, + ctx.getString(R.string.notification_action_buffering), + R.drawable.ic_hourglass_top); + } + + // fallthrough + case NotificationConstants.PLAY_PAUSE: + if (player.getCurrentState() == Player.STATE_COMPLETED) { + return new NotificationActionData(ACTION_PLAY_PAUSE, + ctx.getString(R.string.exo_controls_pause_description), + R.drawable.ic_replay); + } else if (player.isPlaying() + || player.getCurrentState() == Player.STATE_PREFLIGHT + || player.getCurrentState() == Player.STATE_BLOCKED + || player.getCurrentState() == Player.STATE_BUFFERING) { + return new NotificationActionData(ACTION_PLAY_PAUSE, + ctx.getString(R.string.exo_controls_pause_description), + R.drawable.exo_notification_pause); + } else { + return new NotificationActionData(ACTION_PLAY_PAUSE, + ctx.getString(R.string.exo_controls_play_description), + R.drawable.exo_notification_play); + } + + case NotificationConstants.REPEAT: + if (player.getRepeatMode() == REPEAT_MODE_ALL) { + return new NotificationActionData(ACTION_REPEAT, + ctx.getString(R.string.exo_controls_repeat_all_description), + R.drawable.exo_media_action_repeat_all); + } else if (player.getRepeatMode() == REPEAT_MODE_ONE) { + return new NotificationActionData(ACTION_REPEAT, + ctx.getString(R.string.exo_controls_repeat_one_description), + R.drawable.exo_media_action_repeat_one); + } else /* player.getRepeatMode() == REPEAT_MODE_OFF */ { + return new NotificationActionData(ACTION_REPEAT, + ctx.getString(R.string.exo_controls_repeat_off_description), + R.drawable.exo_media_action_repeat_off); + } + + case NotificationConstants.SHUFFLE: + if (player.getPlayQueue() != null && player.getPlayQueue().isShuffled()) { + return new NotificationActionData(ACTION_SHUFFLE, + ctx.getString(R.string.exo_controls_shuffle_on_description), + R.drawable.exo_controls_shuffle_on); + } else { + return new NotificationActionData(ACTION_SHUFFLE, + ctx.getString(R.string.exo_controls_shuffle_off_description), + R.drawable.exo_controls_shuffle_off); + } + + case NotificationConstants.CLOSE: + return new NotificationActionData(ACTION_CLOSE, ctx.getString(R.string.close), + R.drawable.ic_close); + + case NotificationConstants.NOTHING: + default: + // do nothing + return null; + } + } + + + @Override + public boolean equals(@Nullable final Object obj) { + return (obj instanceof NotificationActionData other) + && this.action.equals(other.action) + && this.name.equals(other.name) + && this.icon == other.icon; + } + + @Override + public int hashCode() { + return Objects.hash(action, name, icon); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java index 89bf0b22a..b9607f7ea 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java @@ -13,7 +13,7 @@ import org.schabi.newpipe.util.Localization; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; @@ -65,10 +65,16 @@ public final class NotificationConstants { public static final int CLOSE = 11; @Retention(RetentionPolicy.SOURCE) - @IntDef({NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT, - PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, SHUFFLE, CLOSE}) + @IntDef({NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, + SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, + SHUFFLE, CLOSE}) public @interface Action { } + @Action + public static final int[] ALL_ACTIONS = {NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, + SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, + SHUFFLE, CLOSE}; + @DrawableRes public static final int[] ACTION_ICONS = { 0, @@ -95,16 +101,6 @@ public final class NotificationConstants { CLOSE, }; - @Action - public static final int[][] SLOT_ALLOWED_ACTIONS = { - new int[] {PREVIOUS, REWIND, SMART_REWIND_PREVIOUS}, - new int[] {REWIND, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, - new int[] {NEXT, FORWARD, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, - new int[] {NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, - SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, - new int[] {NOTHING, NEXT, FORWARD, SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, - }; - public static final int[] SLOT_PREF_KEYS = { R.string.notification_slot_0_key, R.string.notification_slot_1_key, @@ -165,14 +161,11 @@ public final class NotificationConstants { /** * @param context the context to use * @param sharedPreferences the shared preferences to query values from - * @param slotCount remove indices >= than this value (set to {@code 5} to do nothing, or make - * it lower if there are slots with empty actions) * @return a sorted list of the indices of the slots to use as compact slots */ - public static List getCompactSlotsFromPreferences( + public static Collection getCompactSlotsFromPreferences( @NonNull final Context context, - final SharedPreferences sharedPreferences, - final int slotCount) { + final SharedPreferences sharedPreferences) { final SortedSet compactSlots = new TreeSet<>(); for (int i = 0; i < 3; i++) { final int compactSlot = sharedPreferences.getInt( @@ -180,14 +173,14 @@ public final class NotificationConstants { if (compactSlot == Integer.MAX_VALUE) { // settings not yet populated, return default values - return new ArrayList<>(SLOT_COMPACT_DEFAULTS); + return SLOT_COMPACT_DEFAULTS; } - // a negative value (-1) is set when the user does not want a particular compact slot - if (compactSlot >= 0 && compactSlot < slotCount) { + if (compactSlot >= 0) { + // compact slot is < 0 if there are less than 3 checked checkboxes compactSlots.add(compactSlot); } } - return new ArrayList<>(compactSlots); + return compactSlots; } } diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java index 05c2e3af6..30420b0c7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java @@ -1,16 +1,19 @@ package org.schabi.newpipe.player.notification; +import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; +import static androidx.media.app.NotificationCompat.MediaStyle; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE; + import android.annotation.SuppressLint; +import android.app.PendingIntent; import android.content.Intent; import android.content.pm.ServiceInfo; import android.graphics.Bitmap; import android.os.Build; import android.util.Log; -import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.app.PendingIntentCompat; @@ -23,23 +26,12 @@ import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi; import org.schabi.newpipe.util.NavigationHelper; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; -import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; -import static androidx.media.app.NotificationCompat.MediaStyle; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_FORWARD; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_REWIND; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_NEXT; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PAUSE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PREVIOUS; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_REPEAT; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_SHUFFLE; - /** * This is a utility class for player notifications. */ @@ -100,29 +92,21 @@ public final class NotificationUtil { final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.getContext(), player.getContext().getString(R.string.notification_channel_id)); + final MediaStyle mediaStyle = new MediaStyle(); - initializeNotificationSlots(); - - // count the number of real slots, to make sure compact slots indices are not out of bound - int nonNothingSlotCount = 5; - if (notificationSlots[3] == NotificationConstants.NOTHING) { - --nonNothingSlotCount; + // setup media style (compact notification slots and media session) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + // notification actions are ignored on Android 13+, and are replaced by code in + // MediaSessionPlayerUi + final int[] compactSlots = initializeNotificationSlots(); + mediaStyle.setShowActionsInCompactView(compactSlots); } - if (notificationSlots[4] == NotificationConstants.NOTHING) { - --nonNothingSlotCount; - } - - // build the compact slot indices array (need code to convert from Integer... because Java) - final List compactSlotList = NotificationConstants.getCompactSlotsFromPreferences( - player.getContext(), player.getPrefs(), nonNothingSlotCount); - final int[] compactSlots = compactSlotList.stream().mapToInt(Integer::intValue).toArray(); - - final MediaStyle mediaStyle = new MediaStyle().setShowActionsInCompactView(compactSlots); player.UIs() .get(MediaSessionPlayerUi.class) .flatMap(MediaSessionPlayerUi::getSessionToken) .ifPresent(mediaStyle::setMediaSession); + // setup notification builder builder.setStyle(mediaStyle) .setPriority(NotificationCompat.PRIORITY_HIGH) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) @@ -157,7 +141,11 @@ public final class NotificationUtil { notificationBuilder.setContentText(player.getUploaderName()); notificationBuilder.setTicker(player.getVideoTitle()); - updateActions(notificationBuilder); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + // notification actions are ignored on Android 13+, and are replaced by code in + // MediaSessionPlayerUi + updateActions(notificationBuilder); + } } @@ -209,12 +197,35 @@ public final class NotificationUtil { // ACTIONS ///////////////////////////////////////////////////// - private void initializeNotificationSlots() { + /** + * The compact slots array from settings contains indices from 0 to 4, each referring to one of + * the five actions configurable by the user. However, if the user sets an action to "Nothing", + * then all of the actions coming after will have a "settings index" different than the index + * of the corresponding action when sent to the system. + * + * @return the indices of compact slots referred to the list of non-nothing actions that will be + * sent to the system + */ + private int[] initializeNotificationSlots() { + final Collection settingsCompactSlots = NotificationConstants + .getCompactSlotsFromPreferences(player.getContext(), player.getPrefs()); + final List adjustedCompactSlots = new ArrayList<>(); + + int nonNothingIndex = 0; for (int i = 0; i < 5; ++i) { notificationSlots[i] = player.getPrefs().getInt( player.getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), NotificationConstants.SLOT_DEFAULTS[i]); + + if (notificationSlots[i] != NotificationConstants.NOTHING) { + if (settingsCompactSlots.contains(i)) { + adjustedCompactSlots.add(nonNothingIndex); + } + nonNothingIndex += 1; + } } + + return adjustedCompactSlots.stream().mapToInt(Integer::intValue).toArray(); } @SuppressLint("RestrictedApi") @@ -227,115 +238,15 @@ public final class NotificationUtil { private void addAction(final NotificationCompat.Builder builder, @NotificationConstants.Action final int slot) { - final NotificationCompat.Action action = getAction(slot); - if (action != null) { - builder.addAction(action); + @Nullable final NotificationActionData data = + NotificationActionData.fromNotificationActionEnum(player, slot); + if (data == null) { + return; } - } - @Nullable - private NotificationCompat.Action getAction( - @NotificationConstants.Action final int selectedAction) { - final int baseActionIcon = NotificationConstants.ACTION_ICONS[selectedAction]; - switch (selectedAction) { - case NotificationConstants.PREVIOUS: - return getAction(baseActionIcon, - R.string.exo_controls_previous_description, ACTION_PLAY_PREVIOUS); - - case NotificationConstants.NEXT: - return getAction(baseActionIcon, - R.string.exo_controls_next_description, ACTION_PLAY_NEXT); - - case NotificationConstants.REWIND: - return getAction(baseActionIcon, - R.string.exo_controls_rewind_description, ACTION_FAST_REWIND); - - case NotificationConstants.FORWARD: - return getAction(baseActionIcon, - R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD); - - case NotificationConstants.SMART_REWIND_PREVIOUS: - if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { - return getAction(R.drawable.exo_notification_previous, - R.string.exo_controls_previous_description, ACTION_PLAY_PREVIOUS); - } else { - return getAction(R.drawable.exo_controls_rewind, - R.string.exo_controls_rewind_description, ACTION_FAST_REWIND); - } - - case NotificationConstants.SMART_FORWARD_NEXT: - if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { - return getAction(R.drawable.exo_notification_next, - R.string.exo_controls_next_description, ACTION_PLAY_NEXT); - } else { - return getAction(R.drawable.exo_controls_fastforward, - R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD); - } - - case NotificationConstants.PLAY_PAUSE_BUFFERING: - if (player.getCurrentState() == Player.STATE_PREFLIGHT - || player.getCurrentState() == Player.STATE_BLOCKED - || player.getCurrentState() == Player.STATE_BUFFERING) { - // null intent -> show hourglass icon that does nothing when clicked - return new NotificationCompat.Action(R.drawable.ic_hourglass_top, - player.getContext().getString(R.string.notification_action_buffering), - null); - } - - // fallthrough - case NotificationConstants.PLAY_PAUSE: - if (player.getCurrentState() == Player.STATE_COMPLETED) { - return getAction(R.drawable.ic_replay, - R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); - } else if (player.isPlaying() - || player.getCurrentState() == Player.STATE_PREFLIGHT - || player.getCurrentState() == Player.STATE_BLOCKED - || player.getCurrentState() == Player.STATE_BUFFERING) { - return getAction(R.drawable.exo_notification_pause, - R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); - } else { - return getAction(R.drawable.exo_notification_play, - R.string.exo_controls_play_description, ACTION_PLAY_PAUSE); - } - - case NotificationConstants.REPEAT: - if (player.getRepeatMode() == REPEAT_MODE_ALL) { - return getAction(R.drawable.exo_media_action_repeat_all, - R.string.exo_controls_repeat_all_description, ACTION_REPEAT); - } else if (player.getRepeatMode() == REPEAT_MODE_ONE) { - return getAction(R.drawable.exo_media_action_repeat_one, - R.string.exo_controls_repeat_one_description, ACTION_REPEAT); - } else /* player.getRepeatMode() == REPEAT_MODE_OFF */ { - return getAction(R.drawable.exo_media_action_repeat_off, - R.string.exo_controls_repeat_off_description, ACTION_REPEAT); - } - - case NotificationConstants.SHUFFLE: - if (player.getPlayQueue() != null && player.getPlayQueue().isShuffled()) { - return getAction(R.drawable.exo_controls_shuffle_on, - R.string.exo_controls_shuffle_on_description, ACTION_SHUFFLE); - } else { - return getAction(R.drawable.exo_controls_shuffle_off, - R.string.exo_controls_shuffle_off_description, ACTION_SHUFFLE); - } - - case NotificationConstants.CLOSE: - return getAction(R.drawable.ic_close, - R.string.close, ACTION_CLOSE); - - case NotificationConstants.NOTHING: - default: - // do nothing - return null; - } - } - - private NotificationCompat.Action getAction(@DrawableRes final int drawable, - @StringRes final int title, - final String intentAction) { - return new NotificationCompat.Action(drawable, player.getContext().getString(title), - PendingIntentCompat.getBroadcast(player.getContext(), NOTIFICATION_ID, - new Intent(intentAction), FLAG_UPDATE_CURRENT, false)); + final PendingIntent intent = PendingIntentCompat.getBroadcast(player.getContext(), + NOTIFICATION_ID, new Intent(data.action()), FLAG_UPDATE_CURRENT, false); + builder.addAction(new NotificationCompat.Action(data.icon(), data.name(), intent)); } private Intent getIntentForNotification() { @@ -364,7 +275,7 @@ public final class NotificationUtil { final Bitmap thumbnail = player.getThumbnail(); if (thumbnail == null || !showThumbnail) { // since the builder is reused, make sure the thumbnail is unset if there is not one - builder.setLargeIcon(null); + builder.setLargeIcon((Bitmap) null); return; } diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java new file mode 100644 index 000000000..bc24fbe81 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -0,0 +1,271 @@ +package org.schabi.newpipe.settings; + +import static org.schabi.newpipe.extractor.utils.Utils.isBlank; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Bundle; +import android.widget.Toast; + +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.preference.Preference; +import androidx.preference.PreferenceManager; + +import org.schabi.newpipe.NewPipeDatabase; +import org.schabi.newpipe.R; +import org.schabi.newpipe.error.ErrorUtil; +import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; +import org.schabi.newpipe.streams.io.StoredFileHelper; +import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.ZipHelper; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Objects; + +public class BackupRestoreSettingsFragment extends BasePreferenceFragment { + + private static final String ZIP_MIME_TYPE = "application/zip"; + + private final SimpleDateFormat exportDateFormat = + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US); + private ContentSettingsManager manager; + private String importExportDataPathKey; + private final ActivityResultLauncher requestImportPathLauncher = + registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), + this::requestImportPathResult); + private final ActivityResultLauncher requestExportPathLauncher = + registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), + this::requestExportPathResult); + + + @Override + public void onCreatePreferences(@Nullable final Bundle savedInstanceState, + @Nullable final String rootKey) { + final File homeDir = ContextCompat.getDataDir(requireContext()); + Objects.requireNonNull(homeDir); + manager = new ContentSettingsManager(new NewPipeFileLocator(homeDir)); + manager.deleteSettingsFile(); + + importExportDataPathKey = getString(R.string.import_export_data_path); + + + addPreferencesFromResourceRegistry(); + + final Preference importDataPreference = requirePreference(R.string.import_data); + importDataPreference.setOnPreferenceClickListener((Preference p) -> { + NoFileManagerSafeGuard.launchSafe( + requestImportPathLauncher, + StoredFileHelper.getPicker(requireContext(), + ZIP_MIME_TYPE, getImportExportDataUri()), + TAG, + getContext() + ); + + return true; + }); + + final Preference exportDataPreference = requirePreference(R.string.export_data); + exportDataPreference.setOnPreferenceClickListener((final Preference p) -> { + NoFileManagerSafeGuard.launchSafe( + requestExportPathLauncher, + StoredFileHelper.getNewPicker(requireContext(), + "NewPipeData-" + exportDateFormat.format(new Date()) + ".zip", + ZIP_MIME_TYPE, getImportExportDataUri()), + TAG, + getContext() + ); + + return true; + }); + + final Preference resetSettings = findPreference(getString(R.string.reset_settings)); + // Resets all settings by deleting shared preference and restarting the app + // A dialogue will pop up to confirm if user intends to reset all settings + assert resetSettings != null; + resetSettings.setOnPreferenceClickListener(preference -> { + // Show Alert Dialogue + final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setMessage(R.string.reset_all_settings); + builder.setCancelable(true); + builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> { + // Deletes all shared preferences xml files. + final SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(requireContext()); + sharedPreferences.edit().clear().apply(); + // Restarts the app + if (getActivity() == null) { + return; + } + NavigationHelper.restartApp(getActivity()); + }); + builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> { + }); + final AlertDialog alertDialog = builder.create(); + alertDialog.show(); + return true; + }); + } + + private void requestExportPathResult(final ActivityResult result) { + assureCorrectAppLanguage(requireContext()); + if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { + // will be saved only on success + final Uri lastExportDataUri = result.getData().getData(); + + final StoredFileHelper file = new StoredFileHelper( + requireContext(), result.getData().getData(), ZIP_MIME_TYPE); + + exportDatabase(file, lastExportDataUri); + } + } + + private void requestImportPathResult(final ActivityResult result) { + assureCorrectAppLanguage(requireContext()); + if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { + // will be saved only on success + final Uri lastImportDataUri = result.getData().getData(); + + final StoredFileHelper file = new StoredFileHelper( + requireContext(), result.getData().getData(), ZIP_MIME_TYPE); + + new androidx.appcompat.app.AlertDialog.Builder(requireActivity()) + .setMessage(R.string.override_current_data) + .setPositiveButton(R.string.ok, (d, id) -> + importDatabase(file, lastImportDataUri)) + .setNegativeButton(R.string.cancel, (d, id) -> + d.cancel()) + .show(); + } + } + + private void exportDatabase(final StoredFileHelper file, final Uri exportDataUri) { + try { + //checkpoint before export + NewPipeDatabase.checkpoint(); + + final SharedPreferences preferences = PreferenceManager + .getDefaultSharedPreferences(requireContext()); + manager.exportDatabase(preferences, file); + + saveLastImportExportDataUri(exportDataUri); // save export path only on success + Toast.makeText(requireContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT) + .show(); + } catch (final Exception e) { + ErrorUtil.showUiErrorSnackbar(this, "Exporting database", e); + } + } + + private void importDatabase(final StoredFileHelper file, final Uri importDataUri) { + // check if file is supported + if (!ZipHelper.isValidZipFile(file)) { + Toast.makeText(requireContext(), R.string.no_valid_zip_file, Toast.LENGTH_SHORT) + .show(); + return; + } + + try { + if (!manager.ensureDbDirectoryExists()) { + throw new IOException("Could not create databases dir"); + } + + if (!manager.extractDb(file)) { + Toast.makeText(requireContext(), R.string.could_not_import_all_files, + Toast.LENGTH_LONG) + .show(); + } + + // if settings file exist, ask if it should be imported. + if (manager.extractSettings(file)) { + new androidx.appcompat.app.AlertDialog.Builder(requireContext()) + .setTitle(R.string.import_settings) + .setNegativeButton(R.string.cancel, (dialog, which) -> { + dialog.dismiss(); + finishImport(importDataUri); + }) + .setPositiveButton(R.string.ok, (dialog, which) -> { + dialog.dismiss(); + final Context context = requireContext(); + final SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + manager.loadSharedPreferences(prefs); + cleanImport(context, prefs); + finishImport(importDataUri); + }) + .show(); + } else { + finishImport(importDataUri); + } + } catch (final Exception e) { + ErrorUtil.showUiErrorSnackbar(this, "Importing database", e); + } + } + + /** + * Remove settings that are not supposed to be imported on different devices + * and reset them to default values. + * @param context the context used for the import + * @param prefs the preferences used while running the import + */ + private void cleanImport(@NonNull final Context context, + @NonNull final SharedPreferences prefs) { + // Check if media tunnelling needs to be disabled automatically, + // if it was disabled automatically in the imported preferences. + final String tunnelingKey = context.getString(R.string.disable_media_tunneling_key); + final String automaticTunnelingKey = + context.getString(R.string.disabled_media_tunneling_automatically_key); + // R.string.disable_media_tunneling_key should always be true + // if R.string.disabled_media_tunneling_automatically_key equals 1, + // but we double check here just to be sure and to avoid regressions + // caused by possible later modification of the media tunneling functionality. + // R.string.disabled_media_tunneling_automatically_key == 0: + // automatic value overridden by user in settings + // R.string.disabled_media_tunneling_automatically_key == -1: not set + final boolean wasMediaTunnelingDisabledAutomatically = + prefs.getInt(automaticTunnelingKey, -1) == 1 + && prefs.getBoolean(tunnelingKey, false); + if (wasMediaTunnelingDisabledAutomatically) { + prefs.edit() + .putInt(automaticTunnelingKey, -1) + .putBoolean(tunnelingKey, false) + .apply(); + NewPipeSettings.setMediaTunneling(context); + } + } + + /** + * Save import path and restart system. + * + * @param importDataUri The import path to save + */ + private void finishImport(final Uri importDataUri) { + // save import path only on success + saveLastImportExportDataUri(importDataUri); + // restart app to properly load db + NavigationHelper.restartApp(requireActivity()); + } + + private Uri getImportExportDataUri() { + final String path = defaultPreferences.getString(importExportDataPathKey, null); + return isBlank(path) ? null : Uri.parse(path); + } + + private void saveLastImportExportDataUri(final Uri importExportDataUri) { + final SharedPreferences.Editor editor = defaultPreferences.edit() + .putString(importExportDataPathKey, importExportDataUri.toString()); + editor.apply(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index c7d107acb..ec2bed67a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -1,106 +1,36 @@ package org.schabi.newpipe.settings; -import static org.schabi.newpipe.extractor.utils.Utils.isBlank; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - -import android.app.Activity; import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.widget.Toast; -import androidx.activity.result.ActivityResult; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; import androidx.preference.Preference; -import androidx.preference.PreferenceManager; import org.schabi.newpipe.DownloaderImpl; -import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; -import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.localization.ContentCountry; import org.schabi.newpipe.extractor.localization.Localization; -import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; -import org.schabi.newpipe.streams.io.StoredFileHelper; -import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.image.ImageStrategy; import org.schabi.newpipe.util.image.PicassoHelper; -import org.schabi.newpipe.util.ZipHelper; import org.schabi.newpipe.util.image.PreferredImageQuality; -import java.io.File; import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Objects; public class ContentSettingsFragment extends BasePreferenceFragment { - private static final String ZIP_MIME_TYPE = "application/zip"; - - private final SimpleDateFormat exportDateFormat = - new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US); - - private ContentSettingsManager manager; - - private String importExportDataPathKey; private String youtubeRestrictedModeEnabledKey; private Localization initialSelectedLocalization; private ContentCountry initialSelectedContentCountry; private String initialLanguage; - private final ActivityResultLauncher requestImportPathLauncher = - registerForActivityResult(new StartActivityForResult(), this::requestImportPathResult); - private final ActivityResultLauncher requestExportPathLauncher = - registerForActivityResult(new StartActivityForResult(), this::requestExportPathResult); @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { - final File homeDir = ContextCompat.getDataDir(requireContext()); - Objects.requireNonNull(homeDir); - manager = new ContentSettingsManager(new NewPipeFileLocator(homeDir)); - manager.deleteSettingsFile(); - - importExportDataPathKey = getString(R.string.import_export_data_path); youtubeRestrictedModeEnabledKey = getString(R.string.youtube_restricted_mode_enabled); addPreferencesFromResourceRegistry(); - final Preference importDataPreference = requirePreference(R.string.import_data); - importDataPreference.setOnPreferenceClickListener((Preference p) -> { - NoFileManagerSafeGuard.launchSafe( - requestImportPathLauncher, - StoredFileHelper.getPicker(requireContext(), - ZIP_MIME_TYPE, getImportExportDataUri()), - TAG, - getContext() - ); - - return true; - }); - - final Preference exportDataPreference = requirePreference(R.string.export_data); - exportDataPreference.setOnPreferenceClickListener((final Preference p) -> { - NoFileManagerSafeGuard.launchSafe( - requestExportPathLauncher, - StoredFileHelper.getNewPicker(requireContext(), - "NewPipeData-" + exportDateFormat.format(new Date()) + ".zip", - ZIP_MIME_TYPE, getImportExportDataUri()), - TAG, - getContext() - ); - - return true; - }); - initialSelectedLocalization = org.schabi.newpipe.util.Localization .getPreferredLocalization(requireContext()); initialSelectedContentCountry = org.schabi.newpipe.util.Localization @@ -158,151 +88,4 @@ public class ContentSettingsFragment extends BasePreferenceFragment { NewPipe.setupLocalization(selectedLocalization, selectedContentCountry); } } - - private void requestExportPathResult(final ActivityResult result) { - assureCorrectAppLanguage(getContext()); - if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { - // will be saved only on success - final Uri lastExportDataUri = result.getData().getData(); - - final StoredFileHelper file = - new StoredFileHelper(getContext(), result.getData().getData(), ZIP_MIME_TYPE); - - exportDatabase(file, lastExportDataUri); - } - } - - private void requestImportPathResult(final ActivityResult result) { - assureCorrectAppLanguage(getContext()); - if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { - // will be saved only on success - final Uri lastImportDataUri = result.getData().getData(); - - final StoredFileHelper file = - new StoredFileHelper(getContext(), result.getData().getData(), ZIP_MIME_TYPE); - - new AlertDialog.Builder(requireActivity()) - .setMessage(R.string.override_current_data) - .setPositiveButton(R.string.ok, (d, id) -> - importDatabase(file, lastImportDataUri)) - .setNegativeButton(R.string.cancel, (d, id) -> - d.cancel()) - .show(); - } - } - - private void exportDatabase(final StoredFileHelper file, final Uri exportDataUri) { - try { - //checkpoint before export - NewPipeDatabase.checkpoint(); - - final SharedPreferences preferences = PreferenceManager - .getDefaultSharedPreferences(requireContext()); - manager.exportDatabase(preferences, file); - - saveLastImportExportDataUri(exportDataUri); // save export path only on success - Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT).show(); - } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(this, "Exporting database", e); - } - } - - private void importDatabase(final StoredFileHelper file, final Uri importDataUri) { - // check if file is supported - if (!ZipHelper.isValidZipFile(file)) { - Toast.makeText(getContext(), R.string.no_valid_zip_file, Toast.LENGTH_SHORT) - .show(); - return; - } - - try { - if (!manager.ensureDbDirectoryExists()) { - throw new IOException("Could not create databases dir"); - } - - if (!manager.extractDb(file)) { - Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG) - .show(); - } - - // if settings file exist, ask if it should be imported. - if (manager.extractSettings(file)) { - new AlertDialog.Builder(requireContext()) - .setTitle(R.string.import_settings) - .setNegativeButton(R.string.cancel, (dialog, which) -> { - dialog.dismiss(); - finishImport(importDataUri); - }) - .setPositiveButton(R.string.ok, (dialog, which) -> { - dialog.dismiss(); - final Context context = requireContext(); - final SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(context); - manager.loadSharedPreferences(prefs); - cleanImport(context, prefs); - finishImport(importDataUri); - }) - .show(); - } else { - finishImport(importDataUri); - } - } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(this, "Importing database", e); - } - } - - /** - * Remove settings that are not supposed to be imported on different devices - * and reset them to default values. - * @param context the context used for the import - * @param prefs the preferences used while running the import - */ - private void cleanImport(@NonNull final Context context, - @NonNull final SharedPreferences prefs) { - // Check if media tunnelling needs to be disabled automatically, - // if it was disabled automatically in the imported preferences. - final String tunnelingKey = context.getString(R.string.disable_media_tunneling_key); - final String automaticTunnelingKey = - context.getString(R.string.disabled_media_tunneling_automatically_key); - // R.string.disable_media_tunneling_key should always be true - // if R.string.disabled_media_tunneling_automatically_key equals 1, - // but we double check here just to be sure and to avoid regressions - // caused by possible later modification of the media tunneling functionality. - // R.string.disabled_media_tunneling_automatically_key == 0: - // automatic value overridden by user in settings - // R.string.disabled_media_tunneling_automatically_key == -1: not set - final boolean wasMediaTunnelingDisabledAutomatically = - prefs.getInt(automaticTunnelingKey, -1) == 1 - && prefs.getBoolean(tunnelingKey, false); - if (wasMediaTunnelingDisabledAutomatically) { - prefs.edit() - .putInt(automaticTunnelingKey, -1) - .putBoolean(tunnelingKey, false) - .apply(); - NewPipeSettings.setMediaTunneling(context); - } - } - - /** - * Save import path and restart system. - * - * @param importDataUri The import path to save - */ - private void finishImport(final Uri importDataUri) { - // save import path only on success - saveLastImportExportDataUri(importDataUri); - // restart app to properly load db - NavigationHelper.restartApp(requireActivity()); - } - - private Uri getImportExportDataUri() { - final String path = defaultPreferences.getString(importExportDataPathKey, null); - return isBlank(path) ? null : Uri.parse(path); - } - - private void saveLastImportExportDataUri(final Uri importExportDataUri) { - final SharedPreferences.Editor editor = defaultPreferences.edit() - .putString(importExportDataPathKey, importExportDataUri.toString()); - editor.apply(); - } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java index 3776d78f6..32e33d55b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java @@ -23,7 +23,7 @@ public class MainSettingsFragment extends BasePreferenceFragment { setHasOptionsMenu(true); // Otherwise onCreateOptionsMenu is not called // Check if the app is updatable - if (!ReleaseVersionUtil.isReleaseApk()) { + if (!ReleaseVersionUtil.INSTANCE.isReleaseApk()) { getPreferenceScreen().removePreference( findPreference(getString(R.string.update_pref_screen_key))); diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index b85b95eb0..421440ea7 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -11,6 +11,7 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.preference.PreferenceManager; +import org.schabi.newpipe.App; import org.schabi.newpipe.R; import org.schabi.newpipe.util.DeviceUtils; @@ -44,14 +45,8 @@ public final class NewPipeSettings { private NewPipeSettings() { } public static void initSettings(final Context context) { - // check if the last used preference version is set - // to determine whether this is the first app run - final int lastUsedPrefVersion = PreferenceManager.getDefaultSharedPreferences(context) - .getInt(context.getString(R.string.last_used_preferences_version), -1); - final boolean isFirstRun = lastUsedPrefVersion == -1; - // first run migrations, then setDefaultValues, since the latter requires the correct types - SettingMigrations.runMigrationsIfNeeded(context, isFirstRun); + SettingMigrations.runMigrationsIfNeeded(context); // readAgain is true so that if new settings are added their default value is set PreferenceManager.setDefaultValues(context, R.xml.main_settings, true); @@ -63,11 +58,12 @@ public final class NewPipeSettings { PreferenceManager.setDefaultValues(context, R.xml.player_notification_settings, true); PreferenceManager.setDefaultValues(context, R.xml.update_settings, true); PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true); + PreferenceManager.setDefaultValues(context, R.xml.backup_restore_settings, true); saveDefaultVideoDownloadDirectory(context); saveDefaultAudioDownloadDirectory(context); - disableMediaTunnelingIfNecessary(context, isFirstRun); + disableMediaTunnelingIfNecessary(context); } static void saveDefaultVideoDownloadDirectory(final Context context) { @@ -145,8 +141,7 @@ public final class NewPipeSettings { R.string.show_remote_search_suggestions_key); } - private static void disableMediaTunnelingIfNecessary(@NonNull final Context context, - final boolean isFirstRun) { + private static void disableMediaTunnelingIfNecessary(@NonNull final Context context) { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final String disabledTunnelingKey = context.getString(R.string.disable_media_tunneling_key); final String disabledTunnelingAutomaticallyKey = @@ -161,7 +156,7 @@ public final class NewPipeSettings { prefs.getInt(disabledTunnelingAutomaticallyKey, -1) == 0 && !prefs.getBoolean(disabledTunnelingKey, false); - if (Boolean.TRUE.equals(isFirstRun) + if (App.getApp().isFirstRun() || (wasDeviceBlacklistUpdated && !wasMediaTunnelingEnabledByUser)) { setMediaTunneling(context); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java index 147d20a36..36abef9e5 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.settings; +import static org.schabi.newpipe.local.bookmark.MergedPlaylistManager.getMergedOrderedPlaylists; + import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -31,7 +33,6 @@ import java.util.List; import java.util.Vector; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.disposables.Disposable; public class SelectPlaylistFragment extends DialogFragment { @@ -90,8 +91,7 @@ public class SelectPlaylistFragment extends DialogFragment { final LocalPlaylistManager localPlaylistManager = new LocalPlaylistManager(database); final RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); - disposable = Flowable.combineLatest(localPlaylistManager.getPlaylists(), - remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge) + disposable = getMergedOrderedPlaylists(localPlaylistManager, remotePlaylistManager) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::displayPlaylists, this::onError); } @@ -118,7 +118,7 @@ public class SelectPlaylistFragment extends DialogFragment { if (selectedItem instanceof PlaylistMetadataEntry) { final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); - onSelectedListener.onLocalPlaylistSelected(entry.uid, entry.name); + onSelectedListener.onLocalPlaylistSelected(entry.getUid(), entry.name); } else if (selectedItem instanceof PlaylistRemoteEntity) { final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index b7bafde75..d731f2f5e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -7,6 +7,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; +import org.schabi.newpipe.App; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; @@ -163,15 +164,14 @@ public final class SettingMigrations { private static final int VERSION = 6; - public static void runMigrationsIfNeeded(@NonNull final Context context, - final boolean isFirstRun) { + public static void runMigrationsIfNeeded(@NonNull final Context context) { // setup migrations and check if there is something to do sp = PreferenceManager.getDefaultSharedPreferences(context); final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); // no migration to run, already up to date - if (isFirstRun) { + if (App.getApp().isFirstRun()) { sp.edit().putInt(lastPrefVersionKey, VERSION).apply(); return; } else if (lastPrefVersion == VERSION) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java index 3ee6668bf..529e53442 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java @@ -266,7 +266,7 @@ public class SettingsActivity extends AppCompatActivity implements */ private void ensureSearchRepresentsApplicationState() { // Check if the update settings are available - if (!ReleaseVersionUtil.isReleaseApk()) { + if (!ReleaseVersionUtil.INSTANCE.isReleaseApk()) { SettingsResourceRegistry.getInstance() .getEntryByPreferencesResId(R.xml.update_settings) .setSearchable(false); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java index b3d0741bb..06e0a7c1e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java @@ -41,6 +41,7 @@ public final class SettingsResourceRegistry { add(UpdateSettingsFragment.class, R.xml.update_settings); add(VideoAudioSettingsFragment.class, R.xml.video_audio_settings); add(ExoPlayerSettingsFragment.class, R.xml.exoplayer_settings); + add(BackupRestoreSettingsFragment.class, R.xml.backup_restore_settings); } private SettingRegistryEntry add( diff --git a/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java index d1a379e66..b8d0aa556 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java @@ -1,9 +1,12 @@ package org.schabi.newpipe.settings; +import android.app.AlertDialog; +import android.content.Context; import android.os.Bundle; import android.widget.Toast; import androidx.preference.Preference; +import androidx.preference.PreferenceManager; import org.schabi.newpipe.NewVersionWorker; import org.schabi.newpipe.R; @@ -36,4 +39,38 @@ public class UpdateSettingsFragment extends BasePreferenceFragment { findPreference(getString(R.string.manual_update_key)) .setOnPreferenceClickListener(manualUpdateClick); } + + public static void askForConsentToUpdateChecks(final Context context) { + new AlertDialog.Builder(context) + .setTitle(context.getString(R.string.check_for_updates)) + .setMessage(context.getString(R.string.auto_update_check_description)) + .setPositiveButton(context.getString(R.string.yes), (d, w) -> { + d.dismiss(); + setAutoUpdateCheckEnabled(context, true); + }) + .setNegativeButton(R.string.no, (d, w) -> { + d.dismiss(); + // set explicitly to false, since the default is true on previous versions + setAutoUpdateCheckEnabled(context, false); + }) + .show(); + } + + private static void setAutoUpdateCheckEnabled(final Context context, final boolean enabled) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putBoolean(context.getString(R.string.update_app_key), enabled) + .putBoolean(context.getString(R.string.update_check_consent_key), true) + .apply(); + } + + /** + * Whether the user was asked for consent to automatically check for app updates. + * @param context + * @return true if the user was asked for consent, false otherwise + */ + public static boolean wasUserAskedForConsent(final Context context) { + return PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(context.getString(R.string.update_check_consent_key), false); + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java index 3e92f297e..7dfddef20 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java +++ b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java @@ -5,35 +5,22 @@ import static org.schabi.newpipe.player.notification.NotificationConstants.ACTIO import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.content.res.ColorStateList; +import android.os.Build; import android.util.AttributeSet; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.RadioButton; -import android.widget.RadioGroup; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.widget.TextViewCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import org.schabi.newpipe.App; import org.schabi.newpipe.R; -import org.schabi.newpipe.databinding.ListRadioIconItemBinding; -import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding; import org.schabi.newpipe.player.notification.NotificationConstants; -import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.ThemeHelper; -import org.schabi.newpipe.views.FocusOverlayView; +import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; @@ -45,8 +32,9 @@ public class NotificationActionsPreference extends Preference { } - @Nullable private NotificationSlot[] notificationSlots = null; - @Nullable private List compactSlots = null; + private NotificationSlot[] notificationSlots; + private List compactSlots; + //////////////////////////////////////////////////////////////////////////// // Lifecycle @@ -56,6 +44,11 @@ public class NotificationActionsPreference extends Preference { public void onBindViewHolder(@NonNull final PreferenceViewHolder holder) { super.onBindViewHolder(holder); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ((TextView) holder.itemView.findViewById(R.id.summary)) + .setText(R.string.notification_actions_summary_android13); + } + holder.itemView.setClickable(false); setupActions(holder.itemView); } @@ -75,13 +68,29 @@ public class NotificationActionsPreference extends Preference { //////////////////////////////////////////////////////////////////////////// private void setupActions(@NonNull final View view) { - compactSlots = NotificationConstants.getCompactSlotsFromPreferences(getContext(), - getSharedPreferences(), 5); + compactSlots = new ArrayList<>(NotificationConstants.getCompactSlotsFromPreferences( + getContext(), getSharedPreferences())); notificationSlots = IntStream.range(0, 5) - .mapToObj(i -> new NotificationSlot(i, view)) + .mapToObj(i -> new NotificationSlot(getContext(), getSharedPreferences(), i, view, + compactSlots.contains(i), this::onToggleCompactSlot)) .toArray(NotificationSlot[]::new); } + private void onToggleCompactSlot(final int i, final CheckBox checkBox) { + if (checkBox.isChecked()) { + compactSlots.remove((Integer) i); + } else if (compactSlots.size() < 3) { + compactSlots.add(i); + } else { + Toast.makeText(getContext(), + R.string.notification_actions_at_most_three, + Toast.LENGTH_SHORT).show(); + return; + } + + checkBox.toggle(); + } + //////////////////////////////////////////////////////////////////////////// // Saving @@ -99,143 +108,10 @@ public class NotificationActionsPreference extends Preference { for (int i = 0; i < 5; i++) { editor.putInt(getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), - notificationSlots[i].selectedAction); + notificationSlots[i].getSelectedAction()); } editor.apply(); } } - - - //////////////////////////////////////////////////////////////////////////// - // Notification action - //////////////////////////////////////////////////////////////////////////// - - private static final int[] SLOT_ITEMS = { - R.id.notificationAction0, - R.id.notificationAction1, - R.id.notificationAction2, - R.id.notificationAction3, - R.id.notificationAction4, - }; - - private static final int[] SLOT_TITLES = { - R.string.notification_action_0_title, - R.string.notification_action_1_title, - R.string.notification_action_2_title, - R.string.notification_action_3_title, - R.string.notification_action_4_title, - }; - - private class NotificationSlot { - - final int i; - @NotificationConstants.Action int selectedAction; - - ImageView icon; - TextView summary; - - NotificationSlot(final int actionIndex, final View parentView) { - this.i = actionIndex; - - final View view = parentView.findViewById(SLOT_ITEMS[i]); - setupSelectedAction(view); - setupTitle(view); - setupCheckbox(view); - } - - void setupTitle(final View view) { - ((TextView) view.findViewById(R.id.notificationActionTitle)) - .setText(SLOT_TITLES[i]); - view.findViewById(R.id.notificationActionClickableArea).setOnClickListener( - v -> openActionChooserDialog()); - } - - void setupCheckbox(final View view) { - final CheckBox compactSlotCheckBox = view.findViewById(R.id.notificationActionCheckBox); - compactSlotCheckBox.setChecked(compactSlots.contains(i)); - view.findViewById(R.id.notificationActionCheckBoxClickableArea).setOnClickListener( - v -> { - if (compactSlotCheckBox.isChecked()) { - compactSlots.remove((Integer) i); - } else if (compactSlots.size() < 3) { - compactSlots.add(i); - } else { - Toast.makeText(getContext(), - R.string.notification_actions_at_most_three, - Toast.LENGTH_SHORT).show(); - return; - } - - compactSlotCheckBox.toggle(); - }); - } - - void setupSelectedAction(final View view) { - icon = view.findViewById(R.id.notificationActionIcon); - summary = view.findViewById(R.id.notificationActionSummary); - selectedAction = getSharedPreferences().getInt( - getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), - NotificationConstants.SLOT_DEFAULTS[i]); - updateInfo(); - } - - void updateInfo() { - if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) { - icon.setImageDrawable(null); - } else { - icon.setImageDrawable(AppCompatResources.getDrawable(getContext(), - NotificationConstants.ACTION_ICONS[selectedAction])); - } - - summary.setText(NotificationConstants.getActionName(getContext(), selectedAction)); - } - - void openActionChooserDialog() { - final LayoutInflater inflater = LayoutInflater.from(getContext()); - final SingleChoiceDialogViewBinding binding = - SingleChoiceDialogViewBinding.inflate(inflater); - - final AlertDialog alertDialog = new AlertDialog.Builder(getContext()) - .setTitle(SLOT_TITLES[i]) - .setView(binding.getRoot()) - .setCancelable(true) - .create(); - - final View.OnClickListener radioButtonsClickListener = v -> { - selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][v.getId()]; - updateInfo(); - alertDialog.dismiss(); - }; - - for (int id = 0; id < NotificationConstants.SLOT_ALLOWED_ACTIONS[i].length; ++id) { - final int action = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][id]; - final RadioButton radioButton = ListRadioIconItemBinding.inflate(inflater) - .getRoot(); - - // if present set action icon with correct color - final int iconId = NotificationConstants.ACTION_ICONS[action]; - if (iconId != 0) { - radioButton.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconId, 0); - - final var color = ColorStateList.valueOf(ThemeHelper - .resolveColorFromAttr(getContext(), android.R.attr.textColorPrimary)); - TextViewCompat.setCompoundDrawableTintList(radioButton, color); - } - - radioButton.setText(NotificationConstants.getActionName(getContext(), action)); - radioButton.setChecked(action == selectedAction); - radioButton.setId(id); - radioButton.setLayoutParams(new RadioGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - radioButton.setOnClickListener(radioButtonsClickListener); - binding.list.addView(radioButton); - } - alertDialog.show(); - - if (DeviceUtils.isTv(getContext())) { - FocusOverlayView.setupFocusObserver(alertDialog); - } - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java new file mode 100644 index 000000000..981ba3e75 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java @@ -0,0 +1,172 @@ +package org.schabi.newpipe.settings.custom; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.ColorStateList; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.TextView; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.widget.TextViewCompat; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.databinding.ListRadioIconItemBinding; +import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding; +import org.schabi.newpipe.player.notification.NotificationConstants; +import org.schabi.newpipe.util.DeviceUtils; +import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.views.FocusOverlayView; + +import java.util.Objects; +import java.util.function.BiConsumer; + +class NotificationSlot { + + private static final int[] SLOT_ITEMS = { + R.id.notificationAction0, + R.id.notificationAction1, + R.id.notificationAction2, + R.id.notificationAction3, + R.id.notificationAction4, + }; + + private static final int[] SLOT_TITLES = { + R.string.notification_action_0_title, + R.string.notification_action_1_title, + R.string.notification_action_2_title, + R.string.notification_action_3_title, + R.string.notification_action_4_title, + }; + + private final int i; + private @NotificationConstants.Action int selectedAction; + private final Context context; + private final BiConsumer onToggleCompactSlot; + + private ImageView icon; + private TextView summary; + + NotificationSlot(final Context context, + final SharedPreferences prefs, + final int actionIndex, + final View parentView, + final boolean isCompactSlotChecked, + final BiConsumer onToggleCompactSlot) { + this.context = context; + this.i = actionIndex; + this.onToggleCompactSlot = onToggleCompactSlot; + + selectedAction = Objects.requireNonNull(prefs).getInt( + context.getString(NotificationConstants.SLOT_PREF_KEYS[i]), + NotificationConstants.SLOT_DEFAULTS[i]); + final View view = parentView.findViewById(SLOT_ITEMS[i]); + + // only show the last two notification slots on Android 13+ + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || i >= 3) { + setupSelectedAction(view); + setupTitle(view); + setupCheckbox(view, isCompactSlotChecked); + } else { + view.setVisibility(View.GONE); + } + } + + void setupTitle(final View view) { + ((TextView) view.findViewById(R.id.notificationActionTitle)) + .setText(SLOT_TITLES[i]); + view.findViewById(R.id.notificationActionClickableArea).setOnClickListener( + v -> openActionChooserDialog()); + } + + void setupCheckbox(final View view, final boolean isCompactSlotChecked) { + final CheckBox compactSlotCheckBox = view.findViewById(R.id.notificationActionCheckBox); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // there are no compact slots to customize on Android 13+ + compactSlotCheckBox.setVisibility(View.GONE); + view.findViewById(R.id.notificationActionCheckBoxClickableArea) + .setVisibility(View.GONE); + return; + } + + compactSlotCheckBox.setChecked(isCompactSlotChecked); + view.findViewById(R.id.notificationActionCheckBoxClickableArea).setOnClickListener( + v -> onToggleCompactSlot.accept(i, compactSlotCheckBox)); + } + + void setupSelectedAction(final View view) { + icon = view.findViewById(R.id.notificationActionIcon); + summary = view.findViewById(R.id.notificationActionSummary); + updateInfo(); + } + + void updateInfo() { + if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) { + icon.setImageDrawable(null); + } else { + icon.setImageDrawable(AppCompatResources.getDrawable(context, + NotificationConstants.ACTION_ICONS[selectedAction])); + } + + summary.setText(NotificationConstants.getActionName(context, selectedAction)); + } + + void openActionChooserDialog() { + final LayoutInflater inflater = LayoutInflater.from(context); + final SingleChoiceDialogViewBinding binding = + SingleChoiceDialogViewBinding.inflate(inflater); + + final AlertDialog alertDialog = new AlertDialog.Builder(context) + .setTitle(SLOT_TITLES[i]) + .setView(binding.getRoot()) + .setCancelable(true) + .create(); + + final View.OnClickListener radioButtonsClickListener = v -> { + selectedAction = NotificationConstants.ALL_ACTIONS[v.getId()]; + updateInfo(); + alertDialog.dismiss(); + }; + + for (int id = 0; id < NotificationConstants.ALL_ACTIONS.length; ++id) { + final int action = NotificationConstants.ALL_ACTIONS[id]; + final RadioButton radioButton = ListRadioIconItemBinding.inflate(inflater) + .getRoot(); + + // if present set action icon with correct color + final int iconId = NotificationConstants.ACTION_ICONS[action]; + if (iconId != 0) { + radioButton.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconId, 0); + + final var color = ColorStateList.valueOf(ThemeHelper + .resolveColorFromAttr(context, android.R.attr.textColorPrimary)); + TextViewCompat.setCompoundDrawableTintList(radioButton, color); + } + + radioButton.setText(NotificationConstants.getActionName(context, action)); + radioButton.setChecked(action == selectedAction); + radioButton.setId(id); + radioButton.setLayoutParams(new RadioGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + radioButton.setOnClickListener(radioButtonsClickListener); + binding.list.addView(radioButton); + } + alertDialog.show(); + + if (DeviceUtils.isTv(context)) { + FocusOverlayView.setupFocusObserver(alertDialog); + } + } + + @NotificationConstants.Action + public int getSelectedAction() { + return selectedAction; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java b/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java index 74fc74c76..bb47a4b91 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java +++ b/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java @@ -1,11 +1,18 @@ package org.schabi.newpipe.streams.io; +import static android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME; +import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; +import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract; +import android.system.Os; +import android.system.StructStatVfs; import android.util.Log; import androidx.annotation.NonNull; @@ -15,6 +22,7 @@ import androidx.documentfile.provider.DocumentFile; import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.util.FilePickerActivityHelper; +import java.io.FileDescriptor; import java.io.IOException; import java.net.URI; import java.nio.file.Files; @@ -26,10 +34,6 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import static android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME; -import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; - public class StoredDirectoryHelper { private static final String TAG = StoredDirectoryHelper.class.getSimpleName(); public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -38,6 +42,10 @@ public class StoredDirectoryHelper { private Path ioTree; private DocumentFile docTree; + /** + * Context is `null` for non-SAF files, i.e. files that use `ioTree`. + */ + @Nullable private Context context; private final String tag; @@ -168,6 +176,46 @@ public class StoredDirectoryHelper { return docTree == null; } + /** + * Get free memory of the storage partition this file belongs to (root of the directory). + * See StackOverflow and + * + * {@code statvfs()} and {@code fstatvfs()} docs + * + * @return amount of free memory in the volume of current directory (bytes), or {@link + * Long#MAX_VALUE} if an error occurred + */ + public long getFreeStorageSpace() { + try { + final StructStatVfs stat; + + if (ioTree != null) { + // non-SAF file, use statvfs with the path directly (also, `context` would be null + // for non-SAF files, so we wouldn't be able to call `getContentResolver` anyway) + stat = Os.statvfs(ioTree.toString()); + + } else { + // SAF file, we can't get a path directly, so obtain a file descriptor first + // and then use fstatvfs with the file descriptor + try (ParcelFileDescriptor parcelFileDescriptor = + context.getContentResolver().openFileDescriptor(getUri(), "r")) { + if (parcelFileDescriptor == null) { + return Long.MAX_VALUE; + } + final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + stat = Os.fstatvfs(fileDescriptor); + } + } + + // this is the same formula used inside the FsStat class + return stat.f_bavail * stat.f_frsize; + } catch (final Throwable e) { + // ignore any error + Log.e(TAG, "Could not get free storage space", e); + return Long.MAX_VALUE; + } + } + /** * Only using Java I/O. Creates the directory named by this abstract pathname, including any * necessary but nonexistent parent directories. diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index 07d0f516d..066d5f570 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -27,6 +27,7 @@ import android.util.Log; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.text.HtmlCompat; import androidx.preference.PreferenceManager; @@ -113,14 +114,14 @@ public final class ExtractorHelper { public static Single getStreamInfo(final int serviceId, final String url, final boolean forceLoad) { checkServiceId(serviceId); - return checkCache(forceLoad, serviceId, url, InfoItem.InfoType.STREAM, + return checkCache(forceLoad, serviceId, url, InfoCache.Type.STREAM, Single.fromCallable(() -> StreamInfo.getInfo(NewPipe.getService(serviceId), url))); } public static Single getChannelInfo(final int serviceId, final String url, final boolean forceLoad) { checkServiceId(serviceId); - return checkCache(forceLoad, serviceId, url, InfoItem.InfoType.CHANNEL, + return checkCache(forceLoad, serviceId, url, InfoCache.Type.CHANNEL, Single.fromCallable(() -> ChannelInfo.getInfo(NewPipe.getService(serviceId), url))); } @@ -130,7 +131,7 @@ public final class ExtractorHelper { final boolean forceLoad) { checkServiceId(serviceId); return checkCache(forceLoad, serviceId, - listLinkHandler.getUrl(), InfoItem.InfoType.CHANNEL, + listLinkHandler.getUrl(), InfoCache.Type.CHANNEL_TAB, Single.fromCallable(() -> ChannelTabInfo.getInfo(NewPipe.getService(serviceId), listLinkHandler))); } @@ -145,10 +146,11 @@ public final class ExtractorHelper { listLinkHandler, nextPage)); } - public static Single getCommentsInfo(final int serviceId, final String url, + public static Single getCommentsInfo(final int serviceId, + final String url, final boolean forceLoad) { checkServiceId(serviceId); - return checkCache(forceLoad, serviceId, url, InfoItem.InfoType.COMMENT, + return checkCache(forceLoad, serviceId, url, InfoCache.Type.COMMENTS, Single.fromCallable(() -> CommentsInfo.getInfo(NewPipe.getService(serviceId), url))); } @@ -162,11 +164,20 @@ public final class ExtractorHelper { CommentsInfo.getMoreItems(NewPipe.getService(serviceId), info, nextPage)); } + public static Single> getMoreCommentItems( + final int serviceId, + final String url, + final Page nextPage) { + checkServiceId(serviceId); + return Single.fromCallable(() -> + CommentsInfo.getMoreItems(NewPipe.getService(serviceId), url, nextPage)); + } + public static Single getPlaylistInfo(final int serviceId, final String url, final boolean forceLoad) { checkServiceId(serviceId); - return checkCache(forceLoad, serviceId, url, InfoItem.InfoType.PLAYLIST, + return checkCache(forceLoad, serviceId, url, InfoCache.Type.PLAYLIST, Single.fromCallable(() -> PlaylistInfo.getInfo(NewPipe.getService(serviceId), url))); } @@ -179,9 +190,10 @@ public final class ExtractorHelper { PlaylistInfo.getMoreItems(NewPipe.getService(serviceId), url, nextPage)); } - public static Single getKioskInfo(final int serviceId, final String url, + public static Single getKioskInfo(final int serviceId, + final String url, final boolean forceLoad) { - return checkCache(forceLoad, serviceId, url, InfoItem.InfoType.PLAYLIST, + return checkCache(forceLoad, serviceId, url, InfoCache.Type.KIOSK, Single.fromCallable(() -> KioskInfo.getInfo(NewPipe.getService(serviceId), url))); } @@ -193,7 +205,7 @@ public final class ExtractorHelper { } /*////////////////////////////////////////////////////////////////////////// - // Utils + // Cache //////////////////////////////////////////////////////////////////////////*/ /** @@ -205,24 +217,25 @@ public final class ExtractorHelper { * @param forceLoad whether to force loading from the network instead of from the cache * @param serviceId the service to load from * @param url the URL to load - * @param infoType the {@link InfoItem.InfoType} of the item + * @param cacheType the {@link InfoCache.Type} of the item * @param loadFromNetwork the {@link Single} to load the item from the network * @return a {@link Single} that loads the item */ private static Single checkCache(final boolean forceLoad, - final int serviceId, final String url, - final InfoItem.InfoType infoType, - final Single loadFromNetwork) { + final int serviceId, + @NonNull final String url, + @NonNull final InfoCache.Type cacheType, + @NonNull final Single loadFromNetwork) { checkServiceId(serviceId); final Single actualLoadFromNetwork = loadFromNetwork - .doOnSuccess(info -> CACHE.putInfo(serviceId, url, info, infoType)); + .doOnSuccess(info -> CACHE.putInfo(serviceId, url, info, cacheType)); final Single load; if (forceLoad) { - CACHE.removeInfo(serviceId, url, infoType); + CACHE.removeInfo(serviceId, url, cacheType); load = actualLoadFromNetwork; } else { - load = Maybe.concat(ExtractorHelper.loadFromCache(serviceId, url, infoType), + load = Maybe.concat(ExtractorHelper.loadFromCache(serviceId, url, cacheType), actualLoadFromNetwork.toMaybe()) .firstElement() // Take the first valid .toSingle(); @@ -237,15 +250,17 @@ public final class ExtractorHelper { * @param the item type's class that extends {@link Info} * @param serviceId the service to load from * @param url the URL to load - * @param infoType the {@link InfoItem.InfoType} of the item + * @param cacheType the {@link InfoCache.Type} of the item * @return a {@link Single} that loads the item */ - private static Maybe loadFromCache(final int serviceId, final String url, - final InfoItem.InfoType infoType) { + private static Maybe loadFromCache( + final int serviceId, + @NonNull final String url, + @NonNull final InfoCache.Type cacheType) { checkServiceId(serviceId); return Maybe.defer(() -> { //noinspection unchecked - final I info = (I) CACHE.getFromKey(serviceId, url, infoType); + final I info = (I) CACHE.getFromKey(serviceId, url, cacheType); if (MainActivity.DEBUG) { Log.d(TAG, "loadFromCache() called, info > " + info); } @@ -259,11 +274,17 @@ public final class ExtractorHelper { }); } - public static boolean isCached(final int serviceId, final String url, - final InfoItem.InfoType infoType) { - return null != loadFromCache(serviceId, url, infoType).blockingGet(); + public static boolean isCached(final int serviceId, + @NonNull final String url, + @NonNull final InfoCache.Type cacheType) { + return null != loadFromCache(serviceId, url, cacheType).blockingGet(); } + + /*////////////////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////////////////*/ + /** * Formats the text contained in the meta info list as HTML and puts it into the text view, * while also making the separator visible. If the list is null or empty, or the user chose not diff --git a/app/src/main/java/org/schabi/newpipe/util/InfoCache.java b/app/src/main/java/org/schabi/newpipe/util/InfoCache.java index a07f05828..b9c91f8a5 100644 --- a/app/src/main/java/org/schabi/newpipe/util/InfoCache.java +++ b/app/src/main/java/org/schabi/newpipe/util/InfoCache.java @@ -27,7 +27,6 @@ import androidx.collection.LruCache; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.extractor.Info; -import org.schabi.newpipe.extractor.InfoItem; import java.util.Map; @@ -48,14 +47,27 @@ public final class InfoCache { // no instance } + /** + * Identifies the type of {@link Info} to put into the cache. + */ + public enum Type { + STREAM, + CHANNEL, + CHANNEL_TAB, + COMMENTS, + PLAYLIST, + KIOSK, + } + public static InfoCache getInstance() { return INSTANCE; } @NonNull - private static String keyOf(final int serviceId, @NonNull final String url, - @NonNull final InfoItem.InfoType infoType) { - return serviceId + url + infoType.toString(); + private static String keyOf(final int serviceId, + @NonNull final String url, + @NonNull final Type cacheType) { + return serviceId + ":" + cacheType.ordinal() + ":" + url; } private static void removeStaleCache() { @@ -83,19 +95,22 @@ public final class InfoCache { } @Nullable - public Info getFromKey(final int serviceId, @NonNull final String url, - @NonNull final InfoItem.InfoType infoType) { + public Info getFromKey(final int serviceId, + @NonNull final String url, + @NonNull final Type cacheType) { if (DEBUG) { Log.d(TAG, "getFromKey() called with: " + "serviceId = [" + serviceId + "], url = [" + url + "]"); } synchronized (LRU_CACHE) { - return getInfo(keyOf(serviceId, url, infoType)); + return getInfo(keyOf(serviceId, url, cacheType)); } } - public void putInfo(final int serviceId, @NonNull final String url, @NonNull final Info info, - @NonNull final InfoItem.InfoType infoType) { + public void putInfo(final int serviceId, + @NonNull final String url, + @NonNull final Info info, + @NonNull final Type cacheType) { if (DEBUG) { Log.d(TAG, "putInfo() called with: info = [" + info + "]"); } @@ -103,18 +118,19 @@ public final class InfoCache { final long expirationMillis = ServiceHelper.getCacheExpirationMillis(info.getServiceId()); synchronized (LRU_CACHE) { final CacheData data = new CacheData(info, expirationMillis); - LRU_CACHE.put(keyOf(serviceId, url, infoType), data); + LRU_CACHE.put(keyOf(serviceId, url, cacheType), data); } } - public void removeInfo(final int serviceId, @NonNull final String url, - @NonNull final InfoItem.InfoType infoType) { + public void removeInfo(final int serviceId, + @NonNull final String url, + @NonNull final Type cacheType) { if (DEBUG) { Log.d(TAG, "removeInfo() called with: " + "serviceId = [" + serviceId + "], url = [" + url + "]"); } synchronized (LRU_CACHE) { - LRU_CACHE.remove(keyOf(serviceId, url, infoType)); + LRU_CACHE.remove(keyOf(serviceId, url, cacheType)); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index 71071d997..f1904565d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -643,6 +643,7 @@ public final class ListHelper { context.getString(R.string.best_resolution_key), defaultFormat, videoStreams); } + @Nullable private static MediaFormat getDefaultFormat(@NonNull final Context context, @StringRes final int defaultFormatKey, @StringRes final int defaultFormatValueKey) { @@ -651,18 +652,14 @@ public final class ListHelper { final String defaultFormat = context.getString(defaultFormatValueKey); final String defaultFormatString = preferences.getString( - context.getString(defaultFormatKey), defaultFormat); + context.getString(defaultFormatKey), + defaultFormat + ); - MediaFormat defaultMediaFormat = getMediaFormatFromKey(context, defaultFormatString); - if (defaultMediaFormat == null) { - preferences.edit().putString(context.getString(defaultFormatKey), defaultFormat) - .apply(); - defaultMediaFormat = getMediaFormatFromKey(context, defaultFormat); - } - - return defaultMediaFormat; + return getMediaFormatFromKey(context, defaultFormatString); } + @Nullable private static MediaFormat getMediaFormatFromKey(@NonNull final Context context, @NonNull final String formatKey) { MediaFormat format = null; @@ -877,6 +874,7 @@ public final class ListHelper { return Comparator.comparing(AudioStream::getAudioLocale, Comparator.nullsLast( Comparator.comparing(locale -> locale.getDisplayName(appLoc)))) - .thenComparing(AudioStream::getAudioTrackType); + .thenComparing(AudioStream::getAudioTrackType, Comparator.nullsLast( + Comparator.naturalOrder())); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index c4034252d..bc113e8f8 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.util; +import static org.schabi.newpipe.MainActivity.DEBUG; + import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; @@ -22,6 +24,7 @@ import org.ocpsoft.prettytime.units.Decade; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.localization.ContentCountry; +import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioTrackType; @@ -82,7 +85,7 @@ public final class Localization { .fromLocale(getPreferredLocale(context)); } - public static ContentCountry getPreferredContentCountry(final Context context) { + public static ContentCountry getPreferredContentCountry(@NonNull final Context context) { final String contentCountry = PreferenceManager.getDefaultSharedPreferences(context) .getString(context.getString(R.string.content_country_key), context.getString(R.string.default_localization_key)); @@ -92,41 +95,43 @@ public final class Localization { return new ContentCountry(contentCountry); } - public static Locale getPreferredLocale(final Context context) { + public static Locale getPreferredLocale(@NonNull final Context context) { return getLocaleFromPrefs(context, R.string.content_language_key); } - public static Locale getAppLocale(final Context context) { + public static Locale getAppLocale(@NonNull final Context context) { return getLocaleFromPrefs(context, R.string.app_language_key); } - public static String localizeNumber(final Context context, final long number) { + public static String localizeNumber(@NonNull final Context context, final long number) { return localizeNumber(context, (double) number); } - public static String localizeNumber(final Context context, final double number) { + public static String localizeNumber(@NonNull final Context context, final double number) { final NumberFormat nf = NumberFormat.getInstance(getAppLocale(context)); return nf.format(number); } - public static String formatDate(final OffsetDateTime offsetDateTime, final Context context) { + public static String formatDate(@NonNull final Context context, + @NonNull final OffsetDateTime offsetDateTime) { return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(getAppLocale(context)).format(offsetDateTime .atZoneSameInstant(ZoneId.systemDefault())); } @SuppressLint("StringFormatInvalid") - public static String localizeUploadDate(final Context context, - final OffsetDateTime offsetDateTime) { - return context.getString(R.string.upload_date_text, formatDate(offsetDateTime, context)); + public static String localizeUploadDate(@NonNull final Context context, + @NonNull final OffsetDateTime offsetDateTime) { + return context.getString(R.string.upload_date_text, formatDate(context, offsetDateTime)); } - public static String localizeViewCount(final Context context, final long viewCount) { + public static String localizeViewCount(@NonNull final Context context, final long viewCount) { return getQuantity(context, R.plurals.views, R.string.no_views, viewCount, localizeNumber(context, viewCount)); } - public static String localizeStreamCount(final Context context, final long streamCount) { + public static String localizeStreamCount(@NonNull final Context context, + final long streamCount) { switch ((int) streamCount) { case (int) ListExtractor.ITEM_COUNT_UNKNOWN: return ""; @@ -140,7 +145,8 @@ public final class Localization { } } - public static String localizeStreamCountMini(final Context context, final long streamCount) { + public static String localizeStreamCountMini(@NonNull final Context context, + final long streamCount) { switch ((int) streamCount) { case (int) ListExtractor.ITEM_COUNT_UNKNOWN: return ""; @@ -153,12 +159,13 @@ public final class Localization { } } - public static String localizeWatchingCount(final Context context, final long watchingCount) { + public static String localizeWatchingCount(@NonNull final Context context, + final long watchingCount) { return getQuantity(context, R.plurals.watching, R.string.no_one_watching, watchingCount, localizeNumber(context, watchingCount)); } - public static String shortCount(final Context context, final long count) { + public static String shortCount(@NonNull final Context context, final long count) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return CompactDecimalFormat.getInstance(getAppLocale(context), CompactDecimalFormat.CompactStyle.SHORT).format(count); @@ -179,37 +186,79 @@ public final class Localization { } } - public static String listeningCount(final Context context, final long listeningCount) { + public static String listeningCount(@NonNull final Context context, final long listeningCount) { return getQuantity(context, R.plurals.listening, R.string.no_one_listening, listeningCount, shortCount(context, listeningCount)); } - public static String shortWatchingCount(final Context context, final long watchingCount) { + public static String shortWatchingCount(@NonNull final Context context, + final long watchingCount) { return getQuantity(context, R.plurals.watching, R.string.no_one_watching, watchingCount, shortCount(context, watchingCount)); } - public static String shortViewCount(final Context context, final long viewCount) { + public static String shortViewCount(@NonNull final Context context, final long viewCount) { return getQuantity(context, R.plurals.views, R.string.no_views, viewCount, shortCount(context, viewCount)); } - public static String shortSubscriberCount(final Context context, final long subscriberCount) { + public static String shortSubscriberCount(@NonNull final Context context, + final long subscriberCount) { return getQuantity(context, R.plurals.subscribers, R.string.no_subscribers, subscriberCount, shortCount(context, subscriberCount)); } - public static String downloadCount(final Context context, final int downloadCount) { + public static String downloadCount(@NonNull final Context context, final int downloadCount) { return getQuantity(context, R.plurals.download_finished_notification, 0, downloadCount, shortCount(context, downloadCount)); } - public static String deletedDownloadCount(final Context context, final int deletedCount) { + public static String deletedDownloadCount(@NonNull final Context context, + final int deletedCount) { return getQuantity(context, R.plurals.deleted_downloads_toast, 0, deletedCount, shortCount(context, deletedCount)); } + public static String replyCount(@NonNull final Context context, final int replyCount) { + return getQuantity(context, R.plurals.replies, 0, replyCount, + String.valueOf(replyCount)); + } + + /** + * @param context the Android context + * @param likeCount the like count, possibly negative if unknown + * @return if {@code likeCount} is smaller than {@code 0}, the string {@code "-"}, otherwise + * the result of calling {@link #shortCount(Context, long)} on the like count + */ + public static String likeCount(@NonNull final Context context, final int likeCount) { + if (likeCount < 0) { + return "-"; + } else { + return shortCount(context, likeCount); + } + } + + /** + * Get a readable text for a duration in the format {@code days:hours:minutes:seconds}. + * Prepended zeros are removed. + * @param duration the duration in seconds + * @return a formatted duration String or {@code 0:00} if the duration is zero. + */ public static String getDurationString(final long duration) { + return getDurationString(duration, true, false); + } + + /** + * Get a readable text for a duration in the format {@code days:hours:minutes:seconds+}. + * Prepended zeros are removed. If the given duration is incomplete, a plus is appended to the + * duration string. + * @param duration the duration in seconds + * @param isDurationComplete whether the given duration is complete or whether info is missing + * @param showDurationPrefix whether the duration-prefix shall be shown + * @return a formatted duration String or {@code 0:00} if the duration is zero. + */ + public static String getDurationString(final long duration, final boolean isDurationComplete, + final boolean showDurationPrefix) { final String output; final long days = duration / (24 * 60 * 60L); /* greater than a day */ @@ -227,7 +276,9 @@ public final class Localization { } else { output = String.format(Locale.US, "%d:%02d", minutes, seconds); } - return output; + final String durationPrefix = showDurationPrefix ? "⏱ " : ""; + final String durationPostfix = isDurationComplete ? "" : "+"; + return durationPrefix + output + durationPostfix; } /** @@ -241,7 +292,8 @@ public final class Localization { * @return duration in a human readable string. */ @NonNull - public static String localizeDuration(final Context context, final int durationInSecs) { + public static String localizeDuration(@NonNull final Context context, + final int durationInSecs) { if (durationInSecs < 0) { throw new IllegalArgumentException("duration can not be negative"); } @@ -278,7 +330,7 @@ public final class Localization { * @param track an {@link AudioStream} of the track * @return the localized name of the audio track */ - public static String audioTrackName(final Context context, final AudioStream track) { + public static String audioTrackName(@NonNull final Context context, final AudioStream track) { final String name; if (track.getAudioLocale() != null) { name = track.getAudioLocale().getDisplayLanguage(getAppLocale(context)); @@ -298,7 +350,8 @@ public final class Localization { } @Nullable - private static String audioTrackType(final Context context, final AudioTrackType trackType) { + private static String audioTrackType(@NonNull final Context context, + final AudioTrackType trackType) { switch (trackType) { case ORIGINAL: return context.getString(R.string.audio_track_type_original); @@ -314,20 +367,45 @@ public final class Localization { // Pretty Time //////////////////////////////////////////////////////////////////////////*/ - public static void initPrettyTime(final PrettyTime time) { + public static void initPrettyTime(@NonNull final PrettyTime time) { prettyTime = time; // Do not use decades as YouTube doesn't either. prettyTime.removeUnit(Decade.class); } - public static PrettyTime resolvePrettyTime(final Context context) { + public static PrettyTime resolvePrettyTime(@NonNull final Context context) { return new PrettyTime(getAppLocale(context)); } - public static String relativeTime(final OffsetDateTime offsetDateTime) { + public static String relativeTime(@NonNull final OffsetDateTime offsetDateTime) { return prettyTime.formatUnrounded(offsetDateTime); } + /** + * @param context the Android context; if {@code null} then even if in debug mode and the + * setting is enabled, {@code textual} will not be shown next to {@code parsed} + * @param parsed the textual date or time ago parsed by NewPipeExtractor, or {@code null} if + * the extractor could not parse it + * @param textual the original textual date or time ago string as provided by services + * @return {@link #relativeTime(OffsetDateTime)} is used if {@code parsed != null}, otherwise + * {@code textual} is returned. If in debug mode, {@code context != null}, + * {@code parsed != null} and the relevant setting is enabled, {@code textual} will + * be appended to the returned string for debugging purposes. + */ + public static String relativeTimeOrTextual(@Nullable final Context context, + @Nullable final DateWrapper parsed, + final String textual) { + if (parsed == null) { + return textual; + } else if (DEBUG && context != null && PreferenceManager + .getDefaultSharedPreferences(context) + .getBoolean(context.getString(R.string.show_original_time_ago_key), false)) { + return relativeTime(parsed.offsetDateTime()) + " (" + textual + ")"; + } else { + return relativeTime(parsed.offsetDateTime()); + } + } + public static void assureCorrectAppLanguage(final Context c) { final Resources res = c.getResources(); final DisplayMetrics dm = res.getDisplayMetrics(); @@ -336,7 +414,8 @@ public final class Localization { res.updateConfiguration(conf, dm); } - private static Locale getLocaleFromPrefs(final Context context, @StringRes final int prefKey) { + private static Locale getLocaleFromPrefs(@NonNull final Context context, + @StringRes final int prefKey) { final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); final String defaultKey = context.getString(R.string.default_localization_key); final String languageCode = sp.getString(context.getString(prefKey), defaultKey); @@ -352,8 +431,10 @@ public final class Localization { return new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue(); } - private static String getQuantity(final Context context, @PluralsRes final int pluralId, - @StringRes final int zeroCaseStringId, final long count, + private static String getQuantity(@NonNull final Context context, + @PluralsRes final int pluralId, + @StringRes final int zeroCaseStringId, + final long count, final String formattedCount) { if (count == 0) { return context.getString(zeroCaseStringId); diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index b0d7dcf73..5dee32371 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.util; +import static android.text.TextUtils.isEmpty; import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams; import android.annotation.SuppressLint; @@ -17,6 +18,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -29,8 +31,10 @@ import org.schabi.newpipe.RouterActivity; import org.schabi.newpipe.about.AboutActivity; import org.schabi.newpipe.database.feed.model.FeedGroupEntity; import org.schabi.newpipe.download.DownloadActivity; +import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.DeliveryMethod; @@ -41,6 +45,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; +import org.schabi.newpipe.fragments.list.comments.CommentRepliesFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; @@ -476,6 +481,35 @@ public final class NavigationHelper { item.getServiceId(), uploaderUrl, item.getUploaderName()); } + /** + * Opens the comment author channel fragment, if the {@link CommentsInfoItem#getUploaderUrl()} + * of {@code comment} is non-null. Shows a UI-error snackbar if something goes wrong. + * + * @param activity the activity with the fragment manager and in which to show the snackbar + * @param comment the comment whose uploader/author will be opened + */ + public static void openCommentAuthorIfPresent(@NonNull final FragmentActivity activity, + @NonNull final CommentsInfoItem comment) { + if (isEmpty(comment.getUploaderUrl())) { + return; + } + try { + openChannelFragment(activity.getSupportFragmentManager(), comment.getServiceId(), + comment.getUploaderUrl(), comment.getUploaderName()); + } catch (final Exception e) { + ErrorUtil.showUiErrorSnackbar(activity, "Opening channel fragment", e); + } + } + + public static void openCommentRepliesFragment(@NonNull final FragmentActivity activity, + @NonNull final CommentsInfoItem comment) { + defaultTransaction(activity.getSupportFragmentManager()) + .replace(R.id.fragment_holder, new CommentRepliesFragment(comment), + CommentRepliesFragment.TAG) + .addToBackStack(CommentRepliesFragment.TAG) + .commit(); + } + public static void openPlaylistFragment(final FragmentManager fragmentManager, final int serviceId, final String url, @NonNull final String name) { diff --git a/app/src/main/java/org/schabi/newpipe/util/RelatedItemInfo.java b/app/src/main/java/org/schabi/newpipe/util/RelatedItemInfo.java deleted file mode 100644 index f96bb0d54..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/RelatedItemInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.schabi.newpipe.util; - -import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.ListInfo; -import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; -import org.schabi.newpipe.extractor.stream.StreamInfo; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class RelatedItemInfo extends ListInfo { - public RelatedItemInfo(final int serviceId, final ListLinkHandler listUrlIdHandler, - final String name) { - super(serviceId, listUrlIdHandler, name); - } - - public static RelatedItemInfo getInfo(final StreamInfo info) { - final ListLinkHandler handler = new ListLinkHandler( - info.getOriginalUrl(), info.getUrl(), info.getId(), Collections.emptyList(), null); - final RelatedItemInfo relatedItemInfo = new RelatedItemInfo( - info.getServiceId(), handler, info.getName()); - final List relatedItems = new ArrayList<>(info.getRelatedItems()); - relatedItemInfo.setRelatedItems(relatedItems); - return relatedItemInfo; - } -} diff --git a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt index 5a54b29d2..3ea19fa4f 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt @@ -1,97 +1,39 @@ package org.schabi.newpipe.util import android.content.pm.PackageManager -import android.content.pm.Signature import androidx.core.content.pm.PackageInfoCompat import org.schabi.newpipe.App import org.schabi.newpipe.error.ErrorInfo import org.schabi.newpipe.error.ErrorUtil.Companion.createNotification import org.schabi.newpipe.error.UserAction -import java.security.MessageDigest -import java.security.NoSuchAlgorithmException -import java.security.cert.CertificateEncodingException -import java.security.cert.CertificateException -import java.security.cert.CertificateFactory -import java.security.cert.X509Certificate import java.time.Instant import java.time.ZonedDateTime import java.time.format.DateTimeFormatter object ReleaseVersionUtil { // Public key of the certificate that is used in NewPipe release versions - private const val RELEASE_CERT_PUBLIC_KEY_SHA1 = - "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15" + private const val RELEASE_CERT_PUBLIC_KEY_SHA256 = + "cb84069bd68116bafae5ee4ee5b08a567aa6d898404e7cb12f9e756df5cf5cab" - @JvmStatic - fun isReleaseApk(): Boolean { - return certificateSHA1Fingerprint == RELEASE_CERT_PUBLIC_KEY_SHA1 - } - - /** - * Method to get the APK's SHA1 key. See https://stackoverflow.com/questions/9293019/#22506133. - * - * @return String with the APK's SHA1 fingerprint in hexadecimal - */ - private val certificateSHA1Fingerprint: String - get() { - val app = App.getApp() - val signatures: List = try { - PackageInfoCompat.getSignatures(app.packageManager, app.packageName) - } catch (e: PackageManager.NameNotFoundException) { - showRequestError(app, e, "Could not find package info") - return "" - } - if (signatures.isEmpty()) { - return "" - } - val x509cert = try { - val cf = CertificateFactory.getInstance("X509") - cf.generateCertificate(signatures[0].toByteArray().inputStream()) as X509Certificate - } catch (e: CertificateException) { - showRequestError(app, e, "Certificate error") - return "" - } - - return try { - val md = MessageDigest.getInstance("SHA1") - val publicKey = md.digest(x509cert.encoded) - byte2HexFormatted(publicKey) - } catch (e: NoSuchAlgorithmException) { - showRequestError(app, e, "Could not retrieve SHA1 key") - "" - } catch (e: CertificateEncodingException) { - showRequestError(app, e, "Could not retrieve SHA1 key") - "" - } - } - - private fun byte2HexFormatted(arr: ByteArray): String { - val str = StringBuilder(arr.size * 2) - for (i in arr.indices) { - var h = Integer.toHexString(arr[i].toInt()) - val l = h.length - if (l == 1) { - h = "0$h" - } - if (l > 2) { - h = h.substring(l - 2, l) - } - str.append(h.uppercase()) - if (i < arr.size - 1) { - str.append(':') - } - } - return str.toString() - } - - private fun showRequestError(app: App, e: Exception, request: String) { - createNotification( - app, ErrorInfo(e, UserAction.CHECK_FOR_NEW_APP_VERSION, request) + @OptIn(ExperimentalStdlibApi::class) + val isReleaseApk by lazy { + @Suppress("NewApi") + val certificates = mapOf( + RELEASE_CERT_PUBLIC_KEY_SHA256.hexToByteArray() to PackageManager.CERT_INPUT_SHA256 ) + val app = App.getApp() + try { + PackageInfoCompat.hasSignatures(app.packageManager, app.packageName, certificates, false) + } catch (e: PackageManager.NameNotFoundException) { + createNotification( + app, ErrorInfo(e, UserAction.CHECK_FOR_NEW_APP_VERSION, "Could not find package info") + ) + false + } } fun isLastUpdateCheckExpired(expiry: Long): Boolean { - return Instant.ofEpochSecond(expiry).isBefore(Instant.now()) + return Instant.ofEpochSecond(expiry) < Instant.now() } /** diff --git a/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java b/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java index 75d9a3892..69dc697fe 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java @@ -11,7 +11,6 @@ import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.util.StreamItemAdapter.StreamInfoWrapper; -import java.util.Comparator; import java.util.List; public class SecondaryStreamHelper { @@ -43,42 +42,27 @@ public class SecondaryStreamHelper { @NonNull final List audioStreams, @NonNull final VideoStream videoStream) { final MediaFormat mediaFormat = videoStream.getFormat(); - if (mediaFormat == null) { + + if (mediaFormat == MediaFormat.WEBM) { + return audioStreams + .stream() + .filter(audioStream -> audioStream.getFormat() == MediaFormat.WEBMA + || audioStream.getFormat() == MediaFormat.WEBMA_OPUS) + .max(ListHelper.getAudioFormatComparator(MediaFormat.WEBMA, + ListHelper.isLimitingDataUsage(context))) + .orElse(null); + + } else if (mediaFormat == MediaFormat.MPEG_4) { + return audioStreams + .stream() + .filter(audioStream -> audioStream.getFormat() == MediaFormat.M4A) + .max(ListHelper.getAudioFormatComparator(MediaFormat.M4A, + ListHelper.isLimitingDataUsage(context))) + .orElse(null); + + } else { return null; } - - switch (mediaFormat) { - case WEBM: - case MPEG_4: // Is MPEG-4 DASH? - break; - default: - return null; - } - - final boolean m4v = mediaFormat == MediaFormat.MPEG_4; - final boolean isLimitingDataUsage = ListHelper.isLimitingDataUsage(context); - - Comparator comparator = ListHelper.getAudioFormatComparator( - m4v ? MediaFormat.M4A : MediaFormat.WEBMA, isLimitingDataUsage); - int preferredAudioStreamIndex = ListHelper.getAudioIndexByHighestRank( - audioStreams, comparator); - - if (preferredAudioStreamIndex == -1) { - if (m4v) { - return null; - } - - comparator = ListHelper.getAudioFormatComparator( - MediaFormat.WEBMA_OPUS, isLimitingDataUsage); - preferredAudioStreamIndex = ListHelper.getAudioIndexByHighestRank( - audioStreams, comparator); - - if (preferredAudioStreamIndex == -1) { - return null; - } - } - - return audioStreams.get(preferredAudioStreamIndex); } public T getStream() { diff --git a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java index acd019ba0..c712157b3 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java @@ -144,6 +144,19 @@ public final class ServiceHelper { .orElse(""); } + /** + * @param serviceId the id of the service + * @return the service corresponding to the provided id + * @throws java.util.NoSuchElementException if there is no service with the provided id + */ + @NonNull + public static StreamingService getServiceById(final int serviceId) { + return ServiceList.all().stream() + .filter(s -> s.getServiceId() == serviceId) + .findFirst() + .orElseThrow(); + } + public static void setSelectedServiceId(final Context context, final int serviceId) { String serviceName; try { diff --git a/app/src/main/java/org/schabi/newpipe/util/StateSaver.java b/app/src/main/java/org/schabi/newpipe/util/StateSaver.java index 91dc5f35b..61fdb602f 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StateSaver.java +++ b/app/src/main/java/org/schabi/newpipe/util/StateSaver.java @@ -27,6 +27,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.os.BundleCompat; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.MainActivity; @@ -82,7 +83,8 @@ public final class StateSaver { return null; } - final SavedState savedState = outState.getParcelable(KEY_SAVED_STATE); + final SavedState savedState = BundleCompat.getParcelable( + outState, KEY_SAVED_STATE, SavedState.class); if (savedState == null) { return null; } diff --git a/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSavable.java b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSavable.java new file mode 100644 index 000000000..acc515dd6 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSavable.java @@ -0,0 +1,15 @@ +package org.schabi.newpipe.util.debounce; + +import org.schabi.newpipe.error.ErrorInfo; + +public interface DebounceSavable { + + /** + * Execute operations to save the data.
+ * Must set {@link DebounceSaver#setIsModified(boolean)} false in this method manually + * after the data has been saved. + */ + void saveImmediate(); + + void showError(ErrorInfo errorInfo); +} diff --git a/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java new file mode 100644 index 000000000..5bd5cdd55 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java @@ -0,0 +1,81 @@ +package org.schabi.newpipe.util.debounce; + +import org.schabi.newpipe.error.ErrorInfo; +import org.schabi.newpipe.error.UserAction; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.subjects.PublishSubject; + +public class DebounceSaver { + + private final long saveDebounceMillis; + + private final PublishSubject debouncedSaveSignal; + + private final DebounceSavable debounceSavable; + + // Has the object been modified + private final AtomicBoolean isModified; + + // Default 10 seconds + private static final long DEFAULT_SAVE_DEBOUNCE_MILLIS = 10000; + + + /** + * Creates a new {@code DebounceSaver}. + * + * @param saveDebounceMillis Save the object milliseconds later after the last change + * occurred. + * @param debounceSavable The object containing data to be saved. + */ + public DebounceSaver(final long saveDebounceMillis, final DebounceSavable debounceSavable) { + this.saveDebounceMillis = saveDebounceMillis; + debouncedSaveSignal = PublishSubject.create(); + this.debounceSavable = debounceSavable; + this.isModified = new AtomicBoolean(); + } + + /** + * Creates a new {@code DebounceSaver}. Save the object 10 seconds later after the last change + * occurred. + * + * @param debounceSavable The object containing data to be saved. + */ + public DebounceSaver(final DebounceSavable debounceSavable) { + this(DEFAULT_SAVE_DEBOUNCE_MILLIS, debounceSavable); + } + + public boolean getIsModified() { + return isModified.get(); + } + + public void setNoChangesToSave() { + isModified.set(false); + } + + public PublishSubject getDebouncedSaveSignal() { + return debouncedSaveSignal; + } + + public Disposable getDebouncedSaver() { + return debouncedSaveSignal + .debounce(saveDebounceMillis, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(ignored -> debounceSavable.saveImmediate(), throwable -> + debounceSavable.showError(new ErrorInfo(throwable, + UserAction.SOMETHING_ELSE, "Debounced saver"))); + } + + public void setHasChangesToSave() { + if (isModified == null || debouncedSaveSignal == null) { + return; + } + + isModified.set(true); + debouncedSaveSignal.onNext(System.currentTimeMillis()); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java b/app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java new file mode 100644 index 000000000..184b73304 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java @@ -0,0 +1,193 @@ +package org.schabi.newpipe.util.text; + +import android.graphics.Paint; +import android.text.Layout; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.text.HtmlCompat; + +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.stream.Description; + +import java.util.function.Consumer; + + +import io.reactivex.rxjava3.disposables.CompositeDisposable; + +/** + *

Class to ellipsize text inside a {@link TextView}.

+ * This class provides all utils to automatically ellipsize and expand a text + */ +public final class TextEllipsizer { + private static final int EXPANDED_LINES = Integer.MAX_VALUE; + private static final String ELLIPSIS = "…"; + + @NonNull private final CompositeDisposable disposable = new CompositeDisposable(); + + @NonNull private final TextView view; + private final int maxLines; + @NonNull private Description content; + @Nullable private StreamingService streamingService; + @Nullable private String streamUrl; + private boolean isEllipsized = false; + @Nullable private Boolean canBeEllipsized = null; + + @NonNull private final Paint paintAtContentSize = new Paint(); + private final float ellipsisWidthPx; + @Nullable private Consumer stateChangeListener = null; + @Nullable private Consumer onContentChanged; + + public TextEllipsizer(@NonNull final TextView view, + final int maxLines, + @Nullable final StreamingService streamingService) { + this.view = view; + this.maxLines = maxLines; + this.content = Description.EMPTY_DESCRIPTION; + this.streamingService = streamingService; + + paintAtContentSize.setTextSize(view.getTextSize()); + ellipsisWidthPx = paintAtContentSize.measureText(ELLIPSIS); + } + + public void setOnContentChanged(@Nullable final Consumer onContentChanged) { + this.onContentChanged = onContentChanged; + } + + public void setContent(@NonNull final Description content) { + this.content = content; + canBeEllipsized = null; + linkifyContentView(v -> { + final int currentMaxLines = view.getMaxLines(); + view.setMaxLines(EXPANDED_LINES); + canBeEllipsized = view.getLineCount() > maxLines; + view.setMaxLines(currentMaxLines); + if (onContentChanged != null) { + onContentChanged.accept(canBeEllipsized); + } + }); + } + + public void setStreamUrl(@Nullable final String streamUrl) { + this.streamUrl = streamUrl; + } + + public void setStreamingService(@NonNull final StreamingService streamingService) { + this.streamingService = streamingService; + } + + /** + * Expand the {@link TextEllipsizer#content} to its full length. + */ + public void expand() { + view.setMaxLines(EXPANDED_LINES); + linkifyContentView(v -> isEllipsized = false); + } + + /** + * Shorten the {@link TextEllipsizer#content} to the given number of + * {@link TextEllipsizer#maxLines maximum lines} and add trailing '{@code …}' + * if the text was shorted. + */ + public void ellipsize() { + // expand text to see whether it is necessary to ellipsize the text + view.setMaxLines(EXPANDED_LINES); + linkifyContentView(v -> { + final CharSequence charSeqText = view.getText(); + if (charSeqText != null && view.getLineCount() > maxLines) { + // Note that converting to String removes spans (i.e. links), but that's something + // we actually want since when the text is ellipsized we want all clicks on the + // comment to expand the comment, not to open links. + final String text = charSeqText.toString(); + + final Layout layout = view.getLayout(); + final float lineWidth = layout.getLineWidth(maxLines - 1); + final float layoutWidth = layout.getWidth(); + final int lineStart = layout.getLineStart(maxLines - 1); + final int lineEnd = layout.getLineEnd(maxLines - 1); + + // remove characters up until there is enough space for the ellipsis + // (also summing 2 more pixels, just to be sure to avoid float rounding errors) + int end = lineEnd; + float removedCharactersWidth = 0.0f; + while (lineWidth - removedCharactersWidth + ellipsisWidthPx + 2.0f > layoutWidth + && end >= lineStart) { + end -= 1; + // recalculate each time to account for ligatures or other similar things + removedCharactersWidth = paintAtContentSize.measureText( + text.substring(end, lineEnd)); + } + + // remove trailing spaces and newlines + while (end > 0 && Character.isWhitespace(text.charAt(end - 1))) { + end -= 1; + } + + final String newVal = text.substring(0, end) + ELLIPSIS; + view.setText(newVal); + isEllipsized = true; + } else { + isEllipsized = false; + } + view.setMaxLines(maxLines); + }); + } + + /** + * Toggle the view between the ellipsized and expanded state. + */ + public void toggle() { + if (isEllipsized) { + expand(); + } else { + ellipsize(); + } + } + + /** + * Whether the {@link #view} can be ellipsized. + * This is only the case when the {@link #content} has more lines + * than allowed via {@link #maxLines}. + * @return {@code true} if the {@link #content} has more lines than allowed via + * {@link #maxLines} and thus can be shortened, {@code false} if the {@code content} fits into + * the {@link #view} without being shortened and {@code null} if the initialization is not + * completed yet. + */ + @Nullable + public Boolean canBeEllipsized() { + return canBeEllipsized; + } + + private void linkifyContentView(final Consumer consumer) { + final boolean oldState = isEllipsized; + disposable.clear(); + TextLinkifier.fromDescription(view, content, + HtmlCompat.FROM_HTML_MODE_LEGACY, streamingService, streamUrl, disposable, + v -> { + consumer.accept(v); + notifyStateChangeListener(oldState); + }); + + } + + /** + * Add a listener which is called when the given content is changed, + * either from ellipsized to full or vice versa. + * @param listener The listener to be called, or {@code null} to remove it. + * The Boolean parameter is the new state. + * Ellipsized content is represented as {@code true}, + * normal or full content by {@code false}. + */ + public void setStateChangeListener(@Nullable final Consumer listener) { + this.stateChangeListener = listener; + } + + private void notifyStateChangeListener(final boolean oldState) { + if (oldState != isEllipsized && stateChangeListener != null) { + stateChangeListener.accept(isEllipsized); + } + } + +} diff --git a/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java b/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java index e59a3dc05..1419ac85a 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java @@ -92,7 +92,7 @@ public final class TextLinkifier { * {@link HtmlCompat#fromHtml(String, int)}. *

* - * @param textView the {@link TextView} to set the the HTML string block linked + * @param textView the {@link TextView} to set the HTML string block linked * @param htmlBlock the HTML string block to be linked * @param htmlCompatFlag the int flag to be set when {@link HtmlCompat#fromHtml(String, * int)} will be called diff --git a/app/src/main/java/org/schabi/newpipe/views/player/CircleClipTapView.kt b/app/src/main/java/org/schabi/newpipe/views/player/CircleClipTapView.kt index e3d142916..8554e7194 100644 --- a/app/src/main/java/org/schabi/newpipe/views/player/CircleClipTapView.kt +++ b/app/src/main/java/org/schabi/newpipe/views/player/CircleClipTapView.kt @@ -80,10 +80,10 @@ class CircleClipTapView(context: Context?, attrs: AttributeSet) : View(context, updatePathShape() } - override fun onDraw(canvas: Canvas?) { + override fun onDraw(canvas: Canvas) { super.onDraw(canvas) - canvas?.clipPath(shapePath) - canvas?.drawPath(shapePath, backgroundPaint) + canvas.clipPath(shapePath) + canvas.drawPath(shapePath, backgroundPaint) } } diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java index 009a4f4be..45211211f 100755 --- a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java @@ -23,7 +23,6 @@ import android.os.Handler; import android.os.Handler.Callback; import android.os.IBinder; import android.os.Message; -import android.os.Parcelable; import android.util.Log; import android.widget.Toast; @@ -36,6 +35,7 @@ import androidx.core.app.NotificationCompat.Builder; import androidx.core.app.PendingIntentCompat; import androidx.core.app.ServiceCompat; import androidx.core.content.ContextCompat; +import androidx.core.content.IntentCompat; import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; @@ -49,6 +49,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import us.shandian.giga.get.DownloadMission; import us.shandian.giga.get.MissionRecoveryInfo; @@ -359,29 +360,29 @@ public class DownloadManagerService extends Service { */ public static void startMission(Context context, String[] urls, StoredFileHelper storage, char kind, int threads, String source, String psName, - String[] psArgs, long nearLength, MissionRecoveryInfo[] recoveryInfo) { - Intent intent = new Intent(context, DownloadManagerService.class); - intent.setAction(Intent.ACTION_RUN); - intent.putExtra(EXTRA_URLS, urls); - intent.putExtra(EXTRA_KIND, kind); - intent.putExtra(EXTRA_THREADS, threads); - intent.putExtra(EXTRA_SOURCE, source); - intent.putExtra(EXTRA_POSTPROCESSING_NAME, psName); - intent.putExtra(EXTRA_POSTPROCESSING_ARGS, psArgs); - intent.putExtra(EXTRA_NEAR_LENGTH, nearLength); - intent.putExtra(EXTRA_RECOVERY_INFO, recoveryInfo); - - intent.putExtra(EXTRA_PARENT_PATH, storage.getParentUri()); - intent.putExtra(EXTRA_PATH, storage.getUri()); - intent.putExtra(EXTRA_STORAGE_TAG, storage.getTag()); + String[] psArgs, long nearLength, + ArrayList recoveryInfo) { + final Intent intent = new Intent(context, DownloadManagerService.class) + .setAction(Intent.ACTION_RUN) + .putExtra(EXTRA_URLS, urls) + .putExtra(EXTRA_KIND, kind) + .putExtra(EXTRA_THREADS, threads) + .putExtra(EXTRA_SOURCE, source) + .putExtra(EXTRA_POSTPROCESSING_NAME, psName) + .putExtra(EXTRA_POSTPROCESSING_ARGS, psArgs) + .putExtra(EXTRA_NEAR_LENGTH, nearLength) + .putExtra(EXTRA_RECOVERY_INFO, recoveryInfo) + .putExtra(EXTRA_PARENT_PATH, storage.getParentUri()) + .putExtra(EXTRA_PATH, storage.getUri()) + .putExtra(EXTRA_STORAGE_TAG, storage.getTag()); context.startService(intent); } private void startMission(Intent intent) { String[] urls = intent.getStringArrayExtra(EXTRA_URLS); - Uri path = intent.getParcelableExtra(EXTRA_PATH); - Uri parentPath = intent.getParcelableExtra(EXTRA_PARENT_PATH); + Uri path = IntentCompat.getParcelableExtra(intent, EXTRA_PATH, Uri.class); + Uri parentPath = IntentCompat.getParcelableExtra(intent, EXTRA_PARENT_PATH, Uri.class); int threads = intent.getIntExtra(EXTRA_THREADS, 1); char kind = intent.getCharExtra(EXTRA_KIND, '?'); String psName = intent.getStringExtra(EXTRA_POSTPROCESSING_NAME); @@ -389,7 +390,9 @@ public class DownloadManagerService extends Service { String source = intent.getStringExtra(EXTRA_SOURCE); long nearLength = intent.getLongExtra(EXTRA_NEAR_LENGTH, 0); String tag = intent.getStringExtra(EXTRA_STORAGE_TAG); - Parcelable[] parcelRecovery = intent.getParcelableArrayExtra(EXTRA_RECOVERY_INFO); + final var recovery = IntentCompat.getParcelableArrayListExtra(intent, EXTRA_RECOVERY_INFO, + MissionRecoveryInfo.class); + Objects.requireNonNull(recovery); StoredFileHelper storage; try { @@ -404,15 +407,11 @@ public class DownloadManagerService extends Service { else ps = Postprocessing.getAlgorithm(psName, psArgs); - MissionRecoveryInfo[] recovery = new MissionRecoveryInfo[parcelRecovery.length]; - for (int i = 0; i < parcelRecovery.length; i++) - recovery[i] = (MissionRecoveryInfo) parcelRecovery[i]; - final DownloadMission mission = new DownloadMission(urls, storage, kind, ps); mission.threadCount = threads; mission.source = source; mission.nearLength = nearLength; - mission.recoveryInfo = recovery; + mission.recoveryInfo = recovery.toArray(new MissionRecoveryInfo[0]); if (ps != null) ps.setTemporalDir(DownloadManager.pickAvailableTemporalDir(this)); diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index 23f1bf6a7..31e7f663d 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -490,7 +490,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb showError(mission, UserAction.DOWNLOAD_POSTPROCESSING, R.string.error_postprocessing_failed); return; case ERROR_INSUFFICIENT_STORAGE: - msg = R.string.error_insufficient_storage; + msg = R.string.error_insufficient_storage_left; break; case ERROR_UNKNOWN_EXCEPTION: if (mission.errObject != null) { diff --git a/app/src/main/java/us/shandian/giga/util/Utility.java b/app/src/main/java/us/shandian/giga/util/Utility.java index 3cfa22bd9..86a08c57f 100644 --- a/app/src/main/java/us/shandian/giga/util/Utility.java +++ b/app/src/main/java/us/shandian/giga/util/Utility.java @@ -2,6 +2,8 @@ package us.shandian.giga.util; import android.content.Context; import android.os.Build; +import android.os.Environment; +import android.os.StatFs; import android.util.Log; import androidx.annotation.ColorInt; @@ -26,10 +28,8 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.HttpURLConnection; import java.util.Locale; -import java.util.Random; import okio.ByteString; -import us.shandian.giga.get.DownloadMission; public class Utility { diff --git a/app/src/main/res/layout-land/list_stream_card_item.xml b/app/src/main/res/layout-land/list_stream_card_item.xml new file mode 120000 index 000000000..70228ee1d --- /dev/null +++ b/app/src/main/res/layout-land/list_stream_card_item.xml @@ -0,0 +1 @@ +../layout/list_stream_item.xml \ No newline at end of file diff --git a/app/src/main/res/layout/comment_replies_header.xml b/app/src/main/res/layout/comment_replies_header.xml new file mode 100644 index 000000000..ed5ba1a10 --- /dev/null +++ b/app/src/main/res/layout/comment_replies_header.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_comments.xml b/app/src/main/res/layout/fragment_comments.xml index b1b644d8c..2a8c747cd 100644 --- a/app/src/main/res/layout/fragment_comments.xml +++ b/app/src/main/res/layout/fragment_comments.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical" - tools:listitem="@layout/list_comments_item" /> + tools:listitem="@layout/list_comment_item" /> + android:src="@drawable/ic_pin" /> + tools:text="Author Name, Lorem ipsum • 5 months ago" /> - + tools:text="@tools:sample/lorem/random[1]" /> + android:src="@drawable/ic_heart" /> - + android:layout_alignParentEnd="true" + android:layout_marginStart="@dimen/video_item_detail_heart_margin" + android:minHeight="0dp" + tools:text="543 replies" /> diff --git a/app/src/main/res/layout/list_comments_mini_item.xml b/app/src/main/res/layout/list_comments_mini_item.xml deleted file mode 100644 index 606a237c5..000000000 --- a/app/src/main/res/layout/list_comments_mini_item.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/list_playlist_bookmark_item.xml b/app/src/main/res/layout/list_playlist_bookmark_item.xml new file mode 100644 index 000000000..6aabd4d07 --- /dev/null +++ b/app/src/main/res/layout/list_playlist_bookmark_item.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/playlist_header.xml b/app/src/main/res/layout/playlist_header.xml index 9c038db3a..c761240d9 100644 --- a/app/src/main/res/layout/playlist_header.xml +++ b/app/src/main/res/layout/playlist_header.xml @@ -80,10 +80,32 @@ tools:text="234 videos" /> + + + + + android:layout_below="@id/playlist_description_read_more"> - + android:id="@+id/summary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:clickable="false" + android:focusable="false" + android:gravity="center" + android:text="@string/notification_actions_summary" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - + - + - + - + - + - + diff --git a/app/src/main/res/values-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml index aca74e743..077cf1106 100644 --- a/app/src/main/res/values-ar-rLY/strings.xml +++ b/app/src/main/res/values-ar-rLY/strings.xml @@ -78,7 +78,7 @@ بليون تعذر تحميل موجز \'%s\'. ؟ - التحقق من وجود تحديثات + التحقق من وجود تحديثات مثيلات خوادم پيرتيوب +100 فيديو ألف @@ -271,7 +271,7 @@ يلغي السجل الحالي والاشتراكات وقوائم التشغيل والإعدادات (اختياريًا) تعطل التطبيق / واجهة المستخدم إعادة التسمية - لم يتبقى مساحة في الجهاز + لم يتبقى مساحة في الجهاز تعذر إعداد قائمة التنزيل اختر مجلد التنزيل لملفات الفيديو تم تعطيل الإشعارات diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 6bfe11eaa..82173d758 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -407,7 +407,7 @@ لا يمكن الكتابة فوق الملف هناك تنزيل معلق بهذا الاسم تم إغلاق NewPipe أثناء العمل على الملف - لم يتبقى مساحة في الجهاز + لم يتبقى مساحة في الجهاز تم فقد التقدم بسبب حذف الملف انتهى وقت الاتصال هل تريد محو سجل التنزيل، أم تريد حذف جميع الملفات التي تم تنزيلها؟ @@ -584,7 +584,7 @@ خلط تكرار يمكنك تحديد ثلاثة إجراءات كحد أقصى لإظهارها في الإشعار المضغوط! - قم بتحرير كل إشعار أدناه من خلال النقر عليه. حدد ما يصل إلى ثلاثة منها لتظهر في الإشعار المضغوط باستخدام مربعات الاختيار الموجودة على اليمين + قم بتحرير كل إجراء إعلام أدناه من خلال النقر عليه. حدد ما يصل إلى ثلاثة منها ليتم عرضها في الإشعار المضغوط باستخدام مربعات الاختيار الموجودة على اليمين. زر الإجراء الخامس زر الإجراء الرابع زر الإجراء الثالث @@ -700,7 +700,7 @@ وضع التالي على قائمة الانتظار تم وضع التالي على قائمة الانتظار جاري المعالجة ... قد يستغرق لحظة - التحقق من وجود تحديثات + التحقق من وجود تحديثات التحقق يدويا من وجود إصدارات جديدة جاري التحقق من وجود تحديثات… عناصر تغذية جديدة @@ -858,4 +858,26 @@ مشاركة قائمة التشغيل شارك تفاصيل قائمة التشغيل مثل اسم قائمة التشغيل وعناوين الفيديو أو كقائمة بسيطة من عناوين URL للفيديو - %1$s: %2$s + + رد %s + رد %s + ردان%s + ردود%s + ردود %s + ردود %s + + عرض المزيد + عرض أقل + قم بتحرير كل إجراء إعلام أدناه من خلال النقر عليه. يتم تعيين الإجراءات الثلاثة الأولى (تشغيل/إيقاف مؤقت، السابق والتالي) بواسطة النظام ولا يمكن تخصيصها. + لا توجد مساحة خالية كافية على الجهاز + اعادة ضبط الإعداداتِ + النسخ الاحتياطيُّ والاستعادة + أعيدوا جميع الإعدادات إلى قيمهم الافتراضية + ستؤدي إعادة ضبط جميع الإعدادات إلى تجاهل جميع إعداداتك المفضلة وإعادة تشغيل التطبيق. +\n +\nهل انت متأكد انك تريد المتابعة؟ + نعم + يمكن لـ NewPipe البحث تلقائيًا عن الإصدارات الجديدة من وقت لآخر وإعلامك بمجرد توفرها. +\nهل تريد تمكين هذا؟ + لا \ No newline at end of file diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index e3de4f4d2..66bfe75de 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -448,7 +448,7 @@ %s video %s video - Yeniləmələri yoxla + Yeniləmələri yoxla Axtarış çubuğunun miniatür önizləməsi Əməliyyat sistem tərəfindən ləğv edildi Avto @@ -548,7 +548,7 @@ İzləniləni sil Sistem qovluğu seçicisini (SAF) istifadə et Bağlantı fasiləsi - Cihazda yer qalmayıb + Cihazda yer qalmayıb Fayl üzərində işləyərkən NewPipe bağlandı Emaldan sonra uğursuz oldu Serverə qoşulmaq mümkün deyil @@ -694,7 +694,7 @@ Bu video yalnız YouTube Music Premium üzvləri üçün əlçatandır, ona görə də NewPipe tərəfindən yayımlamaq və ya endirmək mümkün deyil. İndi açıqlamadakı mətni seçə bilərsiniz. Nəzərə alın ki, seçim rejimində səhifə titrəyə və linklər kliklənməyə bilər. Bildirişdə göstərilən video miniatürünü 16:9-dan 1:1 görünüş nisbətinə qədər kəs - Aşağıdakı hər bir bildiriş fəaliyyətini üzərinə toxunaraq redaktə et. Sağdakı təsdiq qutularından istifadə edərək yığcam bildirişdə göstərmək üçün onların üçünü seç + Aşağıdakı hər bir bildiriş fəaliyyətini üzərinə toxunaraq düzəliş edin. Sağdakı təsdiq qutularından istifadə edərək yığcam bildirişdə göstərmək üçün onların üçünü seçin. Belə fayl/məzmun mənbəyi yoxdur Seçilən yayım xarici oynadıcılar tərəfindən dəstəklənmir Yükləyici tərəfindən hələ dəstəklənməyən yayımlar göstərilmir @@ -769,4 +769,5 @@ Axın yenilənərkən əldə edilən səhifələr.Kanal sürətli rejim istifadə edərək yenilənirsə, bu seçimin heç bir təsiri yoxdur. Yükləyici avatarları Miniatürlər + Aşağıdakı hər bildirişə vuraraq ona düzəliş edin. İlk üç əməl (oynatma/fasilə, əvvəlki və sonrakı) sistem tərəfindən təyin olunub və dəyişdirilə bilməz. \ No newline at end of file diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml index 626e3f284..5cc516f46 100644 --- a/app/src/main/res/values-b+ast/strings.xml +++ b/app/src/main/res/values-b+ast/strings.xml @@ -171,7 +171,7 @@ Yá esiste un ficheru baxáu con esti nome nun pue sobrecribise\'l ficheru Hai una descarga pendiente con esti nome - Nun queda espaciu nel preséu + Nun queda espaciu nel preséu Escosó\'l tiempu d\'espera de la conexón Nun pudieron importase les soscripciones Sotítulos diff --git a/app/src/main/res/values-b+uz+Latn/strings.xml b/app/src/main/res/values-b+uz+Latn/strings.xml index 0ba69ae41..c3c187891 100644 --- a/app/src/main/res/values-b+uz+Latn/strings.xml +++ b/app/src/main/res/values-b+uz+Latn/strings.xml @@ -58,7 +58,7 @@ Kodi bilan ijro etish Faqat ba\'zi qurilmalar 2K / 4K videolarni ijro etishi mumkin Yuqori o\'lchamlarni ko\'rsatish - "Standart pop-up o\'lchamlari" + Standart pop-up o\'lchamlari Standart o\'lchamlari Audio fayllar uchun yuklab olish papkasini tanlash Yuklab olingan videofayllar shu yerda saqlanadi @@ -416,7 +416,7 @@ Ushbu yuklab olishni tiklab bo\'lmaydi Ulanish vaqti tugadi Siljish yo\'qoldi, chunki fayl o\'chirildi - Qurilmada bo\'sh joy qolmadi + Qurilmada bo\'sh joy qolmadi NewPipe fayl ustida ishlash paytida yopilgan Keyingi ishlov berilmadi Topilmadi diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 38905ed7f..dceaaf6c5 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -6,8 +6,8 @@ Патокавы плэер не знойдзены (вы можаце ўсталяваць VLC каб прайграць). Усталяваць Скасаваць - Адкрыць у браўзеры - Адкрыць у асобным акне + Адкрыць ў браўзеры + Адкрыць ў асобным акне Падзяліцца Спампаваць Загрузка файла прамой трансляцыі @@ -37,12 +37,12 @@ Загружаныя аўдыёфайлы захоўваюцца тут Абярыце тэчку загрузкі для аўдыёфайлаў Разрознянне па змаўчанні - Разрозненне усплываючага акна + Разрозненне ўсплываючага акна Высокія разрозненні Толькі некаторыя прылады могуць прайграваць відэа ў 2K/4K - Прайграць у Kodi - Усталяваць адсутную праграму Kore\? - Паказаць опцыю \"Прайграць у Kodi\" + Прайграць ў Kodi + Ўсталяваць адсутную праграму Kore? + Паказаць опцыю \"Прайграць ў Kodi\" Паказаць опцыю прайгравання відэа праз медыяцэнтр Kodi Аўдыё Фармат аўдыё па змаўчанні @@ -70,7 +70,7 @@ Узнавіць прайграванне Працягваць прайграванне пасля перапынкаў (напрыклад, тэлефонных званкоў) Загрузіць - \"Наступнае\" и \"Прапанаванае\" відэа + \"Наступнае\" і \"Прапанаванае\" відэа Паказаць падказку \"Утрымлівайце, каб паставіць у чаргу\" Паказаць падказку пры націсканні фонавай або ўсплывальнай кнопкі ў відэа \"Падрабязнасці:\" URL не падтрымліваецца @@ -227,7 +227,7 @@ \nПалітыка прыватнасці NewPipe падрабязна тлумачыць, якія дадзеныя адпраўляюцца і захоўваюцца пры адпраўцы справаздачы аб збоях. Прачытаць палітыку Ліцэнзія NewPipe - NewPipe - гэта праграмнае забеспячэнне, свабоднае ад копілефта: вы можаце выкарыстоўваць, вывучаць, дзяліцца і паляпшаць яго па жаданні. У прыватнасці, вы можаце распаўсюджваць і/ці змяняць яго ў адпаведнасці з умовамі Агульнай грамадскай ліцэнзіі GNU, апублікаванай Фондам свабоднага праграмнага забеспячэння, альбо версіі 3 Ліцэнзіі, альбо (на ваш выбар) любой пазнейшай версіі. + NewPipe - гэта праграмнае забеспячэнне, свабоднае ад копілефта: вы можаце выкарыстоўваць, вывучаць, дзяліцца і паляпшаць яго па жаданні. Ў прыватнасці, вы можаце распаўсюджваць і/ці змяняць яго ў адпаведнасці з умовамі Агульнай грамадскай ліцэнзіі GNU, апублікаванай Фондам свабоднага праграмнага забеспячэння, альбо версіі 3 Ліцэнзіі, альбо (на ваш выбар) любой пазнейшай версіі. Прачытаць ліцэнзію Гісторыя Гісторыя @@ -253,8 +253,8 @@ Выдаліць Падрабязнасці Налады аўдыё - Утрымлівайце, каб дадаць у чаргу - Пачаць адсюль у фоне + Утрымлівайце, каб дадаць ў чаргу + Пачаць адсюль ў фоне Пачніце гуляць ва ўсплываючым акне Адкрыць бакавую панэль Зачыніць бакавую панэль @@ -262,16 +262,16 @@ Пры адкрыцці спасылкі на кантэнт — %s Відэаплэер Фонавы плэер - Плэер у акне + Аконны прайгравальнік Заўсёды пытацца Атрыманне звестак… Загрузка запытанага кантэнту Стварыць плэйліст Перайменаваць Імя - Дадаць у плэйліст - Усталяваць як мініяцюру плэйліста - Дадаць плэйліст у закладкі + Дадаць ў плэйліст + Ўсталяваць як мініяцюру плэйліста + Дадаць плэйліст ў закладкі Выдаліць закладку Выдаліць плэйліст\? Плэйліст створаны @@ -289,7 +289,7 @@ Прымусова паведамляць пра недастаўляемыя Rx-выключэнні па-за фрагментам або жыццёвым цыкле пасля выдалення Імпарт Імпарт з - Экспарт у + Экспарт ў Імпарт… Экспарт… Імпарт файла @@ -299,17 +299,17 @@ Імпарт падпісак YouTube з Google Takeout: \n \n1. Перайдзіце па гэтым URL: %1$s -\n2. Увайдзіце, калі вас папросяць +\n2. Ўвайдзіце, калі вас папросяць \n3. Націсніце на «Усе дадзеныя ўключаны», затым на «Адмяніць выбар усіх», затым выберыце толькі «падпіскі» і націсніце «ОК» \n4. Націсніце на «Наступны крок», а затым на «Стварыць экспарт» \n5. Націсніце на кнопку «Спампаваць» пасля таго, як яна з\'явіцца \n6. Пстрыкніце ФАЙЛ ІМПАРТУВАЦЬ ніжэй і выберыце спампаваны файл .zip \n7. [Калі імпарт .zip не ўдаецца] Распакуйце файл .csv (звычайна ў раздзеле \"YouTube і YouTube Music/subscriptions/subscriptions.csv\"), націсніце ФАЙЛ ІМПАРТУВАЦЬ ніжэй і выберыце выняты файл CSV - Імпарт падпісак з SoundCloud набраўшы альбо URL, альбо ваш ID: -\n -\n1. Уключыце \"рэжым працоўнага стала\" у браўзэры (сайт недаступны на тэлефоне) -\n2. Перайдзіце на: %1$s -\n3. Увайдзіце, калі неабходна + Імпарт падпісак з SoundCloud набраўшы альбо URL, альбо ваш ID: +\n +\n1. Ўключыце \"рэжым працоўнага стала\" ў браўзэры (сайт недаступны на тэлефоне) +\n2. Перайдзіце на: %1$s +\n3. Увайдзіце, калі неабходна \n4. Скапіруйце адрас з адраснага радка. вашID, soundcloud.com/вашID Гэтае дзеянне можа выклікаць вялікі расход трафіку. @@ -322,7 +322,7 @@ Прапускаць цішыню Крок Скід - У адпаведнасці з Агульным рэгламентам па абароне дадзеных ЕС (GDPR), звяртаем вашу ўвагу на палітыку прыватнасці NewPipe. Калі ласка, уважліва азнаёмцеся з ёй. + Ў адпаведнасці з Агульным рэгламентам па абароне дадзеных ЕС (GDPR), звяртаем вашу ўвагу на палітыку прыватнасці NewPipe. Калі ласка, уважліва азнаёмцеся з ёй. \nВам неабходна прыняць яе ўмовы, каб адправіць нам справаздачу пра памылку. Прыняць Адмовіцца @@ -331,8 +331,8 @@ Пры згортванні плэера Дзеянне пры пераключэнні са стандартнага плэера на іншае прыкладанне — %s Нічога не рабіць - Згарнуць у фонавы плэер - Плэер у акне + Згарнуць ў фонавы плэер + Плэер ў акне Адпісацца Абярыце ўкладку Абнаўленні @@ -356,14 +356,14 @@ Скончана У чарзе прыпынена - у чарзе + дададзены ў чаргу постапрацоўка - Паставіць у чаргу + Дадаць ў чаргу Дзеянне забаронена сістэмай Памылка загрузкі Стварыць унікальнае імя Перазапісаць - Загрузка з такім імем ужо выконваецца + Загрузка з такім імем ўжо выконваецца Паказаць тэкст памылкі Немагчыма стварыць папку прызначэння Немагчыма стварыць файл @@ -377,7 +377,7 @@ Спыніць Максімум спробаў Колькасць спробаў перад адменай загрузкі - Перапыніць у платных сетках + Перапыніць ў платных сетках Карысна пры пераключэнні на мабільную сетку, хоць некаторыя загрузкі не могуць быць прыпыненыя Падзеі Канферэнцыі @@ -394,12 +394,12 @@ Ачысціць дадзеныя Пазіцыі прайгравання выдалены Файл перамешчаны ці выдалены - Файл з такім імем ужо існуе - Файл з такім імем ужо існуе + Файл з такім імем ўжо існуе + Файл з такім імем ўжо існуе немагчыма перазапісаць файл - У чарзе ўжо ёсць загрузка з такім імем + Ў чарзе ўжо ёсць загрузка з такім імем NewPipe была зачынена падчас працы над файлам - Скончылася вольнае месца на прыладзе + Скончылася вольнае месца на прыладзе Прагрэс страчаны, так як файл быў выдалены Час злучэння выйшла Вы ўпэўнены\? @@ -408,8 +408,8 @@ Пачаць загрузку Прыпыніць загрузку Запытваць тэчку загрузкі - Вам будзе прапанавана ўказаць месца захавання кожнай загрузкі. -\nУключыце сістэмны выбарнік тэчкі (SAF), калі вы хочаце загружаць файлы на знешнюю SD-картку + Вам будзе прапанавана указаць месца захавання кожнай загрузкі. +\nЎключыце сістэмны выбарнік тэчкі (SAF), калі вы хочаце загружаць файлы на знешнюю SD-картку Выкарыстоўвайце сродак выбару сістэмных тэчак (SAF) \'Storage Access Framework\' дазваляе загружаць на знешнюю SD-картку Пераключыць службу, выбраную ў дадзены момант: @@ -426,7 +426,7 @@ Кнопка другога дзеяння Кнопка першага дзеяння Групы каналаў - Як у сістэме + Як ў сістэме Мова прылады Выберыце экзэмпляр Выдаліць загружаныя файлы @@ -442,15 +442,15 @@ Ніколі Толькі па Wi-Fi Паказаць арыгінальны час на элементах - Уключыць гук + Ўключыць гук Цішына - Дадаць у чаргу - Даданае у чаргу + Дадаць ў чаргу + Даданае ў чаргу Чарга прайгравання Найбольш папулярнае Лакальнае Нядаўна дададзенае - Няма закладак у плейлісце + Няма закладак ў плейлісце Выберыце плэйліст Кіёск па змаўчанні Так @@ -478,7 +478,7 @@ Кнопка пятага дзеяння Афарбоўваць апавяшчэнне асноўным колерам мініяцюры. Падтрымваецца не ўсімі прыладамі У кампактным апавяшчэнні дасяжна не больш за тры дзеянні! - Дзеянні можна змяніць, націснуўшы на іх. Адзначце не больш за трох для адлюстравання ў кампактным апавяшчэнні + Адрэдагуйце кожнае дзеянне апавяшчэння, націснуўшы на яго. Выберыце да трох з іх, якія будуць адлюстроўвацца ў кампактным апавяшчэнні, выкарыстоўваючы сцяжкі справа. Не ўдалося распазнаць URL-адрас. Адкрыць у іншай праграме\? Апавяшченне плэера Апавяшчэнні @@ -497,9 +497,9 @@ Падпіскі не выбраны Апошняе абнаўленне: %s Аўтаматычна (тэма прылады) - Выберыце ўлюбёную начную тэму - %s + Выберыце любімую начную тэму - %s Дазвол вылучэння тэксту ў апісанні - Ніжэй вы можаце абраць улюбёную начную тэму + Вы можаце выбраць сваю любімую начную тэму ніжэй Гэта опцыя даступна толькі тады, калі %s будзе выбранай тэмаю Загрузка пачалась Апавяшчэнні адключаныя @@ -519,7 +519,7 @@ Адкрыць з дапамогай Начная тэма Адкрыць вэб-сайт - Цяпер Вы можаце вылучаць тэкст у апісанні. Звярніце ўвагу, што ў рэжыме вылучэння старонка можа мігацець, а спасылкі могуць быць недаступныя для націскання. + Цяпер Вы можаце вылучаць тэкст ў апісанні. Звярніце ўвагу, што ў рэжыме вылучэння старонка можа мігацець, а спасылкі могуць быць недаступныя для націскання. Запусціць галоўны прайгравальнік у поўнаэкранным рэжыме Паказаць дэталі канала Нізкая якасць (менш) @@ -574,14 +574,14 @@ Выдалена %1$s зазагрузак Выдалена %1$s зазагрузак - Выдаліць усе загружаныя файлы з дыска\? + Выдаліць ўсе загружаныя файлы з дыска? %d хвіліна %d хвіліны %d хвілінаў %d хвілінаў - Змяніць памер інтэрвалу загрузкі прагрэсіўнага змесціва (у цяперашні час %s). Меншае значэнне можа паскорыць іх першапачатковую загрузку + Змяніць памер інтэрвалу загрузкі прагрэсіўнага змесціва (ў цяперашні час %s). Меншае значэнне можа паскорыць іх першапачатковую загрузку Выключыце, каб схаваць апісанне відэа і дадатковую інфармацыю Прапановы лакальнага пошуку Наладзіць апавяшчэнне аб бягучым прайграванні патоку @@ -608,7 +608,7 @@ Разлік хэша Вырашана Створана аўтаматычна (запампавальнік не знойдзены) - Плэйлісты, якія пазначаны шэрым, ужо ўтрымліваюць гэты элемент. + Плэйлісты, якія пазначаны шэрым, ўжо ўтрымліваюць гэты элемент. %s новы стрым %s новыя стрымы @@ -616,8 +616,8 @@ %s новых стрымаў Каментарыі - У чаргу далей - У чарзе наступны + Ў чаргу далей + Ў чарзе наступны Загрузка звестак аб стрыме… Апрацоўка... Можа заняць некаторы час Дублікат дададзены %d раз @@ -650,7 +650,7 @@ Праверка абнаўленняў… Выдаліць дублікаты\? Выдаліць дублікаты - Вы хочаце выдаліць усе паўтаральныя стрымы ў гэтым плэйлісце\? + Вы хочаце выдаліць ўсе паўтаральныя стрымы ў гэтым плэйлісце? Новыя элементы стужкі %d выбраны @@ -670,7 +670,7 @@ Апавяшчаць аб новых стрымах з падпісак Частата праверкі Патрабуецца падключэнне да сеткі - Праверце наяўнасць абнаўленняў + Праверце наяўнасць абнаўленняў Праверце новыя версіі ўручную Аўтаматычны запуск прайгравання — %s Картка @@ -687,12 +687,12 @@ Стужка Час пасля апошняга абнаўлення, перш чым падпіска лічыцца састарэлай — %s Памылка загрузкі стужкі - Уліковы запіс аўтара быў спынены. -\nNewPipe не зможа загрузіць гэты канал у будучыні. -\nВы хочаце адмовіцца ад падпіскі на гэты канал\? + Ўліковы запіс аўтара быў спынены. +\nNewPipe не зможа загрузіць гэты канал ў будучыні. +\nВы хочаце адмовіцца ад падпіскі на гэты канал? Рэжым хуткай загрузкі стужкі не дае дадатковай інфармацыі аб гэтым. Атрымлівайце са спецыяльнага канала, калі ён даступны - Уключыць хуткі рэжым + Ўключыць хуткі рэжым Катэгорыя Тэгі Ліцэнзія @@ -700,7 +700,7 @@ Не ў спісе Прыватная , - Пераключыць усё + Пераключыць ўсё Стрымы, якія яшчэ не падтрымліваюцца загрузчыкам, не адлюстроўваюцца Мініяцюра аватара канала Аўтар: %s @@ -729,7 +729,7 @@ Уліковы запіс спынены %s дае наступную прычыну: Рэкамендаваны - Унутраная + Ўнутраная Цалкам прагледзеў Гэты кантэнт даступны толькі для аплачаных карыстальнікаў, таму NewPipe не можа яго трансляваць або спампоўваць. Даступны ў некаторых службах, звычайна нашмат хутчэй, але можа вяртаць абмежаваную колькасць элементаў і часта няпоўную інфармацыю (напрыклад, без працягласці, тыпу элемента, без актыўнага стану) @@ -739,7 +739,7 @@ Ніякая праграма на вашай прыладзе не можа адкрыць гэта Стандартнае значэнне ExoPlayer Часткова прагледжана - Як вы думаеце, загрузка корму адбываецца занадта павольна\? Калі так, паспрабуйце ўключыць хуткую загрузку (гэта можна змяніць у наладах або націснуўшы кнопку ніжэй). + Як вы думаеце, загрузка корму адбываецца занадта павольна? Калі так, паспрабуйце ўключыць хуткую загрузку (гэта можна змяніць ў наладах або націснуўшы кнопку ніжэй). \n \nNewPipe прапануе дзве стратэгіі загрузкі корму: \n• Атрыманне ўсяго канала падпіскі павольнае, але поўнае. @@ -775,8 +775,8 @@ арыгінальны дубляваны апісальны - Гукавая дарожка ўжо павінна прысутнічаць у гэтай плыні - Уключыце гэту опцыю, калі ў вас ёсць праблемы з ініцыялізацыяй дэкодэра, якая вяртаецца да дэкодэраў з больш нізкім прыярытэтам, калі ініцыялізацыя асноўных дэкодэраў не ўдаецца. Гэта можа прывесці да нізкай прадукцыйнасці прайгравання, чым пры выкарыстанні асноўных дэкодэраў + Гукавая дарожка ўжо павінна прысутнічаць ў гэтай плыні + Ўключыце гэту опцыю, калі ў вас ёсць праблемы з ініцыялізацыяй дэкодэра, якая вяртаецца да дэкодэраў з больш нізкім прыярытэтам, калі ініцыялізацыя асноўных дэкодэраў не ўдаецца. Гэта можа прывесці да нізкай прадукцыйнасці прайгравання, чым пры выкарыстанні асноўных дэкодэраў Кіраванне некаторымі наладамі ExoPlayer. Каб гэтыя змены ўступілі ў сілу, патрабуецца перазапуск гульца Гэты абыходны шлях вызваляе і паўторна стварае відэакодэкі, калі адбываецца змяненне паверхні, замест таго, каб усталёўваць паверхню непасрэдна для кодэка. ExoPlayer ужо выкарыстоўваецца на некаторых прыладах з гэтай праблемай, гэты параметр мае ўплыў толькі на прыладах з Android 6 і вышэй \n @@ -820,7 +820,7 @@ - %1$s: %2$s Перамясціць селектар галоўнай укладкі ўніз Няма жывых трансляцый - Выберыце якасць выявы і ці спампоўваць выявы ўвогуле, каб паменшыць выкарыстанне дадзеных і памяці. Змены ачышчаюць кэш малюнкаў як у памяці, так і на дыску - %s + Выберыце якасць выявы і ці спампоўваць выявы ўвогуле, каб паменшыць выкарыстанне дадзеных і памяці. Змены ачышчаюць кэш малюнкаў як ў памяці, так і на дыску - %s Прайграць Іншыя опцыі Мініяцюры @@ -830,4 +830,14 @@ Каналы Папярэдні стрым Жывая трансляцыя + + %s адказ + %s адказы + %s адказаў + %s адказаў + + Паказаць больш + Паказаць менш + Адрэдагуйце кожнае дзеянне апавяшчэння, націснуўшы на яго. Першыя тры дзеянні (прайграванне/паўза, папярэдняе і наступнае) задаюцца сістэмай і не могуць быць зменены. + Недастаткова вольнага месца на прыладзе \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index fc845fdf9..bc235446c 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -467,7 +467,7 @@ Изберете инстанция Коментарите са изключени Кои раздели се показват на началната страница - Няма свободно място на устройството + Няма свободно място на устройството %d секунда %d секунди @@ -548,7 +548,7 @@ Бисквитките от reCAPTCHA бяха почистени Проверяване за актуализации… , - Провери за актуализации + Провери за актуализации Процент Неизвестно качество Неизвестен формат diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index 11fe2c2c8..3f0d65c82 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -165,7 +165,7 @@ বন্ধ করুন ডাউনলোড করা ফাইলগুলো ডিলিট করুন ডাওন লোড ইতিহাস মুছুন - ডিভাইস এ স্পেস নেই + ডিভাইস এ স্পেস নেই পাওয়া যায় নি সার্ভার পাওয়া যায় নি এরর দেখান @@ -297,7 +297,7 @@ থাম্বনেল ১:১ অনুপাতে সেট করো সিস্টেম ডিফল্ট প্লেলিস্ট বুকমার্ক করুন - "যখন পর্যাপ্ত নিবেদিত ফিড থেকে ডাটা সংগ্রহ করুন" + যখন পর্যাপ্ত নিবেদিত ফিড থেকে ডাটা সংগ্রহ করুন সবসময় হালনগাদ করুন শেষ হালনাগাদের পর একটি সাবস্ক্রিপশনের আগের সময় সেকেলে বিবেচিত — %s ফিড হালনাগাদ প্রবেশস্থল @@ -317,11 +317,11 @@ চ্যানেল গ্রুপ %d দিন - "%d দিন" + %d দিন %d ঘন্টা - "%d ঘন্টা" + %d ঘন্টা %d মিনিট @@ -401,7 +401,7 @@ কনটেন্টের জন্য পূর্বনির্ধারিত দেশ বাইরের প্লেয়ারসমূহ এ ধরনের লিঙ্কসমূহ সমর্থন করে না হ্যাশ হিসাব করা হচ্ছে - আপডেট চেক করো + আপডেট চেক করো %s জন দেখছে %s জন দেখছে diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml index b2eacd22a..29522f6ad 100644 --- a/app/src/main/res/values-bn-rIN/strings.xml +++ b/app/src/main/res/values-bn-rIN/strings.xml @@ -141,7 +141,7 @@ অ্যাপ এর ভাষা বন্ধ করুন ডাওন লোড ইতিহাস মুছুন - ডিভাইস এ স্পেস নেই + ডিভাইস এ স্পেস নেই পাওয়া যায় নি সার্ভার পাওয়া যায় নি ডাউন লোড হয় নি @@ -275,7 +275,7 @@ নিউ পাইপ ওয়েব সাইট এ যান বিস্তারিত বিবরণ ও খবর এর জন্য ১০০+ ভিডিও - "%s শ্রোতা" + %s শ্রোতা %s শ্রোতা গন বিবরণ diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 13bda43ae..853b04b64 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -10,7 +10,7 @@ বন্ধ করুন ডাউনলোড করা ফাইলগুলো ডিলিট করুন ডাওন লোড ইতিহাস মুছুন - ডিভাইস এ স্পেস নেই + ডিভাইস এ স্পেস নেই পাওয়া যায় নি সার্ভার পাওয়া যায় নি এরর দেখান @@ -596,7 +596,7 @@ পিনকৃত মন্তব্য বিজ্ঞপ্তি হালনাগাদ দেখা হচ্ছে … - হালনাগাদ আছে কিনা দেখো + হালনাগাদ আছে কিনা দেখো মূল প্লেয়ার ফুল স্ক্রীন এ শুরু করুন ধারার নতুন ভুক্তি ত্রুটি প্রতিবেদন এর বিজ্ঞপ্তি diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index a0dbc0b36..871af187b 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -151,7 +151,7 @@ El fitxer no existeix o bé no teniu permisos de lectura/escriptura El nom del fitxer no pot estar en blanc S\'ha produït un error: %1$s - Informeu de l\'error per correu electrònic + Informeu per correu electrònic S\'han produït alguns errors. Informe Informació: @@ -385,7 +385,7 @@ Reprèn la reproducció No es pot sobreescriure el fitxer Hi ha una baixada pendent amb aquest nom - No hi ha espai disponible al dispositiu + No hi ha espai disponible al dispositiu S\'ha perdut el progrés perquè s\'ha eliminat el fitxer S\'ha excedit el temps d\'espera de la connexió Esteu segurs que voleu esborrar el vostre historial de baixades o esborrar-ne tots els fitxers\? @@ -561,7 +561,7 @@ Mescla Repeteix El màxim d\'accions que poden aparèixer en una notificació compacta és de tres! - Editeu cada acció de la notificació tocant el botó corresponent. Podeu seleccionar-ne fins a tres, que es mostraran a les notificacions en format compacte + Editeu cada acció de la notificació tocant el botó corresponent. Podeu seleccionar-ne fins a tres, que es mostraran a les notificacions en format compacte. Cinquè botó d\'acció Quart botó d\'acció Tercer botó d\'acció @@ -640,7 +640,7 @@ Si la rotació automàtica està bloquejada, no inicieu vídeos al mini reproductor, sinó que aneu directament al mode de pantalla completa. Podeu accedir igualment al mini reproductor sortint de pantalla completa Notificació d\'informe d\'error Tancar abruptament el reproductor - Comprovar si hi ha actualitzacions + Comprovar si hi ha actualitzacions Comprovar manualment si hi ha noves versions Baixada finalitzada @@ -692,9 +692,26 @@ Cualitat desconeguda Ordenar Configura la notificació de reproducció actual. - Canvia la mida de l\'interval de càrrega (actualment %s). Un valor inferior pot accelerar la càrrega inicial del vídeo. Els canvis requereixen un reinici del jugador. + Canvia la mida de l\'interval de càrrega en continguts progressius (actualment %s). Un valor inferior pot accelerar la càrrega inicial del vídeo. Ignora els esdeveniments dels botons de reproducció físics Útil, per exemple, si feu servir uns auriculars amb els botons físicament trencats Trieu un gest per la part esquerra de la pantalla Mida de l\'interval de càrrega de reproducció + Acció de gest esquerra + Editeu cada acció de notificació de sota tocant-la. Les tres primeres accions (reproduir/pausa, anterior i següent) són establertes pel sistema i no es poden personalitzar. + Tria un gest per a la meitat dreta del reproductor + Acció del gest dret + Brillantor + Volum + Cap + Mou el selector de pestanya principal a la part inferior + Posició de les pestanyes principals + Prefereix àudio descriptiu + Seleccioneu la pista d\'àudio original independentment de l\'idioma + Prefereix l\'àudio original + Mode ràpid + Carregant Metadades… + Seleccioneu una pista d\'àudio amb descripcions per a persones amb discapacitat visual si està disponible + Nous streams + Notificacions sobre nous streams per a subscripcions \ No newline at end of file diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index df26383a4..e6e375a4c 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -16,12 +16,12 @@ پڕۆژەی نیوپایپ زانیارییە تایبەتییەکانت بە وردی دەپارێزێت. هەروەها به‌رنامه‌كه‌ هیچ زانایارییەکت بەبێ ئاگاداری تۆ بەکارنابات. \n‫سیاسەتی تایبەتی نیوپایپ بە وردی ڕوونکردنەوەت دەداتێ لەسەر ئەو زانیاریانەی وەریاندەگرێت و بەکاریاندەبات. ناتوانرێت لە بیرگەی دەرەکیدا داببەزێنرێت . شوێنی فۆڵده‌ری دابه‌زاندنەکان ڕێکبخرێتەوە؟ - ئایا مەبەستت ئه‌مه‌یه‌ \"%1$s\"؟ + مەبەستت لە ئەمەیە ٪1$s ؟ ماوەی نوێكردنه‌وه‌ی فیید هێڵەکی به‌رده‌وامبوون له‌ (به‌بێ دووباره‌كردنه‌وه‌) نۆبه‌تی کارپێکەر به‌پێی په‌خشی هاوشێوه‌ سنووردانانی نۆرەی دابەزاندن - بیرگەی ناوەکیت پڕ بووە + بیرگەی ناوەکیت پڕ بووە ژمارەی بەژداری نادیارە ناتوانرێت لەسەر ئەو فایله‌وه‌ جێگیر بکرێت په‌ڕه‌ هەڵبژێرە @@ -33,7 +33,7 @@ ناو چارەسەرکردن هه‌ره‌سی هێنا بچوکبوونەوە لەکاتی گۆڕینی به‌رنامه‌ - فایلی ڤیدیۆ دابه‌زێنراوەکان لێرەدا هەڵدەگیرێن + فایلی ڤیدیۆ داگیراوەکان لێرەدا هەڵدەگیرێن هەناردە کردنی مێژوو ، بەژدارییه‌كان ، خشته‌لێدانه‌كان و ڕێكخستنه‌كان بردنەپێشی ناتەواوی خێرا وا لە لێدەرەکە دەکات کە بەخێرایی شوێنەکە بگۆڕێت. بردنەپێشی ٥ یان ١٥ یان ٢٥ چرکەیی لەگەڵ ئەمەدا کارناکات سکاڵاکردن لەسەر نەگەیاندنی Rx ی پەسەندنەکرا لە دەرەوەی پارچە یان چالاکی لەدوای پوختەکردن @@ -90,7 +90,7 @@ لێدان به‌ Kodi ناتوانرێت لێدوانەکان باربکرێن بەستەری دۆخ دابنێ - فایلی دەنگە دابه‌زێنراوەکان لێرەدا هەڵدەگیرێن + فایلی دەنگە داگیراوەکان لێرەدا هەڵدەگیرێن ببورە، هەندێك کێشە ڕوویدا. هەناردە کردن بۆ ڕەفتار @@ -105,13 +105,13 @@ لادانی نیشانه‌كراو مۆڵەتەکان ناتوانرێت به‌ژداریكردنه‌كه‌ نوێبكرێته‌وه‌ - پاشبنەما + پشت شاشە بێ ئەنجامه‌ زمان دەگۆڕدرێت لەدوای داگیرساندنەوەی به‌رنامه‌كه‌ لادانی سەیرکراو پیشاندانی نیشانەکەری شوێنی کارپێکەر لە خشتەکاندا شوێنەکان لە خشتەکاندا - به‌ژداریت + به‌ژداریتکرد بەهۆی گۆڕانکاری لە شێوەی ژێرنووسکردنەکە. پێویستە به‌رنامه‌كه‌ دابگیرسێنیته‌وه‌ %d دیار کراوه‌ @@ -132,8 +132,8 @@ ‫%s بینراو ‫%s بینراوان - ڕاگرتن - فۆڵدەری دابه‌زاندنی فایله‌ دەنگییەکان هەڵبژێرە + وەستاندن + فۆڵدەری داگرتنی فایله‌ دەنگییەکان هەڵبژێرە نوێ سڕینەوەی مێژووی سەیرکراو بەردەوام بوونی کارپێکەر @@ -158,7 +158,7 @@ لێدانی گشتی هەمان فایل/بابەت بوونی نییە دەستپێکردن - به‌ژداری + به‌ژداریکردن بژاردەی ”لێدان بە Kodi“ پیشانبدرێت به‌ژدارییه‌كان پەڕەی بەتاڵ @@ -199,12 +199,12 @@ وێنۆچکەی سه‌روێنه‌ی کەناڵ دەتەوێت ڕێکخستنەکانیش هاوردە بكرینه‌وه‌؟ هیچ لێدەرێکی ڤیدیۆیی نه‌دۆزرایه‌وه‌. ده‌ته‌وێت VLC دابمەزرێنیت؟ - فۆڵده‌ری دابه‌زاندنی ڤیدیۆ + فۆڵده‌ری داگرتنی ڤیدیۆ کردنەوەی پلیکانە ڕووناك ئەو پێشنیازکراوانە هەڵبژێرە کە پیشان دەدرێن لەکاتی گەڕاندا کردارەکە هه‌ره‌سی هێنا, چونکە ئەو فایله‌ سڕاوەتەوە - زیادکردن بۆ + زیادی بکە بۆ به‌ژداری نییه‌ دۆخی پێرتووب خشتەلێدان سازکرا @@ -214,7 +214,7 @@ ∞ ڤیدیۆ بەکارهێنانی بردنەپێشی ناتەواوی خێرا هەڵەیەک ڕوویدا : %1$s - فۆڵده‌ری دابه‌زاندن بۆ فایلی ڤیدیۆکان هەڵبژێرە + فۆڵده‌ری داگرتن بۆ فایلی ڤیدیۆکان هەڵبژێرە ساز کراوه‌ لەلایەن %s بەکارهێنەران بابەت @@ -228,7 +228,7 @@ سیاسەتی تایبەتی نیوپایپ دابه‌زاندن ناكاراکردنی دۆخی خێرا - كردنه‌وه‌ له‌ وێبگه‌ر + ئەم بڕگەی پێڕستە ڤیدیۆیەک یان ستریمێکی دەنگی دەکاتەوە لە وێبگەڕێکدا ڕاژەکە هیچ داتایەک نانێرێت شوێنی کارپێکراوەکان سڕانەوە پەیامەکانی وەشانە نوێیەکانی نیوپایپ @@ -261,7 +261,7 @@ %d ڕۆژان ناولێنانه‌وه‌ - دابه‌زاندن + داگرتن باشە سڕینه‌وه‌ی پاشماوەی مێتاداتا ناتوانرێت ئەمه‌ داببه‌زێنرێته‌وه‌ @@ -326,7 +326,7 @@ قه‌باره‌ی بنەڕەتی بچووککردنەوە بۆ پەنجەرە گۆرانییەکان - دابه‌زاندنی فایلی پەخش + داگرتنی فایلی پەخش شێوازی پیشاندانی خشتە زیادکردنی دۆخ پەسەند @@ -342,7 +342,7 @@ بابەتی پەڕەی سەرەکی دیار کردنی بەژدارییەکان هاورده‌كردنی فایل - فۆڵده‌ری دابه‌زاندنی ده‌نگ + فۆڵده‌ری داگرتنی ده‌نگ هه‌ندێك له‌ قه‌باره‌كان ده‌نگیان تێدا نامێنێته‌وه‌ ڕووداوەکان وێنۆچکەی کەسی بەرزکەرەوە @@ -390,7 +390,7 @@ دەستپێکردنی لێدان لە پاشبنەماوە ناوفایل دانان لەسەر وێنۆچکەی خشتەلێدان - دەربارەی نیوپایپ + دەربارەی NewPipe زیادکردن بۆ خشتەلێدان (نەزانراو) زمانی به‌رنامه‌ @@ -469,7 +469,7 @@ سڕینەوەی پاشماوەی هەموو ماڵپه‌ڕه‌كان بەرنامەکە نه‌دۆزرایه‌وه‌. دابمه‌زرێت؟ ناتوانرێ پشتگیری دۆخەکە بکرێ - دامەزراندن + دابەزاندن ڤیدیۆکان بەستەرەکە پشتگیری نەکراوە قیڕ @@ -484,7 +484,7 @@ لەیەک کاتدا تەنیا یەک بابەت دادەبەزێنرێت دەتەوێت بگەڕێنرێتەوە بۆ شێوازی بنەڕەتی؟ وەستاندنی دابەزاندنەکان - دەربارە + دەربارە و پرسیارەکان پیشاندانی لێدوانەکان بۆ جێبەجێکردنی فرمانەکان لەگەڵ یاسای پاراستنی داتای گشتی ئەوروپیدا (GDPR) , ئێمە سەرنجت ڕادەکێشین بۆ سیاسەتە تایبەتییەکانی نیوپایپ. تکایە بەئاگادارییەوە بیخوێنەره‌وە. \nپێویستە په‌سه‌ندی بکەیت بۆ ناردنی سکاڵاکانت. @@ -502,7 +502,7 @@ کاتی دوای دواین نوێکردنەوە پێش بەژداربوون ڕەچاوکراوە — %s بیرگەی دەرەکی بەردەست نییە گێڕانەوەی کارپێکەر بۆ شوێنی پێشووتر - پاشگه‌زبوونه‌وه‌ + هەڵوەشاندنەوه تراکەکان ڕێکخستنەکانی دەنگ پرست پێ دەکرێت بۆ شوێنی دابەزاندنی هەر بابەتێک. @@ -541,7 +541,7 @@ تێکەڵکردن دووبارە دەتوانیت تا سێ كردار دیار بكه‌یت تا پیشان بدرێن له‌ پەیامەکەدا! - ده‌ستكاری هه‌ر یه‌كێك له‌م كردارانه‌ی خواره‌وه‌ بكه‌ له‌ڕێگه‌ی كرته‌ له‌سه‌ریان. ده‌توانیت تا زیاتر له‌ سێ دانه‌یان هه‌ڵبژێریت له‌ ڕێگای چوارگۆشه‌كانی لای ڕاسته‌وه‌یان، تا پیشان بدرێن له‌ پەیامەکاندا + دەستکاریکردنی هەر کردارێکی ئاگادارکەرەوە لە خوارەوە بە دەستلێدان. ۳- دانە هەڵبژێرە لە ڕێگەی بەکارهێنانی سندوقەبچوکەکە لای ڕاستەوە نیشان دەدرێت پێنجه‌م كرداری دوگمه‌ چواره‌م كرداری دوگمه‌ سێیه‌م كرداری دوگمه‌ @@ -555,7 +555,7 @@ پیشاندانی زانیاری مێتا ناكارایبكه‌ بۆ شاردنه‌وه‌ی دیسکریپشن له‌سه‌ر ڤیدیۆ و زانیاری زیاتر پیشاندانی دیسکریپشن - ڕووكاری شه‌و + ڕووكاری تاریک ئه‌ندرۆید ڕه‌نگی پەیام دڵخواز ده‌كات به‌پێی ڕه‌نگی سه‌ره‌كی وێنۆچكه‌كه‌ ( ڕه‌چاوی ئه‌وه‌ بكه‌ كه‌ ئه‌م تایبه‌تمه‌ندییه‌ هه‌موو ئامێرێك ناگرێته‌وه‌ ) ڕه‌نگكردنی پەیام یوتوب ”دۆخی قه‌ده‌غه‌كراو” پێشكه‌ش ده‌كات كه‌ بابەتە نه‌شیاوه‌كان ده‌شارێته‌وه‌ @@ -582,7 +582,7 @@ \nجا ده‌ته‌وێت به‌ژداری لابده‌یت له‌م كه‌ناڵه‌؟ ناتوانرێت فیید باربکرێت تا ً`%s` . هه‌ڵه‌ له‌ باركردنی فیید - ئه‌م تایبه‌تمه‌ندییه‌ كارابكه‌ گه‌ر ڕوونمای ڕه‌ش یاخوود جامبوونی کارپێکەرت ئه‌زموون كرد + ئەگەر تووشی شاشەی ڕەش یان لکەلکە بوویت لە کاتی پەخشکردنی ڤیدیۆدا، تونێلکردنی میدیا لەکاربخە. ناوەکی تایبەتی خشتەنەکراو @@ -658,18 +658,18 @@ نیوپایپ تووشی کێشەیەک بوو ، کرتە بکە بۆ سکاڵاکردن پیشاندانی ”کڕاش کردنی لێدەرەکە“ سازاندنی پەیامی کێشەیەک - پشکنین بۆ نوێکردنەوە + پشکنین بۆ نوێکردنەوە کێشە لە سکاڵا کردنی پەیام پەیامەکانی سکاڵاکردن لە کێشەکان بابەتە نوێیەکانی فیید لێدوانی هەڵواسراو کڕاش کردنی لێدەر پیشاندانی هەڵەی سناکباڕ - هیچ ڕێکخەرێکی فایلی گونجاو نەدۆزرایەوە بۆ ئەم کردارە. -\nتکایە ڕێکخەری فایلییەک دابمەزرێنە لۆ هەوڵدانی ناکاراکردنی \'%s\' لە ڕێکخستنەکانی دابەزاندندا. + هیچ FileManager پەڕگەی گونجاو بۆ ئەم کردارە نەدۆزراوەتەوە. +\nتکایە بەڕێوەبەری پەڕگەیەک دابمەزرێنە یان هەوڵبدە \'%s\' لە Settings بڕۆ Download لەکاربخە LeakCanary بەردەست نییە - هیچ ڕێکخەرێکی فایلی گونجاو نەدۆزرایەوە بۆ ئەم کردارە. -\nتکایە ڕێکخەرێکی فایلی دابمەزرێنە کە گونجاوبێت لەگەڵ دەسەڵاتی گەیشتن بە بیرگە. + هیچ FileManager گونجاو نەدۆزرایەوە بۆ ئەم کردارە. +\nتکایە FileManager دابمەزرێنە کە گونجاوبێت لەگەڵ دەسەڵاتی گەیشتن بە بیرگە. پشکنین کردن بۆ پەخشی نوێ پەیامەکانی پەخشە نوێیەکان پەیام بکرێم لەکاتی هەبوونی پەخشی نوێی بەژدارییەکان @@ -694,4 +694,12 @@ لەسەدا نیمچەتەن بنەڕەتی ExoPlayer + دیاریکردنی تراکی دەنگی ئەسڵی بێ گوێدانە زمانەکە + دەستکاریکردنی هەر کردارێکی ئاگادارکەرەوە لە خوارەوە بە دەستلێدان. یەکەم سێ کردار (لێدان/وەستان، پێشوو و دواتر) لەلایەن سیستەمەکەوە دانراوە و ناتوانرێت دەستکاری بکرێت. + پەسەند کردنی دەنگی وەسفکراو + گۆڕینی قەبارەی ماوەی لۆد لەسەر ناوەڕۆکی پێشکەوتوو (ئێستا ٪s). بەهایەکی کەمتر لەوانەیە بارکردنی سەرەتا خێراتر بکات + پەسەندکردنی دەنگی ئەسڵی + بەسوودە، بۆ نموونە، ئەگەر هێدسێتێک بەکاربهێنیت لەگەڵ دوگمەی فیزیکی شکاو + قەبارەی نێوان بارکردنی پەخشکردن + دوگمەی ڕووداوەکانی میدیای هاردوێر بەجێبهێڵە \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 5c3997d45..34537446c 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -396,7 +396,7 @@ soubor nelze přepsat Soubor s tímto názvem již čeká na stažení NewPipe byl ukončen v průběhu zpracovávání souboru - V zařízení nezbývá žádné místo + V zařízení nezbývá žádné místo Postup ztracen, protože soubor byl smazán Jste si jisti smazáním své historie stahování nebo smazáním všech stažených souborů\? Omezit frontu stahování @@ -554,7 +554,7 @@ Promíchat Opakovat Do kompaktního oznámení lze vybrat nejvíce tři akce! - Upravte každou akci oznámení níže poklepáním. Pomocí zaškrtávacích políček vpravo vyberte až tři z nich, které se mají zobrazit v kompaktním oznámení + Upravte každou akci oznámení níže poklepáním. Pomocí zaškrtávacích políček vpravo vyberte až tři z nich, které se mají zobrazit v kompaktním oznámení. Páté akční tlačítko Čtvrté akční tlačítko Třetí akční tlačítko @@ -665,7 +665,7 @@ Spustit hlavní přehrávač na celé obrazovce Zpracovávám... může trvat moment Ručně zkontrolovat zda je k dispozici nová verze - Kontrola aktualizací + Kontrola aktualizací NewPipe narazil na problém, klikněte pro nahlášení Došlo k chybě, více v oznámení Vytvořit oznámení o chybě @@ -819,4 +819,23 @@ Kanály Předchozí stream Živě + + %s odpověď + %s odpovědi + %s odpovědí + + Zobrazit více + Upravte každou akci oznámení níže poklepáním. První tři akce (přehrání/pozastavení, předchozí a další) jsou nastaveny systémem a nemohou být přizpůsobeny. + Zobrazit méně + Nedostatek volného místa v zařízení + Záloha a obnovení + Obnovit nastavení + Obnovení všech nastavení na výchozí hodnoty + Obnovením nastavení se zruší všechna preferovaná nastavení a aplikace se restartuje. +\n +\nJste si jisti, že chcete pokračovat? + Ano + Ne + NewPipe může čas od času automaticky kontrolovat nové verze a upozornit vás na jejich dostupnost. +\nChcete tuto funkci povolit? \ No newline at end of file diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index afde293a6..601bc3752 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -3,65 +3,65 @@ Tryk på forstørrelsesglasset for at komme i gang. Udgivet den %1$s Ingen streamafspiller blev fundet. Installér VLC\? - Ingen streamafspiller fundet (du kan installere VLC for at afspille den). - Installer - Annuller + Ingen streamafspiller blev fundet (du kan installere VLC for at afspille den). + Installér + Annullér Åbn i browser - Åbn i pop op-tilstand + Åbn i popup-tilstand Del - Download - Download stream-fil + Hent + Hent stream-fil Søg Indstillinger Mente du \"%1$s\"\? Del med - Benyt ekstern videoafspiller + Brug ekstern videoafspiller Fjerner lyd ved nogle opløsninger Brug ekstern lydafspiller - Abonner + Abonnér Abonnerer - Afmeld abonnement - Abonnement afmeldt + Afmeld + Kanal afmeldt Kunne ikke ændre abonnement Kunne ikke opdatere abonnement Vis info Abonnementer - Gemte spillelister - Vælg fane + Gemte Playlister + Vælg Fane Nyheder Baggrund - Pop op + Popup Føj til - Mappe til download af video - Downloadede videoer gemmes her - Angiv download-mappe for videofiler - Download-mappe for lydfiler - Downloadede lydfiler gemmes her - Angiv download-mappe for lydfiler + Lagringsmappe til videoer + Hentede videoer gemmes her + Vælg lagringsmappe til videofiler + Lagringsmappe til lydfiler + Hentede lydfiler gemmes her + Vælg lagringsmappe til lydfiler Standardopløsning - Standardopløsning for pop op + Standardopløsning til popup Vis højere opløsninger Kun nogle enheder kan afspille 2K-/4K-videoer Afspil med Kodi - Installer manglede Kode-app\? + Installér manglende Kore-app? Vis valgmuligheden \"Afspil med Kodi\" - Vis en knap til at afspille en video via Kodi-mediecenteret + Vis en mulighed for at afspille en video via Kodi-mediecenter Lyd - Standardformat for lydfiler - Standardformat for videofiler + Standard lydformat + Standard videoformat Tema - Lyst - Mørkt + Lys + Mørk Sort - Husk størrelse og placering af pop op - Husk sidste størrelse og placering af pop op-afspiller + Husk popup-egenskaber + Husk sidste størrelse og placering af popup-afspiller Brug hurtig og upræcis søgning - Upræcis søgning lader afspilleren finde placeringer hurtigere, men mindre præcist. Søgninger på 5, 15 eller 25 sekunder fungerer ikke med denne indstilling slået til - Billedcache slettet - Slet metadata-cachen - Slet alle websidedata fra cachen - Metadata-cache slettet - Føj automatisk næste stream til køen + Upræcis søgning lader afspilleren finde positioner hurtigere, men med nedsat præcision. Søgning i 5, 15 eller 25 sekunder fungerer ikke med dette + Billedcache ryddet + Ryd metadata-cache + Fjern alle cached websidedata + Metadata-cache ryddet + Føj automatisk næste stream til kø Fortsæt en afspilningskø, der afsluttes (ikke-gentagende), ved at tilføje en lignende stream Søgeforslag Vælg forslagene, der vises, når der søges @@ -71,99 +71,99 @@ Husk sete videoer Fortsæt afspilning Fortsæt afspilning efter afbrydelser (fx telefonopkald) - Download + Hent Vis \'Næste\' og \'Lignende\' videoer Vis \"Hold for at sætte i kø\"-tip - Vis et tip når der trykkes på baggrunds- eller pop op-knappen på siden med videodetaljer - Denne webadresse er ikke understøttet + Vis tip, når du trykker på baggrunden eller popup-knappen i video \"Detaljer:\" + Ikke-understøttet URL Standardland for indhold Standardsprog for indhold Afspiller - Opførsel + Adfærd Video og lyd Historik og cache Udseende Fejlretning Opdateringer Afspiller i baggrunden - Afspiller i pop op-tilstand + Afspiller i popup-tilstand Indhold Vis aldersbegrænset indhold Live - Downloads - Downloads + Hentet + Hentet Fejlrapport Alle Kanaler - Spillelister + Playlister Én video %s videoer Numre Brugere - Slået fra - Slet + Deaktiveret + Ryd Bedste opløsning Fortryd Fil slettet - Afspil alle + Afspil Alle Altid - Kun én gang + Kun Én Gang Fil - NewPipe notifikation + NewPipe-notifikation Notifikationer for NewPipes afspiller - Notifikation om opdatering af app - Notifikationer for nye NewPipe versioner + Notifikation om app-opdatering + Notifikationer for nye NewPipe-versioner [Ukendt] Skift til baggrund - Skift til pop op + Skift til popup Skift til hovedafspiller - Importer database - Eksporter database - Overskriver din nuværende historik, abonnementer, spillelister og (hvis det ønskes) indstillinger - Eksporter historik, abonnementer, spillelister og indstillinger - Slet visningshistorik + Importér database + Eksportér database + Overskriver din nuværende historik, abonnementer, playlister og (hvis det ønskes) indstillinger + Eksportér historik, abonnementer, playlister og indstillinger + Ryd visningshistorik Sletter historikken over afspillede streams og afspilningspositionerne Slet hele visningshistorikken\? - Visningshistorikken blev slettet - Slet søgehistorik - Sletter historikken for søgeord - Slet hele søgehistorikken\? - Søgehistorikken blev slettet + Visningshistorik slettet + Ryd søgehistorik + Sletter historik for søgeord + Slet hele søgehistorik? + Søgehistorik slettet Fejl Eksternt lager utilgængeligt - Det er endnu ikke muligt at downloade til et eksternt SD-kort. Nulstil download-mappens placering\? + Det er endnu ikke muligt at hente til et eksternt SD-kort. Nulstil lagringsmappens placering? Netværksfejl Kunne ikke indlæse alle miniaturebilleder - Kunne ikke analysere websted - Indhold ikke tilgængeligt - Kunne ikke oprette downloadmenu - App/brugergrænseflade gik ned + Kunne ikke analysere webside + Indhold utilgængeligt + Kunne ikke opsætte download-menu + App/UI gik ned Kunne ikke afspille denne stream Uoprettelig afspillerfejl opstod - Prøver at genoprette efter afspillerfejl + Genopretter efter afspillerfejl Eksterne afspillere understøtter ikke disse typer af links Ingen videostreams fundet Ingen lydstreams fundet Mappen findes ikke Fil eller indholdskilde findes ikke - Filen eksister ikke eller der mangler rettigheder til at læse eller skrive til den - Filavnet kan ikke være tomt + Filen findes ikke, ellers mangler der tilladelse til at læse eller skrive til den + Filnavn må ikke være tomt Der opstod en fejl: %1$s - Ingen streams er tilgængelige for download + Ingen streams tilgængelige til hentning Bruger standardfaner pga. fejl ved indlæsning af gemte faner - Genskab standardindstillinger - Vil du genoprette standardindstillingerne\? + Gendan standardindstillinger + Vil du gendanne standardindstillinger? Undskyld, dette skulle ikke være sket. - Rapporter via e-mail + Rapportér via e-mail Beklager, noget gik galt. - Rapporter - Information: + Rapportér + Info: Hvad skete der: Din kommentar (på engelsk): Detaljer: - Afspil video, længde: + Afspil video, varighed: Uploaders profilbillede Synes godt om Kan ikke lide @@ -173,7 +173,7 @@ Lyd Prøv igen Ingen abonnenter - Antallet af abonnenter er ikke tilgængeligt + Antal abonnenter utilgængeligt Ingen visninger %s visning @@ -190,17 +190,17 @@ Filnavn Tråde Fejl - NewPiper downloader + NewPipe henter Tryk for detaljer Vent venligst… - Kopieret til udklipsholderen - Vælg senere en tilgængelig downloadmappe i indstillingerne - Denne tilladelse behøves for -\nat åbne i pop op-tilstand + Kopieret til udklipsholder + Angiv venligst en lagringsmappe senere i indstillinger + Denne tilladelse er nødvendig for +\nat åbne i popup-tilstand 1 element slettet. reCAPTCHA-udfordring - Der blev anmodet om en reCAPTCHA-udfordring - Download + reCAPTCHA-udfordring anmodet + Hent Tilladte tegn i filnavne Ugyldige tegn bliver erstattet med denne værdi Erstatningstegn @@ -209,30 +209,30 @@ Om NewPipe Tredjepartslicenser © %1$s af %2$s under %3$s - Om & Ofte stillede spørgsmål + Om & Ofte Stillede Spørgsmål Licenser Åben letvægtsstreaming på Android. Bidrag til projektet - Hvad enten du har idéer til oversættelse, designændringer, kodeoprydning eller virkelig tunge kodeændringer, så er hjælp altid velkommen. Jo mere der bliver gjort, jo bedre bliver det! + Uanset om du har idéer til oversættelse, designændringer, oprydning i koden eller virkelig store kodeændringer, er hjælp altid velkommen. Jo mere der bliver gjort, jo bedre bliver det! Se på GitHub - Doner - NewPipe er udviklet af frivillige, der bruger deres fritid på at give dig den bedst mulige brugeroplevelse. Giv noget tilbage for at hjælpe NewPipes udviklere til at gøre appen endnu bedre, mens de nyder en kop kaffe. + Donér + NewPipe er udviklet af frivillige, der bruger deres fritid på at give dig den bedste brugeroplevelse. Giv noget tilbage og hjælp udviklerne med at gøre NewPipe endnu bedre, mens de nyder en kop kaffe. Giv noget tilbage - Websted - Besøg NewPipes websted for mere information og nyheder. - NewPipes fortrolighedspolitik - Læs fortrolighedspolitik - NewPipes licens - NewPipe er copyleft, fri software: Du kan bruge, studere, dele og forbedre den som du vil. Specifikt kan du redistribuere og/eller ændre den under betingelserne i GNU General Public License som udgivet af Free Software Foundation, enten version 3 af licensen eller (efter dit ønske) en vilkårlig senere version. + Webside + Besøg NewPipes webside for mere information og nyheder. + NewPipes Privatlivspolitik + Læs privatlivspolitik + NewPipes Licens + NewPipe er copyleft, fri software: Du kan bruge, studere, dele og forbedre den, som du vil. Specifikt kan du redistribuere og/eller ændre den under betingelserne i GNU General Public License som udgivet af Free Software Foundation, enten version 3 af licensen eller (efter dit ønske) en vilkårlig senere version. Læs licens Historik Historik Vil du slette dette element fra søgehistorikken\? - Sidst afspillet - Mest spillede - Indhold af hovedsiden + Sidst Afspillet + Mest Afspillet + Indhold af hovedside Hvilke faner vises på hovedsiden - Tom side + Tom Side Kioskside Kanalside Vælg en kanal @@ -240,7 +240,7 @@ Vælg en kiosk Eksporteret Importeret - Ikke en gyldig ZIP-fil + Ingen gyldig ZIP-fil Advarsel: Kunne ikke importere alle filer. Dette vil overskrive dine nuværende indstillinger. Vil du også importere indstillinger\? @@ -250,47 +250,47 @@ Fjern Detaljer Lydindstillinger - Hold for at føje til kø - Foretrukket \'åbn\'-handling + Hold for at sætte i kø + Foretrukken \'åbn\'-handling Videoafspiller Baggrundsafspiller - Pop op-afspiller + Popup-afspiller Spørg altid - Henter info … + Henter info… Indlæser det ønskede indhold - Ny spilleliste + Ny Playliste Omdøb Navn - Føj til spilleliste - Slet denne spilleliste\? - Spilleliste oprettet - Ingen undertekster + Føj til playliste + Slet denne playliste? + Playliste oprettet + Ingen Undertekster Tilpas Udfyld Zoom Autogenereret Undertekster - Eksporter til + Eksportér til Nulstil - Accepter + Acceptér Afvis Ingen begrænsning - Begræns opløsning når der bruges mobildata + Begræns opløsning, når mobildata bruges Opdateringer Listevisning Liste Gitter Automatisk Færdig - Afventning + Afventer efterbehandling - Læg i kø + Sæt i kø Handling afvist af systemet - Download fejlede - Generer unikt navn + Hentning mislykkedes + Generér unikt navn Overskriv En fil med dette navn eksisterer allerede - En downloadet fil med dette navn eksisterer allerede + En hentet fil med dette navn eksisterer allerede Der er en download i gang med dette navn Vis fejl Filen kan ikke oprettes @@ -301,10 +301,10 @@ Serveren sender ikke data Serveren accepterer ikke multitrådede downloads; prøv igen med @string/msg_threads = 1 Ikke fundet - Efterbehandling fejlede + Efterbehandling mislykkedes Stop Hændelser - Intet at se her + Ikke andet end fårekyllinger her t mio. mia. @@ -316,63 +316,63 @@ Kunne ikke importere abonnementer Kunne ikke eksportere abonnementer Konferencer - Start afspilningen i baggrunden - Start afspilning i et pop op - Åbn skuffe - Luk skuffe - Hvad:\\nForespørgsel:\\nIndholdssprog:\\nIndholdsland:\\nAppsprog:\\nTjeneste:\\nGMT-tid:\\nPakke:\\nVersion:\\nOS-version: - Standardhandling når indhold åbnes – %s - Anvend som spillelistens miniaturebillede - Bogmærk spilleliste - Fjern bogmærke - Føjet til spillelisten - Miniaturebillede for spilleliste ændret. + Start afspilning i baggrunden + Start afspilning i et popup + Åbn Skuffe + Luk Skuffe + Hvad:\\nForespørgsel:\\nIndholdssprog:\\nIndholdsland:\\nApp-sprog:\\nTjeneste:\\nGMT-tid:\\nPakke:\\nVersion:\\nOS-version: + Standardhandling ved åbning af indhold — %s + Anvend som playlistens miniaturebillede + Bogmærk Playliste + Fjern Bogmærke + Føjet til playliste + Ændret playlistens miniaturebillede. Ændr undertekststørrelse og baggrundsstil. Kræver genstart af appen for at træde i kraft - Monitorering for hukommelseslækager kan få appen til ikke at svare under heap dumping - Rapporter out-of-lifecycle-fejl - Importer - Importer fra - Importerer … - Eksporterer … - Importer fil + Overvågning af hukommelseslækager kan medføre, at appen ikke reagerer, når heap-dumpning udføres + Rapportér out-of-lifecycle-fejl + Importér + Importér fra + Importerer… + Eksporterer… + Importér fil Forrige eksport - Importer YouTube-abonnementer ved at downloade eksportfilen: + Importér YouTube-abonnementer ved at hente eksportfilen: \n \n1. Gå til denne webadresse: %1$s -\n2. Log ind når du bliver bedt om det -\n3. Klik på \"Alle Youtube-data medtages\" og fravælg alt bortset fra \"abonnementer\". -\n4. Klik på \"Næste trin\" og derefter \"Opret eksport\". -\n5. Klik på \"Download\" knappen efter den popper frem. -\n6. Klik på \"IMPORTER FIL\" nederst på denne side og vælg den downloadede .zip fil. -\n7. [Såfremt .zip-importeringen slår fejl] Uddrag .csv filen (som normalt findes i \"YouTube og YouTube Music/abonnementer/abonnementer.csv\"). Klik på \"IMPORTER FIL\" nederst på denne side, og vælg den uddragede .csv fil +\n2. Log ind, når du bliver bedt om det +\n3. Klik på \"Alle Youtube-data medtages\", og fravælg alt bortset fra \"abonnementer\". +\n4. Klik på \"Næste trin\", og derefter \"Opret eksport\". +\n5. Klik på \"Download\"-knappen efter den popper frem. +\n6. Klik på \"IMPORTÉR FIL\" nederst på denne side, og vælg den hentede .zip-fil. +\n7. [Såfremt .zip-importeringen slår fejl] Uddrag .csv-filen (som normalt findes i \"YouTube og YouTube Music/abonnementer/abonnementer.csv\"). Klik på \"IMPORTÉR FIL\" nederst på denne side, og vælg den uddragede .csv-fil ditID, soundcloud.com/ditID - Bemærk at denne operation kan kræve meget netværkstrafik. + Bemærk, at denne operation kan kræve meget netværkstrafik. \n -\nVil du fortsætte\? - Knapper for afspilningshastighed +\nVil du fortsætte? + Knapper for Afspilningshastighed Tempo Tonehøjde Spol forbi stilhed Skridt Vis en notifikation for at foreslå opdatering af appen, når en ny version er tilgængelig - Minimer ved appskift - Handling når der skiftes til en anden app fra hovedvideoafspilleren — %s + Minimér ved appskift + Handling ved skift til anden app fra hovedvideoafspiller — %s Ingen - Minimer til baggrundsafspiller - Minimer til pop op-afspiller + Minimér til baggrundsafspiller + Minimér til popup-afspiller En NewPipe-opdatering er tilgængelig! sat på pause sat i kø - Maksimalt antal genforsøg - Maksimalt antal forsøg før downloaden opgives + Maks. antal genforsøg + Maks. antal forsøg, før downloaden annulleres Afbryd på forbrugsafregnede netværk Nyttigt ved skift til mobildata, selv om nogle downloads ikke kan sættes på pause - Kun HTTPS adresser understøttes + Kun HTTPS-URL\'er understøttes Instansen findes allerede Kunne ikke validere instansen - Skriv instansens adresse + Indtast instans-URL Tilføj instans - Find de instanserne du kan lide på %s + Find de instanser, du kan lide på %s Vælg dine yndlings PeerTube-instanser PeerTube-instanser Afspil automatisk @@ -384,11 +384,11 @@ Vis kommentarer Ingenting Gentag - Femte handlingstast - Fjerde handlingstast - Første handlingstast - Anden handlingstast - Tredje handlingstast + Femte handlingsknap + Fjerde handlingsknap + Første handlingsknap + Anden handlingsknap + Tredje handlingsknap Viser resultater for: %s Åbn med LeakCanary er ikke tilgængelig @@ -410,7 +410,7 @@ Vis ikke Bland Vis beskrivelse - Åbn hjemmeside + Åbn webside Sprog Lav kvalitet (mindre) Start afspilning automatisk — %s @@ -424,138 +424,138 @@ %1$s download slettet %1$s downloads slettet - Slet alle downloadede filer fra drevet\? + Slet alle hentede filer fra drevet? Sæt downloads på pause - Start hovedafspilleren i fuldskærmstilstand - Downloadmappe endnu ikke valgt. Vælg standardmappen nu - Læg automatisk i kø - Konfigurer det spillende streams notifikation - Vis aldersbegrænset indhold (f.eks. 18+) - Slå YouTube \"begrænset tilstand\" til - YouTube har en \"begrænset tilstand\" der skjuler videoer som potientielt er skadelige for børn + Start hovedafspiller i fuld skærm + Lagringsmappe endnu ikke valgt; vælg standardmappen nu + Sæt automatisk i kø + Konfigurér notifikation om igangværende stream + Vis indhold, der muligvis er uegnet for børn, fordi det har en aldersgrænse (f.eks. 18+) + Slå YouTubes \"Begrænset Tilstand\" til + YouTube tilbyder en \"Begrænset Tilstand\", som skjuler potentielt voksenindhold Denne video er aldersbegrænset. \n -\nSlå \"%1$s\" fra i indstillingerne hvis du vil se den. +\nSlå \"%1$s\" til i indstillingerne, hvis du vil se den. Nye streams Notifikationer om nye streams fra abonnementer - reCAPTCHA cookies er ryddet + reCAPTCHA-cookies blev ryddet Slet alle afspilningspositioner\? Filen er flyttet eller slettet - NewPipe stødte ind i en fejl, tryk for at rapportere - Rapporter på GitHub + NewPipe stødte ind i en fejl; tryk for at rapportere + Rapportér på GitHub Høj kvalitet (større) Begræns downloadkøen - Ryd de cookies som NewPipe opbevarer når du løser en reCAPTCHA - Farvelæg notifikationen - Afspillernotifikation - En fejl opstod, se notifikationen - Slå fra for at skjule videobeskrivelsen og yderligere information - Slå fra for at gemme metainformationskasser med yderligere information om streammets skaber, streammets indhold eller en søgeforespørgsel + Ryd de cookies, som NewPipe opbevarer, når du løser en reCAPTCHA + Farvelæg notifikation + Afspiller-notifikation + En fejl opstod; se notifikationen + Slå fra for at skjule videobeskrivelse og yderligere information + Slå fra for at skjule metainfo-bokse med yderligere information om streamskaberen, streamindhold eller en søgeforespørgsel Download fuldført %s downloads fuldført - Den aktive spilleliste bliver udskiftet - Hvis du skifter fra en spiller til en anden, kan din kø blive erstattet - Vis metainformation + Den aktive afspillerkø bliver udskiftet + Ændring fra én afspiller til en anden kan erstatte din kø + Vis metainfo Lokale søgeforslag - Fjerne søgeforslag - Start ikke videoer i miniafspilleren, men gå direkte til fuldskærmstilstand, hvis automatisk rotering er låst. Du kan stadig se miniafspilleren, hvis du går ud af fuldskærmstilstand - Kunne ikke genkende addressen. Vil du åbne den i en anden app\? - Videohashfunktion notifikation - Notifikationer om videohashfunktioners status + Forslag til fjernsøgning + Start ikke videoer i miniafspilleren, men skift direkte til fuldskærmstilstand, hvis automatisk rotation er låst. Du kan stadig få adgang til miniafspilleren ved at forlade fuldskærm + Kunne ikke genkende URL. Åbn med en anden app? + Videohash-notifikation + Notifikationer om videohashing fremskridt Fejlrapport-notifikation Notifikationer for at rapportere fejl Slet afspilningspositioner Sletter alle afspilningspositioner - Spørg hvor filen skal downloades - Et download ad gangen - Slet downloadede filer - Vil du rydde din download historik eller slette alle downloadede filer\? + Spørg, hvor filen skal hentes + Ét download ad gangen + Slet hentede filer + Vil du rydde din downloadhistorik eller slette alle hentede filer? Kan ikke gendanne dette download - Ryd download historik - NewPipe projektet tager dit privatliv seriøst. Derfor samler appen intet data uden dit samtykke. -\nNewPipes fortrolighedspolitik forklarer i detaljer, hvilke data der bliver sendt og opbevaret når du sender en nedbrudsrapport. - Kopier en formatteret rapport + Ryd downloadhistorik + NewPipe-projektet tager dit privatliv meget alvorligt. Derfor indsamler appen ikke nogen data uden dit samtykke. +\nNewPipes privatlivspolitik forklarer i detaljer, hvilke data der sendes og gemmes, når du sender en nedbrudsrapport. + Kopiér formateret rapport Giv tilladelse til at vise over andre apps Vis indikatorer for afspilningsposition i lister Afspilningspositioner slettet - Ryd reCAPTCHA cookies + Ryd reCAPTCHA-cookies Der er en afventende download med dette navn Start downloads Beskær miniaturebillede til 1:1 format Beskær video-miniaturebillede i notifikationen fra 16:9 til 1:1 format - Rediger hver eneste varselshandling nedenunder ved at trykke på dem. Vælg op til tre af dem som bliver vist i den lille notifikation, via afkrydsningsfelterne til højre - Du kan kun vælge op til tre handlinger som kan vises i den lille notifikation! - Buffer + Redigér hver underretningshandling nedenfor ved at trykke på dem. Vælg op til tre af dem, som bliver vist i den lille notifikation via afkrydsningsfelterne til højre. + Du kan kun vælge op til tre handlinger, der kan vises i den lille notifikation! + Buffering Få Android til at vælge notifikationens farve ud fra den primære farve i miniaturebilledet (virker ikke på alle enheder) - Nattema - Frem- og tilbagesøgningstid + Nat-tema + Søgningsvarighed for spole frem/tilbage Denne video er aldersbegrænset. -\nPga. YouTubes politik om aldersbegrænsede videoer har NewPipe ikke adgang til videoen. +\nPga. nye YouTube-politikker om aldersbegrænsede videoer har NewPipe ikke adgang til nogen af dens videostreams og kan derfor ikke afspille dem. Crash afspilleren - Spørg om bekræftelse før du rydder en kø - Forhåndsvisning af miniaturebilleder på statuslinjen + Spørg om bekræftelse, før du rydder en kø + Forhåndsvisning af miniaturebilleder på statuslinje Sæt i kø som næste - Er sat som næste i køen + Sat i kø som næste Download er begyndt Vis miniaturebilleder på både låseskærmen og notifikationer Nylige Notifikationer er slået fra Kommentarer - Relaterede objekter + Relaterede emner Stryg på elementer for at fjerne dem - Vælg en spilleliste - Ingen spilleliste-bogmærker endnu - Sproget ændres når appen genstarter - Spillekø + Vælg en playliste + Ingen playliste-bogmærker endnu + Sproget ændres, når appen genstarter + Afspillerkø Vis kanalens detaljer Sæt i kø Sat i kø - Loader streamets detaljer… - Processere... Det kan tage et øjeblik - Vis hukommelsestab - Deaktiver medietunneler + Indlæser streamdetaljer… + Behandler… Det kan tage et øjeblik + Vis hukommelseslækager + Deaktivér medietunneling Vis billedindikatorer Netværkskrav Alle netværk Kontrolfrekvens Notifikationer ved nye streams - Giv besked om nye streams fra abonnementer + Underret om nye streams fra abonnementer Tjek manuelt efter opdateringer Tjekker efter opdateringer… - Gendanner - \"Hurtig feed\"-tilstand viser ikke mere information om dette. - Tjek efter opdateringer + gendanner + \"Hurtig feed\"-tilstand oplyser ikke mere info om dette. + Tjek efter opdateringer Fjern sete videoer\? - Deaktiver medietunneler hvis du oplever en sort skærm eller hak ved videoafspilning - Venligst tjek om der allerede eksisterer en problemrapport som diskuterer dit crash. Hvis du opretter duplikatrapporter, tager du tid fra os som vi kunne bruge på at fikse fejlen. + Deaktivér medietunneling, hvis du oplever en sort skærm eller hakken ved videoafspilning. + Tjek venligst, om der allerede findes et problem, der diskuterer dit nedbrud. Når du opretter flere tickets, tager du tid fra os, som vi kunne bruge på at løse den faktiske fejl. Tjek efter nye streams Lav en fejlnotifikation Lokale - Udgiverens bruger er blevet slettet. -\nNewpipe kan ikke indlæse dette feed i fremtiden. -\nVil du fjerne dit abonnement på denne kanal\? - Feedet blev sidst opdateret for %s + Udgiverens konto er blevet lukket. +\nNewPipe vil ikke være i stand til at indlæse dette feed i fremtiden. +\nØnsker du at afmelde denne kanal? + Feed sidst opdateret: %s Ikke indlæst: %d Indlæser feed… Nye feed elementer - Tid siden sidste opdatering for at et abonnoment bliver forældet - %s - Altid opdater + Tid efter sidste opdatering, før et abonnement betragtes som forældet — %s + Altid opdatér Vælg abonnementer Dette indhold er ikke tilgængeligt i dit land. Af %s - Videoer, der er blevet set før og efter, at de er blevet tilføjet til spillelisten, vil blive fjernet. -\nEr du sikker\? Dette kan ikke gøres om! + Videoer, der er blevet set før og efter tilføjelse til playlisten, vil blive fjernet. +\nEr du sikker? Dette kan ikke fortrydes! Vis miniaturebillede Tags Aldersbegrænsning - Dette indhold er ikke understøttet af NewPipe. + Dette indhold understøttes endnu ikke af NewPipe. \n \nVi håber at kunne understøtte det i en fremtiden. - Dette indhold er kun tilgængeligt for brugere som har betalt for det. Det kan ikke blive streamet eller downloadet af NewPipe. - Bruger slettet - Dette indhold er privat, så det jan ikke blive streamet eller downloadet af NewPipe. + Dette indhold er kun tilgængeligt for brugere, som har betalt for det, så det kan ikke streames eller downloades af NewPipe. + Konto lukket + Dette indhold er privat, så det kan ikke streames eller downloades af NewPipe. Nyligt tilføjede Fremhævede %s giver denne grund: @@ -570,10 +570,10 @@ Vil du slette denne gruppe\? Licens - %s nyt stream + %s ny stream %s nye streams - Semitone + Halvtone %d time %d timer @@ -583,16 +583,16 @@ %d dage Lavet af %s - Slå hurtigtilstand fra - Slå hurtigtilstand til - Hent fra det dedikerede feed når det er muligt + Deaktivér hurtig-tilstand + Aktivér hurtig-tilstand + Hent fra dedikeret feed, når det er tilgængeligt Feed opdateringsgrænse Feed %d valgt %d valgte - Processerer feed… + Behandler feed… Kanalgrupper %d minut @@ -601,19 +601,19 @@ Fjern sete Vælg en instans Forbindelse afbrudt - Fremskridt tabt fordi filen blev slettet + Fremskridt tabt, fordi filen blev slettet NewPipe blev lukket under arbejde på filen - Kan ikke overskrive filen - For at være i overenstemmelse med GDPR fanger vi din opmærksomhed hentil NewPipes privatpolitik. Venligst læs den med omhu. -\nDu skal acceptere den for at sende os en fejlrapport. + kan ikke overskrive filen + For at overholde den europæiske databeskyttelsesforordning (GDPR) henviser vi hermed din opmærksomhed til NewPipes privatlivspolitik. Læs den venligst omhyggeligt. +\nDu skal acceptere den for at kunne sende os fejlrapporten. Aflænk (kan skabe forvrængning) - Importer en SoundCloud profil ved at skrive enten dit URL eller ID: + Importér en SoundCloud-profil ved at skrive enten dit URL eller ID: \n -\n1. Slå \"desktop-version\" til i mobilbrowsere. +\n1. Slå \"desktop-tilstand\" til i mobilbrowsere. \n2. Gå til denne adresse: %1$s -\n3. Log ind når du bliver spurgt -\n4. Kopier adressen på den profil du bliver henstillet til. - Vis den oprindelige tidsforskel på elementer +\n3. Log ind, når du bliver bedt om det +\n4. Kopiér adressen på den profil, du bliver omdirigeret til. + Vis oprindelig tid siden på elementer Autogenereret (ingen uploader fundet) Slå lyd til Sæt på lydløs @@ -621,7 +621,7 @@ Kunne ikke indlæse kommentarer Standard Kiosk Færdig - Tryk på \"Færdig\" når den er løst + Tryk på \"Færdig\", når den er løst Ingen kommentarer ∞ videoer Ingen lyttere @@ -632,26 +632,26 @@ Ingen seere Skift service, nuværende valg: Kommentarer er slået fra - Ingen apps på din enhed kan åbne dette - Ingen ledig plads på enheden - App sprog + Ingen app på din enhed kan åbne dette + Ingen ledig plads på enheden + App-sprog Ja, og delvist sete videoer Fejl ved indlæsning af feed Kunne ikke indlæse feed for \'%s\'. - Vis \"crash afspilleren\" + Vis \"Crash afspilleren\" Crash appen - Vis et crash alternativ når afspilleren er i brug + Vis en crash mulighed, når afspilleren er i brug Vis en fejl snackbar - Brug system mappevælger (SAF) + Brug systemets mappevælger (SAF) Kanalens avatar-miniaturebillede Dette er et SoundCloud Go+-nummer, i hvert fald i dit land, så det kan ikke streames eller downloades af NewPipe. Der blev ikke fundet nogen passende filhåndtering til denne handling. -\nInstaller en Storage Access Framework-kompatibel filhåndtering +\nInstallér venligst en Storage Access Framework-kompatibel filhåndtering Der blev ikke fundet nogen passende filhåndtering til denne handling. -\nInstaller et filhåndteringsprogram eller prøv at deaktivere \'%s\' i download-indstillingerne +\nInstallér venligst en filhåndtering, eller prøv at deaktivere \'%s\' i downloadindstillingerne Aktivér valg af tekst i beskrivelsen Automatisk (enhedstema) - Deaktiver valg af tekst i beskrivelsen + Deaktivér valg af tekst i beskrivelsen Fastgjort kommentar Du abonnerer nu på denne kanal , @@ -660,50 +660,50 @@ Den er tilgængelig i nogle tjenester og er normalt meget hurtigere, men kan returnere et begrænset antal elementer og ofte ufuldstændige oplysninger (f.eks. ingen varighed, elementtype, ingen live-status) Ukendt format Ukendt kvalitet - Hjertemarkeret af indholdsskaberen - Intervalstørrelse for afspilningsindlæsning + Hjertemarkeret af skaber + Størrelse på afspilningsinterval ExoPlayer-standard Tomt gruppenavn Du vil blive spurgt, hvor du vil gemme hver enkelt download. -\nAktiver systemet mappevælger (SAF), hvis du vil downloade til et eksternt SD-kort - Originaltekster fra tjenester vil være synlige i stream-emner +\nAktivér systemets mappevælger (SAF), hvis du vil hente til et eksternt SD-kort + Originaltekster fra tjenester vil være synlige i stream-elementer Ingen videostreams er tilgængelige for eksterne afspillere Fra Tablet-tilstand - Denne video er kun tilgængelig for YouTube Music Premium-medlemmer, så den kan ikke streames eller downloades af NewPipe. - \"Storage Access Framework\" gør det muligt at downloade til et eksternt SD-kort - Fremtving indberetning af ikke-leverbare Rx-undtagelser uden for fragmentets eller aktivitetens livscyklus efter bortskaffelse - Tryk for at downloade %s + Denne video er kun tilgængelig for YouTube Musik Premium-medlemmer, så den kan ikke streames eller downloades af NewPipe. + \"Storage Access Framework\" gør det muligt at hente til et eksternt SD-kort + Tving rapportering af ikke-levérbare Rx-undtagelser uden for fragmentets eller aktivitetens livscyklus efter bortskaffelse + Tryk for at hente %s Fra og med Android 10 understøttes kun \"Storage Access Framework\" - Synes du, at feed-indlæsning er for langsom\? Hvis det er tilfældet, så prøv at aktivere hurtig indlæsning (du kan ændre det i indstillingerne eller ved at trykke på knappen nedenfor). + Synes du, at feed-indlæsning er for langsom? Hvis det er tilfældet, så prøv at aktivere hurtig indlæsning (du kan ændre det i indstillingerne eller ved at trykke på knappen nedenfor). \n \nNewPipe tilbyder to strategier til feed-indlæsning: \n- Hentning af hele abonnementskanalen, hvilket er langsomt, men komplet. \n- Brug af et dedikeret service endpoint, hvilket er hurtigt, men normalt ikke komplet. \n -\nForskellen mellem de to er, at den hurtige metode normalt mangler nogle oplysninger, f.eks. elementets varighed eller type (kan ikke skelne mellem livevideoer og normale videoer), og den returnerer muligvis færre elementer. +\nForskellen mellem de to, er, at den hurtige metode normalt mangler nogle oplysninger, f.eks. elementets varighed eller type (kan ikke skelne mellem livevideoer og normale videoer), og den returnerer muligvis færre elementer. \n \nYouTube er et eksempel på en tjeneste, der tilbyder denne hurtige metode med sit RSS-feed. \n \nValget er altså et spørgsmål om, hvad du foretrækker: hastighed eller præcise oplysninger. - Den valgte stream er ikke understøttet af eksterne afspillere + Den valgte stream understøttes ikke af eksterne afspillere Denne indstilling er kun tilgængelig, hvis %s er valgt som tema - Du kan nu vælge tekst i beskrivelsen. Bemærk, at siden kan flimre, og at links muligvis ikke kan klikkes på, mens du er i valgtilstand. + Du kan nu vælge tekst inde i beskrivelsen. Bemærk, at siden kan flimre, og at links muligvis ikke kan klikkes på, mens du er i markeringstilstand. Streams, som endnu ikke understøttes af downloaderen, vises ikke Hurtig tilstand Importér eller eksportér abonnementer fra 3-punktsmenuen Ofte stillede spørgsmål Hvis du har problemer med at bruge appen, bør du tjekke disse svar på almindelige spørgsmål! - Se på hjemmeside + Se på webside Vis Picasso-farvede bånd oven på billeder, der angiver deres kilde: rød for netværk, blå for disk og grøn for hukommelse Du kører den nyeste version af NewPipe - På grund af ExoPlayer-begrænsninger blev søgetiden sat til %d sekunder + Pga. ExoPlayer-begrænsninger blev søgevarigheden sat til %d sekunder Vis kun ikke-grupperede abonnementer - Side med spillelister + Playlisteside Du kan vælge dit foretrukne nattema nedenfor - Vælg dit foretrukne nattema - %s + Vælg dit foretrukne nattema — %s Support - Host + Vært Offentlig Ikke oplyst Privat @@ -713,12 +713,104 @@ Ingen lydstreams er tilgængelige for eksterne afspillere Vælg kvalitet til eksterne afspillere Sortér - Ignorer hardware medie knapper + Ignorér hardware medie-knap begivenheder Brugbart f.eks. hvis du bruger et headset med ødelagte fysiske knapper - Playlists der er grået ud, indeholder allerede dette objekt. - Inaktiver permanent thumbnail - Fejlede at kopiere til udklipsholderen + Playlisterne, der er nedtonede, indeholder allerede dette element. + Fjern permanent miniaturebillede + Kunne ikke kopiere til udklipsholder Brug det originale lydspor uanset sprog Foretræk lydbeskrivelser Foretræk original lyd + Hvis tilgængeligt, brug lydbeskrivelser for personer med nedsat syn + Redigér hver underretningshandling nedenfor ved at trykke på dem. De første tre handlinger (afspil/sæt på pause, forrige og næste) er indstillet af systemet og kan ikke tilpasses. + Indlæser Metadata… + Fjern duplikater? + Vælg kvaliteten af billeder, og om billeder overhovedet skal indlæses, for at reducere data- og hukommelsesforbrug. Ændringer rydder både billedcachen i hukommelsen og på disken — %s + Middel kvalitet + Høj kvalitet + Ingen + Set helt + Ingen streams + Vis/skjul streams + Lysstyrke + Lydstyrke + Vælg bevægelse til venstre halvdel af afspillerens skærm + Vælg bevægelse til højre halvdel af afspillerens skærm + Højre bevægelseshandling + Ingen live streams + Lyd: %s + Lydspor + Fjern duplikater + Vis følgende streams + Hent kanal-faner + Faner, der skal hentes, når feedet opdateres. Denne indstilling har ingen effekt, hvis en kanal opdateres i hurtig-tilstand. + Miniaturebilleder + Vælg lydspor til eksterne afspillere + Ukendt + Delvist set + Kommende + original + Videoer + Numre + Live + Kanaler + Playlister + Album + Om + Kanal-faner + Hvilke faner vises på kanalsiderne + Åbn afspilningskø + Skift til fuld skærm + Skift skærmretning + Forrige stream + Næste stream + Afspil + Afspil igen + Varighed + Spol tilbage + Billedkvalitet + Indlæs ikke billeder + Lav kvalitet + Del Playliste + Del playliste med detajler såsom playlistenavn og videotitler eller som en simpel liste over video-URL\'er + Del med Titler + Del URL-liste + + %s svar + %s svar + + Vis mere + Vis mindre + Skift intervalstørrelsen for indlæsning af progressivt indhold (i øjeblikket %s). En lavere værdi kan fremskynde den første indlæsning + Ønsker du at fjerne alle duplikerede streams i denne playliste? + Spol frem + Venstre bevægelseshandling + ExoPlayer-indstillinger + Administrér nogle ExoPlayer-indstillinger. Disse ændringer kræver genstart af afspilleren for at træde i kraft + Position for hovedfaner + Flyt hovedfanens vælger til bunden + Duplikat tilføjet %d gang(e) + Medietunneling var som standard deaktiveret på din enhed, fordi din enhedsmodel er kendt for ikke at understøtte det. + Uploader-avatars + Underkanal-avatars + Avatars + Bannere + Abonnenter + Et lydspor bør allerede være til stede i denne stream + Brug ExoPlayers fallback-funktion til dekoderen + oversat + beskrivende + Shorts + Flere muligheder + \? + - %1$s: %2$s + %1$s +\n%2$s + Aktivér denne indstilling, hvis du oplever initialiseringsproblemer med dekodere, som falder tilbage til dekodere af lavere prioritet, hvis de primære dekoderes initialisering fejler. Dette kan resultere i dårligere afspilningsydelse end ved brug af primære dekodere + Denne løsning frigiver og genindstiller video-codecs, når en overflade ændres, i stedet for at indstille overfladen til codec\'en direkte. Denne indstilling bruges allerede af ExoPlayer på nogle enheder med dette problem, men har kun effekt på Android 6 og nyere. +\n +\nAktivering af denne indstilling kan forhindre afspilningsfejl, når du skifter den aktuelle videoafspiller eller skifter til fuld skærm + Brug altid ExoPlayers overfladeindstilling til video-output som løsning + Kort + %1$s %2$s \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5260287c1..3a308fb25 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -104,7 +104,7 @@ Letzte Größe und Position des Pop-ups merken Suchvorschläge Vorschläge auswählen, die bei der Suche angezeigt werden sollen - Löschen + löschen Beste Auflösung Über NewPipe Lizenzen @@ -405,7 +405,7 @@ Datei kann nicht überschrieben werden Es ist ein ausstehender Download mit diesem Namen vorhanden NewPipe wurde während der Verarbeitung der Datei geschlossen - Kein Speicherplatz mehr auf dem Gerät + Kein Speicherplatz mehr auf dem Gerät Vorgang abgebrochen, da die Datei gelöscht wurde Möchtest du deinen Downloadverlauf oder alle heruntergeladenen Dateien löschen\? Downloadwarteschlange begrenzen @@ -425,7 +425,7 @@ Niemand schaut zu %s Zuschauer - %s Zuschauer + %s Zuschauende Niemand hört zu @@ -541,7 +541,7 @@ Nur über WLAN Nie Du kannst maximal drei Aktionen auswählen, die in der Kompaktbenachrichtigung angezeigt werden sollen! - Bearbeite jede Benachrichtigungsaktion unten, indem du darauf tippst. Wähle mithilfe der Kontrollkästchen rechts bis zu drei aus, die in der Kompaktbenachrichtigung angezeigt werden sollen + Bearbeite jede Benachrichtigungsaktion unten, indem du auf sie tippst. Wähle mithilfe der Kontrollkästchen rechts bis zu drei aus, die in der Kompaktbenachrichtigung angezeigt werden sollen. Konnte die angegebene URL nicht erkennen. Mit einer anderen Anwendung öffnen\? Fünfte Aktionstaste Vierte Aktionstaste @@ -655,7 +655,7 @@ Als Nächstes eingereiht Als Nächstes in Wiedergabe einreihen Verarbeite … Kann einen Moment dauern - Nach Aktualisierungen suchen + Nach Aktualisierungen suchen Suche nach Aktualisierungen … Manuelle Prüfung auf neue Versionen Neue Feed-Elemente @@ -806,4 +806,17 @@ Wiedergabeliste teilen Teile die Wiedergabeliste mit Details wie dem Namen der Wiedergabeliste und den Videotiteln oder als einfache Liste von Video-URLs - %1$s: %2$s + + %s Antwort + %s Antworten + + Mehr zeigen + Weniger zeigen + Bearbeite jede Benachrichtigungsaktion unten, indem du auf sie tippst. Die ersten drei Aktionen (Abspielen/Pause, Zurück und Weiter) sind vom System vorgegeben und können nicht angepasst werden. + Nicht genug freier Speicher auf dem Gerät + Einstellungen zurücksetzen + Setzt alle Einstellungen auf ihre Standardwerte zurück + Ja + Nein + Sichern und Wiederherstellen \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index c2ab656fe..cb5958dc5 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -395,7 +395,7 @@ δεν είναι δυνατή η αντικατάσταση του αρχείου Υπάρχει μια εκκρεμής λήψη με αυτό το όνομα Το NewPipe τερματίστηκε ενώ επεξεργάζονταν το αρχείο - Δεν υπάρχει αρκετός χώρος στη συσκευή + Δεν υπάρχει αρκετός χώρος στη συσκευή Η πρόοδος χάθηκε, επειδή το αρχείο διαγράφηκε Λήξη χρονικού ορίου σύνδεσης Θέλετε να διαγράψετε το ιστορικό λήψεων σας ή να διαγράψετε όλα τα αρχεία που έχετε λάβει; @@ -482,7 +482,7 @@ Ανάμιξη Επανάληψη Μπορείτε να επιλέξετε το πολύ τρεις ενέργειες για εμφάνιση στη σύντομη ειδοποίηση! - Επεξεργαστείτε κάθε ενέργεια ειδοποίησης παρακάτω πατώντας πάνω της. Επιλέξτε έως και τρεις από αυτές για να εμφανίζονται στη σύντομη ειδοποίηση, χρησιμοποιώντας τα πλαίσια ελέγχου στα δεξιά + Επεξεργαστείτε κάθε ενέργεια ειδοποίησης παρακάτω πατώντας πάνω της. Επιλέξτε έως και τρεις από αυτές για να εμφανίζονται στη σύντομη ειδοποίηση, χρησιμοποιώντας τα πλαίσια ελέγχου στα δεξιά. Κουμπί πέμπτης ενέργειας Κουμπί τέταρτης ενέργειας Κουμπί τρίτης ενέργειας @@ -595,7 +595,7 @@ Αυτόματο (θέμα συσκευής) Νυχτερινό θέμα Εμφάνιση λεπτομερειών καναλιού - Απενεργοποιήστε το media tunneling, αν εμφανίζεται μαύρη οθόνη ή διακοπτόμενος ήχος κατά την αναπαραγωγή βίντεο + Απενεργοποιήστε το media tunneling, αν παρατηρείτε μαύρη οθόνη ή διακοπές κατά την αναπαραγωγή βίντεο. Απενεργοποίηση media tunneling Εσωτερικό Ιδιωτικό @@ -654,7 +654,7 @@ Επεξεργασία... Μπορεί να πάρει λίγο χρόνο Έλεγχος αναβάθμισης… Χειροκίνητος έλεγχος για νέα έκδοση - Έλεγχος αναβάθμισης + Έλεγχος αναβάθμισης Νέα αντικείμενα τροφοδοσίας Εμφάνιση «Κατάρρευσης αναπαραγωγέα» Εμφανίζει μια επιλογή κατάρρευσης κατά τη χρήση του αναπαραγωγέα @@ -767,7 +767,7 @@ Συνδρομητές Ποιες καρτέλες εμφανίζονται στις σελίδες των καναλιών Καρτέλες καναλιών - Shorts + Σύντομα Λήψη καρτελών καναλιών Σχετικά Άλμπουμ @@ -806,4 +806,11 @@ Κοινοποίηση λίστας Μοιραστείτε τη λίστα αναπαραγωγής με λεπτομέρειες όπως το όνομα της λίστας αναπαραγωγής και τους τίτλους βίντεο ή ως μια απλή λίστα διευθύνσεων URL βίντεο - %1$s: %2$s + + %s απάντηση + %s απαντήσεις + + Εμφάνιση περισσοτέρων + Εμφάνιση λιγότερων + Επεξεργαστείτε κάθε ενέργεια ειδοποίησης παρακάτω πατώντας σε αυτήν. Οι τρεις πρώτες ενέργειες (αναπαραγωγή/παύση, προηγούμενηο και επόμενο) ορίζονται από το σύστημα και δεν μπορούν να τροποποιηθούν. \ No newline at end of file diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 44e3348ca..3dc932388 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -406,7 +406,7 @@ Elŝutita dosieron kun ĉi tiu nomo jam ekzistas Estas pritraktata elŝuto kun ĉi tiu nomo NewPipe estis fermita dum laborante sur la dosiero - Neniu spaco havebla sur la aparato + Neniu spaco havebla sur la aparato Progreso perdita, ĉar la dosiero estis forviŝita Eltempiĝo de Konekto Ŝangi la servon, nuntempe elektita: @@ -610,4 +610,5 @@ Rilatajn erojn Solvi Malsukcesis kopii al la tondujo + Oni petos al vi kien salvi ĉiujn elŝutojn \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 499610d9b..ce0b32603 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -47,7 +47,7 @@ Miniatura del avatar del usuario Contenido Mostrar contenido con restricción de edad - Pulsa la lupa para empezar. + Toca la lupa para empezar. En directo Descargas Descargas @@ -376,7 +376,7 @@ No encontrado Falló el posprocesamiento NewPipe se cerró mientras se trabajaba en el archivo - No hay suficiente espacio disponible en el dispositivo + No hay suficiente espacio disponible en el dispositivo Progreso perdido, porque el archivo fue borrado El tiempo de conexión expiro No se puede recuperar esta descarga @@ -558,7 +558,7 @@ Almacenar en memoria (búfer) Repetir ¡Puedes seleccionar como máximo tres acciones para mostrar en la notificación compacta! - Edite cada una de las acciones de notificación que aparecen a continuación pulsando sobre ellas. Seleccione hasta tres de ellas para que se muestren en la notificación compacta utilizando las casillas de verificación de la derecha + Edite cada acción de notificación pulsando sobre ella. Seleccione hasta tres de ellas para que se muestren en la notificación compacta utilizando las casillas de verificación de la derecha. Botón de quinta acción Botón de cuarta acción Botón de tercera acción @@ -667,7 +667,7 @@ Añadido el siguiente vídeo a la cola Añadir el siguiente vídeo a la cola Procesando… Podría tomar un momento - Buscar actualizaciones + Buscar actualizaciones Buscar nuevas versiones manualmente Buscando actualizaciones… Nuevos elementos en el muro @@ -822,4 +822,23 @@ Compartir la lista de reproducción Compartir las listas de reproducción con los detalles como el nombre de la lista y los títulos de los vídeos o como una simple lista de una dirección URL con los vídeos - %1$s: %2$s + + %s respuesta + %s respuestas + %s respuestas + + Ver más + Mostrar menos + Edite cada acción de notificación pulsando sobre ella. Las tres primeras acciones (reproducir/pausa, anterior y siguiente) las establece el sistema y no se pueden personalizar. + No hay suficiente espacio libre en el dispositivo + Copia de seguridad y restaurar + Reiniciar ajustes + Restablecer todos los ajustes a sus valores predeterminados + Restablecer todos los ajustes descartará todos sus ajustes preferidos y reiniciará la aplicación. +\n +\n¿Estas seguro que deseas continuar? + + No + NewPipe puede buscar automáticamente nuevas versiones de vez en cuando y notificarle cuando estén disponibles. +\n¿Quieres habilitar esto? \ No newline at end of file diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index fd84e5d45..1d3fcdcb8 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -411,7 +411,7 @@ Aja segi Korda Sa saad valida kuni kolm tegevust, mida kuvatakse lühiteavituses! - Muuda iga teavituse tegevusi sellel toksates. Vali märkekastides paremal kuni kolm teavitust, mida kuvada lühiteates + Muuda iga teavituse tegevusi sellel toksates. Vali märkekastides paremal kuni kolm teavitust, mida kuvada lühiteates. Viies tegevusnupp Neljas tegevusnupp Kolmas tegevusnupp @@ -505,7 +505,7 @@ Kustuta allalaadimiste ajalugu Seda allalaadimist ei saa uuesti alustada Ühendus aegus - Seadmes pole enam ruumi + Seadmes pole enam ruumi Sellise nimega allalaadimine on juba pooleli faili asendamine ei õnnestu Uus @@ -653,7 +653,7 @@ Lisa esitamiseks järgmisena Töötlen andmeid… Võib kuluda mõni hetk Kontrollin uuendusi… - Kontrolli uuendusi + Kontrolli uuendusi Kontrolli uuendusi käsitsi Uued andmevoo kirjed Näita „Jooksuta meediamängija kokku“ nupukest @@ -806,4 +806,22 @@ Jaga esitusloendit Jaga esitusloendit kas väga detailse teabega palade kohta või lihtsa url\'ide loendina - %1$s: %2$s + Näita veel + + %s vastus + %s vastust + + Näita vähem + Muuda iga teavituse tegevust sellel toksates. Kolm esimest tegevust (esita/peata esitus, eelmine video, järgmine video) on süsteemsed ja neid ei saa muuta. + Varundus ja taastamine + NewPipe võib aeg-ajalt automaatselt kontrollida uute versioonide olemasolu ning sind vastavalt teavitada. +\nKas sa soovid sellist võimalust kasuutada? + Lähtesta seadistused + Lähtesta kõik seadistused nende vaikimisi väärtusteks + Seadmes pole enam piisavalt vaba ruumi + Kui lähtestad kõik seadistused, siis kõik sinu muudetud seadistused asendatakse vaikimisi väärtustega ja rakendus käivitub uuesti. +\n +\nKas sa soovid jätkata? + Jah + Ei \ No newline at end of file diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 69a42d1c2..6f94a8f62 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -396,7 +396,7 @@ Ezin da fitxategia gainidatzi Badago izen bereko deskarga bat burutzeke NewPipe itxi egin da fitxategian lanean zegoela - Ez dago lekurik gailuan + Ez dago lekurik gailuan Progresioa galdu da, fitxategia ezabatu delako Zure deskargen historiala garbitu nahi duzu ala deskargatutako fitxategi guztiak ezabatu\? Mugatu deskargen ilara @@ -657,7 +657,7 @@ Jakinarazpenak erroreen berri emateko NewPipe-k errore bat aurkitu du, sakatu berri emateko Errore bat gertatu da, ikusi jakinarazpena - Bilatu eguneraketak + Bilatu eguneraketak Bilatu bertsio berriak eskuz Elementu berriak jarioan Ez da fitxategi kudeatzaile bat aurkitu ekintza honetarako. diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 0dc26070a..2afeaf286 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -384,7 +384,7 @@ ناتوانی در بازنویسی پرونده یک بارگیری دیگر با همین نام در صف قرار دارد نیوپایپ در خلال کار روی پرونده، بسته شد - فضایی روی دستگاه باقی نمانده است + فضایی روی دستگاه باقی نمانده است پیشرفت کار متوفق شد زیرا پرونده پاک شده است پایان زمان اتصال می‌خواهید تاریخچه بارگیری را پاک کنید یا همه پرونده‌هایی که بارگیری شده‌اند؟ @@ -652,7 +652,7 @@ بعدی در صف گذاشته شد در صف گذاشتن بعدی در حال پردازش… ممکن است کمی طول بکشد - بررسی به‌روز رسانی‌ها + بررسی به‌روز رسانی‌ها بررسی دستی برای نگارش‌های جدید بررسی کردن به‌روز رسانی‌ها… موارد خوراک جدید diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 958c82d07..67350d7ba 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -481,7 +481,7 @@ Tätä latausta ei voi palauttaa Yhteys aikakatkaistiin Eteneminen menetettiin, koska tiedosto poistettiin - Laitteella ei ole tilaa + Laitteella ei ole tilaa NewPipe suljettiin, kun se käsitteli tiedostoa Jälkikäsittely epäonnistui Ei löytynyt @@ -652,7 +652,7 @@ Poistettu %1$s latausta Käsitellään… Voi kestää hetken - Tarkista päivitykset + Tarkista päivitykset Tarkista manuaalisesti onko uusia versioita saatavilla Tarkistetaan päivityksiä… Ilmoitukset, joilla raportoidaan virheistä @@ -789,7 +789,14 @@ Kanavat Olet nyt tilannut tämän kanavan Edellinen stream - + Live Haluatko poistaa kaikki ylimääräiset identtiset suoratoistot tästä soittolistasta\? Suoratoistot, joita lataaja ei vielä tue, ei näytetä + + %s vastaus + %s vastausta + + Näytä lisää + Näytä vähemmän + Kanavan välilehdet \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 738f9f58f..9994abb4c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -395,7 +395,7 @@ impossible d’écraser le fichier Il y a un téléchargement en attente avec ce nom NewPipe a été fermé alors qu’il travaillait sur le fichier - Aucun espace disponible sur l’appareil + Aucun espace disponible sur l’appareil Progression perdue car le fichier a été supprimé Voulez-vous effacer l’historique de téléchargement ou supprimer tous les fichiers téléchargés \? Limiter la file d’attente de téléchargement @@ -557,7 +557,7 @@ Lire aléatoirement Répéter Vous pouvez sélectionner au maximum trois actions à faire figurer dans la notification compacte ! - Modifiez chaque action de notification ci-dessous en appuyant dessus. Sélectionnez jusqu’à trois d’entre elles pour les faire apparaitre dans la notification compacte en utilisant les cases à cocher à droite + Modifiez chaque action de notification ci-dessous en appuyant dessus. Sélectionnez jusqu’à trois d’entre elles pour les faire apparaître dans la notification compacte en utilisant les cases à cocher à droite. Cinquième bouton d’action Quatrième bouton d’action Troisième bouton d’action @@ -668,7 +668,7 @@ Traitement en cours… Veuillez patienter Vérifier manuellement de nouvelles versions Vérification des mises à jour… - Vérifier les mises à jour + Vérifier les mises à jour Nouveaux éléments du flux Faire planter le lecteur Afficher « Faire planter le lecteur » @@ -821,4 +821,12 @@ Avatars du téléverseur Sélectionnez la qualité des images et si les images doivent être chargées, pour réduire l\'utilisation de la mémoire et de données. Les modifications vident à la fois le cache des images en mémoire et sur le disque — %s Lire + + %s réponse + %s réponses + %s réponses + + Modifiez chaque action de notification ci-dessous en appuyant dessus. Les trois premières actions (lire/pause, précédent, suivant) sont définies par le système et ne peuvent pas être personnalisées. + Afficher plus + Afficher moins \ No newline at end of file diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index bfc8aa6e1..efe1b7f5e 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -419,7 +419,7 @@ Non se pode recuperar esta descarga O tempo de espera da conexión Perdeuse o progreso porque se eliminou o ficheiro - Non queda espazo no dispositivo + Non queda espazo no dispositivo NewPipe pechouse mentres se traballaba no ficheiro Fallou o post-procesamento Non se atopou @@ -658,7 +658,7 @@ NewPipe atopou un erro, presione para reportar Comentario fixado Enfileirado - Procurar actualizacións + Procurar actualizacións Procurar manualmente novas versións A procurar actualizacións… A partir do Android 10, só o \'Sistema de Acceso ao Almacenamento\' está soportado diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 729db00b6..befde2d53 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -401,7 +401,7 @@ לא ניתן לשכתב את הקובץ כבר יש הורדה ממתינה בשם הזה NewPipe נסגר בזמן העבודה על הקובץ - לא נשאר מקום במכשיר + לא נשאר מקום במכשיר התהליך אבד כיוון שהקובץ נמחק החיבור המתין זמן רב מדי למחוק את היסטוריית ההורדות שלך או למחוק את כל הקבצים שהורדת\? @@ -564,7 +564,7 @@ ערבוב חזרה ניתן לבחור עד שלוש פעולות בלבד שתופענה בהתראות המצומצמות! - ניתן לערוך כל התראה להלן בלחיצה עליה. מותר לבחור עד שלוש מהן שתופענה בהתראה המצומצמת באמצעות תיבות הסימן שמשמאל + ניתן לערוך כל התראה להלן בלחיצה עליה. מותר לבחור עד שלוש מהן שתופענה בהתראה המצומצמת באמצעות תיבות הסימן שמשמאל. כפתור פעולה חמישי כפתור פעולה רביעי כפתור פעולה שלישי @@ -676,7 +676,7 @@ נוסף כהבא בתור הוספה כהבא בתור מתבצע עיבוד… נא להמתין רגע קט - איתור עדכונים + איתור עדכונים מתבצע איתור עדכונים… לנסות לאתר גרסאות חדשות ידנית פריטים חדשים בהזנה @@ -832,4 +832,12 @@ שיתוף רשימת נגינה שיתוף רשימת נגינה עם פרטים כגון שם רשימת נגינה וכותרות סרטונים או כרשימה פשוטה של כתובות סרטונים - %1$s: %2$s + להציג עוד + להציג פחות + + תשובה %s + %s תשובות + %s תשובות + + אפשר לערוך כל פעולה בהתראה להלן על ידי נגיעה בה. שלוש הפעולות הראשונות (נגינה/השהיה, הקודם והבא) מוגדרות על ידי המערכת ונעולות לעריכה. \ No newline at end of file diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 9111f9444..756b1878a 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -200,7 +200,7 @@ इतिहास, सब्सक्रिप्शन, प्लेलिस्ट और सेटिंग निर्यात करें बाहरी प्लेयर्स इन प्रकार के लिंक सपोर्ट नहीं करते कोई वीडियो स्ट्रीम नहीं मिला - कोई ऑडियो स्ट्रीम नहीं मिली + कोई ऑडियो स्ट्रीम नहीं मिला फिर से क्रम देने के लिए खींचें बनाइये ख़ारिज करें @@ -379,7 +379,7 @@ फाइल को ओवरराइट नहीं कर सकते इस नाम का एक डाउनलोड बाकी है फ़ाइल पर कार्य करते समय न्यूपाइप बंद किया गया - डिवाइस पर जगह समाप्त + डिवाइस पर जगह समाप्त प्रगति खो गई, क्योंकि फ़ाइल मिटा दी गई थी कनेक्शन का समय समाप्त क्या आप अपना डाउनलोड इतिहास मिटाना चाहते हैं या सभी डाउनलोड की गई फ़ाइलों को हटाना चाहते हैं\? @@ -471,7 +471,7 @@ शफल करें दोहराएं आप कंपैकट नोटीफिकेशन में दिखाए जाने वाले विकल्प में से अधिकतम 3 को चुन सकते है ! - नीचे दी गई प्रत्येक नोटीफिकेशन क्रिया को उस पर टैप करके संपादित करें। दाईं ओर चेकबॉक्स का उपयोग करके उनमें से अधिकतम तीन का चयन करें जिन्हें कंपैकट नोटीफिकेशन में दिखाया जाना है + नीचे दी गई प्रत्येक नोटीफिकेशन क्रिया को उस पर टैप करके संपादित करें। दाईं ओर चेकबॉक्स का उपयोग करके उनमें से अधिकतम तीन का चयन करें जिन्हें कंपैकट नोटीफिकेशन में दिखाया जाना है। पांचवा एक्शन बटन चतुर्थी एक्शन बटन तृतीय एक्शन बटन @@ -579,7 +579,7 @@ नेटवर्क कनेक्शन आवश्यक नई स्ट्रीम अधिसूचनाएं कोई भी नेटवर्क - अपडेट के लिए जाँच करें + अपडेट के लिए जाँच करें निम्न गुणवत्ता (छोटा) सीकबार थंमनेल पूर्वावलोकन उच्च गुणवत्ता (बड़ा) @@ -628,7 +628,7 @@ सैमीटोन डाउनलोड संपूर्ण - %s डाउनलोडस संपूर्ण + %s डाउनलोड संपूर्ण %d दिन @@ -806,4 +806,11 @@ प्लेलिस्ट साझा करें प्लेलिस्ट को प्लेलिस्ट नाम और वीडियो शीर्षक जैसे विवरण के साथ या वीडियो यूआरएल की एक सरल सूची के रूप में साझा करें - %1$s: %2$s + + %s जवाब + %s जवाब + + और दिखाओ + नीचे दी गई प्रत्येक अधिसूचना कार्रवाई पर टैप करके उसे संपादित करें। पहली तीन क्रियाएँ (चलाएँ/रोकें, पिछली और अगली) सिस्टम द्वारा निर्धारित की जाती हैं और इन्हें अनुकूलित नहीं किया जा सकता है। + कम दिखाएं \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 13535994a..f6c2120f7 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -15,7 +15,7 @@ Dijeli s Koristi vanjski video player Uklanja audiosnimku pri nekim rezolucijama - Koristi vanjski audio player + Koristi eksterni audio player Pretplati se Pretplaćeno Pretplata na kanal otkazana @@ -36,7 +36,7 @@ Prikaži veće rezolucije Samo neki uređaji podržavaju reprodukciju 2K/4K videa Reproduciraj s Kodijem - Instalirati nedostajući Kode program\? + Instalirati nedostajuću Kore aplikaciju? Prikaži opciju „Reproduciraj pomoću Kodija” Prikaži opciju za reproduciranje videozapisa putem Kodija Audiosnimka @@ -56,7 +56,7 @@ Prati gledana videa Nastavi reprodukciju Nastavi reproducirati nakon prekidanja (npr. telefonski pozivi) - Preuzmi + Preuzimanje Prikaži videa „Sljedeći” i „Slični” URL nije podržan Zadani jezik sadržaja @@ -69,7 +69,7 @@ Uživo Preuzimanja Preuzimanja - Prijavi grešku + Izvještaj o grešci Sve Isključeno Očisti @@ -80,14 +80,14 @@ Nije bilo moguće obraditi stranicu Sadržaj nije dostupan Nije bilo moguće postaviti izbornik za preuzimanje - Program/korisničko sučelje su preknuli raditi + Aplikacija/korisničko sučelje su preknuli raditi Oprosti, ovo se nije trebalo dogoditi. - Prijavi pogrešku putem e-maila + Prijavi putem e-maila Žao nam je, došlo je do neke greške. Prijavi Informacije: Što se dogodilo: - Što:\\nZahtjev:\\nJezik sadržaja:\\nZemlja sadržaja:\\nJezik programa:\\nUsluga:\\nGMT vrijeme:\\nPaket:\\nVerzija:\\nVerzija OS-a: + Što:\\nZahtjev:\\nJezik sadržaja:\\nZemlja sadržaja:\\nJezik aplikacije:\\nUsluga:\\nGMT vrijeme:\\nPaket:\\nVerzija:\\nVerzija OS-a: Tvoj komentar (na engleskom): Detalji: Pokreni video, trajanje: @@ -128,7 +128,7 @@ © %1$s od %2$s pod %3$s O aplikaciji i ČPP Licence - Slobodan i mali YouTube program za Android. + Slobodan i mali streaming na Android uređaju. Pogledaj na GitHubu NewPipe licenca Ako imate ideja za prijevod, promjene u dizajnu, čišćenje koda ili neke veće promjene u kodu, pomoć je uvijek dobro došla. Što više radimo, to bolji postajemo! @@ -181,8 +181,8 @@ Novi i popularni Ukloni Detalji - Postavke za audiosnimke - Drži pritisnuto za dodavanje u popis izvođenja + Postavke za audio snimke + Drži pritisnuto za dodavanje u popis [Nepoznato] Doniraj Web-stranica @@ -212,7 +212,7 @@ Ispuni Zumiraj Automatski generirani - Praćenje curenja memorije može uzrokovati greške u radu programa prilikom odlaganje gomile + Praćenje curenja memorije može uzrokovati greške u radu aplikacije prilikom odlaganje gomile Izvijesti o krajevima životnog ciklusa Prikaži informacije Zabilježene playliste @@ -302,12 +302,12 @@ Datoteka obrisana Obavijest za nove NewPipe verzije Briše povijest ključnih riječi pretraživanja - Vanjska pohrana nije dostupna + Eksterna memorija nije dostupna Aktualiziranja - Prikaži obavijest i zatraži aktualiziranje programa kad je dostupna nova verzija + Prikaži obavijest za aktualiziranje aplikacije kad je dostupna nova verzija Popis - Popločeno - Dostupna je nova verzija za NewPipe! + Mreža + Dostupna je nova NewPipe verzija! Preuzimanje nije uspjelo Prikaži pogrešku Izbriši sve podatke web-stranica iz predmemorije @@ -316,9 +316,9 @@ Nastavi završavati (ne ponavljajući) popis reprodukcija dodavanjem povezanog streama Zadana zemlja sadržaja Otkrivanje grešaka - Obavijest o novoj verziji programa - Preuzimanje na vanjsku SD karticu nije moguće. Ponovo postaviti lokaciju mape za preuzimanje\? - Vanjski playeri ne podržavaju ove vrste poveznica + Obavijest o novoj verziji aplikacije + Preuzimanje na eksternu SD karticu nije moguće. Ponovo postaviti lokaciju mape za preuzimanje? + Eksterni playeri ne podržavaju ove vrste poveznica Nije pronađen nijedan videozapis Nije pronađena nijedna audiosnimka Nema takve datoteke/izvora sadržaja @@ -328,13 +328,13 @@ Obnovi standardne vrijednosti Želiš li obnoviti standardne vrijednosti\? Broj pretplatnika nije dostupan - NewPipe razvijaju volonteri koji provode vrijeme kako bi doprinijeli najboljem iskustvu. Doprinesi programerima kako bi poboljšali NewPipe dok uživaju u šalici kave. + NewPipe razvijaju volonteri koji provode vrijeme kako bi doprinijeli najboljem korisničkom iskustvu. Doprinesi programerima kako bi poboljšali NewPipe dok uživaju u šalici kave. Koje su kartice prikazane na glavnoj stranici Konferencije Željena radnja za otvaranje Zadana radnja pri otvaranju sadržaja — %s Titlovi - Promijeni veličinu podnaslova i pozadinske stilove playera. Zahtijeva ponovno pokretanje programa + Promijeni veličinu titlova i stilove pozadine playera. Zahtijeva ponovno pokretanje aplikacije Prisilno izvijesti o neisporučivim Rx iznimaka izvan fragmenta ili životnog ciklusa aktivnosti nakon odlaganja Uvezi SoundCloud profil upisom URL-a ili svog ID-a: \n @@ -345,8 +345,8 @@ Brzina Visina tona Odspoji (može prouzročiti izobličenje) - Smanji prilikom mijenjanje programa - Radnja prilikom prebacivanja na drugi program iz glavnog video playera – %s + Smanji prilikom mijenjanje aplikacije + Radnja prilikom prebacivanja na drugu aplikaciju iz glavnog video playera – %s Smanji na pozadinski player Smanji na skočni player Način prikaza kao popis @@ -356,7 +356,7 @@ pauzirano stavljeno u popis izvođenja naknadna obrada - Popis izvođenja + Dodaj u popis Sustav je odbio radnju Generiraj jedinstveni naziv Prepiši @@ -384,8 +384,8 @@ Nije bilo moguće učitati komentare Zatvori NewPipe je copyleft libre softver: Može se koristiti, proučavati i poboljšavati po volji. Konkretno, može se redistribuirati i / ili modificirati pod uvjetima GNU opće javne licence koju je objavila zaklada Free Software Foundation, pod verzijom 3 licence, ili (po vlastitom izboru) bilo koje kasnije verzije. - Projekt NewPipe ozbiljno shvaća tvou privatnost. Stoga program ne prikuplja nikakve podatke bez tvog pristanka. -\nNewPipe pravila o privatnosti detaljno objašnjavaju koji se podaci šalju i spremaju kad šalješ izvještaje o prekidu rada programa. + Projekt NewPipe ozbiljno shvaća temu o privatnosti. Stoga aplikacija ne prikuplja podatke bez tvog pristanka. +\nNewPipe pravila o privatnosti detaljno objašnjavaju koji se podaci šalju i spremaju kad šalješ izvještaje o prekidu rada aplikacije. Kako bismo se uskladili s Europskom općom uredbom o zaštiti podataka (GDPR), ovime upozoravamo na NewPipe politiku privatnosti. Pažljivo je pročitaj. \nZa slanje izvješća o pogreškama moraš prihvatiti politiku privatnosti. Nastavi reprodukciju @@ -406,7 +406,7 @@ Izbrisati sve pozicije reprodukcije\? Nitko ne gleda Nitko ne sluša - Jezik će se promijeniti nakon ponovnog pokretanja programa + Jezik će se promijeniti nakon ponovnog pokretanja aplikcije Standardni kiosk Podržani su samo HTTP URL-ovi Lokalni @@ -414,8 +414,8 @@ Automatski generirano (prenositelj nedefiniran) Očisti povijest preuzimanja Izbriši preuzete datoteke - Dopusti prikaz iznad drugih programa - Jezik programa + Dopusti prikaz preko drugih aplikacija + Jezik aplikacije Zadani sustav Videa Isključi zvuk @@ -424,22 +424,22 @@ Želiš li izbrisati ovu grupu\? Nova Uvijek aktualiziraj - Omogući brz način - Onemogući brz način - Memorija uređaja je popunjena + Uključi brzi način + Isključi brzi način + Memorija uređaja je popunjena Najomiljeniji Pritisni „Gotovo” kad je riješeno Gotovo ∞ videa Više od 100 videa Prijavi grešku na GitHub-u - Umjetnici + Izvođači Albumi Pjesme Stvoren od %s Nikada Ograniči popis preuzimanja - Koristi birač mapa sustava (SAF) + Koristi sustavksi birač mapa (SAF) Ukloni pregledano Ukloni pogledana videa\? @@ -471,12 +471,12 @@ Gumb druge radnje Gumb prve radnje Prikazuju se rezultati za: %s - Nije bilo moguće prepoznati URL. Želiš li otvoriti s drugim programom\? + Nije bilo moguće prepoznati URL. Otvoriti s jednom drugom aplikacijom? Odreži sličicu na omjer 1:1 Učitavanje u predmemoriju Istovremeno se pokreće jedno preuzimanje Dodano u popis izvođenja - Dodaj u popis izvođenja + Dodaj u popis Reproduciraj popis izvođenja Automatski popis izvođenja Popis izvođenja aktivnog playera će se zamijeniti @@ -509,7 +509,7 @@ Da, i djelomično pogledana videa Odaberi jednu instancu Aplikacija će te pitati kamo spremati preuzimanja. -\nOmogući birač mapa sustava (SAF), ako želiš preuzimati na vanjsku SD karticu +\nUključi sustavksi birač mapa (SAF) ako želiš preuzeti na eksternu SD karticu Nije moguće obnoviti ovo preuzimanje Napredak je izgubljen, jer je datoteka izbrisana NewPipe se zatvorio tijekom rada s datotekom @@ -550,7 +550,7 @@ Promiješaj Ponovi Provjeri je li problem već postoji. Prijavljivanje istog već prijavljenog problema krade nam vrijeme koje bismo mogli utrošiti na ispravljanje greške. - Za uređivanje radnji u obavijestima, dodirni ih. Označi do tri radnje za prikaz u kompaktnoj obavijesti koristeći oznake na desnoj strani + Uredi radnje obavijesti dodirom. Označi do tri radnje za prikaz u kompaktnoj obavijesti koristeći potvrdna polja na desnoj strani. Zbog ograničenja ExoPlayera, trajanje premotavanja postavljeno je na %d s Neka Android prilagodi boju obavijesti prema glavnoj boji sličice (ovo nije dostupno na svim uređajima) Oboji obavijest @@ -560,7 +560,7 @@ Koristi sličicu za pozadinu zaključanog ekrana i za obavijesti Prikaži sličicu Prikaži izvorno vrijeme stavki - „Storage Access Framework” omogućuje preuzimanje na SD karticu + „Storage Access Framework” dozvoljava preuzimanje na eksternu SD karticu Izvorni tekstovi usluga bit će vidljivi u stavkama prijenosa Dostupno je u nekim uslugama. Obično je puno brže, ali može dohvatiti ograničenu količinu stavki i često nepotpune podatke (npr. bez trajanja, vrste stavke, bez stanja uživo) Misliš da je učitavanje feeda presporo\? Ako da, pokušaj omogućiti brzo učitavanje (možeš ga promijeniti u postavkama ili pritiskom na donji gumb). @@ -581,7 +581,7 @@ Isključi za skrivanje polja metapodataka s dodatnim podacima o autoru streama, sadržaju streama ili zahtjevu za pretraživanje Prikaži metapodatke Povezane stavke - Nijedan program na tvom uređaju ovo ne može otvoriti + Nijedna aplikacija na tvom uređaju ovo ne može otvoriti Poglavlja Opis Komentari @@ -601,12 +601,12 @@ Ovaj je video dostupan samo za „YouTube Music Premium” članove, stoga ga NewPipe ne može emitirati ili preuzeti. Ovaj sadržaj je privatan, stoga ga NewPipe ne može emitirati ili preuzeti. Ovaj sadržaj nije dostupan u tvojoj zemlji. - Prekini program + Prekini aplikaciju Riješi - Noćna tema + Tamna tema Prikaži detalje kanala Isključi tuneliranje medija ako doživiš crni ekran ili isprekidanu reprodukciju videa. - Iskljuci medija tuneling + Isključi tuneliranje medija Isklj. Uklj. Način rada na tabletu @@ -615,14 +615,14 @@ Privatno Nenavedeno Javno - Poslužitelj + Računalo Podrška Jezik Dobna granica Licenca Oznake Kategorija - Onemogući biranje teksta u opisu + Isključi biranje teksta u opisu Omogući biranje teksta u opisu Račun ukinut Autorov račun je ukinut. @@ -637,7 +637,7 @@ Visoka kvaliteta (veća) Pregled sličica premotavanja Mapa za preuzimanje još nije postavljena, odaberi standardnu mapu za preuzimanje - Komentari su onemogućeni + Komentari su isključeni Označi kao pogledano Način rada brzog feeda ne pruža više informacija o ovome. Interno @@ -646,15 +646,15 @@ %s pruža ovaj razlog: Obrada u tijeku … Može malo potrajati Za ukljanjanje stavki povuci ih - Prikazati indikatore slike + Prikaži indikatore slike Preuzimanje je gotovo %s preuzimanja su gotova %s preuzimanja su gotova Pokreni glavni player u cjeloekranskom prikazu - Reproduciraj sljedeći - Sljedeći u popisu izvođenja + Dodaj u popis kao sljedeći + Dodano u popis kao sljedeći Prikaži Picassove vrpce u boji na slikama koje označavaju njihov izvor: crvena za mrežu, plava za disk i zelena za memoriju Izbrisano %1$s preuzimanje @@ -665,7 +665,7 @@ Ručno traži nove verzije Traženje novih verzija … Prijedlozi lokalne pretrage - Traži nove verzije + Traži nove verzije Nemoj pokretati videa u mini playeru, već izravno pokreni cjeloekranski prikaz, ako je automatsko okretanje zaključano. Mini playeru i dalje možeš pristupiti napuštanjem cjeloekranskog prikaza Nove stavke feeda Obavijest o prijavi greške @@ -680,7 +680,7 @@ Novi videozapisi Obavijesti novih streamova od pretplaćenih kanala Želiš li izbrisati sve preuzete datoteke\? - Obavijesti su onemogućene + Obavijesti su isljučene Pretplatio/la si se na ovaj kanal , Uključi/isključi sve @@ -695,16 +695,16 @@ Potrebna mrežna veza Primaj obavijesti Za ovu radnju nije pronađen odgovarajući upravljač datoteka. -\nInstaliraj upravljač datoteka ili pokušaj onemogućiti „%s” u postavkama preuzimanja +\nInstaliraj upravljač datoteka ili pokušaj isključiti „%s” u postavkama preuzimanja Prikvačeni komentar Prikazuje opciju prekida rada kad se player koristi Prikaži „Prekini rad playera” ExoPlayer standard Posto Poluton - Streamovi koje program za preuzimanje još ne podržava se ne prikazuju - Vanjski playeri ne podržavaju odabrani stream - Promijeni veličinu intervala učitavanja (trenutačno %s). Niža vrijednost može ubrzati početno učitavanje videa. Promjene zahtijevaju ponovno pokretanje playera + Streamovi koje aplikacija za preuzimanje još ne podržava se ne prikazuju + Eksterni playeri ne podržavaju odabrani stream + Promijenite veličinu intervala učitavanja progresivnog sadržaja (trenutno %s). Niža vrijednost može ubrzati učitavanje %s novi stream %s nova streama @@ -713,9 +713,112 @@ Veličina intervala učitavanja reprodukcije Nepoznat format Nepoznata kvaliteta - Nijedan stream audiosnimaka nije dostupan za vanjske playere - Nijedan video stream nije dostupan za vanjske playere - Odaberi kvalitetu za vanjske playere + Nema audio prijenosa za eksterne playere + Nema video prijenosa za eksterne playere + Odaberi kvalitetu za eksterne playere Za ovu radnju nije pronađen odgovrajući upravljač datoteka. \nInstaliraj „Storage Access Framework” kompatibilni upravljač datoteka + Uredite neke ExoPlayer postavke. Ove promjene zahtjevaju ponovo pokretanje aplikacije + Desna gesta + Odaberite gestu za lijevu polovicu zaslona + Lijeva gesta + Svjetlina + ExoPlayer postavke + Uredite svaku radnju obavijesti ispod tako da je dodirnete. Prve tri akcije (reprodukcija/pauza, prethodna i sljedeća) postavlja sustav i ne mogu se prilagoditi. + Odaberite gestu za desnu polovicu zaslona + Glasnoća + Ništa + Pregledano + Djelomično pregledano + Najava + Razvrstaj + Koristi razervnu funkciju ExoPlayer dekodera + Ignoriranje hardverskih medijskih gumba + Korisno, na primjer, ako koristite slušalice s pokvarenim fizičkim gumbima + Odaberite zvučni zapis s opisima za slabovidne osobe ako je dostupan + Preferiraj originalni zvuk + Odaberite izvorni audio zapis bez obzira na jezik + Preferirajte opisni zvuk + Odaberi audio snimku za eksterne playere + Nepoznato + %1$s %2$s + Nema prijenosa + Položaj glavnih kartica + Premjesti glavni birač kartica dolje + Nema prijenosa uživo + Neuspjelo kopiranje u međuspremnik + Često postavljena pitanja + Ako imaš problema s korištenjem aplikacije, pogledaj odgovore na česta pitanja! + Pogledaj na web stranici + Audio snimka: %s + Traka audio snimke + Zasivljene playliste već sadrže ovu stavku. + Poništi stalni prikaz sličica + Duplikat je dodan %d put(a) + Kartica + Koristiš najnoviju NewPipe verziju + Dodirani za preuzimanje %s + Ukloni duplikate + Ukloniti duplikate? + Želiš li ukloniti sve duple prijenose iz ove playliste? + Pokaži sljedeće prijenose + Pokaži/Sakrij prijenose + Kartice za dohvaćanja kanala + Ova je opcija dostupna samo ako je %s odabrano za temu + Minijature + Avatari + Avatari prenositelja + Avatari podkanala + Pretplatnici + Natpisi + Audio zapis bi već trebao biti prisutan u ovom prijenosu + original + Uživo + Videa + Snimke + Kratka videa + sinkronizirano + opisno + Albumi + Kanali + Playliste + Kartice koje se prikazuju na stranici kanala + Uključi/Isključi položaj ekrana + Informacije + Kartice kanala + Uključi cjeloekranski prikaz + Prethodni prijenos + Sljedeći prijenos + Reproduciraj ponovo + Natrag + Naprijed + Kvaliteta slike + Više opcija + Odaberi kvalitetu slika i da li uopće učitati slike kako bi se smanjilo korištenje podataka i memorije. Promjene brišu predmemoriju slika u memoriji i na disku – %s + Ne učitavaj slike + Niska kvaliteta + Srednja kvaliteta + Visoka kvaliteta + \? + Dijeli playlistu s detaljima kao što su ime playliste i naslovi videa ili kao jednostavan popis URL-ova videa + Dijeli s naslovima + Dijeli popis URL-ova + – %1$s: %2$s + Pokaži više + Pokaži manje + Brzi modus + Učitavanje metapodataka … + Trajanje + Uvezi ili izvezi pretplate iz izbornika s 3 točke + Otvori popis reprodukcije + Reproduciraj + Dijeli playlistu + %1$s +\n%2$s + + %s odgovor + %s odgovora + %s odgovora + + Tuneliranje medija je standardno deaktivirano na tvom uređaju jer je poznato da model tvog uređaja to ne podržava. \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 0b3b10235..5a402c94c 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -348,7 +348,7 @@ A letöltést nem lehet helyrehozni Kapcsolati időtúllépés Az előrehaladás elveszett, mert a fájlt törölték - Nincs hely az eszközön + Nincs hely az eszközön A NewPipe leállt a fájl feldolgozása közben Utófeldolgozás sikertelen Nincs talalat @@ -569,7 +569,7 @@ Feldolgozás… Ez eltarthat egy ideig Az eltávolítás utáni, fragment vagy activity életcikluson kívüli, nem kézbesíthető Rx kivételek jelentésének kényszerítése Eredeti „ennyi ideje” megjelenítése az elemeken - Tiltsa le a médiacsatornázást, ha fekete képernyőt vagy akadozást tapasztal videólejátszáskor + Tiltsa le a médiacsatornázást, ha fekete képernyőt vagy akadozást tapasztal videólejátszáskor. Picasso színes szalagok megjelenítése a képek fölött, megjelölve a forrásukat: piros a hálózathoz, kék a lemezhez, zöld a memóriához Minden letöltésnél meg fogja kérdezni, hogy hova mentse el Válasszon egy példányt @@ -661,7 +661,7 @@ A „lejátszó összeomlasztása” lehetőség megjelenítése Megjeleníti az összeomlasztási lehetőséget a lejátszó használatakor Hangmagasság megtartása (torzítást okozhat) - Frissítések keresése + Frissítések keresése Ne jelenítse meg Megnézettek eltávolítása Eltávolítja a megnézett videókat\? @@ -762,4 +762,66 @@ Kezdőlap választó alulra helyezése Nincs élő adatfolyam Nincs adatfolyam + Az alábbi értesítési műveletek szerkesztéséhez koppintson rá. Az első három műveletet (lejátszás/szünet, előző és következő) a rendszer állítja be, és nem szabhatók testre. + Csatornalapok lekérése + A hírcsatorna frissítésekor lekérendő lapok. Ennek az opciónak nincs hatása, ha egy csatorna frissítése gyors módban történik. + Miniatűrök + Feltöltő avatarjai + Alcsatorna avatarok + Avatarok + Bannerek + Feliratkozók + Csatornák + Lejátszási listák + Albumok + Névjegy + Csatorna fülek + Milyen lapok jelennek meg a csatornaoldalakon + Lejátszási sor megnyitása + Képernyő tájolásának váltása + Teljes képernyőre váltás + Következő közvetítés + Előző közvetítés + Lejátszás + Visszajátszás + További opciók + Időtartam + Visszatekerés + Előre + Képminőség + Az adat- és memóriahasználat csökkentése érdekében válassza ki a képek minőségét valamint azt, hogy a képek egyáltalán betöltésre kerüljenek. A változtatások törlik a memóriában és a lemezen lévő képgyorsítótárat – %s + Ne töltsön be képeket + Alacsony minőség + Közepes minőség + Magas minőségű + \? + Lejátszási lista megosztása + Lejátszási lista megosztása olyan részletekkel, mint például a lejátszási lista neve és a videó címe, vagy a videó URL-jei egyszerű listájaként + Megosztás címekkel + %1$s +\n%2$s + + %s válasz + %s válaszok + + Továbbiak + Mutass kevesebbet + Metaadatok betöltése… + URL-lista megosztása + - %1$s: %2$s + Videók + Dalok + Rövidek + Élő + Nincs elég szabad hely az eszközön + Igen + Nem + Biztonsági mentés és visszaállítás + A NewPipe időről időre automatikusan ellenőrzi az új verziókat, és értesít, amint azok elérhetővé válnak. +\nSzeretné engedélyezni ezt? + Beállítások alaphelyzetbe állítása + Minden beállítás visszaállítása alapértelmezett értékre + Az összes beállítás visszaállítása elveti az összes preferált beállítást, és újraindítja az alkalmazást. +\n +\nBiztosan folytatja? \ No newline at end of file diff --git a/app/src/main/res/values-hy/strings.xml b/app/src/main/res/values-hy/strings.xml index 5becf32fb..dd307a2fa 100644 --- a/app/src/main/res/values-hy/strings.xml +++ b/app/src/main/res/values-hy/strings.xml @@ -151,7 +151,7 @@ Մեծացնել Գեներացված Ներմուծել ֆայլ - Ստուգել թարմացումները + Ստուգել թարմացումները Ինքնին Բարձր որակ (մեծ) Ցածր որակ (փոքր) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 2dc9ecdea..71900400e 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -4,23 +4,23 @@ Dipublikasikan pada %1$s Pasang Batal - Buka di browser + Buka di peramban Bagikan Unduh - Telusuri + Telusur Pengaturan Bagikan dengan Gunakan pemutar video eksternal Gunakan pemutar audio eksternal - Folder unduhan Video + Folder unduhan video Berkas video yang diunduh akan disimpan di sini Pilih folder unduhan untuk berkas video - Folder unduhan Audio + Folder unduhan audio Berkas audio yang diunduh akan disimpan di sini Pilih folder unduhan untuk berkas audio Pilih kualitas Putar dengan Kodi - Instal aplikasi Kode yang hilang\? + Pasang aplikasi Kore yang hilang? Tampilkan opsi \"Putar dengan Kodi\" Tampilkan opsi untuk memutar video via Kodi Audio @@ -66,7 +66,7 @@ Mulai Jeda Ceksum - OK + Oke Nama berkas Galat NewPipe Sedang Mengunduh @@ -74,7 +74,7 @@ Mohon tunggu… Disalin ke papan klip Silakan pilih folder unduhan di pengaturan - Pemutar stream tidak ada. Pasang VLC? + Pemutar stream tidak ditemukan. Pasang VLC? App/UI rusak Apa:\\nPermintaan:\\nBahasa Konten:\\nNegara Konten:\\nBahasa Apl:\\nLayanan:\\nWaktu GMT:\\nPaket:\\nVersi:\\nVersi OS: Thread @@ -85,19 +85,19 @@ r J T - Buka dalam mode popup + Buka di mode tampilan kecil Izin ini dibutuhkan untuk \nmembuka di mode sembul Memutar dalam mode sembul Dinonaktifkan Format video - Pilih kualitas mode popup + Kualitas popup bawaan Tampilkan kualitas yang lebih tinggi Hanya perangkat tertentu saja yang bisa memutar video 2K/4K Latar Belakang - Munculan + Sembulan Bersihkan - Menghapus audio pada beberapa resolusi + Menghapus audio di beberapa resolusi Ingat properti popup Ingat ukuran dan posisi terakhir popup Saran pencarian @@ -237,11 +237,11 @@ Isi Perbesar Otomatis dibuat - Pemutar stream tidak ada (pasang VLC untuk memutarnya). + Pemutar stream ditemukan (kamu bisa pasang VLC untuk memutarnya). Unduh berkas stream Tidak bisa mengubah langganan Tampilkan info - Tambahkan Ke + Tambah Ke Hapus riwayat tontonan Hapus riwayat video yang diputar dan posisi pemutaran Hapus seluruh riwayat tontonan\? @@ -254,7 +254,7 @@ 1 item dihapus. Daftar Putar Putar otomatis video berikutnya - Berhenti berlanggan channel + Berhenti berlangganan saluran Tidak bisa memperbarui langganan Langganan Gunakan tinjau cepat tak pasti @@ -322,7 +322,7 @@ Nihil Minimalkan ke pemutar latar belakang Minimalkan ke pemutar popup - Berhenti Berlanggan + Berhenti Berlangganan Pilih Tab Tema Pembaruan @@ -389,7 +389,7 @@ tidak bisa menimpa ulang berkas Ada unduhan yang dijeda dengan nama ini NewPipe telah ditutup saat sedang memproses berkas - Tidak ada ruang kosong tersisa pada perangkat + Tidak ada ruang kosong tersisa pada perangkat Kehilangan laju, karena berkas telah dihapus Apakah Anda yakin ingin menghapus semua riwayat unduhan dan berkas yang telah diunduh\? Batasi antrean unduhan @@ -534,7 +534,7 @@ Aduk Ulangi Anda bisa memilih hingga tiga tindakan untuk ditampilkan dalam notifikasi ringkas! - Sentuh untuk menyunting tindakan notifikasi di bawah. Pilih hingga tiga diantaranya untuk ditampilkan dalam notifikasi ringkas dengan menggunakan kotak centang di sebelah kanan + Sentuh untuk menyunting tindakan notifikasi di bawah. Pilih hingga tiga diantaranya untuk ditampilkan dalam notifikasi ringkas dengan menggunakan kotak centang di sebelah kanan. Tombol tindakan kelima Tombol tindakan keempat Tombol tindakan ketiga @@ -585,7 +585,7 @@ Tema malam Pengunduhan dimulai Tampilkan detail channel - Nonaktifkan terowongan media (tunnel) jiaka anda mengalami sebuah layar hitam atau kerusakan dalam memutar video. + Nonaktifkan terowongan media (tunnel) jika anda mengalami sebuah layar hitam atau kerusakan dalam memutar video. Nonaktifkan terowongan media (tunnel) Internal Privasi @@ -640,13 +640,13 @@ Tampilkan Ribon bewarna Picasso di atas gambar yang mengindikasikan asalnya: merah untuk jaringan, biru untuk disk dan hijau untuk memori Jangan memulai memutar video di mini player, tapi nyalakan langsung di mode layar penuh, jika rotasi otomatis terkunci. Anda tetap dapat mengakses mini player dengan keluar dari layar penuh Memproses… Mungkin butuh waktu sebentar - Periksa Pembaruan + Periksa Pembaruan Periksa manual untuk versi baru Memeriksa pembaruan… Item feed baru Tampilkan \"Mogokkan pemutar\" Menampilkan opsi penghentian ketika menggunakan pemain video - Hentikan pemain video + Hentikan pemutar video Notifikasi untuk melaporkan kegalatan Notifikasi laporan kegalatan Sebuah kegalatan terjadi, lihat notifikasinya @@ -793,4 +793,21 @@ Bagikan daftar putar dengan detail seperti nama daftar putar dan judul video atau sebagai daftar video URL yang sederhana Banner - %1$s: %2$s + Sentuh untuk menyunting tindakan notifikasi di bawah. Tiga tindakan pertama (mainkan/jeda, sebelumnya dan selanjutnya) disetel oleh sistem dan tidak bisa dikustomisasi. + Tampilkan lebih sedikit + Tampilkan lebih banyak + + %s balasan + + Tidak ada ruang yang cukup pada perangkat + Ya + Tidak + Atur ulang pengaturan + Atur ulang pengaturan ke nilai bawaan + Mengatur ulang semua pengaturan akan mengabaikan pengaturan Anda yang disukai dan memulai ulang aplikasi. +\n +\nApakah Anda yakin ingin melanjutkan? + NewPipe dapat memeriksa versi baru secara berkala dan memberi tahu Anda ketika ada yang baru. +\nApakah Anda ingin mengaktifkan ini? + Cadangkan dan pulihkan \ No newline at end of file diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index ee90a9333..ed5ebe99b 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -438,7 +438,7 @@ Tegund tengingar Allar Sýna tilkynningu þegar ný útgáfa er fáanleg - Leita að uppfærslum + Leita að uppfærslum Leita handvirkt fyrir uppfærslum Fela þegar skipt er um forrit Nota bakgrunnsspilara @@ -680,7 +680,7 @@ Netþjónninn sendir ekki gögn Netþjónninn styður ekki fjölþráðuð niðurhöl, reyndu aftur með @string/msg_threads = 1 NewPipe var lokað á meðan unnið var að skrá - Ekkert pláss eftir á tæki + Ekkert pláss eftir á tæki Framvinda tapaðist vegna þess að skránni var eytt Get ekki endurheimt þetta niðurhal Hreinsa niðurhalsferil diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 71b656234..35a722a57 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -399,7 +399,7 @@ impossibile sovrascrivere il file C\'è un download in corso con questo nome NewPipe è stato chiuso mentre lavorava sul file - Spazio insufficiente sul dispositivo + Spazio insufficiente sul dispositivo Progresso perso poiché il file è stato eliminato Vuoi cancellare la cronologia dei download o eliminare tutti i file scaricati\? Sarà avviato un solo download per volta @@ -549,7 +549,7 @@ La coda del lettore attivo sarà sostituita Chiedi prima di svuotare la coda Cambiare tipo di riproduzione potrebbe sostituire gli elementi in coda - Le azioni dei pulsanti della notifica possono essere modificate qua sotto. Selezionane fino a 3, utilizzando le caselle di controllo sulla destra. Verranno visualizzate nella notifica compatta + Le azioni dei pulsanti della notifica possono essere modificate qua sotto. Selezionane fino a tre da mostrare nella notifica compatta, usando le caselle di controllo sulla destra. Azione pulsante 5 Azione pulsante 4 Azione pulsante 3 @@ -664,7 +664,7 @@ Aggiunto alla coda come prossimo Accoda come prossimo Elaborazione… Potrebbe volerci un attimo - Controlla aggiornamenti + Controlla aggiornamenti Verifica manualmente la presenza di nuove versioni Controllo aggiornamenti… Nuovi elementi feed @@ -779,7 +779,7 @@ Iscritti Quali schede mostrare nelle pagine del canale Schede canale - Short + Shorts Caricamento metadati… Recupera schede del canale Informazioni @@ -819,4 +819,19 @@ Condividi playlist Condividi la playlist con dettagli come il suo nome e i titoli video o come un semplice elenco di URL video - %1$s: %2$s + + %s risposta + %s risposte + %s risposte + + Mostra altro + Le azioni dei pulsanti della notifica possono essere modificate qua sotto. Le prime tre (riproduci/pausa, precedente e successivo) sono impostate dal sistema e non possono essere cambiate. + Mostra meno + Non abbastanza spazio libero sul dispositivo + Backup e ripristino + Azzera le impostazioni + L\'azzeramento di tutte le impostazioni eliminerà tutte le proprie impostazioni e riavvierà l\'app. +\n +\nSei sicuro di voler procedere? + Azzera tutte le impostazioni ai loro valori predefiniti \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 796c6d650..6cbc3c084 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -390,7 +390,7 @@ ファイルを上書きできません 同じファイル名のダウンロードが既に進行中です ファイルの作業中に NewPipe が閉じられました - デバイスに空き容量がありません + デバイスに空き容量がありません ファイルが削除されたため、進行状況が失われました ダウンロード履歴、またはダウンロードしたファイルを消去しますか\? ダウンロード キューの制限 @@ -531,7 +531,7 @@ URL を認識できませんでした。他のアプリで開きますか? 通知に表示されるサムネイルを 16:9 から正方形にします サムネイルを正方形にする - 以下をタップして通知のアクションを編集します。右側にあるチェックボックスを使用してコンパクトな通知に表示するものを 3 つまで選択します + 以下をタップして通知のアクションを編集します。右側にあるチェックボックスを使用してコンパクトな通知に表示するものを3つまで選択します。 コンパクトな通知に表示されるアクションは 3 つまで選ぶことができます! 5 番目のアクションボタン 4 番目のアクションボタン @@ -639,7 +639,7 @@ 処理中… 少し時間がかかるかもしれません 新しいバージョンを手動で確認します アップデートを確認中… - アップデートを確認 + アップデートを確認 次をキューに追加 次をキューに追加しました クリエイターの心をこめて @@ -793,4 +793,11 @@ プレイリストを共有 プレイリスト名やビデオタイトルなどの詳細を含むプレイリスト、またはビデオURLのみのシンプルなリストとしてプレイリストを共有します - %1$s: %2$s + + %sの返信 + + もっと見る + 表示を少なくする + 以下の通知アクションをタップして編集します。 最初の3つのアクション (再生/一時停止、前へ、次へ)はシステムによって設定されており、カスタマイズすることはできません。 + デバイスの空き容量が不足しています \ No newline at end of file diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 9f6227388..ecb2a8495 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -426,7 +426,7 @@ ნებისმიერი ქსელი განახლებები მაჩვენე შეტყობინება აპის განახლების მოთხოვნით, როდესაც ხელმისაწვდომი იქნება ახალი ვერსია - Შეამოწმოთ განახლებები + Შეამოწმოთ განახლებები ხელით შეამოწმეთ ახალი ვერსიები მინიმიზაცია აპის გადამრთველზე მოქმედება სხვა აპზე გადასვლისას მთავარი ვიდეო დამკვრელიდან — %s @@ -461,7 +461,7 @@ სერვერი არ იღებს მრავალ ნაკადის ჩამოტვირთვებს, ხელახლა სცადეთ @string/msg_threads = 1 არ მოიძებნა შემდგომი დამუშავება ვერ მოხერხდა - მოწყობილობაზე არ არის დარჩენილი თავისუფალი ადგილი + მოწყობილობაზე არ არის დარჩენილი თავისუფალი ადგილი Კავშირის დრო ამოიწურა ამ ჩამოტვირთვის აღდგენა შეუძლებელია გადმოწერილი ფაილების წაშლა diff --git a/app/src/main/res/values-kmr/strings.xml b/app/src/main/res/values-kmr/strings.xml index aa4696c78..b55be410d 100644 --- a/app/src/main/res/values-kmr/strings.xml +++ b/app/src/main/res/values-kmr/strings.xml @@ -388,7 +388,7 @@ Vê dakêşanê nikare paşde bibe Dema girêdanê Pêşkeftin winda bû, ji ber ku pel hate jêbirin - Cih li cîhazê namîne + Cih li cîhazê namîne Dema ku pel dixebitî NewPipe hate girtin Pêvajoya şûnda têk çû Peyda nebû diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index a6b3daec9..ec598114d 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,2 +1,43 @@ - \ No newline at end of file + + ಗೆ ಸೇರಿಸಿ + ಮಾಹಿತಿಯನ್ನು ತೋರಿಸಿ + ಚಾನಲ್ ಅನ್‌ಸಬ್‌ಸ್ಕ್ರೈಬ್ ಮಾಡಲಾಗಿದೆ + ಪಾಪ್‌ಅಪ್ ಮೋಡ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ + ನೀವು \"%1$s\" ಎಂದು ಅರ್ಥೈಸಿದ್ದೀರಾ? + ಬಳಸಿ ಹಂಚಿಕೊಳ್ಳಿ + ವೀಡಿಯೊ ಫೈಲ್‌ಗಳಿಗಾಗಿ ಡೌನ್‌ಲೋಡ್ ಫೋಲ್ಡರ್ ಆಯ್ಕೆಮಾಡಿ + ಸಬ್‌ಸ್ಕ್ರೈಬ್ ಮಾಡಲಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ಮಾಡಿದ ಆಡಿಯೊ ಫೈಲ್‌ಗಳನ್ನು ಇಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗುತ್ತದೆ + ಬಾಹ್ಯ ಆಡಿಯೊ ಪ್ಲೇಯರ್ ಬಳಸಿ + ಚಂದಾದಾರಿಕೆಯನ್ನು ಬದಲಾಯಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ + ಪ್ರಾರಂಭಿಸಲು ಭೂತಗನ್ನಡಿಯನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ. + ಕೆಲವು ರೆಸಲ್ಯೂಶನ್‌ಗಳಲ್ಲಿ ಆಡಿಯೊವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ + %1$s ರಂದು ಪ್ರಕಟಿಸಲಾಗಿದೆ + ವೀಕ್ಷಿಸಲಾಗಿದೆ ಎಂದು ಗುರುತಿಸಿ + ಯಾವುದೇ ಸ್ಟ್ರೀಮ್ ಪ್ಲೇಯರ್ ಕಂಡುಬಂದಿಲ್ಲ (ಅದನ್ನು ಪ್ಲೇ ಮಾಡಲು ನೀವು VLC ಅನ್ನು ಸ್ಥಾಪಿಸಬಹುದು). + ಸರಿ + ಬ್ರೌಸರ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ + ಪಾಪ್ಅಪ್ + ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ + ಇದಕ್ಕೆ ಫಲಿತಾಂಶಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿದೆ: %s + ಇದರೊಂದಿಗೆ ತೆರೆಯಿರಿ + ಡೌನ್‌ಲೋಡ್ ಮಾಡಿದ ವೀಡಿಯೊ ಫೈಲ್‌ಗಳನ್ನು ಇಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗುತ್ತದೆ + ಸ್ಟ್ರೀಮ್ ಫೈಲ್ ಅನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ + ರದ್ದುಗೊಳಿಸಿ + ಆಡಿಯೋ ಡೌನ್‌ಲೋಡ್ ಫೋಲ್ಡರ್ + ಬುಕ್ಮಾರ್ಕ್ ಮಾಡಲಾದ ಪ್ಲೇಪಟ್ಟಿಗಳು + ಚಂದಾದಾರಿಕೆಯನ್ನು ನವೀಕರಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ + ಸಬ್‌ಸ್ಕ್ರೈಬ್ + ಹುಡುಕಿ + ಚಂದಾದಾರಿಕೆಗಳು + ಸೆಟ್ಟಿಂಗ್‌ಗಳು + ಬಾಹ್ಯ ವೀಡಿಯೊ ಪ್ಲೇಯರ್ ಬಳಸಿ + ಸ್ಥಾಪಿಸಿ + ಯಾವುದೇ ಸ್ಟ್ರೀಮ್ ಪ್ಲೇಯರ್ ಕಂಡುಬಂದಿಲ್ಲ. VLC ಸ್ಥಾಪಿಸುವುದೇ? + ಶೇರ್ ಮಾಡಿ + ವೀಡಿಯೊ ಡೌನ್‌ಲೋಡ್ ಫೋಲ್ಡರ್ + ಟ್ಯಾಬ್ ಆಯ್ಕೆಮಾಡಿ + ಹಿನ್ನೆಲೆ + ಅನ್‌ಸಬ್‌ಸ್ಕ್ರೈಬ್ ಮಾಡಿ + \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e8651d966..939d97441 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -389,7 +389,7 @@ 파일을 덮어쓰기할 수 없습니다 해당 이름으로 대기된 다운로드가 있습니다 파일 작업 중에 Newpipe가 종료되었습니다 - 남은 저장공간이 없습니다 + 남은 저장공간이 없습니다 파일이 삭제되어 진행이 중지되었습니다 연결시간 초과 모든 다운로드 기록과 파일을 삭제합니다.확실합니까\? @@ -505,8 +505,8 @@ 설명 표시하기 한 플레이어에서 다른 플레이어로 전환하면 대기열이 대체될 수 있습니다 어두운 테마 - 최대 3개까지 축소 알림에 표시될 항목을 고를 수 있습니다! - 아래의 각 알림 작업을 눌러 편집하세요. 오른쪽에 있는 확인란을 사용하여 압축 알림에 표시할 항목을 최대 3개까지 선택 + 축소된 알림에서 최대 3개까지 표시될 항목을 고를 수 있습니다! + 아래의 각 알림 작업을 눌러 편집하세요 오른쪽에 있는 확인란을 사용하여 압축 알림에 표시할 항목을 최대 3개까지 선택 YouTube의 \'제한 모드\' 켜기 비디오 해싱 진행 알림 새로운 스트림 @@ -557,7 +557,7 @@ 확인 빈도 필요한 네트워크 연결 모든 네트워크 - 업데이트 확인 + 업데이트 확인 새로운 버전을 수동으로 확인 자동으로 재생 시작 — %s Wi-Fi에서만 @@ -793,4 +793,5 @@ 채널 이전 스트림 실시간 + 아래의 각 알림 작업을 탭하여 편집하세요. 처음 세 가지 작업(재생/일시 중지, 이전 및 다음)은 시스템에 의해 설정되며 사용자 정의할 수 없습니다. \ No newline at end of file diff --git a/app/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml index 50ba6c205..cc59938b4 100644 --- a/app/src/main/res/values-ku/strings.xml +++ b/app/src/main/res/values-ku/strings.xml @@ -418,7 +418,7 @@ بەسوودە بۆ کاتی گۆڕینی هێڵ بۆ داتای مۆبایل, لەگەڵ ئەوەشدا زۆربەی دابەزاندنەکان ڕاناگرێت داخستن ئەپ داخرا لەکاتی کارکردن لەسەر ئەو پەڕگەیە - بیرگەی ناوەکیت پڕبووە + بیرگەی ناوەکیت پڕبووە کردارەکە شکستی هێنا, چونکە ئەو پەڕگەیە سڕاوەتەوە هێڵی ئینتەرنێت نەما ئایا دەتەوێ مێژووی دابەزاندنەکانت بسڕدرێنەوە یان هەموو فایلە دابەزێنراوەکانت بسڕدرێنەوە؟ diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 37345c38a..c68e49bdd 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -553,7 +553,7 @@ Elementams rodyti orginalų \"prieš\" laiką Nepavyksta prisijungti prie serverio Failo sukurti nepavyko - Įrenginyje nebėra vietos + Įrenginyje nebėra vietos Nepavyko rasti serverio Sistema uždraudė veiksmą Rodyti tik negrupuotas prenumeratas @@ -664,7 +664,7 @@ Sekantis pridėtas į eilę Įtraukti į eilę sekantį Apdorojama… tai gali užtrukti - Tikrinti ar yra atnaujinimų + Tikrinti ar yra atnaujinimų Tikrinti ar yra atnaujinimų rankiniu būdu Tikrinti ar yra atnaujinimų… Nauji sklaidos kanalo elementai diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 9bf252ccb..58b9a9d76 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -371,7 +371,7 @@ Nevar atgūt šo lejupielādi Savienojums pārtraukts Progress zaudēts, jo fails tika izdzēsts - Ierīcē nav vietas + Ierīcē nav vietas Strādājot ar failu, NewPipe tika aizvērts Pēcapstrāde neizdevās Nav atrasts @@ -632,7 +632,7 @@ Lokālie meklēšanas ieteikumi Rādīt attēlu indikatorus Augstas kvalitātes (lielāks) - Pārbaudīt atjauninājumus + Pārbaudīt atjauninājumus Manuāli pārbaudīt, vai ir atjauninājumi Video atskaņošanas joslas sīktēla priekšskatījums Pārbauda, vai ir atjauninājumi… diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 03a285e37..8e03b26dd 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -379,7 +379,7 @@ Ддотека со ова име веќе постои Преземената дадотека со ова име веќе постои NewPipe беше затворен додека работеше на датотеката - Не останува простор на уредот + Не останува простор на уредот Истечено време за поврзување Дали си сигурен\? Ограничи ја редицата за преземање diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 38606fea8..c439593f7 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -494,7 +494,7 @@ ഈ ഡൗൺലോഡ് വീണ്ടെടുക്കാനാവില്ല കണക്ഷൻ കാലഹരണപ്പെട്ടു ഫയൽ ഇല്ലാതാക്കിയതിനാൽ പുരോഗതി നഷ്‌ടപ്പെട്ടു - ഉപകരണത്തിൽ ഇനിയൊരു സ്ഥലവും ബാക്കിയില്ല + ഉപകരണത്തിൽ ഇനിയൊരു സ്ഥലവും ബാക്കിയില്ല ഫയലിൽ പ്രവർത്തിക്കുമ്പോൾ ന്യൂപൈപ്പ് അടച്ചു പോസ്റ്റ്-പ്രോസസ്സിംഗ് പരാജയപ്പെട്ടു കണ്ടെത്താനായില്ല diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 33fd41f59..8c6b88716 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -432,4 +432,8 @@ Jangan tunjuk Nyahbisu Minta Android menyesuaikan warna pemberitahuan sesuai dengan warna utama di thumbnail (perhatikan bahawa ini tidak tersedia dalam semua perangkat) + Auto-main + Sambung main + Tidak boleh memuat komentar + Tutup \ 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 1c8575d70..416ebfd02 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -30,7 +30,7 @@ Mørk Lys Last ned - Vis \"Neste\" og \"Lignende\" -videoer + Vis «Neste» og «Lignende» -videoer Nettadressen støttes ikke Forvalgt innholdsspråk Video og lyd @@ -395,7 +395,7 @@ Kan ikke overskrive filen Det finnes en ventende nedlasting ved dette navnet NewPipe ble lukket under arbeid med filen - Ingen ledig plass på enheten + Ingen ledig plass på enheten Framdrift gikk tapt, fordi filen ble slettet Tilkoblingsavbrudd Ønsker du å slette din nedlastingshistorikk eller slette alle nedlastede filer\? @@ -558,7 +558,7 @@ Tøm reCAPTCHA-kaker reCAPTCHA-kaker har blitt slettet Tøm kaker som NewPipe lagrer når du løser en reCAPTCHA - YouTube tilbyr en \"Begrenset modus\" som skjuler potensielt modent innhold + YouTube tilbyr «Begrenset modus» som skjuler innhold antatt å være for voksne Vis innhold som muligens er uegnet for barn fordi det har en aldersgrense (som 18+) La Android tilpasse varselets farge i henhold til hovedfargen i miniatyrbildet (merk at dette ikke er tilgjengelig på alle enheter) Fargelegg merknad @@ -651,7 +651,7 @@ Start hovedspiller i fullskjerm Still i kø neste I kø neste - Se etter oppdateringer + Se etter oppdateringer Behandler … Kan ta sin tid Krasj avspilleren Vis \"Krasj spilleren\" @@ -772,4 +772,30 @@ Ingen strømmer Kanaler Forrige strøm + Vis mer + + %s svar + %s svar + + Flytt hovedfanevelgeren til bunnen + Hovedfaneposisjon + Videoer + Ukjent + Direkte + Spol tilbake + Spol forover + Bildekvalitet + Vis mindre + Del med titler + Del nettadresse først + - %1$s: %2$s + ExoPlayer-innstillinger + %1$s %2$s + beskrivende + Veksle fullskjermsvising + Bytt skjermretning + Rediger hver merknadshandling nedenfor ved å trykke på den. De første tre (spill av/pause, forrige, og neste) kan ikke tilpasses. + Bruk ExoPlayers dekodings-tilbakefall + Håndter noen ExoPlayer-innstillinger. Disse endringeren krever omstart av avspilleren. + Hvilke faner som vises på kanalsidene \ No newline at end of file diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index b573bb79d..5e6c1eece 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -389,7 +389,7 @@ फाइल अधिलेखन गर्न सकिएन यसै नाम सितको एक फाइल डाउनलोड हुने प्रक्रियामा छ फाइल मा काम गर्दा NewPipe बन्द भएको थियो - उपकरणमा कुनै ठाउँ बाकी छैन + उपकरणमा कुनै ठाउँ बाकी छैन प्रगति हरायो, किनभने फाइल मेटिएको थियो जडान समय सकियो तपाईं आफ्नो डाउनलोड इतिहास वा डाउनलोड फाइल मेटाउन चाहनुहुन्छ\? diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml index b8370a1cb..637eb1751 100644 --- a/app/src/main/res/values-nl-rBE/strings.xml +++ b/app/src/main/res/values-nl-rBE/strings.xml @@ -465,7 +465,7 @@ Download geschiedenis verwijderen Kan deze download niet herstellen Verbinding time-out - Geen vrije ruimte meer op het apparaat + Geen vrije ruimte meer op het apparaat NewPipe werd gesloten terwijl het bezig was met het bestand Er staat al een download met deze naam in wacht Kan bestand niet overschrijven diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index b49526071..2a5206699 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -216,7 +216,7 @@ Dit overschrijft je huidige configuratie. Streambestand downloaden Informatie tonen - Bijgehouden afspeellijsten + Bijgehouden afspeellijsten Toevoegen aan Versleep om de volgorde te wijzigen Aanmaken @@ -374,7 +374,7 @@ Stop Maximum aantal keer proberen Maximum aantal pogingen voordat de download wordt geannuleerd - Pauzeren bij mobiele data verbinding + Pauzeren bij mobiele gegevens­verbinding Handig voor wanneer u naar mobiel internet overschakelt, hoewel sommige downloads niet gepauzeerd kunnen worden Gebeurtenissen Conferenties @@ -386,7 +386,7 @@ Kan reacties niet laden Sluiten Het \'Storage Access Framework\' staat downloads naar een externe SD kaart toe - Wis data + Gegevens wissen Afspelen hervatten Verder afspelen vanaf laatste positie Posities in lijst @@ -396,7 +396,7 @@ Een bestand met dezelfde naam bestaat al Kan bestand niet overschrijven Er is al een download met deze naam bezig - Geen vrije ruimte meer op het apparaat + Geen vrije ruimte meer op het apparaat Voortgang verloren, omdat bestand was verwijderd Wilt u de downloadgeschiedenis of alle gedownloade bestanden verwijderen\? Download­wachtrij limiteren @@ -543,7 +543,7 @@ Willekeurig afspelen Herhaal Je kan maximaal drie acties selecteren om te tonen in de compacte notificatie! - Pas elke meldings­actie hieronder aan door er op te tikken. Selecteer tot drie acties die getoond worden in de compacte melding door gebruik te maken van de selectievakjes aan de rechterkant + Pas elke meldings­actie hieronder aan door er op te tikken. Selecteer tot drie acties die getoond worden in de compacte melding door gebruik te maken van de selectievakjes aan de rechterkant. Vijfde actieknop Vierde actieknop Derde actieknop @@ -656,7 +656,7 @@ ‘Speler crashen’ tonen Toont een crash-optie bij gebruik van de speler Controleer handmatig op nieuwe versies - Controleren op updates + Controleren op updates Bezig met controleren op updates… Nieuwe feeditems Foutrapporten @@ -806,4 +806,22 @@ Afspeellijst delen Deel afspeellijst met details zoals afspeellijstnaam en videotitels of als een eenvoudige lijst met video-URL\'s - %1$s: %2$s + + %s antwoord + %s antwoorden + + Meer tonen + Minder tonen + Bewerk elke meldings­actie hieronder door erop te tikken. De eerste drie acties (afspelen/pauzeren, vorige en volgende) zijn ingesteld door het systeem en kunnen niet worden aangepast. + Onvoldoende vrije ruimte op het apparaat + Ja + Herstelt alle instellingen naar hun standaard­waarde + Als u alle instellingen reset worden al uw voorkeurs­instellingen verwijderd en wordt de app opnieuw gestart. +\n +\nWeet u zeker dat u verder wilt gaan? + Nee + Back-up en herstel + NewPipe kan van tijd tot tijd auto­matisch controleren op nieuwe versies en u op de hoogte stellen zodra deze beschik­baar zijn. +\nWilt u dit inschakelen? + Instellingen resetten \ No newline at end of file diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml index 060021846..94e7ae2f4 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -482,7 +482,7 @@ ߞߊ߬ ߡߊ߬ߝߘߎ߬ߟߌ ߟߎ߬ ߟߊ߫ ߥߦߏ߬ ߞߎߘߊ ߟߎ߫ ߛߏߓߌ߬ߘߐ߬ߓߏ߲ ߠߎ߬ ߟߊߣߊ߬ ߞߙߏ߬ߝߏ߫ ߛߎ߮ ߓߍ߯ ߟߏ߲ߘߐߦߊߟߌ ߟߎ߬ - ߟߏ߲ߘߐߦߊߟߌ ߟߎ߬ ߡߊߝߍߣߍ߲߫ + ߟߏ߲ߘߐߦߊߟߌ ߟߎ߬ ߡߊߝߍߣߍ߲߫ ߞߊ߬ ߓߐߞߏ߫ ߞߎߘߊ ߟߎ߫ ߕߎ߬ߢߊ߬ߟߐ߲߫ ߓߟߏ ߟߊ߫ ߞߵߊ߬ ߡߊߖߌ߰ ߟߥߊߟߌߟߊ߲ ߘߐߦߟߍߡߊ߲߫ ߕߎߡߊ ߝߏߛߌ߬ @@ -517,7 +517,7 @@ ߊ߬ ߕߍ߫ ߡߊߛߐ߬ߘߐ߲߬ ߞߐߝߍ߫-ߦߙߍߞߍߟߌ ߓߘߊ߫ ߗߌߙߏ߲߫ ߣߌߎߔߌߔ ߘߊߕߎ߲߯ ߘߊ߫ ߟߋ߬ ߞߵߊ߬ ߕߘߍ߬ ߊ߬ ߦߋ߫ ߓߊ߯ߙߊ߫ ߟߊ߫ ߞߐߕߐ߮ ߞߊ߲߬ - ߜߍߞߣߍ߫ ߛߌ߫ ߕߍ߫ ߕߙߏߞߏ ߞߊ߲߬ + ߜߍߞߣߍ߫ ߛߌ߫ ߕߍ߫ ߕߙߏߞߏ ߞߊ߲߬ ߢߍߕߊ߮ ߓߘߊ߫ ߝߏ߫߸ ߓߊߏ߬ ߞߐߕߐ߮ ߓߘߊ߫ ߖߏ߬ߛߌ߫ ߟߊ߬ߖߌ߰ߟߌ ߣߌ߲߬ ߕߍ߫ ߛߋ߫ ߟߊߛߊ߬ߦߌ߬ ߟߊ߫ ߞߊ߬ ߞߐߕߐ߯ ߟߊߖߌ߰ߣߍ߲ ߠߎ߬ ߖߏ߬ߛߌ߫ diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index fb2cfd675..f9faa8324 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -18,7 +18,7 @@ ମୋଟ %d ମିନିଟ୍ ଆପ୍ ଭାଷା - ସ୍ୱୟଂଚାଳିତ (ଡିଭାଇସ୍ ଥିମ୍) + ସ୍ୱଚାଳିତ (ଡିଵାଇସ୍ ଥିମ୍) ଗୋପନୀୟତା ନାମ ବିବରଣୀ @@ -147,7 +147,7 @@ ବ୍ୟବହାର ଵିଡ଼ିଓ ଓ ଅଡ଼ିଓ ମିନି ପ୍ଲେୟାରରେ ଭିଡିଓ ଆରମ୍ଭ କରନ୍ତୁ ନାହିଁ, କିନ୍ତୁ ଅଟୋ ଘୂର୍ଣ୍ଣନ ବନ୍ଦ ହୋଇଗଲେ ସିଧାସଳଖ ଫୁଲ୍ ସ୍କ୍ରିନ୍ ମୋଡ୍ କୁ ଯାଆନ୍ତୁ। ଫୁଲ୍ ସ୍କ୍ରିନ୍ ଛାଡି ଆପଣ ଏପର୍ଯ୍ୟନ୍ତ ମିନି ପ୍ଲେୟାରକୁ ପ୍ରବେଶ କରିପାରିବେ - ଏହା ଉପରେ ଟ୍ୟାପ୍ କରି ନିମ୍ନରେ ପ୍ରତ୍ୟେକ ବିଜ୍ଞପ୍ତି କାର୍ଯ୍ୟ ସଂପାଦନ କରନ୍ତୁ। ଡାହାଣରେ ଥିବା ଚେକ୍ ବକ୍ସ ବ୍ୟବହାର କରି କମ୍ପାକ୍ଟ ବିଜ୍ଞପ୍ତିରେ ଦେଖାଯିବାକୁ ସେମାନଙ୍କ ମଧ୍ୟରୁ ତିନୋଟି ପର୍ଯ୍ୟନ୍ତ ଚୟନ କରନ୍ତୁ + ଏହା ଉପରେ ଟ୍ୟାପ କରି ନିମ୍ନରେ ଦିଆଯାଇଥିବା ପ୍ରତ୍ୟେକ ବିଜ୍ଞପ୍ତି କାର୍ଯ୍ୟକୁ ସମ୍ପାଦନ କରନ୍ତୁ । ଡାହାଣ ପଟେ ଥିବା ଚେକବକ୍ସଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି କମ୍ପାକ୍ଟ ବିଜ୍ଞପ୍ତିରେ ଦର୍ଶାଇବା ପାଇଁ ସେଗୁଡ଼ିକ ମଧ୍ୟରୁ ତିନିଟି ଚୟନ କରନ୍ତୁ । ଷ୍ଟ୍ରିମ୍ ସୃଷ୍ଟିକର୍ତ୍ତା, ଷ୍ଟ୍ରିମ୍ ବିଷୟବସ୍ତୁ କିମ୍ବା ଏକ ସନ୍ଧାନ ଅନୁରୋଧ ବିଷୟରେ ଅତିରିକ୍ତ ସୂଚନା ସହିତ ମେଟା ସୂଚନା ବାକ୍ସଗୁଡ଼ିକୁ ଲୁଚାଇବାକୁ ବନ୍ଦ କରନ୍ତୁ ପିଲାମାନଙ୍କ ପାଇଁ ସମ୍ଭବତ content ଅନୁପଯୁକ୍ତ ବିଷୟବସ୍ତୁ ଦେଖାନ୍ତୁ କାରଣ ଏହାର ବୟସ ସୀମା ଅଛି (ଯେପରିକି 18+) ଏହି ଭିଡିଓ ବୟସ-ସୀମିତ ଅଟେ । @@ -378,7 +378,7 @@ ପ୍ରଗତି ହଜିଗଲା, କାରଣ ଫାଇଲ୍ ଡିଲିଟ୍ ହେଲା ପରବର୍ତ୍ତୀ ପ୍ରକ୍ରିୟାକରଣ ବିଫଳ ହେଲା ଫାଇଲରେ କାମ କରିବାବେଳେ ନ୍ୟୁ ପାଇପ୍ ବନ୍ଦ ହୋଇଯାଇଥିଲା - ଡିଭାଇସରେ କୌଣସି ସ୍ଥାନ ବାକି ନାହିଁ + ଡିଭାଇସରେ କୌଣସି ସ୍ଥାନ ବାକି ନାହିଁ ଡିସ୍କରୁ ସମସ୍ତ ଡାଉନଲୋଡ୍ ହୋଇଥିବା ଫାଇଲଗୁଡ଼ିକୁ ଲିଭାନ୍ତୁ\? ବନ୍ଦ କର ସର୍ବାଧିକ ପୁନଃଚେଷ୍ଟା @@ -410,7 +410,7 @@ ସ୍ଵତଃସୃଷ୍ଟ ବିଷୟବସ୍ତୁ ଡାଉନଲୋଡ୍ ଧାଡି ସୀମିତ କରନ୍ତୁ - ଅଦ୍ୟତନ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ + ଅଦ୍ୟତନ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ ନୂତନ ସଂସ୍କରଣଗୁଡ଼ିକ ପାଇଁ ମାନୁଆଲ ଯାଞ୍ଚ କରନ୍ତୁ ବିଚାରାଧୀନ ଅଛି ଦେଖାଯାଇଥିବା ଭିଡିଓଗୁଡିକ ଅପସାରଣ କରିବେ କି\? @@ -806,4 +806,22 @@ ଖେଳ ତାଲିକା ସହଭାଗ କରନ୍ତୁ ପ୍ଲେ-ଲିଷ୍ଟ ନାମ ଏବଂ ଭିଡିଓ ଶୀର୍ଷକ କିମ୍ବା ଭିଡିଓ URLଗୁଡ଼ିକର ଏକ ସରଳ ତାଲିକା ଭାବରେ ବିବରଣୀ ସହିତ ପ୍ଲେ-ଲିଷ୍ଟ ଅଂଶୀଦାର କରନ୍ତୁ - %1$s: %2$s + ଅଧିକ ଦର୍ଶାନ୍ତୁ + ଏହା ଉପରେ ଟ୍ୟାପ କରି ନିମ୍ନରେ ଦିଆଯାଇଥିବା ପ୍ରତ୍ୟେକ ବିଜ୍ଞପ୍ତି କାର୍ଯ୍ୟକୁ ସମ୍ପାଦନ କରନ୍ତୁ । ପ୍ରଥମ ତିନୋଟି କାର୍ଯ୍ୟ (ଖେଳ/ବିରତି, ପୂର୍ବବର୍ତ୍ତୀ ଏବଂ ପରବର୍ତ୍ତୀ) ତନ୍ତ୍ର ଦ୍ୱାରା ସେଟ କରାଯାଇଥାଏ ଏବଂ ଏହାକୁ ଇଚ୍ଛାରୂପଣ କରାଯାଇପାରିବ ନାହିଁ । + + %s ଉତ୍ତର + %s ଉତ୍ତରଗୁଡ଼ିକ + + କମ୍ ଦର୍ଶାନ୍ତୁ + ଉପକରଣରେ ପର୍ଯ୍ୟାପ୍ତ ଖାଲି ସ୍ଥାନ ନାହିଁ + ବ୍ୟାକଅପ୍ ଏବଂ ପୁନରୁଦ୍ଧାର କରନ୍ତୁ + ହଁ + ନା + NewPipe ସ୍ୱୟଂଚାଳିତ ଭାବରେ ସମୟ ସମୟରେ ନୂତନ ସଂସ୍କରଣଗୁଡିକ ଯାଞ୍ଚ କରିପାରିବ ଏବଂ ଥରେ ଉପଲବ୍ଧ ହେବା ପରେ ଆପଣଙ୍କୁ ସୂଚିତ କରିପାରିବ । +\nଆପଣ ଏହାକୁ ସକ୍ଷମ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? + ସେଟିଂସମୂହ ପୁନଃସେଟ୍ କରନ୍ତୁ + ସମସ୍ତ ସେଟିଂସମୂହକୁ ସେମାନଙ୍କର ଡିଫଲ୍ଟ ମୂଲ୍ୟରେ ପୁନଃସେଟ୍ କରନ୍ତୁ + ସମସ୍ତ ସେଟିଂସମୂହକୁ ପୁନଃ ସେଟ୍ କରିବା ଦ୍ବାରା ଆପଣଙ୍କର ସମସ୍ତ ପସନ୍ଦିତ ସେଟିଂସମୂହ ପରିତ୍ୟାଗ ହେବ ଏବଂ ଆପ୍ ପୁନଃ ସେଟ୍ଆରମ୍ଭ ହେବ । +\n +\nଆପଣ ଆଗକୁ ବଢିବାକୁ ଚାହୁଁଛନ୍ତି କି? \ No newline at end of file diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index e74e747f4..196b5b785 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -395,7 +395,7 @@ ਫਾਈਲ ਓਵਰਰਾਈਟ ਨਹੀਂ ਹੋ ਸਕਦੀ ਇਸ ਨਾਮ ਦਾ ਇੱਕ ਡਾਊਨਲੋਡ ਬਕਾਇਆ ਹੈ ਫਾਈਲ ਤੇ ਕੰਮ ਕਰਦੇ ਸਮੇਂ ਨਿਊਪਾਈਪ ਬੰਦ ਕੀਤੀ ਗਈ - ਡਿਵਾਈਸ ਤੇ ਕੋਈ ਜਗ੍ਹਾ ਨਹੀਂ ਬਚੀ ਹੈ + ਡਿਵਾਈਸ ਤੇ ਕੋਈ ਜਗ੍ਹਾ ਨਹੀਂ ਬਚੀ ਹੈ ਪ੍ਰਗਤੀ ਖਤਮ ਹੋ ਗਈ, ਕਿਉਂਕਿ ਫਾਈਲ ਮਿਟਾਈ ਗਈ ਕੁਨੈਕਸ਼ਨ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਡਾਊਨਲੋਡ ਇਤਿਹਾਸ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ ਜਾਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਸਾਰੀਆਂ ਫ਼ਾਈਲਾਂ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ\? @@ -602,7 +602,7 @@ ਬਫ਼ਰਿੰਗ ਸ਼ਫਲ ਦੁਹਰਾਓ - ਹੇਠਾਂ ਹਰੇਕ ਨੋਟੀਫਿਕੇਸ਼ਨ ਕਾਰਵਾਈ ਤੇ ਨੱਪਦਿਆਂ ਇਹਨਾਂ ਨੂੰ ਬਦਲੋ। ਇਹਨਾਂ ਦੇ ਸੱਜੇ ਪਾਸੇ ਬਣੇ ਚੈੱਕਬਾਕਸ ਵਰਤਦਿਆਂ ਇਹਨਾਂ ਵਿਚੋਂ ਵੱਧ-ਤੋਂ-ਵੱਧ ਤਿੰਨ ਕਾਰਵਾਈਆਂ ਨੂੰ ਤੁਸੀਂ ਕੰਪੈਕਟ ਨੋਟੀਫਿਕੇਸ਼ਨ ਵਿੱਚ ਵਿਖਾਉਣ ਲਈ ਚੁਣ ਸਕਦੇ ਹੋ + ਹੇਠਾਂ ਹਰੇਕ ਨੋਟੀਫਿਕੇਸ਼ਨ ਕਾਰਵਾਈ ਤੇ ਨੱਪਦਿਆਂ ਇਹਨਾਂ ਨੂੰ ਬਦਲੋ। ਇਹਨਾਂ ਦੇ ਸੱਜੇ ਪਾਸੇ ਬਣੇ ਚੈੱਕਬਾਕਸ ਵਰਤਦਿਆਂ ਇਹਨਾਂ ਵਿਚੋਂ ਵੱਧ-ਤੋਂ-ਵੱਧ ਤਿੰਨ ਕਾਰਵਾਈਆਂ ਨੂੰ ਤੁਸੀਂ ਕੰਪੈਕਟ ਨੋਟੀਫਿਕੇਸ਼ਨ ਵਿੱਚ ਵਿਖਾਉਣ ਲਈ ਚੁਣ ਸਕਦੇ ਹੋ। ਤੁਸੀਂ ਵੱਧ-ਤੋਂ-ਵੱਧ ਤਿੰਨ ਕਾਰਵਾਈਆਂ ਨੂੰ ਕੰਪੈਕਟ ਨੋਟੀਫਿਕੇਸ਼ਨ ਵਿੱਚ ਵਿਖਾਉਣ ਲਈ ਚੁਣ ਸਕਦੇ ਹੋ! ਪੰਜਵਾਂ ਕਾਰਵਾਈ ਬਟਨ ਚੌਥਾ ਕਾਰਵਾਈ ਬਟਨ @@ -669,7 +669,7 @@ ਸਬਸਕ੍ਰਾਈਬ ਕੀਤੇ ਚੈਨਲ ਉੱਪਰ ਨਵੀਂ ਸਟ੍ਰੀਮ ਉੱਪਲਬਧ ਹੋਣ ਤੇ ਨੋਟੀਫਿਕੇਸ਼ਨ ਰਾਹੀਂ ਸੂਚਿਤ ਕਰੋ ਜਾਂਚ ਅਵਧੀ ਕੋਈ ਵੀ ਨੈੱਟਵਰਕ - ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ + ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ ਘੱਟ ਗੁਣਵੱਤਾ (ਛੋਟਾ ਆਕਾਰ) ਡਿਸਕ ਤੋਂ ਸਾਰੀਆਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਫਾਈਲਾਂ ਹਟਾਓ\? @@ -806,4 +806,11 @@ ਪਲੇਲਿਸਟ ਸਾਂਝੀ ਕਰੋ ਪਲੇਲਿਸਟ ਦੇ ਨਾਮ ਅਤੇ ਵੀਡੀਓ ਸਿਰਲੇਖਾਂ ਜਾਂ ਵੀਡੀਓ URL ਦੀ ਇੱਕ ਸਧਾਰਨ ਸੂਚੀ ਦੇ ਰੂਪ ਵਿੱਚ ਵੇਰਵਿਆਂ ਨਾਲ ਪਲੇਲਿਸਟ ਨੂੰ ਸਾਂਝਾ ਕਰੋ - %1$s: %2$s + + %s ਜਵਾਬ + %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 8c7a28ddc..01f242cd9 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -401,7 +401,7 @@ Nie udało się nadpisać pliku Plik o tej samej nazwie oczekuje na pobranie NewPipe został zamknięty podczas pracy nad plikiem - Brak miejsca na urządzeniu + Brak miejsca na urządzeniu Utracono postęp, ponieważ plik został usunięty Czy chcesz wyczyścić historię pobierania, czy usunąć wszystkie pobrane pliki\? Ogranicz kolejkę pobierania @@ -673,7 +673,7 @@ Przetwarzanie… Może to chwilę potrwać Sprawdzanie aktualizacji… Ręcznie sprawdź dostępność nowych wersji - Sprawdź dostępność aktualizacji + Sprawdź dostępność aktualizacji Nowe pozycje kanału Pokazuje opcję psucia podczas korzystania z odtwarzacza Zepsuj odtwarzacz @@ -827,4 +827,24 @@ Udostępnij playlistę Udostępnij playlistę ze szczegółami, takimi jak nazwa playlisty i tytuły wideo, lub jako prostą listę adresów URL wideo. – %1$s: %2$s + + %s odpowiedź + %s odpowiedzi + %s odpowiedzi + %s odpowiedzi + + Pokaż więcej + Pokaż mniej + Edytuj każdą poniższą akcję powiadomienia, naciskając ją. Pierwsze trzy akcje (odtwórz/wstrzymaj, poprzedni i następny) są ustawione przez system i nie można ich dostosować + Za mało wolnego miejsca na urządzeniu + Kopia zapasowa i przywracanie + Resetuje wszystkie ustawienia do ich domyślnych wartości + Resetuj ustawienia + Zresetowanie wszystkich ustawień odrzuci wszystkie Twoje preferowane ustawienia i ponownie uruchomi aplikację. +\n +\nCzy na pewno chcesz kontynuować? + Tak + Nie + NewPipe może od czasu do czasu automatycznie sprawdzać dostępność nowych wersji i powiadamiać Cię, gdy tylko będą dostępne. +\nCzy chcesz to włączyć? \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 76c6b43fe..1d241de51 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -398,7 +398,7 @@ O arquivo não pode ser sobrescrito Já existe um download pendente com este nome O NewPipe foi fechado enquanto manipulava o arquivo - Sem espaço disponível + Sem espaço disponível O progresso foi perdido pois o arquivo foi excluído Tempo limite de conexão Excluir todo o histórico de downloads ou excluir todos os arquivos baixados\? @@ -554,7 +554,7 @@ Nada Repetir Você pode selecionar até no máximo três botões para mostrar na notificação compacta! - Edite os botões de ação de notificação abaixo tocando em cada um. Selecione até três deles para serem mostrados na notificação compacta usando as caixas de seleção à direita + Edite os botões de ação de notificação abaixo tocando em cada um. Selecione até três deles para serem mostrados na notificação compacta usando as caixas de seleção à direita. Quinto botão de ação Quarto botão de ação Terceiro botão de ação @@ -664,7 +664,7 @@ Sugestões de busca remotas Sugestões de busca locais Processando… Pode demorar um pouco - Procurar por atualizações + Procurar por atualizações Procurar manualmente por novas versões Procurando por atualizações… Travar o reprodutor de vídeo @@ -818,4 +818,13 @@ Miniaturas Duração Transmissão anterior + Banners + Mostrar mais + Edite cada ação de notificação abaixo tocando nela. As três primeiras ações (reproduzir/pausar, anterior e seguinte) são definidas pelo sistema e não podem ser personalizadas. + + %s resposta + %s respostas + %s respostas + + Mostrar menos \ No newline at end of file diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 335c989ed..325c79d3c 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -221,7 +221,7 @@ Última atualização: %s Importar base de dados Relatório de erro - Não há espaço livre no dispositivo + Não há espaço livre no dispositivo Número máximo de tentativas antes de cancelar a descarga A recuperar de um erro do reprodutor Em direto @@ -554,7 +554,7 @@ Baralhar Repetir Pode selecionar, no máximo, três ações para mostrar na notificação compacta! - Edite cada ação de notificação abaixo tocando nela. Selecione até três delas para serem mostrados na notificação compacta a usar as caixas de seleção à direita + Edite cada ação de notificação abaixo, a tocar nela. Selecione até três delas para serem mostradas na notificação compacta, através das caixas de verificação à direita. Quinto botão de ação Quarto botão de ação Terceiro botão de ação @@ -664,7 +664,7 @@ Enfileirado o próximo Pôr na fila o próximo A processar… Pode demorar um momento - Procurar atualizações + Procurar atualizações Verificar manualmente se existe uma nova versão A procurar atualizações… Novos itens @@ -819,4 +819,12 @@ Canais Fluxo anterior Direto + Mostrar mais + Edite cada ação de notificação abaixo a tocar nela. As três primeiras ações (reproduzir/pausa, anterior e seguinte) são definidas pelo sistema e não podem ser personalizadas. + + %s resposta + %s respostas + %s respostas + + Mostrar menos \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index f20bc05b0..e69f79a68 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -73,7 +73,7 @@ Toque para detalhes Por favor aguarde… Copiado para a área de transferência - Tem que definir, nas definições, uma pasta para as descargas + Pode definir, mais tarde, uma pasta para as descargas OK Processos Descarga NewPipe @@ -107,9 +107,9 @@ Acerca de NewPipe Licenças de terceiros © %1$s de %2$s nos termos da %3$s - Sobre e FAQ + Acerca e FAQ Licenças - Aplicação livre de reprodução de transmissões para Android. + Aplicação de reprodução para Android. Ver no GitHub Licença do NewPipe Se tem ideias para: tradução, alterações de desenho, limpeza de código, ou alterações significativas no código fonte - todas as ajudas são bem-vindas. Quanto mais se faz, melhor ficará! @@ -133,7 +133,7 @@ Histórico e cache Desfazer Notificação NewPipe - Notificações para o reprodutor do NewPipe + Notificações para o reprodutor NewPipe Sem resultados Aqui não há nada para ver Sem subscritores @@ -154,7 +154,7 @@ %s vídeos %s vídeos - Transferências + Descargas Carateres permitidos no nome dos ficheiros Os carateres inválidos são substituídos por este valor Carácter de substituição @@ -180,7 +180,7 @@ Detalhes Definições de áudio Iniciar reprodução em segundo plano - Iniciar reprodução num popup + Iniciar reprodução em popup Mostrar informação Listas de reprodução favoritas Sempre @@ -202,7 +202,7 @@ Descarregar ficheiro Adicionar a Utilizar pesquisa rápida - Este tipo de pesquisa e mais rápida mas reduz a precisão. Procurar por 5, 15 ou 25 segundos não funciona corretamente + Este tipo de pesquisa é mais rápida mas reduz a precisão. Procurar por 5, 15 ou 25 segundos não funciona corretamente Cache de imagens limpa País padrão para conteúdo Depuração @@ -237,7 +237,7 @@ Reprodutor \'popup\' Perguntar sempre A obter informação… - A carregar o conteúdo solicitado + A carregar conteúdo solicitado Nova lista de reprodução Mudar nome Adicionar à lista de reprodução @@ -262,13 +262,13 @@ Não foi possível exportar as subscrições Importar subscrições do YouTube do Google Takeout: \n -\n1. Vá para este URL: %1$s -\n2. Faça o login quando solicitado -\n3. Clique em \"Todos os dados incluídos\", depois em \"Desmarcar todos\", depois selecione apenas \"subscrições\" e clique em \"OK\". -\n4. Clique em \"Próximo passo\" e depois em \"Criar exportação\". +\n1. Aceda ao URL: %1$s +\n2. Inicie sessão +\n3. Clique em \"Todos os dados incluídos\", depois em \"Desmarcar todos\", selecione \"Subscrições\" e clique em \"OK\". +\n4. Clique em \"Próximo passo\" e, depois, em \"Criar exportação\". \n5. Clique no botão \"Descarregar\" após aparecer \n6. Clique em IMPORT FILE abaixo e selecione o ficheiro .zip descarregado -\n7. [Se a importação do .zip falhar] Extraia o ficheiro .csv (geralmente em \"YouTube e YouTube Music/subscriptions/subscriptions.csv\"), clique em IMPORT FILE abaixo e selecione o ficheiro csv extraído +\n7. [Se a importação falhar] Extraia o ficheiro .csv (geralmente em \"YouTube e YouTube Music/subscriptions/subscriptions.csv\"), clique em IMPORT FILE abaixo e selecione o ficheiro csv extraído Importe o seu perfil SoundCloud digitando o URL ou a ID.: \n \n1. Ative o modo desktop do seu navegador web (o site não está disponível para dispositivos móveis) @@ -297,7 +297,7 @@ NewPipe é desenvolvido por voluntários que utilizam o seu tempo livre para nos proporcionar a melhor experiência. Retribua para ajudar os programadores a tornarem NewPipe ainda melhor. Contribuir Política de privacidade do NewPipe - O projeto NewPipe leva a sua privacidade muito a sério. Por isso, não recolhe nenhum dado sem o seu consentimento. + O projeto NewPipe leva a sua privacidade muito a sério. Por isso, não recolhe dados sem o seu consentimento. \nA polícia de privacidade do NewPipe explica, em detalhe, os tipos de dados enviados sempre que submete um relatório de erro. Ver política de privacidade Adicionar o próximo vídeo à fila automaticamente @@ -309,7 +309,7 @@ Definir como miniatura da lista de reprodução Ajustar Preencher - Modificar escala de legendas e estilo de fundo. Tem que reiniciar a aplicação para aplicar as alterações + Alterar escala das legendas e do estilo de fundo. Tem que reiniciar a aplicação para aplicar as alterações A monitorização de memória pode tornar a aplicação instável Reportar erros \'out-of-lifecycle\' Forçar reporte de exceções Rx não entregáveis ocorrendo fora do fragmento ou ciclo de vida da atividade após eliminação @@ -343,7 +343,7 @@ Não foi possível ler os separadores guardados e vamos usar os separadores padrão Repor predefinições Deseja repor as predefinições\? - Contagem de subscrições indisponível + Número de subscrições indisponível Separadores mostrados na página principal Atualizações Mostrar uma notificação para pedir a atualização da aplicação se existir uma nova versão @@ -356,7 +356,7 @@ em pausa na fila pós-processamento - Enfileirar + Colocar na fila Ação recusada pelo sistema Falha ao descarregar Gerar nome único @@ -370,14 +370,14 @@ Não foi possível encontrar o servidor Não foi possível ligar ao servidor O servidor não envia dados - O servidor não aceita transferências multi-processo, tente novamente com @string/msg_threads = 1 + O servidor não aceita descargas multi-processo, tente novamente com @string/msg_threads = 1 Não encontrado Falha pós-processamento Parar Tentativas máximas Número máximo de tentativas antes de cancelar a descarga Interromper em redes limitadas - Útil ao trocar para dados móveis, mas algumas transferências não podem ser suspensas + Útil ao trocar para dados móveis, mas algumas descargas não podem ser suspensas Eventos Conferências Pendente @@ -395,22 +395,22 @@ Posições de reprodução removidas Ficheiro movido ou eliminado Já existe um ficheiro com este nome - não é possível sobrescrever o ficheiro + Não foi possível substituir o ficheiro Existe uma descarga pendente com este nome NewPipe foi fechado enquanto trabalhava no ficheiro - Não há espaço livre no dispositivo + Não há espaço livre no dispositivo Progresso perdido, o ficheiro foi eliminado Ligação expirada Deseja limpar o histórico de descargas ou remover todos os ficheiros descarregados\? Limitar fila de descargas Permitir apenas uma descarga de cada vez - Iniciar transferências - Pausa nas transferências - Perguntar para onde transferir - Ser-lhe-á perguntado onde guardar cada transferência. -\nAtive o seletor de pastas do sistema (SAF) se quiser transferir para um cartão SD externo + Iniciar descargas + Pausa nas descargas + Perguntar onde guardar + Ser-lhe-á perguntado onde guardar cada descarga. +\nAtive o seletor de pastas do sistema (SAF) se quiser descarregar para um cartão SD externo Utilizar seletor de pastas do sistema (SAF) - \'Storage Access Framework\' permite transferências para um cartão SD externo + \'Storage Access Framework\' permite descargas para um cartão SD externo Remover posições de reprodução Remove todas as posições de reprodução Remover todas as posições de reprodução\? @@ -428,8 +428,8 @@ %s ouvintes %s ouvintes - O idioma será alterado assim que reiniciar a app - Duração da pesquisa de avanço/recuo rápido + O idioma será alterado assim que reiniciar a aplicação + Duração de avanço/recuo rápido Instâncias PeerTube Defina as suas instâncias PeerTube preferidas Encontre as instâncias que gosta em %s @@ -441,7 +441,7 @@ Local Recentes Mais apreciados - Geração automática (não foi encontrado nenhum enviador) + Geração automática (não foi encontrado nenhum carregador) a recuperar Não é possível recuperar esta descarga Escolha uma instância @@ -451,11 +451,11 @@ Idioma da aplicação Predefinição do sistema Prima \"Feito\" ao resolver - Concluído - Acha que a fonte demora muito tempo a carregar\? Se sim, tente ativar o carregamento rápido (pode alterar a opção nas definições ou no botão abaixo). + Feito + Acha que a fonte demora muito tempo a carregar? Se sim, tente ativar o carregamento rápido (pode alterar a opção nas definições ou no botão abaixo). \n \nNewPipe oferece duas estratégias de carregamento: -\n- Obter todo o canal de subscrição - lento, mas completo. +\n- Obter todo o canal - lento, mas completo. \n- Usar um \'endpoint\' dedicado - mais rápido, mas não completo. \n \nA diferença entre os dois é que o rápido geralmente carece de alguma informação, como a duração ou tipo do item (não consegue distinguir entre vídeos em direto e vídeos normais) e pode mostrar menos itens. @@ -468,11 +468,11 @@ Disponível em alguns serviços, é geralmente muito mais rápido, mas pode devolver uma quantidade limitada de itens e muitas vezes informações incompletas (por exemplo, sem duração, tipo de item, sem estado ativo) Se possível, obter de uma fonte dedicada Atualizar sempre - Tempo após a última atualização antes de a subscrição ser considerada desatualizada - %s + Tempo após a última atualização antes da subscrição ser considerada desatualizada - %s Limite de atualização da fonte Fonte Novo - Deseja apagar este grupo\? + Deseja remover este grupo? O nome do grupo está vazio %d selecionado @@ -547,18 +547,18 @@ Ajustar miniatura de vídeo mostrada na notificação de 16:9 para 1:1 Cortar miniatura na proporção 1:1 Iniciar reprodução automaticamente — %s - Reproduzir fila + Fila de reprodução Nunca A carregar - A fila do reprodutor ativo será substituída + A fila de reprodução atual será substituída URL não reconhecido. Abrir com outra aplicação\? - Enfileiramento automático + Colocar na fila automaticamente Baralhar Apenas em Wi-Fi Nada Mudar de um reprodutor para outro pode substituir a sua fila Pedir confirmação antes de limpar a fila - Edite cada ação de notificação abaixo com um toque. Selecione até três para serem mostradas na notificação compacta utilizando as caixas de seleção à direita + Edite cada ação de notificação abaixo, tocando na mesma. Seleccione até três delas para serem mostradas na notificação compacta, utilizando as caixas de verificação à direita. Pode selecionar, no máximo, três ações para mostrar na notificação compacta! Repetir Quinto botão de ação @@ -572,7 +572,7 @@ Mostrar conteúdo possivelmente impróprio para crianças porque tem um limite de idade (como 18+) Personalizar cor da notificação com a cor principal da miniatura (esta opção não está disponível em todos os dispositivos) Notificação colorida - Usar miniaturas no fundo do ecrã de bloqueio e em notificações + Usar miniatura como fundo do ecrã de bloqueio e nas notificações Mostrar miniatura A calcular \'hash\' Notificar sobre o progresso das \'hash\' dos vídeos @@ -583,18 +583,18 @@ Não possui qualquer aplicação para abrir este ficheiro Capítulos Descrição - Emissões relacionadas + Itens relacionados Comentários Desative para ocultar a descrição do vídeo e informações adicionais Mostrar descrição Abrir com - A app travou + Terminar aplicação Este vídeo tem uma restrição de idade. \nDevido às novas políticas do YouTube quanto a restrição de alguns vídeos, o NewPipe não pode aceder a estes vídeos e, por isso, não os consegue reproduzir. - Este conteúdo só está disponível para utilizadores que pagaram, portanto não pode ser transmitido ou descarregado pelo NewPipe. - Este vídeo está disponível apenas para os membros do YouTube Music Premium, portanto não pode ser transmitido ou descarregado pelo NewPipe. - Este conteúdo é privado, portanto não pode ser transmitido ou descarregado pelo NewPipe. - Esta é uma faixa de SoundCloud Go+, pelo menos no seu país, pelo que não pode ser transmitida ou descarregada por NewPipe. + Este conteúdo só está disponível para utilizadores pagantes e não pode ser reproduzido ou descarregado pelo NewPipe. + Este vídeo só está disponível para os membros do YouTube Music Premium e não pode ser reproduzido ou descarregado pelo NewPipe. + Este conteúdo é privado e não pode ser reproduzido ou descarregado pelo NewPipe. + Esta é uma faixa SoundCloud Go+ e, pelo menos no seu país, não pode ser reproduzida ou descarregada por NewPipe. Este conteúdo não está disponível no seu país. Rádio Destaques @@ -605,37 +605,37 @@ Automático (Tema do dispositivo) Tema escuro Mostrar detalhes do canal - Desative o túnel multimédia se tiver um ecrã preto ou gaguejo na reprodução de vídeo. + Desative o túnel multimédia se tiver um ecrã preto ou paragens na reprodução de vídeos. Desativar túnel multimédia Sempre que descarregar um ficheiro, terá que indicar o local para o guardar - Ainda não foi definida uma pasta de descarregamento, escolha agora a pasta de descarregamento padrão + Ainda não definiu uma pasta para as descargas. Escolha agora a pasta a utilizar %s fornece este motivo: Conta encerrada - O modo de feed rápido não fornece mais informações sobre isto. + O modo de fonte rápida não fornece mais informações sobre isto. A conta do autor foi encerrada. -\nNewPipe não será capaz de carregar este feed no futuro. -\nQuer cancelar a inscrição deste canal\? - Não foi possível carregar o feed para \'%s\'. - Erro ao carregar o feed - A partir do Android 10, apenas o \'Storage Access Framework\' é compatível - Pré-visualização da miniatura da barra de pesquisa +\nNewPipe não será capaz de carregar esta fonte. +\nDeseja cancelar a subscrição deste canal? + Não foi possível carregar a fonte para \'%s\'. + Erro ao carregar a fonte + A partir de Android 10, apenas \'Storage Access Framework\' é compatível + Pré-visualização da miniatura na barra de pesquisa Marcar como visto Desligado Ligado Modo tablet - Abrir site de web - Entusiasmado pelo criador + Abrir site + Adorado pelo criador Interno Privado Não listado Público Servidor - Apoio + Suporte Idioma Limite de idade Privacidade Licença - Marcadores + Etiquetas Categoria Desativar seleção de texto na descrição Ativar seleção de texto na descrição @@ -643,7 +643,7 @@ Não mostrar Baixa qualidade (menor) Alta qualidade (maior) - Comentários estão desativados + Os comentários estão desativados Mostrar fitas coloridas de Picasso em cima das imagens que indicam a sua fonte: vermelho para rede, azul para disco e verde para memória Mostrar indicadores de imagem Sugestões de pesquisa remotas @@ -658,112 +658,112 @@ %s descargas concluídas %s descargas concluídas - Deslizar itens para removê-los + Deslize nos itens para os remover Não iniciar vídeos em mini-reprodutor e ativar ecrã completo, se a rotação automática estiver bloqueada. Pode aceder ao mini-reprodutor se sair do modo de ecrã completo. Iniciar reprodutor principal em ecrã completo - Enfileirado o próximo - Pôr na fila o próximo - A processar… Pode demorar um momento - Procurar atualizações + Seguinte colocado na fila + Colocar seguinte na fila + A processar… Pode levar algum tempo + Procurar atualizações Verificar manualmente se existe uma nova versão A procurar atualizações… Novos itens - Travar o reprodutor + Terminou o reprodutor Mostrar \"Travar o reprodutor\" - Mostra uma opção de travamento ao usar o reprodutor + Mostra uma opção para terminar o reprodutor Notificação para relatórios de erro Notificações para reportar erros NewPipe encontrou um erro, toque para reportar Ocorreu um erro, veja a notificação - Mostrar um snackbar de erro + Mostrar uma barra de erros Criar uma notificação de erro - Nenhum gestor de ficheiros apropriado foi encontrado para esta ação. -\nPor favor, instale um gestor de ficheiros ou tente desativar \'%s\' nas configurações de descarregar + Não foi encontrado um gestor de ficheiros apropriado para esta ação. +\nPor favor, instale um gestor de ficheiros ou tente desativar \'%s\' nas definições das descargas Nenhum gestor de ficheiros apropriado foi encontrado para esta ação. -\nPor favor, instale um gestor de ficheiros compatível com o Storage Access Framework - Comentário fixado - LeakCanary não está disponível - Predefinido do ExoPlayer +\nPor favor, instale um gestor de ficheiros compatível com SAF (Storage Access Framework) + Comentário afixado + LeakCanary não disponível + Predefinição ExoPlayer Notificação do reprodutor Configurar notificação da reprodução do vídeo atual Notificações A carregar detalhes do vídeo… - Verificar se há novos vídeos + Procurar novos vídeos Notificações sobre novos vídeos - Notificar sobre novos vídeos nas assinaturas + Notificar sobre novos vídeos nas subscrições Frequência da verificação - Conexão de rede necessária + Requer uma ligação de rede Qualquer rede - Agora assinou este canal + É agora um subscritor deste canal Alternar tudo - Apagar todos os ficheiros descarregados do disco\? + Remover todos os ficheiros descarregados? Novos vídeos Notificações sobre novos vídeos nas subscrições - %s vídeo novo - %s vídeos novos - %s vídeos novos + %s novo vídeo + %s novos vídeos + %s novos vídeos - Seja notificado - Notificações são desativadas + Obter notificação + As notificações estão desativadas , - Por cento - Semitom - As transmissões que ainda não são suportadas para descarregamento não são mostradas - Não estão disponíveis transmissões de áudio a reprodutores externos - Não estão disponíveis transmissões de vídeo a reprodutores externos + Percentual + Semi-tom + Os vídeos não suportadas para descarga não são mostradas + Não estão disponíveis reproduções de áudio para reprodutores externos + Não estão disponíveis reproduções de vídeo para reprodutores externos Selecione a qualidade para reprodutores externos Formato desconhecido Qualidade desconhecida - A transmissão selecionada não é suportada por reprodutores externos + O vídeo selecionado não é suportado por reprodutores externos Tamanho do intervalo de carregamento da reprodução Perguntas frequentes Ver no site - Se está a ter problemas a usar a aplicação, veja estas respostas para perguntas mais comuns! - Ordenação + Se está a ter problemas a usar a aplicação, veja estas respostas para as perguntas mais comuns! + Ordem Modo rápido Importe ou exporte subscrições no menu 3 pontos Já está a executar a versão mais recente do NewPipe Toque para descarregar %s Esta opção só está disponível se estiver selecionado %s para o tema - Desativar miniatura permanente + Desativar miniatura permanentemente Não foi possível copiar para a área de transferência Cartão As listas de reprodução acinzentadas já contêm este item. - Duplicar adicionado %d vez(es) - Ignorar eventos com botões físicos + Duplicado adicionado %d vez(es) + Ignorar botões físicos Útil, por exemplo, se estiver a utilizar auscultadores com botões físicos danificados Remover duplicados Remover duplicados\? Mostrar/ocultar vídeos - Proximamente - Quer remover todos os vídeos duplicados nesta lista de reprodução\? + Brevemente + Deseja remover todos os vídeos duplicados desta lista de reprodução? Completamente visto - Mostrar os seguintes vídeos + Mostrar vídeos seguintes Parcialmente visto - Ação do gesto para a esquerda - Ação do gesto para a direita + Ação para o gesto à esquerda + Ação para o gesto à direita Brilho Volume - Nenhum - Escolha o gesto para a metade esquerda do ecrã do reprodutor - Escolha o gesto para a metade direita do ecrã do reprodutor + Nada + Escolha o gesto para a parte esquerda do ecrã + Escolha o gesto para a parte direita do ecrã Seleciona o áudio com descrição para pessoas com dificuldades de visão, se disponível - Esta solução alternativa liberta e re-instancia os codificadores de vídeo quando ocorre uma alteração da superfície, em vez de definir diretamente a superfície para o codificador. Já utilizada pelo ExoPlayer em alguns dispositivos com este problema, esta configuração só tem efeito no Android 6 e superior + Esta solução alternativa liberta os codificadores de vídeo quando ocorre uma alteração da superfície, em vez de definir diretamente a superfície para o codificador. Já utilizada pelo ExoPlayer em alguns dispositivos com este problema, esta definição só tem efeito no Android 6 e superior \n -\nA activação desta opção pode evitar erros de reprodução ao mudar o leitor de vídeo atual ou ao mudar para ecrã inteiro - Altera o tamanho do intervalo de carregamento progressivo (o atual é %s). Um valor menor pode acelerar o carregamento inicial do vídeo +\nA ativação desta opção pode evitar erros de reprodução ao mudar o leitor de vídeo atual ou ao mudar para ecrã inteiro + Altera o tamanho do intervalo de carregamento progressivo (atualmente %s). Um valor menor pode acelerar o carregamento inicial do conteúdo Dar preferência ao áudio original Seleciona o áudio original, independentemente do idioma Dar preferência ao áudio descritivo Áudio: %s Faixa de áudio - Já deve existir uma faixa de áudio nesta transmissão - Selecionar faixa de áudio para reprodutores externos + Já deve existir uma faixa de áudio neste vídeo + Selecione a faixa de áudio para reprodutores externos Desconhecida - Configurações ExoPlayer - Gere algumas configurações de ExoPlayer. É necessário reiniciar o reprodutor para aplicar as alterações - Utilizar a função de fallback do descodificador do ExoPlayer + Definições ExoPlayer + Gere algumas definições ExoPlayer. Tem que reiniciar o reprodutor para aplicar as alterações + Utilizar descodificador se recurso do ExoPlayer Ative esta opção se tiver problemas de inicialização do descodificador, que retorna codificadores de baixa prioridade se o descodificador primário falhar. Isto pode resultar num desempenho inferior de reprodução Usar sempre a configuração de saída alternativa de vídeo do ExoPlayer %1$s %2$s @@ -772,12 +772,12 @@ descritivo Mova o seletor do separador principal para a parte inferior Posição dos separadores principais - O túnel multimédia foi desativado por predefinição no seu dispositivo porque se sabe que o modelo do dispositivo não o suporta. + O túnel multimédia foi desativado no seu dispositivo porque o seu dispositivo não tem suporte à opção. Nenhum vídeo em direto Nenhum vídeo A carregar metadados… Obter separadores de canais - Separadores a obter ao atualizar o feed. Esta opção não tem efeito se um canal for atualizado utilizando o modo rápido. + Separadores a obter ao atualizar a fonte. Esta opção não tem efeito se um canal for atualizado utilizando o modo rápido. Qualidade da imagem Vídeos \? @@ -793,7 +793,7 @@ Baixa qualidade Alternar ecrã completo Avatars - Fluxo seguinte + Vídeo seguinte Avatar de subcanais Abrir fila de reprodução Não carregar imagens @@ -815,8 +815,16 @@ Faixas Duração Canais - Fluxo anterior + Vídeo anterior Direto Partilhe a lista de reprodução com detalhes como o nome da lista de reprodução e os títulos dos vídeos ou como uma simples lista de URLs de vídeos Escolha a qualidade das imagens e se pretende carregar imagens, para reduzir a utilização de dados e de memória. As alterações limpam a cache de imagens na memória e no disco - %s + Mostrar mais + + %s resposta + %s respostas + %s respostas + + Mostrar menos + Edite cada ação de notificação abaixo a tocar nela. As três primeiras ações (reproduzir/pausa, anterior e seguinte) são definidas pelo sistema e não podem ser personalizadas. \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 42e23c3a4..c9eaaf2c9 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -30,7 +30,7 @@ Întunecat Luminos Descărcați - Arată videoclipurile care \'Urmează\' şi cele \'Similare\' + Arată videoclipurile care \'Urmează\' și cele \'Similare\' URL nesuportat Limba dorită a conținutului Video și Audio @@ -154,7 +154,7 @@ © %1$s de %2$s sub %3$s Despre & FAQ Licențe - Un player de streaming „uşor” liber, pentru Android. + Un player de streaming „ușor” liber, pentru Android. Vedeți pe GitHub Licența NewPipe Fie că aveți idei de: traducere, modificări de design, curățare a codului sau modificări de cod cu adevărat importante - ajutorul este întotdeauna binevenit. Cu cât se face mai mult, cu atât mai bine devine! @@ -176,7 +176,7 @@ Niciun player pentru streaming găsit. (Totuși, puteți instala VLC). Descărcați fișierul de flux Afișare informații - Playlist-uri salvate + Playlisturi salvate Salvare în Folosește parcurgerea rapidă inexactă Derularea inexactă permite player-ului să deruleze mai rapid, cu o precizie redusă. Derularea timp de 5, 15 sau 25 de secunde nu funcționează cu aceasta @@ -227,23 +227,23 @@ Eliminați Detalii Setări Audio - Apăsaţi pentru a adăuga în coadă + Apăsați pentru a adăuga în coadă Începeți redarea în fundal Începeți redarea în popup - Deschdeţi sertarul - Închideţi sertarul - Opţiunea de deschidere preferată + Deschdeți sertarul + Închideți sertarul + Opțiunea de deschidere preferată Acțiune implicită la deschiderea conținutului - %s Player Video Player Fundal Player Popup Întrebați întotdeauna Nu există fluxuri disponibile pentru descărcare - Trageţi pentru a reordona + Trageți pentru a reordona Creați Respingeți - Redenumiţi - Donaţi + Redenumiți + Donați De asemenea, doriți să importați setări? Nume Listă de redare creată @@ -363,7 +363,7 @@ Redare aleatorie Repetare Puteți selecta cel mult trei acțiuni pentru afișare în notificarea compactă! - Modificați fiecare acțiune de notificare de mai jos, atingând-o. Selectați până la trei dintre ele pentru a fi afișate în notificarea compactă, utilizând casetele de selectare din dreapta + Modifică fiecare acțiune de notificare de mai jos, atingând-o. Selectează până la trei dintre ele pentru a fi afișate în notificarea compactă, utilizând casetele de selectare din dreapta. Al cincilea buton de acțiune Al patrulea buton de acțiune Al treilea buton de acțiune @@ -475,7 +475,7 @@ Nu se poate recupera această descărcare Conexiunea a expirat Progres pierdut, deoarece fișierul a fost șters - Nu a mai rămas spațiu pe dispozitiv + Nu a mai rămas spațiu pe dispozitiv NewPipe a fost închis în timp ce lucra la fișier Post-procesarea a eșuat Nu a fost găsit @@ -604,7 +604,7 @@ Aceasta este o piesă SoundCloud Go+, cel puțin în țara dvs., deci nu poate fi difuzată sau descărcată de NewPipe. Acest conținut nu este disponibil în țara dumneavoastră. Afișați detaliile canalului - Temă Nocturnă + Tema de noapte Următorul pus în coadă Adăugați în coadă pe următorul Marcare ca vizionat @@ -627,7 +627,7 @@ Categorie Dezactivați selectarea textului în descriere Activați selectarea textului în descriere - Acum puteți selecta text în interiorul descrierii. Rețineți că este posibil ca pagina să pâlpâie, iar link-urile să nu poată fi accesate în modul de selecție. + Acum puteți selecta text în interiorul descrierii. Rețineți că este posibil ca pagina să pâlpâie, iar linkurile să nu poată fi accesate în modul de selecție. %s oferă acest motiv: Contul a fost închis Modul rapid nu furnizează mai multe informații în acest sens. @@ -664,7 +664,7 @@ Afișați indicatorii de imagine Dezactivați tunelarea media dacă întâmpinați un ecran negru sau blocaje la redarea video. Procesarea.. Poate dura un moment - Verifică dacă există actualizări + Verifică dacă există actualizări Verifică manual dacă există versiuni noi Comentariu lipit Notificare cu raport de eroare @@ -817,4 +817,16 @@ Transmisia anterioară Alegeți un gest pentru jumătatea stângă din ecranul playerului În direct + Shorturi + Avatarele autorului + Editează fiecare acțiune de notificare de mai jos atingând-o. Primele trei acțiuni (redare/pauză, anterioară și următoare) sunt setate de sistem și nu pot fi personalizate. + + %s răspuns + %s răspunsuri + %s răspunsuri + + Arată mai multe + Arată mai puține + Piste + Nu este suficient spațiu liber pe dispozitiv \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 63d576594..df566853b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -78,7 +78,7 @@ Аудио Повтор Не удалось загрузить все миниатюры - В фоне + Фон В окне Только некоторые устройства поддерживают видео в 2K/4K Формат видео по умолчанию @@ -405,7 +405,7 @@ не удаётся перезаписать файл В очереди уже есть загрузка с таким именем NewPipe была закрыта во время работы над файлом - Закончилось свободное место на устройстве + Закончилось свободное место на устройстве Прогресс потерян, так как файл был удалён Действительно удалить историю загрузок и загруженные файлы\? Ограничить очередь загрузки @@ -562,7 +562,7 @@ Перемешать Повтор В компактном уведомлении доступно не более трёх действий! - Действия можно изменить, нажав на них. Отметьте не более трёх для отображения в компактном уведомлении + Отредактируйте каждое действие уведомления ниже, нажав на него. Выберите до трёх из них, которые будут отображаться в компактном уведомлении, используя флажки справа. Пятое действие Четвёртое действие Третье действие @@ -675,7 +675,7 @@ Добавить следующим Обработка… Подождите немного Проверить обновления вручную - Проверить обновления + Проверить обновления Проверка обновлений… Новое на канале Уведомления @@ -789,7 +789,7 @@ %1$s \n%2$s Вкладки канала - Shorts + Шорты Загрузка метаданных… Изменить ориентацию экрана Низкое качество @@ -823,4 +823,14 @@ Каналы Предыдущий стрим Живая трансляция + Показать больше + Показать меньше + + %s ответ + %s ответа + %s ответов + %s ответов + + Отредактируйте каждое действие уведомления ниже, нажав на него. Первые три действия (воспроизведение/пауза, предыдущее и следующее) задаются системой и не подлежат настройке. + Недостаточно свободного места на устройстве \ No newline at end of file diff --git a/app/src/main/res/values-ryu/strings.xml b/app/src/main/res/values-ryu/strings.xml index abd1f089b..195eb22b3 100644 --- a/app/src/main/res/values-ryu/strings.xml +++ b/app/src/main/res/values-ryu/strings.xml @@ -318,7 +318,7 @@ うんじゅがID, soundcloud.com/うんじゅがid くぬあしっさにゆりちうしんりょうきんがかじゅーいるくとぅがあいびーん。ぐちゅういください。 \n -\nずっこうさびーが\? +\nずっこうさびーが? とうるくさゃすう すくどぅとぅうんていりんちゃーさじんかいへいるかん(ゆがむんかむしりやびらん) むうんぬあいだにはやうくり @@ -330,7 +330,7 @@ みーさるNewPipeバージョンぬちうち がいぶちうちゅるすうちはりようなやびらん っちいちんかいむどぅすん - きていぬしっていふちゅるぎんさびーが\? + きていぬしっていふちゅるぎんさびーが? とうるくさゃすうはりようでぃきましん がいぎ ヨーロッパぬいちぱんデータふぐきせい(GDPR)にじゅんきょしーんたみに、NewPipeぬくじんじょうほうふぐほうしんにぐちゅういください。ゆーゆでぃくぃみそーれー。 @@ -369,7 +369,7 @@ ダウンロードちゅうしするまでぃぬさちゃるいさいしこうかいすん ファイルさちゅるじょさびたん アプリぬこうしんちうち - やしがいぶSDカードんかいダウンロードなやびらん。ダウンロードフォルダーぬばしょリセットさびーが\? + やしがいぶSDカードんかいダウンロードなやびらん。ダウンロードフォルダーぬばしょリセットさびーが? ふずんさったるタブゆみくみらんたみ、デフォルトぬタブしーようさびーん メインページんかいひょうじさりーるタブ みーさるバージョンがいようかのうなとぅちにアプリぬこうしんかくにんすんちうちひょうじさびーん @@ -392,9 +392,9 @@ ファイルうわがきなやびらん いぬファイルめいぬダウンロードぬしでぃにしんこうちゅうやいびーん ファイルぬさぎょうちゅうにNewPipeぬくーららりやびたん - デバイスんかいにりらりょうぬあいびらん + デバイスんかいにりらりょうぬあいびらん ファイルぬさちゅるじょさったるたみ、しんこうじょうちゅーがうしならりやびたん - ダウンロードりりき、あらんでぃダウンロードさるファイルしーょうきょさびーが\? + ダウンロードりりき、あらんでぃダウンロードさるファイルしーょうきょさびーが? ダウンロードキューぬせいぎん どうじーがてぃーちぬダウンロードぬじっこうさりやびーん ダウンロードぬかいし @@ -406,7 +406,7 @@ ストレージアクセスフレームワークー、がいぶSDカードんかいぬダウンロードぬかのうやいびーん さいせいいちのさくじょ まじりぬさいせいいちさちゅるじょさびーん - まじりぬさいせいいちさちゅるじょさびーが\? + まじりぬさいせいいちさちゅるじょさびーが? サービスぬきりけーい、ぎんじぇーぬしんたく: %sふんぬちゃーしが @@ -541,7 +541,7 @@ URLにいるしちなやびらんたん。ふかぬアプリっしふぃらちゃびーが? ちまーにひょうじさりーるサムネイル16:9からせいほうけいなさびーん サムネイルせいほうけいなすん - いかタップしちうちぬアクションへんしゅうさびーん。みぎぬわんかいあんチェックボックスしーようしコンパクトやるちまーにひょうじすし3ちまっししんたくさびーん + いかタップしちうちぬアクションへんしゅうさびーん。みぎぬわんかいあんチェックボックスしーようしコンパクトやるちまーにひょうじすし3ちまっししんたくさびーん。 コンパクトやるちまーにひょうじさりーるアクションー3ちまっしいらぶるくとぅがなやびーん! 5ばんみぬアクションボタン 4ばんみぬアクションボタン @@ -651,7 +651,7 @@ しーょりちゅう… くーてーんじがんがかかいんかむしりやびらん みーさるバージョンしーゅどうでぃかくにんさびーん アップデートかくにんちゅう… - アップデートかくにん + アップデートかくにん ちぎキューんかいちちが ちぎキューんかいちいからさびたん クリエイターぬちむくみてぃ @@ -718,9 +718,9 @@ はいーらでぃひょうじさっとーるプレイリストんかえー、しでぃにくぬアイテムぬくくまっとーいびーん。 %dかいじゅうふくちいかさりやびたん くていサムネイルぬしっていこーいじょ - じゅうふくさくじょさびーが\? + じゅうふくさくじょさびーが? たとぅいねー、ぶちりボタンぬやんでぃたるヘッドセットしーようそーーあいにびんりやいびーん - くぬさいせいリストねーんぬじゅうふくさるストリームまじりさくじょさびーが\? + くぬさいせいリストねーんぬじゅうふくさるストリームまじりさくじょさびーが? ハードウェアメディアボタンイベントむしすん じゅうふくさちゅるじょ ちぎぬストリームひょうじ @@ -806,4 +806,11 @@ プレイリストちゅーゆーいん プレイリストめいてぃがろービデオタイトルんでーぬしょうさいくくむるプレイリスト、あらんでぃビデオURLぬみぬシンプルやるリストとぅしてぃプレイリストちゅーゆーいんさびーん - %1$s: %2$s + + %sぬへんしん + %sぬへんしん + + なーふぃんんーじゅん + ひょうじいきらくすん + いかぬちうちアクションタップしへんしゅうさびーん。さいしょぬみーちぬアクション (さいせい/いちじていし、めーんかい、ちぎんかい)ーシステムにゆってぃしっていさりてぃうぅい、カスタマイズすしぇーなやびらん。 \ No newline at end of file diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index 836ab01c3..a5959086e 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -59,7 +59,7 @@ ᱛᱮᱥᱟᱨ ᱠᱟᱹᱢᱤ ᱵᱩᱛᱟᱹᱢ ᱫᱚᱥᱟᱨ ᱠᱟᱹᱢᱤ ᱵᱩᱛᱟᱹᱢ ᱢᱟᱲᱟᱝ ᱠᱟᱹᱢᱤ ᱵᱩᱛᱟᱹᱢ - ᱛᱷᱟᱹᱢᱵᱽᱱᱮᱞ ᱫᱚ ᱑:᱑ ᱮᱥᱯᱮᱠᱼᱴ ᱨᱮᱥᱤᱭᱚ ᱨᱮ ᱢᱟᱯ ᱢᱮ + ᱛᱷᱟᱹᱢᱵᱽᱱᱮᱞ ᱫᱚ ᱑:᱑ ᱮᱥᱯᱮᱠᱼᱴ ᱨᱮᱥᱤᱭᱚ ᱨᱮ ᱜᱮᱫ ᱟᱠᱟᱱᱟ ᱴᱷᱤᱠ ᱥᱟᱰᱮ ᱧᱮᱞ ᱠᱟᱱ ᱞᱮᱠᱷᱟ ᱪᱤᱱᱦᱟᱹ ᱠᱟᱜ ᱢᱮ @@ -69,4 +69,689 @@ ᱧᱩᱛ ᱧᱩᱛ ᱛᱷᱤᱢ ᱦᱮᱱᱫᱮ + ᱪᱮᱛᱟᱱ ᱥᱤᱠᱷᱱᱟᱹᱛ (ᱢᱟᱨᱟᱝ) + %1$s %2$s + ᱵᱚᱫᱚᱞ ᱢᱮ + ᱡᱚᱛᱚ ᱠᱷᱚᱱ ᱱᱟᱯᱟᱭ ᱨᱩᱣᱟᱹ + ᱦᱟᱹᱴᱤᱧ ᱪᱷᱟᱯᱟ ᱟᱠᱟᱱᱟ + ᱢᱤᱰᱤᱭᱟ ᱴᱚᱱᱮᱞᱤᱝ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱥᱟᱱᱟᱢ ᱚᱵᱚᱫᱷᱤ + ᱰᱤᱯᱷᱚᱞᱴ ᱥᱟᱰᱮ ᱯᱷᱚᱨᱢᱮᱴ + ᱧᱮᱞ ᱱᱟᱜᱟᱢ + ᱠᱷᱤᱞᱚᱱᱰ ᱮᱦᱚᱵ ᱢᱮ + ᱴᱨᱮᱠ + ᱤᱢᱯᱳᱨᱴ ᱰᱮᱴᱟᱵᱮᱥ + ᱪᱷᱟᱸᱪ + ᱟᱢᱟᱜ ᱟᱭᱳ ᱟᱲᱟᱝ, ᱰᱤᱡᱟᱭᱤᱱ ᱵᱮᱵᱚᱥᱛᱟ, ᱠᱳᱰ ᱥᱟᱹᱠᱷᱭᱟᱹᱛ, ᱟᱨᱵᱟᱝ ᱟᱹᱰᱤ ᱢᱟᱨᱟᱝ ᱠᱳᱰ ᱵᱮᱵᱚᱥᱛᱟ ᱨᱮᱱᱟᱜ ᱟᱭᱳ ᱟᱲᱟᱝ ᱢᱮᱱᱟᱜᱼᱟ ⁇ ᱜᱚᱲᱚ ᱫᱚ ᱡᱚᱛᱚ ᱚᱠᱛᱚ ᱨᱮᱜᱮ ᱥᱟᱨᱦᱟᱣ ᱢᱮᱱᱟᱜᱼᱟ ᱾ ᱰᱤᱜᱟᱱ ᱠᱟᱹᱢᱤ ᱦᱩᱭ ᱞᱮᱱ ᱠᱷᱟᱱ ᱚᱱᱟ ᱜᱮ ᱱᱟᱯᱟᱭᱚᱜ-ᱟ ᱾ + GitHub ᱨᱮ ᱧᱮᱞ ᱢᱮ + ᱡᱟᱹᱥᱛᱤ ᱞᱟᱭᱠᱟᱨ + ᱰᱟᱩᱱᱞᱚᱰ ᱟᱠᱟᱱ ᱯᱷᱤᱞᱤᱯ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱰᱤᱥᱠ ᱠᱷᱚᱱ ᱡᱚᱛᱚ ᱰᱟᱩᱱᱞᱚᱰ ᱟᱠᱟᱱ ᱯᱷᱤᱞ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ? + ᱨᱮᱰᱤᱭᱳ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ + ᱜᱽᱨᱤᱰ + ᱥᱴᱚᱯ + ᱰᱟᱩᱱᱞᱚᱰ ᱞᱤᱢᱤᱴᱮᱰ ᱠᱩᱣᱮ + ᱢᱩᱬᱩᱛ ᱴᱮᱵᱽ ᱵᱟᱪᱷᱟᱣᱤᱭᱟᱹ ᱫᱚ ᱞᱟᱛᱟᱨ ᱨᱮ ᱞᱟᱜᱟᱜ ᱢᱮ + ᱵᱞᱮᱠ ᱥᱟᱦᱴᱟ + ᱠᱟᱨᱰ + ᱵᱟᱦᱨᱮ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱵᱷᱤᱰᱤᱭᱳ ᱥᱴᱨᱤᱢ ᱵᱟᱭ ᱧᱟᱢᱚᱜ ᱠᱟᱱᱟ + ᱵᱟᱦᱨᱮ ᱮᱱᱮᱡᱤᱭᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱵᱟᱝ ᱵᱟᱰᱟᱭ ᱟᱠᱟᱱ ᱯᱷᱚᱨᱢᱮᱴ + ᱵᱟᱹᱱᱩᱜᱼᱟ ᱫᱟᱜ + ᱱᱚᱶᱟ ᱞᱟᱹᱠᱛᱤᱭᱟᱱ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ_x000D_ +\nᱯᱚᱯ-ᱟᱯ ᱢᱚᱰ ᱨᱮ ᱮᱛᱦᱚᱵ ᱢᱮ + ᱡᱚᱠᱷᱚᱱ ᱥᱟᱦᱟᱣ ᱟᱠᱟᱱᱟ \"Done\" ᱚᱞ ᱢᱮ + ᱨᱩᱣᱟ. + ᱠᱤᱭᱳᱥᱠ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱱᱟᱣᱟ ᱟᱨ ᱦᱮᱴ + ᱱᱟᱦᱟᱜ ᱥᱮᱞᱮᱫ ᱟᱠᱟᱱᱟ + ᱪᱟᱱᱮᱞ ᱵᱟᱰᱟᱭ ᱧᱮᱞ ᱢᱮ + ᱤᱱᱠᱭᱩᱤᱝ + ᱮᱱᱠᱭᱩᱣᱮᱰ + ᱚᱛᱱᱚ ᱨᱮ ᱥᱮᱞᱮᱫ ᱮᱛᱚᱦᱚᱵ ᱢᱮ + ᱡᱚᱛᱚ ᱚᱠᱛᱚ ᱯᱮᱨᱮᱡ ᱢᱮ + ᱤᱱᱯᱷᱳ ᱧᱟᱢ… + ᱞᱟᱹᱠᱛᱤ ᱟᱠᱟᱱ ᱥᱟᱦᱴᱟ ᱞᱚᱰ ᱮᱱᱟ + ᱧᱩᱛᱩᱢ + ᱥᱮᱨᱮᱧ ᱞᱤᱥᱴᱤ ᱨᱮ ᱥᱮᱞᱮᱫᱽ ᱢᱮ + ᱠᱟᱹᱢᱤᱦᱚᱨᱟ... ᱚᱠᱛᱚ ᱫᱷᱟᱹᱵᱤᱡ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ + ᱠᱨᱟᱥ ᱴᱷᱮᱱ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ + ᱟᱢᱟᱜ ᱚᱱᱚᱞ ᱨᱮ ᱢᱤᱰᱤᱭᱟ ᱴᱩᱱᱮᱞᱤᱝ ᱫᱚ ᱰᱤᱯᱚᱞᱴ ᱞᱮᱠᱟᱛᱮ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ ᱪᱮᱫᱟᱜ ᱥᱮ ᱟᱢᱟᱜ ᱚᱱᱚᱞ ᱢᱚᱰᱮᱞ ᱱᱚᱶᱟ ᱵᱟᱭ ᱥᱚᱦᱚᱫᱟ ᱾ + ᱮᱯᱞᱤᱠᱮᱥᱚᱱ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱢᱤᱫ error ᱥᱱᱮᱠᱵᱟᱨ ᱧᱮᱞ ᱢᱮ + URL ᱥᱮ ᱟᱢᱟᱜ ID ᱚᱞ ᱠᱟᱛᱮᱜ ᱢᱤᱫ SoundCloud ᱯᱨᱚᱯᱷᱟᱭᱤᱞ ᱮᱢ ᱢᱮ:_x000D_ +\n_x000D_ +\n1. ᱢᱤᱫ ᱣᱮᱵᱽ-ᱵᱽᱨᱟᱣᱡᱚᱨ ᱨᱮ \"ᱰᱮᱥᱠᱴᱚᱯ ᱢᱳᱰ\" ᱮᱱᱮᱡ ᱢᱮ (ᱡᱚᱵᱮᱛᱟᱨᱮ ᱢᱚᱵᱟᱭᱤᱞ ᱰᱤᱵᱟᱤᱥ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱝ ᱧᱟᱢᱚᱜᱼᱟ)_x000D_ +\n2. ᱱᱚᱶᱟ URL ᱨᱮ ᱪᱟᱞᱟᱜ ᱢᱮ: %1$s_x000D_ +\n3. ᱞᱚᱜᱤᱱ ᱢᱮ ᱡᱚᱠᱷᱚᱱ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ_x000D_ +\n4. ᱟᱢ ᱚᱠᱟ ᱨᱮ ᱥᱮᱞᱮᱫ ᱞᱮᱱᱟᱢ ᱚᱱᱟ ᱯᱨᱚᱯᱷᱟᱭᱤᱞ URL ᱠᱚᱯᱤ ᱢᱮ. + ᱯᱞᱮᱵᱮᱠ ᱥᱯᱤᱰ ᱠᱚᱱᱴᱨᱚᱞ + ᱴᱮᱢᱯᱳ + ᱵᱚᱫᱚᱞ ᱢᱮ (ᱵᱚᱫᱚᱞ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ) + ᱥᱤᱛᱩᱝ ᱚᱠᱛᱚ ᱨᱮ ᱯᱷᱮᱥᱯᱷᱚᱨᱰ ᱢᱮ + ᱯᱨᱚ᱐ᱤᱥᱚᱫ + ᱮᱠᱥᱮᱯᱴ + ᱱᱟᱣᱟ ᱥᱴᱨᱚᱢ ᱨᱮᱭᱟᱜ ᱠᱷᱚᱵᱚᱨ + ᱪᱟᱪᱞᱟᱣ ᱚᱵᱚᱥᱛᱟ + ᱱᱟᱣᱟ ᱥᱟᱦᱴᱟ ᱧᱮᱞ ᱢᱮ + ᱛᱷᱟᱯᱚᱱ + ᱱᱚᱶᱟ ᱧᱩᱛᱩᱢ ᱟᱱᱟᱜ ᱢᱤᱫ ᱯᱷᱤᱞ ᱢᱮᱱᱟᱜᱼᱟ + ᱱᱚᱶᱟ ᱧᱩᱛᱩᱢ ᱟᱱ ᱢᱤᱫ ᱰᱟᱩᱱᱞᱚᱰ ᱟᱠᱟᱱ ᱯᱷᱤᱞ ᱢᱮᱱᱟᱜᱼᱟ + ᱥᱤᱠᱷᱱᱟᱹᱛ ᱡᱚᱱᱚᱲᱟᱣ ᱵᱟᱭ ᱛᱮᱭᱟᱨ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ + ᱡᱟᱦᱟᱸ ᱨᱮ ᱰᱟᱩᱱᱞᱚᱰ ᱢᱮ + ᱮᱠᱥᱳᱯᱞᱮᱭᱟᱨ ᱞᱟᱹᱠᱛᱤᱭᱟᱜ ᱞᱟᱹᱠᱛᱤᱭᱟᱜ ᱞᱟᱹᱠᱛᱤᱭᱟᱜ ᱚᱠᱛᱚ %d ᱥᱮᱠᱮᱱᱰ ᱨᱮ ᱞᱟᱜᱟᱣ ᱟᱠᱟᱱᱟ + ᱪᱮᱫ ᱱᱟᱣᱟ + ᱵᱟᱦᱨᱮ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹᱠᱚ ᱵᱟᱠᱚ ᱥᱚᱦᱚᱫ ᱟᱠᱟᱫ ᱪᱷᱟᱸᱪ + ᱵᱟᱦᱨᱮ ᱯᱞᱮᱭᱟᱨ ᱞᱟᱹᱜᱤᱫ ᱚᱰᱤᱭᱚ ᱥᱴᱨᱤᱢ ᱵᱟᱭ ᱧᱟᱢᱚᱜ ᱠᱟᱱᱟ + ᱚᱥᱚᱞ + ᱥᱮᱨᱮᱧ + ᱱᱟᱣᱟ + ᱱᱚᱶᱟ ᱨᱮ ᱴᱟᱯ ᱠᱟᱛᱮ ᱞᱟᱛᱟᱨ ᱨᱮ ᱮᱢ ᱟᱠᱟᱱ ᱡᱚᱛᱚ ᱱᱤᱯᱷᱩᱴ ᱮᱠᱥᱚᱱ ᱥᱟᱯᱲᱟᱣ ᱢᱮ ᱾ ᱯᱩᱭᱞᱩ ᱯᱮᱭᱟ ᱠᱟᱹᱢᱤ (ᱯᱞᱮ/ᱯᱷᱟᱣᱩᱥ, ᱯᱩᱭᱞᱩ ᱟᱨ ᱛᱟᱭᱚᱢ) ᱫᱚ ᱥᱤᱥᱴᱚᱢ ᱦᱚᱛᱮᱛᱮ ᱥᱮᱴᱮᱨ ᱟᱠᱟᱱᱟ ᱟᱨ ᱱᱚᱣᱟ ᱠᱚ ᱟᱹᱨᱤᱪᱟᱹᱞᱤ ᱛᱮ ᱵᱟᱭ ᱵᱚᱫᱚᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱾ + ᱟᱱᱰᱨᱚᱭᱮᱰ ᱴᱷᱟᱹᱣᱠᱟᱹ ᱟᱠᱟᱱ ᱪᱤᱛᱟᱹᱨ ᱨᱮ ᱢᱩᱬ ᱪᱤᱛᱟᱹᱨ ᱞᱮᱠᱟᱛᱮ ᱱᱤᱯᱷᱩᱴ ᱪᱤᱛᱟᱹᱨ ᱨᱮᱱᱟᱜ ᱨᱚᱝ ᱵᱮᱱᱟᱣ ᱢᱮ (ᱱᱚᱶᱟ ᱡᱚᱛᱚ ᱰᱤᱵᱟᱤᱥ ᱨᱮ ᱵᱟᱝ ᱧᱟᱢᱚᱜᱼᱟ) + ᱴᱟᱴᱠᱟ ᱜᱩᱱᱠᱚ ᱢᱚᱱᱮ ᱠᱟᱜ ᱢᱮ + ᱯᱷᱮᱥᱯᱷᱚᱨᱰ/ᱨᱟᱭᱣᱤᱞᱰ ᱥᱮᱪᱮᱛ ᱚᱠᱛᱚ + ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ, ᱡᱩᱫᱤ ᱟᱢ ᱡᱟᱦᱟᱸᱭ ᱵᱷᱤᱛᱨᱤ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱ ᱵᱷᱤᱛᱨᱤ ᱵᱚᱫᱚᱞ ᱛᱮ ᱦᱮᱰᱥᱮᱴ ᱠᱚ ᱵᱮᱵᱷᱟᱨ ᱮᱫᱟ + ᱴᱤᱯᱚᱬᱤ ᱠᱚ ᱧᱮᱞ ᱢᱮ + ᱵᱷᱤᱰᱤᱭᱳ ᱵᱟᱵᱚᱫᱽ ᱟᱨ ᱮᱴᱟᱜ ᱵᱟᱰᱟᱭ ᱞᱟᱹᱜᱤᱫ ᱱᱚᱶᱟ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱪᱤᱛᱟᱹᱨ ᱠᱮᱪ ᱵᱚᱫᱚᱞ + ᱠᱟᱪᱷ ᱢᱮᱴᱟᱰᱮᱴᱟ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱥᱟᱱᱟᱢ ᱠᱮᱪ ᱟᱠᱟᱱ ᱣᱮᱵᱽ ᱯᱮᱡ ᱨᱮᱱᱟᱜ ᱰᱮᱴᱟ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱥᱠᱨᱤᱱ ᱨᱮᱱᱟᱜ ᱪᱮᱛᱟᱱ ᱨᱮ ᱪᱤᱱᱦᱟᱹ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱵᱷᱚᱞᱚᱢ + ᱡᱟᱦᱟᱱ ᱵᱟᱝ + ᱛᱚᱨᱡᱚᱢᱟ ᱱᱟᱜᱟᱢ + ᱞᱚᱠᱟᱞ ᱞᱮᱠᱟᱛᱮ ᱥᱩᱪᱚᱱᱟ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱞᱤᱥᱴᱤ ᱨᱮ ᱯᱳᱡᱤᱥᱚᱱ + ᱞᱤᱥᱴᱤ ᱨᱮ ᱯᱞᱮᱼᱯᱷᱚᱞ ᱯᱳᱡᱤᱥᱚᱱ ᱤᱱᱰᱮᱠᱴᱚᱨ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱥᱟᱱᱛᱟᱲᱤ ᱚᱱᱚᱞ + enqueue ᱴᱤᱯ ᱞᱟᱹᱜᱤᱫ ᱫᱚᱦᱚᱭ ᱢᱮ + Details: ᱨᱮ ᱵᱷᱤᱰᱤᱭᱳ ᱨᱮ ᱯᱷᱚᱱ ᱥᱮ ᱯᱚᱯᱯᱟᱯ ᱵᱩᱛᱟᱹᱢ ᱞᱟᱜᱟᱣ ᱞᱮᱠᱷᱟᱱ ᱴᱤᱯ ᱮᱢ ᱢᱮ + %s ᱨᱮ ᱟᱢᱟᱜ ᱞᱟᱹᱠᱛᱤ ᱠᱚ ᱧᱮᱞ ᱢᱮ + ᱱᱟᱜᱟᱢ ᱟᱨ ᱠᱷᱟᱪ + ᱧᱮᱧᱮᱞ + ᱰᱤᱵᱩᱜ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱵᱟᱰᱟᱭ + ᱛᱷᱚᱠ ᱨᱮ ᱥᱮᱞᱮᱫᱚᱜ + ᱱᱚᱶᱟ ᱵᱷᱤᱰᱤᱭᱳ ᱫᱚ ᱩᱢᱮᱨ ᱦᱟᱹᱴᱤᱧ ᱠᱟᱱᱟ._x000D_ +\n_x000D_ +\nᱡᱩᱫᱤ ᱟᱢ ᱱᱚᱶᱟ ᱧᱮᱞᱢᱮ ᱠᱷᱟᱱ ᱟᱢᱟᱜ ᱥᱤᱴᱤᱝ ᱨᱮ %1$s\" ᱚᱞ ᱢᱮ ᱾\" + ᱰᱟᱩᱱᱞᱚᱰ + ᱰᱟᱩᱱᱞᱚᱰ + ᱟᱞᱵᱚᱢ + ᱟᱨᱴᱤᱥᱴ ᱠᱚ + ᱵᱷᱮᱜᱟᱨ ᱦᱚᱲ + ᱠᱞᱟᱨ + ᱱᱤᱭᱩ ᱯᱟᱭᱯᱮ ᱨᱮᱱᱟᱜ ᱯᱞᱮᱭᱟᱨ ᱞᱟᱹᱜᱤᱫ ᱱᱳᱴᱤᱯᱷᱤᱠᱮᱥᱚᱱ + ᱵᱷᱤᱰᱤᱭᱳ ᱦᱟᱥᱤᱝ ᱯᱨᱚᱜᱨᱮᱥ ᱞᱟᱹᱜᱤᱫ ᱠᱷᱚᱵᱚᱨ + ᱱᱟᱣᱟ ᱫᱟᱨᱮ ᱠᱚ + ᱥᱮᱞᱮᱫ ᱞᱟᱹᱜᱤᱫ ᱱᱟᱣᱟ ᱥᱴᱨᱚᱢ ᱠᱚ ᱵᱟᱰᱟᱭ ᱦᱚᱪᱚ + ᱵᱟᱹᱲᱤᱡ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱠᱷᱚᱵᱚᱨ ᱠᱚ + [ᱱᱚᱣᱟ ᱵᱟᱰᱟᱭᱚᱜ ᱠᱟᱱᱟ] + reCAPTCHA ᱠᱩᱠᱤ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + reCAPTCHA ᱠᱩᱠᱤ ᱠᱚ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱮᱠᱥᱯᱳᱨᱴ ᱦᱤᱥᱛᱟ, ᱥᱚᱵᱽᱥᱠᱨᱟᱭᱵᱮᱥᱚᱱ, ᱯᱞᱮ ᱞᱤᱥᱴ ᱟᱨ ᱥᱮᱴᱤᱝ + ᱧᱮᱞ ᱛᱟᱵᱩᱱ ᱯᱮ ᱱᱟᱜᱟᱢ + ᱠᱷᱮᱞ ᱟᱠᱟᱱ ᱥᱴᱨᱤᱢ ᱨᱮᱱᱟᱜ ᱱᱟᱜᱟᱢ ᱟᱨ ᱠᱷᱮᱞ ᱟᱠᱟᱱ ᱯᱳᱡᱤᱥᱚᱱ ᱠᱚ ᱵᱚᱫᱚᱞᱟ + ᱥᱟᱱᱟᱢ ᱧᱮᱞ ᱱᱟᱜᱟᱢ ᱵᱚᱫᱚᱞ ᱢᱮ? + ᱧᱮᱞ ᱛᱟᱵᱩᱱ ᱯᱮ + ᱵᱟᱦᱨᱮ ᱥᱴᱳᱨᱮᱡ ᱵᱟᱭ ᱧᱟᱢᱚᱜ + ᱧᱮᱞ ᱛᱟᱵᱩᱱ ᱯᱮ ᱺ + ᱦᱟᱛᱟᱝ ᱢᱚᱰ + ᱢᱩᱬᱩᱛ ᱴᱮᱵᱽ ᱨᱮᱱᱟᱜ ᱴᱷᱟᱶ + ᱵᱟᱦᱨᱮ SD ᱠᱟᱨᱰ ᱨᱮ ᱰᱟᱩᱱᱞᱚᱰ ᱦᱚᱪᱚ ᱵᱟᱝ ᱜᱟᱱᱚᱜ ᱟ. ᱰᱟᱩᱱᱞᱚᱰ ᱯᱷᱳᱞᱰᱟᱨ ᱞᱚᱠᱮᱥᱚᱱ ᱨᱤᱥᱮᱴ ᱢᱮ ? + ᱡᱚᱛᱚ ᱪᱤᱛᱟᱹᱨ ᱠᱚ ᱞᱚᱰ ᱵᱟᱝ ᱜᱟᱱᱚᱜ ᱠᱟᱱᱟ + ᱣᱮᱵᱽᱥᱟᱭᱤᱴ ᱵᱟᱭ ᱯᱟᱨᱥ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ + ᱥᱟᱦᱴᱟ ᱵᱟᱹᱱᱩᱜ-ᱟ + ᱵᱟᱦᱨᱮ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱠᱚ ᱱᱚᱝᱠᱟᱱ ᱡᱚᱱᱚᱲ ᱠᱚ ᱵᱟᱠᱚ ᱥᱚᱦᱚᱫᱟ + ᱡᱟᱦᱟᱱ ᱵᱷᱤᱰᱤᱭᱳ ᱥᱴᱨᱤᱢ ᱵᱟᱝ ᱧᱟᱢ ᱟᱠᱟᱱᱟ + ᱚᱰᱤᱭᱳ ᱥᱴᱨᱤᱢ ᱵᱟᱝ ᱧᱟᱢ ᱟᱠᱟᱱᱟ + ᱢᱤᱫ ᱵᱷᱩᱞ ᱦᱩᱭ ᱞᱮᱱᱟ, ᱱᱤᱴᱤᱯᱷᱤᱠᱮᱥᱚᱱ ᱧᱮᱞ ᱢᱮ + GitHub ᱨᱮ ᱚᱱᱚᱞ + ᱨᱤᱯᱚᱨᱴ + What:\\nRequest:\\nContent Language:\\nContent ᱫᱤᱥᱚᱢ:\\nApp Language:\\nService:\\nGMT ᱚᱠᱛᱚ:\\nPackage:\\nVersion:\\nOS version: + ᱟᱢᱟᱜ ᱯᱟᱹᱛᱭᱟᱹᱣ (ᱤᱝᱞᱤᱥ ᱛᱮ): + ᱚᱯᱞᱚᱰᱟᱨᱟᱜ ᱟᱵᱟᱛᱟᱨ ᱛᱷᱩᱱᱤᱠᱟ + ᱞᱟᱭᱠ + ᱡᱟᱦᱟᱱ ᱡᱤᱱᱤᱥ + ᱱᱚᱣᱟ ᱨᱮᱫᱚ ᱡᱟᱹᱥᱛᱤ ᱡᱟᱹᱥᱛᱤ ᱠᱨᱤᱠᱮᱴ ᱢᱮᱱᱟᱜᱼᱟ ᱾ + ᱵᱷᱤᱰᱤᱭᱳ + k + M + ᱥᱮᱞᱮᱫᱤᱭᱟᱹ ᱠᱚᱣᱟᱜ ᱞᱮᱠᱷᱟ ᱵᱟᱭ ᱦᱟᱹᱴᱤᱧ ᱟᱠᱟᱱᱟ + ᱵᱟᱱᱩᱜ ᱧᱮᱞ + ᱵᱷᱤᱰᱤᱭᱳ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱑᱐᱐+ ᱵᱷᱤᱰᱤᱭᱳ + ᱡᱤᱣᱤ ᱥᱴᱨᱤᱢ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱪᱮᱠᱥᱢ + ᱵᱚᱫᱚᱞ ᱢᱮ + ᱫᱟᱭᱟ ᱠᱟᱛᱮ ᱮᱛᱦᱚᱵ ᱢᱮ ᱾ + ᱠᱞᱤᱯᱵᱳᱨᱰ ᱨᱮ ᱠᱚᱯᱤ ᱟᱠᱟᱱᱟ + reCAPTCHA ᱪᱟᱞᱟᱣ + ᱵᱮᱱᱟᱣ ᱪᱤᱠᱤ + ᱵᱟᱹᱞᱤ ᱪᱤᱠᱤ ᱠᱚ ᱱᱚᱶᱟ ᱢᱟᱹᱱ ᱥᱟᱞᱟᱜ ᱛᱮ ᱵᱚᱫᱚᱞᱚᱜᱼᱟ + ᱚᱞ ᱟᱨ ᱮᱞ + ᱡᱟᱹᱥᱛᱤ ᱵᱤᱥᱮᱥ ᱪᱤᱠᱤ + ᱪᱟᱹᱞᱩ ZIP ᱯᱷᱤᱞ ᱫᱚ ᱵᱟᱝ + ᱞᱟᱹᱱᱟᱹᱤ: ᱡᱚᱛᱚ ᱪᱤᱱᱦᱟᱹ ᱠᱚ ᱤᱱᱯᱷᱚᱨᱢ ᱵᱟᱭ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ. + ᱱᱚᱶᱟ ᱫᱚ ᱟᱢᱟᱜ ᱱᱮᱛᱟᱨᱟᱜ ᱥᱮᱴᱯᱟᱴ ᱩᱫᱩᱜ ᱮᱫᱟ ᱾ + ᱟᱢ ᱥᱤᱴᱤᱝ ᱦᱚᱸ ᱟᱢᱯᱷᱮᱨᱟᱣ ᱦᱩᱭᱩᱜ ᱛᱟᱢᱟ? + ᱠᱚᱢᱮᱱᱴ ᱞᱚᱰ ᱫᱚ ᱵᱟᱝ ᱜᱟᱱᱚᱜ ᱠᱟᱱᱟ + ᱢᱩᱬ ᱕᱐ + ᱠᱚᱱᱯᱷᱮᱨᱮᱱᱥ + ᱠᱷᱮᱞ ᱞᱟᱦᱟ + ᱥᱟᱦᱟᱣ ᱢᱮ + ᱡᱚᱠᱷᱟ + ᱚᱰᱤᱭᱳ ᱴᱨᱟᱠ + ᱥᱮᱞᱮᱫᱚᱜ ᱞᱟᱹᱜᱤᱫ ᱫᱚᱦᱚᱭ ᱢᱮ + ᱛᱟᱭᱚᱢ ᱛᱮ Enqueue + ᱛᱟᱭᱚᱢ ᱛᱮ ᱥᱮᱞᱮᱫ ᱢᱮ + ᱚᱯᱮᱱ ᱰᱨᱟᱵᱷᱤᱴ + ᱵᱟᱹᱲᱛᱤ ᱞᱟᱹᱠᱛᱤᱭᱟᱱ \"ᱚᱯᱮᱱ\" ᱠᱟᱹᱢᱤ + ⁇ %s ᱡᱚᱠᱷᱚᱱ ᱥᱟᱦᱴᱟ ᱵᱚᱫᱚᱞᱚᱜ-ᱟ + ᱵᱷᱤᱰᱤᱭᱳ ᱯᱞᱮᱭᱟᱨ + ᱯᱟᱛᱷᱟᱢ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ + ᱯᱚᱯᱯᱟᱯ ᱯᱞᱮᱭᱟᱨ + ᱥᱟᱨᱮᱡ ᱟᱠᱟᱱ ᱯᱞᱮ ᱞᱤᱥᱴᱤ ᱨᱮ ᱱᱤᱛᱚᱜ ᱱᱚᱣᱟ ᱡᱤᱱᱤᱥ ᱢᱮᱱᱟᱜᱼᱟ ᱾ + ᱧᱩᱛᱩᱢ ᱵᱚᱫᱚᱞ ᱢᱮ + Unmute + ᱵᱟᱝ ᱥᱮᱴᱮᱨ ᱟᱠᱟᱱ ᱟᱹᱪᱩᱨ ᱦᱟᱹᱴᱤᱧ + ᱯᱮᱭᱞᱤᱥᱴ + %d ᱚᱠᱛᱚ ᱵᱟᱨᱟᱵᱟᱨᱤ ᱥᱮᱞᱮᱫᱚᱜᱼᱟ + ᱪᱤᱛᱟᱹᱨ ᱵᱮᱱᱟᱣ ᱟᱠᱟᱱᱟ + ᱥᱮᱨᱮᱧ ᱞᱤᱥᱴᱤ ᱨᱮᱱᱟᱜ ᱛᱷᱤᱢᱵᱽᱱᱟᱭᱟᱨ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ. + ᱚᱴᱚ-ᱡᱮᱱᱮᱨᱮᱴ (ᱯᱷᱮᱰᱟᱛ ᱵᱟᱭ ᱧᱟᱢ ᱞᱮᱱᱟ) + ᱚᱞ ᱪᱤᱠᱤ ᱵᱟᱹᱱᱩᱜ + ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ + ᱯᱷᱤᱞᱰ + ᱡᱩᱢ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱜ ᱪᱷᱟᱸᱪ ᱚᱞ ᱨᱮᱱᱟᱜ ᱟᱥᱚᱞ ᱟᱨ ᱯᱟᱛᱷᱟᱢ ᱥᱴᱟᱭᱤᱞᱥ ᱵᱚᱫᱚᱞ ᱢᱮ. ᱪᱟᱹᱞᱩᱜ ᱞᱟᱹᱜᱤᱫ ᱮᱯ ᱮᱥᱴᱟᱨᱴ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ + ᱢᱮᱢᱚᱨᱤ ᱞᱤᱠᱟᱞ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + LeakCanary ᱵᱟᱭ ᱧᱟᱢᱚᱜ ᱠᱟᱱᱟ + ᱢᱮᱢᱚᱨᱤ ᱞᱤᱠᱟᱞ ᱢᱚᱱᱤᱴᱚᱨᱤᱝ ᱦᱤᱯ ᱰᱟᱢᱯᱤᱝ ᱚᱠᱛᱚ ᱨᱮ ᱮᱯᱞᱤᱠᱮᱥᱚᱱ ᱨᱟᱥᱴᱨᱤᱭ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ + ᱡᱤᱭᱚᱱ ᱪᱤᱠᱤ ᱠᱷᱚᱱ ᱵᱟᱦᱨᱮ ᱨᱮ ᱵᱷᱮᱜᱟᱨ ᱠᱚ ᱚᱱᱚᱞ ᱢᱮ + ᱱᱮᱴᱣᱟᱨᱠ ᱞᱟᱹᱜᱤᱫ red, ᱰᱤᱥᱠ ᱞᱟᱹᱜᱤᱫ blue ᱟᱨ ᱢᱮᱢᱚᱨᱤ ᱞᱟᱹᱜᱤᱫ green + ᱯᱞᱮᱭᱟᱨ ᱵᱮᱵᱷᱟᱨ ᱚᱠᱛᱮ ᱨᱮ ᱠᱨᱟᱥ ᱚᱯᱥᱚᱱ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ + ᱤᱢᱯᱳᱨᱴ + ᱤᱢᱯᱚᱨᱴ + ᱮᱥᱯᱳᱨᱴ ᱢᱮ + ᱤᱢᱯᱳᱨᱴ… + ᱮᱠᱥᱯᱳᱨᱴ… + ᱤᱢᱯᱳᱨᱴ ᱯᱷᱤᱞ + ᱢᱟᱲᱟᱝ ᱮᱠᱥᱯᱚᱨᱴ + ᱥᱚᱵᱥᱠᱨᱤᱯᱥᱚᱱ ᱮᱢ ᱫᱟᱲᱮᱭᱟᱜ ᱵᱟᱝ ᱠᱟᱱᱟ + \"ᱱᱚᱶᱟ ᱚᱯᱨᱮᱥᱚᱱ ᱨᱮ ᱱᱮᱴᱣᱟᱨᱠ ᱰᱷᱮᱨ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱢᱮᱱᱛᱮ ᱢᱟᱱᱟᱣ ᱢᱮ ᱾\" +\n +\nᱟᱢ ᱫᱚ ᱪᱮᱫ ᱮᱢ ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ?\" + ᱨᱤᱥᱮᱴ + ᱥᱮᱢᱤᱴᱚᱱ + ᱰᱷᱮᱨᱚᱜ + ᱞᱤᱢᱤᱴ ᱵᱟᱹᱱᱩᱜ + ᱢᱚᱵᱟᱤᱞ ᱰᱟᱴᱟ ᱵᱮᱵᱷᱟᱨ ᱚᱠᱛᱚ ᱨᱮ ᱞᱤᱢᱤᱴ ᱫᱟᱲᱮ + ᱱᱟᱣᱟ ᱵᱷᱮᱨᱥᱚᱱ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱢᱟᱱᱩᱣᱟᱞ ᱪᱮᱠ + ᱟᱯᱞᱤᱠᱮᱥᱚᱱ ᱥᱤᱴᱤᱪ ᱨᱮ ᱞᱟᱹᱴᱩ ᱦᱚᱪᱚ + ᱢᱩᱬᱩᱛ ᱵᱷᱤᱰᱤᱭᱳ ᱯᱞᱮᱭᱟᱨ ᱠᱷᱚᱱ ᱮᱴᱟᱜ ᱮᱯᱞᱤᱠᱮᱥᱚᱱ ᱨᱮ ᱥᱮᱞᱮᱫᱚᱜ ᱚᱠᱛᱚ ᱨᱮᱱᱟᱜ ᱠᱟᱹᱢᱤ ⁇ %s + ᱡᱟᱦᱟᱱ ᱵᱟᱝ + ᱚᱛᱱᱚ ᱯᱞᱮᱭᱟᱨ ᱨᱮ ᱞᱟᱹᱴᱩ ᱦᱚᱪᱚ + Wi-Fi ᱨᱮᱜᱮ + Seekbar thumbnail preview ᱧᱮᱞᱢᱮ + ᱠᱚᱢ ᱠᱣᱟᱞᱤᱴᱤ (ᱢᱤᱫ ᱠᱷᱚᱱ ᱠᱚᱢ) + ᱵᱟᱝ ᱧᱮᱞᱚᱜ + ᱟᱢ NewPipe ᱨᱮᱭᱟᱜ ᱱᱟᱣᱟ ᱵᱟᱨᱥᱚᱱ ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ + ᱯᱮᱱᱰᱤᱝ + ᱠᱩᱠᱞᱤ + ᱯᱟᱥᱴᱟ ᱯᱨᱚᱥᱟᱥᱚᱱ + ᱤᱱᱠᱭᱩᱤᱝ + ᱥᱤᱥᱴᱮᱢ ᱦᱚᱛᱮᱛᱮ ᱵᱟᱝ ᱧᱟᱢ ᱟᱠᱟᱱ ᱠᱟᱹᱢᱤ + ᱯᱷᱟᱹᱞ ᱪᱮᱛᱟᱱ ᱵᱟᱝ ᱚᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ + ᱱᱚᱶᱟ ᱧᱩᱛᱩᱢ ᱛᱮ ᱢᱤᱫ ᱰᱟᱩᱱᱞᱚᱰ ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ + ᱱᱚᱶᱟ ᱧᱩᱛᱩᱢ ᱛᱮ ᱢᱤᱫ ᱚᱞᱚᱜ ᱠᱟᱱ ᱰᱟᱩᱱᱞᱚᱰ ᱢᱮᱱᱟᱜᱼᱟ + ᱴᱷᱟᱶ ᱯᱷᱳᱞᱰᱚᱨ ᱛᱮᱭᱟᱨ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ + ᱱᱚᱶᱟ ᱰᱟᱩᱱᱞᱚᱰ ᱫᱚ ᱵᱟᱭ ᱨᱤᱠᱳᱨᱰ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ + ᱰᱟᱩᱱᱞᱚᱰ ᱦᱤᱥᱛᱟ ᱪᱷᱟᱯᱟ ᱢᱮ + ᱟᱹᱰᱤ ᱡᱟᱹᱥᱛᱤ ᱫᱟᱲᱮ + ᱢᱤᱴᱟᱨ ᱛᱮᱭᱟᱨ ᱟᱠᱟᱱ ᱱᱮᱴᱣᱟᱨᱠ ᱨᱮ ᱵᱚᱫᱚᱞ + ᱰᱟᱩᱱᱞᱚᱰ ᱮᱦᱚᱵ ᱢᱮ + ᱧᱮᱞᱚᱜ ᱟᱠᱟᱱ ᱥᱟᱯᱲᱟᱣ ᱢᱮ + ᱧᱮᱞ ᱟᱠᱟᱱ ᱵᱷᱤᱰᱤᱭᱳ ᱠᱚ ᱪᱷᱩᱴᱟᱹᱣ? + ᱫᱩᱯᱞᱟᱹᱲ ᱠᱚ ᱦᱮᱡ ᱢᱮ + ᱭ, ᱟᱨ ᱵᱷᱤᱰᱤᱭᱳ ᱠᱚ ᱡᱟᱦᱟᱸ ᱞᱮᱠᱟ ᱧᱮᱞᱚᱜᱼᱟ + ᱪᱟᱱᱮᱞ ᱜᱨᱩᱯ + ᱯᱷᱤᱰ ᱢᱩᱪᱟᱹᱫ ᱵᱚᱫᱚᱞᱟᱠᱟᱱ: %s + ᱵᱟᱝ ᱞᱚᱰ ᱟᱠᱟᱱᱟ: %d + ᱯᱷᱤᱰ ᱞᱚᱰᱚᱜ ᱠᱟᱱᱟ… + ᱠᱟᱹᱢᱤᱦᱚᱨᱟ ᱡᱚᱢᱟᱜ… + ᱜᱩᱴ ᱧᱩᱛᱩᱢ ᱵᱟᱱᱩᱜᱼᱟ + ᱟᱢ ᱱᱚᱶᱟ ᱜᱽᱨᱩᱯ ᱵᱚᱫᱚᱞ ᱢᱮᱢᱮ? + ᱱᱟᱣᱟ + ᱦᱟᱹᱴᱤᱧ ᱢᱚᱰ ᱨᱮ ᱥᱮᱞᱮᱫ ᱢᱮ + ᱞᱟᱛᱟᱨ ᱨᱮ ᱚᱞ ᱟᱠᱟᱱ ᱥᱴᱨᱤᱢ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱪᱟᱱᱮᱞ ᱛᱟᱵᱽ ᱠᱚ ᱦᱟᱛᱟᱣ + %s ᱛᱮᱭᱟᱨ ᱟᱠᱟᱱᱟ + %s ᱦᱚᱛᱮᱛᱮ + ᱛᱟᱢᱤᱞ ᱪᱤᱛᱟᱹᱨ ᱧᱮᱞ ᱢᱮ + ᱪᱮᱯᱴᱟᱨᱥ + ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ ᱟᱢ ᱢᱤᱫ ᱯᱷᱤᱞ ᱢᱟᱱᱮᱡᱚᱨ ᱤᱱᱥᱴᱚᱞ ᱢᱮ ᱟᱨᱵᱟᱝ ᱰᱟᱩᱱᱞᱚᱰ ᱥᱤᱴᱤᱝ ᱨᱮ ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚ ᱞᱟᱹᱜᱤᱫ ᱯᱨᱚᱵᱷᱟᱣ ᱢᱮ\" + ᱱᱚᱶᱟ ᱵᱷᱤᱰᱤᱭᱳ ᱫᱚ ᱭᱩᱴᱭᱩᱵᱽ ᱢᱤᱣᱡᱤᱠ ᱯᱨᱤᱢᱤᱭᱟᱢ ᱥᱮᱞᱮᱫᱤᱭᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱜᱮ ᱧᱟᱢᱚᱜᱼᱟ, ᱚᱱᱟᱛᱮ ᱱᱚᱶᱟ ᱫᱚ ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯ ᱦᱚᱛᱮᱛᱮ ᱵᱟᱝ ᱥᱴᱨᱤᱢ ᱟᱨ ᱵᱟᱝ ᱰᱟᱩᱱᱞᱳᱰ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ + %s ᱫᱚ ᱱᱚᱶᱟ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ: + ᱚᱴᱚᱢᱟᱴᱤᱠ (ᱰᱤᱵᱟᱤᱥ ᱛᱷᱮᱢ) + ᱟᱢᱟᱜ ᱯᱩᱭᱞᱩ ᱧᱤᱫᱟᱹ ᱛᱷᱤᱢ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ⁇ %s + ᱟᱢ ᱞᱟᱛᱟᱨ ᱨᱮ ᱟᱢᱟᱜ ᱧᱤᱫᱟᱹ ᱪᱮᱛᱟᱱ ᱵᱟᱪᱷᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ ᱟ + ᱪᱤᱛᱟᱹᱨ ᱨᱮ ᱚᱞ ᱪᱤᱠᱤ ᱵᱟᱪᱷᱟᱣ ᱫᱚᱦᱚᱭ ᱢᱮ + ᱛᱷᱚᱠ + ᱞᱟᱭᱥᱮᱱᱥ + ᱟᱵᱟᱛᱟᱨ + ᱵᱮᱱᱚᱨ + ᱵᱟᱝ ᱞᱤᱥᱴᱤ ᱟᱠᱟᱱᱟ + ᱱᱤᱴᱤᱯᱷᱤᱠᱮᱥᱚᱱ ᱠᱚ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱟᱢ ᱱᱤᱛᱚᱜ ᱱᱚᱣᱟ ᱪᱮᱱᱮᱞ ᱨᱮ ᱥᱮᱞᱮᱫ ᱟᱠᱟᱱᱟᱢ + , + ᱵᱟᱦᱨᱮ ᱯᱞᱮᱭᱟᱨ ᱞᱟᱹᱜᱤᱫ ᱚᱰᱤᱭᱚ ᱴᱨᱟᱠ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱵᱟᱝ ᱵᱟᱰᱟᱭ ᱧᱟᱢ ᱟᱠᱟᱱ ᱥᱤᱠᱷᱱᱟᱹᱛ + ᱵᱟᱝ ᱵᱟᱰᱟᱭ + ᱡᱚᱛᱚ ᱛᱮ ᱧᱮᱞᱚᱜᱼᱟ + ExoPlayer ᱨᱮᱭᱟᱜ ᱵᱷᱤᱰᱤᱭᱳ ᱟᱯᱩᱴ ᱥᱩᱨᱥᱩᱯᱩᱨ ᱥᱤᱴᱤᱝ ᱣᱟᱨᱠᱟᱨᱚᱸᱜ ᱵᱮᱵᱷᱟᱨ ᱢᱮ + ᱵᱷᱤᱰᱤᱭᱳ + ᱴᱨᱮᱠ + ᱥᱟᱨᱴᱥ + ᱡᱤᱣᱤ + ᱪᱟᱱᱮᱞ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱠᱚ + ᱟᱞᱵᱚᱢ + ᱤᱫᱤ ᱠᱟᱛᱮ + ᱪᱟᱱᱮᱞ ᱛᱟᱵᱽ + ᱥᱠᱨᱤᱱ ᱴᱳᱜᱞ ᱳᱭᱨᱮᱱᱟᱥᱚᱱ + ᱢᱟᱲᱟᱝ ᱥᱴᱨᱤᱢ + ᱯᱷᱚᱨᱣᱟᱨᱰ + ᱪᱤᱛᱟᱹᱨ ᱥᱤᱠᱷᱱᱟᱹᱛ + ᱪᱤᱛᱟᱹᱨ ᱵᱟᱝ ᱞᱚᱰ ᱢᱮ + \? + ᱥᱮᱨᱮᱧ ᱛᱟᱹᱞᱠᱟᱹ ᱮᱢ + ᱧᱩᱛᱩᱢ ᱥᱟᱶ ᱮᱱᱮᱢ ᱢᱮ + %1$s +\n%2$s + ᱟᱨᱦᱚᱸ ᱧᱮᱞ ᱢᱮ + ᱠᱚᱢ ᱧᱮᱞ ᱢᱮ + ᱛᱟᱹᱞᱠᱟᱹ ᱪᱟᱪᱞᱟᱣ ᱞᱟᱹᱜᱤᱫ ᱯᱟᱹᱛᱭᱟᱹᱣ ᱮᱢ ᱢᱮ + ᱢᱤᱫ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟ. ᱠᱷᱚᱱ ᱮᱴᱟᱜ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟ. ᱛᱮ ᱵᱚᱫᱚᱞ ᱠᱟᱛᱮᱜ ᱟᱢᱟᱜ ᱠᱩᱠᱞᱤ ᱵᱚᱫᱚᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ + ᱮᱠᱴᱤᱵᱽ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱠᱩᱠᱞᱤ ᱫᱚ ᱵᱚᱫᱚᱞᱚᱜ-ᱟ + ᱞᱟᱹᱱᱟᱹᱤ ᱠᱚ ᱧᱮᱞ ᱢᱮ + ᱰᱤᱯᱷᱚᱞᱴ ᱠᱚ ᱨᱤᱯᱚᱴ ᱢᱮ + ᱯᱞᱮᱵᱮᱠ ᱞᱚᱰ ᱤᱱᱴᱟᱨᱵᱷᱮᱞ ᱨᱮᱱᱟᱜ ᱢᱟᱨᱟᱝ + ᱞᱚᱠᱟᱞ + ᱮᱠᱟᱩᱱᱴ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱵᱟᱰᱟᱭ ᱢᱮ + ᱦᱟᱨᱰᱣᱮᱭᱟᱨ ᱢᱤᱰᱤᱭᱟ ᱵᱩᱴᱚᱱ ᱦᱮᱯᱨᱟᱣ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + System folder picker (SAF) ᱵᱮᱵᱷᱟᱨ ᱢᱮ + ᱰᱤᱯᱷᱚᱞᱴ ᱵᱷᱤᱰᱤᱭᱳ ᱯᱷᱚᱨᱢᱮᱴ + ᱯᱚᱯ-ᱟᱯ ᱨᱮᱱᱟᱜ ᱢᱩᱪᱟᱹᱫ ᱢᱟᱨᱟᱝ ᱟᱨ ᱴᱷᱟᱶ ᱫᱚᱦᱚᱭ ᱢᱮ + ᱦᱩᱰᱤᱧ ᱵᱟᱹᱱᱩᱜ-ᱟ ᱞᱟᱦᱟᱱᱛᱤ ᱵᱮᱵᱷᱟᱨ + ᱵᱟᱝ ᱥᱟᱹᱨᱤ ᱧᱮᱞ ᱠᱷᱟᱱ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱫᱚ ᱵᱟᱹᱲᱛᱤ ᱥᱟᱹᱠᱷᱭᱟᱹᱛ ᱥᱟᱶ ᱥᱟᱹᱠᱷᱭᱟᱹᱛ ᱠᱚ ᱵᱟᱰᱟᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱾ ᱕, ᱑᱕ ᱟᱨ ᱒᱕ ᱥᱮᱠᱮᱱᱰ ᱞᱟᱹᱜᱤᱫ ᱧᱮᱞᱚᱜᱼᱟ ᱱᱚᱶᱟ ᱥᱟᱶ ᱵᱟᱝ ᱠᱟᱹᱢᱤᱭᱟ + ᱮᱱᱮᱢ ᱠᱚ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱫ ᱱᱚᱶᱟ ᱵᱚᱫᱚᱞ ᱢᱮ + \'Next\' ᱟᱨ \'Similar\' ᱵᱷᱤᱰᱤᱭᱳ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱢᱮᱴᱟ ᱴᱷᱟᱶ ᱧᱮᱞ ᱢᱮ + ᱥᱴᱨᱤᱢ ᱵᱮᱱᱟᱣᱤᱭᱟᱹ, ᱥᱴᱨᱤᱢ ᱟᱹᱛᱩ ᱟᱨᱵᱟᱝ ᱥᱟᱨᱪ ᱞᱟᱹᱠᱛᱤ ᱵᱟᱵᱚᱛ ᱟᱨᱦᱚᱸ ᱵᱟᱰᱟᱭ ᱞᱟᱹᱜᱤᱫ ᱢᱮᱴᱟ ᱤᱱᱯᱷᱳ ᱵᱚᱠᱥ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱱᱮᱛᱟᱨᱟᱜ ᱥᱴᱨᱤᱢ ᱨᱮ ᱚᱴᱚᱼᱠᱤᱣ + ᱡᱚᱲᱟᱣ ᱟᱠᱟᱱ ᱥᱴᱨᱤᱢ ᱥᱮᱞᱮᱫ ᱠᱟᱛᱮ ᱪᱟᱪᱞᱟᱣ ᱪᱮᱛᱟᱱ (ᱱᱚᱱ-ᱨᱩᱯᱨᱤᱭᱩᱴᱤᱝ) ᱯᱞᱮᱼᱵᱷᱤᱠ ᱪᱮᱛᱟᱱ ᱪᱟᱪᱞᱟᱣ ᱢᱮ + ᱯᱟᱹᱨᱥᱤ ᱵᱟᱝ ᱧᱮᱞ ᱠᱟᱛᱮᱜ ᱚᱨᱡᱤᱱᱤᱭᱟᱞ ᱚᱰᱤᱭᱚ ᱴᱨᱟᱠ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱰᱤᱥᱠᱨᱤᱯᱴᱤᱵᱷ ᱚᱰᱤᱭᱚ ᱵᱟᱹᱲᱛᱤ + ᱚᱴᱚ-ᱮᱱᱠᱭᱩᱤᱝ + ᱡᱩᱫᱤ ᱢᱮᱱᱟᱜ ᱟ ᱡᱩᱫᱤ ᱧᱮᱞ ᱵᱟᱹᱲᱤᱡ ᱦᱚᱲ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱢᱤᱫ ᱚᱰᱤᱭᱚ ᱴᱨᱟᱠ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱢᱮᱴᱟᱰᱮᱴᱟ ᱠᱮᱪ ᱵᱚᱫᱚᱞ + ᱞᱤᱣᱮᱴ ᱡᱮᱥᱴ ᱮᱠᱥᱚᱱ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱥᱠᱨᱤᱱ ᱨᱮᱱᱟᱜ ᱪᱮᱛᱟᱱ ᱨᱮ ᱪᱤᱱᱦᱟᱹ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱞᱟᱹᱭ ᱪᱤᱠᱤ ᱠᱟᱹᱢᱤ + ᱢᱤᱱᱤ ᱯᱞᱮᱭᱟᱨ ᱨᱮ ᱵᱷᱤᱰᱤᱭᱳ ᱠᱚ ᱮᱛᱚᱦᱚᱵ ᱵᱟᱝ ᱢᱮ, ᱡᱩᱫᱤ ᱚᱴᱚᱯᱷᱤ ᱨᱚᱴᱮᱴ ᱞᱚᱠ ᱟᱠᱟᱱᱟ ᱮᱱᱠᱷᱟᱱ ᱥᱟᱹᱛ ᱥᱠᱨᱤᱱ ᱨᱮ ᱵᱚᱫᱚᱞ ᱢᱮ ᱾ ᱟᱢ ᱢᱤᱱᱤ ᱯᱞᱮᱭᱟᱨ ᱪᱷᱟᱰᱟ ᱠᱟᱛᱮᱜ ᱦᱚᱸ ᱥᱮᱞᱮᱫ ᱫᱟᱲᱮᱭᱟᱜ ᱟ + ᱚᱴᱚᱯᱷᱟᱭ + ᱵᱟᱝ ᱥᱚᱦᱚᱫ ᱟᱠᱟᱱ URL + URL ᱵᱟᱭ ᱧᱟᱢ ᱫᱟᱲᱮᱭᱟᱫᱼᱟ. ᱮᱴᱟᱜ ᱟᱯᱞᱤᱠᱮᱥᱚᱱ ᱛᱮ ᱮᱛᱚᱦᱚᱵ ᱢᱮ? + ᱡᱤᱞᱤᱧ + ᱛᱚᱨᱡᱚᱢᱟ ᱥᱟᱯᱲᱟᱣ + ᱧᱮᱞ ᱛᱟᱵᱩᱱ ᱯᱮ ᱺ + ᱨᱮᱢᱳᱴ ᱥᱟᱨᱪ ᱥᱩᱯᱨᱮᱥ + ᱧᱮᱞ ᱟᱠᱟᱱ ᱵᱷᱤᱰᱤᱭᱳ ᱠᱚ ᱪᱮᱛᱟᱱ ᱨᱮ ᱫᱚᱦᱚᱭ ᱢᱮ + ᱡᱟᱦᱟᱱᱟᱜ ᱵᱚᱫᱚᱞ ᱛᱟᱭᱚᱢ ᱠᱷᱮᱞ ᱫᱚᱦᱚᱭ ᱢᱮ (ᱡᱮᱞᱮᱠᱟ: ᱯᱷᱚᱱ ᱠᱚᱞ) + ᱰᱟᱩᱱᱞᱚᱰ ᱢᱮ + ᱢᱩᱬᱩᱛ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱡᱚᱛᱚ ᱪᱤᱛᱟᱹᱨ ᱨᱮ ᱮᱛᱚᱦᱚᱵ ᱢᱮ + ᱰᱤᱯᱷᱚᱞᱴ ᱠᱚᱱᱴᱮᱱᱴ ᱯᱟᱹᱨᱥᱤ + PeerTube ᱚᱞ ᱠᱚ + ᱟᱢᱟᱜ ᱧᱩᱛᱩᱢᱟᱱ PeerTube ᱵᱷᱤᱛᱨᱤ ᱠᱚ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱞᱟᱹᱱᱟᱹᱤ ᱫᱚᱦᱚ ᱫᱟᱲᱮᱭᱟᱜ ᱵᱟᱝ + ᱵᱷᱤᱰᱤᱭᱳ ᱟᱨ ᱚᱰᱤᱭᱳ + HTTPS URL ᱠᱚ ᱜᱮ ᱥᱚᱯᱷᱴᱣᱮᱨ ᱟᱠᱟᱱᱟ + ᱤᱱᱥᱴᱮᱱᱥ ᱫᱚ ᱟᱞᱮ ᱢᱮᱱᱟᱜᱼᱟ + ᱟᱹᱱᱟᱹᱨᱤ + ᱚᱛᱱᱚᱜ ᱪᱤᱛᱟᱹᱨ ᱠᱷᱚᱸᱫᱽᱨᱚᱸᱫᱽ ᱥᱟᱯᱲᱟᱣ + ᱜᱤᱫᱨᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱝ ᱞᱟᱹᱠᱛᱤᱭᱟᱱ ᱠᱟᱛᱷᱟ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ ᱪᱮᱫᱟᱜ ᱥᱮ ᱱᱚᱣᱟ ᱨᱮ ᱢᱤᱫ ᱩᱢᱮᱨ ᱞᱤᱢᱤᱴ ᱢᱮᱱᱟᱜᱼᱟ (ᱡᱮᱞᱮᱠᱟ ᱑᱘+) + ᱭᱩᱴᱭᱩᱵᱽ ᱢᱤᱫ \"Restricted Mode\" ᱮᱢᱚᱜᱼᱟ ᱡᱟᱦᱟᱸ ᱫᱚ ᱯᱚᱴᱮᱱᱥᱤᱭᱟᱞ ᱢᱚᱰᱩᱞ ᱠᱚᱱᱴᱮᱱᱴ (mature content) ᱫᱚᱦᱚᱭᱟ + ᱱᱤᱭᱟᱹ ᱵᱷᱤᱰᱤᱭᱳ ᱫᱚ ᱩᱢᱚᱨ ᱞᱟᱹᱜᱤᱫ ᱜᱮ᱾_x000D_ +\nᱭᱩᱴᱭᱩᱵᱽ ᱨᱮ ᱵᱷᱤᱰᱤᱭᱳ ᱨᱮᱱᱟᱜ ᱩᱢᱮᱨ ᱫᱚᱦᱚ ᱞᱟᱹᱜᱤᱫ ᱱᱟᱣᱟ ᱯᱳᱞᱤᱥᱤ ᱠᱷᱟᱹᱛᱤᱨ, ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯᱷ ᱫᱚ ᱟᱡᱟᱜ ᱵᱷᱤᱰᱤᱭᱳ ᱥᱴᱨᱤᱢ ᱨᱮᱱᱟᱜ ᱡᱟᱦᱟᱸᱭ ᱦᱚᱸ ᱥᱮᱴᱮᱨ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱵᱟᱝ ᱟᱨ ᱚᱱᱟᱛᱮ ᱱᱚᱶᱟ ᱯᱞᱮᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ᱾ + ᱡᱤᱣᱤ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱠᱚ + ᱵᱷᱤᱰᱤᱭᱳ + ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ + ᱜᱷᱚᱴᱚᱱ ᱠᱚ + ᱮᱯ ᱟᱯᱰᱮᱴ ᱱᱳᱴᱤᱯᱮᱥᱚᱱ + ᱱᱟᱣᱟ ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯ ᱵᱟᱨᱥᱚᱱ ᱞᱟᱹᱜᱤᱫ ᱠᱷᱚᱵᱚᱨ + ᱪᱷᱟᱸᱪ ᱠᱚ + ᱵᱷᱤᱰᱤᱭᱳ ᱦᱟᱥ ᱱᱮᱴᱤᱯᱷᱤᱠᱮᱥᱚᱱ + ᱪᱟᱹᱠᱨᱤ ᱠᱷᱚᱵᱚᱨ ᱠᱷᱚᱵᱚᱨ + ᱯᱷᱚᱱ ᱨᱮ ᱥᱮᱞᱮᱫᱽ ᱢᱮ + ᱯᱚᱯ-ᱟᱯ ᱨᱮ ᱥᱮᱞᱮᱫ ᱢᱮ + ᱢᱩᱬᱩᱛ ᱨᱮ ᱞᱤᱱ ᱢᱮ + ᱮᱠᱥᱯᱳᱨᱴ ᱰᱮᱴᱟᱵᱮᱥ + ᱟᱢᱟᱜ ᱱᱮᱛᱟᱨᱟᱜ ᱱᱟᱜᱟᱢ, ᱥᱟᱵᱽᱥᱠᱨᱟᱭᱵᱽᱥ, ᱯᱷᱟᱭᱞᱤᱥᱴ ᱟᱨ (ᱵᱟᱪᱷᱱᱟᱣ ᱞᱮᱠᱟᱛᱮ) ᱥᱤᱴᱤᱝ ᱠᱚ ᱩᱪᱟᱹᱲᱟ + ᱟᱢ ᱢᱤᱫ reCAPTCHA ᱥᱟᱯᱲᱟᱣ ᱞᱮᱠᱷᱟᱱ NewPipe ᱥᱟᱦᱴᱟᱨᱮ ᱥᱚᱫᱚᱨ ᱟᱠᱟᱱ ᱠᱩᱠᱤ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱯᱷᱟᱭᱞᱟᱣ ᱴᱷᱟᱶ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱡᱚᱛᱚ ᱯᱞᱮᱤᱯᱷᱟᱭᱤᱰ ᱯᱚᱡᱤᱥᱚᱱ ᱠᱚ ᱵᱚᱫᱚᱞᱟ + ᱡᱚᱛᱚ ᱯᱞᱮᱵᱟᱠ ᱯᱳᱡᱤᱥᱚᱱ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ? + ᱠᱷᱮᱞᱚᱸᱰ ᱨᱮᱱᱟᱜ ᱴᱷᱟᱶ ᱠᱚ ᱪᱷᱩᱴᱟᱹᱣ ᱟᱠᱟᱱᱟ + ᱥᱩᱪᱚᱱᱟ ᱨᱮᱭᱟᱜ ᱱᱟᱜᱟᱢ ᱪᱷᱟᱯᱟ ᱢᱮ + ᱪᱷᱟᱸᱪ:Search keywords ᱨᱮᱱᱟᱜ ᱱᱟᱜᱟᱢ ᱪᱷᱟᱸᱪᱟᱣᱟ + ᱥᱟᱱᱟᱢ ᱥᱩᱪᱚᱱᱟ ᱱᱟᱜᱟᱢ ᱵᱚᱫᱚᱞ ᱢᱮ? + ᱰᱟᱩᱱᱞᱚᱰ ᱢᱮᱱᱩ ᱵᱟᱭ ᱥᱮᱯᱴᱮᱭᱟ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ + ᱮᱯᱞᱤᱠᱮᱥᱚᱱ/UI ᱠᱨᱟᱥ ᱞᱮᱱᱟ + ᱱᱚᱣᱟ ᱥᱴᱨᱤᱢ ᱯᱞᱮ ᱵᱟᱭ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱵᱟᱭ ᱵᱩᱡᱷᱟᱹᱣ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ + ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱦᱩᱫᱤᱥ ᱠᱷᱚᱱ ᱨᱤᱠᱳᱨᱰ + ᱱᱚᱶᱟ ᱯᱷᱤᱞ ᱵᱟᱹᱱᱩᱜᱼᱟ ᱥᱮ ᱱᱚᱶᱟ ᱨᱮ ᱚᱞ ᱟᱨ ᱯᱟᱲᱦᱟᱣ ᱞᱟᱹᱜᱤᱫ ᱫᱟᱣ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱯᱷᱤᱞᱤᱢ ᱧᱩᱛᱩᱢ ᱵᱚᱸᱫᱚ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱵᱟᱝ + %1$s ᱢᱤᱫ ᱵᱷᱩᱞ ᱦᱩᱭ ᱞᱮᱱᱟ: + ᱰᱟᱩᱱᱞᱚᱰ ᱞᱟᱹᱜᱤᱫ ᱡᱟᱦᱟᱱ ᱥᱴᱨᱤᱢ ᱵᱟᱭ + ᱥᱚᱞᱦᱮ ᱟᱠᱟᱱ ᱴᱮᱵᱽ ᱠᱚ ᱵᱟᱝ ᱯᱟᱲᱦᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ, ᱚᱱᱟᱛᱮ ᱵᱮᱵᱷᱟᱨᱤᱡᱽ ᱠᱚ + ᱟᱢ ᱥᱩᱯᱚᱨᱴ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮᱢᱮ? + ᱮᱴᱟᱜ ᱮᱯ ᱨᱮ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱫ ᱫᱟᱣ ᱮᱢ ᱢᱮ + NewPipe ᱨᱮ ᱢᱤᱫᱴᱟᱹᱝ ᱵᱷᱩᱞ ᱧᱟᱢ ᱞᱮᱱᱟ, ᱠᱷᱚᱵᱚᱨ ᱞᱟᱹᱜᱤᱫ ᱴᱟᱯ ᱢᱮ + ᱤᱧᱤᱧ ᱵᱟᱹᱧ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ, ᱱᱚᱶᱟ ᱫᱚ ᱵᱟᱝ ᱦᱩᱭᱩᱜ ᱞᱟᱹᱠᱛᱤ ᱛᱟᱦᱮᱸᱫ ᱾ + ᱤ-ᱢᱮᱞ ᱤᱫᱤ ᱠᱟᱛᱮ ᱨᱤᱯᱚᱨᱴ ᱢᱮ + ᱯᱷᱚᱨᱢᱮᱴ ᱟᱠᱟᱱ ᱨᱤᱯᱳᱨᱴ ᱠᱚᱯᱤ ᱢᱮ + ᱟᱢᱟᱜ ᱠᱨᱮᱥᱴ ᱵᱟᱵᱚᱫᱽ ᱛᱮ ᱢᱤᱫ ᱚᱱᱚᱞ ᱢᱮᱱᱟᱜ ᱟ ᱥᱮ ᱵᱟᱝ ᱚᱱᱟ ᱧᱮᱞ ᱢᱮ ᱾ ᱰᱩᱯᱞᱤᱠᱮᱴ ᱴᱤᱠᱮᱴ ᱵᱮᱱᱟᱣ ᱞᱮᱠᱷᱟᱱ, ᱟᱢ ᱫᱚ ᱚᱠᱛᱚ ᱟᱢᱮᱢ ᱦᱟᱛᱟᱣᱮᱫᱟ ᱡᱟᱦᱟᱸ ᱫᱚ ᱟᱥᱚᱞ ᱵᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱨᱮ ᱟᱢᱮᱢ ᱦᱟᱛᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱾ + ᱤᱱᱯᱷᱳ: + ᱪᱮᱫ ᱦᱩᱭ ᱞᱮᱱᱟ: + ᱞᱟᱹᱱᱟᱹᱤ: + ᱵᱷᱤᱰᱤᱭᱚ ᱯᱞᱮ, ᱚᱠᱛᱚ: + ᱵᱟᱹᱲᱤᱡ + ᱠᱚᱢᱮᱱᱴ + ᱥᱟᱶᱦᱮᱫ ᱮᱞᱠᱷᱟ + ᱞᱟᱹᱱᱟᱹᱤ + ᱓ ᱰᱳᱴ ᱢᱮᱱᱩ ᱠᱷᱚᱱ ᱤᱢᱯᱳᱨᱴ ᱟᱨ ᱮᱠᱥᱯᱳᱨᱴ ᱥᱟᱵᱥᱠᱨᱟᱭᱵᱮᱥᱚᱱ + ᱱᱟᱣᱟ ᱟᱹᱨᱡᱤ ᱞᱟᱹᱜᱤᱫ ᱟᱹᱪᱩᱨ ᱢᱮ + ᱚᱰᱤᱭᱳ + ᱟᱨᱦᱚᱸ ᱯᱟᱲᱦᱟᱣ ᱢᱮ + ᱵᱤ + ᱱᱤᱛᱚᱜ ᱵᱟᱪᱷᱟᱣ ᱟᱠᱟᱱ ᱴᱳᱜᱞ ᱥᱮᱵᱟ: + ᱚᱵᱷᱤᱱᱮᱛᱟᱨ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱚᱠᱚᱭ ᱦᱚᱸ ᱵᱟᱝ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ + ᱜᱚᱴᱟ ᱟᱠᱟᱱᱟ + ᱱᱤᱭᱩ ᱯᱟᱭᱯᱮ ᱵᱟᱵᱚᱛ + ᱯᱮᱨᱟᱜ ᱞᱟᱭᱥᱮᱱᱥ + ᱰᱳᱱᱮᱴ ᱢᱮ + ᱡᱟᱦᱟᱸᱭ ᱜᱮᱭ ᱟᱸᱡᱚᱢᱟ + ∞ ᱵᱷᱤᱰᱤᱭᱳ + ᱵᱟᱦᱟ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱠᱚᱢᱮᱱᱴ ᱫᱚ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱮᱛᱚᱦᱚᱵ + ᱯᱷᱟᱭᱩᱥ + ᱵᱮᱱᱟᱣ ᱢᱮ + ᱛᱷᱚᱠ ᱨᱮ ᱛᱟᱭᱚᱢ ᱛᱮ ᱢᱤᱫ ᱰᱟᱩᱱᱞᱚᱰ ᱯᱷᱳᱞᱰᱟᱨ ᱮᱢ ᱢᱮ + ᱢᱤᱫᱴᱟᱹᱝ ᱡᱤᱱᱤᱥ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + reCAPTCHA challenge ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ + ᱥᱚᱞᱦᱮ + ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯ ᱫᱚ ᱱᱤᱡᱮᱨᱟᱜ ᱚᱠᱛᱚ ᱟᱢᱟᱜ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱵᱷᱟᱹᱜᱤ ᱵᱷᱟᱹᱜᱤ ᱥᱟᱹᱜᱟᱹᱭ ᱮᱢ ᱠᱟᱛᱮ ᱵᱮᱱᱟᱣ ᱟᱠᱟᱱᱟ ᱾ ᱱᱤᱣ ᱯᱟᱭᱤᱯ ᱵᱮᱨᱮᱫ ᱞᱟᱹᱜᱤᱫ ᱮᱱᱮᱢᱤᱭᱟᱹ ᱠᱚ ᱜᱚᱲᱚ ᱥᱚᱦᱚᱫ ᱮᱢ ᱢᱮ ᱾ + ᱣᱮᱵᱽᱥᱟᱭᱤᱴ + ᱱᱤᱣ ᱯᱟᱭᱯ ᱨᱮᱭᱟᱜ ᱞᱟᱭᱥᱮᱱᱥ + ᱱᱤᱭᱩ ᱯᱟᱭᱯ ᱫᱚ ᱠᱚᱯᱤᱞᱮᱠᱴ ᱞᱤᱵᱨᱮ ᱥᱚᱯᱷᱴᱣᱮᱭᱟᱨ ᱠᱟᱱᱟ ᱾ ᱟᱢ ᱱᱚᱶᱟ ᱵᱮᱵᱷᱟᱨ, ᱯᱟᱲᱦᱟᱣ, ᱮᱴᱟᱜ ᱦᱚᱲ ᱥᱟᱶ ᱮᱢ ᱟᱨ ᱵᱮᱵᱚᱥᱛᱟ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ ᱟᱢ ᱱᱚᱶᱟ GNU General Public License ᱞᱮᱠᱟᱛᱮ ᱯᱟᱨᱥᱟᱞ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱟᱨ/ᱟᱨᱵᱟᱝ Free Software Foundation ᱦᱚᱛᱮᱛᱮ ᱵᱮᱵᱦᱟᱨ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ, ᱡᱟᱦᱟᱸ ᱫᱚ ᱞᱟᱭᱥᱮᱱᱥ ᱨᱮᱱᱟᱜ ᱵᱟᱨᱥᱚᱱ ᱓ ᱥᱮ (ᱛᱟᱢᱟᱜ ᱵᱟᱪᱷᱟᱣ ᱞᱮᱠᱟᱛᱮ) ᱡᱟᱦᱟᱸᱱᱟᱜ ᱛᱟᱭᱚᱢ ᱵᱟᱨᱥᱚᱱ ᱠᱟᱱᱟ ᱾ + ᱟᱨᱦᱚᱸ ᱵᱟᱰᱟᱭ ᱟᱨ ᱠᱷᱚᱵᱚᱨ ᱞᱟᱹᱜᱤᱫ NewPipe ᱣᱮᱵᱽᱥᱟᱭᱤᱴ ᱧᱮᱞ ᱢᱮ ᱾ + ᱡᱩᱫᱤ ᱟᱢ ᱱᱚᱶᱟ ᱮᱯᱞᱤᱠᱮᱥᱚᱱ ᱵᱮᱵᱚᱦᱟᱨ ᱨᱮ ᱠᱷᱟᱡᱽᱱᱟ ᱮ ᱧᱟᱢᱟ, ᱮᱱᱠᱷᱟᱱ ᱱᱚᱶᱟ ᱥᱟᱸᱣ ᱡᱩᱲᱟᱹᱣ ᱟᱠᱟᱱ ᱯᱩᱥᱴᱟᱹ ᱠᱚ ᱧᱮᱞ ᱢᱮ! + ᱣᱮᱵᱽᱥᱟᱭᱤᱴ ᱨᱮ ᱧᱮᱞ ᱢᱮ + ᱱᱟᱜᱟᱢ + ᱱᱟᱜᱟᱢ + ᱟᱢ ᱱᱚᱶᱟ ᱡᱤᱱᱤᱥ ᱥᱟᱸᱪᱟᱨ ᱱᱟᱜᱟᱢ ᱠᱷᱚᱱ ᱵᱚᱫᱚᱞ ᱢᱮᱢᱮ? + ᱢᱩᱪᱟᱹᱫ ᱠᱷᱮᱞ ᱟᱠᱟᱱᱟ + ᱡᱟᱹᱥᱛᱤ ᱠᱷᱮᱞ ᱟᱠᱟᱱ + ᱢᱩᱬᱩᱛ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱥᱟᱦᱴᱟ + ᱢᱩᱬᱩᱛ ᱥᱟᱦᱴᱟ ᱨᱮ ᱚᱠᱟ ᱛᱟᱵᱽ ᱠᱚ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱡᱤᱱᱤᱥ ᱠᱚ ᱵᱟᱧᱪᱟᱣ ᱞᱟᱹᱜᱤᱫ ᱥᱣᱟᱭᱯ ᱢᱮ + ᱰᱤᱯᱷᱚᱞᱴ ᱠᱤᱭᱳᱥᱠ + ᱠᱤᱭᱳᱥᱠ ᱥᱟᱦᱴᱟ + ᱪᱟᱱᱮᱞ ᱥᱟᱦᱴᱟ + ᱪᱟᱱᱮᱞ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱱᱤᱛ ᱦᱟᱹᱵᱤᱡ ᱪᱮᱱᱮᱞ ᱥᱚᱵᱽᱥᱠᱨᱟᱭᱵᱽ ᱵᱟᱝ + ᱢᱤᱫ ᱯᱷᱟᱤᱞᱤᱥᱴ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱤᱢᱯᱳᱨᱴ + ᱱᱤᱛ ᱦᱟᱹᱵᱤᱡ playlist bookmarks ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱮᱯ ᱮᱦᱚᱵ ᱞᱮᱠᱷᱟᱱ ᱯᱟᱹᱨᱥᱤ ᱵᱚᱫᱚᱞᱚᱜ-ᱟ + ᱮᱥᱯᱟᱨᱴ ᱟᱠᱟᱱᱟ + ᱴᱨᱮᱱᱰᱤᱝ + ᱚᱰᱤᱭᱳ ᱥᱮᱴᱤᱝ + ᱚᱰᱤᱭᱳ: %s + ᱥᱴᱨᱤᱢ ᱨᱮᱱᱟᱜ ᱰᱮᱴᱮᱞ ᱞᱚᱰ ᱠᱟᱱᱟ… + ᱥᱩᱯᱚᱨ ᱰᱨᱟᱵᱷᱤᱴ + ᱱᱟᱶᱟ ᱯᱞᱮᱭᱞᱤᱥᱴ + ᱢᱩᱴ + ᱢᱤᱫ ᱯᱚᱯ-ᱟᱯ ᱨᱮ ᱠᱷᱮᱞᱚᱸᱰ ᱮᱛᱚᱦᱚᱵ ᱢᱮ + ᱯᱷᱟᱤᱞᱤᱯᱷᱟᱤᱞᱤ ᱞᱮᱠᱟᱛᱮ ᱥᱮᱞᱮᱫ ᱢᱮ + ᱵᱩᱠᱢᱟᱨᱠ ᱯᱞᱮᱭᱞᱤᱥᱴ + ᱪᱷᱟᱸᱪ + ᱡᱤᱵᱚᱱ ᱪᱟᱞᱟᱣ ᱛᱟᱭᱚᱢ ᱡᱟᱹᱛᱤ ᱥᱮ ᱠᱟᱹᱢᱤ ᱡᱤᱭᱚᱱ ᱪᱮᱛᱟᱱ ᱵᱟᱦᱨᱮ ᱨᱮ ᱵᱟᱝ ᱦᱟᱹᱴᱤᱧ ᱫᱟᱲᱮᱭᱟᱜ Rx ᱮᱥᱠᱮᱪᱥᱚᱱ ᱨᱮᱱᱟᱜ ᱯᱷᱚᱨᱥ ᱨᱮᱯᱚᱨᱴᱤᱝ + ᱵᱩᱠᱢᱟᱨᱠ ᱚᱪᱚᱭ ᱢᱮ + ᱱᱚᱶᱟ ᱯᱷᱟᱭᱞᱤᱥᱴ ᱵᱚᱫᱚᱞ ᱢᱮ? + ᱚᱴᱚ-ᱡᱮᱱᱮᱨᱮᱴ + ᱡᱤᱱᱤᱥ ᱠᱚᱨᱮᱱᱟᱜ ᱢᱩᱞ ᱚᱠᱛᱚ ᱧᱮᱞ ᱢᱮ + ᱥᱮᱵᱟ ᱠᱷᱚᱱ ᱚᱨᱡᱤᱱᱤᱭᱟᱞ ᱴᱮᱠᱥᱴ ᱠᱚ ᱥᱴᱨᱤᱢ ᱤᱴᱮᱢ ᱨᱮ ᱧᱮᱞᱚᱜᱼᱟ + ᱡᱩᱫᱤ ᱟᱢ ᱵᱷᱤᱰᱤᱭᱳ ᱯᱞᱮᱭᱚᱯ ᱨᱮ ᱵᱞᱮᱠ ᱥᱠᱨᱤᱱ ᱟᱨᱵᱟᱝ ᱠᱷᱟᱹᱞᱤ ᱥᱴᱮᱴᱞᱤᱝ ᱮᱢ ᱧᱟᱢᱟ ᱮᱱᱠᱷᱟᱱ ᱢᱤᱰᱤᱭᱟ ᱴᱩᱱᱮᱞᱤᱝ ᱵᱚᱫᱚᱞ ᱢᱮ ᱾ + ᱪᱤᱛᱟᱹᱨ ᱪᱤᱱᱦᱟᱹ ᱠᱚ ᱧᱮᱞ ᱢᱮ + ᱱᱟᱣᱟ ᱥᱴᱨᱤᱢ ᱞᱟᱹᱜᱤᱫ ᱪᱟᱪᱞᱟᱣ ᱢᱮ + ᱢᱤᱫ error notification ᱛᱮᱭᱟᱨ ᱢᱮ + ᱥᱮᱞᱮᱫ ᱮᱠᱥᱯᱳᱨᱴ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ + \"ᱜᱩᱜᱩᱞ ᱴᱮᱠᱟᱣᱩᱴ ᱠᱷᱚᱱ ᱭᱩᱴᱭᱩᱵᱽ ᱥᱚᱵᱽᱥᱠᱨᱟᱭᱵᱽᱥ ᱤᱱᱯᱳᱨᱴ ᱢᱮ: +\n +\n1. ᱱᱚᱶᱟ URL ᱨᱮ ᱪᱟᱞᱟᱜ ᱢᱮ: %1$s +\n2. ᱞᱚᱜᱤᱱ ᱢᱮ ᱡᱚᱠᱷᱚᱱ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ +\n3. \"\'All data included\"\' ᱨᱮ ᱞᱤᱱ ᱢᱮ, ᱛᱟᱭᱚᱢ \"\'Deselect all\"\' ᱨᱮ ᱞᱤᱱ ᱢᱮ, ᱛᱟᱭᱚᱢ \"\'subscriptions\"\' ᱨᱮ ᱞᱤᱱ ᱢᱮ ᱟᱨ \"\'OK\"\' ᱨᱮ ᱞᱤᱱ ᱢᱮ +\n4. \"Next step\" ᱨᱮ ᱟᱨ ᱚᱱᱟ ᱛᱟᱭᱚᱢ \"Create export\" ᱨᱮ ᱞᱤᱱ ᱢᱮ +\n5. ᱱᱚᱶᱟ ᱧᱮᱞ ᱛᱟᱭᱚᱢ \"\"Download\"\" ᱵᱩᱴᱚᱱ ᱨᱮ ᱠᱞᱤᱠ ᱢᱮ +\n6. ᱞᱟᱛᱟᱨ ᱨᱮ IMPORT FILE ᱨᱮ ᱞᱤᱱ ᱢᱮ ᱟᱨ ᱰᱟᱩᱱᱞᱚᱰ ᱟᱠᱟᱱ . zip ᱯᱷᱤᱞ ᱵᱟᱪᱷᱟᱣ ᱢᱮ +\n7. ᱡᱩᱫᱤ .zip ᱤᱱᱯᱷᱚᱨᱴ ᱞᱟᱹᱠᱛᱤᱭᱟᱱ ᱵᱟᱝ ᱠᱟᱱᱟ ᱮᱱᱠᱷᱟᱱ .csv ᱯᱷᱤᱞ ᱮᱠᱥᱴᱨᱟᱠᱴ ᱢᱮ (\"ᱭᱩᱴᱭᱩᱵᱽ ᱟᱨ ᱭᱩᱴᱭᱩᱵᱽ ᱢᱤᱣᱡᱤᱠ/ᱥᱵᱽᱥᱠᱨᱟᱭᱵᱮᱥᱚᱱ/ᱥᱵᱽᱥᱠᱨᱟᱭᱵᱮᱥᱚᱱ.ᱥᱤᱵᱷ\" ᱨᱮ), ᱞᱟᱛᱟᱨ ᱨᱮ IMPORT FILE ᱨᱮ ᱠᱞᱤᱠ ᱢᱮ ᱟᱨ ᱮᱠᱥᱴᱨᱟᱠᱴ ᱟᱠᱟᱱ csv ᱯᱷᱤᱞ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + yourID, soundcloud.com/yourid + \"ᱤᱭᱩᱨᱚᱯᱤᱭᱚ ᱡᱚᱡᱚᱱᱟ ᱯᱨᱚᱫᱚᱭᱚᱜᱤᱠᱤ (ᱡᱤ.ᱰᱤ.ᱯᱤ.ᱟᱨ.) ᱥᱟᱞᱟᱜ ᱥᱚᱢᱵᱚᱸᱫᱷ ᱞᱟᱹᱜᱤᱫ ᱛᱮ, ᱱᱚᱣᱟ ᱦᱚᱛᱮᱛᱮ ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯ ᱨᱮᱭᱟᱜ ᱯᱨᱟᱭᱵᱷᱮᱴ ᱯᱚᱞᱤᱥᱤ ᱨᱮ ᱟᱵᱚᱣᱟᱜ ᱟᱫᱷᱤᱱᱤᱭᱚᱢ ᱠᱚ ᱩᱫᱩᱜ ᱦᱚᱪᱚᱭᱟ। ᱱᱚᱶᱟ ᱫᱚ ᱞᱟᱹᱭᱛᱮ ᱯᱟᱲᱦᱟᱣ ᱢᱮ ᱾ +\nᱟᱢ ᱫᱚ ᱱᱚᱶᱟ ᱯᱟᱹᱛᱭᱟᱹᱣ ᱢᱮ ᱡᱮ ᱟᱢ ᱵᱷᱤᱜᱽ ᱨᱮᱯᱳᱨᱴ ᱮᱢ ᱢᱮ ᱾\" + ᱥᱚᱵᱥᱠᱨᱟᱭᱵᱮᱥᱚᱱ ᱠᱷᱚᱱ ᱱᱟᱣᱟ ᱥᱴᱨᱤᱢ ᱠᱚ ᱵᱟᱰᱟᱭ ᱢᱮ + ᱚᱯᱰᱮᱴᱥ + ᱡᱚᱠᱷᱚᱱ ᱱᱟᱣᱟ ᱵᱟᱨᱥᱚᱱ ᱢᱮᱱᱟᱜ-ᱟ ᱮᱯ ᱟᱯᱰᱮᱴ ᱞᱟᱹᱜᱤᱫ ᱯᱟᱹᱛᱭᱟᱹᱣ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱯᱚᱯ-ᱟᱯ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱞᱟᱹᱜᱤᱫ ᱞᱟᱹᱴᱩ ᱦᱚᱪᱚ + ᱚᱞᱪᱤᱠᱤ ᱛᱮ ᱨᱩᱭᱟᱹᱲᱚᱜ ᱢᱮ - %s + ᱞᱤᱥᱴᱤ ᱧᱮᱧᱮᱞ ᱢᱚᱰ + ᱞᱤᱥᱴᱤ + ᱚᱴᱚ + ᱱᱟᱣᱟ ᱯᱟᱭᱤᱯ ᱚᱯᱰᱮᱴ ᱢᱮᱱᱟᱜ-ᱟ! + %s ᱠᱚᱰ ᱦᱚᱪᱚ ᱞᱟᱹᱜᱤᱫ ᱴᱟᱯ ᱢᱮ + ᱢᱩᱪᱟᱹᱫ + ᱱᱤᱛᱚᱜᱟᱜ ᱠᱷᱚᱵᱚᱨ ᱠᱚ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ… + ᱰᱟᱩᱱᱞᱚᱰ ᱵᱟᱹᱱᱩᱜ-ᱟ + ᱧᱩᱛᱩᱢ ᱛᱮᱭᱟᱨ ᱢᱮ + ᱩᱫᱩᱜ ᱢᱮ + ᱪᱷᱟᱸᱪ:Fact + ᱯᱷᱤᱞ ᱵᱮᱱᱟᱣ ᱫᱚ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ + ᱥᱮᱵᱟ ᱧᱟᱢ ᱵᱟᱝ ᱜᱟᱱᱚᱜ ᱠᱟᱱᱟ + ᱥᱮᱵᱟ ᱫᱚ ᱚᱱᱚᱞ ᱵᱟᱝ ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ + ᱥᱟᱨᱵᱟᱨ ᱫᱚ ᱢᱚᱞᱴᱤ-ᱛᱷᱨᱮᱰ ᱰᱟᱩᱱᱞᱚᱰ ᱠᱚ ᱵᱟᱝ ᱮᱢᱚᱜ ᱠᱟᱱᱟ, @string/msg_threads = 1 ᱥᱟᱶᱛᱮ ᱮᱱᱮᱡ ᱢᱮ + ᱛᱟᱭᱚᱢ ᱛᱮᱭᱟᱨ ᱵᱟᱝ ᱪᱟᱞᱟᱣ ᱟᱠᱟᱱᱟ + ᱱᱩᱯᱯᱟᱭᱤᱯᱷ ᱯᱷᱤᱞ ᱨᱮ ᱠᱟᱹᱢᱤ ᱚᱠᱛᱚ ᱨᱮ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱯᱟᱹᱛᱭᱟᱹᱣ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ, ᱪᱮᱫᱟᱜ ᱥᱮ ᱯᱷᱤᱞᱤᱯᱟᱭᱤᱰ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱡᱚᱱᱚᱲ ᱚᱠᱛᱚ + ᱟᱢ ᱟᱢᱟᱜ ᱰᱟᱩᱱᱞᱚᱰ ᱦᱤᱥᱛᱟ ᱵᱚᱫᱚᱞ ᱢᱮ ᱟᱨᱵᱟᱝ ᱥᱟᱱᱟᱢ ᱰᱟᱩᱱᱞᱚᱰ ᱟᱠᱟᱱ ᱯᱷᱤᱞ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ? + ᱰᱟᱩᱱᱞᱚᱰ ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚ ᱞᱟᱹᱜᱤᱫ ᱡᱚᱛᱚ ᱠᱷᱚᱱ ᱵᱟᱲᱛᱤ ᱥᱟᱸᱣᱛᱟ + ᱢᱚᱵᱟᱤᱞ ᱰᱟᱴᱟ ᱨᱮ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱫ ᱵᱮᱵᱚᱦᱟᱨᱚᱜ ᱟ, ᱢᱮᱱᱠᱷᱟᱱ ᱛᱤᱱᱟᱹᱜ ᱜᱟᱱ ᱰᱟᱩᱱᱞᱚᱰ ᱠᱚ ᱵᱚᱫᱚᱞ ᱫᱟᱲᱮᱭᱟᱜ ᱟ + ᱢᱤᱫᱴᱟᱹᱝ ᱰᱟᱩᱱᱞᱚᱰ ᱢᱤᱫ ᱚᱠᱛᱚ ᱨᱮ ᱪᱟᱞᱟᱜᱼᱟ + ᱰᱟᱩᱱᱞᱚᱰ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱡᱚᱛᱚ ᱰᱟᱩᱱᱞᱚᱰ ᱠᱚ ᱡᱟᱦᱟᱸ ᱨᱮ ᱫᱚᱦᱚᱭ ᱢᱮ ᱚᱱᱟ ᱟᱢᱮᱢ ᱩᱫᱩᱜᱟ._x000D_ +\nᱡᱩᱫᱤ ᱟᱢ ᱵᱟᱦᱨᱮ SD ᱠᱟᱨᱰ ᱨᱮ ᱰᱟᱩᱱᱞᱚᱰ ᱞᱟᱹᱜᱤᱫ ᱮᱠᱴᱤᱵᱷ ᱢᱮ + ᱟᱢ ᱡᱟᱦᱟᱸ ᱨᱮ ᱡᱚᱛᱚ ᱰᱟᱩᱱᱞᱚᱰ ᱥᱟᱦᱴᱟ ᱠᱚ ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚᱭ ᱢᱮ + \'ᱥᱴᱚᱨᱮᱡᱽ ᱮᱠᱥᱮᱥ ᱯᱷᱨᱮᱢᱠᱣᱟᱨᱠ\' ᱢᱤᱫ ᱵᱟᱦᱨᱮ SD ᱠᱟᱨᱰ ᱨᱮ ᱰᱟᱩᱱᱞᱚᱰ ᱞᱟᱹᱜᱤᱫ ᱮᱢᱚᱜ ᱟ + ᱚᱸᱰᱨᱚᱭᱮᱰ ᱑᱐ ᱠᱷᱚᱱ ᱮᱛᱚᱦᱚᱵ ᱠᱟᱛᱮ \'ᱥᱴᱳᱨᱮᱡ ᱮᱠᱥᱮᱥ ᱯᱷᱨᱮᱢᱚᱠ\' ᱜᱮ ᱥᱚᱯᱷᱚᱨᱴ ᱟᱠᱟᱱᱟ + ᱢᱤᱫ ᱤᱱᱥᱴᱟᱱᱥ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱟᱯᱞᱤᱠᱮᱥᱚᱱ ᱯᱟᱹᱨᱥᱤ + ᱥᱤᱥᱴᱮᱢ ᱰᱤᱯᱚᱞᱴ + ᱰᱩᱯᱞᱤᱠᱮᱴ ᱠᱚ ᱪᱷᱩᱴᱟᱹᱣ? + ᱟᱢ ᱱᱚᱶᱟ ᱯᱷᱟᱭᱞᱤᱥᱴ ᱨᱮ ᱡᱚᱛᱚ ᱫᱩᱯᱞᱤᱠᱮᱴ ᱥᱴᱨᱤᱢ ᱠᱚ ᱦᱮᱡ ᱟᱫᱮᱭᱟᱢ ᱥᱮ? + ᱥᱮᱨᱮᱧ ᱛᱟᱹᱞᱤᱠᱟ ᱨᱮ ᱥᱮᱞᱮᱫᱚᱜ ᱢᱟᱲᱟᱝ ᱟᱨ ᱛᱟᱭᱚᱢ ᱨᱮ ᱡᱟᱦᱟᱸ ᱵᱷᱤᱰᱤᱣ ᱠᱚ ᱧᱮᱞ ᱟᱠᱟᱱᱟ, ᱚᱱᱟ ᱠᱚ ᱪᱷᱟᱹᱲ ᱮᱢ ᱦᱩᱭᱩᱜᱼᱟ ᱾ ᱟᱢ ᱠᱤ ᱜᱚᱴᱟᱵᱩᱴᱟᱹ ᱠᱟᱱᱟ? ᱱᱚᱶᱟ ᱫᱚ ᱨᱩᱣᱟᱹᱲ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ + ᱱᱟᱣᱟ ᱡᱚᱢᱟᱜ ᱡᱤᱱᱤᱥ ᱠᱚ + ᱥᱮᱞᱮᱫ ᱠᱚ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + ᱪᱮᱫ ᱦᱚᱸ ᱵᱟᱱᱟᱣ ᱟᱠᱟᱱᱟ + ᱡᱟᱦᱟᱱᱟᱜ ᱵᱟᱝ ᱜᱩᱴ ᱟᱠᱟᱱ ᱥᱚᱵᱥᱠᱨᱟᱭᱵᱮᱥ ᱠᱚ ᱧᱮᱞ ᱢᱮ + ᱯᱷᱮᱰ + ᱯᱷᱤᱰ ᱟᱯᱰᱮᱴ ᱞᱟᱦᱟᱨᱮ + ᱢᱤᱫ ᱥᱚᱵᱥᱠᱨᱟᱭᱵᱽᱥᱚᱱ ᱵᱟᱥᱠᱮᱴᱮᱰ ᱦᱩᱭᱩᱜ ᱢᱟᱬᱟᱝ ᱨᱮ ᱯᱟᱹᱪᱷᱤᱢ ᱩᱫᱩᱜ ᱠᱷᱚᱱ ᱛᱟᱭᱚᱢ ᱚᱠᱛᱚ ⁇ %s + Error loading feed ᱥᱟᱯᱲᱟᱣ + \'%s\' ᱞᱟᱹᱜᱤᱫ ᱯᱷᱤᱰ ᱞᱚᱰ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ. + ᱮᱠᱴᱚᱨᱟᱜ ᱮᱠᱟᱶᱴ ᱫᱚ ᱛᱷᱟᱯᱚᱱ ᱟᱠᱟᱱᱟ ᱾ +\nNewPipe ᱫᱚ ᱱᱚᱣᱟ ᱯᱷᱤᱰ ᱫᱚ ᱟᱵᱚ ᱦᱟᱵᱤᱡ ᱵᱟᱝ ᱞᱚᱰ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ +\nᱟᱢ ᱱᱚᱣᱟ ᱪᱟᱱᱮᱨ ᱠᱷᱚᱱ ᱟᱹᱪᱩᱨ ᱚᱪᱚᱜ ᱠᱟᱹᱢᱤᱭᱟᱢ ᱥᱮ? + ᱯᱷᱚᱥᱴ ᱯᱷᱤᱰ ᱢᱚᱰ ᱱᱚᱶᱟ ᱨᱮ ᱵᱟᱹᱲᱛᱤ ᱵᱟᱰᱟᱭ ᱮᱢ ᱮᱫᱟ ᱾ + ᱡᱩᱫᱤ ᱢᱮᱱᱟᱜᱼᱟ ᱮᱢ ᱟᱠᱟᱱ ᱡᱚᱢᱟᱜ ᱠᱷᱚᱱ ᱦᱟᱛᱟᱣ + ᱟᱭᱢᱟ ᱥᱮᱵᱟ ᱨᱮ ᱧᱟᱢᱚᱜᱼᱟ, ᱱᱚᱶᱟ ᱫᱚ ᱟᱥᱚᱠᱟᱭ ᱛᱮ ᱟᱹᱰᱤ ᱦᱩᱰᱤᱧ ᱜᱮᱭᱟ ᱢᱮᱱᱠᱷᱟᱱ ᱢᱤᱫ ᱞᱮᱠᱟᱱ ᱤᱱᱴᱮᱢᱥ ᱟᱨ ᱟᱥᱚᱠᱟᱭ ᱛᱮ ᱵᱟᱝ ᱯᱩᱨᱟᱹᱣ ᱟᱠᱟᱱ ᱤᱱᱯᱷᱚᱨᱢᱮᱥᱚᱱ (ᱡᱮᱞᱮᱠᱟ ᱺ ᱚᱠᱛᱚ ᱵᱟᱝ, ᱤᱱᱴᱮᱢᱥ ᱨᱮᱱᱟᱜ ᱞᱮᱠᱷᱟ, ᱚᱞᱤᱵᱽ ᱥᱴᱮᱴᱥ ᱵᱟᱝ) ᱨᱩᱣᱟᱹᱲ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ + ᱯᱷᱮᱥᱵᱩᱠ ᱢᱚᱰ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱟᱢᱟᱜ ᱢᱚᱱᱮ ᱨᱮ ᱡᱚᱢᱟᱜ ᱨᱮᱱᱟᱜ ᱞᱚᱰ ᱟᱹᱰᱤ ᱟᱞᱜᱟ ᱜᱮᱭᱟ? ᱡᱩᱫᱤ ᱱᱚᱶᱟ ᱦᱩᱭᱩᱜ ᱠᱟᱱᱟ, ᱮᱱᱠᱷᱟᱱ ᱥᱯᱤᱰ ᱞᱚᱰ ᱮᱱᱟᱝ ᱮᱱᱮᱡ (ᱡᱟᱦᱟᱸ ᱫᱚ ᱟᱢ ᱥᱤᱴᱤᱝ ᱨᱮ ᱥᱮ ᱞᱟᱛᱟᱨ ᱨᱮᱭᱟᱜ ᱵᱩᱛᱟᱹᱢ ᱚᱞ ᱠᱟᱛᱮᱢ ᱵᱚᱫᱚᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ) +\n +\nᱱᱤᱣ ᱯᱟᱭᱤᱯ ᱫᱚ ᱵᱟᱨᱭᱟ ᱯᱷᱤᱰ ᱞᱚᱰ ᱥᱴᱨᱮᱴᱡᱤ ᱮᱢᱚᱜ ᱠᱟᱱᱟ: +\n• ᱡᱚᱛᱚ ᱥᱚᱵᱽᱥᱠᱨᱟᱭᱵᱽ ᱪᱮᱱᱮᱞ ᱦᱟᱛᱟᱣ, ᱡᱟᱦᱟᱸ ᱫᱚ ᱟᱞᱜᱟ ᱜᱮᱭᱟ ᱢᱮᱱᱠᱷᱟᱱ ᱯᱩᱨᱟᱹᱣᱜᱼᱟ ᱾ +\n• ᱰᱮᱰᱤᱠᱮᱴᱮᱰ ᱥᱮᱵᱟ ᱮᱱᱰ ᱯᱚᱭᱮᱱᱴ ᱵᱮᱵᱷᱟᱨ, ᱡᱟᱦᱟᱸ ᱫᱚ ᱦᱩᱲᱟᱹᱜ ᱜᱮᱭᱟ ᱢᱮᱱᱠᱷᱟᱱ ᱟᱥᱚᱠᱟᱭ ᱛᱮ ᱯᱩᱨᱟᱹᱣ ᱫᱚ ᱵᱟᱝ ᱦᱩᱭᱩᱜᱼᱟ ᱾ +\n +\nᱵᱟᱨᱭᱟ ᱛᱟᱞᱟᱨᱮᱱᱟᱜ ᱵᱷᱮᱜᱟᱨ ᱫᱚ ᱦᱩᱭᱩᱜ ᱠᱟᱱᱟ ᱡᱮ ᱦᱩᱲᱟᱹᱜ ᱢᱤᱫᱴᱟᱹᱝ ᱜᱮ ᱤᱱᱯᱷᱚᱨᱢᱮᱥᱚᱱ ᱵᱟᱹᱱᱩᱜᱼᱟ, ᱡᱮᱞᱮᱠᱟ ᱤᱢᱮᱠᱥ ᱨᱮᱱᱟᱜ ᱚᱠᱛᱚ ᱥᱮ ᱛᱷᱚᱠ (ᱡᱟᱦᱟᱸ ᱫᱚ ᱞᱟᱭᱤᱵᱷᱤᱰᱤᱭᱳ ᱟᱨ ᱱᱚᱨᱢᱟᱞ ᱨᱮᱱᱟᱜ ᱵᱷᱮᱜᱟᱨ ᱵᱟᱭ ᱵᱟᱰᱟᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ) ᱟᱨ ᱱᱚᱶᱟ ᱠᱚᱢ ᱤᱢᱮᱠᱥ ᱠᱚ ᱨᱩᱣᱟᱹᱲ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ +\n +\nᱭᱩᱴᱭᱩᱵᱽ ᱫᱚ ᱢᱤᱫᱴᱮᱱ ᱥᱮᱵᱟ ᱨᱮᱱᱟᱜ ᱢᱤᱫ ᱞᱟᱹᱱᱟᱹᱤ ᱠᱟᱱᱟ ᱡᱟᱦᱟᱸ ᱫᱚ ᱱᱚᱶᱟ ᱦᱚᱨᱟ ᱟᱡᱟᱜ RSS ᱯᱷᱤᱰ ᱥᱟᱶᱛᱮ ᱮᱢᱚᱜ ᱠᱟᱱᱟ ᱾ +\n +\nᱚᱱᱟᱛᱮ ᱵᱟᱪᱷᱟᱣ ᱫᱚ ᱟᱢ ᱪᱮᱫ ᱜᱮᱢ ᱵᱷᱟᱹᱵᱤᱛᱟ: ᱦᱩᱲᱟᱹᱜ ᱟᱨᱵᱟᱝ ᱥᱟᱹᱠᱷᱭᱟᱹᱛ ᱠᱟᱛᱷᱟ ᱾ + ᱥᱴᱨᱤᱢ ᱠᱚ ᱵᱚᱫᱚᱞ/ᱥᱚᱫᱚᱨ ᱢᱮ + ᱡᱚᱠᱷᱚᱱ ᱯᱷᱤᱰ ᱚᱯᱰᱮᱴ ᱦᱩᱭᱩᱜᱼᱟ, ᱚᱱᱟ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱴᱮᱵᱽ ᱠᱚ ᱦᱟᱛᱟᱣ ᱢᱮ ᱾ ᱡᱩᱫᱤ ᱢᱤᱫ ᱪᱮᱱᱮᱞ ᱫᱚ fast mode ᱵᱮᱵᱷᱟᱨ ᱠᱟᱛᱮ update ᱟᱠᱟᱱᱟ ᱮᱱᱠᱷᱟᱱ ᱱᱚᱶᱟ option ᱫᱚ ᱵᱟᱹᱱᱩᱜ-ᱟ ᱾ + ᱱᱩᱣᱟ ᱯᱟᱭᱤᱯ ᱱᱤᱛ ᱦᱟᱹᱵᱤᱡ ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱥᱚᱯᱷᱴᱣᱮᱭᱟᱨ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾_x000D_ +\n_x000D_ +\nᱱᱚᱶᱟ ᱫᱚ ᱢᱚᱵᱟᱤᱞ ᱣᱟᱨᱥᱚᱱ ᱨᱮ ᱥᱚᱯᱷᱴᱣᱮᱨ ᱦᱩᱭᱩᱜᱼᱟ ᱾ + ᱞᱚᱠ ᱥᱠᱨᱤᱱ ᱛᱷᱚᱠ ᱟᱨ ᱱᱳᱴᱤᱯᱷᱤᱠᱮᱥᱚᱱ ᱵᱟᱱᱟᱨ ᱞᱟᱹᱜᱤᱫ ᱛᱚᱢᱱᱤᱞ ᱵᱮᱵᱷᱟᱨ ᱢᱮ + ᱟᱢᱟᱜ ᱰᱤᱵᱟᱤᱥ ᱨᱮ ᱱᱚᱶᱟ ᱮᱯ ᱵᱟᱹᱱᱩᱜ-ᱟ + ᱱᱚᱶᱟ ᱠᱟᱹᱢᱤ ᱞᱟᱹᱜᱤᱫ ᱫᱚ ᱞᱟᱹᱠᱛᱤᱭᱟᱱ ᱯᱷᱤᱞ ᱢᱮᱱᱮᱡᱚᱨ ᱵᱟᱝ ᱧᱟᱢ ᱞᱮᱱᱟ ᱾ %s + ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱟᱢᱟᱜ ᱫᱤᱥᱚᱢ ᱨᱮ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾ + ᱱᱩᱶᱟ ᱫᱚ ᱢᱤᱫ ᱥᱟᱣᱚᱱᱰᱠᱞᱟᱣᱩᱰ ᱜᱳ+ ᱴᱨᱟᱠ ᱠᱟᱱᱟ, ᱟᱢᱟᱜ ᱫᱤᱥᱚᱢ ᱨᱮ ᱡᱚᱛᱚ ᱠᱷᱚᱱ ᱠᱚᱢ, ᱚᱱᱟᱛᱮ ᱱᱩᱭᱯᱟᱭᱤᱯ ᱫᱚ ᱱᱚᱣᱟ ᱴᱨᱟᱠ ᱥᱴᱨᱤᱢ ᱟᱨ ᱰᱟᱩᱱᱞᱚᱰ ᱵᱟᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱾ + ᱱᱩᱣᱟ ᱯᱟᱭᱤᱯ ᱫᱚ ᱱᱤᱡᱮᱨᱟᱜ ᱠᱟᱱᱟ, ᱚᱱᱟᱛᱮ ᱱᱩᱣᱟ ᱯᱟᱭᱤᱯ ᱫᱚ ᱱᱚᱣᱟ ᱵᱟᱭ ᱰᱟᱩᱱᱞᱚᱰ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱟᱨ ᱵᱟᱝ ᱥᱴᱨᱤᱢ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ᱾ + ᱱᱩᱣᱟ ᱯᱟᱭᱤᱯ ᱨᱮᱱ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱚ ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱡᱟᱦᱟᱸᱱ ᱯᱮᱨᱮᱡ ᱥᱮᱞᱮᱫ ᱠᱚᱣᱟ, ᱚᱱᱟᱛᱮ ᱱᱩᱣᱟ ᱯᱟᱭᱤᱯ ᱨᱮᱱ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱚ ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱡᱟᱦᱟᱸᱱ ᱯᱮᱨᱮᱡ ᱥᱮᱞᱮᱫ ᱵᱟᱝ ᱠᱚ ᱧᱟᱢ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ᱾ + ᱯᱷᱮᱭᱟᱨ ᱟᱠᱟᱱ + ᱱᱚᱶᱟ ᱵᱟᱪᱷᱟᱣ ᱫᱚ ᱚᱱᱠᱟᱱ ᱞᱟᱹᱜᱤᱫ ᱜᱮ ᱵᱮᱵᱦᱟᱨᱚᱜᱼᱟ ᱡᱩᱫᱤ %s ᱴᱷᱤᱠᱟᱹᱱᱟ + ᱰᱟᱩᱱᱞᱚᱰ ᱮᱛᱚᱦᱚᱵ ᱟᱠᱟᱱᱟ + ᱟᱢ ᱱᱤᱛᱚᱜ ᱚᱞ ᱪᱤᱠᱤ ᱨᱮ ᱚᱞ ᱪᱤᱠᱤ ᱵᱟᱪᱷᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ ᱟ. ᱵᱟᱰᱟᱭ ᱢᱮ ᱡᱮ ᱥᱟᱦᱴᱟ ᱫᱚ ᱵᱷᱤᱸᱰᱟᱹᱣ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱟᱨ ᱵᱟᱪᱷᱟᱣ ᱢᱚᱰ ᱨᱮ ᱞᱤᱱᱠ ᱫᱚ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱾ + ᱪᱤᱠᱤ ᱨᱮ ᱚᱞ ᱵᱟᱪᱷᱟᱣ ᱞᱟᱹᱜᱤᱫ ᱮᱱᱮᱢ ᱢᱮ + ᱯᱨᱟᱭᱵᱷᱮᱴ + ᱩᱢᱚᱨ ᱨᱮᱭᱟᱜ ᱞᱤᱢᱤᱴ + ᱯᱟᱹᱨᱥᱤ + ᱥᱚᱦᱚᱫ + ᱛᱷᱩᱢᱱᱟᱭᱤᱴ + ᱚᱯᱞᱚᱰᱟᱨ ᱟᱵᱟᱛᱟᱨ + ᱥᱟᱵ-ᱪᱟᱱᱮᱞ ᱟᱵᱟᱛᱟᱨ + ᱯᱨᱟᱭᱵᱷᱮᱴ + ᱤᱱᱴᱟᱨᱱᱟᱞ + ᱥᱚᱵᱽᱥᱠᱨᱟᱭᱵᱟᱨ + ᱯᱤᱱ ᱟᱠᱟᱱ ᱟᱹᱱᱤᱡ + ᱵᱮᱱᱟᱣᱤᱡᱟᱜ ᱢᱚᱱᱮ + ᱚᱯᱮᱱ ᱣᱮᱵᱥᱟᱤᱴ + ᱴᱮᱵᱽᱞᱮᱴ ᱢᱚᱰ + ᱨᱮ + ᱚᱯᱷ + ᱮᱠᱥᱳᱯᱞᱮᱭᱟᱨ ᱰᱤᱯᱚᱞᱴ + ᱥᱟᱱᱟᱢ ᱪᱟᱞᱟᱣ ᱢᱮ + ᱡᱟᱦᱟᱸᱱ ᱥᱴᱨᱤᱢ ᱠᱚ ᱱᱤᱛ ᱫᱷᱟᱹᱵᱤᱡ ᱰᱟᱩᱱᱞᱚᱰᱡᱚᱨ ᱦᱚᱛᱮᱛᱮ ᱥᱚᱯᱷᱴᱣᱮᱰ ᱵᱟᱱᱟᱣ ᱟᱠᱟᱱᱟ ᱚᱱᱟ ᱠᱚ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱱᱚᱶᱟ ᱥᱴᱨᱤᱢ ᱨᱮ ᱢᱤᱫᱴᱟᱹᱝ ᱚᱰᱤᱭᱳ ᱴᱨᱟᱠ ᱢᱮᱱᱟᱜ ᱛᱟᱭᱟ + ᱡᱟᱹᱥᱛᱤ ᱧᱮᱞ ᱟᱠᱟᱱᱟ + ᱢᱟᱲᱟᱝ ᱨᱮ + ᱡᱩᱫᱤ ᱟᱢᱟᱜ ᱰᱤᱠᱳᱰᱟᱨ ᱮᱛᱚᱦᱚᱵ ᱨᱮᱭᱟᱜ ᱞᱟᱹᱞᱤᱥ ᱛᱟᱦᱮᱸᱱᱟ, ᱮᱱᱠᱷᱟᱱ ᱱᱚᱶᱟ ᱵᱟᱪᱷᱱᱟᱣ ᱫᱚ ᱮᱥᱮᱨ ᱢᱮ, ᱡᱟᱦᱟᱫᱚ ᱮᱛᱚᱦᱚᱵ ᱰᱤᱠᱳᱰᱟᱨ ᱮᱛᱚᱦᱚᱵ ᱵᱟᱝ ᱦᱩᱭ ᱞᱮᱱ ᱠᱷᱟᱱ ᱞᱟᱛᱟᱨᱼᱯᱨᱳᱭᱨᱤᱴᱤ ᱰᱤᱠᱳᱰᱟᱨ ᱨᱮ ᱨᱩᱣᱟᱹᱲ ᱦᱮᱡᱚᱜᱼᱟ ᱾ ᱱᱚᱶᱟ ᱨᱮᱭᱟᱜ ᱚᱡᱮ ᱫᱚ ᱮᱛᱚᱦᱚᱵ ᱰᱤᱠᱳᱰᱟᱨ ᱠᱚ ᱵᱮᱣᱦᱟᱨ ᱚᱠᱛᱚ ᱠᱷᱚᱱ ᱵᱮᱼᱲᱤᱪᱟᱹᱲ ᱯᱟᱥᱱᱟᱶ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ + ᱱᱚᱶᱟ ᱠᱟᱹᱢᱤ ᱦᱚᱨᱟ ᱫᱚ ᱥᱩᱨᱥᱩᱯᱩᱨ ᱵᱚᱫᱚᱞ ᱦᱩᱭ ᱞᱮᱱ ᱠᱷᱟᱱ ᱵᱷᱤᱰᱤᱭᱳ ᱠᱳᱰᱮᱠ ᱠᱚ ᱵᱚᱫᱚᱞ ᱟᱨ ᱨᱤ-ᱤᱱᱥᱴᱟᱱᱥᱟᱭᱤᱴ (re-instantiates) ᱮᱫᱟ, ᱚᱱᱟ ᱫᱚ ᱥᱩᱨᱥᱩᱯᱩᱨ ᱠᱳᱰᱮᱠ ᱨᱮ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱥᱩᱯᱩᱨ ᱥᱩᱯᱩᱨ ᱛᱮ ᱥᱮᱴᱮᱨᱚᱜ ᱨᱮᱱᱟᱜ ᱠᱟᱱᱟ ᱾ ᱱᱚᱶᱟ ᱥᱟᱛᱟᱢ ᱥᱟᱶᱛᱮ ᱮᱠᱥᱳᱯᱞᱮᱭᱟᱨ ᱟᱭᱢᱟ ᱚᱱᱚᱞ ᱨᱮ ᱵᱮᱵᱷᱟᱨ ᱟᱠᱟᱱᱟ, ᱱᱚᱶᱟ ᱥᱤᱴᱤᱝ ᱫᱚ ᱟᱱᱰᱨᱚᱭᱮᱰ ᱖ ᱟᱨ ᱚᱱᱟ ᱠᱷᱚᱱ ᱪᱮᱛᱟᱱ ᱨᱮᱜᱮ ᱟᱯᱷᱮᱠᱴ ᱟᱠᱟᱱᱟ +\n +\nᱱᱮᱛᱟᱨᱟᱜ ᱵᱷᱤᱰᱤᱭᱳ ᱯᱞᱮᱭᱟᱨ ᱥᱮ ᱯᱷᱩᱞᱥᱠᱨᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫᱚᱜ ᱡᱚᱠᱷᱚᱱ ᱱᱚᱶᱟ ᱵᱟᱪᱷᱟᱣ ᱮᱢ ᱫᱚᱦᱚ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ + ᱪᱮᱱᱟᱞ ᱥᱟᱦᱴᱟ ᱠᱚᱨᱮ ᱪᱮᱫ ᱴᱮᱵᱽ ᱠᱚ ᱵᱚᱫᱚᱞᱟ + ᱚᱯᱮᱱ ᱯᱞᱮ ᱠᱭᱩᱤ + ᱡᱚᱛᱚ ᱥᱠᱨᱤᱱ ᱨᱮ ᱵᱷᱮᱡᱟ ᱢᱮ + ᱱᱮᱛᱟᱨᱟᱜ ᱫᱟᱜᱽ + ᱠᱷᱮᱞ + ᱨᱤᱯᱷᱟᱭᱤᱞ + ᱚᱠᱛᱚ + ᱨᱩᱣᱟ. + ᱰᱮᱴᱟ ᱟᱨ ᱢᱮᱢᱚᱨᱤ ᱵᱮᱵᱷᱟᱨ ᱠᱚᱢ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱪᱤᱛᱟᱹᱨ ᱠᱚᱣᱟᱜ ᱥᱤᱠᱷᱱᱟᱹᱛ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ᱟᱨ ᱪᱤᱛᱟᱹᱨ ᱠᱚ ᱚᱞ ᱦᱚᱪᱚ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱵᱟᱪᱷᱟᱣ ᱢᱮ. ᱢᱤᱢᱮᱨᱤ ᱟᱨ ᱚᱱ-ᱰᱤᱥᱠ ᱪᱤᱛᱟᱹᱨ ᱠᱮᱪ ᱵᱟᱱᱟᱨ ⁇ %s ᱪᱷᱟᱯᱟ ᱟᱠᱟᱱᱟ + ᱞᱟᱯᱷᱟᱝ ᱥᱤᱠᱷᱱᱟ. + ᱛᱟᱞᱢᱟ ᱥᱤᱠᱷᱱᱟᱹᱛ + ᱩᱥᱩᱞ ᱥᱤᱠᱷᱱᱟᱹᱛ + ᱯᱷᱟᱭᱞᱤᱥᱴ ᱧᱩᱛᱩᱢ ᱟᱨ ᱵᱷᱤᱰᱤᱭᱳ ᱧᱩᱛᱩᱢ ᱞᱮᱠᱟᱛᱮ ᱟᱨᱵᱟᱝ ᱵᱷᱤᱰᱤᱭᱳ URL ᱨᱮᱱᱟᱜ ᱢᱤᱫ ᱞᱮᱠᱟᱱ ᱞᱤᱥᱴᱤ ᱞᱮᱠᱟᱛᱮ ᱴᱷᱟᱶ ᱮᱢ ᱢᱮ + URL ᱛᱟᱹᱞᱠᱟᱹ ᱥᱟᱯᱲᱟᱣ + - %1$s: %2$s + ᱱᱚᱴᱤᱯᱷᱤᱠᱮᱥᱚᱱ ᱨᱮ ᱑᱖:᱙ ᱠᱷᱚᱱ ᱑:᱑ ᱟᱥᱯᱮᱠᱴ ᱚᱱᱩᱯᱟᱹᱛ ᱨᱮ ᱵᱷᱤᱰᱤᱭᱳ ᱛᱷᱚᱢᱵᱱᱮᱞ ᱜᱮᱫᱽ ᱢᱮ + ᱨᱤᱠᱟᱵᱽᱞᱤᱝ + ᱰᱤᱯᱷᱚᱞᱴ ᱠᱚᱱᱴᱮᱱᱴ ᱫᱤᱥᱚᱢ + ᱯᱤᱪ + ᱦᱳᱥᱴ + ExoPlayer ᱨᱮᱭᱟᱜ ᱰᱤᱠᱚᱰᱟᱨ ᱯᱷᱮᱞᱚᱵᱮᱠ ᱯᱷᱤᱪᱚᱨ ᱵᱮᱵᱷᱟᱨ ᱢᱮ + ᱮᱠᱥᱳᱯᱞᱮᱭᱟᱨ ᱥᱮᱴᱤᱝ + ᱮᱠᱥᱳᱯᱞᱮᱭᱟᱨ ᱥᱮᱴᱤᱸᱜᱽᱥ ᱠᱚ ᱪᱟᱪᱞᱟᱣ ᱢᱮ ᱾ ᱱᱚᱶᱟ ᱞᱟᱹᱜᱤᱫ ᱠᱷᱮᱞᱚᱸᱰᱤᱭᱟᱹ ᱠᱚᱣᱟᱜ ᱨᱤᱥᱴᱟᱨᱴ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ + ᱰᱟᱵᱞᱤᱝ + ᱰᱮᱥᱠᱨᱤᱯᱴᱤᱵ + ᱯᱨᱚᱜᱨᱮᱥᱤᱵᱽ ᱠᱚᱴᱮᱱᱴ (ᱱᱤᱛᱚᱜ %s) ᱨᱮ ᱞᱚᱰ ᱤᱱᱴᱟᱨᱵᱷᱮᱞ ᱨᱮᱱᱟᱜ ᱢᱟᱨᱟᱝ ᱮᱢ ᱢᱮ ᱢᱤᱫ ᱠᱚᱢ ᱵᱷᱚᱞᱮᱡᱽ ᱟᱡᱟᱜ ᱮᱛᱚᱦᱚᱵ ᱞᱚᱰᱰᱤᱝ ᱨᱮᱱᱟᱜ ᱟᱯᱞᱚᱰ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ + ᱚᱥᱴᱨᱮᱞᱤᱭᱟᱱ ᱚᱰᱤᱭᱳ ᱞᱟᱹᱜᱤᱫ + ᱴᱚᱴᱷᱟᱠᱤᱭᱟ. ᱥᱩᱪᱚᱱᱟ + ᱠᱷᱚᱵᱚᱨ ᱮᱦᱚᱵ ᱢᱮ + ᱢᱩᱪᱟᱹᱫ ᱠᱷᱚᱵᱚᱨ ᱴᱷᱟᱹᱶᱠᱟᱹ + ᱤᱱᱥᱴᱮᱸᱥ ᱥᱮᱞᱮᱫ ᱢᱮ + ᱤᱱᱥᱴᱟᱱᱥ URL ᱮᱢ ᱢᱮ + ᱚᱯᱰᱮᱴᱥ + ᱯᱚᱯ-ᱟᱯ ᱢᱚᱰ ᱨᱮ ᱠᱷᱮᱞᱚᱸᱰ + ᱥᱟᱦᱴᱟ + ᱩᱢᱮᱨ ᱞᱟᱹᱠᱛᱤᱭᱟᱱ ᱠᱟᱛᱷᱟ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱭᱩᱴᱭᱩᱵᱽ ᱨᱮᱱᱟᱜ \"Restricted Mode\" ᱚᱞ ᱢᱮ + ᱡᱚᱛᱚ + ᱱᱮᱴᱣᱟᱨᱠ ᱵᱷᱩᱞ + ᱟᱱᱰᱨᱚᱭᱮᱰ ᱨᱮ ᱞᱤᱵᱨᱮ ᱞᱟᱭᱤᱴᱣᱮᱴ ᱥᱴᱨᱤᱢᱤᱝ + ᱴᱮᱜᱥ + ᱥᱚᱨᱠᱟᱨᱤ + ᱚᱱᱚᱞ ᱨᱮ ᱡᱟᱭᱜᱟ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱪᱮᱱᱮᱞ ᱨᱮᱱᱟᱜ ᱟᱵᱟᱛᱟᱨ ᱛᱷᱩᱱᱤᱠᱟ + ᱥᱮᱨᱮᱧ ᱞᱤᱥᱴᱤ ᱥᱟᱦᱴᱟ + ᱵᱟᱝ + ᱰᱟᱩᱱᱞᱚᱰ ᱢᱮ + ᱯᱷᱤᱞ ᱧᱩᱛᱩᱢ ᱨᱮ ᱞᱟᱹᱠᱛᱤᱭᱟᱱ ᱪᱤᱠᱤ ᱠᱚ + ᱪᱷᱟᱸᱪ:Copy to clipboard ᱨᱮ ᱪᱷᱟᱸᱪ:Copy to clipboard + ᱧᱩᱛᱩᱢ ᱵᱚᱫᱚᱞ ᱢᱮ + ᱯᱷᱤᱞᱤᱢ ᱧᱩᱛᱩᱢ + ᱛᱷᱤᱭᱮᱴᱚᱨ + ᱪᱷᱟᱸᱪ + NewPipe ᱮᱯᱞᱤᱠᱮᱥᱚᱱ + ᱵᱟᱰᱟᱭ ᱞᱟᱹᱜᱤᱫ ᱴᱟᱯ ᱢᱮ + ᱦᱟᱥᱤ ᱠᱚᱞᱠᱟᱛᱟ + ᱱᱤᱛ ᱦᱟᱹᱵᱤᱡ ᱰᱟᱩᱱᱞᱚᱰ ᱯᱷᱳᱞᱰᱟᱨ ᱥᱮᱴ ᱵᱟᱝ, ᱱᱤᱛ ᱦᱟᱹᱵᱤᱡ ᱰᱟᱩᱱᱞᱚᱰ ᱯᱷᱳᱞᱰᱟᱨ ᱵᱟᱪᱷᱟᱣ ᱢᱮ + © %1$s ᱛᱮ %2$s ᱞᱟᱛᱟᱨ ᱨᱮ %3$s + About & FAQ + ᱞᱟᱭᱥᱮᱱᱥ + ᱥᱮᱞᱮᱫᱚᱜ ᱢᱮ + ᱱᱤᱭᱩ ᱯᱟᱭᱯᱮ ᱨᱮᱱᱟᱜ ᱯᱨᱟᱭᱵᱷᱮᱴᱤ ᱯᱚᱞᱤᱥᱤ + ᱱᱤᱣ ᱯᱟᱭᱤᱯ ᱯᱨᱚᱡᱮᱠᱴ ᱟᱢᱟᱜ ᱯᱨᱟᱭᱵᱷᱮᱴ ᱟᱹᱰᱤ ᱥᱚᱨᱮᱥ ᱜᱮᱭᱟ. ᱚᱱᱟᱛᱮ, ᱟᱢᱟᱜ ᱟᱹᱭᱫᱟᱹᱨᱤ ᱵᱟᱹᱱᱩᱜ ᱠᱷᱟᱱ ᱱᱚᱶᱟ ᱮᱯᱞᱤᱠᱮᱥᱚᱱ ᱡᱟᱦᱟᱱ ᱰᱮᱴᱟ ᱵᱟᱭ ᱥᱟᱢᱟᱝᱟ ᱾_x000D_ +\nᱱᱤᱣ ᱯᱟᱭᱤᱯ ᱨᱮᱱᱟᱜ ᱯᱨᱟᱭᱵᱷᱮᱴ ᱯᱚᱞᱤᱥᱤ ᱨᱮ ᱡᱟᱦᱟᱸᱱ ᱰᱮᱴᱟ ᱠᱚ ᱟᱢ ᱠᱨᱮᱥᱚᱨ ᱨᱮᱯᱳᱨᱴ ᱚᱠᱟᱛᱮ ᱟᱢ ᱚᱠᱟ ᱞᱮᱠᱟᱛᱮ ᱟᱢᱟᱜ ᱰᱮᱴᱟ ᱠᱚ ᱵᱚᱫᱚᱞᱟ ᱟᱨ ᱵᱚᱫᱚᱞᱟ ᱚᱱᱟ ᱵᱟᱵᱚᱫ ᱛᱮ ᱡᱚᱛᱚ ᱵᱟᱰᱟᱭ ᱮᱢᱚᱜ ᱟ᱾ + ᱯᱨᱟᱭᱵᱷᱮᱴᱤ ᱯᱚᱞᱤᱥ ᱯᱟᱲᱦᱟᱣ ᱢᱮ + ᱞᱟᱭᱥᱮᱱᱥ ᱯᱟᱲᱦᱟᱣ ᱢᱮ + ᱟᱥᱚᱠᱟᱭ ᱛᱮ ᱚᱞ ᱟᱠᱟᱱ ᱯᱩᱥᱴᱟ + ᱯᱷᱤᱞ ᱥᱮᱴᱮᱨ ᱟᱠᱟᱱᱟ ᱟᱨ ᱵᱟᱝ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ + ᱱᱚᱝᱠᱟᱱ ᱯᱷᱳᱞᱰᱟᱨ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱱᱚᱝᱠᱟᱱ ᱯᱷᱤᱞ/ᱥᱟᱹᱛ ᱨᱮᱱᱟᱜ ᱜᱟᱞᱢᱟᱨᱟᱣ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱢᱮᱴᱟᱰᱟᱴᱟ ᱞᱚᱰ ᱠᱟᱱᱟ… + ᱪᱟᱱᱮᱞ + ᱵᱚᱫᱚᱞ ᱢᱮ + ᱥᱟᱱᱟᱢ ᱠᱷᱮᱞ ᱢᱮ + ᱡᱚᱦᱚᱜ + ᱢᱤᱫ ᱫᱷᱟᱣ + ᱯᱷᱤᱞᱤ + ᱱᱳᱴᱤᱯᱷᱤᱠᱮᱥᱚᱱ + ᱱᱤᱭᱩ ᱯᱟᱭᱯᱮ ᱵᱟᱰᱟᱭ ᱦᱚᱪᱚ + ᱥᱴᱮᱯ + ᱥᱮᱵᱟ ᱨᱮ ᱡᱚᱲᱟᱣ ᱵᱟᱭ ᱫᱟᱲᱮᱭᱟᱜ ᱠᱟᱱᱟ + ᱵᱟᱝ ᱧᱟᱢ ᱟᱠᱟᱱᱟ + ᱥᱮᱞᱮᱫ + ᱤᱧ ᱵᱟᱹᱧ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ, ᱡᱟᱦᱟᱸᱱᱟᱜ ᱠᱷᱟᱹᱛᱤᱨ ᱤᱧ ᱵᱟᱹᱧ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ + ᱞᱟᱹᱠᱛᱤᱭᱟᱱ ᱱᱮᱴᱣᱟᱨᱠ ᱡᱚᱲᱟᱣ + ᱡᱟᱦᱟᱱ ᱱᱮᱴᱣᱟᱨᱠ + ᱥᱩᱯᱩᱨ + ᱰᱷᱮᱨ ᱵᱟᱪᱷᱟᱣ \ No newline at end of file diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index b9178cf87..a497844ab 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -316,7 +316,7 @@ Impossìbile recuperare custu iscarrigamentu Connessione iscadida Su progressu s\'est pèrdidu, ca su documentu est istadu iscantzelladu - Perunu ispàtziu abarradu in su dispositivu + Perunu ispàtziu abarradu in su dispositivu NewPipe est istadu serradu in su mentres chi fiat traballende a su documentu Post-protzessamentu fallidu No agatadu @@ -652,7 +652,7 @@ Postu in lista comente imbeniente Pone in lista comente imbeniente Protzessende… Bi diat pòdere chèrrere unu pagu de tempus - Chirca agiornamentos + Chirca agiornamentos Verìfica in manera manuale pro versiones noas Controllende sos agiornamentos… NewPipe at rilevadu un\'errore, toca pro lu sinnalare diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index cbc248b8b..85f248b11 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -23,4 +23,11 @@ ඇතුලත් කරන්න මෙයට චලන රූප භාගත කරන ස්තානය චලන රූප ගබඩා කිරීමට ස්තානයක් තෝරන්න + උත්පතන මාදිලියේ විවෘත කරන්න + සමඟ බෙදාගන්න + බාහිර ශ්‍රව්‍ය වාදකය භාවිතා කරන්න + සමහර විභේදනවලදී හඬ ඉවත් කරයි + ප්‍රවාහ වාදකයක් හමු නොවීය (ඔබට එය වාදනය කිරීමට VLC ස්ථාපනය කළ හැක). + ප්‍රවාහ ගොනුව බාගන්න + ප්‍රවාහ වාදකයක් හමු නොවීය. VLC ස්ථාපනය කරන්නද? \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 3ad8a7023..e3aa5b6bc 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -15,15 +15,15 @@ Použiť externý prehrávač zvuku Prevzaté video ukladať do Prevzaté video súbory sú uložené tu - Vyberte priečinok na ukladanie videí - Adresár pre stiahnuté audio - Vyberte adresár pre ukladanie audio súborov + Vyberte priečinok pre stiahnuté video + Priečinok pre stiahnuté audio + Vyberte priečinok pre audio súbory Prevzaté zvukové súbory sú uložené tu Štandardné rozlíšenie Prehrať cez Kodi Nainštalovať chýbajúcu aplikáciu Kore\? Zobraziť možnosť \"Prehrať cez Kodi\" - Zobraziť možnosť \"Prehrať cez Kodi\" + Zobrazí možnosť prehrať video cez mediálne centrum Kodi Zvuk Predvolený zvukový formát Téma @@ -86,7 +86,7 @@ M B Požiadavka reCAPTCHA - Spustiť v okne + Otvoriť vo vyskakovacom okne Tieto práva sú potrebné pre \nprehrávanie v mini okne Preferovaný formát videa @@ -100,11 +100,11 @@ Vyčistiť Odstráni audio pri niektorých rozlíšeniach Zapamätať si parametre mini okna - Zapamätať si posledné nastavenie veľkosti a pozície mini okna + Zapamätá si posledné nastavenie veľkosti a pozície vyskakovacieho okna Hľadať návrhy Vyberte návrhy, ktoré sa majú zobrazovať pri vyhľadávaní Najlepšie rozlíšenie - Odber + Odoberať Odoberané Odber zrušený Nemožno zmeniť odber @@ -112,16 +112,16 @@ Odbery Čo je nové Hľadať v histórií - Hľadané výrazy ukladať lokálne + Hľadané výrazy uloží lokálne História pozretí - Ukladať históriu pozretých videí + Uloží históriu pozretých videí Pokračovať v prehrávaní - Pokračovať po prerušeniach (napr. telefonát) + Pokračouje v prehrávaní po prerušení (napr. po telefonáte) NewPipe oznámenia Notifikácie NewPipe prehrávača Preberanie Povolené znaky v názvoch súborov - Neplatné znaky budú nahradené týmito znakmi + Neplatné znaky budú nahradené znakmi s týmto významom Náhradný znak Písmená a číslice Väčšina špeciálnych znakov @@ -163,12 +163,12 @@ %s videí Nebol nájdený žiadny prehrávač pre stream (môžete si nainštalovať napr. VLC). - Stiahnuť súbor stream + Stiahnuť súbor streamu Zobraziť info Uložené zoznamy Pridať do Zobrazovať tip \"Pridať podržaním\" - Zobraziť tip pre stlačenie tlačidiel \"Pozadie\" alebo \"V okne\" v detailoch videa + Zobrazí tip pri stlačení tlačidiel pozadia alebo vyskakovacieho okna videa \"Podrobnosti:\" Predvolený obsah pre krajinu Prehrať všetko Vždy @@ -179,8 +179,8 @@ Prepnúť na Video Importovať databázu Exportovať databázu - Prepíše aktuálnu históriu, odbery, zoznamy skladieb a (voliteľne aj) nastavenia - Exportovať históriu, odbery, zoznamy skladieb a nastavenia + Prepíše aktuálnu históriu, odbery, zoznamy skladieb a (voliteľne) nastavenia + Exportuje históriu, odbery, zoznamy skladieb a nastavenia Nepodarilo sa prehrať tento stream Pri prehrávaní došlo k chybe a nemožno pokračovať Zotavovanie po chybe v prehrávaní @@ -247,7 +247,7 @@ Rýchly posun umožňuje prejsť na novú pozíciu rýchlejšie, ale s menšou presnosťou. Posun o 5, 15 alebo 25 sekúnd v tomto prípade nie je možný Vyrovnávacia pamäť obrázkov vymazaná Vymazať metadáta uložené vo vyrovnávacej pamäti - Odstrániť všetky údaje webových stránok vo vyrovnávacej pamäti + Odstráni všetky údaje webových stránok vo vyrovnávacej pamäti Vyrovnávacia pamäť metadát bola vymazaná Automaticky zaradiť ďalší stream Končiaci (neopakujúci sa) zoznam prehrávania bude pokračovať súvisiacim streamom @@ -264,9 +264,9 @@ Automaticky vygenerované Titulky Upravte mierku textu titulkov prehrávača a štýly pozadia. Vyžaduje sa reštart aplikácie - Monitorovanie pretečenia pamäte môže spôsobiť, že aplikácia nebude reagovať + Monitorovanie úniku pamäte môže spôsobiť, že aplikácia nebude reagovať Nahlásiť mimo-cyklické chyby - Vynútiť hlásenie výnimiek nedoručiteľných Rx mimo časového cyklu fragmentov alebo aktivity po zneškodnení + Vynúti hlásenie nedoručiteľných výnimiek Rx mimo časového cyklu fragmentov alebo aktivity po zneškodnení Import Importovať z Exportovať do @@ -304,7 +304,7 @@ Vymazať celú históriu pozretí\? História pozretí bola vymazaná Vymazať históriu vyhľadávania - Vymaže históriu vyhľadávania kľúčových slov + Odstráni históriu vyhľadávania kľúčových slov Vymazať celú históriu vyhľadávania\? História vyhľadávaní bola vymazaná 1 položka bola vymazaná. @@ -328,7 +328,7 @@ Krok Vynulovať Minimalizovať pri prepnutí aplikácie - Akcia pri prepnutí na inú aplikáciu z hlavného prehrávača videa — %s + Akcia pri prepnutí do inej aplikácie z hlavného prehrávača videa - %s Nič Prehrávať na pozadí Prehrávať v okne @@ -345,7 +345,7 @@ Obnoviť predvolené nastavenia Chcete obnoviť predvolené hodnoty\? Počet odberateľov nie je k dispozícii - Karty, ktoré sa zobrazujú na hlavnej stránke + Aké karty sa zobrazujú na hlavnej stránke Konferencie Aktualizácie Zobrazí sa výzva na aktualizáciu aplikácie, keď je k dispozícii nová verzia @@ -379,7 +379,7 @@ Post-spracovanie zlyhalo Stop Maximum opakovaní - Maximálny počet pokusov pred zrušením stiahnutia + Maximálny počet pokusov pred zrušením sťahovania Pozastaviť pri prechode na mobilné dáta Je to užitočné pri prechode na mobilné dáta, niektoré sťahovania však nemožno pozastaviť Zobraziť komentáre @@ -389,16 +389,16 @@ Nemožno načítať komentáre Zatvoriť Obnoviť prehrávanie - Obnovenie poslednej pozície prehrávania + Obnoví poslednú pozíciu prehrávania Poradie v zoznamoch - Zobraziť indikátory polohy prehrávania v zoznamoch + Zobrazí indikátory pozície prehrávania v zoznamoch Vymazať údaje Pozície prehrávania boli odstránené Súbor bol presunutý alebo odstránený súbor nemožno prepísať Súbor s rovnakým názvom už čaká na stiahnutie NewPipe bol ukončený počas spracovávania súboru - Máš plnú pamäť + V zariadení už nie je voľné miesto Nemožno pokračovať, súbor bol vymazaný Spojenie vypršalo Chcete vymazať históriu sťahovania alebo odstrániť všetky stiahnuté súbory\? @@ -477,7 +477,7 @@ %d dní Skupiny kanálov - Aktualizované: %s + Zdroj naposledy aktualizovaný: %s Nenačítané: %d Načítavanie zdroja… Spracovávanie zdroja… @@ -493,7 +493,7 @@ Nová Zdroj Interval obnovy zdroja - Čas po ktorom sa kontrola odberu považuje za zastaralú — %s + Čas od poslednej aktualizácie, kedy sa odber považuje za neaktuálny - %s Vždy aktualizovať Načítať z vyhradeného zdroja, ak je k dispozícii Služba je dostupná len pre určité zdroje, zvyčajne je rýchlejšia ale môže vrátiť obmedzený počet položiek alebo neúplné informácie (napr. dĺžka, typ, status) @@ -539,7 +539,7 @@ Zobraziť iba nezoskupené odbery Nikdy Iba na WiFi - Spustiť automatické prehrávanie — %s + Spustí automatické prehrávanie - %s Prehrať zoznam Zatiaľ bez záložiek zoznamu Vyberte zoznam skladieb @@ -547,38 +547,38 @@ Nemožno rozpoznať URL. Otvoriť pomocou inej aplikácie\? Automatický rad Zoznam aktuálneho prehrávača bude prepísaný - Prepnutie prehrávača môže vymazať zoznam + Prepnutie z jedného prehrávača na druhý môže nahradiť vaše poradie Vyžadovať povolenie pred vyčistením zoznamu Nič Bufferuje Náhodné Opakovať V kompaktnom oznámení môžete vybrať najviac tri akcie, ktoré sa majú zobraziť! - Všetky nasledovné upozornenia môžete upraviť poklepaním. Tri z nich môžete vybrať a tieto sa budú zobrazovať v kompaktnom oznámení + Všetky nasledovné oznámenia môžete upraviť poklepaním. Tri z nich môžete vybrať a tieto sa budú zobrazovať v kompaktnom oznámení. Akčné tlačidlo päť Akčné tlačidlo štyri Akčné tlačidlo tri Akčné tlačidlo dva Akčné tlačidlo jedna - Orezať pomer strán videa zobrazovaného v miniatúre z 16:9 na 1:1 + Oreže miniatúru videa zobrazenej v oznámení z pomeru strán 16:9 na 1:1 Orezať pomer strán miniatúry na 1: 1 Zobraziť memory leaks Zaradené do poradia Zaradiť do poradia - Vymazať cookies, ktoré NewPipe ukladá, keď vyriešite reCAPTCHA + Vymaže cookies, ktoré NewPipe ukladá, keď vyriešite reCAPTCHA reCAPTCHA cookies boli vymazané Vymazať cookies reCAPTCHA YouTube poskytuje \"Obmedzený režim\", ktorý skrýva potenciálny obsah pre dospelých - Zobraziť obsah, ktorý je možno nevhodný pre deti, pretože má vekovú hranicu (napríklad 18+) - Nechajte Android, aby prispôsobil farbu upozornenia podľa hlavnej farby v miniatúre (nemusí to fungovať na všetkých zariadeniach) + Zobrazí obsah, ktorý je pravdepodobne nevhodný pre deti, pretože má vekové obmedzenie (napríklad 18+) + Nechajte Android, aby prispôsobil farbu oznámenia podľa hlavnej farby v miniatúre (nemusí to fungovať na všetkých zariadeniach) Farby upozornení Zobrazovať miniatúru - Používať miniatúru ako pozadie pri uzamknutej obrazovke a v upozorneniach + Použije miniatúru pre pozadie uzamknutej obrazovke a oznámenia Počítanie hash - Upozornenie pri generovaní hash z názu videa + Oznámenia o priebehu hašovania videa Oznámenie o hashovaní videa Nedávne - Vypnite, pokiaľ nechcete zobrazovať meta informácie ako autor streamu, obsah streamu alebo vyhľadávanie + Vypnutím skryjete metainformačné polia s ďalšími informáciami o autorovi streamu, obsahu streamu alebo požiadavke na vyhľadávanie Zobrazovať meta informácie Kapitoly Popis @@ -605,7 +605,7 @@ Zobraziť podrobnosti kanála Vyrieš Nočná téma - Ak vám video pri prehrávaní seká alebo sa zobrazuje čierna obrazovka zakážte tunelovanie médií + Ak sa video pri prehrávaní seká alebo sa objaví čierna obrazovka, vypnite tunelovanie médií. Zakázať tunelovanie médií Interné Súkromné @@ -631,8 +631,8 @@ \nChcete sa odhlásiť z odberu tohto kanála\? Nemožno načítať informačný kanál \'%s\'. Chyba pri načítaní kanála - \'Storage Access Framework\' je podporovaný len od Androidu 10 a vyššie - Pri každom sťahovaní sa zobrazí výzva kam uložiť súbor + Od Androidu 10 je podporovaný len \'Storage Access Framework\' + Pri každom sťahovaní sa zobrazí výzva, kam uložiť súbor Nie je nastavený adresár na sťahovanie, nastavte ho teraz Označiť ako videné Vypnuté @@ -653,18 +653,18 @@ Nízka kvalita (menšie) Vysoká kvalita (väčšie) Náhľad miniatúry pri vyhľadávaní - Zobraziť farebné stužky Picassa na obrázkoch podľa ich zdroja: červená pre sieť, modrá pre disk a zelená pre pamäť + Zobrazí farebné pásiky Picasso na obrázkoch podľa ich zdroja: červený pre sieť, modrý pre disk a zelený pre pamäť Zobraziť indikátory obrázka Potiahnutím vymazať Komentáre sú zakázané - Pri zamknutej auto-rotácií nespúšťať videá v mini prehrávači, ale prepnúť sa priamo do režimu celej obrazovky. Prístup k mini prehrávaču bude po ukončení režimu celej obrazovky + Ak je automatické otáčanie zablokované, nespustí videá v miniprehrávači, ale prepne sa do celoobrazovkového režimu. Do miniprehrávača sa dostanete po ukončení režimu celej obrazovky Hlavný prehrávač na celej obrazovke Návrhy vzdialeného vyhľadávania Miestne návrhy vyhľadávania Pridať do zoznamu Ďaľší v poradí Spracovávanie... môže to chvíľku trvať - Skontrolovať aktualizácie + Skontrolovať aktualizácie Ručne skontrolovať nové verzie Kontrolujú sa aktualizácie… Nové položky informačného kanála @@ -689,13 +689,13 @@ Vymazať všetky stiahnuté súbory z disku\? Upozornenia sú vypnuté Upozornenie z prehrávača - Nastavte notifikáciu aktuálneho prehrávania + Nastavte oznámenie aktuálneho streamu prehrávania Je vyžadované pripojenie na internet Začali ste odoberať tento kanál , Zapnúť všetko Nové streamy - Upozornenia na nové streamy v odberoch + Oznámenia o nových streamoch v odberoch %s nový stream %s nové streamy @@ -703,7 +703,7 @@ Skontrolovať nové streamy Upozornenia na nové streamy - Upozorniť na nové streamy z odberov + Upozorní na nové streamy z odberov Akákoľvek sieť Dostávať upozornenia Poltón @@ -732,9 +732,9 @@ Rýchly režim Import alebo export odberov z 3-bodkovej ponuky Akcia gesta vľavo - Akcia pre gesto v pravej polovici obrazovky + Vyberte gesto pre pravú polovicu obrazovky prehrávača Akcia gesta vpravo - Akcia pre gesto v ľavej polovici obrazovky + Vyberte gesto pre ľavú polovicu obrazovky prehrávača Jas Hlasitosť Nič @@ -742,11 +742,11 @@ Odstrániť duplikáty\? Rozpozerané Dopozerané - Toto sa môže hodiť v prípade, že máte pokazené tlačidlo na slúchadlách + Toto sa môže hodiť v prípade, že máte pokazené tlačidlá na slúchadlách Ignorovať fyzické tlačidlá - Zmena veľkosti intervalu načítania pri progresívnom obsahu (v súčasnosti %s). Nižšia hodnota môže urýchliť ich počiatočné načítanie + Zmení veľkosť intervalu načítania pri progresívnom obsahu (v súčasnosti %s). Nižšia hodnota môže urýchliť ich počiatočné načítanie Preferovať pôvodné audio - Vyberať pôvodnú zvukový stopu bez ohľadu na nastavený jazyk + Vyberte pôvodnú zvukovú stopu bez ohľadu na jazyk Preferovať prehrávanie popisu Zvuk: %s Zvuková stopa @@ -756,41 +756,41 @@ Výber zvukovej stopy pre externé prehrávače Neznáma Nadchádzajúce - Úprava nastavení prehrávača ExoPlayer. Pre aplikovanie týchto zmien je nutné reštartovať ExoPlayer + Úprava niektorých nastavení prehrávača ExoPlayer. Pre aplikovanie týchto zmien je potrebné reštartovať prehrávač Túto možnosť povoľte, ak máte problémy s inicializáciou dekodéra, ktorý sa v prípade zlyhania inicializácie primárnych dekodérov vráti k dekodérom s nižšou prioritou. To môže mať za následok nižší výkon prehrávania ako pri použití primárnych dekodérov - Používať funkciu záložného dekodéra prehrávača ExoPlayer + Použiť funkciu záložného dekodéra prehrávača ExoPlayer Vždy použiť nastavenia výstupu videa ExoPlayera %1$s %2$s povodná dabovaná - Vyberať zvukovú stopu s popisom pre zrakovo postihnutých ak je k dispozicií + Vyberte zvukovú stopu s popisom pre osoby so zrakovým postihnutím, ak je k dispozícii Zobrazovať/Skrývať streamy Nastavenia ExoPlayer - Toto riešenie spôsobí to, že pri zmene textúry sa aktuálne používaný kodek uvoľní a potom znova použije, miesto toho aby sa textúra rovno použila na kodek. Toto nastavenie, ktoré už ExoPlayer používa na niektorých zariadeniach s týmto problémom, má vplyv len na Android 6 a vyšší + Toto riešenie uvoľní a znovu nastaví videokodeky pri zmene rozhrania namiesto priameho nastavenia rozhrania kodeku. Nastavenie už používa ExoPlayer na niektorých zariadeniach s týmto problémom a týka sa len systému Android 6 a vyššie \n -\nPovolenie tejto možnosti môže zabrániť chybám pri prehrávaní pri prepínaní aktuálneho prehrávača videa alebo prepínaní na celú obrazovku +\nPovolením tejto možnosti môžete zabrániť chybám prehrávania pri zmene aktuálneho prehrávača videa alebo pri prepnutí do režimu celej obrazovky popisná Videá Odberatelia - Karty zobrazované na stránkach kanála + Aké karty sa zobrazujú na stránkach kanála Karty kanálov Krátke Načítavanie metadát… Prepnúť orientáciu obrazovky - Pozícia hlavných kariet + Poloha hlavných kariet Prepnúť na celú obrazovku Načítať karty kanála - Ďalší prúd + Ďalší stream Vaše zariadenie nepodporuje tunelovanie medií a preto bolo tunelovanie medií zakázané. Otvoriť zoznam prehrávania O kanále Dopredu Albumy Dozadu - Opakovať - Karty na načítanie pri aktualizácii informačného kanála. Táto možnosť nemá žiadny účinok, ak je kanál aktualizovaný pomocou rýchleho režimu. + Opäť prehrať + Karty, ktoré sa majú načítať pri aktualizácii informačného kanála. Táto možnosť nemá žiadny účinok, ak je kanál aktualizovaný pomocou rýchleho režimu. Zoznamy skladieb - Presunúť lištu kariet dolu + Presunie výber hlavnej karty do spodnej časti Žiadne živé prenosy Prehrať Ďalšie možnosti @@ -798,6 +798,44 @@ Dĺžka Žiadne videá Kanály - Predchádzajúci prúd + Predchádzajúci stream Naživo + Kvalita obrázkov + \? + Nízka kvalita + Avatary + Nenačítavať obrázky + Vysoká kvalita + Zdieľať playlist + Stredná kvalita + Bannery + Vyberte kvalitu obrázkov a či sa majú obrázky vôbec načítať, aby sa znížila spotreba dát a pamäte. Zmeny vymažú vyrovnávaciu pamäť obrázkov v pamäti a na disku - %s + Zobraziť viac + Zobraziť menej + Každú akciu oznámenia upravte poklepaním. Prvé tri akcie (prehrať/pozastaviť, predchádzajúce, ďalšie) sú nastavené systémom a nie je možné ich prispôsobiť. + Náhľady + Avatary uploadera + Sub-kanálové avatary + Zdieľať s názvami + Zdieľať URL playlistu + + %s odpoveď + %s odpovede + %s odpovedí + + Zdieľajte playlist s podrobnosťami, ako je jeho názov a názvy videí, alebo ako jednoduchý zoznam URL adries videí + - %1$s: %2$s + %1$s +\n%2$s + Nedostatok voľného miesta v zariadení + Obnoví všetky nastavenia na pôvodné hodnoty + Obnoviť nastavenia + Záloha a obnovenie + Obnovením nastavení sa zrušia všetky preferované nastavenia a aplikácia sa reštartuje. +\n +\nSte si istí, že chcete pokračovať? + Nie + Áno + NewPipe môže z času na čas automaticky kontrolovať nové verzie a upozorniť vás, keď budú k dispozícii. +\nChcete to povoliť? \ No newline at end of file diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 4912db031..b0fcec406 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -211,7 +211,7 @@ Počisti zgodovino prenosa Ni mogoče povrniti prenos Napredek je izgubljen, ker je bila datoteka izbrisana - Ni več prostora v vaši napravi + Ni več prostora v vaši napravi NewPipe se je zaprl medtem ko je delal z datoteko Ni najden Strežnik ne pošilja informacij diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index 247ff682a..e37c3a36d 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -520,7 +520,7 @@ Dajintan lama hagaajin karo Ku xidhidii ayaa wakhtigii ka dhacay Hawshii socotay way kala kacday, sababtoo ah shayga waa lala saaray - Meel banaan oo wax lagu kaydiyo aalaada kuma hadhin + Meel banaan oo wax lagu kaydiyo aalaada kuma hadhin NewPipe waxaa la xidhay asagoo fayl ka shaqaynaya Habayntii way guuldareystay Lama helin diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index dc8ca02f4..5196d606a 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -82,7 +82,7 @@ Nuk mund të rikuperohet ky shkarkim Koha e lidhjes skadoi Progresi humbi, pasi skedari është fshirë - Nuk ka vend bosh në pajisje + Nuk ka vend bosh në pajisje NewPipe u mbyll ndërkohë që po punohej mbi skedarin Procesimi dështoi Nuk u gjet diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 8bb8d1b3d..996b3fa2e 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -227,7 +227,7 @@ Ограничи ред за преузимање Затвори Корисно при преласку на мобилне податке, иако нека преузимања не могу бити обустављена - Прекид на мереним мрежама + Прекид на мрежама с ограничењем Највећи број покушаја пре отказивања преузимања Највише покушаја Заустави @@ -237,7 +237,7 @@ Није могуће опоравити ово преузимање Веза је истекла Напредак је изгубљен, јер је фајл избрисан - Недовољно меморије на уређају + Недовољно меморије на уређају NewPipe је затворен док је радио на фајлу Накнадна обрада није успела Није пронађено @@ -280,7 +280,7 @@ Умањи при мењању апликације Прикажи обавештење када постоји нова верзија апликације Ажурирања - Ограничи резолуцију када користиш мобилне податке + Ограничење резолуције када се користе мобилни подаци Одбиј Без ограничења Прихвати @@ -448,7 +448,7 @@ Очисти историју гледања Чисти колачиће које NewPipe чува када решите „reCAPTCHA“ Извоз историје, праћења, плејлисти и подешавања - Замениће вашу тренутну историју, праћења, плејлисте и (опционо) подешавања + Замениће вашу тренутну историју, праћења, плејлисте и (опционално) подешавања „reCAPTCHA“ колачићи су очишћени Очисти „reCAPTCHA“ колачиће Извоз базе података @@ -485,7 +485,7 @@ Није могуће потврдити инстанцу Унесите URL адресу инстанце Додајте инстанцу - Пронађите инстанце које Вам се свиђају на %s + Пронађите инстанце које вам се свиђају на %s PeerTube инстанце Изаберите своје омиљене PeerTube инстанце URL адреса није препозната. Отворити помоћу друге апликације\? @@ -521,7 +521,7 @@ Дугме треће радње Дугме друге радње Дугме прве радње - Исеците сличицу видеа приказану у обавештењу с размере 16:9 на размеру 1:1 + Исеците сличицу видео снимка приказану у обавештењу с размере 16:9 на размеру 1:1 Исеци сличицу на размеру 1:1 Приказивање резултата за: %s Отвори помоћу @@ -572,9 +572,9 @@ Желите ли да избришете ову групу\? Празан назив групе - %d одабран - %d одабрана - %d одабраних + %d изабран + %d изабрана + %d изабраних Нема изабраних праћења Изабрана праћења @@ -715,11 +715,11 @@ Плејлисте које су затамњене већ садрже ову ставку. LeakCanary није доступан Додирните да бисте преузели %s - Провери ажурирања + Провери ажурирања Преглед сличице траке за претрагу Прикажи следеће стримове Прикажи/сакриј стримове - Користите резервну функцију декодера ExoPlayer-а + Користи резервну функцију декодера ExoPlayer-а оригинални Промените величину интервала учитавања на прогресивним садржајима (тренутно %s). Нижа вредност може убрзати њихово почетно учитавање Корисно, на пример, ако користите слушалице са поквареним физичким дугмадима @@ -731,7 +731,7 @@ Конфигуришите обавештење о тренутно репродукованом стриму Изаберите оригинални аудио снимак, без обзира на језик Изаберите аудио снимак са описима за особе са оштећеним видом, ако је доступан - Дај предност описном аудио снимку + Преферирај описни аудио снимак Радња покретом удесно Осветљеност Јачина звука @@ -764,7 +764,7 @@ описни синхронизовани Управљајте неким подешавањима ExoPlayer-а. Ове промене захтевају рестартовање плејера да би се примениле - Дај предност оригиналном аудио снимку + Преферирај оригинални аудио снимак Ово заобилазно решење ослобађа и поново инстанцира кодеке видео снимка када дође до промене површине, уместо да се површина директно поставља на кодек. ExoPlayer већ користи ово на неким уређајима са овим проблемом, ово подешавање утиче само на Android 6 и новије верзије. \n \nОмогућавање ове опције може спречити грешке при репродукцији приликом пребацивања тренутног плејера видео снимака или преласка на цео екран @@ -819,4 +819,19 @@ Дели плејлисту Делите плејлисту са детаљима, као што су назив плејлисте и наслови видео снимака или као једноставна листа URL адреса видео снимака -%1$s: %2$s + + %s одговор + %s одговора + %s одговора + + Прикажи више + Прикажи мање + Измените сваку радњу обавештења у наставку тако што ћете је додирнути. Прве три радње (пусти/паузирај, претходни и следећи) поставља систем и не могу се прилагодити. + Нема довољно слободног меморијског простора на уређају + Прављење резервне копије и враћање + Ресетуј подешавања + Ресетујте сва подешавања на подразумеване вредности + Ресетовање свих подешавања ће одбацити сва жељена подешавања и поново покренути апликацију. +\n +\nЖелите ли заиста да наставите? \ No newline at end of file diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index d3739e29e..54aab0cdf 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -503,7 +503,7 @@ Kan inte återställa den här hämtningen Anslutnings avbrott Framsteg förlorat, för att filen blev borttagen - Inget utrymme kvar på enhet + Inget utrymme kvar på enhet NewPipe stängdes under arbete med en fil Vänligen kontrollera om en felrapport som tar upp din krasch redan finns. Att skapa ärenden när en felrapport redan finns, tar av den tid som vi annars kunde ha ägnat åt att fixa den faktiska buggen. Rapportera på GitHub @@ -567,7 +567,7 @@ Inaktivera för att dölja videobeskrivning och ytterligare information Visa meta-information Använd miniatyrbild som bakgrund till både låsskärm och aviseringar - Redigera varje aviseringsåtgärd nedan genom att trycka på den. Välj upp till tre av dem som ska visas i den kompakta aviseringen genom att använda kryssrutorna till höger + Redigera varje aviseringsåtgärd nedan genom att trycka på den. Välj upp till tre av dem som ska visas i den kompakta aviseringen genom att använda kryssrutorna till höger. Låt Android färglägga varje avisering individuellt utifrån miniatyrbildens huvudsakliga färg (notera att detta inte är tillgängligt på alla enheter) Färglägg avisering Du kan som mest välja tre åtgärder att visa i den kompakta aviseringen! @@ -652,7 +652,7 @@ Förslag via fjärrsökning Starta inte videor i minispelaren, utan byt till helskärmsläge direkt, om automatisk rotation är låst. Du kan fortfarande komma åt minispelaren genom att gå ut ur helskärmsläge Visa Picasso färgade band ovanpå bilderna som anger deras källa: rött för nätverk, blått för disk och grönt för minne - Sök efter uppdateringar + Sök efter uppdateringar Kolla manuellt efter nya versioner Söker efter uppdateringar… Nya flödes objekt @@ -806,4 +806,12 @@ - %1$s: %2$s Dela spellistan med detaljer så som spellistans namn och video-titlarna eller som en enkel lista med URL till videorna Välj bildkvalitet och om bilder överhuvudtaget ska laddas för att minska data och minnesanvändningen. Ändringar rensar både i minnet och bildcache på disk – %s + Visa mer + + %s svar + %s svar + + Visa mindre + Redigera varje aviseringsåtgärd nedan genom att trycka på den. De tre första åtgärderna (spela/pausa, föregående och nästa) är satta av systemet och kan inte ändras. + Inte tillräckligt med ledigt utrymme på enheten \ No newline at end of file diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 9b97445e1..73cc5a3da 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -145,8 +145,8 @@ %s பார்வைகள் - காணொளிகள் - காணொளிகள் + %sகாணொளிகள் + %sகாணொளிகள் முதற்பக்கத்துக்கு மாற்று எந்த காணொலியும் இல்லை diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index aaf1bf4cb..b2846823c 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -146,7 +146,7 @@ పూర్తి స్క్రీన్‌లో ప్రధాన ప్లేయర్‌ని ప్రారంభించండి డీబగ్ చేయండి ఏమిలేదు - నోటిఫికేషన్‌లో చూపబడిన వీడియో థంబ్‌నెయిల్‌ను 16:9 నుండి 1:1 కారక నిష్పత్తికి స్కేల్ చేయండి (వక్రీకరణలను ప్రవేశపెట్టవచ్చు) + నోటిఫికేషన్‌లో చూపబడిన వీడియో థంబ్‌నెయిల్‌ను 16:9 నుండి 1:1 కారక నిష్పత్తికి మార్చుము పునరావృతం చేయండి వీడియో \"వివరాలు:\"లో బ్యాక్‌గ్రౌండ్ లేదా పాప్‌అప్ బటన్‌ను నొక్కినప్పుడు చిట్కాను చూపు URLని గుర్తించడం సాధ్యపడలేదు. మరొక యాప్‌తో తెరవాలా\? @@ -177,7 +177,7 @@ ట్యాబ్‌ని ఎంచుకోండి సభ్యత్వాన్ని తొలగించుము సమాచారాన్ని చూపుము - సూక్ష్మచిత్రాన్ని 1:1 కారక నిష్పత్తికి స్కేల్ చేయండి + సూక్ష్మచిత్రాన్ని 1:1 కారక నిష్పత్తికి మార్చుము ఒక ప్లేయర్ నుండి మరొక ప్లేయర్‌కు మారడం వలన మీ క్యూను భర్తీ చేయవచ్చు సంబంధిత స్ట్రీమ్‌ను జోడించడం ద్వారా (పునరావృతం కాని) ప్లేబ్యాక్ క్యూను ముగించడాన్ని కొనసాగించండి దిగువన ఉన్న ప్రతి నోటిఫికేషన్ చర్యను దానిపై నొక్కడం ద్వారా సవరించండి. కుడివైపు ఉన్న చెక్‌బాక్స్‌లను ఉపయోగించడం ద్వారా కాంపాక్ట్ నోటిఫికేషన్‌లో చూపబడే వాటిలో మూడు వరకు ఎంచుకోండి @@ -201,7 +201,7 @@ షఫుల్ చేయండి బఫరింగ్ డిఫాల్ట్ వీడియో ఫార్మాట్ - రాత్రి థీమ్ + చీకటి థీమ్ చీకటి ఫాస్ట్-ఫార్వర్డ్/-రివైండ్ సీక్ వ్యవధి క్యూను క్లియర్ చేయడానికి ముందు నిర్ధారణ కోసం అడగండి @@ -462,4 +462,11 @@ ఆగిపోయింది క్రమం %sని డౌన్‌లోడ్ చేయడానికి నొక్కండి + దిగువన ఉన్న ప్రతి నోటిఫికేషన్ చర్యను దానిపై నొక్కడం ద్వారా సవరించండి. మొదటి మూడు చర్యలు (ప్లే/పాజ్, మునుపటి మరియు తదుపరి) సిస్టమ్ ద్వారా సెట్ చేయబడ్డాయి మరియు అనుకూలీకరించబడవు. + భాషతో సంబంధం లేకుండా అసలు ఆడియో ట్రాక్‌ని ఎంచుకోండి + వివరణాత్మక ఆడియోకు ప్రాధాన్యత ఇవ్వండి + ఉదాహరణకు, మీరు విరిగిన భౌతిక బటన్‌లతో హెడ్‌సెట్‌ని ఉపయోగిస్తుంటే ఉపయోగకరంగా ఉంటుంది + ప్రగతిశీల విషయాలపై లోడ్ విరామం పరిమాణాన్ని మార్చండి (ప్రస్తుతం %s). తక్కువ విలువ వాటి ప్రారంభ లోడింగ్‌ను వేగవంతం చేయవచ్చు + హార్డ్‌వేర్ మీడియా బటన్ ఈవెంట్‌లను విస్మరించండి + అసలు శ్రావ్యయానికి ప్రాధాన్యత ఇవ్వండి \ No newline at end of file diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml index ab3505453..7845242c2 100644 --- a/app/src/main/res/values-ti/strings.xml +++ b/app/src/main/res/values-ti/strings.xml @@ -47,7 +47,7 @@ ፖፕኣፕ ትሑዝ ድንቀት ዝወረዱ ናይ ተንቃሳቀሴ-ምስሌ ፋይላት ኣብዚ ይኽዘኑ - + ናይ እኽሊ ምስሊ ምስ ናይ 1:1 ርክባት ቀዳማይ ወሰን ተጠዋቃ ደገመ @@ -62,4 +62,9 @@ ተንቃሳቀሴ-ምስሌ ፋይል ኣራግፍ ታብ ምረጽ ድሕረ ባይታ + ብርሃን + ጸልማት + ካልኣይ ስጉምቲ መጠወቒ + ሳልሳይ ስጉምቲ መጠወቒ + ሓሙሻይ ስጉምቲ መጠወቒ \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index b1ecacfc3..adecbf3ff 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -2,7 +2,7 @@ Başlamak için büyütece dokunun. %1$s tarihinde yayınlandı - Akış oynatıcısı bulunamadı. VLC kurulsun mu\? + Video oynatıcısı bulunamadı. VLC kurulsun mu? Kur İptal Tarayıcıda aç @@ -25,7 +25,7 @@ Eksik Kore uygulaması yüklensin mi\? \"Kodi ile oynat\" seçeneğini göster Kodi ortam merkezi üzerinden video oynatmak için bir seçenek göster - Öntanımlı ses biçimi + Varsayılan ses biçimi Tema Koyu Açık @@ -77,11 +77,11 @@ Panoya kopyalandı Lütfen daha sonra ayarlardan uygun indirme klasörü belirleyin İndirme menüsü ayarlanamadı - Açılır pencere kipinde aç - Öntanımlı açılır pencere çözünürlüğü + Açılır pencere modunda aç + Varsayılan açılır pencere çözünürlüğü Yüksek çözünürlükleri göster Yalnızca bazı aygıtlar 2K/4K videoları oynatabilir - Öntanımlı video biçimi + Varsayılan video biçimi Siyah Açılır pencere kipinde oynatılıyor Tümü @@ -190,7 +190,7 @@ Ana Görünüme Geç Çekmeceyi Aç Çekmeceyi Kapat - Akış oynatıcı bulunamadı (Oynatmak için VLC kurabilirsiniz). + Video oynatıcı bulunamadı (Oynatmak için VLC kurabilirsiniz). Her Zaman Yalnızca Bir Kez Dış oynatıcılar bu tür bağlantıları desteklemez @@ -243,7 +243,7 @@ Parçanın dışında veya atımdan sonraki etkinlik yaşam döngüsündeki teslim edilemeyen Rx beklentilerinin bildirimini zorla Hızlı isabetsiz konumlama kullan İsabetsiz konumlama, oynatıcının daha düşük hassasiyetle daha hızlı konumlamasını sağlar. 5, 15 veya 25 saniyeye konumlama bununla birlikte çalışmamaktadır - Sonraki akışı kuyruğa kendiliğinden ekle + Sonraki videoyu kuyruğa otomatik ekle İlgili bir akış ekleyerek sonlanacak (yinelemeyen) oynatma sırasını sürdür Dosya Böylesi klasör yok @@ -280,7 +280,7 @@ \n \nSürdürmek istiyor musunuz\? Resim önbelleği temizlendi - Önbelleğe alınmış üstverileri temizle + Önbelleğe alınmış meta verileri silin Önbelleğe alınmış tüm web sayfası verilerini kaldır Üstveri önbelleği temizlendi Oynatım Hızı Denetimleri @@ -390,7 +390,7 @@ dosyanın üzerine yazılamaz Bu isim ile bekleyen bir indirme var NewPipe bu dosya üzerinde çalışırken kapandı - Aygıt üzerinde yer yok + Aygıt üzerinde yer yok İlerleme kaybedildi, çünkü dosya silinmiş Bağlantı zaman aşımı İndirme geçmişinizi temizlemek veya indirilen tüm dosyaları silmek istiyor musunuz\? @@ -423,7 +423,7 @@ %s dinleyici Uygulama yeniden başlatıldıktan sonra dil değişecektir - Hızlı ileri/geri konumlama süresi + Hızlı ileri/geri atlama süresi PeerTube örnekleri Favori PeerTube örneklerinizi seçin %s adresinde beğendiğiniz örnekleri bulun @@ -474,8 +474,8 @@ Abonelikleri seç Abonelik seçilmedi - %d öğe seçildi - "%d öğe seçildi" + %d öge seçildi + %d öge seçildi Boş grup adı Bu grubu silmek istiyor musunuz\? @@ -534,16 +534,16 @@ Oynatmayı kendiliğinden başlat — %s Oynatma kuyruğu URL tanınamadı. Başka bir uygulamayla açılsın mı\? - Kendiliğinden kuyruğa alış + Otomatik kuyruğa alış Etkin oynatıcının kuyruğu değiştirilecek Bir oynatıcıdan diğerine geçmek kuyruğunuzu değiştirebilir - Bir kuyruğu temizlemeden önce onay iste + Bir sırayı temizlemeden önce onay iste Hiçbir şey Ara belleğe alınıyor Karıştır - Tekrarla + Yinele Bildirim sekmesinde gösterilecek en fazla üç eylem seçebilirsiniz! - Aşağıdaki bildirim eyleminlerini üzerine dokunarak düzenleyin. Sağdaki onay kutularını kullanarak sıkışık bildirimde gösterilecek üç eylemi seçin + Aşağıdaki bildirim eyleminlerini üzerine dokunarak düzenleyin. Sağdaki onay kutularını kullanarak üst bildirimde gösterilecek üç eylemi seçin. Beşinci eylem düğmesi Dördüncü eylem düğmesi Üçüncü eylem düğmesi @@ -632,7 +632,7 @@ Yüksek nitelik (geniş) Yorumlar devre dışı Yaratıcısınca kalplendi - İzlendi olarak imle + İzlendi olarak işaretle Resimlerin üzerinde kaynaklarını gösteren Picasso renkli şeritler göster: ağ için kırmızı, disk için mavi ve bellek için yeşil Resim göstergelerini göster Uzak arama önerileri @@ -651,7 +651,7 @@ Sonrakini sıraya ekle Sonraki sıraya eklendi İşleniyor… Biraz zaman alabilir - Güncellemeleri denetle + Güncellemeleri denetle Yeni sürümleri el ile denetleyin Güncellemeler denetleniyor… Yeni akış ögeleri @@ -667,7 +667,7 @@ \nLütfen Depolama Erişimi Çerçevesi uyumlu dosya yönetici kurun Hata raporları için bildirimler Oynatıcı kullanırken çöktürme seçeneği gösterir - Oynatıcıyı çöktür + Oynatıcıyı çökert Sabitlenmiş yorum LeakCanary yok ExoPlayer öntanımlısı @@ -732,7 +732,7 @@ Ses düzeyi Hiçbiri Parmağınızı ekranın sol yarısında kaydırırken ne yapılacağını seçin - Varsa, görsel engelli kişiler için betimlemeli ses parçasını seç + Varsa, görsel engelli kişiler için betimli ses parçasını seç Bilinmeyen ExoPlayer ayarları Kimi ExoPlayer ayarlarını yönet. Bu değişiklikler oynatıcının yeniden başlatılmasını gerektirir @@ -741,15 +741,15 @@ betimlemeli %1$s %2$s Dış oynatıcılar için ses parçası seç - Dil ne olursa olsun özgün ses parçasını seç + Dilden bağımsız olarak orijinal ses parçasını seçin Ses: %s - Özgün sesi yeğle - Betimlemeli sesi yeğle + Orijinal sesi tercih edin + Açıklayıcı sesi tercih edin Ses parçası Bu akışta halihazırda bir ses parçası bulunmalıdır Akış yok Canlı akış yok - Sağ kaydırma eylemi + Sağ hareket işlemi Ana sekme seçiciyi alta taşı Ana sekme konumu Ortam tünelleme öntanımlı olarak devre dışı bırakılmıştır çünkü aygıt modelinizin desteği bilinmemektedir. @@ -759,7 +759,7 @@ \n \nBu ayarı etkinleştirmek, geçerli video oynatıcısını değiştirirken veya tam ekrana geçerkenki oynatma hatalarını önleyebilir Progresif içeriklerdeki yükleme aralığı boyutunu değiştir (şu anda %s). Daha düşük değer ilk yüklemeyi hızlandırabilir - Sol kaydırma eylemi + Sol hareket işlemi Parmağınızı ekranın sağ yarısında kaydırırken ne yapılacağını seçin ExoPlayer\'ın çözücü yedek özelliğini kullan Resim niteliği @@ -806,4 +806,22 @@ Kanallar Önceki akış Canlı + Daha fazla göster + Daha az göster + Aşağıdaki her bir bildirim eylemini üzerine dokunarak düzenleyin. İlk üç eylem (oynat/duraklat, önceki ve sonraki) sistem tarafından ayarlanır ve özelleştirilemez. + + %s yanıt + %s yanıt + + Aygıtta yeterli boş alan yok + Yedekle ve geri yükle + Ayarları sıfırla + Tüm ayarları öntanımlı değerlerine sıfırlayın + Tüm ayarları sıfırlamak, tercih edilen tüm ayarlarınızı kaldırır ve uygulamayı yeniden başlatır. +\n +\nDevam etmek istediğinizden emin misiniz? + Evet + Hayır + NewPipe zaman zaman yeni sürümleri kendiliğinden denetleyebilir ve kullanılabilir olduklarında sizi bilgilendirebilir. +\nBunu etkinleştirmek istiyor musunuz? \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 16212c933..fa63499ea 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -398,7 +398,7 @@ не можу перезаписати файл Завантаження з такою назвою вже додано в чергу NewPipe був закритий під час роботи над файлом - На пристрої не залишилося вільного місця + На пристрої не залишилося вільного простору Прогрес втрачено через видалення файлу Час очікування з\'єднання вичерпано Очистити історію завантажень чи завантажені файли\? @@ -605,7 +605,7 @@ Кольорувати повідомлення основним кольором мініатюри. Підтримується не всіма пристроями Кольорове сповіщення У компактному сповіщенні є не більше трьох дій! - Дії можна змінити, натиснувши на них. Позначте не більше трьох для показу в компактному сповіщенні + Відредагуйте кожну дію сповіщення, натиснувши на неї. Виберіть до трьох з них, які будуть показані в компактному сповіщенні, за допомогою прапорців праворуч. Обрізати мініатюру відео показувану в сповіщенні з пропорцій 16: 9 до 1:1 Вимкнення тунелювання медіаданих за наявності чорного екрана або гальмування під час відтворення відео. Вимкнути тунелювання медіа @@ -657,7 +657,7 @@ Видалено %1$s завантажень - Завантаження завершено + %s завантаження завершено %s завантаження завершено %s завантажень завершено %s завантажень завершено @@ -668,7 +668,7 @@ Заплановано наступним Запланувати наступним Обробка… Трохи заждіть - Перевірити наявність оновлень + Перевірити наявність оновлень Перевірка нових версій вручну Перевірка оновлень… Нові записи стрічки @@ -823,4 +823,14 @@ Поділитися добіркою Поділитися добіркою з подробицями, такими як назва добірки та назви відео, або просто списком URL-адрес відео - %1$s: %2$s + Показати більше + Відредагуйте кожну дію сповіщення, натиснувши на неї. Перші три дії (відтворення/пауза, попередній і наступний) встановлюються системою і не можуть бути змінені. + + %s відповідь + %s відповіді + %s відповідей + %s відповідей + + Показати менше + Недостатньо вільного простору на пристрої \ No newline at end of file diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 1cd5de013..d70927121 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -390,7 +390,7 @@ فائل برتحریر نہیں کر سکتا اس نام کے ساتھ ڈاؤن لوڈ زیر التوا ہے فائل پر کام کرنے کے دوران نیو پائپ بند کردی گئی تھی - آلہ میں کوئی جگہ نہیں بچی + آلہ میں کوئی جگہ نہیں بچی پیشرفت ختم ہوگئی، کیونکہ فائل کو حذف کردیا گیا تھا رابطے کا وقت ختم کیا آپ اپنی ڈاؤن لوڈ کی سرگزشت کو صاف کرنا چاہتے ہیں یا ڈاؤن لوڈ کی گئی تمام فائلوں کو حذف کرنا چاہتے ہیں؟ diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index f6719c1ed..dcb8d2f84 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -10,30 +10,30 @@ Chia sẻ Tải về Tìm kiếm - Thiết đặt + Cài đặt Ý bạn là \"%1$s\"\? Chia sẻ với - Sử dụng trình phát băng hình bên ngoài + Sử dụng trình phát video bên ngoài Loại bỏ âm thanh ở một số độ phân giải Sử dụng trình phát âm thanh bên ngoài Trình phát nổi - Thư mục băng hình tải về - Băng hình đã tải về được lưu ở đây - Chọn thư mục tải xuống cho các tệp băng hình + Thư mục video tải về + Video đã tải về được lưu ở đây + Chọn thư mục tải xuống cho các tệp video Thư mục tải xuống âm thanh Các tệp âm thanh đã tải xuống được lưu trữ tại đây Chọn thư mục tải xuống cho các tệp âm thanh Độ phân giải mặc định Độ phân giải cửa sổ bật lên mặc định Hiện độ phân giải cao hơn - Chỉ một số thiết bị có thể phát băng hình 2K/4K + Chỉ một số thiết bị có thể phát video 2K/4K Phát với Kodi Cài đặt ứng dụng Kore bị thiếu\? Hiển thị tùy chọn \"Phát với Kodi\" - Hiển thị tùy chọn phát băng hình qua trung tâm truyền thông Kodi + Hiển thị tùy chọn phát video qua trung tâm truyền thông Kodi Âm thanh Định dạng âm thanh mặc định - Định dạng băng hình mặc định + Định dạng video mặc định Nền Chủ đề Sáng @@ -44,7 +44,7 @@ Đề xuất tìm kiếm Chọn các đề xuất để hiển thị khi tìm kiếm Tải về - Hiện các cuộn băng \"Tiếp theo\" và \"Tương tự\" + Hiện các video \"Tiếp theo\" và \"Tương tự\" URL không hỗ trợ Vẻ ngoài Đang phát trong nền @@ -57,7 +57,7 @@ Báo lỗi Tất cả Vô hiệu - Dọn dẹp + Xóa Độ phân giải tốt nhất Lỗi Lỗi kết nối mạng @@ -65,7 +65,7 @@ Không thể phân tích cú pháp trang web Nội dung không khả dụng Không thể thiết lập menu tải về - Ứng dụng / Giao diện người dùng bị lỗi + Ứng dụng/Giao diện người dùng bị lỗi Có vẻ NewPipe đã xảy ra lỗi, lướt xuống kiểm tra xem. Báo lỗi qua email Rất tiếc, đã xảy ra lỗi rồi. @@ -75,11 +75,11 @@ Loại lỗi:\\nYêu cầu:\\nNgôn ngữ của nội dung:\\nVùng miền (quốc gia) của nội dung:\\nNgôn ngữ của ứng dụng:\\nDịch vụ:\\nThời gian GMT:\\nTên gói:\\nPhiên bản:\\nPhiên bản hệ điều hành: Nhận xét của bạn (bằng tiếng Anh): Chi tiết: - Phát băng hình, thời lượng: + Phát video, thời lượng: Hình thu nhỏ của avatar người tải lên Lượt thích Lượt không thích - Băng hình + Video Âm thanh Thử lại nghìn @@ -95,9 +95,9 @@ Lỗi NewPipe đang tải xuống Chạm để biết chi tiết - Đợi chút xíu nha… + Vui lòng chờ… Đã sao chép vào khay nhớ tạm - Hãy chọn một thư mục tải xuống trong phần thiết đặt + Vui lòng xác định thư mục tải xuống sau trong cài đặt Sự cho phép này là cần thiết để \nmở trong chế độ bật lên reCAPTCHA @@ -114,7 +114,7 @@ Đọc giấy phép Đóng góp Ngôn ngữ nội dung ưu tiên - Băng hình và âm thanh + Video và âm thanh Lịch sử xem Lịch sử và bộ nhớ đệm Không tìm thấy @@ -133,18 +133,18 @@ Dùng tua nhanh ít chính xác Tua ít chính xác cho phép trình phát giảm độ chính xác để tua tới vị trí nhanh hơn. Tua khoảng 5, 15 hoặc 25 giây không hoạt động với điều này Đã xóa bộ nhớ cache hình ảnh - Lau sạch siêu dữ liệu đã lưu đệm - Loại bỏ mọi dữ liệu trang web đã lưu đệm + Xóa sạch siêu dữ liệu đã lưu đệm + Xóa bỏ mọi dữ liệu trang web đã lưu đệm Đã xóa bộ nhớ cache siêu dữ liệu Tự động xếp hàng luồng phát tiếp theo Tiếp tục hàng đợi (không lặp lại) bằng cách thêm một luồng phát liên quan Lịch sử tìm kiếm Lưu trữ truy vấn tìm kiếm cục bộ - Theo dõi các cuộn băng đã xem + Theo dõi các video đã xem Tiếp tục đang phát Tiếp tục phát lại sau khi bị gián đoạn (ví dụ: cuộc gọi) Hiển thị mẹo \"Giữ để xếp hàng\" - Hiển thị mẹo khi nhấn vào nút nền hoặc nút bật lên trong \"Chi tiết:\" cuốn băng + Hiển thị mẹo khi nhấn vào nút nền hoặc nút bật lên trong \"Chi tiết:\" video Quốc gia nội dung mặc định Trình phát Hành vi @@ -163,16 +163,16 @@ [Không xác định] Chuyển sang nền Chuyển sang Cửa sổ bật lên - Chuyển sang Main + Chuyển sang Chính Nhập cơ sở dữ liệu Xuất cơ sở dữ liệu - Ghi đè lịch sử, đăng ký, danh sách phát và các thiết đặt (tùy chọn) hiện tại của bạn - Xuất lịch sử, đăng ký, danh sách phát và các thiết đặt - Dọn dẹp lịch sử xem + Ghi đè lịch sử, đăng ký, danh sách phát và các cài đặt (tùy chọn) hiện tại của bạn + Xuất lịch sử, đăng ký, danh sách phát và các cài đặt + Xóa lịch sử xem Xóa lịch sử các luồng đã phát và các vị trí phát lại Xóa toàn bộ lịch sử xem\? Đã xoá lịch sử xem - Dọn dẹp lịch sử tìm kiếm + Xóa lịch sử tìm kiếm Xóa lịch sử tìm kiếm mà bạn đã ghi Xóa toàn bộ lịch sử tìm kiếm\? Đã xóa lịch sử tìm kiếm @@ -180,11 +180,11 @@ Đã xảy ra lỗi trình phát không thể khôi phục Phục hồi lại trình phát bị lỗi Trình phát ngoài không hỗ trợ các loại liên kết này - Không tìm thấy luồng băng hình nào + Không tìm thấy luồng video nào Không tìm thấy luồng âm thanh nào Thư mục không hợp lệ - Tệp / nguồn nội dung không hợp lệ - Tệp không tồn tại hoặc không có quyền đọc / ghi + Tệp/nguồn nội dung không hợp lệ + Tệp không tồn tại hoặc không có quyền đọc/ghi Tên tệp không được để trống Đã xảy ra lỗi: %1$s Không có luồng nào để tải về @@ -192,17 +192,17 @@ Kéo để sắp xếp lại Không có người đăng ký - %s người đăng kí + %s người đăng ký Không có lượt xem nào %s lượt xem - Không có cuộn băng nào + Không có video nào - %s cuộn băng + %s video - Tạo nên + Tạo Bỏ qua Đổi tên Đã xóa 1 mục. @@ -239,12 +239,12 @@ Không có tệp ZIP hợp lệ Cảnh báo: Không thể nhập tất cả các tệp. Thao tác này sẽ ghi đè cài đặt hiện tại của bạn. - Bạn có muốn cũng nhập các thiết đặt không\? + Bạn có muốn cũng nhập các cài đặt không? Thịnh hành - Mới và đang hot + Mới và nóng Loại bỏ Chi tiết - Thiết đặt âm thanh + Cài đặt âm thanh Giữ để xếp hàng Bắt đầu phát từ đây trong nền Bắt đầu phát trong cửa sổ bật lên @@ -252,7 +252,7 @@ Đóng ngăn Hành động \'mở\' được ưu tiên Hành động mặc định khi mở nội dung — %s - Trình phát băng hình + Trình phát video Trình phát nền Trình phát bật lên Luôn luôn hỏi @@ -276,8 +276,8 @@ Tự động tạo ra Phụ đề Thay đổi tỷ lệ văn bản và kiểu nền phụ đề trình phát. Yêu cầu khởi động lại ứng dụng để có hiệu lực - Theo dõi rò rỉ bộ nhớ có thể khiến ứng dụng trở nên không phản hồi khi đổ xô đống - Báo các lỗi out-of-lifecycle + Giám sát rò rỉ bộ nhớ có thể khiến ứng dụng không phản hồi khi tải vùng lưu trữ + Báo cáo lỗi ngoài vòng đời Buộc báo cáo ngoại lệ Rx không thể gửi được bên ngoài vòng đời của mảnh hoặc hoạt động sau khi xử lý Nhập Nhập từ @@ -292,10 +292,10 @@ \n \n1. Vào URL này: %1$s \n2. Đăng nhập khi được yêu cầu -\n3. Nhấn chọn \"Bao gồm tất cả dữ liệu trên YouTube\", sau đó nhấn \"Bỏ chọn tất cả\", sau đó chỉ chọn mục \"đăng kí\" rồi nhấn OK +\n3. Nhấn chọn \"Bao gồm tất cả dữ liệu trên YouTube\", sau đó nhấn \"Bỏ chọn tất cả\", sau đó chỉ chọn mục \"đăng ký\" rồi nhấn OK \n4. Nhấn nút \"Bước tiếp theo\" rồi nhấn \"Tạo tệp xuất\" \n5. Nhấn nút \"Tải xuống\" khi nó xuất hiện -\n6. Từ file zip mới tải về, trích xuất file .json ra (thường nằm ở đường dẫn \"YouTube và YouTube Music/đăng kí/subscriptions.json\") rồi nhập vào đây. +\n6. Từ file zip mới tải về, trích xuất file .json ra (thường nằm ở đường dẫn \"YouTube và YouTube Music/đăng ký/subscriptions.json\") rồi nhập vào đây. \n7. [Nếu nhập file .zip không thành công] Hãy giải nén tệp .csv (thường nó được để dưới phần \"YouTube and YouTube Music/subscriptions/subscriptions.csv\"), nhấn vào nút NHẬP TỆP ở phía bên dưới rồi chọn tệp csv đã được giải nén Để nhập hồ sơ SoundCloud bằng cách nhập URL hoặc ID của bạn, hãy làm các bước như sau: \n @@ -309,7 +309,7 @@ Điều khiển tốc độ phát lại Tốc độ Độ cao - Bỏ gắn (có thể gây méo nhưng vui) + Bỏ gắn (có thể gây méo) Tua nhanh trong im lặng Bước Đặt lại @@ -320,7 +320,7 @@ Không giới hạn Giới hạn độ phân giải khi sử dụng 3G, 4G Thu nhỏ khi chuyển qua ứng dụng khác - Hành động khi chuyển sang ứng dụng khác từ trình phát băng hình chính — %s + Hành động khi chuyển sang ứng dụng khác từ trình phát video chính — %s Không Thu nhỏ xuống trình phát nền Thu nhỏ xuống trình phát bật lên @@ -385,14 +385,14 @@ Khôi phục vị trí phát lại cuối cùng Vị trí phát trong danh sách Hiển chỉ báo vị trí phát lại trong danh sách - Dọn dẹp dữ liệu + Xóa dữ liệu Đã xoá vị trí phát Tệp đã di chuyển hoặc đã xoá Tên file này đã tồn tại Không thể ghi đè lên tệp - Có một bản tải xuống đang chờ xử lí với tên này - Newpipe đã bị đóng khi đang xử lí tệp - Không đủ dung lượng trên máy + Có một bản tải xuống đang chờ xử lý với tên này + NewPipe đã bị đóng khi đang xử lý tệp + Không đủ dung lượng trên máy Quá trình tải bị hủy, vì tập tin đã bị xoá Kết nối hết thời gian Bạn muốn xóa lịch sử tải về hay xóa tất cả những file đã tải về\? @@ -425,10 +425,10 @@ %d giây - Có, và cuốn băng đã xem một phần - Những cuốn băng đã xem trước và sau khi thêm vào danh sách phát sẽ bị loại bỏ. -\nBạn có chắc không\? Điều này không thể được hoàn tác! - Loại bỏ các cuốn băng đã xem\? + Có, và video đã xem một phần + Những video đã xem trước và sau khi thêm vào danh sách phát sẽ bị xóa. +\nBạn có chắc không? Điều này không thể được hoàn tác! + Xóa video đã xem? Xóa đã xem Mặc định hệ thống Ngôn ngữ ứng dụng @@ -444,8 +444,8 @@ Ngôn ngữ sẽ thay đổi khi ứng dụng khởi động lại Bấm \"Xong\" khi hoàn thành Đã hoàn thành - ∞ cuộn băng - 100+ cuốn băng + ∞ video + 100+ video %s người nghe @@ -458,17 +458,17 @@ Nghệ sĩ Album Bài hát - Các cuốn băng - Cuốn băng này bị giới hạn độ tuổi. + Các video + Video này bị giới hạn độ tuổi. \n -\nBật \"%1$s\" trong thiết đặt nếu bạn muốn xem cuốn băng này. - Bật chế độ hạn chế Youtube +\nBật \"%1$s\" trong cài đặt nếu bạn muốn xem video này. + Bật chế độ hạn chế YouTube Chỉ URL HTTPS được hỗ trợ Chọn thực thể PeerTube ưa thích Thực thể PeerTube Thời lượng tua-nhanh tới/-lùi Dòng chữ mô tả thời gian gốc từ các dịch vụ sẽ được hiển thị thay thế - Hiện thời gian gốc trên các item + Hiển thị thời gian ban đầu trước đây trên các mục Chọn dịch vụ; dịch vụ hiện tại: Bởi %s Được tạo bởi %s @@ -476,13 +476,13 @@ NewPipe chưa hỗ trợ loại nội dung này. \n \nCó thể nó sẽ được hỗ trợ bởi một phiên bản mới hơn trong tương lai. - Bạn có nghĩ rằng tải nguồn cấp dữ liệu quá chậm\? Nếu vậy, hãy thử bật tải nhanh (bạn có thể thay đổi nó này trong thiết đặt hoặc bằng cách nhấn nút bên dưới). + Bạn có nghĩ rằng tải nguồn cấp dữ liệu quá chậm? Nếu vậy, hãy thử bật tải nhanh (bạn có thể thay đổi nó này trong cài đặt hoặc bằng cách nhấn nút bên dưới). \n \nNewPipe cung cấp hai chiến lược tải nguồn cấp dữ liệu: \n• Tìm nạp toàn bộ kênh đăng ký, tuy chậm nhưng đầy đủ. \n• Sử dụng điểm cuối dịch vụ chuyên dụng, nhanh nhưng thường không hoàn thiện. \n -\nSự khác biệt giữa hai loại này là cái nào nhanh thường thiếu một số thông tin, chẳng hạn như thời lượng hoặc loại mục (không thể phân biệt giữa các cuốn băng trực tiếp và bình thường) và nó có thể trả về ít mục hơn. +\nSự khác biệt giữa hai loại này là cái nào nhanh thường thiếu một số thông tin, chẳng hạn như thời lượng hoặc loại mục (không thể phân biệt giữa các video trực tiếp và bình thường) và nó có thể trả về ít mục hơn. \n \nYouTube là một ví dụ về dịch vụ cung cấp phương pháp nhanh này với nguồn cấp RSS. \n @@ -523,7 +523,7 @@ Vui lòng kiểm tra xem vấn đề mà bạn đang gặp đã báo cáo trước đó hay chưa. Nếu bạn tạo quá nhiều báo cáo trùng lặp, bạn sẽ khiến cho chúng tôi tốn thời gian để đọc chúng thay vì sửa lỗi bạn gặp. Báo cáo trên GitHub Sao chép bản báo cáo đã được định dạng - Không thể đọc URL này. Mở với app khác\? + Không thể đọc URL này. Mở với ứng dụng khác? Tự động xếp hàng Hàng đợi của trình phát hiện tại sẽ bị thay thế Việc chuyển từ trình phát này sang trình phát khác có thể sẽ thay thế hàng đợi @@ -533,13 +533,13 @@ Ngẫu nhiên Lặp lại Bạn có thể chọn tối đa ba hành động để hiện trong thông báo thu gọn! - Chỉnh sửa mỗi hành động trong thông báo ở dưới bằng cách nhấn vào nó. Chọn tối đa ba hành động để hiện trong thông báo thu gọn bằng cách sử dụng các ô đánh dấu bên phải + Chỉnh sửa từng hành động thông báo bên dưới bằng cách nhấn vào nó. Chọn tối đa ba trong số chúng để hiển thị trong thông báo thu gọn bằng cách sử dụng các hộp kiểm ở bên phải. Nút hành động thứ năm Nút hành động thứ tư Nút hành động thứ ba Nút hành động thứ hai Nút hành động đầu tiên - Cắt bớt hình thu nhỏ cuộn băng hiển thị trong thông báo từ tỷ lệ khung hình 16:9 xuống 1:1 + Cắt bớt hình thu nhỏ video hiển thị trong thông báo từ tỷ lệ khung hình 16:9 xuống 1:1 Chỉnh ảnh thu nhỏ thành tỉ lệ 1:1 Đang hiện kết quả cho: %s Xếp hàng @@ -550,8 +550,8 @@ Đã xếp hàng Xoá Cookie mà NewPipe lưu trữ sau khi bạn hoàn thành nó Cookie reCAPTCHA đã được xóa - Dọn dẹp Cookie của reCAPCHA - YouTube cung cấp \"Chế độ hạn chế\" để ẩn nội dung không phụ hợp + Xóa Cookie của reCAPCHA + YouTube cung cấp \"Chế độ hạn chế\" để ẩn nội dung có khả năng dành cho người trưởng thành Yêu cầu Android tùy chỉnh màu của thông báo theo màu chính của ảnh thu nhỏ (lưu ý rằng việc này không khả dụng trên tất cả thiết bị) Tô màu thông báo Thiết bị của bạn không có ứng dụng để mở tệp này @@ -561,11 +561,11 @@ Mô tả Các mục liên quan Bình luận - Thông báo cho quá trình băm cuốn băng - Thông báo băm cuộn băng - Tắt để ẩn các hộp siêu thông tin có thông tin bổ sung về người tạo luồng, nội dung luồng hoặc yêu cầu tìm kiếm - Hiển thị siêu thông tin - Tắt để ẩn mô tả cuộn băng và các thông tin bổ sung + Thông báo cho quá trình băm video + Thông báo băm video + Tắt để ẩn các hộp thông tin meta có thông tin bổ sung về người tạo luồng, nội dung luồng hoặc yêu cầu tìm kiếm + Hiển thị thông tin meta + Tắt để ẩn mô tả video và các thông tin bổ sung Hiện mô tả Mở bằng Tệp đang được tải xuống @@ -574,18 +574,18 @@ Tự động (giao diện hệ thống) Radio Nội dung này chỉ dành cho người dùng trả phí, nên NewPipe không thể phát hay tải xuống. - Cuốn băng này chỉ được dành cho thành viên YouTube Music Premium, nên NewPipe không thể phát hay tải xuống. + Video này chỉ được dành cho thành viên YouTube Music Premium, nên NewPipe không thể phát hay tải xuống. Nội dung này được để ở chế độ riêng tư, nên NewPipe không thể phát hay tải xuống. - Đây là một track SoundCloud Go+, nên NewPipe không thể phát hay tải xuống được, ít nhất là tại quốc gia của bạn. + Đây là bản nhạc SoundCloud Go+, ít nhất là ở quốc gia của bạn, vì vậy NewPipe không thể phát trực tuyến hoặc tải xuống bản nhạc này. Nội dung này không có sẵn ở quốc gia của bạn. Làm văng ứng dụng - Cuốn băng này bị giới hạn độ tuổi. -\nDo chính sách mới của YouTube với các cuốn băng bị giới hạn độ tuổi, NewPipe không thể truy cập bất kỳ luồng băng hình nào của nó và do đó không thể phát nó. + Video này bị giới hạn độ tuổi. +\nDo chính sách mới của YouTube với các video bị giới hạn độ tuổi, NewPipe không thể truy cập bất kỳ luồng video nào của nó và do đó không thể phát nó. Chủ đề đêm Nổi bật Hiện chi tiết kênh Hoàn thành - Tắt tính năng truyền tải phương tiện nếu bạn gặp phải tình trạng màn hình đen hoặc giật hình khi phát lại cuốn băng. + Tắt tính năng truyền tải phương tiện nếu bạn gặp phải tình trạng màn hình đen hoặc giật hình khi phát lại video. Tắt truyền phương tiện qua đường hầm Tắt Bật @@ -614,7 +614,7 @@ \nBạn có muốn hủy đăng ký khỏi kênh này không\? Không thể tải nguồn cấp dữ liệu cho \'%s\'. Lỗi tải nguồn cấp dữ liệu - \'Storage Access Framework\' chỉ được hỗ trợ từ Android 10 trở đi + \'Khung truy cập lưu trữ\' chỉ được hỗ trợ từ Android 10 trở lên Bạn sẽ được hỏi nơi bạn muốn lưu mỗi mục tải xuống Chưa có thư mục tải xuống nào được đặt, hãy chọn thư mục tải xuống mặc định ngay Không hiện @@ -635,11 +635,11 @@ %s lượt tải xuống đã hoàn tất Vuốt các mục để xóa chúng - Không bắt đầu các cuốn băng ở trình phát mini, mà chuyển trực tiếp thành chế độ toàn màn hình, nếu tự động xoay bị khóa. Bạn vẫn có thể truy cập trình phát mini bằng cách thoát khỏi toàn màn hình + Không bắt đầu các video ở trình phát mini, mà chuyển trực tiếp thành chế độ toàn màn hình, nếu tự động xoay bị khóa. Bạn vẫn có thể truy cập trình phát mini bằng cách thoát khỏi toàn màn hình Khởi động trình phát chính ở toàn màn hình Đã xếp kế tiếp vào hàng Xếp kế tiếp vào hàng - Đang xử lý... Có thể hơi lâu + Đang xử lý... Có thể mất chút thời gian Thông báo lỗi Thông báo để báo cáo lỗi NewPipe đã gặp sự cố, nhấn để xem và báo cáo @@ -648,7 +648,7 @@ Hiện tùy chọn dừng đột ngột khi sử dụng trình phát Hiện thanh báo lỗi Tạo thông báo lỗi - Kiểm tra cập nhật + Kiểm tra cập nhật Kiểm tra phiên bản mới theo cách thủ công Đang kiểm tra cập nhật… Mục nguồn cấp dữ liệu mới @@ -684,11 +684,11 @@ , Nửa cung Luồng băng hình mà không được trình tải xuống hỗ trợ sẽ không hiển thị - Không có luồng băng hình nào khả dụng cho trình phát bên ngoài + Không có luồng video nào khả dụng cho trình phát bên ngoài Luồng phát đã chọn không được trình phát ngoài hỗ trợ Không có luồng âm thanh nào khả dụng cho máy phát bên ngoài Chọn chất lượng cho trình chạy ngoài - Định dạng không xác định (:P) + Định dạng không xác định Độ phân giải không xác định Kích thước tải thời lượng phát lại Thể loại @@ -717,9 +717,9 @@ Thay đổi kích thước khoảng thời gian tải trên nội dung lũy tiến (hiện tại là %s). Giá trị thấp hơn có thể tăng tốc độ tải ban đầu của chúng Một bản âm thanh đã có sẵn trong luồng này Sử dụng tính năng bộ giải mã dự phòng của ExoPlayer - Giải pháp thay thế này giải phóng và khởi tạo lại codec băng hình khi xảy ra thay đổi bề mặt, thay vì thiết đặt trực tiếp bề mặt vào codec. Đã được ExoPlayer sử dụng trên một số thiết bị gặp sự cố này, thiết đặt này chỉ ảnh hưởng đến Android 6 trở lên + Giải pháp thay thế này giải phóng và khởi tạo lại codec video khi xảy ra thay đổi bề mặt, thay vì cài đặt trực tiếp bề mặt vào codec. Đã được ExoPlayer sử dụng trên một số thiết bị gặp sự cố này, cài đặt này chỉ ảnh hưởng đến Android 6 trở lên \n -\nBật tùy chọn này có thể ngăn lỗi phát lại khi chuyển đổi trình phát băng hình hiện tại hoặc chuyển sang chế độ toàn màn hình +\nBật tùy chọn này có thể ngăn lỗi phát lại khi chuyển đổi trình phát video hiện tại hoặc chuyển sang chế độ toàn màn hình Bật tùy chọn này nếu bạn gặp sự cố khởi tạo bộ giải mã, vấn đề này sẽ quay trở lại bộ giải mã có mức độ ưu tiên thấp hơn nếu quá trình khởi tạo bộ giải mã chính thất bại. Điều này có thể dẫn đến hiệu suất phát lại kém hơn so với khi sử dụng bộ giải mã chính Luôn dùng biện pháp thay thế cho bề mặt đầu ra video cho ExoPlayer nguyên gốc @@ -741,8 +741,8 @@ Sắp tới Không rõ Chọn bản âm thanh cho máy phát bên ngoài - Thiết đặt ExoPlayer - Quản lý một số thiết đặt ExoPlayer. Những thay đổi này yêu cầu khởi động lại trình phát để có hiệu lực + Cài đặt ExoPlayer + Quản lý một số cài đặt ExoPlayer. Những thay đổi này yêu cầu khởi động lại trình phát để có hiệu lực Đang tải siêu dữ liệu… Vị trí tab chính Tìm nạp các tab kênh @@ -753,7 +753,7 @@ Không có luồng trực tiếp Ảnh xem trước Không có luồng - Các cuộn băng + Các video Người đăng ký Những thẻ nào được hiển thị trên các trang kênh Thẻ kênh @@ -763,7 +763,7 @@ %1$s %2$s Luồng tiếp theo Hình đại diện kênh phụ - Mở hàng chờ phát + Mở phát hàng chờ Giới thiệu Phát lại Băng rôn @@ -787,10 +787,27 @@ Tua đi Album Tua lại - Chia sẻ danh sách phát với các thông tin chi tiết như tên danh sách phát và tiêu đề video hoặc dưới dạng danh sách URL cuốn băng đơn giản + Chia sẻ danh sách phát với các thông tin chi tiết như tên danh sách phát và tiêu đề video hoặc dưới dạng danh sách URL video đơn giản Chất lượng trung bình - %1$s: %2$s Chọn chất lượng hình ảnh và chọn có tải chất lượng ảnh hay không, để giảm mức sử dụng dữ liệu và bộ nhớ. Thay đổi xoá cache ảnh cho cả trong bộ nhớ lẫn ổ cứng - %s Track Trực tiếp + Chỉnh sửa từng hành động thông báo bên dưới bằng cách nhấn vào nó. Ba hành động đầu tiên (phát/tạm dừng, trước đó và tiếp theo) do hệ thống thiết lập và không thể tùy chỉnh. + Hiện ít hơn + Hiện nhiều hơn + + %s hồi đáp + + Không đủ dung lượng trống trên thiết bị + Sao lưu và khôi phục + Đặt lại cài đặt + Đặt lại tất cả cài đặt về giá trị mặc định + Việc đặt lại tất cả cài đặt sẽ loại bỏ tất cả các cài đặt ưa thích của bạn và khởi động lại ứng dụng. +\n +\nBạn có chắc muốn tiếp tục? + Không + NewPipe có thể tự động kiểm tra các phiên bản mới theo thời gian và thông báo cho bạn khi chúng có sẵn. +\nBạn có muốn kích hoạt tính năng này không? + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 47752a150..fa8d31022 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -393,7 +393,7 @@ 无法覆盖文件 已暂停下载包含此名称的任务 NewPipe 在处理文件时被关闭 - 设备上没有剩余储存空间 + 设备上没有剩余储存空间 进度丢失,文件已被删除 连接超时 是否清空下载记录或删除所有下载的文件? @@ -534,7 +534,7 @@ 随机播放 单曲循环 最多可以选择三个操作显示在紧凑通知中! - 点击编辑下面的每一个通知操作。使用右方的复选框选择在紧凑通知中显示的动作,最多可以选择三个 + 轻按下面的每一个通知操作进行编辑。使用右方的复选框选择在紧凑通知中显示的操作,最多可以选择三个。 第五操作按钮 第四操作按钮 第三操作按钮 @@ -642,7 +642,7 @@ 处理中…可能需要一些时间 手动检查新版本 检查更新中… - 检查更新 + 检查更新 新订阅源条目 显示\"使播放器崩溃\" 在使用播放器时显示一个崩溃选项 @@ -793,4 +793,21 @@ 分享播放列表 分享详细的播放列表(带名称和视频标题等信息)或只分享视频网址列表 - %1$s: %2$s + + %s 条回复 + + 显示较多 + 显示较少 + 轻按下方的每个通知操作进行编辑。头三个动作(播放/暂停、上一个和下一个)是系统设置的,不能自定义。 + 设备剩余空间不足 + 备份和还原 + 重置设置 + 将所有设置重置为默认值 + 重置所有设置会取消你所有的偏好设置并重启应用。 +\n +\n你确定想要继续吗? + + NewPipe 可以自动时不时地检查新版本并在新版本可用时通知你。 +\n你想开启该功能吗? + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 3afaf86de..a59da9c4e 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -44,7 +44,7 @@ 撳放大鏡開始。 內容 顯示年齡設限嘅內容 - 實況 + 直播 問題 載入唔晒全部縮圖 解析唔到網站 @@ -155,7 +155,7 @@ 開啟方式係 搜尋到嘅嘢係:%s 第五粒動作掣 - 撳下面嘅掣去更改對應嘅通知動作。用右手邊嘅格仔剔選最多三個,擺喺精簡通知度 + 撳下面嘅掣去更改對應嘅通知動作。用右手邊嘅格仔剔選最多三個,擺落去精簡通知度。 精簡通知最多淨係擺到三個動作! 循環播放 顯示留言 @@ -288,7 +288,7 @@ 節奏 播放速度控掣 系統預設 - 檢查有冇更新 + 檢查有冇更新 個 App 用咩語言 睇咗嗰啲剷咗佢 係咪要剷走睇咗嘅影片? @@ -454,7 +454,7 @@ 建立唔到呢個目的地資料夾 建立唔到安全連線 喺幕後開始播放 - 部機冇晒位 + 部機冇晒位 頂櫳重試幾多次 若然有機會用到流動數據嘅時候,可能會用得著,雖則有啲下載冇得暫停 輪住嚟下載 @@ -516,7 +516,7 @@ 主機 喺影片詳情撳一下「幕後播」或「浮面播」個掣嘅時候顯示提示 紀錄 - 歷史紀錄 + 紀錄 攝咗做下個播 記憶體洩露監測喺傾卸堆疊嘢嘅時候,或者會導致個 app 冇反應 閂埋選單 @@ -721,7 +721,7 @@ 揀選右手邊播放器螢幕嘅手勢 右手邊手勢動作 調整光暗度 - 調整大細聲 + 音量 乜都唔使做 優先揀原聲 優先揀旁述 @@ -793,4 +793,11 @@ 頻道 上一個串流 直播 + + %s 個回覆 + + 摺埋 + 拉開 + 撳下面嘅掣去更改對應嘅通知動作。頭三個動作 (播放/暫停、上一個、下一個) 系統預設咗,冇得揀。 + 部機冇晒位 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8e613161a..c404edeca 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -390,7 +390,7 @@ 無法覆寫檔案 已有擱置中的下載與此同名 NewPipe 在處理檔案時被關閉 - 裝置上沒有剩餘的空間 + 裝置上沒有剩餘的空間 進度遺失,因為檔案已被刪除 您想要清除您的下載歷史記錄,還是刪除所有已下載的檔案? 限制下載佇列 @@ -534,7 +534,7 @@ 隨機播放 循環播放 您可以選取最多三個動作來顯示在簡潔通知中! - 透過輕觸下方的通知來編輯它。使用右側的勾選框,最多可以選取三個在簡潔通知中顯示 + 透過輕觸下方的通知來編輯它。使用右側的勾選框,最多可以選取三個在簡潔通知中顯示。 第五動作按鈕 第四動作按鈕 第三動作按鈕 @@ -640,7 +640,7 @@ 已將下一個加入佇列 將下一個加入佇列 正在處理……可能需要一點時間 - 檢查更新 + 檢查更新 手動檢查新版本 正在檢查更新…… 新 feed 項目 @@ -793,4 +793,21 @@ 分享播放清單 分享包含播放清單名稱與影片標題等詳細資訊的播放清單,或是僅作為簡單的影片 URL 清單 - %1$s:%2$s + + %s 個回覆 + + 顯示更多 + 顯示較少 + 透過點擊下面的每個通知操作來編輯它。前三個動作(播放/暫停、上一個與下一個)由系統設定,無法自訂。 + 裝置上沒有足夠的空間 + 備份與還原 + 重設設定 + 將所有設定重設為預設值 + 重設所有設定將會放棄您的所有偏好設定並重新啟動應用程式。 +\n +\n您確定您想要繼續嗎? + + + NewPipe 可以隨時自動檢查新版本,並在新版本可用時通知您。 +\n您想要啟用此功能嗎? \ No newline at end of file diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 880fa92da..fb68a464d 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -233,6 +233,8 @@ prefer_descriptive_audio last_resize_mode + reset_settings + debug_pref_screen_key allow_heap_dumping_key @@ -492,6 +494,7 @@ + update_check_consent_key update_app_key manual_update_key update_pref_screen_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 435e9a382..c4ad8b1d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,6 +7,8 @@ Install Cancel OK + Yes + No Open in browser Mark as watched Open in popup mode @@ -57,7 +59,8 @@ Third action button Fourth action button Fifth action button - Edit each notification action below by tapping on it. Select up to three of them to be shown in the compact notification by using the checkboxes on the right + Edit each notification action below by tapping on it. Select up to three of them to be shown in the compact notification by using the checkboxes on the right. + Edit each notification action below by tapping on it. The first three actions (play/pause, previous and next) are set by the system and cannot be customized. You can select at most three actions to show in the compact notification! Repeat Shuffle @@ -152,6 +155,7 @@ Updates Player notification Configure current playing stream notification + Backup and restore Playing in background Playing in popup mode Content @@ -554,8 +558,11 @@ Updates Show a notification to prompt app update when a new version is available - Check for updates + Check for updates + NewPipe can automatically check for new versions from time to time and notify you once they are available.\nDo you want to enable this? + @string/check_for_updates Manually check for new versions + Minimize on app switch Action when switching to other app from main video player — %s @@ -595,6 +602,10 @@ Download finished %s downloads finished + Reset settings + Reset all settings to their default values + + Resetting all settings will discard all of your preferred settings and restarts the app.\n\nAre you sure you want to proceed? Generate unique name Overwrite @@ -615,7 +626,8 @@ Not found Post-processing failed NewPipe was closed while working on the file - No space left on device + Not enough free space on device + No space left on device Progress lost, because the file was deleted Connection timeout Cannot recover this download @@ -838,4 +850,10 @@ Share URL list - %1$s: %2$s %1$s\n%2$s + + %s reply + %s replies + + Show more + Show less diff --git a/app/src/main/res/xml/appearance_settings.xml b/app/src/main/res/xml/appearance_settings.xml index beb46cdf5..d8f6bf552 100644 --- a/app/src/main/res/xml/appearance_settings.xml +++ b/app/src/main/res/xml/appearance_settings.xml @@ -23,14 +23,6 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> - - + + + + + + + \ 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 45f187452..2cdc6c545 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -126,20 +126,6 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> - - - - + + + + localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + final List mergedPlaylists = + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); + + assertEquals(0, mergedPlaylists.size()); + } + + @Test + public void onlyLocalPlaylists() { + final List localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", false, -1, 0, 1)); + localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", false, -1, 1, 1)); + localPlaylists.add(new PlaylistMetadataEntry(3, "name3", "", false, -1, 3, 1)); + final List mergedPlaylists = + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); + + assertEquals(3, mergedPlaylists.size()); + assertEquals(0, mergedPlaylists.get(0).getDisplayIndex()); + assertEquals(1, mergedPlaylists.get(1).getDisplayIndex()); + assertEquals(3, mergedPlaylists.get(2).getDisplayIndex()); + } + + @Test + public void onlyRemotePlaylists() { + final List localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + remotePlaylists.add(new PlaylistRemoteEntity( + 1, "name1", "url1", "", "", 1, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 2, "name2", "url2", "", "", 2, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 3, "name3", "url3", "", "", 4, 1L)); + final List mergedPlaylists = + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); + + assertEquals(3, mergedPlaylists.size()); + assertEquals(1, mergedPlaylists.get(0).getDisplayIndex()); + assertEquals(2, mergedPlaylists.get(1).getDisplayIndex()); + assertEquals(4, mergedPlaylists.get(2).getDisplayIndex()); + } + + @Test + public void sameIndexWithDifferentName() { + final List localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", false, -1, 0, 1)); + localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", false, -1, 1, 1)); + remotePlaylists.add(new PlaylistRemoteEntity( + 1, "name3", "url1", "", "", 0, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 2, "name4", "url2", "", "", 1, 1L)); + final List mergedPlaylists = + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); + + assertEquals(4, mergedPlaylists.size()); + assertTrue(mergedPlaylists.get(0) instanceof PlaylistMetadataEntry); + assertEquals("name1", ((PlaylistMetadataEntry) mergedPlaylists.get(0)).name); + assertTrue(mergedPlaylists.get(1) instanceof PlaylistRemoteEntity); + assertEquals("name3", ((PlaylistRemoteEntity) mergedPlaylists.get(1)).getName()); + assertTrue(mergedPlaylists.get(2) instanceof PlaylistMetadataEntry); + assertEquals("name2", ((PlaylistMetadataEntry) mergedPlaylists.get(2)).name); + assertTrue(mergedPlaylists.get(3) instanceof PlaylistRemoteEntity); + assertEquals("name4", ((PlaylistRemoteEntity) mergedPlaylists.get(3)).getName()); + } + + @Test + public void sameNameWithDifferentIndex() { + final List localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", false, -1, 1, 1)); + localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", false, -1, 3, 1)); + remotePlaylists.add(new PlaylistRemoteEntity( + 1, "name1", "url1", "", "", 0, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 2, "name2", "url2", "", "", 2, 1L)); + final List mergedPlaylists = + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); + + assertEquals(4, mergedPlaylists.size()); + assertTrue(mergedPlaylists.get(0) instanceof PlaylistRemoteEntity); + assertEquals("name1", ((PlaylistRemoteEntity) mergedPlaylists.get(0)).getName()); + assertTrue(mergedPlaylists.get(1) instanceof PlaylistMetadataEntry); + assertEquals("name1", ((PlaylistMetadataEntry) mergedPlaylists.get(1)).name); + assertTrue(mergedPlaylists.get(2) instanceof PlaylistRemoteEntity); + assertEquals("name2", ((PlaylistRemoteEntity) mergedPlaylists.get(2)).getName()); + assertTrue(mergedPlaylists.get(3) instanceof PlaylistMetadataEntry); + assertEquals("name2", ((PlaylistMetadataEntry) mergedPlaylists.get(3)).name); + } +} diff --git a/app/src/test/java/org/schabi/newpipe/local/subscription/FeedGroupIconTest.kt b/app/src/test/java/org/schabi/newpipe/local/subscription/FeedGroupIconTest.kt index 1479c3d1e..2fc44d9b7 100644 --- a/app/src/test/java/org/schabi/newpipe/local/subscription/FeedGroupIconTest.kt +++ b/app/src/test/java/org/schabi/newpipe/local/subscription/FeedGroupIconTest.kt @@ -9,7 +9,7 @@ class FeedGroupIconTest { fun `No gaps and repeated ids`() { val usedIds = HashSet() - for ((shouldBeId, currentIcon) in FeedGroupIcon.values().withIndex()) { + for ((shouldBeId, currentIcon) in FeedGroupIcon.entries.withIndex()) { val added = usedIds.add(currentIcon.id) assertTrue("Repeated ids (current item: ${currentIcon.name} - ${currentIcon.id})", added) @@ -24,7 +24,7 @@ class FeedGroupIconTest { fun `No icons pointing to the same attr`() { val usedIcons = HashSet() - for (groupIcon in FeedGroupIcon.values()) { + for (groupIcon in FeedGroupIcon.entries) { val added = usedIcons.add(groupIcon.drawableResource) assertTrue("Repeated icon (current item: ${groupIcon.name} - ${groupIcon.id})", added) } diff --git a/build.gradle b/build.gradle index f1c861c9f..6d19a6f8a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.8.22' + ext.kotlin_version = '1.9.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.1.1' + classpath 'com.android.tools.build:gradle:8.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/doc/README.asm.md b/doc/README.asm.md index af855c4cd..2a7c10f51 100644 --- a/doc/README.asm.md +++ b/doc/README.asm.md @@ -20,9 +20,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](/README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -সতৰ্কবাণী: এই এপটো নিৰ্মিয়মান অৱস্থাত আছে, গতিকে আপুনি ব্যৱহাৰ কৰোঁতে কোনো অসুবিধাৰ সন্মুখীন হ'ব পাৰে। যদি আপুনি অসুবিধাৰ সন্মুখীন হয়, ইছ্যু টেমপ্লেট পূৰণ কৰি আমাৰ GITHUB ৰিপ'জিটৰিত এটা ইছ্যু খোলক। - -NEWPIPE, GOOGLE PLAY ষ্ট'ৰত ৰাখিলে তেওঁলোকৰ চৰ্ত আৰু নিয়ম উলংঘা হয় +> [!warning] +> এই এপটো নিৰ্মিয়মান অৱস্থাত আছে, গতিকে আপুনি ব্যৱহাৰ কৰোঁতে কোনো অসুবিধাৰ সন্মুখীন হ'ব পাৰে। যদি আপুনি অসুবিধাৰ সন্মুখীন হয়, ইছ্যু টেমপ্লেট পূৰণ কৰি আমাৰ GITHUB ৰিপ'জিটৰিত এটা ইছ্যু খোলক। +> +> NEWPIPE, GOOGLE PLAY ষ্ট'ৰত ৰাখিলে তেওঁলোকৰ চৰ্ত আৰু নিয়ম উলংঘা হয় ## স্ত্ৰীনশ্বট diff --git a/doc/README.de.md b/doc/README.de.md index 3714020ff..e269da05c 100644 --- a/doc/README.de.md +++ b/doc/README.de.md @@ -21,9 +21,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -WARNUNG: DIESE APP BEFINDET SICH IN EINER BETA-PHASE, DAHER KÖNNTEST DU BUGS BEGEGNEN. FALLS DIES PASSIERT, ERSTELLE EIN ISSUE (AUF ENGLISCH) IN UNSEREM GITHUB REPOSITORY, INDEM DU DIE VORLAGE DORT AUSFÜLLST. - -NEWPIPE, ODER JEGLICHEN FORK DAVON, IM GOOGLE PLAYSTORE ANZUBIETEN, VERLETZT DESSEN GESCHÄFTSBEDINGUNGEN. +> [!warning] +> DIESE APP BEFINDET SICH IN EINER BETA-PHASE, DAHER KÖNNTEST DU BUGS BEGEGNEN. FALLS DIES PASSIERT, ERSTELLE EIN ISSUE (AUF ENGLISCH) IN UNSEREM GITHUB REPOSITORY, INDEM DU DIE VORLAGE DORT AUSFÜLLST. +> +> NEWPIPE, ODER JEGLICHEN FORK DAVON, IM GOOGLE PLAYSTORE ANZUBIETEN, VERLETZT DESSEN GESCHÄFTSBEDINGUNGEN. ## Screenshots diff --git a/doc/README.es.md b/doc/README.es.md index 5aa5eb55c..b6a00e9d3 100644 --- a/doc/README.es.md +++ b/doc/README.es.md @@ -19,9 +19,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -AVISO: ESTA ES UNA VERSIÓN BETA, POR LO TANTO, PUEDES ENCONTRAR BUGS. SI ENCUENTRAS UNO ABRA UN ISSUE A TRAVÉS DE NUESTRO REPOSITORIO DE GITHUB. - -COLOCAR NEWPIPE O CUALQUIER FORK DE NEWPIPE EN LA GOOGLE PLAY STORE VIOLARÁ SUS TÉRMINOS Y CONDICIONES. +> [!warning] +> ESTA ES UNA VERSIÓN BETA, POR LO TANTO, PUEDES ENCONTRAR BUGS. SI ENCUENTRAS UNO ABRA UN ISSUE A TRAVÉS DE NUESTRO REPOSITORIO DE GITHUB. +> +> COLOCAR NEWPIPE O CUALQUIER FORK DE NEWPIPE EN LA GOOGLE PLAY STORE VIOLARÁ SUS TÉRMINOS Y CONDICIONES. ## Capturas de Pantalla diff --git a/doc/README.fr.md b/doc/README.fr.md index b68c1e321..7d4673b69 100644 --- a/doc/README.fr.md +++ b/doc/README.fr.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -ATTENTION: CETTE APP EST EN BETA, VOUS POUVEZ DONC RENCONTRER DES BUGS. SI C'EST LE CAS, OUVREZ UNE ISSUE DANS NOTRE DÉPÔT GITHUB EN REMPLISSANT LE MODÈLE D'ISSUE. - -METTRE NEWPIPE, OU N'IMPORTE QUEL FORK DE NEWPIPE, SUR LE GOOGLE PLAY STORE ENFREINT LEURS CONDITIONS D'UTILISATION +> [!warning] +> CETTE APP EST EN BETA, VOUS POUVEZ DONC RENCONTRER DES BUGS. SI C'EST LE CAS, OUVREZ UNE ISSUE DANS NOTRE DÉPÔT GITHUB EN REMPLISSANT LE MODÈLE D'ISSUE. +> +> METTRE NEWPIPE, OU N'IMPORTE QUEL FORK DE NEWPIPE, SUR LE GOOGLE PLAY STORE ENFREINT LEURS CONDITIONS D'UTILISATION ## Captures d'écran diff --git a/doc/README.hi.md b/doc/README.hi.md index 536be4d43..282e75420 100644 --- a/doc/README.hi.md +++ b/doc/README.hi.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -चेतावनी: यह एक बीटा संस्करण है, तो अगर आपको इसमें बग्स नज़र आते हैं, कृपया हमारे GitHub रिपॉज़िटरी के ज़रिए एक समस्या खोल दें। - -NewPipe या इसके किसी फोर्क को Google Play Store पर डालने पर Store के नियमों और शर्तों का उल्लंघन होता है। +> [!warning] +> यह एक बीटा संस्करण है, तो अगर आपको इसमें बग्स नज़र आते हैं, कृपया हमारे GitHub रिपॉज़िटरी के ज़रिए एक समस्या खोल दें। +> +> NewPipe या इसके किसी फोर्क को Google Play Store पर डालने पर Store के नियमों और शर्तों का उल्लंघन होता है। ## ऐप कैसी दिखती है diff --git a/doc/README.it.md b/doc/README.it.md index bca6a23d2..55ae12380 100644 --- a/doc/README.it.md +++ b/doc/README.it.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -ATTENZIONE: QUEST'APP È IN BETA, QUINDI POTRESTI IMBATTERTI IN BUG. SE COSÌ FOSSE, APRI UN'ISSUE SUL NOSTRO REPOSITORIO GITHUB COMPILANDO IL TEMPLATE ISSUE. - -METTERE NEWPIPE, O QUALSIASI SUA FORK, NEL GOOGLE PLAY STORE, VIOLA I LORO TERMINI E CONDIZIONI. +> [!warning] +> QUEST'APP È IN BETA, QUINDI POTRESTI IMBATTERTI IN BUG. SE COSÌ FOSSE, APRI UN'ISSUE SUL NOSTRO REPOSITORIO GITHUB COMPILANDO IL TEMPLATE ISSUE. +> +> METTERE NEWPIPE, O QUALSIASI SUA FORK, NEL GOOGLE PLAY STORE, VIOLA I LORO TERMINI E CONDIZIONI. ## Screenshot diff --git a/doc/README.ja.md b/doc/README.ja.md index e4eb74694..4d0f348f6 100644 --- a/doc/README.ja.md +++ b/doc/README.ja.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -注意: これはベータ版のため、バグが発生する可能性があります。もしバグが発生した場合、GitHub のリポジトリで Issue を開いてください。 - -NewPipe 及びいずれのフォークを Google Play ストアに公開すると、Google の取引条件の違反になります。 +> [!warning] +> これはベータ版のため、バグが発生する可能性があります。もしバグが発生した場合、GitHub のリポジトリで Issue を開いてください。 +> +> NewPipe 及びいずれのフォークを Google Play ストアに公開すると、Google の取引条件の違反になります。 ## スクリーンショット diff --git a/doc/README.ko.md b/doc/README.ko.md index 0ba15ab43..dd9eb7705 100644 --- a/doc/README.ko.md +++ b/doc/README.ko.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -경고: 이 버전은 베타 버전이므로, 버그가 발생할 수도 있습니다. 만약 버그가 발생하였다면, 우리의 GitHub 저장소에서 Issue를 열람하여 주십시오. - -NewPipe 또는 NewPipe 포크를 구글 플레이스토어에 올리는 것은 그들의 이용약관을 위반합니다. +> [!warning] +> 이 버전은 베타 버전이므로, 버그가 발생할 수도 있습니다. 만약 버그가 발생하였다면, 우리의 GitHub 저장소에서 Issue를 열람하여 주십시오. +> +> NewPipe 또는 NewPipe 포크를 구글 플레이스토어에 올리는 것은 그들의 이용약관을 위반합니다. ## 스크린샷 diff --git a/doc/README.pa.md b/doc/README.pa.md index d40e1b84f..321e6b7d0 100644 --- a/doc/README.pa.md +++ b/doc/README.pa.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -ਚੇਤਾਵਨੀ: ਇਹ ਐਪ ਬੀਟਾ ਵਿੱਚ ਹੈ, ਇਸ ਲਈ ਤੁਸੀਂ ਬੱਗ ਦਾ ਸਾਹਮਣਾ ਕਰ ਸਕਦੇ ਹੋ। ਜੇਕਰ ਤੁਸੀਂ ਅਜਿਹਾ ਕਰਦੇ ਹੋ, ਤਾਂ ਟੈਮਪਲੇਟ ਨੂੰ ਭਰ ਕੇ ਸਾਡੀ ਗਿਟਹੱਬ ਰਿਪੋਜ਼ਟਰੀ ਵਿੱਚ ਇੱਕ ਮੁੱਦਾ ਖੋਲ੍ਹੋ - -ਗੂਗਲ ਪਲੇ ਸਟੋਰ ਵਿੱਚ ਨਿਊਪਾਈਪ ਜਾਂ ਇਸ ਦਾ ਕੋਈ ਵੀ ਫੋਰਕ ਲਗਾਉਣਾ ਉਹਨਾਂ ਦੇ ਨਿਯਮਾਂ ਅਤੇ ਸ਼ਰਤਾਂ ਦੀ ਉਲੰਘਣਾ ਕਰਦਾ ਹੈ। +> [!warning] +> ਇਹ ਐਪ ਬੀਟਾ ਵਿੱਚ ਹੈ, ਇਸ ਲਈ ਤੁਸੀਂ ਬੱਗ ਦਾ ਸਾਹਮਣਾ ਕਰ ਸਕਦੇ ਹੋ। ਜੇਕਰ ਤੁਸੀਂ ਅਜਿਹਾ ਕਰਦੇ ਹੋ, ਤਾਂ ਟੈਮਪਲੇਟ ਨੂੰ ਭਰ ਕੇ ਸਾਡੀ ਗਿਟਹੱਬ ਰਿਪੋਜ਼ਟਰੀ ਵਿੱਚ ਇੱਕ ਮੁੱਦਾ ਖੋਲ੍ਹੋ +> +> ਗੂਗਲ ਪਲੇ ਸਟੋਰ ਵਿੱਚ ਨਿਊਪਾਈਪ ਜਾਂ ਇਸ ਦਾ ਕੋਈ ਵੀ ਫੋਰਕ ਲਗਾਉਣਾ ਉਹਨਾਂ ਦੇ ਨਿਯਮਾਂ ਅਤੇ ਸ਼ਰਤਾਂ ਦੀ ਉਲੰਘਣਾ ਕਰਦਾ ਹੈ। ## ਸਕਰੀਨਸ਼ਾਟ diff --git a/doc/README.pl.md b/doc/README.pl.md index 6412e10f5..64f75eaea 100644 --- a/doc/README.pl.md +++ b/doc/README.pl.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -UWAGA: TO JEST WERSJA BETA, WIĘC MOŻESZ NAPOTKAĆ BŁĘDY. JEŚLI TAK SIĘ STANIE, OTWÓRZ NOWY "ISSUE" NA GITHUBIE. - -PUBLIKOWANIE NEWPIPE LUB JAKIEGOKOLWIEK JEGO FORKU W SKLEPIE PLAY NARUSZA ICH WARUNKI UŻYTKOWANIA. +> [!warning] +> TO JEST WERSJA BETA, WIĘC MOŻESZ NAPOTKAĆ BŁĘDY. JEŚLI TAK SIĘ STANIE, OTWÓRZ NOWY "ISSUE" NA GITHUBIE. +> +> PUBLIKOWANIE NEWPIPE LUB JAKIEGOKOLWIEK JEGO FORKU W SKLEPIE PLAY NARUSZA ICH WARUNKI UŻYTKOWANIA. ## Screenshoty diff --git a/doc/README.pt_BR.md b/doc/README.pt_BR.md index a439adc68..01fa718ad 100644 --- a/doc/README.pt_BR.md +++ b/doc/README.pt_BR.md @@ -19,9 +19,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -AVISO: ESTA É UMA VERSÃO BETA, PORTANTO, VOCÊ PODE ENCONTRAR BUGS. ENCONTROU ALGUM, ABRA UM ISSUE ATRAVÉS DO NOSSO REPOSITÓRIO GITHUB. - -COLOCAR NEWPIPE OU QUALQUER FORK DELE NA GOOGLE PLAY STORE VIOLA SEUS TERMOS E CONDIÇÕES. +> [!warning] +> ESTA É UMA VERSÃO BETA, PORTANTO, VOCÊ PODE ENCONTRAR BUGS. ENCONTROU ALGUM, ABRA UM ISSUE ATRAVÉS DO NOSSO REPOSITÓRIO GITHUB. +> +> COLOCAR NEWPIPE OU QUALQUER FORK DELE NA GOOGLE PLAY STORE VIOLA SEUS TERMOS E CONDIÇÕES. ## Screenshots diff --git a/doc/README.ro.md b/doc/README.ro.md index 77a6798bf..40e61e015 100644 --- a/doc/README.ro.md +++ b/doc/README.ro.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -Atenţionare: ACEASTA ESTE O VERSIUNE BETA, AŞA CĂ S-AR PUTE SĂ ÎNTÂLNIŢI ERORI. DACĂ SE ÎNTÂMPLĂ ACEST LUCRU, DESCHIDEŢI UN ISSUE PRIN REPSITORY-UL NOSTRU GITHUB. - -PUNERA NEWPIPE SAU ORICĂRUI FORK AL ACESTUIA ÎN MAGAZINUL GOOGLE PLAY LE ÎNCALCĂ TERMENII ŞI CONDIŢIILE. +> [!warning] +> ACEASTA ESTE O VERSIUNE BETA, AŞA CĂ S-AR PUTE SĂ ÎNTÂLNIŢI ERORI. DACĂ SE ÎNTÂMPLĂ ACEST LUCRU, DESCHIDEŢI UN ISSUE PRIN REPSITORY-UL NOSTRU GITHUB. +> +> PUNERA NEWPIPE SAU ORICĂRUI FORK AL ACESTUIA ÎN MAGAZINUL GOOGLE PLAY LE ÎNCALCĂ TERMENII ŞI CONDIŢIILE. ## Capturi de ecran diff --git a/doc/README.ru.md b/doc/README.ru.md index 1590b2399..35058c981 100644 --- a/doc/README.ru.md +++ b/doc/README.ru.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -ВНИМАНИЕ: ЭТО ПРИЛОЖЕНИЕ НАХОДИТСЯ В СТАДИИ БЕТА, ПОЭТОМУ ВЫ МОЖЕТЕ ОБНАРУЖИТЬ ОШИБКИ. ЕСЛИ ЭТО СЛУЧИТСЯ, ОТКРОЙТЕ ВОПРОС В НАШЕМ РЕПОЗИТОРИИ GITHUB, ЗАПОЛНЯ ЕГО ПО ШАБЛОНУ. - -РАЗМЕЩЕНИЕ NEWPIPE ИЛИ ЕГО ОТВЕТВЛЕНИЙ В МАГАЗИНЕ GOOGLE PLAY НАРУШАЕТ ИХ УСЛОВИЯ И ПОЛОЖЕНИЯ. +> [!warning] +> ЭТО ПРИЛОЖЕНИЕ НАХОДИТСЯ В СТАДИИ БЕТА, ПОЭТОМУ ВЫ МОЖЕТЕ ОБНАРУЖИТЬ ОШИБКИ. ЕСЛИ ЭТО СЛУЧИТСЯ, ОТКРОЙТЕ ВОПРОС В НАШЕМ РЕПОЗИТОРИИ GITHUB, ЗАПОЛНЯ ЕГО ПО ШАБЛОНУ. +> +> РАЗМЕЩЕНИЕ NEWPIPE ИЛИ ЕГО ОТВЕТВЛЕНИЙ В МАГАЗИНЕ GOOGLE PLAY НАРУШАЕТ ИХ УСЛОВИЯ И ПОЛОЖЕНИЯ. ## Скриншоты diff --git a/doc/README.ryu.md b/doc/README.ryu.md index fe30a9ba9..22ac6343d 100644 --- a/doc/README.ryu.md +++ b/doc/README.ryu.md @@ -18,9 +18,10 @@ *ふかぬぎんぐっしゆむん: [English](../README.md), [Español](README.es.md), [हिन्दी](README.hi.md), [한국어](README.ko.md), [Soomaali](README.so.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [日本語](README.ja.md), [Română](README.ro.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -ちゅうい:くれーベータばんぬたみ、バグぬはっせいすがのうゆいがあいびーん。むしバグぬはっせいさーあい、GitHubぬリポジトリっしIssueふぃらちくぃみそーれー。 - -NewPipeうゆびいじりぬフォークGoogle Playストアんかいかんかいしーねー、Googleぬとぅいふぃちじょうきんぬいはんになります。 +> [!warning] +> くれーベータばんぬたみ、バグぬはっせいすがのうゆいがあいびーん。むしバグぬはっせいさーあい、GitHubぬリポジトリっしIssueふぃらちくぃみそーれー。 +> +> NewPipeうゆびいじりぬフォークGoogle Playストアんかいかんかいしーねー、Googleぬとぅいふぃちじょうきんぬいはんになります。 ## スクリーンショット diff --git a/doc/README.so.md b/doc/README.so.md index 56a696db3..1d3351256 100644 --- a/doc/README.so.md +++ b/doc/README.so.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -DIGNIIN: MIDKAN, NOOCA APP-KA EE HADDA WALI TIJAABO AYUU KU JIRAA, SIDAA DARTEED CILLADO AYAAD LA KULMI KARTAA. HADAAD LA KULANTO, KA FUR ARIN SHARAXAYA QAYBTANADA ARRIMAHA EE GITHUB-KA. - -NEWPIPE AMA KUWA KU SALAYSAN IN PLAYSTORE-KA LA GALIYO WAXAY KA HOR IMANAYSAA SHARCIGA IYO SHURUUDAHA AY LEEYIHIIN. +> [!warning] +> MIDKAN, NOOCA APP-KA EE HADDA WALI TIJAABO AYUU KU JIRAA, SIDAA DARTEED CILLADO AYAAD LA KULMI KARTAA. HADAAD LA KULANTO, KA FUR ARIN SHARAXAYA QAYBTANADA ARRIMAHA EE GITHUB-KA. +> +> NEWPIPE AMA KUWA KU SALAYSAN IN PLAYSTORE-KA LA GALIYO WAXAY KA HOR IMANAYSAA SHARCIGA IYO SHURUUDAHA AY LEEYIHIIN. ## Sawir-shaashadeed diff --git a/doc/README.sr.md b/doc/README.sr.md index 775585f1a..7f0ee65b7 100644 --- a/doc/README.sr.md +++ b/doc/README.sr.md @@ -21,9 +21,10 @@ *Прочитајте овај документ на другим језицима: [Deutsch](README.de.md), [English](README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [Српски](README.sr.md)* -УПОЗОРЕЊЕ: ОВА АПЛИКАЦИЈА ЈЕ У БЕТА ВЕРЗИЈИ, ТАКО ДА МОЖЕТЕ НАИЋИ НА ГРЕШКЕ. АКО НАИЂЕТЕ, НАПИШИТЕ ИЗВЕШТАЈ У НАШЕМ GITHUB РЕПОЗИТОРИЈУМУ ПОПУЊАВАЊЕМ ШАБЛОНА ИЗВЕШТАЈА. - -ПОСТАВЉАЊЕ АПЛИКАЦИЈЕ NEWPIPE ИЛИ БИЛО КОГ ЊЕНОГ ФОРКА, У GOOGLE PLAY ПРОДАВНИЦУ КРШИ ЊИХОВЕ УСЛОВЕ И ОДРЕДБЕ. +> [!warning] +> ОВА АПЛИКАЦИЈА ЈЕ У БЕТА ВЕРЗИЈИ, ТАКО ДА МОЖЕТЕ НАИЋИ НА ГРЕШКЕ. АКО НАИЂЕТЕ, НАПИШИТЕ ИЗВЕШТАЈ У НАШЕМ GITHUB РЕПОЗИТОРИЈУМУ ПОПУЊАВАЊЕМ ШАБЛОНА ИЗВЕШТАЈА. +> +> ПОСТАВЉАЊЕ АПЛИКАЦИЈЕ NEWPIPE ИЛИ БИЛО КОГ ЊЕНОГ ФОРКА, У GOOGLE PLAY ПРОДАВНИЦУ КРШИ ЊИХОВЕ УСЛОВЕ И ОДРЕДБЕ. ## Снимци екрана diff --git a/doc/README.tr.md b/doc/README.tr.md index a9c91bfbd..d8accd5a7 100644 --- a/doc/README.tr.md +++ b/doc/README.tr.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -UYARI: BU SÜRÜM BETA SÜRÜMÜDÜR, BU NEDENLE HATALARLA KARŞILAŞABİLİRSİNİZ. HATA BULURSANIZ BU GITHUB DEPOSUNDA BUNU BİLDİRİN. - -GOOGLE PLAY STORE'A NEWPIPE VEYA BAŞKA BİR KOPYASINI KOYMAK, PLAY STORE ŞARTLARINI VE KOŞULLARINI İHLAL EDER. +> [!warning] +> BU SÜRÜM BETA SÜRÜMÜDÜR, BU NEDENLE HATALARLA KARŞILAŞABİLİRSİNİZ. HATA BULURSANIZ BU GITHUB DEPOSUNDA BUNU BİLDİRİN. +> +> GOOGLE PLAY STORE'A NEWPIPE VEYA BAŞKA BİR KOPYASINI KOYMAK, PLAY STORE ŞARTLARINI VE KOŞULLARINI İHLAL EDER. ## Ekran görüntüleri diff --git a/doc/README.zh_TW.md b/doc/README.zh_TW.md index a21f2ca4c..445618b1c 100644 --- a/doc/README.zh_TW.md +++ b/doc/README.zh_TW.md @@ -18,9 +18,10 @@ *Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)* -警告:這是測試版本,可能會發生錯誤。如果遇到錯誤,請在我們的 GITHUB REPO 開 ISSUE 回報。 - -將 NEWPIPE 或其任何分支上傳至 GOOGLE PLAY 商店違反了他們的使用者合約。 +> [!warning] +> 這是測試版本,可能會發生錯誤。如果遇到錯誤,請在我們的 GITHUB REPO 開 ISSUE 回報。 +> +> 將 NEWPIPE 或其任何分支上傳至 GOOGLE PLAY 商店違反了他們的使用者合約。 ## 截圖 diff --git a/fastlane/metadata/android/ar/changelogs/996.txt b/fastlane/metadata/android/ar/changelogs/996.txt new file mode 100644 index 000000000..1dd4e3de5 --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/996.txt @@ -0,0 +1,2 @@ +تم إصلاح NullPointerException عند فتح قناة / مؤتمر في media.ccc.de. +حاول احدهم كسر الهدية التي قدمناها لك، لكننا تخطينا الأمر. diff --git a/fastlane/metadata/android/bg/changelogs/64.txt b/fastlane/metadata/android/bg/changelogs/64.txt new file mode 100644 index 000000000..f5ece1568 --- /dev/null +++ b/fastlane/metadata/android/bg/changelogs/64.txt @@ -0,0 +1,6 @@ +### Подобрения +-Добавена е възможността да се ограничи качеството на видеото ако се използва мобилен интернет. #1339 +- Запаметява яркостта за сесията #1442 +- Оптимизиране при изтегляне с по-слаби процесори #1431 +### Поправени +- Поправен срив при отваряне на изтегления #1441 diff --git a/fastlane/metadata/android/bn/changelogs/64.txt b/fastlane/metadata/android/bn/changelogs/64.txt index d5e5901e5..e76c3130e 100644 --- a/fastlane/metadata/android/bn/changelogs/64.txt +++ b/fastlane/metadata/android/bn/changelogs/64.txt @@ -1,5 +1,5 @@ ### অগ্রগতিসমূহ -- মোবাইল ডাটা ব্যবহার করার সময় ভিডিও মানের সীমা নির্ধারণ করা যাবে। #1339। +- মোবাইল ডাটা ব্যবহার করার সময় ভিডিও মানের সীমা নির্ধারণ করা যাবে। #1339। - প্লেয়ারের উজ্জ্বলতা মনে রাখুন #1442। - দুর্বল সিপিইউর ডিভাইসে পারফরম্যান্স উন্নত করা হয়েছে #1431। - মিডিয়া সেশন এখন সঠিকভাবে কাজ করে #1433। diff --git a/fastlane/metadata/android/cs/changelogs/996.txt b/fastlane/metadata/android/cs/changelogs/996.txt new file mode 100644 index 000000000..a220330bc --- /dev/null +++ b/fastlane/metadata/android/cs/changelogs/996.txt @@ -0,0 +1,2 @@ +Opravena chyba NullPointerException při otevírání kanálu / konference na media.ccc.de. +Ježíšek nám málem doručil rozbitý dárek pro vás, ale opravili jsme to. diff --git a/fastlane/metadata/android/de/changelogs/65.txt b/fastlane/metadata/android/de/changelogs/65.txt index 3405af363..6815d432a 100644 --- a/fastlane/metadata/android/de/changelogs/65.txt +++ b/fastlane/metadata/android/de/changelogs/65.txt @@ -1,4 +1,5 @@ Verbesserungen + -Menüanimation deaktiviert #1486 -Löschen von Downloads rückgängig machen #1472 -Option zum Downloaden im Teilen-Menü #1498 @@ -6,5 +7,4 @@ Verbesserungen -Minimieren der Wiedergabe beim Beenden #1354 -Datenbank Versions Aktualisierung und Datenbestand Wiederherstellung #1510 -ExoPlayer 2.8.2 aktualisiert #1392 --Überarbeitung der Wiedergabegeschwindigkeitskontrolle um Änderungen besser zu unterstützen --hinzufügen der Möglichkeit Stille zu überspringen(Hilfreich bei Audiobücher und einigen Musikarten) unterstützt dadurch eine nahtlose Wiedergabe (Nicht so toll bei Liedern, in denen es auch stille Passagen gibt.) +-Überarbeitung der Wiedergabegeschwindigkeitskontrolle um Änderungen besser zu unterstützen. diff --git a/fastlane/metadata/android/de/changelogs/66.txt b/fastlane/metadata/android/de/changelogs/66.txt new file mode 100644 index 000000000..937bfd642 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/66.txt @@ -0,0 +1,18 @@ +# Versionsänderung von v0.13.7 + +### Behebungen +- Probleme der Filterung von v.0.13.6 behoben + +# Versionsänderung von v0.13.6 + +### Verbesserungen + +- Animation der Burgermenü-Icon entfernt #1486 +- Löschen von Downloads rückgängig machen #1472 +- Im "Teilen"-Tab die Download option hinzugefügt #1498 +- Option "Teilen" zum Long-Tap-Menü hinzugefügt #1454 + +### Behebungen + +- Probleme mit dem kaputten Video Info layout behoben. #1440 / #1491 +- Probleme mit dem Verlauf wurden behoben. #1497. diff --git a/fastlane/metadata/android/de/changelogs/68.txt b/fastlane/metadata/android/de/changelogs/68.txt new file mode 100644 index 000000000..6cdbd55a0 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/68.txt @@ -0,0 +1,19 @@ +# Änderungen von v0.14.1 + +### Fixed +- nicht entschlüsselt Video url #1659 +- Beschreibungs Link nicht extrahierbar #1657 + +# Änderungen von v0.14.0 + +### New +- Neues Schubladendesign #1461 +- Neue anpassbare Titelseite #1461 + +### Verbesserungen +- Reworked Gesture Controls #1604 +- Neue Möglichkeit, den Popup-Player #1597 zu schließen + +### Fixed +- Fehler beheben, wenn die Anzahl der Abonnements nicht verfügbar ist. Schließt #1649. +- Zeigen Sie "Abonnentenzählung nicht verfügbar" in diesen Fällen. diff --git a/fastlane/metadata/android/de/changelogs/69.txt b/fastlane/metadata/android/de/changelogs/69.txt new file mode 100644 index 000000000..8802508a3 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/69.txt @@ -0,0 +1,14 @@ +### New +- Long-tap Löschen & Teilen in Abonnements #1516 +- Tablet UI & Rasterlistenlayout #1617 + +### Verbesserungen +- Speichern und Nachladen des zuletzt verwendeten Seitenverhältnisses #1748 +- Separate Einstellungen für Lautstärke & Helligkeitsgesten #1644 + +### Fixes +- Anzahl der Abonnements +- Foreground Service Erlaubnis für API 28+ Geräte #1830 hinzugefügt + +### Known Bugs +- Wiedergabe kann nicht auf Android P gespeichert werden diff --git a/fastlane/metadata/android/de/changelogs/70.txt b/fastlane/metadata/android/de/changelogs/70.txt new file mode 100644 index 000000000..59a09b210 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/70.txt @@ -0,0 +1,10 @@ +ACHTUNG: Diese Version ist wahrscheinlich ein Bugfest. + +### Verbesserungen +* heruntergeladene Dateien können mit einem Klick #1879 geöffnet werden +* Drop-Unterstützung für Android 4.1 - 4.3 #1884 +* Streams aus der aktuellen Warteschlange entfernt, indem sie nach rechts swipen #1915 + +### Fixed +* Crash mit Standard-Auflösung eingestellt auf beste und begrenzte mobile Datenauflösung #1835 +* Pop-up-Spieler-Absturz behoben #1874 diff --git a/fastlane/metadata/android/de/changelogs/71.txt b/fastlane/metadata/android/de/changelogs/71.txt new file mode 100644 index 000000000..0132de3c3 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/71.txt @@ -0,0 +1,8 @@ +### Verbesserungen +* App-Update-Benachrichtigung für GitHub build hinzufügen (#1608 von @krtkush) +* Verschiedene Verbesserungen des Downloaders (#1944 von @kapodamy): +* Fügen Sie fehlende weiße Icons hinzu und verwenden Sie hardcored Weg, um die Icon Farben zu ändern +* neue MPEG-4 muxer fixieren nicht-synchrone Video- und Audiostreams (#2039) + +### Fixed +* YouTube Live-Streams spielen nach kurzer Zeit (#1996 von @yausername) diff --git a/fastlane/metadata/android/de/changelogs/740.txt b/fastlane/metadata/android/de/changelogs/740.txt new file mode 100644 index 000000000..0d28a157a --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/740.txt @@ -0,0 +1,12 @@ +

Verbesserungen

+
    +
  • Make Links in Kommentare klickbar, erhöhen Textgröße
  • +
  • seek zum Klicken von Zeitstempel-Links in Kommentare
  • +
  • Beliebte Registerkarte basierend auf kürzlich ausgewähltem Zustand anzeigen
  • +
  • Add-Unterstützung für Invidious links
  • +
+

Fixed

+
    +
  • fixed scroll w/kommentare und verwandten Streams deaktiviert
  • +
  • fixiert CheckForNewAppVersionTask wird ausgeführt, wenn es sollten't
  • +
diff --git a/fastlane/metadata/android/de/changelogs/750.txt b/fastlane/metadata/android/de/changelogs/750.txt new file mode 100644 index 000000000..78a25282a --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/750.txt @@ -0,0 +1,15 @@ +Neu +Playback Lebenslauf #2288 +• Resume Streams, wo Sie letztes Mal aufgehört haben +Downloader Verbesserungen #2149 + + +Verbessert +• Gemaketten entfernen #2295 +• Handle (auto)Rotationsänderungen während des Aktivitätszyklus #2444 + +Behoben +• Fixed Downloads bei 99,9% #2440 +• Aktualisieren der Spielwarteschlange Metadaten #2453 +• [SoundCloud] Fester Absturz beim Laden von Wiedergabelisten TeamNewPipe/NewPipeExtractor#170 +• [YouTube] Feste Dauer kann nicht paresd TeamNewPipe/NewPipeExtractor#177 diff --git a/fastlane/metadata/android/de/changelogs/760.txt b/fastlane/metadata/android/de/changelogs/760.txt new file mode 100644 index 000000000..f0eac002f --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/760.txt @@ -0,0 +1,24 @@ +Veränderung in 0,17.1 + +Neu +• thailändische Lokalisierung + + +Verbessert +• Schalter für SAF / Legacy Datei Picker #2521 hinzufügen + +Behoben +• Löschen Sie Löschtasten in Downloads Ansicht beim Umschalten von Apps #2487 + +Veränderungen in 0,17,0 + +Neu +Playback Lebenslauf #2288 +• Resume Streams, wo Sie letztes Mal aufgehört haben +Downloader Verbesserungen #2149 + +Verbessert +• Gemaketten entfernen #2295 + +Behoben +• Behoben ausgewählter Subtitle Track Name nicht angezeigt #2394 diff --git a/fastlane/metadata/android/de/changelogs/780.txt b/fastlane/metadata/android/de/changelogs/780.txt new file mode 100644 index 000000000..4deac3709 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/780.txt @@ -0,0 +1,11 @@ +Veränderungen in 0,17,3 + +Verbessert +• Hinzugefügte Option, um die Wiedergabezustände #2550 zu löschen +• Versteckte Verzeichnisse in der Datei Picker #2591 anzeigen +• Unterstützung von URLs von `invidio.us` Instanzen, die mit NewPipe #2488 geöffnet werden sollen +• Unterstützung für `music.youtube.com` URLs TeamNewPipe/NewPipeExtractor#194 + +Behoben +• [YouTube] Behoben 'java.lang.IllegalArgumentException #192 +• [YouTube] Feste Live-Streams nicht arbeiten TeamNewPipe/NewPipeExtractor#195 diff --git a/fastlane/metadata/android/de/changelogs/790.txt b/fastlane/metadata/android/de/changelogs/790.txt new file mode 100644 index 000000000..345cb37aa --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/790.txt @@ -0,0 +1,13 @@ +Verbessert +• Weitere Titel hinzufügen, um die Zugänglichkeit für Blinde zu verbessern #2655 +• Sprache des Download-Ordners konsistenter und weniger eindeutig #2637 + +Behoben +• Fixed Scrolling in Video Detail Fragment #2672 +• Entfernen Sie doppelte Suche klare Box-Animationen zu einem #2695 +• [SoundCloud] Client_id Extraktion fix #2745 + +Entwicklung +• Hinzufügen fehlender Abhängigkeiten von NewPipeExtractor in NewPipe #2535 +• Migration auf AndroidX #2685 +• Update auf ExoPlayer 2.10.6 #2697, #2736 diff --git a/fastlane/metadata/android/de/changelogs/800.txt b/fastlane/metadata/android/de/changelogs/800.txt new file mode 100644 index 000000000..792b15461 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/800.txt @@ -0,0 +1,11 @@ +Neu +• PeerTube Unterstützung ohne P2P (#2201) (Beta) + Es könnte Probleme mit SSL Handshakes auf Android 4.4 & 7.1 geben, wenn auf bestimmte Instanzen zugegriffen wird. + +Verbessert +• Neue Localization and Downloader von Extractor #2713 +• Schwarze Navigationsleiste für schwarzes Theme #2569 + +Behoben +• Fehler wurde behoben, der den Popup-Player nicht verschieben konnte, wenn ein anderer Finger beim Verschieben des Popup-Players #2772 platziert wurde +• [SoundCloud] Audio-Stream-Extraktion diff --git a/fastlane/metadata/android/de/changelogs/810.txt b/fastlane/metadata/android/de/changelogs/810.txt new file mode 100644 index 000000000..9b57ce03c --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/810.txt @@ -0,0 +1,11 @@ +Neu +• Video Vorschau auf den Sperrbildschirm beim Spielen im Hintergrund anzeigen + +Verbessert +• Fügen Sie lokale Wiedergabeliste hinzu, um zu löschen, wenn lange Drücken auf Hintergrund / Popup-Taste + +Behoben +• Mehrere Probleme behoben, wenn ein Video zu NewPipe geteilt und seine Streams direkt herunterladen +• [YouTube] Festes Einschalten Null verursacht NPE +• [YouTube] Behobene Betrachtungskommentare beim Öffnen eines invidio.us url +• [SoundCloud] Update von client_id diff --git a/fastlane/metadata/android/de/changelogs/840.txt b/fastlane/metadata/android/de/changelogs/840.txt new file mode 100644 index 000000000..73e9904ec --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/840.txt @@ -0,0 +1,10 @@ +Neu +• Sprachauswahl hinzugefügt, um die App-Sprache zu ändern +• Hinzugefügt senden zu Kodi Taste zu Spieler zusammenklappbare Menü + +Verbessert +• Entfernen von dot-menu für Schublade und verstecken Geschichte Taste, wenn die Uhr Geschichte nicht in den Einstellungen aktiviert ist + +Behoben +• Fix initialer Helligkeitswert für die Geste +• [YouTube] Timestamps in der Beschreibung sind wieder anklickbar diff --git a/fastlane/metadata/android/de/changelogs/930.txt b/fastlane/metadata/android/de/changelogs/930.txt new file mode 100644 index 000000000..e2ddba83f --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/930.txt @@ -0,0 +1,12 @@ +Neu +• Suche auf YouTube Musik +• Basic Android TV-Unterstützung + +Verbessert +• Hinzugefügt die Fähigkeit, alle beobachteten Videos von einer lokalen Wiedergabeliste zu entfernen +• Nachricht anzeigen, wenn der Inhalt noch nicht unterstützt wird, anstatt zu stürzen + +Behoben +• Feste altersbeschränkte Inhaltseinstellung nicht funktionieren +• Behoben bestimmte Arten von reCAPTCHAs +• Feste Sichtbarkeit der Gruppen-Sort-Taste im Abonnement-Bereich diff --git a/fastlane/metadata/android/de/changelogs/940.txt b/fastlane/metadata/android/de/changelogs/940.txt index 3cbf73516..e96340b16 100644 --- a/fastlane/metadata/android/de/changelogs/940.txt +++ b/fastlane/metadata/android/de/changelogs/940.txt @@ -10,4 +10,3 @@ Verbessert Behoben - Einfrieren des Download-Dialogs behoben -... diff --git a/fastlane/metadata/android/de/changelogs/951.txt b/fastlane/metadata/android/de/changelogs/951.txt index 7c6d93982..790d6329d 100644 --- a/fastlane/metadata/android/de/changelogs/951.txt +++ b/fastlane/metadata/android/de/changelogs/951.txt @@ -8,4 +8,3 @@ Neu Verbessert - Anwendungsmetadaten nicht mehr in gemuxte Dateien schreiben - Fehlerhafte Streams nicht aus der Warteschlange entfernen -... diff --git a/fastlane/metadata/android/de/changelogs/990.txt b/fastlane/metadata/android/de/changelogs/990.txt index a629c8654..6e56a8e70 100644 --- a/fastlane/metadata/android/de/changelogs/990.txt +++ b/fastlane/metadata/android/de/changelogs/990.txt @@ -8,8 +8,6 @@ Neu: Verbessert: • Player-Code in kleine Komponenten refaktorisiert: weniger RAM-Verbrauch, weniger Bugs • Skalierungsmodus für Miniaturansicht -… Behoben: • Verschiedene Probleme mit Player-Benachrichtigung: veraltete/fehlende Medieninfos, verzerrte Miniaturansicht -… diff --git a/fastlane/metadata/android/de/changelogs/995.txt b/fastlane/metadata/android/de/changelogs/995.txt index 9c9ed1147..0c026e50f 100644 --- a/fastlane/metadata/android/de/changelogs/995.txt +++ b/fastlane/metadata/android/de/changelogs/995.txt @@ -11,6 +11,4 @@ Verbessert Behoben • [YouTube] Erhalten der Like-Anzahl • Nicht reagierende Popups und Abstürze des Players -• Auswahl der falschen Sprachen in der Sprachauswahl -• Audio-Fokus des Players respektiert keine Stummschaltung -… +• Hinzufügen von Wiedergabelistenelementen funktionierte gelegentlich nicht diff --git a/fastlane/metadata/android/de/changelogs/996.txt b/fastlane/metadata/android/de/changelogs/996.txt new file mode 100644 index 000000000..bcdea8f4f --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/996.txt @@ -0,0 +1,2 @@ +Eine NullPointerException beim Öffnen eines Kanals / einer Konferenz in media.ccc.de behoben. +Der Grinch hat versucht, unser Weihnachtsgeschenk für Sie zu zerstören, aber wir haben es behoben. diff --git a/fastlane/metadata/android/de/full_description.txt b/fastlane/metadata/android/de/full_description.txt index a476e427e..ef2ac9577 100644 --- a/fastlane/metadata/android/de/full_description.txt +++ b/fastlane/metadata/android/de/full_description.txt @@ -1 +1 @@ -NewPipe verwendet keine Bibliotheken des Google-Frameworks oder der YouTube-API. Es analysiert die Website, um die benötigten Informationen zu erlangen. Aus diesem Grund kann die App ohne die Google Services verwendet werden. Ebenso wird kein YouTube-Konto für NewPipe benötigt und es ist FLOSS (Freie Software / Open-Source-Software). +NewPipe verwendet keine Bibliotheken des Google-Frameworks oder der YouTube-API. Es analysiert die Website, um die benötigten Informationen zu erlangen. Aus diesem Grund kann die App ohne die Google Services verwendet werden. Ebenso wird kein YouTube-Konto für NewPipe benötigt und es ist FOSS (Freie Software / Open-Source-Software). diff --git a/fastlane/metadata/android/de/short_description.txt b/fastlane/metadata/android/de/short_description.txt index 138e55523..ec47872eb 100644 --- a/fastlane/metadata/android/de/short_description.txt +++ b/fastlane/metadata/android/de/short_description.txt @@ -1 +1 @@ -Eine freie, leichtgewichtige YouTube-App für Android. +Eine freie, leichte YouTube-App für Android. diff --git a/fastlane/metadata/android/el/changelogs/65.txt b/fastlane/metadata/android/el/changelogs/65.txt new file mode 100644 index 000000000..0417512c7 --- /dev/null +++ b/fastlane/metadata/android/el/changelogs/65.txt @@ -0,0 +1,26 @@ +### Βελτιώσεις + +- Απενεργοποίηση της κίνησης του εικονιδίου του burgermenu #1486 +- Αναίρεση διαγραφής λήψεων #1472 +- Επιλογή λήψης στο μενού κοινής χρήσης #1498 +- Προστέθηκε επιλογή κοινής χρήσης στο μενού παρατεταμένου πατήματος #1454 +- Ελαχιστοποίηση του κύριου προγράμματος αναπαραγωγής κατά την έξοδο #1354 +- Ενημέρωση της έκδοσης της βιβλιοθήκης και διόρθωση αντιγράφων ασφαλείας της βάσης δεδομένων #1510 +- Ενημέρωση ExoPlayer 2.8.2 #1392 + - Ανασχεδιάστηκε ο διάλογος ελέγχου της ταχύτητας αναπαραγωγής ώστε να υποστηρίζει διαφορετικά μεγέθη βημάτων για ταχύτερη αλλαγή ταχύτητας. + - Προστέθηκε μια εναλλαγή για γρήγορη μετακίνηση προς τα εμπρός κατά τη διάρκεια σιωπής στον έλεγχο ταχύτητας αναπαραγωγής. Αυτό θα πρέπει να είναι χρήσιμο για τα ακουστικά βιβλία και ορισμένα είδη μουσικής και μπορεί να φέρει μια πραγματικά απρόσκοπτη εμπειρία (και μπορεί να σπάσει ένα τραγούδι με πολλές σιωπές =\\). + - Αναδιαμόρφωση της ανάλυσης πηγής πολυμέσων ώστε να επιτρέπεται η μετάδοση μεταδεδομένων μαζί με τα πολυμέσα εσωτερικά στον αναπαραγωγέα, αντί να γίνεται χειροκίνητα. Τώρα έχουμε μια ενιαία πηγή μεταδεδομένων και είναι άμεσα διαθέσιμα κατά την έναρξη της αναπαραγωγής. + - Διορθώθηκε η μη ενημέρωση των απομακρυσμένων μεταδεδομένων της λίστας αναπαραγωγής όταν είναι διαθέσιμα νέα μεταδεδομένα κατά το άνοιγμα του τμήματος της λίστας αναπαραγωγής. + - Διάφορες διορθώσεις στο UI: #1383, τα στοιχεία ελέγχου ειδοποίησης αναπαραγωγής στο παρασκήνιο είναι τώρα πάντα λευκά, είναι ευκολότερο να κλείσετε το αναδυόμενο πρόγραμμα αναπαραγωγής μέσω εκτίναξης +- Χρήση νέου εξαηωγέα με ανασχεδιασμένη αρχιτεκτονική για πολλαπλές υπηρεσίες + +### Διορθώσεις + +- Διόρθωση #1440 Προβληματική διάταξη πληροφοριών βίντεο #1491 +- Διόρθωση ιστορικού προβολής #1497 + - #1495, ενημέρωση των μεταδεδομένων (μικρογραφία, τίτλος και αριθμός βίντεο) μόλις ο χρήστης αποκτήσει πρόσβαση στη λίστα αναπαραγωγής. + - #1475, με την καταχώριση μιας προβολής στη βάση δεδομένων όταν ο χρήστης ξεκινάει ένα βίντεο σε εξωτερική συσκευή αναπαραγωγής σε λεπτομερές θραύσμα. +- Διόρθωση του χρονικού ορίου creen σε περίπτωση λειτουργίας αναδυόμενου παραθύρου. #1463 (Διορθώθηκε το #640) +- Διόρθωση του κύριου προγράμματος αναπαραγωγής βίντεο #1509 + - #1412] Διορθώθηκε η λειτουργία επανάληψης που προκαλεί NPE του αναπαραγωγέα όταν λαμβάνεται νέα εντολή ενώ εργάζεται στο παρασκήνιο. + - Διορθώθηκε κατά την ελαχιστοποίηση του αναπαραγωγέα σε αναδυόμενο παράθυρο, να μην τον καταστρέφει όταν δεν έχει χορηγηθεί άδεια αναδυόμενου παραθύρου. diff --git a/fastlane/metadata/android/el/changelogs/963.txt b/fastlane/metadata/android/el/changelogs/963.txt new file mode 100644 index 000000000..b6c30aa9d --- /dev/null +++ b/fastlane/metadata/android/el/changelogs/963.txt @@ -0,0 +1 @@ +• [YouTube] Διόρθωση της συνέχισης ενός καναλιού diff --git a/fastlane/metadata/android/el/changelogs/996.txt b/fastlane/metadata/android/el/changelogs/996.txt new file mode 100644 index 000000000..58223cb1a --- /dev/null +++ b/fastlane/metadata/android/el/changelogs/996.txt @@ -0,0 +1,2 @@ +Διορθώθηκε μια NullPointerException κατά το άνοιγμα ενός καναλιού/συνεδρίου στο media.ccc.de. +Ο Grinch προσπάθησε να σπάσει το χριστουγεννιάτικο δώρο μας προς εσάς, αλλά το διορθώσαμε. diff --git a/fastlane/metadata/android/en-US/changelogs/997.txt b/fastlane/metadata/android/en-US/changelogs/997.txt new file mode 100644 index 000000000..954324070 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/997.txt @@ -0,0 +1,17 @@ +New +• Add comment replies +• Allow reordering playlists +• Show playlist description and duration +• Allow resetting settings + +Improved +• [Android 13+] Restore custom notification actions +• Request consent for update check +• Allow notification play/pause while buffering +• Reorder some settings + +Fixed +• [YouTube] Fix comments not loading, plus other fixes and improvements +• Solve vulnerability in settings import and switch to JSON +• Various download fixes +• Trim search text \ No newline at end of file diff --git a/fastlane/metadata/android/es/changelogs/996.txt b/fastlane/metadata/android/es/changelogs/996.txt new file mode 100644 index 000000000..e620b525f --- /dev/null +++ b/fastlane/metadata/android/es/changelogs/996.txt @@ -0,0 +1,2 @@ +Corregida una NullPointerException al abrir un canal / conferencia en media.ccc.de. +El Grinch trató de romper nuestro regalo de Navidad para usted, pero lo arreglamos. diff --git a/fastlane/metadata/android/fr/changelogs/750.txt b/fastlane/metadata/android/fr/changelogs/750.txt index 1276a514d..422529abf 100644 --- a/fastlane/metadata/android/fr/changelogs/750.txt +++ b/fastlane/metadata/android/fr/changelogs/750.txt @@ -2,7 +2,7 @@ Nouveau Reprise de lecture #2288 • Reprise des flux où ils s'étaient arrêtés Améliorations du téléchargeur #2149 -• Utilisation du Storage Access Framework pour stocker sur cartes SD +• Utilisation du Storage Access Framework pour stocker sur cartes SD • Nouveau multiplexeur mp4 • Peut changer le dossier de téléchargement • Respect des réseaux tarifés @@ -10,12 +10,12 @@ Améliorations du téléchargeur #2149 Améliorations • Suppression des chaînes gema #2295 -• Gestion des changements de rotation #2444 +• Gestion des changements de rotation #2444 • Uniformisation des menus longue-pression #2368 Corrections • Nom de la piste de sous-titres sélectionnée qui ne s'affiche pas #2394 -• Ne plante pas quand la vérification de la mise à jour de l'application échoue #2423 +• Ne plante pas quand la vérification de la mise à jour de l'application échoue #2423 • Téléchargements bloqués à 99,9 % #2440 • Mise à jour des métadonnées de la file de lecture #2453 • [SoundCloud] Ne plante pas lors du chargement des playlists TeamNewPipe/NewPipeExtractor#170 diff --git a/fastlane/metadata/android/hi/changelogs/996.txt b/fastlane/metadata/android/hi/changelogs/996.txt new file mode 100644 index 000000000..0a9369d87 --- /dev/null +++ b/fastlane/metadata/android/hi/changelogs/996.txt @@ -0,0 +1,2 @@ +Media.ccc.de में चैनल/कॉन्फ़्रेंस खोलते समय NullPointerException को ठीक किया गया। +ग्रिंच ने आपको हमारा क्रिसमस उपहार तोड़ने की कोशिश की, लेकिन हमने इसे ठीक कर दिया। diff --git a/fastlane/metadata/android/hr/full_description.txt b/fastlane/metadata/android/hr/full_description.txt index 14826925e..1e3977d33 100644 --- a/fastlane/metadata/android/hr/full_description.txt +++ b/fastlane/metadata/android/hr/full_description.txt @@ -1 +1 @@ -NewPipe ne koristi nijednu Googleovu biblioteku niti YouTube API, već samo analizira web-stranicu kako bi pribavio potrebne podatke. Stoga se ovaj program može koristiti na uređajima bez da su Googleove usluge instalirane. Također, za korištenje programa NewPipe nije potreban YouTube račun. NewPipe je slobodan program otvorenog koda. +NewPipe ne koristi nijednu Googleovu biblioteku niti YouTube API, već samo analizira web-stranicu kako bi pribavio potrebne podatke. Stoga se ova aplikacija može koristiti na uređajima bez instaliranih Google usluga. Također, NewPipe se može koristiti bez YouTube računa. NewPipe je slobodna aplikacija otvorenog koda. diff --git a/fastlane/metadata/android/hu/short_description.txt b/fastlane/metadata/android/hu/short_description.txt index 50752eeaf..5e18acc4f 100644 --- a/fastlane/metadata/android/hu/short_description.txt +++ b/fastlane/metadata/android/hu/short_description.txt @@ -1 +1 @@ -Ingyenes, könnyű YouTube felület Androidra. +Egy ingyenes, könnyű YouTube felület Androidra. diff --git a/fastlane/metadata/android/id/changelogs/66.txt b/fastlane/metadata/android/id/changelogs/66.txt index 62c8fd3e7..adbbff49c 100644 --- a/fastlane/metadata/android/id/changelogs/66.txt +++ b/fastlane/metadata/android/id/changelogs/66.txt @@ -11,12 +11,12 @@ - batalkan penghapusan unduhan #1472 - Opsi unduh di menu berbagi #1498 - Menambahkan opsi berbagi ke menu ketuk panjang #1454 -- Minimalkan pemain utama di pintu keluar #1354 -- Pembaruan versi perpustakaan dan perbaikan cadangan basis data #1510 +- Minimalkan pemutar utama saat keluar #1354 +- Pembaruan versi library dan perbaikan cadangan basis data #1510 - Pembaruan ExoPlayer 2.8.2 #1392 - Mengolah ulang dialog kontrol kecepatan pemutaran untuk mendukung ukuran langkah yang berbeda untuk perubahan kecepatan yang lebih cepat. - Menambahkan tombol untuk maju cepat selama hening di kontrol kecepatan pemutaran. Ini akan berguna untuk buku audio dan genre musik tertentu, dan dapat menghadirkan pengalaman yang benar-benar mulus (dan dapat memecah lagu dengan banyak keheningan =\\). - - Resolusi sumber media yang difaktorkan ulang untuk memungkinkan penyampaian metadata bersama media secara internal di pemutar, daripada melakukannya secara manual. Sekarang kami memiliki satu sumber metadata dan langsung tersedia saat pemutaran dimulai. + - Refaktor ulang resolusi sumber media untuk memungkinkan penyampaian metadata bersama media secara internal di pemutar, daripada melakukannya secara manual. Sekarang kami memiliki satu sumber metadata dan langsung tersedia saat pemutaran dimulai. - Memperbaiki metadata daftar putar jarak jauh yang tidak diperbarui ketika metadata baru tersedia ketika fragmen daftar putar dibuka. - Berbagai perbaikan UI: #1383, kontrol notifikasi pemutar latar belakang kini selalu berwarna putih, lebih mudah untuk mematikan pemutar popup melalui lemparan - Gunakan ekstraktor baru dengan arsitektur yang difaktorkan ulang untuk multilayanan @@ -25,9 +25,9 @@ - Perbaiki #1440 Tata Letak Info Video Rusak #1491 - Lihat perbaikan riwayat #1497 - - #1495, dengan memperbarui metadata (thumbnail, judul, dan jumlah video) segera setelah pengguna mengakses playlist. + - #1495, dengan memperbarui metadata (thumbnail, judul, dan jumlah video) segera setelah pengguna mengakses daftar putar. - #1475, dengan mendaftarkan tampilan di database saat pengguna memulai video di pemutar eksternal pada fragmen detail. - Perbaiki batas waktu layar jika ada mode popup. #1463 (Diperbaiki #640) - Perbaikan pemutar video utama #1509 - - [#1412] Memperbaiki mode pengulangan yang menyebabkan NPE pemain ketika niat baru diterima saat aktivitas pemain berada di latar belakang. - - Memperbaiki meminimalkan pemain ke popup tidak menghancurkan pemain ketika izin popup tidak diberikan. + - [#1412] Memperbaiki mode pengulangan yang menyebabkan pemutar NPE ketika intent baru diterima saat aktivitas pemutar berada di latar belakang. + - Perbaiki meminimalkan pemutar ke popup tidak menghancurkan pemutar ketika izin popup tidak diberikan. diff --git a/fastlane/metadata/android/id/changelogs/972.txt b/fastlane/metadata/android/id/changelogs/972.txt index e6b705eeb..99990cc90 100644 --- a/fastlane/metadata/android/id/changelogs/972.txt +++ b/fastlane/metadata/android/id/changelogs/972.txt @@ -11,4 +11,4 @@ Memperbarui instance Invidious dan mendukung tautan Piped. Diperbaiki [YouTube] Konten yang dibatasi usia -Cegah jendela bocor Pengecualian saat membuka dialog pilihan +Cegah Pengecualian jendela bocor saat membuka dialog pilihan diff --git a/fastlane/metadata/android/id/changelogs/975.txt b/fastlane/metadata/android/id/changelogs/975.txt index 65e6bcebd..b8f4aca1f 100644 --- a/fastlane/metadata/android/id/changelogs/975.txt +++ b/fastlane/metadata/android/id/changelogs/975.txt @@ -1,17 +1,17 @@ Baru -• Tampilkan pratinjau thumbnail saat mencari -• Mendeteksi komentar yang dinonaktifkan -• Izinkan menandai item feed sebagai ditonton -• Tampilkan komentar hati +• Menampilkan Thumbnail ketika Mempercepat +• Deteksi komentar yang dimatikan +• Penandaan item feed sebagai ditonton +• Menampilkan Komentar disukai -Ditingkatkan -• Memperbaiki metadata dan tata letak tag -• Menerapkan warna layanan ke komponen UI +Peningkatan +• Pembaruan Layout metadata dan tag +• Penerapan warna layanan pada komponen UI -Tetap -• Perbaiki thumbnail di mini player -• Memperbaiki buffering tanpa akhir pada item antrian duplikat -• Beberapa perbaikan pemain seperti rotasi dan penutupan lebih cepat -• Perbaiki ReCAPTCHA yang tersisa dimuat di latar belakang -• Nonaktifkan klik saat menyegarkan feed -• Memperbaiki beberapa pengunduh yang mogok +Perbaikan +• Perbaiki thumbnail pada mini player +• Perbaiki buffering tanpa akhir pada item antrian duplikat +• Perbaikan Beberapa player rotasi layar dan penutupan yang lebih cepat +• Perbaiki ReCaptcha tetap termuat di latar belakang +• Matikan klik saat memuat feed +• Perbaiki beberapa kerusakan pengunduh diff --git a/fastlane/metadata/android/id/changelogs/976.txt b/fastlane/metadata/android/id/changelogs/976.txt index d76b10e91..d3c7e505b 100644 --- a/fastlane/metadata/android/id/changelogs/976.txt +++ b/fastlane/metadata/android/id/changelogs/976.txt @@ -1,9 +1,9 @@ -• Ditambahkan opsi untuk langsung membuka pemutar dalam layar penuh +• Tambahkan opsi untuk langsung membuka pemutar dalam layar penuh • Izinkan memilih jenis saran pencarian yang akan ditampilkan • Tema gelap kini lebih gelap + layar splash gelap ditambahkan -• Peningkatan pemilih file untuk menghilangkan file yang tidak diinginkan +• Peningkatan pemilih file untuk menyembunyikan file yang tidak diinginkan • Memperbaiki impor langganan YouTube -• Memutar ulang streaming memerlukan ketukan pada tombol putar ulang lagi +• Memutar ulang stream memerlukan ketukan pada tombol putar ulang lagi • Memperbaiki sesi audio penutup • [Android TV] Memperbaiki lompatan seekbar yang panjang saat menggunakan DPad diff --git a/fastlane/metadata/android/id/changelogs/995.txt b/fastlane/metadata/android/id/changelogs/995.txt index 277b2468a..b3db15bf1 100644 --- a/fastlane/metadata/android/id/changelogs/995.txt +++ b/fastlane/metadata/android/id/changelogs/995.txt @@ -4,13 +4,13 @@ Baru • Dapatkan URL ke semua gambar Ditingkatkan -• Aksesibilitas antarmuka pemain -• Pilihan audio yang lebih baik untuk download video saja -• Pilihan untuk memasukkan nama playlist dan video ke konten playlist bersama +• Aksesibilitas antarmuka pemutar +• Pilihan audio yang lebih baik untuk unduhan video saja +• Pilihan untuk memasukkan nama playlist dan video ke konten daftar putar bersama -Tetap +Diperbaiki • [YouTube] Perbaiki penghitungan jumlah suka -• Memperbaiki pemain yang tidak merespons popup dan crash +• Perbaiki pemutar yang tidak merespons popup dan crash • Pemilihan bahasa yang salah di pemilih bahasa • Fokus audio pemutar tidak menerapkan mode senyap -• Penambahan item playlist terkadang tidak berfungsi +• Penambahan item daftar putar terkadang tidak berfungsi diff --git a/fastlane/metadata/android/id/changelogs/996.txt b/fastlane/metadata/android/id/changelogs/996.txt new file mode 100644 index 000000000..d1fd9d5b7 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/996.txt @@ -0,0 +1,2 @@ +Perbaiki NullPointerException saat membuka saluran / konferensi di media.ccc.de. +The Grinch mencoba untuk merusak hadiah Natal kami ke anda, tapi kami memperbaikinya. diff --git a/fastlane/metadata/android/it/changelogs/996.txt b/fastlane/metadata/android/it/changelogs/996.txt new file mode 100644 index 000000000..0d69a3264 --- /dev/null +++ b/fastlane/metadata/android/it/changelogs/996.txt @@ -0,0 +1,2 @@ +Corretto un NullPointerException quando si apre un canale / conferenza in media.ccc.de. +Il Grinch ha cercato di rompere il nostro regalo di Natale per te, ma l'abbiamo risolto. diff --git a/fastlane/metadata/android/ja/changelogs/954.txt b/fastlane/metadata/android/ja/changelogs/954.txt index e927e8e51..6b0da1598 100644 --- a/fastlane/metadata/android/ja/changelogs/954.txt +++ b/fastlane/metadata/android/ja/changelogs/954.txt @@ -1,6 +1,6 @@ ・新しいアプリケーションの利用体系: 詳細ページで動画を再生し、下にスワイプしてプレイヤーを最小化する ・MediaStyle 通知: 通知のカスタマイズ可能なアクション、パフォーマンスの向上 -・デスクトップアプリのような NewPipe 使用中のリサイズ +・デスクトップアプリのような NewPipe 使用中のリサイズ ・サポートしていない URL のとき他のアプリで開くオプションを表示する ・取得できなかった場合の検索サジェストのユーザー体験の向上 diff --git a/fastlane/metadata/android/ka/changelogs/64.txt b/fastlane/metadata/android/ka/changelogs/64.txt index 5d32b7c0d..de50608ab 100644 --- a/fastlane/metadata/android/ka/changelogs/64.txt +++ b/fastlane/metadata/android/ka/changelogs/64.txt @@ -1,5 +1,5 @@ ### გაუმჯობესებები - - დაემატა ვიდეოს ხარისხის შეზღუდვის შესაძლებლობა მობილური ინტერნეტის გამოყენებისას. #1339 + - დაემატა ვიდეოს ხარისხის შეზღუდვის შესაძლებლობა მობილური ინტერნეტის გამოყენებისას. #1339 - დაიმახსოვრე სიკაშკაშე #1442 სესიისთვის - გააუმჯობესეთ ჩამოტვირთვის შესრულება სუსტი CPU-ებისთვის #1431 - დაამატეთ (სამუშაო) მხარდაჭერა მედია სესიისთვის #1433 diff --git a/fastlane/metadata/android/ka/changelogs/69.txt b/fastlane/metadata/android/ka/changelogs/69.txt index b5d54c565..4bc9f3c03 100644 --- a/fastlane/metadata/android/ka/changelogs/69.txt +++ b/fastlane/metadata/android/ka/changelogs/69.txt @@ -11,7 +11,7 @@ - დაამატეთ მხარდაჭერა ლოკალიზაციის #1792-ისთვის ### ასწორებს - - დააფიქსირეთ დროის ანალიზი . ფორმატში, ამიტომ NewPipe შეიძლება გამოყენებულ იქნას ფინეთში + - დააფიქსირეთ დროის ანალიზი . ფორმატში, ამიტომ NewPipe შეიძლება გამოყენებულ იქნას ფინეთში - შეასწორეთ გამოწერების რაოდენობა - დაამატეთ წინა პლანზე სერვისის ნებართვა API 28+ მოწყობილობებისთვის #1830 diff --git a/fastlane/metadata/android/ka/changelogs/740.txt b/fastlane/metadata/android/ka/changelogs/740.txt index 3589197f9..b6f13456b 100644 --- a/fastlane/metadata/android/ka/changelogs/740.txt +++ b/fastlane/metadata/android/ka/changelogs/740.txt @@ -3,11 +3,11 @@
  • გახადეთ კომენტარებში ბმულები დაწკაპუნებით, გაზარდეთ ტექსტის ზომა
  • მოძებნეთ კომენტარებში დროის ანაბეჭდის ბმულებზე დაწკაპუნებით
  • სასურველი ჩანართის ჩვენება ახლახან არჩეული მდგომარეობის მიხედვით
  • -
  • დაამატე დასაკრავი სია რიგში, როდესაც დიდხანს დააწკაპუნებთ 'ფონზე' დასაკრავი სიის ფანჯარაში
  • +
  • დაამატე დასაკრავი სია რიგში, როდესაც დიდხანს დააწკაპუნებთ 'ფონზე' დასაკრავი სიის ფანჯარაში
  • მოძებნეთ გაზიარებული ტექსტი, როდესაც ის არ არის URL
  • -
  • დაამატეთ "გაზიარება მიმდინარე დროს" ღილაკი მთავარ ვიდეო დამკვრელზე
  • +
  • დაამატეთ "გაზიარება მიმდინარე დროს" ღილაკი მთავარ ვიდეო დამკვრელზე
  • დახურვის ღილაკის დამატება მთავარ დამკვრელზე, როდესაც ვიდეო რიგი დასრულდება
  • -
  • დაამატეთ "დაკვრა პირდაპირ ფონზე" ვიდეო სიის ელემენტების მენიუს ხანგრძლივი დაჭერისთვის
  • +
  • დაამატეთ "დაკვრა პირდაპირ ფონზე" ვიდეო სიის ელემენტების მენიუს ხანგრძლივი დაჭერისთვის
  • გააუმჯობესეთ ინგლისური თარგმანი Play/Enqueue ბრძანებებისთვის
  • შესრულების მცირე გაუმჯობესება
  • გამოუყენებელი ფაილების წაშლა
  • diff --git a/fastlane/metadata/android/ka/changelogs/850.txt b/fastlane/metadata/android/ka/changelogs/850.txt index c78859a4e..7f6cc55e2 100644 --- a/fastlane/metadata/android/ka/changelogs/850.txt +++ b/fastlane/metadata/android/ka/changelogs/850.txt @@ -1 +1 @@ -ამ გამოშვებაში განახლდა YouTube ვებსაიტის ვერსია. ვებსაიტის ძველი ვერსია შეწყდება მარტში და, შესაბამისად, თქვენ უნდა განაახლოთ NewPipe. +ამ გამოშვებაში განახლდა YouTube ვებსაიტის ვერსია. ვებსაიტის ძველი ვერსია შეწყდება მარტში და, შესაბამისად, თქვენ უნდა განაახლოთ NewPipe. diff --git a/fastlane/metadata/android/ka/changelogs/967.txt b/fastlane/metadata/android/ka/changelogs/967.txt index 5d1835b2e..232586a73 100644 --- a/fastlane/metadata/android/ka/changelogs/967.txt +++ b/fastlane/metadata/android/ka/changelogs/967.txt @@ -1 +1 @@ -გაასწორა YouTube, რომელიც ევროკავშირში არ მუშაობს გამართულად. ეს გამოწვეული იყო ახალი ქუქი-ჩანაწერით და კონფიდენციალურობის თანხმობის სისტემით, რომელიც მოითხოვს NewPipe-ს დააყენოს თანხმობის ქუქი-ფაილები. +გაასწორა YouTube, რომელიც ევროკავშირში არ მუშაობს გამართულად. ეს გამოწვეული იყო ახალი ქუქი-ჩანაწერით და კონფიდენციალურობის თანხმობის სისტემით, რომელიც მოითხოვს NewPipe-ს დააყენოს თანხმობის ქუქი-ფაილები. diff --git a/fastlane/metadata/android/ka/changelogs/978.txt b/fastlane/metadata/android/ka/changelogs/978.txt index 600962d14..787d5d12e 100644 --- a/fastlane/metadata/android/ka/changelogs/978.txt +++ b/fastlane/metadata/android/ka/changelogs/978.txt @@ -1 +1 @@ -დაფიქსირდა შემოწმების შესრულება NewPipe-ის ახალი ვერსიისთვის. ეს შემოწმება ხანდახან ძალიან ადრე სრულდებოდა და, შესაბამისად, იწვევს აპის ავარიას. ეს ახლა უნდა გამოსწორდეს. +დაფიქსირდა შემოწმების შესრულება NewPipe-ის ახალი ვერსიისთვის. ეს შემოწმება ხანდახან ძალიან ადრე სრულდებოდა და, შესაბამისად, იწვევს აპის ავარიას. ეს ახლა უნდა გამოსწორდეს. diff --git a/fastlane/metadata/android/ka/changelogs/988.txt b/fastlane/metadata/android/ka/changelogs/988.txt index e42877c9c..3c74e29f5 100644 --- a/fastlane/metadata/android/ka/changelogs/988.txt +++ b/fastlane/metadata/android/ka/changelogs/988.txt @@ -1,2 +1,2 @@ [YouTube] გაასწორდა შეცდომა „ვერ მივიღე ნაკადი“ რომელიმე ვიდეოს დაკვრის მცდელობისას - [YouTube] შეასწორდა „შემდეგი კონტენტი მიუწვდომელია ამ აპში“. მოთხოვნილი ვიდეოს ნაცვლად ნაჩვენები შეტყობინება + [YouTube] შეასწორდა „შემდეგი კონტენტი მიუწვდომელია ამ აპში“. მოთხოვნილი ვიდეოს ნაცვლად ნაჩვენები შეტყობინება diff --git a/fastlane/metadata/android/ka/full_description.txt b/fastlane/metadata/android/ka/full_description.txt index eff57aabe..e65267b46 100644 --- a/fastlane/metadata/android/ka/full_description.txt +++ b/fastlane/metadata/android/ka/full_description.txt @@ -1 +1 @@ -NewPipe არ იყენებს Google Framework ბიბლიოთეკას ან YouTube API-ს. ის მხოლოდ აანალიზებს ვებსაიტს, რათა მოიპოვოს მისთვის საჭირო ინფორმაცია. ამიტომ ამ აპლიკაციის გამოყენება შესაძლებელია მოწყობილობებზე Google სერვისების დაყენების გარეშე. გარდა ამისა, თქვენ არ გჭირდებათ YouTube ანგარიში NewPipe-ის გამოსაყენებლად და ეს არის FLOSS. +NewPipe არ იყენებს Google Framework ბიბლიოთეკას ან YouTube API-ს. ის მხოლოდ აანალიზებს ვებსაიტს, რათა მოიპოვოს მისთვის საჭირო ინფორმაცია. ამიტომ ამ აპლიკაციის გამოყენება შესაძლებელია მოწყობილობებზე Google სერვისების დაყენების გარეშე. გარდა ამისა, თქვენ არ გჭირდებათ YouTube ანგარიში NewPipe-ის გამოსაყენებლად და ეს არის FLOSS. diff --git a/fastlane/metadata/android/ko/changelogs/65.txt b/fastlane/metadata/android/ko/changelogs/65.txt new file mode 100644 index 000000000..5c2a302e1 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/65.txt @@ -0,0 +1,27 @@ +### 개선사항 + +- 버거 메뉴 아이콘 애니메이션 비활성화 #1486 +- 다운로드 삭제 취소 #1472 +- 공유 메뉴 #1498의 다운로드 옵션 +- 길게 탭하는 메뉴 #1454에 공유 옵션 추가 +- 1354번 출구에서 메인 플레이어 최소화 +- 라이브러리 버전 업데이트 및 데이터베이스 백업 수정 #1510 +- ExoPlayer 2.8.2 업데이트 #1392 + - 더 빠른 속도 변경을 위해 다양한 단계 크기를 지원하도록 재생 속도 제어 대화 상자를 재작업 했습니다. + - 재생 속도 제어에서 무음 중에 빨리 감기하는 토글을 추가했습니다. 이는 오디오북과 특정 음악 장르에 도움이 될 것이며 진정한 원활한 경험을 제공할 수 있습니다(그리고 무음이 많은 노래를 깨뜨릴 수 있습니다 =\\). + - 수동으로 수행하는 대신 플레이어 내부에서 미디어와 함께 메타데이터를 전달할 수 있도록 미디어 소스 해상도를 리팩터링했습니다. 이제 단일 메타데이터 소스가 있으며 재생이 시작되면 바로 사용할 수 있습니다. + - 재생 목록 조각이 열릴 때 새 메타데이터를 사용할 수 있을 때 원격 재생 목록 메타데이터가 업데이트되지 않는 문제를 수정했습니다. + - 다양한 UI 수정: #1383, 백그라운드 플레이어 알림 컨트롤은 이제 항상 흰색이며, 플링을 통해 팝업 플레이어를 더 쉽게 종료할 수 있습니다. +- 멀티서비스를 위해 리팩토링된 아키텍처를 갖춘 새로운 추출기 사용 + + +### 수정됨 + +- #1440 깨진 비디오 정보 레이아웃 수정 #1491 +- 기록 수정 #1497 보기 + - #1495, 사용자가 재생 목록에 액세스하는 즉시 메타데이터(썸네일, 제목 및 동영상 개수)를 업데이트합니다. + - #1475, 사용자가 디테일 프래그먼트의 외부 플레이어에서 비디오를 시작할 때 데이터베이스에 뷰를 등록합니다. +- 팝업 모드의 경우 크린 시간 초과 문제를 수정합니다. #1463 (고정 #640) +- 기본 비디오 플레이어 수정 #1509 + - [#1412] 플레이어 활동이 백그라운드에 있는 동안 새로운 인텐트가 수신되면 반복 모드에서 플레이어 NPE가 발생하는 문제를 수정했습니다. + - 팝업 권한이 부여되지 않은 경우 플레이어를 팝업으로 최소화하면 플레이어가 삭제되지 않는 문제가 해결되었습니다. diff --git a/fastlane/metadata/android/ko/changelogs/66.txt b/fastlane/metadata/android/ko/changelogs/66.txt new file mode 100644 index 000000000..430ff96e0 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/66.txt @@ -0,0 +1,29 @@ +# v0.13.7의 변경 내역 +### 수정됨 +- v0.13.6의 정렬 필터 문제 수정 + +# v0.13.6의 변경 내역 +### 개선 사항 +- 버거 메뉴 아이콘 애니메이션 비활성화 #1486 +- 다운로드 삭제 취소 #1472 +- 공유 메뉴 #1498의 다운로드 옵션 +- 길게 탭하는 메뉴 #1454에 공유 옵션 추가 +- 1354번 출구에서 메인 플레이어 최소화 +- 라이브러리 버전 업데이트 및 데이터베이스 백업 수정 #1510 +- ExoPlayer 2.8.2 업데이트 #1392 + - 더 빠른 속도 변경을 위해 다양한 단계 크기를 지원하도록 재생 속도 제어 대화 상자를 재작업했습니다. + - 재생 속도 제어에서 무음 중에 빨리 감기하는 토글을 추가했습니다. 이는 오디오북과 특정 음악 장르에 도움이 될 것이며 진정한 원활한 경험을 제공할 수 있습니다(그리고 무음이 많은 노래를 깨뜨릴 수 있습니다 =\\). + - 수동으로 수행하는 대신 플레이어 내부에서 미디어와 함께 메타데이터를 전달할 수 있도록 미디어 소스 해상도를 리팩터링했습니다. 이제 단일 메타데이터 소스가 있으며 재생이 시작되면 바로 사용할 수 있습니다. + - 재생 목록 조각이 열릴 때 새 메타데이터를 사용할 수 있을 때 원격 재생 목록 메타데이터가 업데이트되지 않는 문제를 수정했습니다. + - 다양한 UI 수정: #1383, 백그라운드 플레이어 알림 컨트롤은 이제 항상 흰색이며, 플링을 통해 팝업 플레이어를 더 쉽게 종료할 수 있습니다. +- 멀티서비스를 위해 리팩토링된 아키텍처를 갖춘 새로운 추출기 사용 + +### 수정 +- #1440 깨진 비디오 정보 레이아웃 수정 #1491 +- 기록 수정 #1497 보기 + - #1495, 사용자가 재생 목록에 액세스하는 즉시 메타데이터(썸네일, 제목 및 동영상 개수)를 업데이트합니다. + - #1475, 사용자가 디테일 프래그먼트의 외부 플레이어에서 비디오를 시작할 때 데이터베이스에 뷰를 등록합니다. +- 팝업 모드의 경우 크린 시간 초과 문제를 수정합니다. #1463 (고정 #640) +- 기본 비디오 플레이어 수정 #1509 + - [#1412] 플레이어 활동이 백그라운드에 있는 동안 새로운 인텐트가 수신되면 반복 모드에서 플레이어 NPE가 발생하는 문제를 수정했습니다. + - 팝업 권한이 부여되지 않은 경우 플레이어를 팝업으로 최소화하면 플레이어가 삭제되지 않는 문제가 해결되었습니다. diff --git a/fastlane/metadata/android/ko/changelogs/68.txt b/fastlane/metadata/android/ko/changelogs/68.txt new file mode 100644 index 000000000..77f8fad41 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/68.txt @@ -0,0 +1,31 @@ +# v0.14.1의 변경 사항 + +### 수정됨 +- 비디오 URL #1659를 해독하지 못하던 문제 수정 +- 설명 링크가 잘 추출되지 않던 문제 수정 #1657 + +# v0.14.0의 변경 사항 + +### 새로운 +- 새로운 서랍 디자인 #1461 +- 새로운 맞춤형 첫 페이지 #1461 + +### 개선 사항 +- 재작업된 제스처 컨트롤 #1604 +- 팝업 플레이어를 닫는 새로운 방법 #1597 + +### 수정됨 +- 구독 횟수를 확인할 수 없을 때 발생하는 오류를 수정합니다. #1649를 닫습니다. + - 이 경우 "구독자 수를 확인할 수 없음"을 표시합니다. +- YouTube 재생목록이 비어 있을 때 NPE 수정 +- SoundCloud의 키오스크에 대한 빠른 수정 +- 리팩터링 및 버그 수정 #1623 + - 순환 검색 결과 #1562 수정 + - 탐색 막대가 정적으로 배치되지 않은 문제 수정 + - YT Premium 비디오가 올바르게 차단되지 않는 문제 수정 + - 가끔 비디오가 로드되지 않는 문제 수정(DASH 구문 분석으로 인해) + - 동영상 설명의 링크 수정 + - 누군가 외부 SD 카드에 다운로드하려고 하면 경고 표시 + - 아무것도 표시되지 않음 예외 트리거 보고서 수정 + - Android 8.1용 백그라운드 플레이어에는 썸네일이 표시되지 않습니다. [여기 참조](https://github.com/TeamNewPipe/NewPipe/issues/943) +- 방송수신기 등록 문제 수정. #1641을 닫습니다. diff --git a/fastlane/metadata/android/ko/changelogs/69.txt b/fastlane/metadata/android/ko/changelogs/69.txt new file mode 100644 index 000000000..0083b5e58 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/69.txt @@ -0,0 +1,19 @@ +### 새로운 +- 길게 탭하여 구독정보 삭제 및 공유 #1516 +- 태블릿 UI 및 그리드 목록 레이아웃 #1617 + +### 개선 사항 +- 마지막으로 사용된 화면 비율 #1748을 저장하고 다시 로드합니다. +- 전체 비디오 이름 #1771을 사용하여 다운로드 활동에서 선형 레이아웃을 활성화합니다. +- 구독 탭 #1516 내에서 직접 구독 삭제 및 공유 +- 재생 대기열이 이미 종료된 경우 대기열에 추가하면 비디오 재생이 시작됩니다. #1783 +- 볼륨 및 밝기 제스처에 대한 별도 설정 #1644 +- 현지화 #1792 지원 추가 + +### 수정 +- 에 대한 시간 구문 분석을 수정합니다. 형식이므로 핀란드에서 NewPipe를 사용할 수 있습니다. +- 구독 수 수정 +- API 28+ 장치에 대한 포그라운드 서비스 권한 추가 #1830 + +### 알려진 버그 +- Android P에서는 재생 상태를 저장할 수 없습니다. diff --git a/fastlane/metadata/android/ko/changelogs/70.txt b/fastlane/metadata/android/ko/changelogs/70.txt new file mode 100644 index 000000000..15c41627f --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/70.txt @@ -0,0 +1,25 @@ +주의: 이 버전은 아마도 지난 버전과 마찬가지로 버그페스트일 것입니다. 그러나 17. 이후 전체 종료로 인해 깨진 버전이 버전이 없는 것보다 낫습니다. 오른쪽? ̅\_(ツ)_/̅ + +### 개선 사항 +* 이제 다운로드한 파일을 한 번의 클릭으로 열 수 있습니다 #1879 +* 안드로이드 4.1 - 4.3 #1884에 대한 지원 중단 +* 이전 플레이어 #1884 제거 +* 스트림을 오른쪽으로 스와이프하여 현재 재생 대기열에서 스트림을 제거합니다. #1915 +* 새 스트림이 수동으로 대기열에 추가되면 자동 대기열에 추가된 스트림을 제거합니다. #1878 +* 다운로드를 위한 후처리 및 누락된 기능 구현 #1759 by @kapodamy + * 후처리 인프라 + * 적절한 오류 처리 "인프라"(다운로더용) + * 여러 다운로드 대신 대기열 + * 직렬화된 보류 중인 다운로드(`.giga` 파일)를 앱 데이터로 이동 + * 최대 다운로드 재시도 구현 + * 적절한 멀티스레드 다운로드 일시중지 + * 모바일 네트워크로 전환할 때 다운로드 중지(작동하지 않음, 두 번째 항목 참조) + * 다음 다운로드를 위해 스레드 수를 저장하세요 + * 많은 불일치가 수정되었습니다. + +### 수정됨 +* 기본 해상도가 최고 및 제한된 모바일 데이터 해상도 #1835로 설정된 경우 충돌 수정 +* 팝업 플레이어 충돌 수정 #1874 +* 백그라운드 플레이어를 열려고 할 때 NPE #1901 +* 자동 대기열이 활성화된 경우 새 스트림을 삽입하는 문제 수정 #1878 +* 암호 해독 폐쇄 문제 수정 diff --git a/fastlane/metadata/android/ko/changelogs/71.txt b/fastlane/metadata/android/ko/changelogs/71.txt new file mode 100644 index 000000000..c38a4e6c7 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/71.txt @@ -0,0 +1,10 @@ +### 개선 사항 +* GitHub 빌드에 대한 앱 업데이트 알림 추가(@krtkush의 #1608) +* 다운로더의 다양한 개선 사항(@kapodamy의 #1944): + * 누락된 흰색 아이콘을 추가하고 하드코어 방식을 사용하여 아이콘 색상을 변경합니다. + * 반복자가 초기화되었는지 확인하세요(수정 #2031) + * 새 먹서에서 "사후 처리 실패" 오류가 있는 다운로드 재시도 허용 + * 비동기 비디오 및 오디오 스트림을 수정하는 새로운 MPEG-4 먹서(#2039) + +### 수정됨 +* YouTube 실시간 스트리밍은 잠시 후에 재생이 중지됩니다(@yausername의 #1996). diff --git a/fastlane/metadata/android/ko/changelogs/730.txt b/fastlane/metadata/android/ko/changelogs/730.txt new file mode 100644 index 000000000..b30d9896a --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/730.txt @@ -0,0 +1,2 @@ +# 고정 +- 핫픽스 복호화 기능 오류가 다시 발생했습니다. diff --git a/fastlane/metadata/android/ko/changelogs/740.txt b/fastlane/metadata/android/ko/changelogs/740.txt new file mode 100644 index 000000000..41c78eb2a --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/740.txt @@ -0,0 +1,23 @@ +

    개선 사항

    +
      +
    • 댓글의 링크를 클릭 가능하게 만들고 텍스트 크기를 늘립니다.
    • +
    • 댓글의 타임스탬프 링크 클릭
    • +
    • 최근 선택한 상태를 기준으로 기본 탭 표시
    • +
    • 재생목록 창에서 '배경'을 길게 클릭하면 대기열에 재생목록 추가
    • +
    • URL이 아닌 공유 텍스트 검색
    • +
    • 기본 비디오 플레이어에 "현재 시간 공유" 버튼 추가
    • +
    • 동영상 대기열이 완료되면 기본 플레이어에 닫기 버튼 추가
    • +
    • 동영상 목록 항목의 길게 누르기 메뉴에 "백그라운드에서 직접 재생" 추가
    • +
    • Play/Enqueue 명령에 대한 영어 번역 개선
    • +
    • 작은 성능 개선
    • +
    • 사용하지 않는 파일 제거
    • +
    • ExoPlayer를 2.9.6으로 업데이트
    • +
    • Invidious 링크에 대한 지원 추가
    • +
    +

    수정됨

    +
      +
    • 댓글 및 관련 스트림이 비활성화된 고정 스크롤
    • +
    • CheckForNewAppVersionTask가 실행되어서는 안 될 때 실행되는 문제를 수정했습니다.
    • +
    • YouTube 구독 가져오기 수정: URL이 잘못된 구독은 무시하고 제목이 비어 있는 구독은 유지
    • +
    • 잘못된 YouTube URL 수정: 서명 태그 이름이 스트림 로드를 방해하는 '서명'이 아닐 수도 있습니다.
    • +
    diff --git a/fastlane/metadata/android/ko/changelogs/750.txt b/fastlane/metadata/android/ko/changelogs/750.txt new file mode 100644 index 000000000..08450674b --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/750.txt @@ -0,0 +1,22 @@ +새로운 +재생 재개 #2288 +• 지난번에 중단한 스트림 재개 +다운로더 개선 사항 #2149 +• 저장소 액세스 프레임워크를 사용하여 외부 SD 카드에 다운로드 저장 +• 새로운 mp4 먹서 +• 선택적으로 다운로드를 시작하기 전에 다운로드 디렉터리를 변경합니다. +• 종량제 네트워크를 존중합니다. + + +향상 +• gema 문자열 #2295 제거됨 +• 활동 수명주기 동안 (자동)회전 변경 처리 #2444 +• 길게 누르기 메뉴를 일관성있게 만들기 #2368 + +결정된 +• 선택한 자막 트랙 이름이 표시되지 않는 문제 수정 #2394 +• 앱 업데이트 확인 실패 시 충돌 방지(GitHub 버전) #2423 +• 다운로드가 99.9%에서 멈추는 현상 수정 #2440 +• 재생 대기열 메타데이터 업데이트 #2453 +• [SoundCloud] 재생 목록을 로드할 때 발생하는 충돌 수정 TeamNewPipe/NewPipeExtractor#170 +• [YouTube] 고정 기간은 TeamNewPipe/NewPipeExtractor#177로 파싱할 수 없습니다. diff --git a/fastlane/metadata/android/ko/changelogs/760.txt b/fastlane/metadata/android/ko/changelogs/760.txt new file mode 100644 index 000000000..78097b1ab --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/760.txt @@ -0,0 +1,43 @@ +0.17.1의 변경 사항 + +새로운 +• 태국어 현지화 + + +향상 +• 재생목록을 다시 길게 누르는 메뉴에 여기서 재생 시작 작업 추가 #2518 +• SAF/레거시 파일 선택기 #2521용 스위치 추가 + +결정된 +• 앱 전환 시 다운로드 보기에서 버튼이 사라지는 문제 수정 #2487 +• 시청 기록이 비활성화되어 있어도 고정 재생 위치가 저장됩니다. +• 목록 보기의 재생 위치로 인해 성능이 저하되는 문제 수정 #2517 +• [추출기] ReCaptchaActivity #2527, TeamNewPipe/NewPipeExtractor#186 수정 +• [Extractor] [YouTube] 결과에 재생목록이 있을 때 일반 검색 오류 수정 TeamNewPipe/NewPipeExtractor#185 + + + +0.17.0의 변경 사항 + +새로운 +재생 재개 #2288 +• 지난번에 중단한 스트림 재개 +다운로더 개선 사항 #2149 +• 저장소 액세스 프레임워크를 사용하여 외부 SD 카드에 다운로드 저장 +• 새로운 mp4 먹서 +• 선택적으로 다운로드를 시작하기 전에 다운로드 디렉터리를 변경합니다. +• 종량제 네트워크를 존중합니다. + + +향상 +• gema 문자열 #2295 제거됨 +• 활동 수명주기 동안 (자동)회전 변경 처리 #2444 +• 길게 누르기 메뉴를 일관성있게 만들기 #2368 + +결정된 +• 선택한 자막 트랙 이름이 표시되지 않는 문제 수정 #2394 +• 앱 업데이트 확인 실패 시 충돌 방지(GitHub 버전) #2423 +• 다운로드가 99.9%에서 멈추는 현상 수정 #2440 +• 재생 대기열 메타데이터 업데이트 #2453 +• [SoundCloud] 재생 목록을 로드할 때 발생하는 충돌 수정 TeamNewPipe/NewPipeExtractor#170 +• [YouTube] 고정 기간은 TeamNewPipe/NewPipeExtractor#177로 파싱할 수 없습니다. diff --git a/fastlane/metadata/android/ko/changelogs/770.txt b/fastlane/metadata/android/ko/changelogs/770.txt new file mode 100644 index 000000000..3008815d8 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/770.txt @@ -0,0 +1,4 @@ +0.17.2의 변경 사항 + +고치다 +• 사용할 수 있는 동영상이 없는 문제 수정 diff --git a/fastlane/metadata/android/ko/changelogs/780.txt b/fastlane/metadata/android/ko/changelogs/780.txt new file mode 100644 index 000000000..114b38dd5 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/780.txt @@ -0,0 +1,12 @@ +0.17.3의 변경 사항 + +향상 +• 재생 상태를 지우는 옵션 추가 #2550 +• 파일 선택기에 숨겨진 디렉터리 표시 #2591 +• `invidio.us` 인스턴스의 URL이 NewPipe #2488로 열리도록 지원합니다. +• 'music.youtube.com' URL에 대한 지원 추가 TeamNewPipe/NewPipeExtractor#194 + +결정된 +• [YouTube] 'java.lang.IllegalArgumentException #192 수정 +• [YouTube] 실시간 스트림이 작동하지 않는 문제 수정 TeamNewPipe/NewPipeExtractor#195 +• 스트림을 다운로드할 때 Android Pie의 성능 문제 수정 #2592 diff --git a/fastlane/metadata/android/ko/changelogs/790.txt b/fastlane/metadata/android/ko/changelogs/790.txt new file mode 100644 index 000000000..c426443b2 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/790.txt @@ -0,0 +1,14 @@ +향상 +• 시각 장애인의 접근성을 높이기 위해 더 많은 제목을 추가합니다. #2655 +• 다운로드 폴더 설정 언어를 보다 일관되고 덜 모호하게 만듭니다. #2637 + +결정된 +• 블록의 마지막 바이트가 다운로드되었는지 확인 #2646 +• 비디오 세부 정보 조각 #2672의 스크롤 문제를 해결했습니다. +• 이중 검색 지우기 상자 애니메이션을 하나로 제거 #2695 +• [SoundCloud] client_id 추출 수정 #2745 + +개발 +• NewPipeExtractor에서 상속된 누락된 종속성을 NewPipe #2535에 추가합니다. +• AndroidX #2685로 마이그레이션 +• ExoPlayer 2.10.6 #2697, #2736으로 업데이트 diff --git a/fastlane/metadata/android/ko/changelogs/800.txt b/fastlane/metadata/android/ko/changelogs/800.txt new file mode 100644 index 000000000..04907891e --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/800.txt @@ -0,0 +1,27 @@ +새로운 +• P2P 없이 PeerTube 지원(#2201)[베타]: + ◦ PeerTube 인스턴스에서 비디오 시청 및 다운로드 + ◦ 전체 PeerTube 세계에 액세스하려면 설정에 인스턴스를 추가하세요. + ◦ 특정 인스턴스에 액세스할 때 Android 4.4 및 7.1에서 SSL 핸드셰이크에 문제가 있어 네트워크 오류가 발생할 수 있습니다. + +• 다운로더(#2679): + ◦ 다운로드 ETA 계산 + ◦ opus(webm 파일)를 ogg로 다운로드 + ◦ 오랜 일시 중지 후 다운로드를 재개하려면 만료된 다운로드 링크를 복구하세요. + +향상 +• KioskFragment가 선호하는 콘텐츠 국가의 변경 사항을 인식하고 모든 기본 탭의 성능을 향상시킵니다. #2742 +• 추출기 #2713에서 새로운 현지화 및 다운로더 구현을 사용합니다. +• '기본 키오스크' 문자열을 번역 가능하게 만듭니다. +• 검은색 테마용 검은색 탐색 모음 #2569 + +결정된 +• 팝업플레이어 이동 중 다른 손가락을 놓으면 팝업플레이어가 이동되지 않는 버그 수정 #2772 +• 업로더가 없는 재생목록을 허용하고 이 문제 #2724, TeamNewPipe/NewPipeExtractor#219와 관련된 충돌을 해결합니다. +• Android 4.4 기기(API 19/KitKat)에서 TLS1.1/1.2를 활성화하여 MediaCCC 및 일부 PeerTube 인스턴스와의 TLS 핸드셰이크 수정 #2792 +• [SoundCloud] client_id 추출 수정 TeamNewPipe/NewPipeExtractor#217 +• [SoundCloud] 오디오 스트림 추출 수정 + +개발 +• ExoPlayer를 2.10.8 #2791, #2816으로 업데이트 +• Gradle을 3.5.1로 업데이트하고 Kotlin 지원 #2714를 추가합니다. diff --git a/fastlane/metadata/android/ko/changelogs/810.txt b/fastlane/metadata/android/ko/changelogs/810.txt new file mode 100644 index 000000000..38ab2c4b3 --- /dev/null +++ b/fastlane/metadata/android/ko/changelogs/810.txt @@ -0,0 +1,19 @@ +새로운 +• 백그라운드에서 재생할 때 잠금 화면에 동영상 미리보기 이미지 표시 + +향상 +• 배경/팝업 버튼을 길게 누르면 대기열에 로컬 재생 목록 추가 +• 기본 페이지 탭을 스크롤 가능하게 만들고 탭이 하나만 있는 경우 숨깁니다. +• 백그라운드 플레이어의 알림 썸네일 업데이트 양 제한 +• 빈 로컬 재생목록에 더미 썸네일 추가 +• *.webm 대신 *.opus 파일 확장자를 사용하고 다운로드 드롭다운에서 'WebM Opus' 대신 형식 라벨에 'opus'를 표시합니다. +• '다운로드'에 다운로드한 파일이나 다운로드 기록을 삭제하는 버튼 추가 +• [YouTube] /c/shortened_url 채널 링크에 지원 추가 + +결정된 +• NewPipe에 비디오를 공유하고 해당 스트림을 직접 다운로드할 때 여러 문제가 해결되었습니다. +• 생성 스레드 외부에서 플레이어 액세스 수정 +• 검색결과 페이지 고정 +• [YouTube] Null 전환으로 인해 NPE가 발생하는 문제 수정 +• [YouTube] invidio.us URL을 열 때 댓글 보기 문제를 해결했습니다. +• [SoundCloud] client_id 업데이트됨 diff --git a/fastlane/metadata/android/pa/changelogs/996.txt b/fastlane/metadata/android/pa/changelogs/996.txt new file mode 100644 index 000000000..ac5940bc3 --- /dev/null +++ b/fastlane/metadata/android/pa/changelogs/996.txt @@ -0,0 +1,2 @@ +media.ccc.de ਵਿੱਚ ਇੱਕ ਚੈਨਲ / ਕਾਨਫਰੰਸ ਖੋਲ੍ਹਣ ਵੇਲੇ ਇੱਕ NullPointerException ਫਿਕਸ ਕੀਤਾ ਗਿਆ। +ਗ੍ਰਿੰਚ ਨੇ ਤੁਹਾਡੇ ਲਈ ਸਾਡੇ ਕ੍ਰਿਸਮਸ ਤੋਹਫ਼ੇ ਨੂੰ ਤੋੜਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ, ਪਰ ਅਸੀਂ ਇਸਨੂੰ ਠੀਕ ਕਰ ਦਿੱਤਾ। diff --git a/fastlane/metadata/android/pt-PT/changelogs/995.txt b/fastlane/metadata/android/pt-PT/changelogs/995.txt new file mode 100644 index 000000000..b22d3122a --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/995.txt @@ -0,0 +1,16 @@ +Novo +- Separadores de canais de suporte +- Selecionar a qualidade da imagem +- Obter URLs para todas as imagens + +Melhorado +- Acessibilidade das interfaces do leitor +- Melhor seleção de áudio para transferências apenas de vídeo +- Opção para incluir nomes de listas de reprodução e de vídeos no conteúdo de listas de reprodução partilhadas + +Corrigido +- YouTube] Correção da contagem de gostos +- Correção de popups e falhas do leitor que não responde +- Seleção de idiomas errados no seletor de idiomas +- A focagem do áudio do leitor não respeitava o silêncio +- A adição de itens à lista de reprodução não funcionava ocasionalmente. diff --git a/fastlane/metadata/android/pt/changelogs/995.txt b/fastlane/metadata/android/pt/changelogs/995.txt index ebe4e742f..db7a18347 100644 --- a/fastlane/metadata/android/pt/changelogs/995.txt +++ b/fastlane/metadata/android/pt/changelogs/995.txt @@ -5,10 +5,12 @@ Novo Melhorado - Acessibilidade das interfaces do leitor -- Opção para adicionar o nome da lista de reprodução e o nome do vídeo ao conteúdo de partilha da lista de reprodução -- Melhorias internas e atualizações de dependências +- Melhor seleção de áudio para transferências apenas de vídeo +- Opção para incluir nomes de listas de reprodução e de vídeos no conteúdo de listas de reprodução partilhadas Corrigido +- YouTube] Correção da contagem de gostos +- Correção de popups e falhas do leitor que não responde - Seleção de idiomas errados no seletor de idiomas -- O foco do áudio do leitor não estava a respeitar o silêncio -- A adição de itens a listas de reprodução não funcionava em casos específicos +- A focagem do áudio do leitor não estava a respeitar o silêncio +- A adição de itens à lista de reprodução não funcionava ocasionalmente. diff --git a/fastlane/metadata/android/pt/changelogs/996.txt b/fastlane/metadata/android/pt/changelogs/996.txt new file mode 100644 index 000000000..4f1c8f229 --- /dev/null +++ b/fastlane/metadata/android/pt/changelogs/996.txt @@ -0,0 +1,2 @@ +Foi corrigida uma NullPointerException ao abrir um canal/conferência em media.ccc.de. +O Grinch tentou estragar a nossa prenda de Natal, mas nós resolvemos o problema. diff --git a/fastlane/metadata/android/ru/changelogs/995.txt b/fastlane/metadata/android/ru/changelogs/995.txt index dcefd106a..11b54b662 100644 --- a/fastlane/metadata/android/ru/changelogs/995.txt +++ b/fastlane/metadata/android/ru/changelogs/995.txt @@ -1,14 +1,16 @@ -Из нового +Новое • Поддержка вкладок каналов • Выбор качества изображения • Получение URL всех изображений -Улучшения -• Доступность интерфейсов проигрывателей -• Добавлена возможность добавлять название подборки и название видеораспространяемого содержимого подборки. -• Внутренние усовершенствования и обновление зависимостей +Улучшено +• Доступность интерфейсов плееров +• Выбор аудио для скачивания только видео +• Возможность включения названий плейлистов и видео в общий плейлист -Исправления +Исправлено +• Получение кол-ва лайков +• Плеер не видит всплывающие окона и вылетает • Выбор неправильных языков в переключателе языков • Фокусировка звука проигрывателя не учитывала выключение звука -• Добавление элементов в подборки не работало в определённых случаях +• Добавление элемента плейлиста иногда не работает diff --git a/fastlane/metadata/android/ru/changelogs/996.txt b/fastlane/metadata/android/ru/changelogs/996.txt new file mode 100644 index 000000000..d516b0bc2 --- /dev/null +++ b/fastlane/metadata/android/ru/changelogs/996.txt @@ -0,0 +1,2 @@ +Исправлена ошибка NullPointerException при открытии канала/конференции в media.ccc.de. +Гринч пытался сломать наш рождественский подарок вам, но мы починили его. diff --git a/fastlane/metadata/android/si/full_description.txt b/fastlane/metadata/android/si/full_description.txt new file mode 100644 index 000000000..c5300a7e9 --- /dev/null +++ b/fastlane/metadata/android/si/full_description.txt @@ -0,0 +1 @@ +NewPipe කිසිදු Google රාමු පුස්තකාලයක් හෝ YouTube API භාවිතා නොකරයි. එය අවශ්‍ය තොරතුරු ලබා ගැනීම සඳහා වෙබ් අඩවිය විග්‍රහ කරයි. එබැවින් මෙම යෙදුම Google සේවා ස්ථාපනය කර නොමැති උපාංග මත භාවිතා කළ හැක. එසේම, ඔබට NewPipe භාවිතා කිරීමට YouTube ගිණුමක් අවශ්‍ය නොවන අතර එය FLOSS වේ. diff --git a/fastlane/metadata/android/si/short_description.txt b/fastlane/metadata/android/si/short_description.txt new file mode 100644 index 000000000..6f79e214d --- /dev/null +++ b/fastlane/metadata/android/si/short_description.txt @@ -0,0 +1 @@ +Android සඳහා නොමිලේ සැහැල්ලු YouTube ඉදිරි අන්තය. diff --git a/fastlane/metadata/android/sk/changelogs/995.txt b/fastlane/metadata/android/sk/changelogs/995.txt new file mode 100644 index 000000000..9729b3f70 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/995.txt @@ -0,0 +1,16 @@ +Nové +- Podpora kariet kanálov +- Výber kvality obrázka +- Získate adresy URL všetkých obrázkov + +Vylepšené +- Prístupnosť rozhrania prehrávača +- Lepší výber zvuku pri sťahovaní videa +- Možnosť zahrnúť názvy playlistov a videí do zdieľaného obsahu playlistov + +Opravené +- [YouTube] Oprava získavania počtu lajkov +- Oprava vyskakovacích okien a pádov prehrávača +- Výber nesprávnych jazykov vo výbere jazyka +- Zameranie zvuku prehrávača nerešpektovalo stlmenie +- Občas nefungovalo pridávanie položiek do playlistu diff --git a/fastlane/metadata/android/sk/changelogs/996.txt b/fastlane/metadata/android/sk/changelogs/996.txt new file mode 100644 index 000000000..25fa111b5 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/996.txt @@ -0,0 +1,2 @@ +Opravená výnimka NullPointerException pri otvorení kanála/konferencie v media.ccc.de. +Grinch sa pokúsil prekaziť náš vianočný darček pre vás, ale opravili sme to. diff --git a/fastlane/metadata/android/sr/changelogs/996.txt b/fastlane/metadata/android/sr/changelogs/996.txt new file mode 100644 index 000000000..749b74624 --- /dev/null +++ b/fastlane/metadata/android/sr/changelogs/996.txt @@ -0,0 +1,2 @@ +Поправљен NullPointerException приликом отварања канала/конференције у media.ccc.de. +Гринч је покушао да вам поквари наш божићни поклон, али смо то поправили. diff --git a/fastlane/metadata/android/sv/changelogs/63.txt b/fastlane/metadata/android/sv/changelogs/63.txt index dcf52354b..51c6551c1 100644 --- a/fastlane/metadata/android/sv/changelogs/63.txt +++ b/fastlane/metadata/android/sv/changelogs/63.txt @@ -1,7 +1,7 @@ ### Förbättringar -- Import/export inställningar #1333 +- Import/exportinställningar #1333 - Minskade överdragningen (prestandaförbättring) #1371 -- Små kod förbättringar #1375 +- Små kodförbättringar #1375 - La till allt om GDPR #1420 ### Fixade diff --git a/fastlane/metadata/android/sv/changelogs/64.txt b/fastlane/metadata/android/sv/changelogs/64.txt index f874eebff..182fc0156 100644 --- a/fastlane/metadata/android/sv/changelogs/64.txt +++ b/fastlane/metadata/android/sv/changelogs/64.txt @@ -1,8 +1,8 @@ ### Förbättringar -- La till möjligheten all begränsa video kvaliteten vid användning av mobildata. #1339 +- La till möjligheten all begränsa videokvaliteten vid användning av mobildata. #1339 - Kom ihåg ljusstyrka för sessionen #1442 -- Förbättra nedladdnings prestandan på svagare CPUs #1431 -- La till (fungerande) support för media sessioner #1433 +- Förbättra nedladdningsprestandan på svagare CPUs #1431 +- La till (fungerande) support för mediasessioner #1433 ### Fixade - Fixade krasch vid öppning av nedladdningar (fixen är nu tillgänglig för release builds) #1441 diff --git a/fastlane/metadata/android/sv/changelogs/957.txt b/fastlane/metadata/android/sv/changelogs/957.txt new file mode 100644 index 000000000..a8be4f09b --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/957.txt @@ -0,0 +1,10 @@ +• Förenade specifika kö åtgärder +• Två finger gest att stänga spelare +• Tillåt rensning av reCAPTCHA-cookies +• Inställning för att inte färglägga meddelande +• Förbättra hur videoinformation öppnas för att fixa oändlig buffring, buggigt beteende när du delar till NewPipe och andra inkonsekvenser +• Snabba upp YouTube-videor och fixa åldersbegränsning +• Fixa krasch vid snabb fram/bak spolning +• Ordna inte listor genom att dra miniatyrer +• Kom alltid ihåg popup egenskaper +• Lägga till Santali språk diff --git a/fastlane/metadata/android/sv/changelogs/958.txt b/fastlane/metadata/android/sv/changelogs/958.txt new file mode 100644 index 000000000..268362f87 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/958.txt @@ -0,0 +1,10 @@ +Nytt och förbättrat: +• La till inställning för att dölja miniatyr på låsskärmen +• Dra för att uppdatera flöde +• Förbättrad prestanda vid hämtning av lokala listor + +Fixat: +• Fixade krasch när du startar NewPipe efter att den togs bort från RAM +• Fixade krasch vid start när det inte finns någon internetanslutning +• Fixade respekt för ljusstyrka- och volymgestinställningar +• [YouTube] Fixade långa spellistor diff --git a/fastlane/metadata/android/sv/changelogs/961.txt b/fastlane/metadata/android/sv/changelogs/961.txt new file mode 100644 index 000000000..6e1abc229 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/961.txt @@ -0,0 +1,12 @@ +• [YouTube] Mix stöd +• [YouTube] Visa information om offentliga programföretag och Covid-19 +• [media.ccc.de] La till nya videor +• La till somalisk översättning + +• Många interna förbättringar + +• Fixade delning av videor ifrån spelaren +• Fixade blank ReCaptcha webview +• Fixade krasch som inträffade när du tar bort en ström från en lista +• [PeerTube] Fixade relaterade strömmar +• [YouTube] Fixade YouTube Musiksökning diff --git a/fastlane/metadata/android/sv/changelogs/964.txt b/fastlane/metadata/android/sv/changelogs/964.txt new file mode 100644 index 000000000..cbdc9f4ff --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/964.txt @@ -0,0 +1,7 @@ +• La till stöd för kapitel i spelarkontroller +• [PeerTube] La till Sepia-sökning +• La till dela knappen i videodetaljvy och flyttad strömbeskrivning till flik layouten +• Inaktivera återställande av ljusstyrka om ljusstyrks gest är inaktiverad +• Lägg till listobjekt för att spela video på kodi +• Fixade krasch när ingen standardwebbläsare är inställd på vissa enheter och förbättrade dela dialog +• Växla spela/pausa med hårdvaru mellanslags knapp i fullskärm diff --git a/fastlane/metadata/android/sv/changelogs/965.txt b/fastlane/metadata/android/sv/changelogs/965.txt new file mode 100644 index 000000000..9b298947a --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/965.txt @@ -0,0 +1,6 @@ +Fixade krasch som inträffade vid förflyttning av kanalgrupper. +Fixade hämtning av fler YouTube videor från kanaler och spellistor. +Fixade hämtning av YouTube kommentarer. +La till stöd för /watch /, /v / och /w / subpaths i YouTube adresser. +Fixade extraktion av SoundCloud klient id och geobegränsat innehåll. +La till nordkurdisk lokalisering. diff --git a/fastlane/metadata/android/sv/changelogs/966.txt b/fastlane/metadata/android/sv/changelogs/966.txt new file mode 100644 index 000000000..b8e183604 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/966.txt @@ -0,0 +1,14 @@ +Nytt: +• La till en ny tjänst: Bandcamp + +Förbättrat: +• Lägg till en inställning för att appen ska följa enhetstemat +• Förhindra vissa kraschar genom att visa en förbättrad felpanel +• Visa mer information om varför innehåll är otillgängligt +• Hårdvaru mellanslagsknapp ändrar spela/pausa +• Visa "Nedladdning startad" toast + +Fixat: +• Fixade liten miniatyr i videodetaljer medan du spelar i bakgrunden +• Fixade tom titel i minimerad spelare +• Fixade senaste storleksläge som inte återställdes korrekt diff --git a/fastlane/metadata/android/sv/changelogs/968.txt b/fastlane/metadata/android/sv/changelogs/968.txt new file mode 100644 index 000000000..4582309f4 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/968.txt @@ -0,0 +1,7 @@ +La till kanalinformationsalternativ i långtrycksmenyn. +La till funktionalitet för att byta namn från spellistans gränssnitt. +Låt användaren pausa medan en video buffrar. +Polerade det ljusa temat. +Fixade överlappande teckensnitt när du använder en större teckenstorlek. +Fixade ingen video på Formuler och Zephier enheter. +Fixade olika kraschar. diff --git a/fastlane/metadata/android/sv/changelogs/970.txt b/fastlane/metadata/android/sv/changelogs/970.txt new file mode 100644 index 000000000..020ee2c1b --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/970.txt @@ -0,0 +1,11 @@ +Nytt +• Visa innehåll metadata (taggar, kategorier, licens, ...) under beskrivningen +• Lägg till alternativet "Visa kanalinformation" i distans (icke-lokala) spellistor +• Lägg till "Öppna i webbläsare" -alternativ för långtrycksmeny + +Fixat +• Fixade rotations krasch på video detalj sida +• Fixade "Spela med Kodi" knappen i spelaren som alltid uppmanade till att installera Kore +• Fixade och förbättrad inställning av import och exportvägar +• [YouTube] Fixade kommentarers gilla antal +Och mycket mer diff --git a/fastlane/metadata/android/sv/changelogs/975.txt b/fastlane/metadata/android/sv/changelogs/975.txt new file mode 100644 index 000000000..f280c3830 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/975.txt @@ -0,0 +1,15 @@ +Nytt +• Visa en miniatyr förhandsvisning medan du söker +• Upptäck avstängda kommentarer +• Tillåt markering av ett flödesobjekt som visat +• Visa kommentar hjärtan + +Förbättrat +• Förbättrade metadata och tagg layout +• Applicera service färg till UI komponenter + +Fixat +• Fixade miniatyrbild i minispelare +• Fixade oändlig buffring på dubbla köartiklar +• Vissa spelare fixar som rotation och snabbare stängning +• Fixade ReCAPTCHA som var kvar i bakgrunden diff --git a/fastlane/metadata/android/sv/changelogs/976.txt b/fastlane/metadata/android/sv/changelogs/976.txt new file mode 100644 index 000000000..96b005170 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/976.txt @@ -0,0 +1,9 @@ +• La till alternativ för att direkt öppna spelare i fullskärm +• Tillåt användaren att välja vilka typer av sökförslag som ska visas +• Mörkt tema är nu mörkare + mörk startskärm tillagd +• Förbättrade fil väljare att gråa ut oönskade filer +• Fixade import av YouTube prenumerationer +• Att spela en ström kräver tryck på spela knappen igen +• [Android TV] Fixa långa sökbar hopp vid användning av en DPad + +För att se ytterligare ändringar, se ändringsloggen (och blogginlägg) från Länkfliken nedan. diff --git a/fastlane/metadata/android/sv/changelogs/983.txt b/fastlane/metadata/android/sv/changelogs/983.txt new file mode 100644 index 000000000..edc397a29 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/983.txt @@ -0,0 +1,9 @@ +La till nytt dubbel tryck för att söka UI och beteende +Gör inställningar sökbara +Markera fäst kommentarer som sådana +Lägg till öppna med app stöd för FSFE PeerTube instans +Lägg till felmeddelanden +Fixade repris av första kö objekt vid spelar förändring +Vänta längre vid buffring under livestreams innan misslyckande +Fixade ordning av lokala sökresultat +Fixade tomma objektfält i spelkö diff --git a/fastlane/metadata/android/sv/changelogs/986.txt b/fastlane/metadata/android/sv/changelogs/986.txt new file mode 100644 index 000000000..2ebaacaa8 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/986.txt @@ -0,0 +1,15 @@ +Nytt +• Avisering för nya strömningar +• Sömlös övergång mellan bakgrund och videospelare +• Ändra pitch av semitoner +• Lägg till huvudspelarens kö i en spellista + +Förbättrat +• Kom ihåg hastighet/pitch steg storlek +• Mildra initial lång buffring i videospelaren +• Förbättra spelarens UI för Android TV +• Bekräfta innan borttagning av alla nedladdade filer + +Fixat +• Fixade media knapp som inte gömde spelarkontroller +• Fixade uppspelningsåterställning vid spelar förändring diff --git a/fastlane/metadata/android/sv/changelogs/987.txt b/fastlane/metadata/android/sv/changelogs/987.txt new file mode 100644 index 000000000..59416e59e --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/987.txt @@ -0,0 +1,12 @@ +Nytt +• Stöd andra leveransmetoder än progressiv HTTP: snabbare uppspelningstid, fixar för PeerTube och SoundCloud, uppspelning av nyligen avslutade YouTube strömmar +• La till knapp för att lägga till en fjärrspellista till en lokal +• Bildförhandsvisning i Android 10+ dela blad + +Förbättrat +• Förbättrade uppspelningsparametrar dialog +• Flytta abonnemang import/exportknappar till trepunktsmenyn + +Fixat +• Fixade ta bort tittade videor från spellista +• Fixade dela meny tema och "lägg till i spellista" diff --git a/fastlane/metadata/android/sv/changelogs/988.txt b/fastlane/metadata/android/sv/changelogs/988.txt new file mode 100644 index 000000000..09335ecbc --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/988.txt @@ -0,0 +1,2 @@ +[YouTube] Fixade "Kan inte få någon ström" fel vid spelning av någon video +[YouTube] Fixade "Det följande innehållet är inte tillgängligt på den här appen." meddelandet visas istället för videon som begärs diff --git a/fastlane/metadata/android/sv/changelogs/989.txt b/fastlane/metadata/android/sv/changelogs/989.txt new file mode 100644 index 000000000..28dcdf245 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/989.txt @@ -0,0 +1,3 @@ +• [YouTube] Fixade oändlig laddning vid försök att spela någon video +• [YouTube] Fixade throttling på vissa videor +• Uppgraderade jsoup-biblioteket till 1.15.3, vilket inkluderar en säkerhetsfix diff --git a/fastlane/metadata/android/sv/changelogs/990.txt b/fastlane/metadata/android/sv/changelogs/990.txt new file mode 100644 index 000000000..1f1069acc --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/990.txt @@ -0,0 +1,15 @@ +Denna version avslutar stödet för Android 4.4 KitKat, nu är den minsta versionen Android 5 Lollipop! + +Nytt +• Ladda ner från lång-tryck menyn +• Dölj framtida videor i flödet +• Dela lokala spellistor + +Förbättrat +• Refaktorera spelarkoden i små komponenter: mindre RAM används, mindre buggar +• Förbättra miniatyrernas skalläge +• Vectorisera bildplatshållare + +Fixat +• Fixade problem med spelarens meddelande: föråldrad/saknad media info, förvrängd miniatyr +• Fixade fullskärm som använde 1/4 av skärmen diff --git a/fastlane/metadata/android/sv/changelogs/991.txt b/fastlane/metadata/android/sv/changelogs/991.txt new file mode 100644 index 000000000..7f79c5ee7 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/991.txt @@ -0,0 +1,13 @@ +Nytt +• Lägg till "Öppna i webbläsare" knappen i felpanel +• Lägg till inställning för att visa kanalgrupper som lista +• [YouTube] Långtryck på strömsegment för att dela tidsstämpel URL +• Lägg till play kö knapp till minispelare + +Förbättrat +• Lägg till isländsk lokalisering och uppdatera många andra översättningar +• Många interna förbättringar + +Fixat +• Fixade flera kraschar +• [YouTube] Fixade laddning av kanaler, icke-dedikerade flöden och uppspelningsproblem i vissa länder diff --git a/fastlane/metadata/android/sv/changelogs/992.txt b/fastlane/metadata/android/sv/changelogs/992.txt new file mode 100644 index 000000000..253f1e531 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/992.txt @@ -0,0 +1,17 @@ +Nytt +• Prenumerant antal i videodetaljer +• Ladda ner från kön +• Ställ in en permanent miniatyr fr spellista +• Långtryckt hashtags och länkar +• Kortvys läge + +Förbättrat +• Större minispelar stäng knapp +• Mjukare miniatyrs nedskalning +• Sikta på Android 13 (API 33) +• Sökning pausar inte längre spelaren + +Fixat +• Fixade overlay på DeX/Mouse +• Tillåt bakgrundsspelare utan separata ljudströmmar +• Olika YouTube-fixar och mer… diff --git a/fastlane/metadata/android/sv/changelogs/993.txt b/fastlane/metadata/android/sv/changelogs/993.txt new file mode 100644 index 000000000..a06eafcd7 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/993.txt @@ -0,0 +1,12 @@ +Nytt +• La till varning vid tilläggning av dubbletter i spellista och la till knapp för att ta bort dem +• Tillåt ignorering av hårdvaruknappar +• Tillåt att dölja delvis tittade videor från flödet + +Förbättrat +• Använd fler rutnät kolumner på stora skärmar +• Gör framstegsindikatorer konsekventa med inställningar + +Fixat +• Fixade öppning av webbläsar URLs, nedladdningar och externa spelare på Android 11+ +• Fixade interaktion med helskärm som kräver två tryck på MIUI diff --git a/fastlane/metadata/android/sv/changelogs/994.txt b/fastlane/metadata/android/sv/changelogs/994.txt new file mode 100644 index 000000000..a07e83500 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/994.txt @@ -0,0 +1,15 @@ +Nytt +• Stöd flera ljudspår/språk +• Tillåt inställning av volym och ljusstyrks gester på valfri sida av skärmen +• Stöd för att visa huvudflikar längst ner på skärmen + +Förbättrat +• [Bandcamp] Hantera spår bakom betalvägg + +Fixat +• [YouTube] 403 HTTP-fel för strömmar +• Svart spelare vid byte till huvudspelare från spellistans vy +• Spela service minnesläckor +• [PeerTube] Uppladdare och sub kanal avatarer var utbytta + +och mer diff --git a/fastlane/metadata/android/sv/changelogs/995.txt b/fastlane/metadata/android/sv/changelogs/995.txt new file mode 100644 index 000000000..be1460739 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/995.txt @@ -0,0 +1,16 @@ +Nytt +• Stöd kanalflikar +• Välj bildkvalitet +• Få webbadresser till alla bilder + +Förbättrat +• Tillgänglighet för spelargränssnitt +• Bättre ljudval för endast video nedladdningar +• Alternativ att inkludera spellista och videonamn till delat spellistinnehåll + +Fixat +• [YouTube] Fixade hämtning av gillaantal +• Fixade spelare som inte svarar popup och kraschar +• Val av fel språk i språkväljare +• Spelarens ljudfokus respekterade inte mute +• Spellist-objekt addition som ibland inte fungerade diff --git a/fastlane/metadata/android/sv/changelogs/996.txt b/fastlane/metadata/android/sv/changelogs/996.txt new file mode 100644 index 000000000..8ee87d8c8 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/996.txt @@ -0,0 +1,2 @@ +Fixade en NullPointerException när du öppnar en kanal / konferens i media.ccc.de. +Grinchen försökte förstöra vår julklapp till er, men vi fixade den. diff --git a/fastlane/metadata/android/sv/short_description.txt b/fastlane/metadata/android/sv/short_description.txt index 6b29f892c..d0e04f585 100644 --- a/fastlane/metadata/android/sv/short_description.txt +++ b/fastlane/metadata/android/sv/short_description.txt @@ -1 +1 @@ -En gratis lättviktsklient för YouTube på Android. +En gratis lättviktsklient för YouTube för Android. diff --git a/fastlane/metadata/android/tr/full_description.txt b/fastlane/metadata/android/tr/full_description.txt index 9422ff2ba..2b31f8dfd 100644 --- a/fastlane/metadata/android/tr/full_description.txt +++ b/fastlane/metadata/android/tr/full_description.txt @@ -1,2 +1,2 @@ -NewPipe herhangi bir Google frameworkü veya YouTube API'si kullanmaz. Sadece, ihtiyaç duyduğu bilgiyi edinmek için web sitesini ayrıştırır. -Bu nedenle Google hizmetlerinin kurulmadığı aygıtlarda kullanılabilir. Ayrıca, NewPipe'ı kullanırken YouTube hesabına ihtiyacınız yok, ve bu özgür ve açık kaynaklı bir yazılımdır. +NewPipe herhangi bir Google framework’ü ya da YouTube API’ı kullanmaz. Yalnızca, kendisine gereken bilgiyi edinmek için web sitesini ayrıştırır. +Bu nedenle Google hizmetlerinin kurulmadığı aygıtlarda kullanılabilir. Ayrıca NewPipe'ı kullanırken bir YouTube hesabına gerek yoktur ve NewPipe özgür ve açık kaynaklı bir yazılımdır. diff --git a/fastlane/metadata/android/uk/changelogs/996.txt b/fastlane/metadata/android/uk/changelogs/996.txt new file mode 100644 index 000000000..e9fd2c523 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/996.txt @@ -0,0 +1,2 @@ +Виправлено NullPointerException під час відкриття каналу / конференції у media.ccc.de. +Грінч намагався зламати наш різдвяний подарунок, але ми його полагодили. diff --git a/fastlane/metadata/android/vi/changelogs/65.txt b/fastlane/metadata/android/vi/changelogs/65.txt index 3bffa51b0..de47bd38b 100644 --- a/fastlane/metadata/android/vi/changelogs/65.txt +++ b/fastlane/metadata/android/vi/changelogs/65.txt @@ -1,9 +1,27 @@ ##Cải thiện: +### Cải tiến -- Tắt hoạt ảnh icon burgermenu #1486 -- Cho phép hoàn tắc nội dung đã tải xuống #1472 -- Tùy chọn tải xuống ở menu chia sẻ #1498 -- Đã thêm tùy chọn chia sẻ vào menu nhẫn giữ #1454 -- Thu nhỏ bộ phát phương tiện chính khi thoát #1354 -- Sửa lỗi Cập nhật thư viện và cơ sở dữ liệu dự phòng #1510 -- Cập nhật trình phát ExoPlayer 2.8.2 #1392 +- Tắt hoạt ảnh biểu tượng burgermenu #1486 +- hoàn tác xóa các bản tải xuống # 1472 +- Tùy chọn tải xuống trong menu chia sẻ #1498 +- Đã thêm tùy chọn chia sẻ vào menu nhấn dài #1454 +- Thu nhỏ trình phát chính ở lối ra #1354 +- Cập nhật phiên bản thư viện và sửa lỗi sao lưu cơ sở dữ liệu #1510 +- Cập nhật ExoPlayer 2.8.2 #1392 + - Làm lại hộp thoại kiểm soát tốc độ phát lại để hỗ trợ các kích cỡ bước khác nhau nhằm thay đổi tốc độ nhanh hơn. + - Đã thêm nút chuyển đổi để tua đi nhanh trong khi im lặng trong điều khiển tốc độ phát lại. Điều này sẽ hữu ích cho sách nói và một số thể loại âm nhạc nhất định, đồng thời có thể mang lại trải nghiệm liền mạch thực sự (và có thể ngắt một bài hát có nhiều khoảng lặng =\\). + - Độ phân giải nguồn phương tiện được tái cấu trúc để cho phép truyền siêu dữ liệu cùng với phương tiện bên trong trình phát, thay vì thực hiện thủ công. Bây giờ chúng tôi có một nguồn siêu dữ liệu duy nhất và có sẵn trực tiếp khi quá trình phát lại bắt đầu. + - Đã sửa lỗi siêu dữ liệu danh sách phát từ xa không cập nhật khi có siêu dữ liệu mới khi mở đoạn danh sách phát. + - Các bản sửa lỗi giao diện người dùng khác nhau: #1383, các điều khiển thông báo trình phát nền giờ đây luôn có màu trắng, dễ dàng tắt trình phát bật lên hơn thông qua thao tác ném +- Sử dụng trình trích xuất mới với kiến trúc được tái cấu trúc cho nhiều dịch vụ + +### Sửa lỗi + +- Sửa lỗi #1440 Bố cục thông tin video bị hỏng #1491 +- Xem lịch sử sửa lỗi #1497 + - #1495, bằng cách cập nhật siêu dữ liệu (hình thu nhỏ, tiêu đề và số lượng video) ngay khi người dùng truy cập danh sách phát. + - #1475, bằng cách đăng ký chế độ xem trong cơ sở dữ liệu khi người dùng bắt đầu phát video trên trình phát bên ngoài trên đoạn chi tiết. +- Sửa lỗi hết thời gian chờ màn hình trong trường hợp ở chế độ bật lên. #1463 (Đã sửa #640) +- Sửa lỗi trình phát video chính #1509 + - [#1412] Đã sửa lỗi chế độ lặp lại khiến người chơi gặp NPE khi nhận được ý định mới trong khi hoạt động của người chơi ở chế độ nền. + - Đã sửa lỗi thu nhỏ trình phát thành cửa sổ bật lên không hủy trình phát khi không được cấp quyền bật lên. diff --git a/fastlane/metadata/android/vi/changelogs/66.txt b/fastlane/metadata/android/vi/changelogs/66.txt new file mode 100644 index 000000000..99b09fe05 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/66.txt @@ -0,0 +1,33 @@ +# Nhật ký thay đổi của v0.13.7 + +### Đã sửa +- Khắc phục sự cố bộ lọc sắp xếp của v0.13.6 + +# Nhật ký thay đổi của v0.13.6 + +### Cải tiến + +- Tắt hoạt ảnh biểu tượng burgermenu #1486 +- hoàn tác xóa tải xuống #1472 +- Tùy chọn tải xuống trong menu chia sẻ #1498 +- Đã thêm tùy chọn chia sẻ vào menu nhấn dài #1454 +- Thu nhỏ trình phát chính ở lối ra #1354 +- Phiên bản thư viện cập nhật và bản sửa lỗi sao lưu cơ sở dữ liệu #1510 +- ExoPlayer 2.8.2 Cập nhật #1392 +- Làm lại hộp thoại kiểm soát tốc độ phát lại để hỗ trợ các kích cỡ bước khác nhau nhằm thay đổi tốc độ nhanh hơn. +- Đã thêm nút chuyển đổi để tua đi nhanh trong khi im lặng trong điều khiển tốc độ phát lại. Điều này sẽ hữu ích cho sách nói và một số thể loại âm nhạc nhất định, đồng thời có thể mang lại trải nghiệm liền mạch thực sự (và có thể ngắt một bài hát có nhiều khoảng lặng =\\). +- Độ phân giải nguồn phương tiện được tái cấu trúc để cho phép truyền siêu dữ liệu cùng với phương tiện nội bộ trong trình phát thay vì thực hiện thủ công. Bây giờ chúng tôi có một nguồn siêu dữ liệu duy nhất và có sẵn trực tiếp khi quá trình phát lại bắt đầu. +- Đã sửa lỗi siêu dữ liệu danh sách phát từ xa không cập nhật khi có siêu dữ liệu mới khi mở đoạn danh sách phát. +- Nhiều bản sửa lỗi giao diện người dùng khác nhau: #1383, các điều khiển thông báo trình phát nền giờ đây luôn có màu trắng, dễ dàng tắt trình phát cửa sổ bật lên thông qua thao tác ném +- Sử dụng trình trích xuất mới với kiến trúc được tái cấu trúc cho nhiều dịch vụ + +### Sửa lỗi + +- Sửa lỗi #1440 Bố cục thông tin video bị hỏng #1491 +- Xem lịch sử sửa lỗi #1497 +- #1495, bằng cách cập nhật siêu dữ liệu (hình thu nhỏ, tiêu đề và số lượng video) ngay khi người dùng truy cập vào danh sách phát. +- #1475, bằng cách đăng ký chế độ xem trong cơ sở dữ liệu khi người dùng bắt đầu video trên trình phát bên ngoài trên đoạn chi tiết. +- Sửa lỗi màn hình hết thời gian chờ trong chế độ bật lên. #1463 (Đã sửa #640) +- Sửa lỗi trình phát video chính #1509 +- [#1412] Đã sửa lỗi chế độ lặp lại khiến người chơi NPE khi nhận được ý định mới trong khi hoạt động của người chơi ở trong nền. +- Đã sửa lỗi thu nhỏ trình phát thành cửa sổ bật lên không hủy trình phát khi không được cấp quyền cửa sổ bật lên. diff --git a/fastlane/metadata/android/vi/changelogs/68.txt b/fastlane/metadata/android/vi/changelogs/68.txt new file mode 100644 index 000000000..2247be255 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/68.txt @@ -0,0 +1,31 @@ +# thay đổi của v0.14.1 + +### Đã sửa +- Đã sửa lỗi không giải mã được url video #1659 +- Sửa lỗi link mô tả không giải nén tốt #1657 + +# thay đổi của v0.14.0 + +### Mới +- Thiết kế ngăn kéo mới #1461 +- Trang trước có thể tùy chỉnh mới #1461 + +### Cải tiến +- Điều khiển cử chỉ được làm lại #1604 +- Cách mới để đóng trình phát cửa sổ bật lên #1597 + +### Đã sửa +- Sửa lỗi khi không có số lượng đăng ký. Đóng #1649. +- Hiển thị "Không có số lượng người đăng ký" trong những trường hợp đó +- Khắc phục NPE khi danh sách phát YouTube trống +- Sửa nhanh các ki-ốt trong SoundCloud +- Tái cấu trúc và sửa lỗi #1623 +- Sửa kết quả tìm kiếm theo chu kỳ #1562 +- Sửa lỗi thanh Tìm kiếm không được bố trí tĩnh +- Sửa lỗi video YT Premium không bị chặn đúng cách +- Khắc phục Video đôi khi không tải (do phân tích cú pháp DASH) +- Sửa các liên kết trong phần mô tả video +- Hiển thị cảnh báo khi ai đó cố gắng tải xuống thẻ sdcard bên ngoài +- sửa lỗi không hiển thị báo cáo kích hoạt ngoại lệ +- hình thu nhỏ không hiển thị trong trình phát nền dành cho android 8.1 [xem tại đây](https://github.com/TeamNewPipe/NewPipe/issues/943) +- Sửa lỗi đăng ký máy thu phát sóng. Đóng #1641. diff --git a/fastlane/metadata/android/vi/changelogs/69.txt b/fastlane/metadata/android/vi/changelogs/69.txt new file mode 100644 index 000000000..a6fb21da0 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/69.txt @@ -0,0 +1,19 @@ +### Mới +- Nhấn và giữ để xóa và chia sẻ trong đăng ký #1516 +- Giao diện người dùng máy tính bảng và bố cục danh sách lưới #1617 + +### Cải tiến +- lưu trữ và tải lại tỷ lệ khung hình được sử dụng lần cuối # 1748 +- Bật bố cục tuyến tính trong hoạt động Tải xuống với tên video đầy đủ #1771 +- Xóa và chia sẻ đăng ký trực tiếp từ trong tab đăng ký #1516 +- Việc xếp hàng hiện sẽ kích hoạt phát video nếu hàng đợi phát đã kết thúc #1783 +- Riêng cài đặt cho cử chỉ âm lượng và độ sáng #1644 +- Thêm hỗ trợ Bản địa hóa #1792 + +### Sửa lỗi +- Sửa lỗi phân tích thời gian cho . định dạng, vì vậy NewPipe có thể được sử dụng ở Phần Lan +- Sửa số lượng đăng ký +- Thêm quyền dịch vụ nền trước cho các thiết bị API 28+ #1830 + +### Lỗi đã biết +- Không thể lưu trạng thái phát lại trên Android P diff --git a/fastlane/metadata/android/vi/changelogs/820.txt b/fastlane/metadata/android/vi/changelogs/820.txt index f04843377..8554471a8 100644 --- a/fastlane/metadata/android/vi/changelogs/820.txt +++ b/fastlane/metadata/android/vi/changelogs/820.txt @@ -1 +1 @@ -Sửa regex trong hàm giải mã đang làm cho phần YouTube không xài được. +Sửa regex trong hàm giải mã đang làm cho phần YouTube không dùng được. diff --git a/fastlane/metadata/android/vi/changelogs/996.txt b/fastlane/metadata/android/vi/changelogs/996.txt new file mode 100644 index 000000000..2f92c6dc2 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/996.txt @@ -0,0 +1,2 @@ +Đã sửa lỗi NullPointerException khi mở kênh/hội nghị trong media.ccc.de. +Grinch đã cố làm vỡ món quà Giáng sinh của chúng tôi dành cho bạn, nhưng chúng tôi đã sửa nó. diff --git a/fastlane/metadata/android/zh_Hant_HK/changelogs/995.txt b/fastlane/metadata/android/zh_Hant_HK/changelogs/995.txt index ddd775f08..87050f4a5 100644 --- a/fastlane/metadata/android/zh_Hant_HK/changelogs/995.txt +++ b/fastlane/metadata/android/zh_Hant_HK/changelogs/995.txt @@ -1,14 +1,16 @@ 新嘢 • 支援頻道分頁 • 啲圖有得揀畫質 -• 啲圖全部都有得攞返個 URL +• 啲圖全部都有得攞返條拎 進步 • 播放器介面暢易達 -• 分享播放清單有得加埋播放清單個名同入面啲片名 -• 內部提昇以及依賴元件更新 +• 揀返條靚啲嘅聲黐返落去齋晝面嘅影片下載 +• 分享播放清單時有得加埋播放清單個名同入面啲片名 修正 +• [YouTube] 執返掂個 like 數 +• 執返好播放器話冇回應個框框同埋閃退 • 揀選版面語言揀錯文 • 播放器聲音焦點無視噤聲 • 加入項目去播放清單有時唔得 diff --git a/fastlane/metadata/android/zh_Hant_HK/changelogs/996.txt b/fastlane/metadata/android/zh_Hant_HK/changelogs/996.txt new file mode 100644 index 000000000..8b136fc42 --- /dev/null +++ b/fastlane/metadata/android/zh_Hant_HK/changelogs/996.txt @@ -0,0 +1,2 @@ +修正開啟 media.ccc.de 嘅頻道/會議時發生 NullPointerException 失卻指標錯誤。 +柳煙輕愜夜,俊緒留江影。謝謝您陪我哋跑完悲歡離合嘅一年。 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2c3425d49..d022615ff 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionSha256Sum=38f66cd6eef217b4c35855bb11ea4e9fbc53594ccccb5fb82dfd317ef8c2c5a3 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists