From 96d6b309ec394dafb98ab944c842fda905af649e Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Wed, 13 Apr 2022 19:41:07 +0800 Subject: [PATCH 001/123] Migrate database --- .../6.json | 737 ++++++++++++++++++ .../newpipe/database/DatabaseMigrationTest.kt | 5 + .../org/schabi/newpipe/NewPipeDatabase.java | 4 +- .../schabi/newpipe/database/AppDatabase.java | 4 +- .../schabi/newpipe/database/Migrations.java | 42 +- .../playlist/model/PlaylistEntity.java | 12 + 6 files changed, 800 insertions(+), 4 deletions(-) create mode 100644 app/schemas/org.schabi.newpipe.database.AppDatabase/6.json diff --git a/app/schemas/org.schabi.newpipe.database.AppDatabase/6.json b/app/schemas/org.schabi.newpipe.database.AppDatabase/6.json new file mode 100644 index 000000000..34d457f83 --- /dev/null +++ b/app/schemas/org.schabi.newpipe.database.AppDatabase/6.json @@ -0,0 +1,737 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "cc9c4d84f52f49105b1c4216b948b5f7", + "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": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "stream_id", + "access_date" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "stream_id" + ], + "autoGenerate": false + }, + "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, `thumbnail_url` TEXT, `display_index` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnail_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayIndex", + "columnName": "display_index", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "playlist_id", + "join_index" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "stream_id", + "subscription_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "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": { + "columnNames": [ + "group_id", + "subscription_id" + ], + "autoGenerate": false + }, + "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": { + "columnNames": [ + "subscription_id" + ], + "autoGenerate": false + }, + "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, 'cc9c4d84f52f49105b1c4216b948b5f7')" + ] + } +} \ 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 28dea13e9..6d05a45bf 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -84,6 +84,11 @@ class DatabaseMigrationTest { true, Migrations.MIGRATION_4_5 ) + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, + true, Migrations.MIGRATION_5_6 + ) + val migratedDatabaseV3 = getMigratedDatabase() val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst() diff --git a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java index 402d4648d..fc3423994 100644 --- a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java +++ b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java @@ -5,6 +5,7 @@ import static org.schabi.newpipe.database.Migrations.MIGRATION_1_2; import static org.schabi.newpipe.database.Migrations.MIGRATION_2_3; 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 android.content.Context; import android.database.Cursor; @@ -24,7 +25,8 @@ public final class NewPipeDatabase { private static AppDatabase getDatabase(final Context context) { return Room .databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) - .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5) + .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, + MIGRATION_5_6) .build(); } 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 28ddc8184..563e80b17 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_5; +import static org.schabi.newpipe.database.Migrations.DB_VER_6; 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_5 + version = DB_VER_6 ) 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/Migrations.java b/app/src/main/java/org/schabi/newpipe/database/Migrations.java index 7de08442c..a8f093ba0 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -23,6 +23,7 @@ public final class Migrations { public static final int DB_VER_3 = 3; public static final int DB_VER_4 = 4; public static final int DB_VER_5 = 5; + public static final int DB_VER_6 = 6; private static final String TAG = Migrations.class.getName(); public static final boolean DEBUG = MainActivity.DEBUG; @@ -184,7 +185,46 @@ 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"); + } + }; + + public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) { + @Override + public void migrate(@NonNull final SupportSQLiteDatabase database) { + try { + database.beginTransaction(); + + // create a temp table to initialize display_index + database.execSQL("CREATE TABLE `playlists_tmp` " + + "(`uid` INTEGER NOT NULL, " + + "`name` TEXT, `thumbnail_url` TEXT," + + "`display_index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)"); + database.execSQL("INSERT INTO `playlists_tmp` (`uid`, `name`, `thumbnail_url`)" + + "SELECT `uid`, `name`, `thumbnail_url` FROM `playlists`"); + + // drop the old table and create new one + database.execSQL("DROP TABLE `playlists`"); + database.execSQL("CREATE TABLE `playlists` " + + "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`name` TEXT, `thumbnail_url` TEXT," + + "`display_index` INTEGER NOT NULL UNIQUE)"); + + // insert temp data into the new table + // set display_index start from zero + database.execSQL("INSERT INTO `playlists` SELECT * FROM `playlists_tmp`"); + database.execSQL("UPDATE `playlists` SET `display_index` = `display_index` - 1"); + + // drop tmp table + database.execSQL("DROP TABLE `playlists_tmp`"); + + // create index on the new table + database.execSQL("CREATE INDEX `index_playlists_name` ON `playlists` (`name`)"); + + database.setTransactionSuccessful(); + } finally { + database.endTransaction(); + } } }; 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 71abf2732..c1ae0a2b3 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 @@ -15,6 +15,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"; @PrimaryKey(autoGenerate = true) @ColumnInfo(name = PLAYLIST_ID) @@ -26,6 +27,9 @@ public class PlaylistEntity { @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) private String thumbnailUrl; + @ColumnInfo(name = PLAYLIST_DISPLAY_INDEX) + private long displayIndex = 0; + public PlaylistEntity(final String name, final String thumbnailUrl) { this.name = name; this.thumbnailUrl = thumbnailUrl; @@ -54,4 +58,12 @@ public class PlaylistEntity { public void setThumbnailUrl(final String thumbnailUrl) { this.thumbnailUrl = thumbnailUrl; } + + public long getDisplayIndex() { + return displayIndex; + } + + public void setDisplayIndex(final long displayIndex) { + this.displayIndex = displayIndex; + } } From c34549a47d0c39050ddd34357e43adfce56ea638 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Wed, 13 Apr 2022 21:35:38 +0800 Subject: [PATCH 002/123] Update database migrations and getter/setter --- .../6.json | 12 +++-- .../schabi/newpipe/database/Migrations.java | 44 ++++++++++++------- .../database/playlist/PlaylistLocalItem.java | 1 + .../playlist/PlaylistMetadataEntry.java | 6 ++- .../playlist/dao/PlaylistStreamDAO.java | 2 + .../playlist/model/PlaylistRemoteEntity.java | 25 +++++++++++ .../local/bookmark/BookmarkFragment.java | 1 - .../local/playlist/LocalPlaylistManager.java | 15 +++++-- 8 files changed, 83 insertions(+), 23 deletions(-) diff --git a/app/schemas/org.schabi.newpipe.database.AppDatabase/6.json b/app/schemas/org.schabi.newpipe.database.AppDatabase/6.json index 34d457f83..3ef363e30 100644 --- a/app/schemas/org.schabi.newpipe.database.AppDatabase/6.json +++ b/app/schemas/org.schabi.newpipe.database.AppDatabase/6.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 6, - "identityHash": "cc9c4d84f52f49105b1c4216b948b5f7", + "identityHash": "9ffc14521c566beed378d77430de3f0c", "entities": [ { "tableName": "subscriptions", @@ -447,7 +447,7 @@ }, { "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)", + "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", @@ -485,6 +485,12 @@ "affinity": "TEXT", "notNull": false }, + { + "fieldPath": "displayIndex", + "columnName": "display_index", + "affinity": "INTEGER", + "notNull": true + }, { "fieldPath": "streamCount", "columnName": "stream_count", @@ -731,7 +737,7 @@ "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, 'cc9c4d84f52f49105b1c4216b948b5f7')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9ffc14521c566beed378d77430de3f0c')" ] } } \ No newline at end of file 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 a8f093ba0..ffca6cca5 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -195,32 +195,46 @@ public final class Migrations { try { database.beginTransaction(); + // update playlists // create a temp table to initialize display_index database.execSQL("CREATE TABLE `playlists_tmp` " - + "(`uid` INTEGER NOT NULL, " + + "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + "`name` TEXT, `thumbnail_url` TEXT," - + "`display_index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)"); + + "`display_index` INTEGER NOT NULL DEFAULT 0)"); database.execSQL("INSERT INTO `playlists_tmp` (`uid`, `name`, `thumbnail_url`)" + "SELECT `uid`, `name`, `thumbnail_url` FROM `playlists`"); - // drop the old table and create new one + // replace the old table database.execSQL("DROP TABLE `playlists`"); - database.execSQL("CREATE TABLE `playlists` " - + "(`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " - + "`name` TEXT, `thumbnail_url` TEXT," - + "`display_index` INTEGER NOT NULL UNIQUE)"); - - // insert temp data into the new table - // set display_index start from zero - database.execSQL("INSERT INTO `playlists` SELECT * FROM `playlists_tmp`"); - database.execSQL("UPDATE `playlists` SET `display_index` = `display_index` - 1"); - - // drop tmp table - database.execSQL("DROP TABLE `playlists_tmp`"); + database.execSQL("ALTER TABLE `playlists_tmp` RENAME TO `playlists`"); // create index on the new table database.execSQL("CREATE INDEX `index_playlists_name` ON `playlists` (`name`)"); + + // 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 DEFAULT 0," + + "`stream_count` INTEGER)"); + database.execSQL("INSERT INTO `remote_playlists_tmp` (`uid`, `service_id`, " + + "`name`, `url`, `thumbnail_url`, `uploader`, `stream_count`)" + + "SELECT `uid`, `service_id`, `name`, `url`, `thumbnail_url`, `uploader`, " + + "`stream_count` FROM `remote_playlists`"); + + // replace the old table + 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 INDEX `index_remote_playlists_name` " + + "ON `remote_playlists` (`name`)"); + database.execSQL("CREATE UNIQUE INDEX `index_remote_playlists_service_id_url` " + + "ON `remote_playlists` (`service_id`, `url`)"); + database.setTransactionSuccessful(); } finally { database.endTransaction(); 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 43dbd89ea..bc17c51c4 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 @@ -14,6 +14,7 @@ public interface PlaylistLocalItem extends LocalItem { static List merge( final List localPlaylists, final List remotePlaylists) { + // todo: merge algorithm final List items = new ArrayList<>( localPlaylists.size() + remotePlaylists.size()); items.addAll(localPlaylists); 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..29bad45dc 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,6 +2,7 @@ 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_URL; @@ -15,14 +16,17 @@ public class PlaylistMetadataEntry implements PlaylistLocalItem { public final String name; @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) public final String thumbnailUrl; + @ColumnInfo(name = PLAYLIST_DISPLAY_INDEX) + public final 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 long displayIndex, final long streamCount) { this.uid = uid; this.name = name; this.thumbnailUrl = thumbnailUrl; + this.displayIndex = displayIndex; this.streamCount = streamCount; } 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 4941d9395..3fb96a21f 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 @@ -15,6 +15,7 @@ import java.util.List; import io.reactivex.rxjava3.core.Flowable; 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.PLAYLIST_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; @@ -75,6 +76,7 @@ public interface PlaylistStreamDAO extends BasicDAO { @Transaction @Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + ", " + PLAYLIST_THUMBNAIL_URL + ", " + + PLAYLIST_DISPLAY_INDEX + ", " + "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT + " FROM " + PLAYLIST_TABLE 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 2e9a15d7d..1fddfa732 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 @@ -31,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) @@ -52,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; + @ColumnInfo(name = REMOTE_PLAYLIST_STREAM_COUNT) private Long streamCount; @@ -66,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(), @@ -136,6 +153,14 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { this.uploader = uploader; } + public long getDisplayIndex() { + return displayIndex; + } + + public void setDisplayIndex(final long displayIndex) { + this.displayIndex = displayIndex; + } + public Long getStreamCount() { return streamCount; } diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index f272a8831..2f36cbd55 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -308,7 +308,6 @@ public final class BookmarkFragment extends BaseLocalListFragment { /*Do nothing on success*/ }, throwable -> showError( 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 33296aa84..aabda1bf0 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 @@ -96,12 +96,17 @@ public class LocalPlaylistManager { } public Maybe renamePlaylist(final long playlistId, final String name) { - return modifyPlaylist(playlistId, name, null); + return modifyPlaylist(playlistId, name, null, -1); } public Maybe changePlaylistThumbnail(final long playlistId, final String thumbnailUrl) { - return modifyPlaylist(playlistId, null, thumbnailUrl); + return modifyPlaylist(playlistId, null, thumbnailUrl, -1); + } + + public Maybe changePlaylistDisplayIndex(final long playlistId, + final long displayIndex) { + return modifyPlaylist(playlistId, null, null, displayIndex); } public String getPlaylistThumbnail(final long playlistId) { @@ -110,7 +115,8 @@ public class LocalPlaylistManager { private Maybe modifyPlaylist(final long playlistId, @Nullable final String name, - @Nullable final String thumbnailUrl) { + @Nullable final String thumbnailUrl, + final long displayIndex) { return playlistTable.getPlaylist(playlistId) .firstElement() .filter(playlistEntities -> !playlistEntities.isEmpty()) @@ -122,6 +128,9 @@ public class LocalPlaylistManager { if (thumbnailUrl != null) { playlist.setThumbnailUrl(thumbnailUrl); } + if (displayIndex != -1) { + playlist.setDisplayIndex(displayIndex); + } return playlistTable.update(playlist); }).subscribeOn(Schedulers.io()); } From 270a541a7c98e975d6c848fce9ee232b6620432b Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Wed, 13 Apr 2022 22:46:24 +0800 Subject: [PATCH 003/123] Implement algorithm to merge playlists --- .../database/playlist/PlaylistLocalItem.java | 58 ++++++++++++++++--- .../playlist/PlaylistMetadataEntry.java | 5 ++ .../playlist/model/PlaylistRemoteEntity.java | 1 + 3 files changed, 57 insertions(+), 7 deletions(-) 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 bc17c51c4..ae81ce3f5 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 @@ -11,18 +11,62 @@ import java.util.List; public interface PlaylistLocalItem extends LocalItem { String getOrderingName(); + long getDisplayIndex(); + static List merge( final List localPlaylists, final List remotePlaylists) { - // todo: merge algorithm - final List items = new ArrayList<>( + + // Merge localPlaylists and remotePlaylists by displayIndex. + // If two items have the same displayIndex, sort them in CASE_INSENSITIVE_ORDER. + // This algorithm is similar to the merge operation in merge sort. + + final List result = new ArrayList<>( localPlaylists.size() + remotePlaylists.size()); - items.addAll(localPlaylists); - items.addAll(remotePlaylists); + 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++; + } + addItemsWithSameIndex(result, itemsWithSameIndex); - Collections.sort(items, Comparator.comparing(PlaylistLocalItem::getOrderingName, - Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))); + // If displayIndex does not match actual index, update displayIndex. + // This may happen when a new list is created with default displayIndex = 0. + // todo: update displayIndex - return items; + return result; + } + + 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 displayIndex, + // add previous items with same index to the result. + addItemsWithSameIndex(result, itemsWithSameIndex); + itemsWithSameIndex.clear(); + } + itemsWithSameIndex.add(item); + } + + static void addItemsWithSameIndex(final List result, + final List itemsWithSameIndex) { + if (itemsWithSameIndex.size() > 1) { + 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/database/playlist/PlaylistMetadataEntry.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java index 29bad45dc..f54ffff13 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 @@ -39,4 +39,9 @@ public class PlaylistMetadataEntry implements PlaylistLocalItem { public String getOrderingName() { return name; } + + @Override + public long getDisplayIndex() { + return 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 1fddfa732..454526769 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 @@ -153,6 +153,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { this.uploader = uploader; } + @Override public long getDisplayIndex() { return displayIndex; } From ba8370bcfd980089ef5984b6ca55e4b072a69fee Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:13:42 +0800 Subject: [PATCH 004/123] Save changes to the database and bugfix --- .../database/playlist/PlaylistLocalItem.java | 14 ++-- .../playlist/dao/PlaylistRemoteDAO.java | 4 ++ .../playlist/model/PlaylistEntity.java | 6 +- .../newpipe/local/LocalItemListAdapter.java | 1 + .../local/bookmark/BookmarkFragment.java | 65 ++++++++++++++++++- .../local/playlist/LocalPlaylistManager.java | 5 +- .../local/playlist/RemotePlaylistManager.java | 15 +++++ 7 files changed, 100 insertions(+), 10 deletions(-) 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 ae81ce3f5..5bf50cd97 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 @@ -24,6 +24,12 @@ public interface PlaylistLocalItem extends LocalItem { final List result = new ArrayList<>( localPlaylists.size() + remotePlaylists.size()); final List itemsWithSameIndex = new ArrayList<>(); + + // The data from database may not be in the displayIndex order + Collections.sort(localPlaylists, + Comparator.comparingLong(PlaylistMetadataEntry::getDisplayIndex)); + Collections.sort(remotePlaylists, + Comparator.comparingLong(PlaylistRemoteEntity::getDisplayIndex)); int i = 0; int j = 0; while (i < localPlaylists.size()) { @@ -41,10 +47,6 @@ public interface PlaylistLocalItem extends LocalItem { } addItemsWithSameIndex(result, itemsWithSameIndex); - // If displayIndex does not match actual index, update displayIndex. - // This may happen when a new list is created with default displayIndex = 0. - // todo: update displayIndex - return result; } @@ -52,8 +54,8 @@ public interface PlaylistLocalItem extends LocalItem { final List itemsWithSameIndex) { if (!itemsWithSameIndex.isEmpty() && itemsWithSameIndex.get(0).getDisplayIndex() != item.getDisplayIndex()) { - // The new item has a different displayIndex, - // add previous items with same index to the result. + // The new item has a different displayIndex, add previous items with same + // index to the result. addItemsWithSameIndex(result, itemsWithSameIndex); itemsWithSameIndex.clear(); } 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..ade857464 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 @@ -31,6 +31,10 @@ 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); 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 c1ae0a2b3..82f697ab3 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,6 +2,7 @@ package org.schabi.newpipe.database.playlist.model; import androidx.room.ColumnInfo; import androidx.room.Entity; +import androidx.room.Ignore; import androidx.room.Index; import androidx.room.PrimaryKey; @@ -28,11 +29,12 @@ public class PlaylistEntity { private String thumbnailUrl; @ColumnInfo(name = PLAYLIST_DISPLAY_INDEX) - private long displayIndex = 0; + private long displayIndex; - public PlaylistEntity(final String name, final String thumbnailUrl) { + public PlaylistEntity(final String name, final String thumbnailUrl, final long displayIndex) { this.name = name; this.thumbnailUrl = thumbnailUrl; + this.displayIndex = displayIndex; } public long getUid() { 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 05e2fdac0..e2bfd5977 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -142,6 +142,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter subscriptions) { + + // If displayIndex does not match actual index, update displayIndex. + // This may happen when a new list is created + // or on the first run after database update + // or displayIndex is not continuous for some reason. + checkDisplayIndexUpdate(subscriptions); + handleResult(subscriptions); if (databaseSubscription != null) { databaseSubscription.request(1); @@ -212,7 +219,8 @@ public final class BookmarkFragment extends BaseLocalListFragment { /*Do nothing on success*/ }, throwable -> showError( + new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, + "Changing local playlist display_index"))); + disposables.add(disposable); + } + + private void changeRemotePlaylistDisplayIndex(final long id, final long displayIndex) { + + if (remotePlaylistManager == null) { + return; + } + + if (DEBUG) { + Log.d(TAG, "Updating remote playlist id=[" + id + "] " + + "with new display_index=[" + displayIndex + "]"); + } + + final Disposable disposable = + remotePlaylistManager.changePlaylistDisplayIndex(id, displayIndex) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(longs -> { /*Do nothing on success*/ }, throwable -> showError( + new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, + "Changing remote playlist display_index"))); + disposables.add(disposable); + } + + private void checkDisplayIndexUpdate(@NonNull final List result) { + for (int i = 0; i < result.size(); i++) { + final PlaylistLocalItem item = result.get(i); + if (item.getDisplayIndex() != i) { + if (item instanceof PlaylistMetadataEntry) { + changeLocalPlaylistDisplayIndex(((PlaylistMetadataEntry) item).uid, i); + } else if (item instanceof PlaylistRemoteEntity) { + changeRemotePlaylistDisplayIndex(((PlaylistRemoteEntity) item).getUid(), i); + } + } + } + } } 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 aabda1bf0..47817f9e4 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 @@ -40,8 +40,11 @@ public class LocalPlaylistManager { return Maybe.empty(); } final StreamEntity defaultStream = streams.get(0); + + // Make sure the new playlist is always on the top of bookmark. + // The index will be reassigned to non-negative number in BookmarkFragment. final PlaylistEntity newPlaylist = - new PlaylistEntity(name, defaultStream.getThumbnailUrl()); + new PlaylistEntity(name, defaultStream.getThumbnailUrl(), -1); return Maybe.fromCallable(() -> database.runInTransaction(() -> upsertStreams(playlistTable.insert(newPlaylist), streams, 0)) 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..b49f149d6 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 @@ -8,6 +8,7 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import java.util.List; import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Maybe; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -33,6 +34,20 @@ public class RemotePlaylistManager { .subscribeOn(Schedulers.io()); } + public Maybe changePlaylistDisplayIndex(final long playlistId, + final long displayIndex) { + return playlistRemoteTable.getPlaylist(playlistId) + .firstElement() + .filter(playlistRemoteEntities -> !playlistRemoteEntities.isEmpty()) + .map(playlistRemoteEntities -> { + final PlaylistRemoteEntity playlist = playlistRemoteEntities.get(0); + if (displayIndex != -1) { + playlist.setDisplayIndex(displayIndex); + } + return playlistRemoteTable.update(playlist); + }).subscribeOn(Schedulers.io()); + } + public Single onBookmark(final PlaylistInfo playlistInfo) { return Single.fromCallable(() -> { final PlaylistRemoteEntity playlist = new PlaylistRemoteEntity(playlistInfo); From bfb56b4144f1fec9680dbafd5fe79420d731e0a7 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Thu, 14 Apr 2022 16:59:52 +0800 Subject: [PATCH 005/123] UI design and behavior --- .../playlist/model/PlaylistEntity.java | 1 - .../local/bookmark/BookmarkFragment.java | 207 +++++++++++++----- .../local/holder/LocalPlaylistItemHolder.java | 21 +- .../holder/RemotePlaylistItemHolder.java | 22 +- .../layout/list_playlist_bookmark_item.xml | 84 +++++++ 5 files changed, 282 insertions(+), 53 deletions(-) create mode 100644 app/src/main/res/layout/list_playlist_bookmark_item.xml 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 82f697ab3..cdbbdebc0 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,7 +2,6 @@ package org.schabi.newpipe.database.playlist.model; import androidx.room.ColumnInfo; import androidx.room.Entity; -import androidx.room.Ignore; import androidx.room.Index; import androidx.room.PrimaryKey; diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 6d2fbfbfc..63c63c1e0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.local.bookmark; +import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; + import android.os.Bundle; import android.os.Parcelable; import android.text.InputType; @@ -12,6 +14,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.RecyclerView; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -41,6 +45,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; public final class BookmarkFragment extends BaseLocalListFragment, Void> { + private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State protected Parcelable itemsListState; @@ -48,6 +53,7 @@ public final class BookmarkFragment extends BaseLocalListFragment() { @Override public void selected(final LocalItem selectedItem) { @@ -126,6 +135,14 @@ public final class BookmarkFragment extends BaseLocalListFragment - changeLocalPlaylistName( - selectedItem.uid, - dialogBinding.dialogEditText.getText().toString())) - .setNegativeButton(R.string.cancel, null) - .setNeutralButton(R.string.delete, (dialog, which) -> { - showDeleteDialog(selectedItem.name, - localPlaylistManager.deletePlaylist(selectedItem.uid)); - dialog.dismiss(); - }) - .create() - .show(); - } - - private void showDeleteDialog(final String name, final Single deleteReactor) { - if (activity == null || disposables == null) { - return; - } - - new AlertDialog.Builder(activity) - .setTitle(name) - .setMessage(R.string.delete_playlist_prompt) - .setCancelable(true) - .setPositiveButton(R.string.delete, (dialog, i) -> - disposables.add(deleteReactor - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(ignored -> { /*Do nothing on success*/ }, throwable -> - showError(new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Deleting playlist"))))) - .setNegativeButton(R.string.cancel, null) - .show(); - } + /*////////////////////////////////////////////////////////////////////////// + // Playlist Metadata Manipulation + //////////////////////////////////////////////////////////////////////////*/ private void changeLocalPlaylistName(final long id, final String name) { if (localPlaylistManager == null) { @@ -379,5 +350,141 @@ public final class BookmarkFragment extends BaseLocalListFragment items = itemListAdapter.getItemsList(); + for (int i = 0; i < items.size(); i++) { + final LocalItem item = items.get(i); + if (item instanceof PlaylistMetadataEntry) { + changeLocalPlaylistDisplayIndex(((PlaylistMetadataEntry) item).uid, i); + } else if (item instanceof PlaylistRemoteEntity) { + changeLocalPlaylistDisplayIndex(((PlaylistRemoteEntity) item).getUid(), i); + } + } + } + + private ItemTouchHelper.SimpleCallback getItemTouchCallback() { + int directions = ItemTouchHelper.UP | ItemTouchHelper.DOWN; + if (shouldUseGridLayout(requireContext())) { + directions |= ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; + } + return new ItemTouchHelper.SimpleCallback(directions, + 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) { + if (source.getItemViewType() != target.getItemViewType() + || itemListAdapter == null) { + return false; + } + + // todo: is it correct + final int sourceIndex = source.getBindingAdapterPosition(); + final int targetIndex = target.getBindingAdapterPosition(); + final boolean isSwapped = itemListAdapter.swapItems(sourceIndex, targetIndex); + if (isSwapped) { + // todo + //saveChanges(); + saveImmediate(); + } + 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) { + } + }; + } + + /////////////////////////////////////////////////////////////////////////// + // Utils + /////////////////////////////////////////////////////////////////////////// + + private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) { + showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid())); + } + + private void showLocalDialog(final PlaylistMetadataEntry selectedItem) { + final DialogEditTextBinding dialogBinding + = DialogEditTextBinding.inflate(getLayoutInflater()); + dialogBinding.dialogEditText.setHint(R.string.name); + dialogBinding.dialogEditText.setInputType(InputType.TYPE_CLASS_TEXT); + dialogBinding.dialogEditText.setText(selectedItem.name); + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setView(dialogBinding.getRoot()) + .setPositiveButton(R.string.rename_playlist, (dialog, which) -> + changeLocalPlaylistName( + selectedItem.uid, + dialogBinding.dialogEditText.getText().toString())) + .setNegativeButton(R.string.cancel, null) + .setNeutralButton(R.string.delete, (dialog, which) -> { + showDeleteDialog(selectedItem.name, + localPlaylistManager.deletePlaylist(selectedItem.uid)); + dialog.dismiss(); + }) + .create() + .show(); + } + + private void showDeleteDialog(final String name, final Single deleteReactor) { + if (activity == null || disposables == null) { + return; + } + + new AlertDialog.Builder(activity) + .setTitle(name) + .setMessage(R.string.delete_playlist_prompt) + .setCancelable(true) + .setPositiveButton(R.string.delete, (dialog, i) -> + disposables.add(deleteReactor + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(ignored -> { /*Do nothing on success*/ }, throwable -> + showError(new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, + "Deleting playlist"))))) + .setNegativeButton(R.string.cancel, null) + .show(); + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index f8c5176ec..57a944709 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -1,8 +1,10 @@ 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; @@ -13,13 +15,16 @@ import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; public class LocalPlaylistItemHolder extends PlaylistItemHolder { + private final View itemHandleView; + public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { - super(infoItemBuilder, parent); + this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); } LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { super(infoItemBuilder, layoutId, parent); + itemHandleView = itemView.findViewById(R.id.itemHandle); } @Override @@ -38,6 +43,20 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView); + 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, + LocalPlaylistItemHolder.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 440353ac7..9ecfa6979 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 @@ -1,8 +1,11 @@ package org.schabi.newpipe.local.holder; import android.text.TextUtils; +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.extractor.NewPipe; @@ -14,14 +17,17 @@ import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; public class RemotePlaylistItemHolder extends PlaylistItemHolder { + private final View itemHandleView; + public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { - super(infoItemBuilder, parent); + this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); } RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { super(infoItemBuilder, layoutId, parent); + itemHandleView = itemView.findViewById(R.id.itemHandle); } @Override @@ -46,6 +52,20 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); + 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, + RemotePlaylistItemHolder.this); + } + return false; + }; + } } 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..642ea2949 --- /dev/null +++ b/app/src/main/res/layout/list_playlist_bookmark_item.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + From 3c4882569931f91de0fc5dd0a980739781d6526c Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Fri, 15 Apr 2022 20:44:54 +0800 Subject: [PATCH 006/123] Debounced saver & bugfix & clean code --- .../database/playlist/PlaylistLocalItem.java | 4 + .../playlist/PlaylistMetadataEntry.java | 2 +- .../database/playlist/dao/PlaylistDAO.java | 14 + .../playlist/dao/PlaylistStreamDAO.java | 14 +- .../playlist/model/PlaylistEntity.java | 11 + .../playlist/model/PlaylistRemoteEntity.java | 2 +- .../newpipe/local/LocalItemListAdapter.java | 1 - .../local/bookmark/BookmarkFragment.java | 286 +++++++++++++----- .../local/dialog/PlaylistAppendDialog.java | 2 +- .../local/playlist/LocalPlaylistManager.java | 24 +- .../local/playlist/RemotePlaylistManager.java | 26 +- 11 files changed, 288 insertions(+), 98 deletions(-) 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 5bf50cd97..47c6dd617 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 @@ -45,6 +45,10 @@ public interface PlaylistLocalItem extends LocalItem { addItem(result, localPlaylists.get(i), itemsWithSameIndex); i++; } + while (j < remotePlaylists.size()) { + addItem(result, remotePlaylists.get(j), itemsWithSameIndex); + j++; + } addItemsWithSameIndex(result, itemsWithSameIndex); return result; 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 f54ffff13..ff80049a3 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 @@ -17,7 +17,7 @@ public class PlaylistMetadataEntry implements PlaylistLocalItem { @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) public final String thumbnailUrl; @ColumnInfo(name = PLAYLIST_DISPLAY_INDEX) - public final long displayIndex; + public long displayIndex; @ColumnInfo(name = PLAYLIST_STREAM_COUNT) public final long streamCount; 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/PlaylistStreamDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java index 3fb96a21f..0fce984f3 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 @@ -82,7 +82,19 @@ public interface PlaylistStreamDAO extends BasicDAO { + " FROM " + PLAYLIST_TABLE + " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE + " ON " + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID - + " GROUP BY " + JOIN_PLAYLIST_ID + + " GROUP BY " + PLAYLIST_ID + " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC") Flowable> getPlaylistMetadata(); + + @Transaction + @Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + ", " + PLAYLIST_THUMBNAIL_URL + ", " + + PLAYLIST_DISPLAY_INDEX + ", " + + "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT + + + " FROM " + PLAYLIST_TABLE + + " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE + + " ON " + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID + + " GROUP BY " + PLAYLIST_ID + + " ORDER BY " + PLAYLIST_DISPLAY_INDEX) + Flowable> getDisplayIndexOrderedPlaylistMetadata(); } 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 cdbbdebc0..272e8a5bc 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,12 +2,15 @@ package org.schabi.newpipe.database.playlist.model; import androidx.room.ColumnInfo; import androidx.room.Entity; +import androidx.room.Ignore; import androidx.room.Index; 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.database.playlist.PlaylistMetadataEntry; + @Entity(tableName = PLAYLIST_TABLE, indices = {@Index(value = {PLAYLIST_NAME})}) public class PlaylistEntity { @@ -36,6 +39,14 @@ public class PlaylistEntity { this.displayIndex = displayIndex; } + @Ignore + public PlaylistEntity(final PlaylistMetadataEntry item) { + this.uid = item.uid; + this.name = item.name; + this.thumbnailUrl = item.thumbnailUrl; + this.displayIndex = item.displayIndex; + } + public long getUid() { return uid; } 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 454526769..adea2738b 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 @@ -54,7 +54,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { private String uploader; @ColumnInfo(name = REMOTE_PLAYLIST_DISPLAY_INDEX) - private long displayIndex; + private long displayIndex = -1; // Make sure the new item is on the top @ColumnInfo(name = REMOTE_PLAYLIST_STREAM_COUNT) private Long streamCount; 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 e2bfd5977..05e2fdac0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -142,7 +142,6 @@ public class LocalItemListAdapter extends RecyclerView.Adapter, Void> { + // todo: add to playlists, item handle should be invisible + + // Save the list 10s after the last change occurred + private static final long SAVE_DEBOUNCE_MILLIS = 10000; private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State protected Parcelable itemsListState; @@ -55,6 +67,16 @@ public final class BookmarkFragment extends BaseLocalListFragment debouncedSaveSignal; + + /* Has the playlist been fully loaded from db */ + private AtomicBoolean isLoadingComplete; + /* Has the playlist been modified (e.g. items reordered or deleted) */ + private AtomicBoolean isModified; + + // Map from (uid, local/remote item) to the saved display index in the database. + private Map, Long> displayIndexInDatabase; + /////////////////////////////////////////////////////////////////////////// // Fragment LifeCycle - Creation /////////////////////////////////////////////////////////////////////////// @@ -69,6 +91,12 @@ public final class BookmarkFragment extends BaseLocalListFragment(); } @Nullable @@ -154,6 +182,10 @@ public final class BookmarkFragment extends BaseLocalListFragment subscriptions) { - - // If displayIndex does not match actual index, update displayIndex. - // This may happen when a new list is created - // or on the first run after database update - // or displayIndex is not continuous for some reason. - checkDisplayIndexUpdate(subscriptions); - - handleResult(subscriptions); + if (isModified == null || !isModified.get()) { + checkDisplayIndexModified(subscriptions); + handleResult(subscriptions); + isLoadingComplete.set(true); + } if (databaseSubscription != null) { databaseSubscription.request(1); } @@ -296,86 +338,170 @@ public final class BookmarkFragment extends BaseLocalListFragment result) { + if (isModified != null && isModified.get()) { return; } - if (DEBUG) { - Log.d(TAG, "Updating local playlist id=[" + id + "] " - + "with new display_index=[" + displayIndex + "]"); - } + displayIndexInDatabase.clear(); - final Disposable disposable = - localPlaylistManager.changePlaylistDisplayIndex(id, displayIndex) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(longs -> { /*Do nothing on success*/ }, throwable -> showError( - new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Changing local playlist display_index"))); - disposables.add(disposable); - } - - private void changeRemotePlaylistDisplayIndex(final long id, final long displayIndex) { - - if (remotePlaylistManager == null) { - return; - } - - if (DEBUG) { - Log.d(TAG, "Updating remote playlist id=[" + id + "] " - + "with new display_index=[" + displayIndex + "]"); - } - - final Disposable disposable = - remotePlaylistManager.changePlaylistDisplayIndex(id, displayIndex) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(longs -> { /*Do nothing on success*/ }, throwable -> showError( - new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Changing remote playlist display_index"))); - disposables.add(disposable); - } - - private void checkDisplayIndexUpdate(@NonNull final List result) { + // If the display index does not match actual index in the list, update the display index. + // This may happen when a new list is created + // or on the first run after database update + // or displayIndex is not continuous for some reason. + boolean isDisplayIndexModified = false; for (int i = 0; i < result.size(); i++) { final PlaylistLocalItem item = result.get(i); if (item.getDisplayIndex() != i) { - if (item instanceof PlaylistMetadataEntry) { - changeLocalPlaylistDisplayIndex(((PlaylistMetadataEntry) item).uid, i); - } else if (item instanceof PlaylistRemoteEntity) { - changeRemotePlaylistDisplayIndex(((PlaylistRemoteEntity) item).getUid(), i); - } + isDisplayIndexModified = true; } + + // Updating display index in the item does not affect the value inserts into + // database, which will be recalculated during the database update. Updating + // display index in the item here is to determine whether it is recently modified. + // Save the index read from the database. + if (item instanceof PlaylistMetadataEntry) { + + displayIndexInDatabase.put(new Pair<>(((PlaylistMetadataEntry) item).uid, + LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM), item.getDisplayIndex()); + ((PlaylistMetadataEntry) item).displayIndex = i; + + } else if (item instanceof PlaylistRemoteEntity) { + + displayIndexInDatabase.put(new Pair<>(((PlaylistRemoteEntity) item).getUid(), + LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM), + item.getDisplayIndex()); + ((PlaylistRemoteEntity) item).setDisplayIndex(i); + + } + } + + if (isDisplayIndexModified) { + saveChanges(); } } - private void saveImmediate() { - if (localPlaylistManager == null || remotePlaylistManager == null - || itemListAdapter == null) { + private void saveChanges() { + if (isModified == null || debouncedSaveSignal == null) { return; } - // todo: debounce - /* + + isModified.set(true); + debouncedSaveSignal.onNext(System.currentTimeMillis()); + } + + private Disposable getDebouncedSaver() { + if (debouncedSaveSignal == null) { + return Disposable.empty(); + } + + return debouncedSaveSignal + .debounce(SAVE_DEBOUNCE_MILLIS, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(ignored -> saveImmediate(), throwable -> + showError(new ErrorInfo(throwable, UserAction.SOMETHING_ELSE, + "Debounced saver"))); + } + + private void saveImmediate() { + if (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 + "]"); + Log.w(TAG, "Attempting to save playlists in bookmark when bookmark " + + "is not loaded or playlists not modified"); return; } - */ - // todo: is it correct? + 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) { - changeLocalPlaylistDisplayIndex(((PlaylistMetadataEntry) item).uid, i); + ((PlaylistMetadataEntry) item).displayIndex = i; + + final Long uid = ((PlaylistMetadataEntry) item).uid; + final Pair key = new Pair<>(uid, + LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM); + final Long databaseIndex = displayIndexInDatabase.remove(key); + + if (databaseIndex != null) { + if (databaseIndex != i) { + localItemsUpdate.add((PlaylistMetadataEntry) item); + } + } else { + // This should be impossible. + continue; + } } else if (item instanceof PlaylistRemoteEntity) { - changeLocalPlaylistDisplayIndex(((PlaylistRemoteEntity) item).getUid(), i); + ((PlaylistRemoteEntity) item).setDisplayIndex(i); + + final Long uid = ((PlaylistRemoteEntity) item).getUid(); + final Pair key = new Pair<>(uid, + LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM); + final Long databaseIndex = displayIndexInDatabase.remove(key); + + if (databaseIndex != null) { + if (databaseIndex != i) { + remoteItemsUpdate.add((PlaylistRemoteEntity) item); + } + } else { + // This should be impossible. + continue; + } } } + + // Find deleted items + for (final Pair key : displayIndexInDatabase.keySet()) { + if (key.second.equals(LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM)) { + localItemsDeleteUid.add(key.first); + } else if (key.second.equals(LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM)) { + remoteItemsDeleteUid.add(key.first); + } + } + + displayIndexInDatabase.clear(); + + // 1. Update local playlists + // 2. Update remote playlists + // 3. Set isModified false + disposables.add(localPlaylistManager.updatePlaylists(localItemsUpdate, localItemsDeleteUid) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> disposables.add(remotePlaylistManager.updatePlaylists( + remoteItemsUpdate, remoteItemsDeleteUid) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> { + if (isModified != null) { + isModified.set(false); + } + }, + throwable -> showError(new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, + "Saving playlist")) + )), + throwable -> showError(new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, "Saving playlist")) + )); + } private ItemTouchHelper.SimpleCallback getItemTouchCallback() { @@ -404,17 +530,26 @@ public final class BookmarkFragment extends BaseLocalListFragment { - showDeleteDialog(selectedItem.name, - localPlaylistManager.deletePlaylist(selectedItem.uid)); + showDeleteDialog(selectedItem.name, selectedItem); dialog.dismiss(); }) .create() .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; } @@ -476,13 +610,7 @@ 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(); } 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 a874cdd62..58a10af22 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 @@ -85,7 +85,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog { final View newPlaylistButton = view.findViewById(R.id.newPlaylist); newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog()); - playlistDisposables.add(playlistManager.getPlaylists() + playlistDisposables.add(playlistManager.getDisplayIndexOrderedPlaylists() .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::onPlaylistsReceived)); } 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 47817f9e4..c68a22b01 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 @@ -41,6 +41,7 @@ public class LocalPlaylistManager { } final StreamEntity defaultStream = streams.get(0); + // 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. final PlaylistEntity newPlaylist = @@ -85,10 +86,31 @@ public class LocalPlaylistManager { })).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> getPlaylists() { return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io()); } + public Flowable> getDisplayIndexOrderedPlaylists() { + return playlistStreamTable.getDisplayIndexOrderedPlaylistMetadata() + .subscribeOn(Schedulers.io()); + } + public Flowable> getPlaylistStreams(final long playlistId) { return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io()); } @@ -107,7 +129,7 @@ public class LocalPlaylistManager { return modifyPlaylist(playlistId, null, thumbnailUrl, -1); } - public Maybe changePlaylistDisplayIndex(final long playlistId, + public Maybe updatePlaylistDisplayIndex(final long playlistId, final long displayIndex) { return modifyPlaylist(playlistId, null, null, displayIndex); } 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 b49f149d6..1dbd726ae 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,16 +7,18 @@ 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.Maybe; 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(); } @@ -34,18 +36,16 @@ public class RemotePlaylistManager { .subscribeOn(Schedulers.io()); } - public Maybe changePlaylistDisplayIndex(final long playlistId, - final long displayIndex) { - return playlistRemoteTable.getPlaylist(playlistId) - .firstElement() - .filter(playlistRemoteEntities -> !playlistRemoteEntities.isEmpty()) - .map(playlistRemoteEntities -> { - final PlaylistRemoteEntity playlist = playlistRemoteEntities.get(0); - if (displayIndex != -1) { - playlist.setDisplayIndex(displayIndex); - } - return playlistRemoteTable.update(playlist); - }).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) { From 0aa08a5e4049952bc830522b3053d347199ee3f7 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Fri, 15 Apr 2022 23:19:24 +0800 Subject: [PATCH 007/123] Use new item holder --- .../newpipe/local/LocalItemListAdapter.java | 21 ++++-- .../local/bookmark/BookmarkFragment.java | 18 ++--- .../LocalBookmarkPlaylistItemHolder.java | 63 ++++++++++++++++ .../local/holder/LocalPlaylistItemHolder.java | 19 +---- .../RemoteBookmarkPlaylistItemHolder.java | 71 +++++++++++++++++++ .../holder/RemotePlaylistItemHolder.java | 20 +----- 6 files changed, 163 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java create mode 100644 app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.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 05e2fdac0..161d35ee5 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.model.StreamStateEntity; 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.LocalPlaylistGridItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder; @@ -20,6 +21,7 @@ import org.schabi.newpipe.local.holder.LocalPlaylistStreamGridItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder; 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.RemotePlaylistGridItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder; import org.schabi.newpipe.util.FallbackViewHolder; @@ -66,6 +68,8 @@ public class LocalItemListAdapter extends RecyclerView.Adapter localItems; @@ -74,6 +78,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter, Void> { - // todo: add to playlists, item handle should be invisible // Save the list 10s after the last change occurred private static final long SAVE_DEBOUNCE_MILLIS = 10000; @@ -126,6 +125,8 @@ public final class BookmarkFragment extends BaseLocalListFragment { + 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/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index 57a944709..2cfc94463 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.local.holder; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -15,16 +14,14 @@ import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; public class LocalPlaylistItemHolder extends PlaylistItemHolder { - private final View itemHandleView; public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { - this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); + this(infoItemBuilder, R.layout.list_playlist_mini_item, parent); } LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { super(infoItemBuilder, layoutId, parent); - itemHandleView = itemView.findViewById(R.id.itemHandle); } @Override @@ -43,20 +40,6 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView); - 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, - LocalPlaylistItemHolder.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..345223b08 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java @@ -0,0 +1,71 @@ +package org.schabi.newpipe.local.holder; + +import android.text.TextUtils; +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.extractor.NewPipe; +import org.schabi.newpipe.local.LocalItemBuilder; +import org.schabi.newpipe.local.history.HistoryRecordManager; +import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.PicassoHelper; + +import java.time.format.DateTimeFormatter; + +public class RemoteBookmarkPlaylistItemHolder extends PlaylistItemHolder { + 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; + + itemTitleView.setText(item.getName()); + itemStreamCountView.setText(Localization.localizeStreamCountMini( + itemStreamCountView.getContext(), item.getStreamCount())); + // Here is where the uploader name is set in the bookmarked playlists library + if (!TextUtils.isEmpty(item.getUploader())) { + itemUploaderView.setText(Localization.concatenateStrings(item.getUploader(), + NewPipe.getNameOfService(item.getServiceId()))); + } else { + itemUploaderView.setText(NewPipe.getNameOfService(item.getServiceId())); + } + + PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); + + 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 9ecfa6979..d2059bfed 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 @@ -1,8 +1,6 @@ package org.schabi.newpipe.local.holder; import android.text.TextUtils; -import android.view.MotionEvent; -import android.view.View; import android.view.ViewGroup; import org.schabi.newpipe.R; @@ -17,17 +15,15 @@ import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; public class RemotePlaylistItemHolder extends PlaylistItemHolder { - private final View itemHandleView; public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { - this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); + this(infoItemBuilder, R.layout.list_playlist_mini_item, parent); } RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { super(infoItemBuilder, layoutId, parent); - itemHandleView = itemView.findViewById(R.id.itemHandle); } @Override @@ -52,20 +48,6 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); - 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, - RemotePlaylistItemHolder.this); - } - return false; - }; - } } From c24aed054f9da7dcde634faf570f0314d44f6a87 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Sat, 16 Apr 2022 12:00:02 +0800 Subject: [PATCH 008/123] Fix sonar warning and typo --- .../schabi/newpipe/database/Migrations.java | 16 ++--- .../newpipe/local/LocalItemListAdapter.java | 15 +++-- .../local/bookmark/BookmarkFragment.java | 64 +++++++++---------- .../LocalBookmarkPlaylistItemHolder.java | 11 +--- .../local/holder/LocalPlaylistItemHolder.java | 3 +- .../RemoteBookmarkPlaylistItemHolder.java | 19 +----- .../holder/RemotePlaylistItemHolder.java | 3 +- 7 files changed, 53 insertions(+), 78 deletions(-) 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 ffca6cca5..1013899ca 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -195,8 +195,8 @@ public final class Migrations { try { database.beginTransaction(); - // update playlists - // create a temp table to initialize display_index + // 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, `thumbnail_url` TEXT," @@ -204,16 +204,16 @@ public final class Migrations { database.execSQL("INSERT INTO `playlists_tmp` (`uid`, `name`, `thumbnail_url`)" + "SELECT `uid`, `name`, `thumbnail_url` FROM `playlists`"); - // replace the old table + // Replace the old table. database.execSQL("DROP TABLE `playlists`"); database.execSQL("ALTER TABLE `playlists_tmp` RENAME TO `playlists`"); - // create index on the new table + // Create index on the new table. database.execSQL("CREATE INDEX `index_playlists_name` ON `playlists` (`name`)"); - // update remote_playlists - // create a temp table to initialize display_index + // 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, " @@ -225,11 +225,11 @@ public final class Migrations { + "SELECT `uid`, `service_id`, `name`, `url`, `thumbnail_url`, `uploader`, " + "`stream_count` FROM `remote_playlists`"); - // replace the old table + // Replace the old table. database.execSQL("DROP TABLE `remote_playlists`"); database.execSQL("ALTER TABLE `remote_playlists_tmp` RENAME TO `remote_playlists`"); - // create index on the new table + // Create index on the new table. database.execSQL("CREATE INDEX `index_remote_playlists_name` " + "ON `remote_playlists` (`name`)"); database.execSQL("CREATE UNIQUE INDEX `index_remote_playlists_service_id_url` " 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 161d35ee5..5c22cee24 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -256,12 +256,17 @@ public class LocalItemListAdapter extends RecyclerView.Adapter, Void> { - // Save the list 10s after the last change occurred + // Save the list 10 seconds after the last change occurred private static final long SAVE_DEBOUNCE_MILLIS = 10000; private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State @@ -281,6 +281,7 @@ public final class BookmarkFragment extends BaseLocalListFragment disposables.add(remotePlaylistManager.updatePlaylists( remoteItemsUpdate, remoteItemsDeleteUid) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> { - if (isModified != null) { - isModified.set(false); - } - }, - throwable -> showError(new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Saving playlist")) - )), + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> { + if (isModified != null) { + isModified.set(false); + } + }, + throwable -> showError(new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, + "Saving playlist")) + )), throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK, "Saving playlist")) )); @@ -529,22 +527,21 @@ public final class BookmarkFragment extends BaseLocalListFragment Date: Sat, 16 Apr 2022 12:44:24 +0800 Subject: [PATCH 009/123] Fix sonar warning --- .../newpipe/local/bookmark/BookmarkFragment.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 14100ef81..b19107817 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -446,12 +446,10 @@ public final class BookmarkFragment extends BaseLocalListFragment Date: Sun, 17 Apr 2022 14:53:02 +0800 Subject: [PATCH 010/123] Reuse DebounceSaver --- .../database/playlist/PlaylistLocalItem.java | 8 +- .../local/bookmark/BookmarkFragment.java | 81 +++++++------------ .../local/playlist/LocalPlaylistFragment.java | 73 ++++++----------- .../schabi/newpipe/util/DebounceSavable.java | 15 ++++ .../schabi/newpipe/util/DebounceSaver.java | 68 ++++++++++++++++ 5 files changed, 142 insertions(+), 103 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/DebounceSavable.java create mode 100644 app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java 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 47c6dd617..0e7beba41 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 @@ -17,15 +17,15 @@ public interface PlaylistLocalItem extends LocalItem { final List localPlaylists, final List remotePlaylists) { - // Merge localPlaylists and remotePlaylists by displayIndex. - // If two items have the same displayIndex, sort them in CASE_INSENSITIVE_ORDER. + // Merge localPlaylists and remotePlaylists by display index. + // If two items have the same display index, sort them in CASE_INSENSITIVE_ORDER. // 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<>(); - // The data from database may not be in the displayIndex order + // The data from database may not be in the display index order Collections.sort(localPlaylists, Comparator.comparingLong(PlaylistMetadataEntry::getDisplayIndex)); Collections.sort(remotePlaylists, @@ -58,7 +58,7 @@ public interface PlaylistLocalItem extends LocalItem { final List itemsWithSameIndex) { if (!itemsWithSameIndex.isEmpty() && itemsWithSameIndex.get(0).getDisplayIndex() != item.getDisplayIndex()) { - // The new item has a different displayIndex, add previous items with same + // The new item has a different display index, add previous items with same // index to the result. addItemsWithSameIndex(result, itemsWithSameIndex); itemsWithSameIndex.clear(); diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index b19107817..ceeb980c7 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -35,6 +35,8 @@ import org.schabi.newpipe.local.holder.LocalBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.holder.RemoteBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; +import org.schabi.newpipe.util.DebounceSavable; +import org.schabi.newpipe.util.DebounceSaver; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; @@ -42,7 +44,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import icepick.State; @@ -50,12 +51,10 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.subjects.PublishSubject; -public final class BookmarkFragment extends BaseLocalListFragment, Void> { +public final class BookmarkFragment extends BaseLocalListFragment, Void> + implements DebounceSavable { - // Save the list 10 seconds after the last change occurred - private static final long SAVE_DEBOUNCE_MILLIS = 10000; private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State protected Parcelable itemsListState; @@ -66,12 +65,10 @@ public final class BookmarkFragment extends BaseLocalListFragment debouncedSaveSignal; - - /* Has the playlist been fully loaded from db */ + /* Have the bookmarked playlists been fully loaded from db */ private AtomicBoolean isLoadingComplete; - /* Has the playlist been modified (e.g. items reordered or deleted) */ - private AtomicBoolean isModified; + + private DebounceSaver debounceSaver; // Map from (uid, local/remote item) to the saved display index in the database. private Map, Long> displayIndexInDatabase; @@ -91,9 +88,8 @@ public final class BookmarkFragment extends BaseLocalListFragment(); } @@ -183,9 +179,11 @@ public final class BookmarkFragment extends BaseLocalListFragment subscriptions) { - if (isModified == null || !isModified.get()) { + if (debounceSaver == null || !debounceSaver.getIsModified()) { checkDisplayIndexModified(subscriptions); handleResult(subscriptions); isLoadingComplete.set(true); @@ -346,11 +343,11 @@ public final class BookmarkFragment extends BaseLocalListFragment result) { - if (isModified != null && isModified.get()) { + if (debounceSaver != null && debounceSaver.getIsModified()) { return; } @@ -358,8 +355,9 @@ public final class BookmarkFragment extends BaseLocalListFragment saveImmediate(), throwable -> - showError(new ErrorInfo(throwable, UserAction.SOMETHING_ELSE, - "Debounced saver"))); - } - - private void saveImmediate() { + @Override + public void saveImmediate() { if (itemListAdapter == null) { return; } // List must be loaded and modified in order to save - if (isLoadingComplete == null || isModified == null - || !isLoadingComplete.get() || !isModified.get()) { + if (isLoadingComplete == null || debounceSaver == null + || !isLoadingComplete.get() || !debounceSaver.getIsModified()) { Log.w(TAG, "Attempting to save playlists in bookmark when bookmark " + "is not loaded or playlists not modified"); return; @@ -485,8 +462,8 @@ public final class BookmarkFragment extends BaseLocalListFragment { - if (isModified != null) { - isModified.set(false); + if (debounceSaver != null) { + debounceSaver.setIsModified(false); } }, throwable -> showError(new ErrorInfo(throwable, @@ -544,7 +521,7 @@ public final class BookmarkFragment extends BaseLocalListFragment, Void> { - // Save the list 10 seconds after the last change occurred - private static final long SAVE_DEBOUNCE_MILLIS = 10000; +public class LocalPlaylistFragment extends BaseLocalListFragment, Void> + implements DebounceSavable { private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State protected Long playlistId; @@ -85,13 +84,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment debouncedSaveSignal; private CompositeDisposable disposables; /* Has the playlist been fully loaded from db */ private AtomicBoolean isLoadingComplete; - /* Has the playlist been modified (e.g. items reordered or deleted) */ - private AtomicBoolean isModified; + + private DebounceSaver debounceSaver; + /* Is the playlist currently being processed to remove watched videos */ private boolean isRemovingWatched = false; @@ -109,12 +108,11 @@ 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); } @@ -441,7 +441,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment saveImmediate(), throwable -> - showError(new ErrorInfo(throwable, UserAction.SOMETHING_ELSE, - "Debounced saver"))); - } - - private void saveImmediate() { + @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()) { + if (isLoadingComplete == null || debounceSaver == null + || !isLoadingComplete.get() || !debounceSaver.getIsModified()) { Log.w(TAG, "Attempting to save playlist when local playlist " + "is not loaded or not modified: playlist id=[" + playlistId + "]"); return; @@ -664,8 +643,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment { - if (isModified != null) { - isModified.set(false); + if (debounceSaver != null) { + debounceSaver.setIsModified(false); } }, throwable -> showError(new ErrorInfo(throwable, @@ -708,7 +687,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment + * 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/DebounceSaver.java b/app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java new file mode 100644 index 000000000..b17d7a29c --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java @@ -0,0 +1,68 @@ +package org.schabi.newpipe.util; + +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; + + + /** + * 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(); + } + + public boolean getIsModified() { + return isModified.get(); + } + + public void setIsModified(final boolean isModified) { + this.isModified.set(isModified); + } + + 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 saveChanges() { + if (isModified == null || debouncedSaveSignal == null) { + return; + } + + isModified.set(true); + debouncedSaveSignal.onNext(System.currentTimeMillis()); + } +} From 6526ff1612ad430325a5bf8af7b425055306a0e6 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Sun, 17 Apr 2022 20:20:20 +0800 Subject: [PATCH 011/123] Add tests --- .../newpipe/database/DatabaseMigrationTest.kt | 92 ++++++++++++++- .../local/bookmark/BookmarkFragment.java | 3 +- .../playlist/PlaylistLocalItemTest.java | 105 ++++++++++++++++++ 3 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java 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 6d05a45bf..73b6313db 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -13,6 +13,8 @@ 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.stream.StreamType @RunWith(AndroidJUnit4::class) @@ -21,13 +23,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 @@ -123,6 +129,90 @@ class DatabaseMigrationTest { assertNull(secondStreamFromMigratedDatabase.isUploadDateApproximation) } + @Test + fun migrateDatabaseFrom5to6() { + val databaseInV5 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_5) + + val localUid1: Long + val localUid2: Long + val remoteUid1: Long + val remoteUid2: Long + databaseInV5.run { + localUid1 = insert( + "playlists", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("name", DEFAULT_NAME + "1") + put("thumbnail_url", DEFAULT_THUMBNAIL) + } + ) + localUid2 = insert( + "playlists", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("name", DEFAULT_NAME + "2") + put("thumbnail_url", DEFAULT_THUMBNAIL) + } + ) + 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_6, + true, Migrations.MIGRATION_5_6 + ) + + val migratedDatabaseV6 = getMigratedDatabase() + var localListFromDB = migratedDatabaseV6.playlistDAO().all.blockingFirst() + var remoteListFromDB = migratedDatabaseV6.playlistRemoteDAO().all.blockingFirst() + + assertEquals(1, localListFromDB.size) + assertEquals(localUid2, localListFromDB[0].uid) + assertEquals(0, localListFromDB[0].displayIndex) + assertEquals(1, remoteListFromDB.size) + assertEquals(remoteUid1, remoteListFromDB[0].uid) + assertEquals(0, remoteListFromDB[0].displayIndex) + + val localUid3 = migratedDatabaseV6.playlistDAO().insert( + PlaylistEntity(DEFAULT_NAME + "3", DEFAULT_THUMBNAIL, -1) + ) + val remoteUid3 = migratedDatabaseV6.playlistRemoteDAO().insert( + PlaylistRemoteEntity( + DEFAULT_THIRD_SERVICE_ID, DEFAULT_NAME, DEFAULT_THIRD_URL, + DEFAULT_THUMBNAIL, DEFAULT_UPLOADER_NAME, -1, 10 + ) + ) + + localListFromDB = migratedDatabaseV6.playlistDAO().all.blockingFirst() + remoteListFromDB = migratedDatabaseV6.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/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index ceeb980c7..a79719525 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -385,7 +385,7 @@ public final class BookmarkFragment extends BaseLocalListFragment localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + final List mergedPlaylists = + PlaylistLocalItem.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", "", 2, 1)); + localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 1, 1)); + localPlaylists.add(new PlaylistMetadataEntry(3, "name3", "", 0, 1)); + final List mergedPlaylists = + PlaylistLocalItem.merge(localPlaylists, remotePlaylists); + + assertEquals(3, mergedPlaylists.size()); + assertEquals(0, mergedPlaylists.get(0).getDisplayIndex()); + assertEquals(1, mergedPlaylists.get(1).getDisplayIndex()); + assertEquals(2, 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", "", "", 2, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 2, "name2", "url2", "", "", 1, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 3, "name3", "url3", "", "", 0, 1L)); + final List mergedPlaylists = + PlaylistLocalItem.merge(localPlaylists, remotePlaylists); + + assertEquals(3, mergedPlaylists.size()); + assertEquals(0, mergedPlaylists.get(0).getDisplayIndex()); + assertEquals(1, mergedPlaylists.get(1).getDisplayIndex()); + assertEquals(2, mergedPlaylists.get(2).getDisplayIndex()); + } + + @Test + public void sameIndexWithDifferentName() { + final List localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 0, 1)); + localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 1, 1)); + remotePlaylists.add(new PlaylistRemoteEntity( + 1, "name3", "url1", "", "", 0, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 2, "name4", "url2", "", "", 1, 1L)); + final List mergedPlaylists = + PlaylistLocalItem.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", "", 1, 1)); + localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 3, 1)); + remotePlaylists.add(new PlaylistRemoteEntity( + 1, "name1", "url1", "", "", 0, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 2, "name2", "url2", "", "", 2, 1L)); + final List mergedPlaylists = + PlaylistLocalItem.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); + } +} From d32490a4be288348a8377c84dbf8c82253240bf6 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Wed, 11 May 2022 16:47:34 +0800 Subject: [PATCH 012/123] Create sub-package and default interval for DebounceSaver & sort playlists in db --- .../database/playlist/PlaylistLocalItem.java | 6 +----- .../database/playlist/dao/PlaylistRemoteDAO.java | 5 +++++ .../newpipe/local/bookmark/BookmarkFragment.java | 10 +++++----- .../local/playlist/LocalPlaylistFragment.java | 6 +++--- .../local/playlist/RemotePlaylistManager.java | 4 ++++ .../newpipe/settings/SelectPlaylistFragment.java | 4 ++-- .../util/{ => debounce}/DebounceSavable.java | 2 +- .../util/{ => debounce}/DebounceSaver.java | 15 ++++++++++++++- 8 files changed, 35 insertions(+), 17 deletions(-) rename app/src/main/java/org/schabi/newpipe/util/{ => debounce}/DebounceSavable.java (89%) rename app/src/main/java/org/schabi/newpipe/util/{ => debounce}/DebounceSaver.java (81%) 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 0e7beba41..8b01a636a 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 @@ -16,6 +16,7 @@ public interface PlaylistLocalItem extends LocalItem { static List merge( final List localPlaylists, final List remotePlaylists) { + // The playlists from the database must be in the display index order. // Merge localPlaylists and remotePlaylists by display index. // If two items have the same display index, sort them in CASE_INSENSITIVE_ORDER. @@ -25,11 +26,6 @@ public interface PlaylistLocalItem extends LocalItem { localPlaylists.size() + remotePlaylists.size()); final List itemsWithSameIndex = new ArrayList<>(); - // The data from database may not be in the display index order - Collections.sort(localPlaylists, - Comparator.comparingLong(PlaylistMetadataEntry::getDisplayIndex)); - Collections.sort(remotePlaylists, - Comparator.comparingLong(PlaylistRemoteEntity::getDisplayIndex)); int i = 0; int j = 0; while (i < localPlaylists.size()) { 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 ade857464..8118bc40f 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; @@ -39,6 +40,10 @@ public interface PlaylistRemoteDAO extends BasicDAO { + 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> getDisplayIndexOrderedPlaylists(); + @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/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index a79719525..b0833dd9c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -35,8 +35,8 @@ import org.schabi.newpipe.local.holder.LocalBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.holder.RemoteBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; -import org.schabi.newpipe.util.DebounceSavable; -import org.schabi.newpipe.util.DebounceSaver; +import org.schabi.newpipe.util.debounce.DebounceSavable; +import org.schabi.newpipe.util.debounce.DebounceSaver; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; @@ -89,7 +89,7 @@ public final class BookmarkFragment extends BaseLocalListFragment(); } @@ -185,8 +185,8 @@ public final class BookmarkFragment extends BaseLocalListFragment> getDisplayIndexOrderedPlaylists() { + return playlistRemoteTable.getDisplayIndexOrderedPlaylists().subscribeOn(Schedulers.io()); + } + public Flowable> getPlaylist(final PlaylistInfo info) { return playlistRemoteTable.getPlaylist(info.getServiceId(), info.getUrl()) .subscribeOn(Schedulers.io()); 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 e8491d52c..cc47c3f1c 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -90,8 +90,8 @@ 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 = Flowable.combineLatest(localPlaylistManager.getDisplayIndexOrderedPlaylists(), + remotePlaylistManager.getDisplayIndexOrderedPlaylists(), PlaylistLocalItem::merge) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::displayPlaylists, this::onError); } diff --git a/app/src/main/java/org/schabi/newpipe/util/DebounceSavable.java b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSavable.java similarity index 89% rename from app/src/main/java/org/schabi/newpipe/util/DebounceSavable.java rename to app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSavable.java index 189dce9c6..acc515dd6 100644 --- a/app/src/main/java/org/schabi/newpipe/util/DebounceSavable.java +++ b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSavable.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe.util; +package org.schabi.newpipe.util.debounce; import org.schabi.newpipe.error.ErrorInfo; diff --git a/app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java similarity index 81% rename from app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java rename to app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java index b17d7a29c..367174ab7 100644 --- a/app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java +++ b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe.util; +package org.schabi.newpipe.util.debounce; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; @@ -21,6 +21,9 @@ public class DebounceSaver { // 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}. @@ -36,6 +39,16 @@ public class DebounceSaver { 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(); } From ba394a7ab4d3a0c1ee705565c8543e9160d9ebb5 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Wed, 11 May 2022 18:08:14 +0800 Subject: [PATCH 013/123] Update test and Javadoc --- .../database/playlist/PlaylistLocalItem.java | 27 ++++++++++-- .../playlist/PlaylistLocalItemTest.java | 41 +++++++++++++++---- 2 files changed, 56 insertions(+), 12 deletions(-) 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 8b01a636a..352d12d6b 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 @@ -13,13 +13,34 @@ public interface PlaylistLocalItem extends LocalItem { long getDisplayIndex(); + /** + * 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 in the display index order + * @param remotePlaylists remote playlists in the display index order + * @return merged playlists + */ static List merge( final List localPlaylists, final List remotePlaylists) { - // The playlists from the database must be in the display index order. - // Merge localPlaylists and remotePlaylists by display index. - // If two items have the same display index, sort them in CASE_INSENSITIVE_ORDER. + for (int i = 1; i < localPlaylists.size(); i++) { + if (localPlaylists.get(i).getDisplayIndex() + < localPlaylists.get(i - 1).getDisplayIndex()) { + throw new IllegalArgumentException( + "localPlaylists is not in the display index order"); + } + } + + for (int i = 1; i < remotePlaylists.size(); i++) { + if (remotePlaylists.get(i).getDisplayIndex() + < remotePlaylists.get(i - 1).getDisplayIndex()) { + throw new IllegalArgumentException( + "remotePlaylists is not in the display index order"); + } + } + // This algorithm is similar to the merge operation in merge sort. final List result = new ArrayList<>( diff --git a/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java b/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java index e5f717144..98f611037 100644 --- a/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java +++ b/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java @@ -24,16 +24,26 @@ public class PlaylistLocalItemTest { public void onlyLocalPlaylists() { final List localPlaylists = new ArrayList<>(); final List remotePlaylists = new ArrayList<>(); - localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 2, 1)); + localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 0, 1)); localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 1, 1)); - localPlaylists.add(new PlaylistMetadataEntry(3, "name3", "", 0, 1)); + localPlaylists.add(new PlaylistMetadataEntry(3, "name3", "", 3, 1)); final List mergedPlaylists = PlaylistLocalItem.merge(localPlaylists, remotePlaylists); assertEquals(3, mergedPlaylists.size()); assertEquals(0, mergedPlaylists.get(0).getDisplayIndex()); assertEquals(1, mergedPlaylists.get(1).getDisplayIndex()); - assertEquals(2, mergedPlaylists.get(2).getDisplayIndex()); + assertEquals(3, mergedPlaylists.get(2).getDisplayIndex()); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidLocalPlaylists() { + final List localPlaylists = new ArrayList<>(); + final List remotePlaylists = new ArrayList<>(); + localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 2, 1)); + localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 1, 1)); + localPlaylists.add(new PlaylistMetadataEntry(3, "name3", "", 0, 1)); + PlaylistLocalItem.merge(localPlaylists, remotePlaylists); } @Test @@ -41,18 +51,31 @@ public class PlaylistLocalItemTest { final List localPlaylists = new ArrayList<>(); final List remotePlaylists = new ArrayList<>(); remotePlaylists.add(new PlaylistRemoteEntity( - 1, "name1", "url1", "", "", 2, 1L)); + 1, "name1", "url1", "", "", 1, 1L)); remotePlaylists.add(new PlaylistRemoteEntity( - 2, "name2", "url2", "", "", 1, 1L)); + 2, "name2", "url2", "", "", 2, 1L)); remotePlaylists.add(new PlaylistRemoteEntity( - 3, "name3", "url3", "", "", 0, 1L)); + 3, "name3", "url3", "", "", 4, 1L)); final List mergedPlaylists = PlaylistLocalItem.merge(localPlaylists, remotePlaylists); assertEquals(3, mergedPlaylists.size()); - assertEquals(0, mergedPlaylists.get(0).getDisplayIndex()); - assertEquals(1, mergedPlaylists.get(1).getDisplayIndex()); - assertEquals(2, mergedPlaylists.get(2).getDisplayIndex()); + assertEquals(1, mergedPlaylists.get(0).getDisplayIndex()); + assertEquals(2, mergedPlaylists.get(1).getDisplayIndex()); + assertEquals(4, mergedPlaylists.get(2).getDisplayIndex()); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidRemotePlaylists() { + 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", "", "", 3, 1L)); + remotePlaylists.add(new PlaylistRemoteEntity( + 3, "name3", "url3", "", "", 0, 1L)); + PlaylistLocalItem.merge(localPlaylists, remotePlaylists); } @Test From 9ecef6f01103fc3aa0c719dd7abea4169d06c7d6 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Thu, 23 Jun 2022 19:20:16 +0800 Subject: [PATCH 014/123] Add abstract methods in PlaylistLocalItem & rename setIsModified --- .../database/playlist/PlaylistLocalItem.java | 4 ++++ .../playlist/PlaylistMetadataEntry.java | 14 +++++++++-- .../playlist/model/PlaylistEntity.java | 4 ++-- .../playlist/model/PlaylistRemoteEntity.java | 2 ++ .../local/bookmark/BookmarkFragment.java | 23 +++++++++---------- .../local/dialog/PlaylistAppendDialog.java | 4 ++-- .../local/playlist/LocalPlaylistFragment.java | 4 ++-- .../settings/SelectPlaylistFragment.java | 2 +- .../newpipe/util/debounce/DebounceSaver.java | 4 ++-- 9 files changed, 38 insertions(+), 23 deletions(-) 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 352d12d6b..3d58d3f7c 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 @@ -13,6 +13,10 @@ public interface PlaylistLocalItem extends LocalItem { long getDisplayIndex(); + long getUid(); + + void setDisplayIndex(long displayIndex); + /** * Merge localPlaylists and remotePlaylists by the display index. * If two items have the same display index, sort them in {@code CASE_INSENSITIVE_ORDER}. 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 ff80049a3..f1ead0fa4 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 @@ -11,13 +11,13 @@ 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_URL) public final String thumbnailUrl; @ColumnInfo(name = PLAYLIST_DISPLAY_INDEX) - public long displayIndex; + private long displayIndex; @ColumnInfo(name = PLAYLIST_STREAM_COUNT) public final long streamCount; @@ -44,4 +44,14 @@ public class PlaylistMetadataEntry implements PlaylistLocalItem { 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/model/PlaylistEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java index 272e8a5bc..508b55508 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 @@ -41,10 +41,10 @@ public class PlaylistEntity { @Ignore public PlaylistEntity(final PlaylistMetadataEntry item) { - this.uid = item.uid; + this.uid = item.getUid(); this.name = item.name; this.thumbnailUrl = item.thumbnailUrl; - this.displayIndex = item.displayIndex; + this.displayIndex = item.getDisplayIndex(); } public long getUid() { 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 adea2738b..82baed82c 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 @@ -105,6 +105,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { && TextUtils.equals(getUploader(), info.getUploaderName()); } + @Override public long getUid() { return uid; } @@ -158,6 +159,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { return displayIndex; } + @Override public void setDisplayIndex(final long displayIndex) { this.displayIndex = displayIndex; } diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index b0833dd9c..e9cf83239 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -139,7 +139,7 @@ public final class BookmarkFragment extends BaseLocalListFragment(((PlaylistMetadataEntry) item).uid, + displayIndexInDatabase.put(new Pair<>(item.getUid(), LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM), item.getDisplayIndex()); - ((PlaylistMetadataEntry) item).displayIndex = i; + item.setDisplayIndex(i); } else if (item instanceof PlaylistRemoteEntity) { - displayIndexInDatabase.put(new Pair<>(((PlaylistRemoteEntity) item).getUid(), - LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM), - item.getDisplayIndex()); - ((PlaylistRemoteEntity) item).setDisplayIndex(i); + displayIndexInDatabase.put(new Pair<>(item.getUid(), + LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM), item.getDisplayIndex()); + item.setDisplayIndex(i); } } @@ -415,9 +414,9 @@ public final class BookmarkFragment extends BaseLocalListFragment key = new Pair<>(uid, LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM); final Long databaseIndex = displayIndexInDatabase.remove(key); @@ -463,7 +462,7 @@ public final class BookmarkFragment extends BaseLocalListFragment { if (debounceSaver != null) { - debounceSaver.setIsModified(false); + debounceSaver.setNoChangesToSave(); } }, throwable -> showError(new ErrorInfo(throwable, @@ -563,7 +562,7 @@ public final class BookmarkFragment extends BaseLocalListFragment changeLocalPlaylistName( - selectedItem.uid, + selectedItem.getUid(), dialogBinding.dialogEditText.getText().toString())) .setNegativeButton(R.string.cancel, null) .setNeutralButton(R.string.delete, (dialog, which) -> { 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 58a10af22..a778e6578 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 @@ -147,12 +147,12 @@ public final class PlaylistAppendDialog extends PlaylistDialog { if (playlist.thumbnailUrl.equals("drawable://" + R.drawable.dummy_thumbnail_playlist)) { playlistDisposables.add(manager - .changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl()) + .changePlaylistThumbnail(playlist.getUid(), streams.get(0).getThumbnailUrl()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignored -> successToast.show())); } - playlistDisposables.add(manager.appendToPlaylist(playlist.uid, streams) + playlistDisposables.add(manager.appendToPlaylist(playlist.getUid(), streams) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignored -> successToast.show())); 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 1b8302cac..d129e658e 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 @@ -221,7 +221,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment { if (debounceSaver != null) { - debounceSaver.setIsModified(false); + debounceSaver.setNoChangesToSave(); } }, throwable -> showError(new ErrorInfo(throwable, 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 cc47c3f1c..905a44fd1 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -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/util/debounce/DebounceSaver.java b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java index 367174ab7..911e978ff 100644 --- a/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java +++ b/app/src/main/java/org/schabi/newpipe/util/debounce/DebounceSaver.java @@ -53,8 +53,8 @@ public class DebounceSaver { return isModified.get(); } - public void setIsModified(final boolean isModified) { - this.isModified.set(isModified); + public void setNoChangesToSave() { + isModified.set(false); } public PublishSubject getDebouncedSaveSignal() { From 4e401bc059ae1ede7f452fcfc68dd6d8c8987500 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Thu, 23 Jun 2022 20:36:21 +0800 Subject: [PATCH 015/123] Update playlists in parallel --- .../local/bookmark/BookmarkFragment.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index e9cf83239..9b93cc3e6 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -454,21 +454,16 @@ public final class BookmarkFragment extends BaseLocalListFragment disposables.add(remotePlaylistManager.updatePlaylists( - remoteItemsUpdate, remoteItemsDeleteUid) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> { - if (debounceSaver != null) { - debounceSaver.setNoChangesToSave(); - } - }, - throwable -> showError(new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Saving playlist")) - )), + .subscribe(() -> { + if (debounceSaver != null) { + debounceSaver.setNoChangesToSave(); + } + }, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK, "Saving playlist")) )); From 898a936064f19830779b0bf22f7c75b4bd4dd4df Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Thu, 23 Jun 2022 23:19:59 +0800 Subject: [PATCH 016/123] Update index modification logic & redo sorting in the merge algorithm --- .../database/playlist/PlaylistLocalItem.java | 23 ++--- .../local/bookmark/BookmarkFragment.java | 86 +++++++------------ .../local/playlist/LocalPlaylistFragment.java | 6 +- .../newpipe/util/debounce/DebounceSaver.java | 2 +- .../playlist/PlaylistLocalItemTest.java | 23 ----- 5 files changed, 39 insertions(+), 101 deletions(-) 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 3d58d3f7c..4314b0f82 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 @@ -21,29 +21,18 @@ public interface PlaylistLocalItem extends LocalItem { * 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 in the display index order - * @param remotePlaylists remote playlists in the display index order + * @param localPlaylists local playlists + * @param remotePlaylists remote playlists * @return merged playlists */ static List merge( final List localPlaylists, final List remotePlaylists) { - for (int i = 1; i < localPlaylists.size(); i++) { - if (localPlaylists.get(i).getDisplayIndex() - < localPlaylists.get(i - 1).getDisplayIndex()) { - throw new IllegalArgumentException( - "localPlaylists is not in the display index order"); - } - } - - for (int i = 1; i < remotePlaylists.size(); i++) { - if (remotePlaylists.get(i).getDisplayIndex() - < remotePlaylists.get(i - 1).getDisplayIndex()) { - throw new IllegalArgumentException( - "remotePlaylists is not in the display index order"); - } - } + Collections.sort(localPlaylists, + Comparator.comparingLong(PlaylistMetadataEntry::getDisplayIndex)); + Collections.sort(remotePlaylists, + Comparator.comparingLong(PlaylistRemoteEntity::getDisplayIndex)); // This algorithm is similar to the merge operation in merge sort. diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 9b93cc3e6..200fde562 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -41,9 +41,7 @@ import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import icepick.State; @@ -70,8 +68,7 @@ public final class BookmarkFragment extends BaseLocalListFragment, Long> displayIndexInDatabase; + private List> deletedItems; /////////////////////////////////////////////////////////////////////////// // Fragment LifeCycle - Creation @@ -89,9 +86,9 @@ public final class BookmarkFragment extends BaseLocalListFragment(); + deletedItems = new ArrayList<>(); } @Nullable @@ -186,7 +183,8 @@ public final class BookmarkFragment extends BaseLocalListFragment(item.getUid(), + LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM)); + } else if (item instanceof PlaylistRemoteEntity) { + deletedItems.add(new Pair<>(item.getUid(), + LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM)); + } + + debounceSaver.setHasChangesToSave(); } private void checkDisplayIndexModified(@NonNull final List result) { @@ -351,9 +357,7 @@ public final class BookmarkFragment extends BaseLocalListFragment(item.getUid(), - LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM), item.getDisplayIndex()); - item.setDisplayIndex(i); - - } else if (item instanceof PlaylistRemoteEntity) { - - displayIndexInDatabase.put(new Pair<>(item.getUid(), - LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM), item.getDisplayIndex()); - item.setDisplayIndex(i); - + break; } } if (debounceSaver != null && isDisplayIndexModified) { - debounceSaver.saveChanges(); + debounceSaver.setHasChangesToSave(); } } @@ -414,43 +401,28 @@ public final class BookmarkFragment extends BaseLocalListFragment key = new Pair<>(uid, - LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM); - final Long databaseIndex = displayIndexInDatabase.remove(key); - - // The database index should not be null because inserting new item into database - // is not handled here. NullPointerException has occurred once, but I can't - // reproduce it. Enhance robustness here. - if (databaseIndex != null && databaseIndex != i) { + if (((PlaylistMetadataEntry) item).getDisplayIndex() != i) { + ((PlaylistMetadataEntry) item).setDisplayIndex(i); localItemsUpdate.add((PlaylistMetadataEntry) item); } } else if (item instanceof PlaylistRemoteEntity) { - ((PlaylistRemoteEntity) item).setDisplayIndex(i); - - final Long uid = ((PlaylistRemoteEntity) item).getUid(); - final Pair key = new Pair<>(uid, - LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM); - final Long databaseIndex = displayIndexInDatabase.remove(key); - - if (databaseIndex != null && databaseIndex != i) { + if (((PlaylistRemoteEntity) item).getDisplayIndex() != i) { + ((PlaylistRemoteEntity) item).setDisplayIndex(i); remoteItemsUpdate.add((PlaylistRemoteEntity) item); } } } // Find deleted items - for (final Pair key : displayIndexInDatabase.keySet()) { - if (key.second.equals(LocalItem.LocalItemType.PLAYLIST_LOCAL_ITEM)) { - localItemsDeleteUid.add(key.first); - } else if (key.second.equals(LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM)) { - remoteItemsDeleteUid.add(key.first); + 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); } } - displayIndexInDatabase.clear(); + deletedItems.clear(); // 1. Update local playlists // 2. Update remote playlists @@ -515,7 +487,7 @@ public final class BookmarkFragment extends BaseLocalListFragment localPlaylists = new ArrayList<>(); - final List remotePlaylists = new ArrayList<>(); - localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 2, 1)); - localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 1, 1)); - localPlaylists.add(new PlaylistMetadataEntry(3, "name3", "", 0, 1)); - PlaylistLocalItem.merge(localPlaylists, remotePlaylists); - } - @Test public void onlyRemotePlaylists() { final List localPlaylists = new ArrayList<>(); @@ -65,19 +55,6 @@ public class PlaylistLocalItemTest { assertEquals(4, mergedPlaylists.get(2).getDisplayIndex()); } - @Test(expected = IllegalArgumentException.class) - public void invalidRemotePlaylists() { - 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", "", "", 3, 1L)); - remotePlaylists.add(new PlaylistRemoteEntity( - 3, "name3", "url3", "", "", 0, 1L)); - PlaylistLocalItem.merge(localPlaylists, remotePlaylists); - } - @Test public void sameIndexWithDifferentName() { final List localPlaylists = new ArrayList<>(); From 8ad7bf60d7ed3bcde5bda0d601ae5d920c042eb3 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Thu, 23 Jun 2022 23:31:56 +0800 Subject: [PATCH 017/123] Delete saveImmediate warnings & add comments --- .../org/schabi/newpipe/local/bookmark/BookmarkFragment.java | 3 +-- .../schabi/newpipe/local/playlist/LocalPlaylistFragment.java | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 200fde562..4be4838ec 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -66,6 +66,7 @@ public final class BookmarkFragment extends BaseLocalListFragment> deletedItems; @@ -385,8 +386,6 @@ public final class BookmarkFragment extends BaseLocalListFragment Date: Wed, 16 Aug 2023 21:24:55 +0200 Subject: [PATCH 018/123] Trim search string and remove duplicate records from the database Co-authored-by: Yingwei Zheng --- .../8.json | 737 ++++++++++++++++++ .../newpipe/database/DatabaseMigrationTest.kt | 61 +- .../org/schabi/newpipe/NewPipeDatabase.java | 3 +- .../schabi/newpipe/database/AppDatabase.java | 4 +- .../schabi/newpipe/database/Migrations.java | 10 + .../fragments/list/search/SearchFragment.java | 42 +- 6 files changed, 842 insertions(+), 15 deletions(-) create mode 100644 app/schemas/org.schabi.newpipe.database.AppDatabase/8.json 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/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt index 88e737205..cd048ac44 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -8,6 +8,7 @@ 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 @@ -25,8 +26,11 @@ class DatabaseMigrationTest { 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_SEARCH1 = " abc " + private const val DEFAULT_SEARCH2 = " abc" } @get:Rule @@ -106,6 +110,11 @@ class DatabaseMigrationTest { Migrations.MIGRATION_6_7 ) + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, + true, Migrations.MIGRATION_5_6 + ) + val migratedDatabaseV3 = getMigratedDatabase() val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst() @@ -140,6 +149,56 @@ class DatabaseMigrationTest { assertNull(secondStreamFromMigratedDatabase.isUploadDateApproximation) } + @Test + fun migrateDatabaseFrom5to6() { + val databaseInV5 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_5) + + databaseInV5.run { + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", DEFAULT_SERVICE_ID) + put("search", DEFAULT_SEARCH1) + } + ) + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", DEFAULT_SERVICE_ID) + put("search", DEFAULT_SEARCH2) + } + ) + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", DEFAULT_SECOND_SERVICE_ID) + put("search", DEFAULT_SEARCH1) + } + ) + insert( + "search_history", SQLiteDatabase.CONFLICT_FAIL, + ContentValues().apply { + put("service_id", DEFAULT_SECOND_SERVICE_ID) + put("search", DEFAULT_SEARCH2) + } + ) + close() + } + + testHelper.runMigrationsAndValidate( + AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, + true, Migrations.MIGRATION_5_6 + ) + + val migratedDatabaseV6 = getMigratedDatabase() + val listFromDB = migratedDatabaseV6.searchHistoryDAO().all.blockingFirst() + + assertEquals(2, listFromDB.size) + assertEquals("abc", listFromDB[0].search) + assertEquals("abc", listFromDB[1].search) + assertNotEquals(listFromDB[0].serviceId, listFromDB[1].serviceId) + } + private fun getMigratedDatabase(): AppDatabase { val database: AppDatabase = Room.databaseBuilder( ApplicationProvider.getApplicationContext(), diff --git a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java index 856fbff8b..c4f9feba7 100644 --- a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java +++ b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java @@ -7,6 +7,7 @@ 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 android.content.Context; import android.database.Cursor; @@ -27,7 +28,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) .build(); } 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..d03823e66 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_8; 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_8 ) 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/Migrations.java b/app/src/main/java/org/schabi/newpipe/database/Migrations.java index 1886b87c2..65c5626a5 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,7 @@ 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; private static final String TAG = Migrations.class.getName(); public static final boolean DEBUG = MainActivity.DEBUG; @@ -235,6 +236,15 @@ 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 id FROM search_history GROUP BY trim(search), service_id) tmp)"); + database.execSQL("UPDATE search_history SET search = trim(search)"); + } + }; + private Migrations() { } } 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 26a283229..87c48b69c 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 @@ -384,6 +384,7 @@ public class SearchFragment extends BaseListFragment 0)) { search(!TextUtils.isEmpty(searchString) ? searchString : searchEditText.getText().toString(), this.contentFilter, ""); @@ -494,7 +495,8 @@ public class SearchFragment extends BaseListFragment 1; } @Override @@ -597,8 +602,13 @@ 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) { @@ -786,12 +797,12 @@ public class SearchFragment extends BaseListFragment showSnackBarError(new ErrorInfo( - throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId))); + throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId))); } @Override @@ -804,6 +815,11 @@ public class SearchFragment extends BaseListFragment { }, + ignored -> { + }, throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.SEARCHED, theSearchString, serviceId)) )); @@ -973,6 +990,9 @@ public class SearchFragment extends BaseListFragment cannot be bundled without creating some containers From 4af5b5f6f21c28b718224690a6239d13f019f032 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Wed, 16 Aug 2023 22:01:58 +0200 Subject: [PATCH 019/123] Fix database migration and string trimming Co-authored-by: Yingwei Zheng --- .../newpipe/database/DatabaseMigrationTest.kt | 23 +++++++++++-------- .../schabi/newpipe/database/Migrations.java | 4 ++-- .../fragments/list/search/SearchFragment.java | 17 ++++---------- 3 files changed, 21 insertions(+), 23 deletions(-) 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 cd048ac44..c0c608a58 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -111,8 +111,10 @@ class DatabaseMigrationTest { ) testHelper.runMigrationsAndValidate( - AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, - true, Migrations.MIGRATION_5_6 + AppDatabase.DATABASE_NAME, + Migrations.DB_VER_8, + true, + Migrations.MIGRATION_7_8 ) val migratedDatabaseV3 = getMigratedDatabase() @@ -150,10 +152,13 @@ class DatabaseMigrationTest { } @Test - fun migrateDatabaseFrom5to6() { - val databaseInV5 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_5) + fun migrateDatabaseFrom7to8() { + val databaseInV7 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_7) - databaseInV5.run { + val defaultSearch1 = " abc " + val defaultSearch2 = " abc" + + databaseInV7.run { insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { @@ -186,12 +191,12 @@ class DatabaseMigrationTest { } testHelper.runMigrationsAndValidate( - AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, - true, Migrations.MIGRATION_5_6 + AppDatabase.DATABASE_NAME, Migrations.DB_VER_8, + true, Migrations.MIGRATION_7_8 ) - val migratedDatabaseV6 = getMigratedDatabase() - val listFromDB = migratedDatabaseV6.searchHistoryDAO().all.blockingFirst() + val migratedDatabaseV8 = getMigratedDatabase() + val listFromDB = migratedDatabaseV8.searchHistoryDAO().all.blockingFirst() assertEquals(2, listFromDB.size) assertEquals("abc", listFromDB[0].search) 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 65c5626a5..4b1a34dd6 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -239,8 +239,8 @@ 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 id FROM search_history GROUP BY trim(search), service_id) tmp)"); + 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)"); } }; 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 87c48b69c..df4a47bc5 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; @@ -398,7 +399,7 @@ public class SearchFragment extends BaseListFragment 0)) { + && !isBlank(searchEditText.getText().toString()))) { search(!TextUtils.isEmpty(searchString) ? searchString : searchEditText.getText().toString(), this.contentFilter, ""); @@ -496,7 +497,7 @@ public class SearchFragment extends BaseListFragment 1; } @Override @@ -604,11 +602,6 @@ public class SearchFragment extends BaseListFragment Date: Wed, 4 May 2022 20:20:19 +0800 Subject: [PATCH 020/123] Refactor database migration test and string trimming --- .../newpipe/database/DatabaseMigrationTest.kt | 11 +++---- .../fragments/list/search/SearchFragment.java | 29 ++++++++++++------- 2 files changed, 22 insertions(+), 18 deletions(-) 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 c0c608a58..19053ba97 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -28,9 +28,6 @@ class DatabaseMigrationTest { 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_SEARCH1 = " abc " - private const val DEFAULT_SEARCH2 = " abc" } @get:Rule @@ -163,28 +160,28 @@ class DatabaseMigrationTest { "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", DEFAULT_SERVICE_ID) - put("search", DEFAULT_SEARCH1) + put("search", defaultSearch1) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", DEFAULT_SERVICE_ID) - put("search", DEFAULT_SEARCH2) + put("search", defaultSearch2) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", DEFAULT_SECOND_SERVICE_ID) - put("search", DEFAULT_SEARCH1) + put("search", defaultSearch1) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("service_id", DEFAULT_SECOND_SERVICE_ID) - put("search", DEFAULT_SEARCH2) + put("search", defaultSearch2) } ) close() 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 df4a47bc5..07d41c160 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 @@ -385,9 +385,8 @@ public class SearchFragment extends BaseListFragment suggestionPublisher - .onNext(searchEditText.getText().toString()), + .onNext(getSearchEditString()), throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY, "Deleting item failed"))); @@ -942,6 +941,14 @@ 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); From ef40ac7bb37a1e10214e48bd1b9ee82f88d69fd3 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Thu, 5 May 2022 14:15:19 +0800 Subject: [PATCH 021/123] Fix a typo --- .../schabi/newpipe/fragments/list/search/SearchFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 07d41c160..ebdde2f95 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 @@ -945,7 +945,7 @@ public class SearchFragment extends BaseListFragment Date: Wed, 16 Aug 2023 22:18:53 +0200 Subject: [PATCH 022/123] Apply review --- .../newpipe/database/DatabaseMigrationTest.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 19053ba97..65f41d8fa 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -13,6 +13,7 @@ import org.junit.Assert.assertNull import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.schabi.newpipe.extractor.ServiceList import org.schabi.newpipe.extractor.stream.StreamType @RunWith(AndroidJUnit4::class) @@ -26,7 +27,7 @@ class DatabaseMigrationTest { 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 = 1 + private const val DEFAULT_SECOND_SERVICE_ID = 0 private const val DEFAULT_SECOND_URL = "https://www.youtube.com/watch?v=ncQU6iBn5Fc" } @@ -155,32 +156,37 @@ class DatabaseMigrationTest { 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", DEFAULT_SERVICE_ID) + put("service_id", serviceId) put("search", defaultSearch1) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { - put("service_id", DEFAULT_SERVICE_ID) + put("service_id", serviceId) put("search", defaultSearch2) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { - put("service_id", DEFAULT_SECOND_SERVICE_ID) + put("service_id", otherServiceId) put("search", defaultSearch1) } ) insert( "search_history", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { - put("service_id", DEFAULT_SECOND_SERVICE_ID) + put("service_id", otherServiceId) put("search", defaultSearch2) } ) From 9118ecd68fa8793df4db46958e4d63015d5e1c61 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Thu, 17 Aug 2023 16:51:31 +0200 Subject: [PATCH 023/123] Remove unnecessary debug warning and use JDoc instead --- .../fragments/list/search/SearchFragment.java | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) 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 ebdde2f95..558f8df44 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 @@ -802,40 +802,42 @@ 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(); @@ -844,6 +846,7 @@ public class SearchFragment extends BaseListFragment showSnackBarError(new ErrorInfo(throwable, UserAction.SEARCHED, theSearchString, serviceId)) )); + + // load search results suggestionPublisher.onNext(theSearchString); startLoading(false); } From 5d101e7b88a824272ff78512c537432a489a1b31 Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Mon, 24 Oct 2022 09:54:22 +1100 Subject: [PATCH 024/123] Added reset button but not working as intended. --- .../settings/AppearanceSettingsFragment.java | 18 ++++++++++++++++++ .../settings/ResetSettingsFragment.java | 14 ++++++++++++++ app/src/main/res/values/settings_keys.xml | 2 ++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/main_settings.xml | 8 ++++++++ gradle/wrapper/gradle-wrapper.properties | 2 ++ settings.gradle | 10 +++++----- 7 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java index ef0e8670c..c8fa3e392 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java @@ -80,4 +80,22 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment { ActivityCompat.recreate(getActivity()); } } + + public void resetToDefault() { + final String themeKey = getString(R.string.theme_key); + final String startThemeKey = defaultPreferences + .getString(themeKey, getString(R.string.default_theme_value)); + final String autoDeviceThemeKey = getString(R.string.auto_device_theme_key); + if (startThemeKey.equals(autoDeviceThemeKey)) { + applyThemeChange(startThemeKey, themeKey, autoDeviceThemeKey); + } else { + if (startThemeKey.equals(R.string.light_theme_key)) { + removePreference(getString(R.string.light_theme_key)); + } else if (startThemeKey.equals(R.string.dark_theme_key)) { + removePreference(getString(R.string.dark_theme_key)); + } else { + removePreference(getString(R.string.black_theme_key)); + } + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java new file mode 100644 index 000000000..94936f6ca --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java @@ -0,0 +1,14 @@ +package org.schabi.newpipe.settings; + +import android.os.Bundle; + +public class ResetSettingsFragment extends BasePreferenceFragment { + + private AppearanceSettingsFragment appearanceSettings; + @Override + public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { + addPreferencesFromResourceRegistry(); + + appearanceSettings.resetToDefault(); + } +} diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 51abe14fb..de883420c 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -222,6 +222,8 @@ prefer_original_audio prefer_descriptive_audio last_resize_mode + + reset_pref_screen_key debug_pref_screen_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e5bbffaff..f83938139 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,6 +151,7 @@ History and cache Appearance Debug + Reset All Settings Updates Player notification Configure current playing stream notification diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml index 1b1c17e85..a569f391c 100644 --- a/app/src/main/res/xml/main_settings.xml +++ b/app/src/main/res/xml/main_settings.xml @@ -53,4 +53,12 @@ android:key="@string/debug_pref_screen_key" android:title="@string/settings_category_debug_title" app:iconSpaceReserved="false" /> + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2c3425d49..fcaba806a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,3 +1,4 @@ +#Fri Oct 21 13:32:17 AEDT 2022 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f @@ -5,3 +6,4 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle b/settings.gradle index 0338fde6c..a0aed9527 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,8 +4,8 @@ include ':app' // We assume, that NewPipe and NewPipe Extractor have the same parent directory. // If this is not the case, please change the path in includeBuild(). -//includeBuild('../NewPipeExtractor') { -// dependencySubstitution { -// substitute module('com.github.TeamNewPipe:NewPipeExtractor') using project(':extractor') -// } -//} +includeBuild('../NewPipeExtractor') { + dependencySubstitution { + substitute module('com.github.TeamNewPipe:NewPipeExtractor') using project(':extractor') + } +} From aa1847189ba9503636eb567ac64ca199c06e6c62 Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Tue, 25 Oct 2022 13:18:31 +1100 Subject: [PATCH 025/123] Added reset button but slightly working as intended. --- .../settings/AppearanceSettingsFragment.java | 18 ------------------ .../settings/ResetSettingsFragment.java | 13 +++++++++++-- .../settings/SettingsResourceRegistry.java | 1 + app/src/main/res/xml/reset_settings.xml | 4 ++++ 4 files changed, 16 insertions(+), 20 deletions(-) create mode 100644 app/src/main/res/xml/reset_settings.xml diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java index c8fa3e392..ef0e8670c 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java @@ -80,22 +80,4 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment { ActivityCompat.recreate(getActivity()); } } - - public void resetToDefault() { - final String themeKey = getString(R.string.theme_key); - final String startThemeKey = defaultPreferences - .getString(themeKey, getString(R.string.default_theme_value)); - final String autoDeviceThemeKey = getString(R.string.auto_device_theme_key); - if (startThemeKey.equals(autoDeviceThemeKey)) { - applyThemeChange(startThemeKey, themeKey, autoDeviceThemeKey); - } else { - if (startThemeKey.equals(R.string.light_theme_key)) { - removePreference(getString(R.string.light_theme_key)); - } else if (startThemeKey.equals(R.string.dark_theme_key)) { - removePreference(getString(R.string.dark_theme_key)); - } else { - removePreference(getString(R.string.black_theme_key)); - } - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java index 94936f6ca..aa7f528c6 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java @@ -2,13 +2,22 @@ package org.schabi.newpipe.settings; import android.os.Bundle; +import androidx.core.app.ActivityCompat; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.ThemeHelper; + public class ResetSettingsFragment extends BasePreferenceFragment { - private AppearanceSettingsFragment appearanceSettings; @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { addPreferencesFromResourceRegistry(); - appearanceSettings.resetToDefault(); + // reset appearance to light theme + defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply(); + defaultPreferences.edit().putString(getString(R.string.theme_key), + getString(R.string.light_theme_key)).apply(); + ThemeHelper.setDayNightMode(requireContext(), "light_theme"); } } 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..20d6555a5 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(ResetSettingsFragment.class, R.xml.main_settings); } private SettingRegistryEntry add( diff --git a/app/src/main/res/xml/reset_settings.xml b/app/src/main/res/xml/reset_settings.xml new file mode 100644 index 000000000..624ed13ae --- /dev/null +++ b/app/src/main/res/xml/reset_settings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 58517d1d27571b8f563656b42c472d9624ee2893 Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Wed, 26 Oct 2022 17:09:53 +1100 Subject: [PATCH 026/123] Added reset button but working as intended for theme. --- .../settings/DebugSettingsFragment.java | 13 +++++++++++ .../settings/ResetSettingsFragment.java | 23 ------------------- .../settings/SettingsResourceRegistry.java | 2 -- app/src/main/res/values/settings_keys.xml | 2 +- app/src/main/res/xml/debug_settings.xml | 5 ++++ app/src/main/res/xml/main_settings.xml | 8 ------- app/src/main/res/xml/reset_settings.xml | 4 ---- 7 files changed, 19 insertions(+), 38 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java delete mode 100644 app/src/main/res/xml/reset_settings.xml diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 0f4c9765e..690220da7 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -10,7 +10,9 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.feed.notifications.NotificationWorker; +import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.PicassoHelper; +import org.schabi.newpipe.util.ThemeHelper; import java.util.Optional; @@ -35,6 +37,8 @@ public class DebugSettingsFragment extends BasePreferenceFragment { findPreference(getString(R.string.show_error_snackbar_key)); final Preference createErrorNotificationPreference = findPreference(getString(R.string.create_error_notification_key)); + final Preference resetSettings = + findPreference(getString(R.string.reset_settings)); assert allowHeapDumpingPreference != null; assert showMemoryLeaksPreference != null; @@ -86,6 +90,15 @@ public class DebugSettingsFragment extends BasePreferenceFragment { new ErrorInfo(new RuntimeException(DUMMY), UserAction.UI_ERROR, DUMMY)); return true; }); + + // reset appearance to light theme + resetSettings.setOnPreferenceClickListener(preference -> { + defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply(); + defaultPreferences.edit().putString(getString(R.string.theme_key), + getString(R.string.light_theme_key)).apply(); + ThemeHelper.setDayNightMode(requireContext(), "light_theme"); + return true; + }); } /** diff --git a/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java deleted file mode 100644 index aa7f528c6..000000000 --- a/app/src/main/java/org/schabi/newpipe/settings/ResetSettingsFragment.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.schabi.newpipe.settings; - -import android.os.Bundle; - -import androidx.core.app.ActivityCompat; - -import org.schabi.newpipe.R; -import org.schabi.newpipe.util.Constants; -import org.schabi.newpipe.util.ThemeHelper; - -public class ResetSettingsFragment extends BasePreferenceFragment { - - @Override - public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { - addPreferencesFromResourceRegistry(); - - // reset appearance to light theme - defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply(); - defaultPreferences.edit().putString(getString(R.string.theme_key), - getString(R.string.light_theme_key)).apply(); - ThemeHelper.setDayNightMode(requireContext(), "light_theme"); - } -} 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 20d6555a5..2fe4f42ed 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java @@ -30,7 +30,6 @@ public final class SettingsResourceRegistry { private SettingsResourceRegistry() { add(MainSettingsFragment.class, R.xml.main_settings).setSearchable(false); - add(AppearanceSettingsFragment.class, R.xml.appearance_settings); add(ContentSettingsFragment.class, R.xml.content_settings); add(DebugSettingsFragment.class, R.xml.debug_settings).setSearchable(false); @@ -41,7 +40,6 @@ 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(ResetSettingsFragment.class, R.xml.main_settings); } private SettingRegistryEntry add( diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index de883420c..42d434900 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -223,7 +223,7 @@ prefer_descriptive_audio last_resize_mode - reset_pref_screen_key + reset_settings debug_pref_screen_key diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml index 84bb281f3..66836f138 100644 --- a/app/src/main/res/xml/debug_settings.xml +++ b/app/src/main/res/xml/debug_settings.xml @@ -71,4 +71,9 @@ android:title="@string/create_error_notification" app:singleLineTitle="false" app:iconSpaceReserved="false" /> + diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml index a569f391c..1b1c17e85 100644 --- a/app/src/main/res/xml/main_settings.xml +++ b/app/src/main/res/xml/main_settings.xml @@ -53,12 +53,4 @@ android:key="@string/debug_pref_screen_key" android:title="@string/settings_category_debug_title" app:iconSpaceReserved="false" /> - diff --git a/app/src/main/res/xml/reset_settings.xml b/app/src/main/res/xml/reset_settings.xml deleted file mode 100644 index 624ed13ae..000000000 --- a/app/src/main/res/xml/reset_settings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From 2103a040922227ad16c92a938eb5671d68e20569 Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Wed, 26 Oct 2022 19:13:41 +1100 Subject: [PATCH 027/123] Cleaned up xml files. --- app/src/main/res/values/settings_keys.xml | 1 + app/src/main/res/xml/debug_settings.xml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 42d434900..ea283e62c 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -222,6 +222,7 @@ prefer_original_audio prefer_descriptive_audio last_resize_mode + reset_settings diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml index 66836f138..511bcd19a 100644 --- a/app/src/main/res/xml/debug_settings.xml +++ b/app/src/main/res/xml/debug_settings.xml @@ -72,7 +72,6 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> From 076e9eee01ca3f55f56124ce407364182ff8278d Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 08:58:11 +1100 Subject: [PATCH 028/123] Added alert dialogue and restarts the app when resetting settings. --- .../settings/DebugSettingsFragment.java | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 690220da7..56c88f730 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.settings; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; @@ -10,9 +12,8 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.feed.notifications.NotificationWorker; -import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PicassoHelper; -import org.schabi.newpipe.util.ThemeHelper; import java.util.Optional; @@ -91,12 +92,30 @@ public class DebugSettingsFragment extends BasePreferenceFragment { return true; }); - // reset appearance to light theme + // 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 resetSettings.setOnPreferenceClickListener(preference -> { - defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply(); - defaultPreferences.edit().putString(getString(R.string.theme_key), - getString(R.string.light_theme_key)).apply(); - ThemeHelper.setDayNightMode(requireContext(), "light_theme"); + final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setMessage("Resetting all settings will discard " + + "all of your preferred settings and restarts the app. " + + "Are you sure you want to do this?"); + builder.setCancelable(true); + builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialogInterface, final int i) { + NavigationHelper.restartApp(getActivity()); + } + }); + builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialogInterface, final int i) { + } + }); + final AlertDialog alertDialog = builder.create(); + alertDialog.show(); +// SharedPreferences sharedPreferences = +// PreferenceManager.getDefaultSharedPreferences(requireContext()); +// sharedPreferences = null; return true; }); } From 5c46412faac23f27940cba860c8e8a1219375edd Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 09:19:04 +1100 Subject: [PATCH 029/123] Changed alert dialogue. --- .../java/org/schabi/newpipe/settings/DebugSettingsFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 56c88f730..88addd73e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -98,7 +98,7 @@ public class DebugSettingsFragment extends BasePreferenceFragment { final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setMessage("Resetting all settings will discard " + "all of your preferred settings and restarts the app. " - + "Are you sure you want to do this?"); + + "Are you sure you want to proceed?"); builder.setCancelable(true); builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override From 23de9bf93ed6629afc8080ffa6a3f2467d0f76b1 Mon Sep 17 00:00:00 2001 From: Zhidong Piao Date: Sat, 29 Oct 2022 09:38:00 +1100 Subject: [PATCH 030/123] clear shared preference xmls --- .../newpipe/settings/DebugSettingsFragment.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 88addd73e..5c5f6a039 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -3,9 +3,11 @@ package org.schabi.newpipe.settings; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import androidx.preference.Preference; +import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; @@ -94,6 +96,7 @@ public class DebugSettingsFragment extends BasePreferenceFragment { // 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 -> { final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setMessage("Resetting all settings will discard " @@ -113,9 +116,13 @@ public class DebugSettingsFragment extends BasePreferenceFragment { }); final AlertDialog alertDialog = builder.create(); alertDialog.show(); -// SharedPreferences sharedPreferences = -// PreferenceManager.getDefaultSharedPreferences(requireContext()); -// sharedPreferences = null; + + // delete all shared preferences xml files. + final SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(requireContext()); + sharedPreferences.edit().clear().apply(); + + return true; }); } From 81ad50e82a0eb183cd348259445e8ffe25d9f6c8 Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 09:52:22 +1100 Subject: [PATCH 031/123] Added delete xml method inside the yes dialogue. --- .../settings/DebugSettingsFragment.java | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 5c5f6a039..5952fbe5b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.settings; import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; @@ -98,31 +97,27 @@ public class DebugSettingsFragment extends BasePreferenceFragment { // 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("Resetting all settings will discard " + "all of your preferred settings and restarts the app. " + "Are you sure you want to proceed?"); builder.setCancelable(true); - builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { - @Override - public void onClick(final DialogInterface dialogInterface, final int i) { - NavigationHelper.restartApp(getActivity()); + builder.setPositiveButton("Yes", (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("Cancel", new DialogInterface.OnClickListener() { - @Override - public void onClick(final DialogInterface dialogInterface, final int i) { - } + builder.setNegativeButton("Cancel", (dialogInterface, i) -> { }); final AlertDialog alertDialog = builder.create(); alertDialog.show(); - - // delete all shared preferences xml files. - final SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(requireContext()); - sharedPreferences.edit().clear().apply(); - - return true; }); } From 06d256294f31e944cc41ff9af12b13ce10b39202 Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 10:24:11 +1100 Subject: [PATCH 032/123] Revert changes made to dev. --- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fcaba806a..28d3f9e02 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,3 @@ -#Fri Oct 21 13:32:17 AEDT 2022 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f @@ -7,3 +6,4 @@ networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index a0aed9527..0338fde6c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,8 +4,8 @@ include ':app' // We assume, that NewPipe and NewPipe Extractor have the same parent directory. // If this is not the case, please change the path in includeBuild(). -includeBuild('../NewPipeExtractor') { - dependencySubstitution { - substitute module('com.github.TeamNewPipe:NewPipeExtractor') using project(':extractor') - } -} +//includeBuild('../NewPipeExtractor') { +// dependencySubstitution { +// substitute module('com.github.TeamNewPipe:NewPipeExtractor') using project(':extractor') +// } +//} From a239a26b17c1259b1e261d3bcf0c99b1e1581059 Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 10:26:11 +1100 Subject: [PATCH 033/123] Revert changes made to dev. --- .../org/schabi/newpipe/settings/SettingsResourceRegistry.java | 1 + 1 file changed, 1 insertion(+) 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 2fe4f42ed..b3d0741bb 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java @@ -30,6 +30,7 @@ public final class SettingsResourceRegistry { private SettingsResourceRegistry() { add(MainSettingsFragment.class, R.xml.main_settings).setSearchable(false); + add(AppearanceSettingsFragment.class, R.xml.appearance_settings); add(ContentSettingsFragment.class, R.xml.content_settings); add(DebugSettingsFragment.class, R.xml.debug_settings).setSearchable(false); From 25a73090f5bb36f77cb77edca11f89c99513f49d Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 10:29:16 +1100 Subject: [PATCH 034/123] Revert changes made to dev. --- app/src/main/res/values/settings_keys.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index ea283e62c..51abe14fb 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -223,9 +223,6 @@ prefer_descriptive_audio last_resize_mode - - reset_settings - debug_pref_screen_key allow_heap_dumping_key From 40a3e1b18a4c2abfd412c875d83912ea6d710def Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 10:30:41 +1100 Subject: [PATCH 035/123] Revert committed file change of settings key. --- app/src/main/res/values/settings_keys.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 51abe14fb..ea283e62c 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -223,6 +223,9 @@ prefer_descriptive_audio last_resize_mode + + reset_settings + debug_pref_screen_key allow_heap_dumping_key From d7ef9b1f0c1cbba8cf216ab696a0ba93a281c24b Mon Sep 17 00:00:00 2001 From: Vincent Tanumihardja Date: Sat, 29 Oct 2022 11:33:10 +1100 Subject: [PATCH 036/123] Added strings to strings.xml to allow translation. --- .../schabi/newpipe/settings/DebugSettingsFragment.java | 8 +++----- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 5952fbe5b..f2a4e1190 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -99,11 +99,9 @@ public class DebugSettingsFragment extends BasePreferenceFragment { resetSettings.setOnPreferenceClickListener(preference -> { // Show Alert Dialogue final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setMessage("Resetting all settings will discard " - + "all of your preferred settings and restarts the app. " - + "Are you sure you want to proceed?"); + builder.setMessage(R.string.reset_all_settings); builder.setCancelable(true); - builder.setPositiveButton("Yes", (dialogInterface, i) -> { + builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> { // Deletes all shared preferences xml files. final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()); @@ -114,7 +112,7 @@ public class DebugSettingsFragment extends BasePreferenceFragment { } NavigationHelper.restartApp(getActivity()); }); - builder.setNegativeButton("Cancel", (dialogInterface, i) -> { + builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> { }); final AlertDialog alertDialog = builder.create(); alertDialog.show(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f83938139..9c843784e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -595,6 +595,8 @@ Download finished %s downloads finished + + Resetting all settings will discard all of your preferred settings and restarts the app. Are you sure you want to proceed? Generate unique name Overwrite From ad0855ac83715179d1be3068492f136271e78072 Mon Sep 17 00:00:00 2001 From: vincetzr <110076924+vincetzr@users.noreply.github.com> Date: Sat, 29 Oct 2022 21:21:26 +1100 Subject: [PATCH 037/123] Update app/src/main/res/xml/debug_settings.xml Ensuring title to be fully displayed on small devices. Co-authored-by: Tobi --- app/src/main/res/xml/debug_settings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml index 511bcd19a..1c66dbdb3 100644 --- a/app/src/main/res/xml/debug_settings.xml +++ b/app/src/main/res/xml/debug_settings.xml @@ -74,5 +74,6 @@ From f2e352832a12d1282a4b6b7dddf389cf8e7887cf Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 17 Sep 2023 17:21:59 +0200 Subject: [PATCH 038/123] Create new settings category: Backup and restore Following settings have been move to the new category: - import database (from ContenttSettings) - export database (from ContenttSettings) - reset settings (from DebugSettings) --- .../BackupRestoreSettingsFragment.java | 271 ++++++++++++++++++ .../settings/ContentSettingsFragment.java | 217 -------------- .../settings/DebugSettingsFragment.java | 32 --- .../newpipe/settings/NewPipeSettings.java | 1 + .../settings/SettingsResourceRegistry.java | 1 + app/src/main/res/values/settings_keys.xml | 1 - app/src/main/res/values/strings.xml | 1 + .../main/res/xml/backup_restore_settings.xml | 24 ++ app/src/main/res/xml/content_settings.xml | 14 - app/src/main/res/xml/debug_settings.xml | 5 - app/src/main/res/xml/main_settings.xml | 6 + 11 files changed, 304 insertions(+), 269 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java create mode 100644 app/src/main/res/xml/backup_restore_settings.xml 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 ee34f01dd..ec3b1b2d7 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -1,104 +1,34 @@ 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.PicassoHelper; -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 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 @@ -154,151 +84,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/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index f2a4e1190..0f4c9765e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -1,19 +1,15 @@ package org.schabi.newpipe.settings; -import android.app.AlertDialog; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import androidx.preference.Preference; -import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.feed.notifications.NotificationWorker; -import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PicassoHelper; import java.util.Optional; @@ -39,8 +35,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment { findPreference(getString(R.string.show_error_snackbar_key)); final Preference createErrorNotificationPreference = findPreference(getString(R.string.create_error_notification_key)); - final Preference resetSettings = - findPreference(getString(R.string.reset_settings)); assert allowHeapDumpingPreference != null; assert showMemoryLeaksPreference != null; @@ -92,32 +86,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment { new ErrorInfo(new RuntimeException(DUMMY), UserAction.UI_ERROR, DUMMY)); return true; }); - - // 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; - }); } /** 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..f280324cf 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -63,6 +63,7 @@ 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); 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/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index ea283e62c..30efca080 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -223,7 +223,6 @@ prefer_descriptive_audio last_resize_mode - reset_settings diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9c843784e..314eaa9bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,6 +155,7 @@ Updates Player notification Configure current playing stream notification + Backup and restore Playing in background Playing in popup mode Content diff --git a/app/src/main/res/xml/backup_restore_settings.xml b/app/src/main/res/xml/backup_restore_settings.xml new file mode 100644 index 000000000..b8fee0aa6 --- /dev/null +++ b/app/src/main/res/xml/backup_restore_settings.xml @@ -0,0 +1,24 @@ + + + + + + + + \ 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 73a849af7..dac5dccc1 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -124,20 +124,6 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> - - - - - diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml index 1b1c17e85..5f96989f9 100644 --- a/app/src/main/res/xml/main_settings.xml +++ b/app/src/main/res/xml/main_settings.xml @@ -47,6 +47,12 @@ android:title="@string/settings_category_updates_title" app:iconSpaceReserved="false" /> + + Date: Sun, 17 Sep 2023 17:31:45 +0200 Subject: [PATCH 039/123] Remove strange change --- gradle/wrapper/gradle-wrapper.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 28d3f9e02..2c3425d49 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -5,5 +5,3 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists From df2e0be08dd36c89b747a31dc3ab40d016f9d1e6 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 17 Sep 2023 17:35:52 +0200 Subject: [PATCH 040/123] Add summary to reset preference --- app/src/main/res/values/strings.xml | 5 +++-- app/src/main/res/xml/backup_restore_settings.xml | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 314eaa9bd..1dbffa4ae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,7 +151,6 @@ History and cache Appearance Debug - Reset All Settings Updates Player notification Configure current playing stream notification @@ -596,8 +595,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. Are you sure you want to proceed? + 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 diff --git a/app/src/main/res/xml/backup_restore_settings.xml b/app/src/main/res/xml/backup_restore_settings.xml index b8fee0aa6..ef6a3cde3 100644 --- a/app/src/main/res/xml/backup_restore_settings.xml +++ b/app/src/main/res/xml/backup_restore_settings.xml @@ -18,7 +18,8 @@ \ No newline at end of file From 6c9955755339cae73d1f90567bf52999779fcb96 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Fri, 12 May 2023 11:29:09 +0200 Subject: [PATCH 041/123] Add playlist description to PlaylistFragment --- .../list/playlist/PlaylistFragment.java | 17 +++++++++++++++++ app/src/main/res/layout/playlist_header.xml | 11 ++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index e82a984d5..95cead389 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -1,7 +1,9 @@ package org.schabi.newpipe.fragments.list.playlist; +import static org.schabi.newpipe.extractor.utils.Utils.isBlank; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; +import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; import android.content.Context; import android.os.Bundle; @@ -17,6 +19,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.text.HtmlCompat; import com.google.android.material.shape.CornerFamily; import com.google.android.material.shape.ShapeAppearanceModel; @@ -37,6 +40,7 @@ import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper; +import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.info_list.dialog.InfoItemDialog; @@ -51,6 +55,7 @@ import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.PlayButtonHelper; +import org.schabi.newpipe.util.text.TextLinkifier; import java.util.ArrayList; import java.util.List; @@ -321,6 +326,18 @@ public class PlaylistFragment extends BaseListInfoFragment + + + android:layout_below="@id/playlist_description"> Date: Tue, 20 Jun 2023 15:56:43 +0200 Subject: [PATCH 042/123] Ellipsize playlist description if it is longer than 5 lines The description can be expanded / collapsed via a "show more" / "show less" button. --- .../list/playlist/PlaylistFragment.java | 30 ++- .../holder/CommentInfoItemHolder.java | 123 ++--------- .../newpipe/util/text/TextEllipsizer.java | 195 ++++++++++++++++++ app/src/main/res/layout/playlist_header.xml | 17 +- app/src/main/res/values/strings.xml | 2 + 5 files changed, 249 insertions(+), 118 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 95cead389..ab3963d61 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -3,7 +3,7 @@ package org.schabi.newpipe.fragments.list.playlist; import static org.schabi.newpipe.extractor.utils.Utils.isBlank; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; -import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; +import static org.schabi.newpipe.util.ServiceHelper.getServiceById; import android.content.Context; import android.os.Bundle; @@ -19,7 +19,6 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.text.HtmlCompat; import com.google.android.material.shape.CornerFamily; import com.google.android.material.shape.ShapeAppearanceModel; @@ -52,10 +51,10 @@ import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.image.PicassoHelper; -import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.PlayButtonHelper; -import org.schabi.newpipe.util.text.TextLinkifier; +import org.schabi.newpipe.util.external_communication.ShareUtils; +import org.schabi.newpipe.util.image.PicassoHelper; +import org.schabi.newpipe.util.text.TextEllipsizer; import java.util.ArrayList; import java.util.List; @@ -329,13 +328,24 @@ public class PlaylistFragment 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()) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java index 8327b398b..a3f0384ad 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java @@ -1,10 +1,7 @@ package org.schabi.newpipe.info_list.holder; -import static android.text.TextUtils.isEmpty; import static org.schabi.newpipe.util.ServiceHelper.getServiceById; -import android.graphics.Paint; -import android.text.Layout; import android.text.method.LinkMovementMethod; import android.text.style.URLSpan; import android.view.View; @@ -15,42 +12,28 @@ import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.text.HtmlCompat; import androidx.fragment.app.FragmentActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.comments.CommentsInfoItem; -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.external_communication.ShareUtils; 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; +import org.schabi.newpipe.util.text.TextEllipsizer; public class CommentInfoItemHolder extends InfoItemHolder { - 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; @@ -61,13 +44,8 @@ public class CommentInfoItemHolder extends InfoItemHolder { private final ImageView itemPinnedView; private final Button repliesButton; - private final CompositeDisposable disposables = new CompositeDisposable(); - @Nullable - private Description commentText; - @Nullable - private StreamingService streamService; - @Nullable - private String streamUrl; + @NonNull + private final TextEllipsizer textEllipsizer; public CommentInfoItemHolder(final InfoItemBuilder infoItemBuilder, final ViewGroup parent) { @@ -88,9 +66,14 @@ public class CommentInfoItemHolder extends InfoItemHolder { commentVerticalPadding = (int) infoItemBuilder.getContext() .getResources().getDimension(R.dimen.comments_vertical_padding); - paintAtContentSize = new Paint(); - paintAtContentSize.setTextSize(itemContentView.getTextSize()); - ellipsisWidthPx = paintAtContentSize.measureText(ELLIPSIS); + textEllipsizer = new TextEllipsizer(itemContentView, COMMENT_DEFAULT_LINES, null); + textEllipsizer.setStateChangeListener(isEllipsized -> { + if (Boolean.TRUE.equals(isEllipsized)) { + denyLinkFocus(); + } else { + determineMovementMethod(); + } + }); } @Override @@ -139,16 +122,16 @@ public class CommentInfoItemHolder extends InfoItemHolder { // setup comment content and click listeners to expand/ellipsize it - streamService = getServiceById(item.getServiceId()); - streamUrl = item.getUrl(); - commentText = item.getCommentText(); - ellipsize(); + textEllipsizer.setStreamingService(getServiceById(item.getServiceId())); + textEllipsizer.setStreamUrl(item.getUrl()); + textEllipsizer.setContent(item.getCommentText()); + textEllipsizer.ellipsize(); //noinspection ClickableViewAccessibility itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); itemView.setOnClickListener(view -> { - toggleEllipsize(); + textEllipsizer.toggle(); if (itemBuilder.getOnCommentsSelectedListener() != null) { itemBuilder.getOnCommentsSelectedListener().selected(item); } @@ -202,76 +185,4 @@ public class CommentInfoItemHolder extends InfoItemHolder { 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/util/text/TextEllipsizer.java b/app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java new file mode 100644 index 000000000..41084926b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java @@ -0,0 +1,195 @@ +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 caBeEllipsized = 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.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; + caBeEllipsized = null; + linkifyContentView(v -> { + final int currentMaxLines = view.getMaxLines(); + view.setMaxLines(EXPANDED_LINES); + caBeEllipsized = view.getLineCount() > maxLines; + view.setMaxLines(currentMaxLines); + if (onContentChanged != null) { + onContentChanged.accept(caBeEllipsized); + } + }); + } + + 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 ellipsed 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 caBeEllipsized; + } + + 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. + * The Boolean parameter is the new state. + * Ellipsized content is represented as {@code true}, + * normal or full content by {@code false}. + */ + public void setStateChangeListener(final Consumer listener) { + this.stateChangeListener = listener; + } + + public void removeStateChangeListener() { + this.stateChangeListener = null; + } + + private void notifyStateChangeListener(final boolean oldState) { + if (oldState != isEllipsized && stateChangeListener != null) { + stateChangeListener.accept(isEllipsized); + } + } + +} diff --git a/app/src/main/res/layout/playlist_header.xml b/app/src/main/res/layout/playlist_header.xml index 2d6f30676..c761240d9 100644 --- a/app/src/main/res/layout/playlist_header.xml +++ b/app/src/main/res/layout/playlist_header.xml @@ -87,12 +87,25 @@ android:layout_below="@id/playlist_meta" android:paddingHorizontal="@dimen/video_item_search_padding" android:paddingTop="6dp" - tools:text="This is a multiline playlist description. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit" /> + android:maxLines="5" + tools:text="This is a multiline playlist description. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit" /> + + + android:layout_below="@id/playlist_description_read_more"> %s reply %s replies + Show more + Show less From 9ff1b5230fda1411d031cca528a2b6febed788e4 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 23 Dec 2023 17:53:27 +0100 Subject: [PATCH 043/123] Improve TextEllipsizer class --- .../newpipe/util/text/TextEllipsizer.java | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) 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 index 41084926b..184b73304 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/TextEllipsizer.java @@ -33,7 +33,7 @@ public final class TextEllipsizer { @Nullable private StreamingService streamingService; @Nullable private String streamUrl; private boolean isEllipsized = false; - @Nullable private Boolean caBeEllipsized = null; + @Nullable private Boolean canBeEllipsized = null; @NonNull private final Paint paintAtContentSize = new Paint(); private final float ellipsisWidthPx; @@ -45,6 +45,7 @@ public final class TextEllipsizer { @Nullable final StreamingService streamingService) { this.view = view; this.maxLines = maxLines; + this.content = Description.EMPTY_DESCRIPTION; this.streamingService = streamingService; paintAtContentSize.setTextSize(view.getTextSize()); @@ -57,14 +58,14 @@ public final class TextEllipsizer { public void setContent(@NonNull final Description content) { this.content = content; - caBeEllipsized = null; + canBeEllipsized = null; linkifyContentView(v -> { final int currentMaxLines = view.getMaxLines(); view.setMaxLines(EXPANDED_LINES); - caBeEllipsized = view.getLineCount() > maxLines; + canBeEllipsized = view.getLineCount() > maxLines; view.setMaxLines(currentMaxLines); if (onContentChanged != null) { - onContentChanged.accept(caBeEllipsized); + onContentChanged.accept(canBeEllipsized); } }); } @@ -135,7 +136,7 @@ public final class TextEllipsizer { } /** - * Toggle the view between the ellipsed and expanded state. + * Toggle the view between the ellipsized and expanded state. */ public void toggle() { if (isEllipsized) { @@ -146,16 +147,17 @@ public final class TextEllipsizer { } /** - * 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. + * 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 caBeEllipsized; + return canBeEllipsized; } private void linkifyContentView(final Consumer consumer) { @@ -173,19 +175,15 @@ public final class TextEllipsizer { /** * 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. + * @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(final Consumer listener) { + public void setStateChangeListener(@Nullable final Consumer listener) { this.stateChangeListener = listener; } - public void removeStateChangeListener() { - this.stateChangeListener = null; - } - private void notifyStateChangeListener(final boolean oldState) { if (oldState != isEllipsized && stateChangeListener != null) { stateChangeListener.accept(isEllipsized); From 9220e324638284404d2bd234a460b0ce6bade4f1 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Dec 2023 10:54:31 +0100 Subject: [PATCH 044/123] Fix FeedDAOTest --- .../java/org/schabi/newpipe/database/FeedDAOTest.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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) { From 5a4dae207001da7e3bd3f65dcb7dfb89b6c82ae8 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Dec 2023 11:36:12 +0100 Subject: [PATCH 045/123] Fix settings_notification.xml indentation --- .../main/res/layout/settings_notification.xml | 111 +++++++++--------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/app/src/main/res/layout/settings_notification.xml b/app/src/main/res/layout/settings_notification.xml index d4af3fe71..e5d73a4ae 100644 --- a/app/src/main/res/layout/settings_notification.xml +++ b/app/src/main/res/layout/settings_notification.xml @@ -1,5 +1,4 @@ - + android:id="@+id/textView" + 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" /> - + - + - + - + - + - + From 30f0db1d28987805612325839432898d1ba77625 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Dec 2023 12:13:08 +0100 Subject: [PATCH 046/123] Customize only 2 notification actions on Android 13+ --- .../custom/NotificationActionsPreference.java | 34 +++++++++++++++---- .../main/res/layout/settings_notification.xml | 4 +-- app/src/main/res/values/strings.xml | 3 +- 3 files changed, 31 insertions(+), 10 deletions(-) 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..29a7c49a0 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 @@ -6,6 +6,7 @@ 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; @@ -35,6 +36,7 @@ import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.views.FocusOverlayView; import java.util.List; +import java.util.Objects; import java.util.stream.IntStream; public class NotificationActionsPreference extends Preference { @@ -56,6 +58,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); } @@ -137,11 +144,19 @@ public class NotificationActionsPreference extends Preference { NotificationSlot(final int actionIndex, final View parentView) { this.i = actionIndex; - + selectedAction = Objects.requireNonNull(getSharedPreferences()).getInt( + getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), + NotificationConstants.SLOT_DEFAULTS[i]); final View view = parentView.findViewById(SLOT_ITEMS[i]); - setupSelectedAction(view); - setupTitle(view); - setupCheckbox(view); + + // 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); + } else { + view.setVisibility(View.GONE); + } } void setupTitle(final View view) { @@ -153,6 +168,14 @@ public class NotificationActionsPreference extends Preference { void setupCheckbox(final View view) { 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 33+ + compactSlotCheckBox.setVisibility(View.GONE); + view.findViewById(R.id.notificationActionCheckBoxClickableArea) + .setVisibility(View.GONE); + return; + } + compactSlotCheckBox.setChecked(compactSlots.contains(i)); view.findViewById(R.id.notificationActionCheckBoxClickableArea).setOnClickListener( v -> { @@ -174,9 +197,6 @@ public class NotificationActionsPreference extends Preference { 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(); } diff --git a/app/src/main/res/layout/settings_notification.xml b/app/src/main/res/layout/settings_notification.xml index e5d73a4ae..b8195f18c 100644 --- a/app/src/main/res/layout/settings_notification.xml +++ b/app/src/main/res/layout/settings_notification.xml @@ -7,7 +7,7 @@ android:paddingTop="16dp"> + app:layout_constraintTop_toBottomOf="@+id/summary" /> 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 From aab6580195c2448b12a72b439550bd2ad2a3ed42 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Dec 2023 12:31:59 +0100 Subject: [PATCH 047/123] Extract NotificationSlot from NotificationActionsPreference --- .../custom/NotificationActionsPreference.java | 187 ++---------------- .../settings/custom/NotificationSlot.java | 172 ++++++++++++++++ 2 files changed, 193 insertions(+), 166 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java 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 29a7c49a0..43e9d6f0c 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,38 +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.List; -import java.util.Objects; import java.util.stream.IntStream; public class NotificationActionsPreference extends Preference { @@ -47,8 +31,9 @@ public class NotificationActionsPreference extends Preference { } - @Nullable private NotificationSlot[] notificationSlots = null; - @Nullable private List compactSlots = null; + private NotificationSlot[] notificationSlots; + private List compactSlots; + //////////////////////////////////////////////////////////////////////////// // Lifecycle @@ -85,10 +70,26 @@ public class NotificationActionsPreference extends Preference { compactSlots = NotificationConstants.getCompactSlotsFromPreferences(getContext(), getSharedPreferences(), 5); 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 @@ -106,156 +107,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; - selectedAction = Objects.requireNonNull(getSharedPreferences()).getInt( - getContext().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); - } 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 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 33+ - compactSlotCheckBox.setVisibility(View.GONE); - view.findViewById(R.id.notificationActionCheckBoxClickableArea) - .setVisibility(View.GONE); - return; - } - - 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); - 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..412215d0f --- /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 33+ + 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.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(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; + } +} From 9fb8125655060a343eb0a68d9a3fe69b01a28b9d Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Dec 2023 14:04:18 +0100 Subject: [PATCH 048/123] Allow each notification slot to contain any possible action --- .../notification/NotificationConstants.java | 37 +++++++--------- .../player/notification/NotificationUtil.java | 43 ++++++++++++------- .../custom/NotificationActionsPreference.java | 5 ++- .../settings/custom/NotificationSlot.java | 6 +-- 4 files changed, 48 insertions(+), 43 deletions(-) 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 3fa7c2623..551e2b863 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 @@ -23,6 +23,8 @@ 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; @@ -101,21 +103,7 @@ public final class NotificationUtil { new NotificationCompat.Builder(player.getContext(), player.getContext().getString(R.string.notification_channel_id)); - 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; - } - 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 int[] compactSlots = initializeNotificationSlots(); final MediaStyle mediaStyle = new MediaStyle().setShowActionsInCompactView(compactSlots); player.UIs() @@ -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") 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 43e9d6f0c..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 @@ -20,6 +20,7 @@ import org.schabi.newpipe.App; import org.schabi.newpipe.R; import org.schabi.newpipe.player.notification.NotificationConstants; +import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; @@ -67,8 +68,8 @@ 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(getContext(), getSharedPreferences(), i, view, compactSlots.contains(i), this::onToggleCompactSlot)) 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 index 412215d0f..074532876 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java +++ b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java @@ -130,13 +130,13 @@ class NotificationSlot { .create(); final View.OnClickListener radioButtonsClickListener = v -> { - selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][v.getId()]; + selectedAction = NotificationConstants.ALL_ACTIONS[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]; + for (int id = 0; id < NotificationConstants.ALL_ACTIONS.length; ++id) { + final int action = NotificationConstants.ALL_ACTIONS[id]; final RadioButton radioButton = ListRadioIconItemBinding.inflate(inflater) .getRoot(); From 2c4c28309900276bfa5e36a3cebbf0a16730a1a9 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 16 Nov 2023 09:47:27 +0100 Subject: [PATCH 049/123] Extract NotificationActionData from NotificationUtil --- .../notification/NotificationActionData.java | 168 ++++++++++++++++++ .../player/notification/NotificationUtil.java | 136 ++------------ 2 files changed, 184 insertions(+), 120 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java 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..fd5e03bf1 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java @@ -0,0 +1,168 @@ +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.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; + +public final class NotificationActionData { + @Nullable + private final String action; + @NonNull + private final String name; + @DrawableRes + private final int icon; + + public NotificationActionData(@Nullable final String action, @NonNull final String name, + @DrawableRes final int icon) { + this.action = action; + this.name = name; + this.icon = icon; + } + + @Nullable + public String action() { + return action; + } + + @NonNull + public String name() { + return name; + } + + @DrawableRes + public int icon() { + return icon; + } + + + @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) { + // null intent action -> show hourglass icon that does nothing when clicked + return new NotificationActionData(null, + 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; + } + } +} 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 551e2b863..72b979f9d 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; @@ -29,19 +32,6 @@ 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. */ @@ -238,115 +228,21 @@ 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; + final PendingIntent intent; + if (data.action() == null) { + intent = null; + } else { + intent = PendingIntentCompat.getBroadcast(player.getContext(), NOTIFICATION_ID, + new Intent(data.action()), FLAG_UPDATE_CURRENT, false); } - } - 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)); + builder.addAction(new NotificationCompat.Action(data.icon(), data.name(), intent)); } private Intent getIntentForNotification() { From 5edafca05ae485940094fb940a4fbac2886d4bd9 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 16 Nov 2023 11:04:01 +0100 Subject: [PATCH 050/123] Implement notification actions via MediaSessionConnector on Android 13+ --- .../mediasession/MediaSessionPlayerUi.java | 111 ++++++++++++++++++ .../SessionConnectorActionProvider.java | 51 ++++++++ .../player/notification/NotificationUtil.java | 18 ++- 3 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/mediasession/SessionConnectorActionProvider.java 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..53d6c297a 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,14 +16,20 @@ 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.ArrayList; +import java.util.List; import java.util.Optional; public class MediaSessionPlayerUi extends PlayerUi @@ -163,4 +171,107 @@ 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; + } + + final List actions = new ArrayList<>(2); + for (int i = 3; i < 5; ++i) { + // only use the fourth and fifth actions (the settings page also shows only the last 2) + final int action = player.getPrefs().getInt( + player.getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), + NotificationConstants.SLOT_DEFAULTS[i]); + + @Nullable final NotificationActionData data = + NotificationActionData.fromNotificationActionEnum(player, action); + + if (data != null) { + actions.add(new SessionConnectorActionProvider(data, context)); + } + } + + sessionConnector.setCustomActionProviders( + actions.toArray(new MediaSessionConnector.CustomActionProvider[0])); + } + + @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..7b109c149 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/SessionConnectorActionProvider.java @@ -0,0 +1,51 @@ +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) { + if (data.action() == null) { + return null; + } else { + return new PlaybackStateCompat.CustomAction.Builder( + data.action(), data.name(), data.icon() + ).build(); + } + } +} 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 72b979f9d..b3cfed1ce 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 @@ -92,15 +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(); - final int[] compactSlots = initializeNotificationSlots(); - - final MediaStyle mediaStyle = new MediaStyle().setShowActionsInCompactView(compactSlots); + // 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); + } 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) @@ -135,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); + } } From 17e88f17498234f50ea58ba22bc8c6ab72d6d673 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Dec 2023 15:08:44 +0100 Subject: [PATCH 051/123] Do not update notification actions if nothing changed This should avoid costly updates of the media session. --- .../mediasession/MediaSessionPlayerUi.java | 45 ++++++++++++------- .../notification/NotificationActionData.java | 17 ++++++- 2 files changed, 45 insertions(+), 17 deletions(-) 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 53d6c297a..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 @@ -28,9 +28,11 @@ import org.schabi.newpipe.player.ui.PlayerUi; import org.schabi.newpipe.player.ui.VideoPlayerUi; import org.schabi.newpipe.util.StreamTypeUtil; -import java.util.ArrayList; 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 { @@ -42,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 = @@ -71,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 @@ -88,6 +98,7 @@ public class MediaSessionPlayerUi extends PlayerUi mediaSession.release(); mediaSession = null; } + prevNotificationActions = List.of(); } @Override @@ -187,23 +198,25 @@ public class MediaSessionPlayerUi extends PlayerUi return; } - final List actions = new ArrayList<>(2); - for (int i = 3; i < 5; ++i) { - // only use the fourth and fifth actions (the settings page also shows only the last 2) - final int action = player.getPrefs().getInt( - player.getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), - NotificationConstants.SLOT_DEFAULTS[i]); + // 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()); - @Nullable final NotificationActionData data = - NotificationActionData.fromNotificationActionEnum(player, action); - - if (data != null) { - actions.add(new SessionConnectorActionProvider(data, context)); - } + // 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)); } - - sessionConnector.setCustomActionProviders( - actions.toArray(new MediaSessionConnector.CustomActionProvider[0])); } @Override 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 index fd5e03bf1..98ee3d7b8 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java @@ -20,6 +20,8 @@ import androidx.annotation.Nullable; import org.schabi.newpipe.R; import org.schabi.newpipe.player.Player; +import java.util.Objects; + public final class NotificationActionData { @Nullable private final String action; @@ -50,7 +52,6 @@ public final class NotificationActionData { return icon; } - @Nullable public static NotificationActionData fromNotificationActionEnum( @NonNull final Player player, @@ -165,4 +166,18 @@ public final class NotificationActionData { return null; } } + + + @Override + public boolean equals(@Nullable final Object obj) { + return (obj instanceof NotificationActionData other) + && Objects.equals(this.action, other.action) + && this.name.equals(other.name) + && this.icon == other.icon; + } + + @Override + public int hashCode() { + return Objects.hash(action, name, icon); + } } From 4b1824e8c16aed9725ba970ca00b8cae37171cf6 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Dec 2023 15:13:18 +0100 Subject: [PATCH 052/123] Allow play/pausing from notification when buffering This change is in line with a recent change in how the play/pause button behaves in the player ui: if the buffering indicator is shown, it's still possible to toggle play/pause, to allow e.g. pausing videos before they even start. This change was needed because on Android 13+ notification actions can't be null, and thus the buffering hourglass action wasn't shown. --- .../SessionConnectorActionProvider.java | 10 +++------- .../notification/NotificationActionData.java | 16 ++++++++++------ .../player/notification/NotificationUtil.java | 10 ++-------- 3 files changed, 15 insertions(+), 21 deletions(-) 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 index 7b109c149..a5c9fccc9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasession/SessionConnectorActionProvider.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/SessionConnectorActionProvider.java @@ -40,12 +40,8 @@ public class SessionConnectorActionProvider implements MediaSessionConnector.Cus @Nullable @Override public PlaybackStateCompat.CustomAction getCustomAction(@NonNull final Player player) { - if (data.action() == null) { - return null; - } else { - return new PlaybackStateCompat.CustomAction.Builder( - data.action(), data.name(), data.icon() - ).build(); - } + 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 index 98ee3d7b8..b3abcd0b5 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java @@ -11,6 +11,7 @@ import static org.schabi.newpipe.player.notification.NotificationConstants.ACTIO 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; @@ -23,21 +24,23 @@ import org.schabi.newpipe.player.Player; import java.util.Objects; public final class NotificationActionData { - @Nullable + + @NonNull private final String action; @NonNull private final String name; @DrawableRes private final int icon; - public NotificationActionData(@Nullable final String action, @NonNull final String name, + + public NotificationActionData(@NonNull final String action, @NonNull final String name, @DrawableRes final int icon) { this.action = action; this.name = name; this.icon = icon; } - @Nullable + @NonNull public String action() { return action; } @@ -52,6 +55,8 @@ public final class NotificationActionData { return icon; } + + @SuppressLint("PrivateResource") // we currently use Exoplayer's internal strings and icons @Nullable public static NotificationActionData fromNotificationActionEnum( @NonNull final Player player, @@ -105,8 +110,7 @@ public final class NotificationActionData { if (player.getCurrentState() == Player.STATE_PREFLIGHT || player.getCurrentState() == Player.STATE_BLOCKED || player.getCurrentState() == Player.STATE_BUFFERING) { - // null intent action -> show hourglass icon that does nothing when clicked - return new NotificationActionData(null, + return new NotificationActionData(ACTION_PLAY_PAUSE, ctx.getString(R.string.notification_action_buffering), R.drawable.ic_hourglass_top); } @@ -171,7 +175,7 @@ public final class NotificationActionData { @Override public boolean equals(@Nullable final Object obj) { return (obj instanceof NotificationActionData other) - && Objects.equals(this.action, other.action) + && this.action.equals(other.action) && this.name.equals(other.name) && this.icon == other.icon; } 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 b3cfed1ce..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 @@ -244,14 +244,8 @@ public final class NotificationUtil { return; } - final PendingIntent intent; - if (data.action() == null) { - intent = null; - } else { - intent = PendingIntentCompat.getBroadcast(player.getContext(), NOTIFICATION_ID, - new Intent(data.action()), 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)); } From f98548698af0e4d5a58c99a187b35c3e92d10119 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Dec 2023 21:55:32 +0100 Subject: [PATCH 053/123] Android 33 -> Android 13 Co-authored-by: Tobi --- .../org/schabi/newpipe/settings/custom/NotificationSlot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 074532876..981ba3e75 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java +++ b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationSlot.java @@ -89,7 +89,7 @@ class NotificationSlot { 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 33+ + // there are no compact slots to customize on Android 13+ compactSlotCheckBox.setVisibility(View.GONE); view.findViewById(R.id.notificationActionCheckBoxClickableArea) .setVisibility(View.GONE); From 04bdc1cc0bcb178be478ad1d60c1f5513d5c9bad Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Dec 2023 23:46:16 +0100 Subject: [PATCH 054/123] Base cache key on info type instead of item type It didn't really made sense to consider two cache keys as equal based on the type of items contained within that list. --- .../fragments/detail/VideoDetailFragment.java | 8 +-- .../schabi/newpipe/util/ExtractorHelper.java | 58 +++++++++++-------- .../org/schabi/newpipe/util/InfoCache.java | 42 +++++++++----- 3 files changed, 68 insertions(+), 40 deletions(-) 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 4da0a561e..bcdeb7ef7 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,7 +72,6 @@ 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; @@ -107,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; @@ -1445,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/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index c2748f725..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))); } @@ -175,7 +177,7 @@ public final class ExtractorHelper { 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))); } @@ -188,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))); } @@ -202,7 +205,7 @@ public final class ExtractorHelper { } /*////////////////////////////////////////////////////////////////////////// - // Utils + // Cache //////////////////////////////////////////////////////////////////////////*/ /** @@ -214,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(); @@ -246,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); } @@ -268,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)); } } From 55373c95d94262d98cb877d235dea1a2fca8dce9 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Dec 2023 23:49:09 +0100 Subject: [PATCH 055/123] Update NewPipeExtractor to include MediaCCC channel fix --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6b51f6029..36bb35dc9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,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.Stypox:NewPipeExtractor:aaf3231fc75d7b4177549fec4aa7e672bfe84015' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From 66e8e2a6968081abd850d81bc32fbacb7919ecaf Mon Sep 17 00:00:00 2001 From: Roshan Jossy Date: Mon, 1 Jan 2024 15:07:37 +0100 Subject: [PATCH 056/123] use GitHub markdown to emphasise warning in Readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9fabb5ac7..d159c77fc 100644 --- a/README.md +++ b/README.md @@ -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 From 575e8090043dde229bf9c8b2057c869042a0e6a7 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Sat, 6 Jan 2024 17:24:53 +0100 Subject: [PATCH 057/123] Don't write defaultFormat setting, use default value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nowhere else does this (write a setting if it’s not set). It took me a while to see that this code does not do what it intends, because `defaultFormat` is already the default value in the first `context.getString`, so calling `getMediaFormatFromKey` again is the exact same call (“do you know the definition of insanity…”) and will return `null` again … So let’s drop the setting write and just rely on the default values. --- .../java/org/schabi/newpipe/util/ListHelper.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) 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..7cb8f1f37 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; From 2051334bba247422dc8203fa10f89f998ee8642a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 8 Jan 2024 11:55:57 +0800 Subject: [PATCH 058/123] Bump GH actions Old ones are deprecated. --- .github/workflows/ci.yml | 16 ++++++++-------- .github/workflows/image-minimizer.yml | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03b04b7c4..4800f8578 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: contents: read steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: gradle/wrapper-validation-action@v1 - name: create and checkout 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,12 +111,12 @@ 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" 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: | From 32d2606a6544961ecd8766ea445c729c301ed14f Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Sat, 20 Jan 2024 22:26:24 +0100 Subject: [PATCH 059/123] BaseDescriptionFragment: Assert member is initialized `streamInfo` and `channelInfo` have to be initialized, since the only way to construct the class it to pass them. So we can remove the null check boilerplate and make some of the accessors `NonNull`. --- .../detail/BaseDescriptionFragment.java | 26 +++++++-------- .../fragments/detail/DescriptionFragment.java | 26 +++------------ .../list/channel/ChannelAboutFragment.java | 33 +++++++------------ 3 files changed, 27 insertions(+), 58 deletions(-) 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..ba233559c 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,10 +24,8 @@ import icepick.State; public class DescriptionFragment extends BaseDescriptionFragment { @State - StreamInfo streamInfo = null; + StreamInfo streamInfo; - public DescriptionFragment() { - } public DescriptionFragment(final StreamInfo streamInfo) { this.streamInfo = streamInfo; @@ -35,44 +34,29 @@ public class DescriptionFragment extends BaseDescriptionFragment { @Nullable @Override protected Description getDescription() { - if (streamInfo == null) { - return null; - } return streamInfo.getDescription(); } - @Nullable + @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/list/channel/ChannelAboutFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java index 56418800d..30c3aac5a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java @@ -2,12 +2,12 @@ package org.schabi.newpipe.fragments.list.channel; import static org.schabi.newpipe.extractor.stream.StreamExtractor.UNKNOWN_SUBSCRIBER_COUNT; -import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.schabi.newpipe.R; @@ -26,15 +26,12 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { @State protected ChannelInfo channelInfo; - public static ChannelAboutFragment getInstance(final ChannelInfo channelInfo) { + public static ChannelAboutFragment getInstance(final @NonNull ChannelInfo channelInfo) { final ChannelAboutFragment fragment = new ChannelAboutFragment(); fragment.channelInfo = channelInfo; return fragment; } - public ChannelAboutFragment() { - super(); - } @Override protected void initViews(final View rootView, final Bundle savedInstanceState) { @@ -45,26 +42,20 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { @Nullable @Override protected Description getDescription() { - if (channelInfo == null) { - return null; - } - return new Description(channelInfo.getDescription(), Description.PLAIN_TEXT); + return new Description( + channelInfo.getDescription(), + Description.PLAIN_TEXT + ); } - @Nullable + @NonNull @Override protected StreamingService getService() { - if (channelInfo == null) { - return null; - } return channelInfo.getService(); } @Override protected int getServiceId() { - if (channelInfo == null) { - return -1; - } return channelInfo.getServiceId(); } @@ -74,12 +65,9 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { return null; } - @Nullable + @NonNull @Override public List getTags() { - if (channelInfo == null) { - return null; - } return channelInfo.getTags(); } @@ -93,10 +81,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, From 6dd62335e923cd5fd57d2d48d6790805897816a2 Mon Sep 17 00:00:00 2001 From: opusforlife2 <53176348+opusforlife2@users.noreply.github.com> Date: Sat, 27 Jan 2024 16:36:13 +0000 Subject: [PATCH 060/123] Update Matrix room URL to new link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fabb5ac7..cec679a58 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ - +


ScreenshotsSupported ServicesDescriptionFeaturesInstallation and updatesContributionDonateLicense

From 27730a20d6c76725cd14dc7f00ecf146995fdcc9 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Mon, 5 Feb 2024 10:52:08 +0530 Subject: [PATCH 061/123] Update Jsoup to 1.17.2 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6b51f6029..60414e0e5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -238,7 +238,7 @@ dependencies { kapt "frankiesardo:icepick-processor:${icepickVersion}" // HTML parser - implementation "org.jsoup:jsoup:1.16.2" + implementation "org.jsoup:jsoup:1.17.2" // HTTP client implementation "com.squareup.okhttp3:okhttp:4.12.0" From 9d6ac67c46a6cc55e03dec0599d8070f7430098d Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Mon, 18 Mar 2024 14:43:16 +0900 Subject: [PATCH 062/123] Update TextLinkifier.java minor fix --- .../main/java/org/schabi/newpipe/util/text/TextLinkifier.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From e6a4a3fa4f1f1c8cee90a207efd5f8896dcbc25c Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Mon, 18 Mar 2024 09:59:34 +0100 Subject: [PATCH 063/123] Translated using Weblate (Danish) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 96.4% (703 of 729 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Danish) Currently translated at 96.4% (703 of 729 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Danish) Currently translated at 88.3% (644 of 729 strings) Translated using Weblate (Danish) Currently translated at 88.3% (644 of 729 strings) Translated using Weblate (Belarusian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Russian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Georgian) Currently translated at 92.2% (71 of 77 strings) Translated using Weblate (Uzbek (latin)) Currently translated at 62.6% (457 of 729 strings) Translated using Weblate (Santali) Currently translated at 12.6% (92 of 729 strings) Translated using Weblate (French) Currently translated at 89.6% (69 of 77 strings) Translated using Weblate (Japanese) Currently translated at 11.6% (9 of 77 strings) Translated using Weblate (Bulgarian) Currently translated at 5.1% (4 of 77 strings) Translated using Weblate (Bengali) Currently translated at 20.7% (16 of 77 strings) Translated using Weblate (German) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Bengali (India)) Currently translated at 40.7% (297 of 729 strings) Translated using Weblate (Kurdish (Central)) Currently translated at 85.5% (624 of 729 strings) Translated using Weblate (Tamil) Currently translated at 46.6% (340 of 729 strings) Translated using Weblate (Bengali (Bangladesh)) Currently translated at 55.1% (402 of 729 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Greek) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (German) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Portuguese (Portugal)) Currently translated at 98.7% (76 of 77 strings) Translated using Weblate (Hebrew) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Hebrew) Currently translated at 99.4% (725 of 729 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Croatian) Currently translated at 99.4% (725 of 729 strings) Translated using Weblate (ryu (generated) (ryu)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Croatian) Currently translated at 2.5% (2 of 77 strings) Translated using Weblate (Malay) Currently translated at 48.6% (355 of 729 strings) Translated using Weblate (Croatian) Currently translated at 99.3% (724 of 729 strings) Translated using Weblate (Romanian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Finnish) Currently translated at 98.3% (717 of 729 strings) Translated using Weblate (Hungarian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 22.0% (17 of 77 strings) Translated using Weblate (Norwegian Bokmål) Currently translated at 95.4% (696 of 729 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Vietnamese) Currently translated at 42.8% (33 of 77 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Esperanto) Currently translated at 70.7% (516 of 729 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Azerbaijani) Currently translated at 94.6% (690 of 729 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (French) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Santali) Currently translated at 10.0% (73 of 729 strings) Translated using Weblate (Turkish) Currently translated at 42.8% (33 of 77 strings) Translated using Weblate (German) Currently translated at 81.8% (63 of 77 strings) Translated using Weblate (Portuguese (Portugal)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Kannada) Currently translated at 5.4% (40 of 729 strings) Translated using Weblate (Russian) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Croatian) Currently translated at 88.7% (647 of 729 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Odia) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Slovak) Currently translated at 20.7% (16 of 77 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (French) Currently translated at 99.8% (728 of 729 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Catalan) Currently translated at 86.5% (631 of 729 strings) Translated using Weblate (Telugu) Currently translated at 58.9% (430 of 729 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Greek) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Tigrinya) Currently translated at 8.9% (65 of 729 strings) Translated using Weblate (Russian) Currently translated at 98.7% (76 of 77 strings) Translated using Weblate (Belarusian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Slovak) Currently translated at 99.7% (727 of 729 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Italian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Russian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Odia) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Kurdish (Central)) Currently translated at 85.5% (624 of 729 strings) Translated using Weblate (Estonian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Japanese) Currently translated at 99.8% (728 of 729 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (German) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Italian) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Punjabi) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Czech) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Portuguese (Portugal)) Currently translated at 99.8% (728 of 729 strings) Translated using Weblate (Punjabi) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Swedish) Currently translated at 99.8% (728 of 729 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Polish) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Czech) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Portuguese) Currently translated at 99.8% (728 of 729 strings) Translated using Weblate (Italian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (German) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (German) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (ryu (generated) (ryu)) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Serbian) Currently translated at 18.1% (14 of 77 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (77 of 77 strings) Translated using Weblate (Greek) Currently translated at 24.6% (19 of 77 strings) Translated using Weblate (Estonian) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Polish) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Greek) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (German) Currently translated at 100.0% (728 of 728 strings) Translated using Weblate (ryu (generated) (ryu)) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Kannada) Currently translated at 5.5% (40 of 726 strings) Translated using Weblate (Sinhala) Currently translated at 2.6% (2 of 76 strings) Translated using Weblate (Sinhala) Currently translated at 4.1% (30 of 726 strings) Translated using Weblate (Bulgarian) Currently translated at 5.2% (4 of 76 strings) Translated using Weblate (Punjabi) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Turkish) Currently translated at 99.8% (725 of 726 strings) Translated using Weblate (Slovak) Currently translated at 98.4% (715 of 726 strings) Translated using Weblate (Italian) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (French) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Polish) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Greek) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Italian) Currently translated at 99.5% (723 of 726 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Czech) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (German) Currently translated at 100.0% (726 of 726 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 21.0% (16 of 76 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (725 of 725 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (725 of 725 strings) Translated using Weblate (Romanian) Currently translated at 99.8% (724 of 725 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (725 of 725 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (725 of 725 strings) Co-authored-by: Agnieszka C Co-authored-by: Alex25820 Co-authored-by: Alexthegib Co-authored-by: Allan Nordhøy Co-authored-by: Andrey F Co-authored-by: Angelk90 Co-authored-by: Chethan <76928501+ch3thanhs@users.noreply.github.com> Co-authored-by: Danr Co-authored-by: David Svane Co-authored-by: Deleted User Co-authored-by: DuninduH Co-authored-by: Eric Co-authored-by: Fjuro Co-authored-by: GET100PERCENT Co-authored-by: Ghost of Sparta Co-authored-by: Heidhou chazanouvha Co-authored-by: Hosted Weblate Co-authored-by: Igor Rückert Co-authored-by: Igor Sorocean Co-authored-by: Ihfandi Co-authored-by: Ihor Hordiichuk Co-authored-by: Jan Layola Co-authored-by: Jeff Huang Co-authored-by: Juan Martinez Co-authored-by: KarmaKat Co-authored-by: Kuko Co-authored-by: LiJu09 Co-authored-by: Martin Constantino–Bodin Co-authored-by: Mehmet Co-authored-by: Michalis Co-authored-by: Milan Co-authored-by: Milo Ivir Co-authored-by: NEXI Co-authored-by: Nils Van Zuijlen Co-authored-by: Nista <42772160+Nista11@users.noreply.github.com> Co-authored-by: Oğuz Ersen Co-authored-by: P.O Co-authored-by: Philip Goto Co-authored-by: Pi-Cla Co-authored-by: Prasanta-Hembram Co-authored-by: Priit Jõerüüt Co-authored-by: Random Co-authored-by: Ray Co-authored-by: Rex_sa Co-authored-by: Scrambled777 Co-authored-by: Sergio Marques Co-authored-by: ShareASmile Co-authored-by: Subbarayudu Co-authored-by: Subham Jena Co-authored-by: T1z3n Co-authored-by: Terry Louwers Co-authored-by: TobiGr Co-authored-by: Vasilis K Co-authored-by: VfBFan Co-authored-by: Xəyyam Qocayev Co-authored-by: Yaron Shahrabani Co-authored-by: bittin1ddc447d824349b2 Co-authored-by: cat <158170307+cultcats@users.noreply.github.com> Co-authored-by: ds-z Co-authored-by: dyare darbani Co-authored-by: fsbat0 Co-authored-by: gallegonovato Co-authored-by: hshbuk Co-authored-by: jspast Co-authored-by: kuragehime Co-authored-by: ngocanhtve Co-authored-by: pjammo Co-authored-by: ssantos Co-authored-by: trunars Co-authored-by: v1s7 Co-authored-by: Åzze Co-authored-by: Çağla Pickaxe Co-authored-by: Макар Разин Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ar/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/bg/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/bn/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/cs/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/de/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/el/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/es/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/fr/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hi/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hr/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/id/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/it/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ja/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ka/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pa/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pt/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pt_PT/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ru/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/si/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sk/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sr/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sv/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/tr/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/uk/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/vi/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/zh_Hant_HK/ Translation: NewPipe/Metadata --- app/src/main/res/values-ar/strings.xml | 13 +- app/src/main/res/values-az/strings.xml | 3 +- app/src/main/res/values-b+uz+Latn/strings.xml | 2 +- app/src/main/res/values-be/strings.xml | 125 ++-- app/src/main/res/values-bn-rBD/strings.xml | 6 +- app/src/main/res/values-bn-rIN/strings.xml | 2 +- app/src/main/res/values-ca/strings.xml | 23 +- app/src/main/res/values-ckb/strings.xml | 60 +- app/src/main/res/values-cs/strings.xml | 10 +- app/src/main/res/values-da/strings.xml | 228 +++--- app/src/main/res/values-de/strings.xml | 13 +- app/src/main/res/values-el/strings.xml | 13 +- app/src/main/res/values-eo/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 12 +- app/src/main/res/values-et/strings.xml | 9 +- app/src/main/res/values-fi/strings.xml | 9 +- app/src/main/res/values-fr/strings.xml | 10 +- app/src/main/res/values-he/strings.xml | 10 +- app/src/main/res/values-hi/strings.xml | 13 +- app/src/main/res/values-hr/strings.xml | 213 ++++-- app/src/main/res/values-hu/strings.xml | 53 +- app/src/main/res/values-in/strings.xml | 42 +- app/src/main/res/values-it/strings.xml | 12 +- app/src/main/res/values-ja/strings.xml | 8 +- app/src/main/res/values-kn/strings.xml | 43 +- app/src/main/res/values-ms/strings.xml | 4 + app/src/main/res/values-nb-rNO/strings.xml | 30 +- app/src/main/res/values-nl/strings.xml | 15 +- app/src/main/res/values-or/strings.xml | 11 +- app/src/main/res/values-pa/strings.xml | 9 +- app/src/main/res/values-pl/strings.xml | 9 + app/src/main/res/values-pt-rBR/strings.xml | 11 +- app/src/main/res/values-pt-rPT/strings.xml | 10 +- app/src/main/res/values-pt/strings.xml | 234 +++--- app/src/main/res/values-ro/strings.xml | 19 +- app/src/main/res/values-ru/strings.xml | 15 +- app/src/main/res/values-ryu/strings.xml | 23 +- app/src/main/res/values-sat/strings.xml | 687 +++++++++++++++++- app/src/main/res/values-si/strings.xml | 7 + app/src/main/res/values-sk/strings.xml | 149 ++-- app/src/main/res/values-sr/strings.xml | 30 +- app/src/main/res/values-sv/strings.xml | 9 +- app/src/main/res/values-ta/strings.xml | 4 +- app/src/main/res/values-te/strings.xml | 13 +- app/src/main/res/values-ti/strings.xml | 7 +- app/src/main/res/values-tr/strings.xml | 51 +- app/src/main/res/values-uk/strings.xml | 13 +- app/src/main/res/values-vi/strings.xml | 10 +- app/src/main/res/values-zh-rCN/strings.xml | 8 +- app/src/main/res/values-zh-rHK/strings.xml | 14 +- app/src/main/res/values-zh-rTW/strings.xml | 8 +- .../metadata/android/ar/changelogs/996.txt | 2 + .../metadata/android/bg/changelogs/64.txt | 6 + .../metadata/android/bn/changelogs/64.txt | 2 +- .../metadata/android/cs/changelogs/996.txt | 2 + .../metadata/android/de/changelogs/65.txt | 4 +- .../metadata/android/de/changelogs/66.txt | 18 + .../metadata/android/de/changelogs/68.txt | 19 + .../metadata/android/de/changelogs/69.txt | 14 + .../metadata/android/de/changelogs/70.txt | 10 + .../metadata/android/de/changelogs/71.txt | 8 + .../metadata/android/de/changelogs/740.txt | 12 + .../metadata/android/de/changelogs/750.txt | 15 + .../metadata/android/de/changelogs/760.txt | 24 + .../metadata/android/de/changelogs/780.txt | 11 + .../metadata/android/de/changelogs/790.txt | 13 + .../metadata/android/de/changelogs/800.txt | 11 + .../metadata/android/de/changelogs/810.txt | 11 + .../metadata/android/de/changelogs/840.txt | 10 + .../metadata/android/de/changelogs/930.txt | 12 + .../metadata/android/de/changelogs/940.txt | 1 - .../metadata/android/de/changelogs/951.txt | 1 - .../metadata/android/de/changelogs/990.txt | 2 - .../metadata/android/de/changelogs/995.txt | 4 +- .../metadata/android/de/changelogs/996.txt | 2 + .../metadata/android/de/full_description.txt | 2 +- .../metadata/android/de/short_description.txt | 2 +- .../metadata/android/el/changelogs/65.txt | 26 + .../metadata/android/el/changelogs/963.txt | 1 + .../metadata/android/el/changelogs/996.txt | 2 + .../metadata/android/es/changelogs/996.txt | 2 + .../metadata/android/fr/changelogs/750.txt | 6 +- .../metadata/android/hi/changelogs/996.txt | 2 + .../metadata/android/hr/full_description.txt | 2 +- .../metadata/android/id/changelogs/66.txt | 12 +- .../metadata/android/id/changelogs/972.txt | 2 +- .../metadata/android/id/changelogs/975.txt | 28 +- .../metadata/android/id/changelogs/976.txt | 6 +- .../metadata/android/id/changelogs/995.txt | 12 +- .../metadata/android/id/changelogs/996.txt | 2 + .../metadata/android/it/changelogs/996.txt | 2 + .../metadata/android/ja/changelogs/954.txt | 2 +- .../metadata/android/ka/changelogs/64.txt | 2 +- .../metadata/android/ka/changelogs/69.txt | 2 +- .../metadata/android/ka/changelogs/740.txt | 6 +- .../metadata/android/ka/changelogs/850.txt | 2 +- .../metadata/android/ka/changelogs/967.txt | 2 +- .../metadata/android/ka/changelogs/978.txt | 2 +- .../metadata/android/ka/changelogs/988.txt | 2 +- .../metadata/android/ka/full_description.txt | 2 +- .../metadata/android/pa/changelogs/996.txt | 2 + .../metadata/android/pt-PT/changelogs/995.txt | 16 + .../metadata/android/pt/changelogs/995.txt | 10 +- .../metadata/android/pt/changelogs/996.txt | 2 + .../metadata/android/ru/changelogs/995.txt | 16 +- .../metadata/android/ru/changelogs/996.txt | 2 + .../metadata/android/si/full_description.txt | 1 + .../metadata/android/si/short_description.txt | 1 + .../metadata/android/sk/changelogs/995.txt | 16 + .../metadata/android/sk/changelogs/996.txt | 2 + .../metadata/android/sr/changelogs/996.txt | 2 + .../metadata/android/sv/changelogs/63.txt | 4 +- .../metadata/android/sv/changelogs/64.txt | 6 +- .../metadata/android/sv/changelogs/957.txt | 10 + .../metadata/android/sv/changelogs/958.txt | 10 + .../metadata/android/sv/changelogs/961.txt | 12 + .../metadata/android/sv/changelogs/964.txt | 7 + .../metadata/android/sv/changelogs/965.txt | 6 + .../metadata/android/sv/changelogs/966.txt | 14 + .../metadata/android/sv/changelogs/968.txt | 7 + .../metadata/android/sv/changelogs/970.txt | 11 + .../metadata/android/sv/changelogs/975.txt | 15 + .../metadata/android/sv/changelogs/976.txt | 9 + .../metadata/android/sv/changelogs/983.txt | 9 + .../metadata/android/sv/changelogs/986.txt | 15 + .../metadata/android/sv/changelogs/987.txt | 12 + .../metadata/android/sv/changelogs/988.txt | 2 + .../metadata/android/sv/changelogs/989.txt | 3 + .../metadata/android/sv/changelogs/990.txt | 15 + .../metadata/android/sv/changelogs/991.txt | 13 + .../metadata/android/sv/changelogs/992.txt | 17 + .../metadata/android/sv/changelogs/993.txt | 12 + .../metadata/android/sv/changelogs/994.txt | 15 + .../metadata/android/sv/changelogs/995.txt | 16 + .../metadata/android/sv/changelogs/996.txt | 2 + .../metadata/android/sv/short_description.txt | 2 +- .../metadata/android/tr/full_description.txt | 4 +- .../metadata/android/uk/changelogs/996.txt | 2 + .../metadata/android/vi/changelogs/996.txt | 2 + .../android/zh_Hant_HK/changelogs/995.txt | 8 +- .../android/zh_Hant_HK/changelogs/996.txt | 2 + 141 files changed, 2410 insertions(+), 600 deletions(-) create mode 100644 fastlane/metadata/android/ar/changelogs/996.txt create mode 100644 fastlane/metadata/android/bg/changelogs/64.txt create mode 100644 fastlane/metadata/android/cs/changelogs/996.txt create mode 100644 fastlane/metadata/android/de/changelogs/66.txt create mode 100644 fastlane/metadata/android/de/changelogs/68.txt create mode 100644 fastlane/metadata/android/de/changelogs/69.txt create mode 100644 fastlane/metadata/android/de/changelogs/70.txt create mode 100644 fastlane/metadata/android/de/changelogs/71.txt create mode 100644 fastlane/metadata/android/de/changelogs/740.txt create mode 100644 fastlane/metadata/android/de/changelogs/750.txt create mode 100644 fastlane/metadata/android/de/changelogs/760.txt create mode 100644 fastlane/metadata/android/de/changelogs/780.txt create mode 100644 fastlane/metadata/android/de/changelogs/790.txt create mode 100644 fastlane/metadata/android/de/changelogs/800.txt create mode 100644 fastlane/metadata/android/de/changelogs/810.txt create mode 100644 fastlane/metadata/android/de/changelogs/840.txt create mode 100644 fastlane/metadata/android/de/changelogs/930.txt create mode 100644 fastlane/metadata/android/de/changelogs/996.txt create mode 100644 fastlane/metadata/android/el/changelogs/65.txt create mode 100644 fastlane/metadata/android/el/changelogs/963.txt create mode 100644 fastlane/metadata/android/el/changelogs/996.txt create mode 100644 fastlane/metadata/android/es/changelogs/996.txt create mode 100644 fastlane/metadata/android/hi/changelogs/996.txt create mode 100644 fastlane/metadata/android/id/changelogs/996.txt create mode 100644 fastlane/metadata/android/it/changelogs/996.txt create mode 100644 fastlane/metadata/android/pa/changelogs/996.txt create mode 100644 fastlane/metadata/android/pt-PT/changelogs/995.txt create mode 100644 fastlane/metadata/android/pt/changelogs/996.txt create mode 100644 fastlane/metadata/android/ru/changelogs/996.txt create mode 100644 fastlane/metadata/android/si/full_description.txt create mode 100644 fastlane/metadata/android/si/short_description.txt create mode 100644 fastlane/metadata/android/sk/changelogs/995.txt create mode 100644 fastlane/metadata/android/sk/changelogs/996.txt create mode 100644 fastlane/metadata/android/sr/changelogs/996.txt create mode 100644 fastlane/metadata/android/sv/changelogs/957.txt create mode 100644 fastlane/metadata/android/sv/changelogs/958.txt create mode 100644 fastlane/metadata/android/sv/changelogs/961.txt create mode 100644 fastlane/metadata/android/sv/changelogs/964.txt create mode 100644 fastlane/metadata/android/sv/changelogs/965.txt create mode 100644 fastlane/metadata/android/sv/changelogs/966.txt create mode 100644 fastlane/metadata/android/sv/changelogs/968.txt create mode 100644 fastlane/metadata/android/sv/changelogs/970.txt create mode 100644 fastlane/metadata/android/sv/changelogs/975.txt create mode 100644 fastlane/metadata/android/sv/changelogs/976.txt create mode 100644 fastlane/metadata/android/sv/changelogs/983.txt create mode 100644 fastlane/metadata/android/sv/changelogs/986.txt create mode 100644 fastlane/metadata/android/sv/changelogs/987.txt create mode 100644 fastlane/metadata/android/sv/changelogs/988.txt create mode 100644 fastlane/metadata/android/sv/changelogs/989.txt create mode 100644 fastlane/metadata/android/sv/changelogs/990.txt create mode 100644 fastlane/metadata/android/sv/changelogs/991.txt create mode 100644 fastlane/metadata/android/sv/changelogs/992.txt create mode 100644 fastlane/metadata/android/sv/changelogs/993.txt create mode 100644 fastlane/metadata/android/sv/changelogs/994.txt create mode 100644 fastlane/metadata/android/sv/changelogs/995.txt create mode 100644 fastlane/metadata/android/sv/changelogs/996.txt create mode 100644 fastlane/metadata/android/uk/changelogs/996.txt create mode 100644 fastlane/metadata/android/vi/changelogs/996.txt create mode 100644 fastlane/metadata/android/zh_Hant_HK/changelogs/996.txt diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 6bfe11eaa..ce62198cd 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -584,7 +584,7 @@ خلط تكرار يمكنك تحديد ثلاثة إجراءات كحد أقصى لإظهارها في الإشعار المضغوط! - قم بتحرير كل إشعار أدناه من خلال النقر عليه. حدد ما يصل إلى ثلاثة منها لتظهر في الإشعار المضغوط باستخدام مربعات الاختيار الموجودة على اليمين + قم بتحرير كل إجراء إعلام أدناه من خلال النقر عليه. حدد ما يصل إلى ثلاثة منها ليتم عرضها في الإشعار المضغوط باستخدام مربعات الاختيار الموجودة على اليمين. زر الإجراء الخامس زر الإجراء الرابع زر الإجراء الثالث @@ -858,4 +858,15 @@ مشاركة قائمة التشغيل شارك تفاصيل قائمة التشغيل مثل اسم قائمة التشغيل وعناوين الفيديو أو كقائمة بسيطة من عناوين URL للفيديو - %1$s: %2$s + + رد %s + رد %s + ردان%s + ردود%s + ردود %s + ردود %s + + عرض المزيد + عرض أقل + قم بتحرير كل إجراء إعلام أدناه من خلال النقر عليه. يتم تعيين الإجراءات الثلاثة الأولى (تشغيل/إيقاف مؤقت، السابق والتالي) بواسطة النظام ولا يمكن تخصيصها. \ 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..da08069ac 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -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+uz+Latn/strings.xml b/app/src/main/res/values-b+uz+Latn/strings.xml index 0ba69ae41..3bd940d11 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 diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 38905ed7f..ed24aac81 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,10 +394,10 @@ Ачысціць дадзеныя Пазіцыі прайгравання выдалены Файл перамешчаны ці выдалены - Файл з такім імем ужо існуе - Файл з такім імем ужо існуе + Файл з такім імем ўжо існуе + Файл з такім імем ўжо існуе немагчыма перазапісаць файл - У чарзе ўжо ёсць загрузка з такім імем + Ў чарзе ўжо ёсць загрузка з такім імем 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 выбраны @@ -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,13 @@ Каналы Папярэдні стрым Жывая трансляцыя + + %s адказ + %s адказы + %s адказаў + %s адказаў + + Паказаць больш + Паказаць менш + Адрэдагуйце кожнае дзеянне апавяшчэння, націснуўшы на яго. Першыя тры дзеянні (прайграванне/паўза, папярэдняе і наступнае) задаюцца сістэмай і не могуць быць зменены. \ No newline at end of file diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index 11fe2c2c8..4f27d60ce 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -297,7 +297,7 @@ থাম্বনেল ১:১ অনুপাতে সেট করো সিস্টেম ডিফল্ট প্লেলিস্ট বুকমার্ক করুন - "যখন পর্যাপ্ত নিবেদিত ফিড থেকে ডাটা সংগ্রহ করুন" + যখন পর্যাপ্ত নিবেদিত ফিড থেকে ডাটা সংগ্রহ করুন সবসময় হালনগাদ করুন শেষ হালনাগাদের পর একটি সাবস্ক্রিপশনের আগের সময় সেকেলে বিবেচিত — %s ফিড হালনাগাদ প্রবেশস্থল @@ -317,11 +317,11 @@ চ্যানেল গ্রুপ %d দিন - "%d দিন" + %d দিন %d ঘন্টা - "%d ঘন্টা" + %d ঘন্টা %d মিনিট diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml index b2eacd22a..cb5c0e595 100644 --- a/app/src/main/res/values-bn-rIN/strings.xml +++ b/app/src/main/res/values-bn-rIN/strings.xml @@ -275,7 +275,7 @@ নিউ পাইপ ওয়েব সাইট এ যান বিস্তারিত বিবরণ ও খবর এর জন্য ১০০+ ভিডিও - "%s শ্রোতা" + %s শ্রোতা %s শ্রোতা গন বিবরণ diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index a0dbc0b36..6f4724f3b 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ó: @@ -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ó @@ -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..3cfa5ef7b 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -16,7 +16,7 @@ پڕۆژەی نیوپایپ زانیارییە تایبەتییەکانت بە وردی دەپارێزێت. هەروەها به‌رنامه‌كه‌ هیچ زانایارییەکت بەبێ ئاگاداری تۆ بەکارنابات. \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` . هه‌ڵه‌ له‌ باركردنی فیید - ئه‌م تایبه‌تمه‌ندییه‌ كارابكه‌ گه‌ر ڕوونمای ڕه‌ش یاخوود جامبوونی کارپێکەرت ئه‌زموون كرد + ئەگەر تووشی شاشەی ڕەش یان لکەلکە بوویت لە کاتی پەخشکردنی ڤیدیۆدا، تونێلکردنی میدیا لەکاربخە. ناوەکی تایبەتی خشتەنەکراو @@ -665,11 +665,11 @@ لێدوانی هەڵواسراو کڕاش کردنی لێدەر پیشاندانی هەڵەی سناکباڕ - هیچ ڕێکخەرێکی فایلی گونجاو نەدۆزرایەوە بۆ ئەم کردارە. -\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..1a5b83e52 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -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 @@ -819,4 +819,12 @@ 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ě \ 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..f0ffba311 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -4,64 +4,64 @@ Udgivet den %1$s Ingen streamafspiller blev fundet. Installér VLC\? Ingen streamafspiller fundet (du kan installere VLC for at afspille den). - Installer - Annuller + 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 for 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 Lyd Standardformat for lydfiler Standardformat for videofiler 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 + Slet metadata-cache + Fjern alle cached websidedata Metadata-cache slettet - Føj automatisk næste stream til køen + 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,63 +71,63 @@ 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 [Ukendt] Skift til baggrund - Skift til pop op + Skift til popup Skift til hovedafspiller - Importer database - Eksporter database + Importér database + Eksportér database Overskriver din nuværende historik, abonnementer, spillelister og (hvis det ønskes) indstillinger - Eksporter historik, abonnementer, spillelister og indstillinger - Slet visningshistorik + Eksportér historik, abonnementer, spillelister og indstillinger + Ryd visningshistorik Sletter historikken over afspillede streams og afspilningspositionerne Slet hele visningshistorikken\? Visningshistorikken blev slettet - Slet søgehistorik + Ryd søgehistorik Sletter historikken for søgeord Slet hele søgehistorikken\? Søgehistorikken blev slettet @@ -367,12 +367,12 @@ Maksimalt antal forsøg før downloaden opgives 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 @@ -426,42 +426,42 @@ Slet alle downloadede filer fra drevet\? Sæt downloads på pause - Start hovedafspilleren i fuldskærmstilstand + Start hovedafspiller i fuld skærm 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 + 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 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 + 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 videobeskrivelsen og yderligere information - Slå fra for at gemme metainformationskasser med yderligere information om streammets skaber, streammets indhold eller en søgeforespørgsel + 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\? + 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? Videohashfunktion notifikation Notifikationer om videohashfunktioners status Fejlrapport-notifikation @@ -479,22 +479,22 @@ Kopier en formatteret rapport Giv tilladelse til at vise over andre apps Vis indikatorer for afspilningsposition i lister - Afspilningspositioner slettet - Ryd reCAPTCHA cookies + Afspilningspositioner blev slettet + 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 + 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 som kan vises i den lille notifikation! - Buffer + 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ø + Spørg om bekræftelse, før du rydder en kø Forhåndsvisning af miniaturebilleder på statuslinjen Sæt i kø som næste Er sat som næste i køen @@ -661,7 +661,7 @@ Ukendt format Ukendt kvalitet Hjertemarkeret af indholdsskaberen - Intervalstørrelse for afspilningsindlæsning + Størrelse på afspilningsinterval ExoPlayer-standard Tomt gruppenavn Du vil blive spurgt, hvor du vil gemme hver enkelt download. @@ -713,7 +713,7 @@ 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 @@ -721,4 +721,68 @@ Brug det originale lydspor uanset sprog Foretræk lydbeskrivelser Foretræk original lyd + Brug lydbeskrivelser for personer med nedsat syn, hvis tilgængeligt + 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 brugerdefineres. + 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 playliste navn 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 \ 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..7fd1d4fed 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 @@ -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 @@ -806,4 +806,11 @@ 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. \ 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..881f91b78 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -482,7 +482,7 @@ Ανάμιξη Επανάληψη Μπορείτε να επιλέξετε το πολύ τρεις ενέργειες για εμφάνιση στη σύντομη ειδοποίηση! - Επεξεργαστείτε κάθε ενέργεια ειδοποίησης παρακάτω πατώντας πάνω της. Επιλέξτε έως και τρεις από αυτές για να εμφανίζονται στη σύντομη ειδοποίηση, χρησιμοποιώντας τα πλαίσια ελέγχου στα δεξιά + Επεξεργαστείτε κάθε ενέργεια ειδοποίησης παρακάτω πατώντας πάνω της. Επιλέξτε έως και τρεις από αυτές για να εμφανίζονται στη σύντομη ειδοποίηση, χρησιμοποιώντας τα πλαίσια ελέγχου στα δεξιά. Κουμπί πέμπτης ενέργειας Κουμπί τέταρτης ενέργειας Κουμπί τρίτης ενέργειας @@ -595,7 +595,7 @@ Αυτόματο (θέμα συσκευής) Νυχτερινό θέμα Εμφάνιση λεπτομερειών καναλιού - Απενεργοποιήστε το media tunneling, αν εμφανίζεται μαύρη οθόνη ή διακοπτόμενος ήχος κατά την αναπαραγωγή βίντεο + Απενεργοποιήστε το media tunneling, αν παρατηρείτε μαύρη οθόνη ή διακοπές κατά την αναπαραγωγή βίντεο. Απενεργοποίηση media tunneling Εσωτερικό Ιδιωτικό @@ -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..90f5fd2ec 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -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..2da61b8d5 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 @@ -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 @@ -822,4 +822,12 @@ 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 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..1a9c5daac 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 @@ -806,4 +806,11 @@ 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. \ No newline at end of file diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 958c82d07..a8ee1093e 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -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..81ab11b88 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -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 @@ -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-he/strings.xml b/app/src/main/res/values-he/strings.xml index 729db00b6..5357e38fd 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -564,7 +564,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..d155fab95 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -200,7 +200,7 @@ इतिहास, सब्सक्रिप्शन, प्लेलिस्ट और सेटिंग निर्यात करें बाहरी प्लेयर्स इन प्रकार के लिंक सपोर्ट नहीं करते कोई वीडियो स्ट्रीम नहीं मिला - कोई ऑडियो स्ट्रीम नहीं मिली + कोई ऑडियो स्ट्रीम नहीं मिला फिर से क्रम देने के लिए खींचें बनाइये ख़ारिज करें @@ -471,7 +471,7 @@ शफल करें दोहराएं आप कंपैकट नोटीफिकेशन में दिखाए जाने वाले विकल्प में से अधिकतम 3 को चुन सकते है ! - नीचे दी गई प्रत्येक नोटीफिकेशन क्रिया को उस पर टैप करके संपादित करें। दाईं ओर चेकबॉक्स का उपयोग करके उनमें से अधिकतम तीन का चयन करें जिन्हें कंपैकट नोटीफिकेशन में दिखाया जाना है + नीचे दी गई प्रत्येक नोटीफिकेशन क्रिया को उस पर टैप करके संपादित करें। दाईं ओर चेकबॉक्स का उपयोग करके उनमें से अधिकतम तीन का चयन करें जिन्हें कंपैकट नोटीफिकेशन में दिखाया जाना है। पांचवा एक्शन बटन चतुर्थी एक्शन बटन तृतीय एक्शन बटन @@ -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..330ae2d5c 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,8 +424,8 @@ Želiš li izbrisati ovu grupu\? Nova Uvijek aktualiziraj - Omogući brz način - Onemogući brz način + Uključi brzi način + Isključi brzi način Memorija uređaja je popunjena Najomiljeniji Pritisni „Gotovo” kad je riješeno @@ -433,13 +433,13 @@ ∞ 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 @@ -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..58428df59 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -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 @@ -762,4 +762,55 @@ 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ő \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 2dc9ecdea..1bab98066 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 @@ -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 @@ -646,7 +646,7 @@ 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,10 @@ 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 + \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 71b656234..17cdb1c4b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -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 @@ -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,12 @@ 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 \ 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..93b52997c 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -531,7 +531,7 @@ URL を認識できませんでした。他のアプリで開きますか? 通知に表示されるサムネイルを 16:9 から正方形にします サムネイルを正方形にする - 以下をタップして通知のアクションを編集します。右側にあるチェックボックスを使用してコンパクトな通知に表示するものを 3 つまで選択します + 以下をタップして通知のアクションを編集します。右側にあるチェックボックスを使用してコンパクトな通知に表示するものを3つまで選択します。 コンパクトな通知に表示されるアクションは 3 つまで選ぶことができます! 5 番目のアクションボタン 4 番目のアクションボタン @@ -793,4 +793,10 @@ プレイリストを共有 プレイリスト名やビデオタイトルなどの詳細を含むプレイリスト、またはビデオURLのみのシンプルなリストとしてプレイリストを共有します - %1$s: %2$s + + %sの返信 + + もっと見る + 表示を少なくする + 以下の通知アクションをタップして編集します。 最初の3つのアクション (再生/一時停止、前へ、次へ)はシステムによって設定されており、カスタマイズすることはできません。 \ No newline at end of file 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-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..c1cb3db93 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 @@ -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 @@ -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-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index b49526071..b08e20202 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 @@ -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 @@ -806,4 +806,11 @@ 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. \ No newline at end of file diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index fb2cfd675..89715cd84 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+) ଏହି ଭିଡିଓ ବୟସ-ସୀମିତ ଅଟେ । @@ -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-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index e74e747f4..ee5b4aacc 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -602,7 +602,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..2a7d80396 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -827,4 +827,13 @@ 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ć \ 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..cca254aea 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -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 @@ -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..0dae4fc78 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -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 @@ -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..799166a89 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,7 +395,7 @@ 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 @@ -404,13 +404,13 @@ 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 + 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..bc3b81e1e 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -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 @@ -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 @@ -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. @@ -817,4 +817,15 @@ 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 \ 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..32454ae72 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 Формат видео по умолчанию @@ -562,7 +562,7 @@ Перемешать Повтор В компактном уведомлении доступно не более трёх действий! - Действия можно изменить, нажав на них. Отметьте не более трёх для отображения в компактном уведомлении + Отредактируйте каждое действие уведомления ниже, нажав на него. Выберите до трёх из них, которые будут отображаться в компактном уведомлении, используя флажки справа. Пятое действие Четвёртое действие Третье действие @@ -789,7 +789,7 @@ %1$s \n%2$s Вкладки канала - Shorts + Шорты Загрузка метаданных… Изменить ориентацию экрана Низкое качество @@ -823,4 +823,13 @@ Каналы Предыдущий стрим Живая трансляция + Показать больше + Показать меньше + + %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..dab5815db 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カードんかいダウンロードなやびらん。ダウンロードフォルダーぬばしょリセットさびーが? ふずんさったるタブゆみくみらんたみ、デフォルトぬタブしーようさびーん メインページんかいひょうじさりーるタブ みーさるバージョンがいようかのうなとぅちにアプリぬこうしんかくにんすんちうちひょうじさびーん @@ -394,7 +394,7 @@ ファイルぬさぎょうちゅうにNewPipeぬくーららりやびたん デバイスんかいにりらりょうぬあいびらん ファイルぬさちゅるじょさったるたみ、しんこうじょうちゅーがうしならりやびたん - ダウンロードりりき、あらんでぃダウンロードさるファイルしーょうきょさびーが\? + ダウンロードりりき、あらんでぃダウンロードさるファイルしーょうきょさびーが? ダウンロードキューぬせいぎん どうじーがてぃーちぬダウンロードぬじっこうさりやびーん ダウンロードぬかいし @@ -406,7 +406,7 @@ ストレージアクセスフレームワークー、がいぶSDカードんかいぬダウンロードぬかのうやいびーん さいせいいちのさくじょ まじりぬさいせいいちさちゅるじょさびーん - まじりぬさいせいいちさちゅるじょさびーが\? + まじりぬさいせいいちさちゅるじょさびーが? サービスぬきりけーい、ぎんじぇーぬしんたく: %sふんぬちゃーしが @@ -541,7 +541,7 @@ URLにいるしちなやびらんたん。ふかぬアプリっしふぃらちゃびーが? ちまーにひょうじさりーるサムネイル16:9からせいほうけいなさびーん サムネイルせいほうけいなすん - いかタップしちうちぬアクションへんしゅうさびーん。みぎぬわんかいあんチェックボックスしーようしコンパクトやるちまーにひょうじすし3ちまっししんたくさびーん + いかタップしちうちぬアクションへんしゅうさびーん。みぎぬわんかいあんチェックボックスしーようしコンパクトやるちまーにひょうじすし3ちまっししんたくさびーん。 コンパクトやるちまーにひょうじさりーるアクションー3ちまっしいらぶるくとぅがなやびーん! 5ばんみぬアクションボタン 4ばんみぬアクションボタン @@ -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..65dbffbae 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-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..a76bc1d4d 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,9 +389,9 @@ 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ý @@ -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,11 +653,11 @@ 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 @@ -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,33 @@ 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 \ No newline at end of file diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 8bb8d1b3d..66659c77a 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -227,7 +227,7 @@ Ограничи ред за преузимање Затвори Корисно при преласку на мобилне податке, иако нека преузимања не могу бити обустављена - Прекид на мереним мрежама + Прекид на мрежама с ограничењем Највећи број покушаја пре отказивања преузимања Највише покушаја Заустави @@ -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 изабраних Нема изабраних праћења Изабрана праћења @@ -719,7 +719,7 @@ Преглед сличице траке за претрагу Прикажи следеће стримове Прикажи/сакриј стримове - Користите резервну функцију декодера ExoPlayer-а + Користи резервну функцију декодера ExoPlayer-а оригинални Промените величину интервала учитавања на прогресивним садржајима (тренутно %s). Нижа вредност може убрзати њихово почетно учитавање Корисно, на пример, ако користите слушалице са поквареним физичким дугмадима @@ -731,7 +731,7 @@ Конфигуришите обавештење о тренутно репродукованом стриму Изаберите оригинални аудио снимак, без обзира на језик Изаберите аудио снимак са описима за особе са оштећеним видом, ако је доступан - Дај предност описном аудио снимку + Преферирај описни аудио снимак Радња покретом удесно Осветљеност Јачина звука @@ -764,7 +764,7 @@ описни синхронизовани Управљајте неким подешавањима ExoPlayer-а. Ове промене захтевају рестартовање плејера да би се примениле - Дај предност оригиналном аудио снимку + Преферирај оригинални аудио снимак Ово заобилазно решење ослобађа и поново инстанцира кодеке видео снимка када дође до промене површине, уместо да се површина директно поставља на кодек. ExoPlayer већ користи ово на неким уређајима са овим проблемом, ово подешавање утиче само на Android 6 и новије верзије. \n \nОмогућавање ове опције може спречити грешке при репродукцији приликом пребацивања тренутног плејера видео снимака или преласка на цео екран @@ -819,4 +819,12 @@ Дели плејлисту Делите плејлисту са детаљима, као што су назив плејлисте и наслови видео снимака или као једноставна листа URL адреса видео снимака -%1$s: %2$s + + %s одговор + %s одговора + %s одговора + + Прикажи више + Прикажи мање + Измените сваку радњу обавештења у наставку тако што ћете је додирнути. Прве три радње (пусти/паузирај, претходни и следећи) поставља систем и не могу се прилагодити. \ 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..2f66aa0de 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -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! @@ -806,4 +806,11 @@ - %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. \ 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..056434a4d 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 @@ -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 @@ -475,7 +475,7 @@ Abonelik seçilmedi %d öğe seçildi - "%d öğe seçildi" + %d öğe 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 @@ -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,11 @@ 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 + \ 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..76813a04f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -605,7 +605,7 @@ Кольорувати повідомлення основним кольором мініатюри. Підтримується не всіма пристроями Кольорове сповіщення У компактному сповіщенні є не більше трьох дій! - Дії можна змінити, натиснувши на них. Позначте не більше трьох для показу в компактному сповіщенні + Відредагуйте кожну дію сповіщення, натиснувши на неї. Виберіть до трьох з них, які будуть показані в компактному сповіщенні, за допомогою прапорців праворуч. Обрізати мініатюру відео показувану в сповіщенні з пропорцій 16: 9 до 1:1 Вимкнення тунелювання медіаданих за наявності чорного екрана або гальмування під час відтворення відео. Вимкнути тунелювання медіа @@ -657,7 +657,7 @@ Видалено %1$s завантажень - Завантаження завершено + %s завантаження завершено %s завантаження завершено %s завантажень завершено %s завантажень завершено @@ -823,4 +823,13 @@ Поділитися добіркою Поділитися добіркою з подробицями, такими як назва добірки та назви відео, або просто списком URL-адрес відео - %1$s: %2$s + Показати більше + Відредагуйте кожну дію сповіщення, натиснувши на неї. Перші три дії (відтворення/пауза, попередній і наступний) встановлюються системою і не можуть бути змінені. + + %s відповідь + %s відповіді + %s відповідей + %s відповідей + + Показати менше \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index f6719c1ed..bac361395 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -533,7 +533,7 @@ 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 @@ -551,7 +551,7 @@ 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 + 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 @@ -793,4 +793,10 @@ 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 + \ 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..f1d3fbf61 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -534,7 +534,7 @@ 随机播放 单曲循环 最多可以选择三个操作显示在紧凑通知中! - 点击编辑下面的每一个通知操作。使用右方的复选框选择在紧凑通知中显示的动作,最多可以选择三个 + 轻按下面的每一个通知操作进行编辑。使用右方的复选框选择在紧凑通知中显示的操作,最多可以选择三个。 第五操作按钮 第四操作按钮 第三操作按钮 @@ -793,4 +793,10 @@ 分享播放列表 分享详细的播放列表(带名称和视频标题等信息)或只分享视频网址列表 - %1$s: %2$s + + %s 条回复 + + 显示较多 + 显示较少 + 轻按下方的每个通知操作进行编辑。头三个动作(播放/暂停、上一个和下一个)是系统设置的,不能自定义。 \ 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..e9eb5bf12 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 第五粒動作掣 - 撳下面嘅掣去更改對應嘅通知動作。用右手邊嘅格仔剔選最多三個,擺喺精簡通知度 + 撳下面嘅掣去更改對應嘅通知動作。用右手邊嘅格仔剔選最多三個,擺落去精簡通知度。 精簡通知最多淨係擺到三個動作! 循環播放 顯示留言 @@ -516,7 +516,7 @@ 主機 喺影片詳情撳一下「幕後播」或「浮面播」個掣嘅時候顯示提示 紀錄 - 歷史紀錄 + 紀錄 攝咗做下個播 記憶體洩露監測喺傾卸堆疊嘢嘅時候,或者會導致個 app 冇反應 閂埋選單 @@ -721,7 +721,7 @@ 揀選右手邊播放器螢幕嘅手勢 右手邊手勢動作 調整光暗度 - 調整大細聲 + 音量 乜都唔使做 優先揀原聲 優先揀旁述 @@ -793,4 +793,10 @@ 頻道 上一個串流 直播 + + %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..382a95479 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -534,7 +534,7 @@ 隨機播放 循環播放 您可以選取最多三個動作來顯示在簡潔通知中! - 透過輕觸下方的通知來編輯它。使用右側的勾選框,最多可以選取三個在簡潔通知中顯示 + 透過輕觸下方的通知來編輯它。使用右側的勾選框,最多可以選取三個在簡潔通知中顯示。 第五動作按鈕 第四動作按鈕 第三動作按鈕 @@ -793,4 +793,10 @@ 分享播放清單 分享包含播放清單名稱與影片標題等詳細資訊的播放清單,或是僅作為簡單的影片 URL 清單 - %1$s:%2$s + + %s 個回覆 + + 顯示更多 + 顯示較少 + 透過點擊下面的每個通知操作來編輯它。前三個動作(播放/暫停、上一個與下一個)由系統設定,無法自訂。 \ No newline at end of file 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/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/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/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/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 失卻指標錯誤。 +柳煙輕愜夜,俊緒留江影。謝謝您陪我哋跑完悲歡離合嘅一年。 From bec18e13d3a82f6b946d6bced353dd97178743c9 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Mon, 27 Nov 2023 09:08:53 +0530 Subject: [PATCH 064/123] Improve app signature check --- .../schabi/newpipe/util/ReleaseVersionUtil.kt | 88 ++++--------------- 1 file changed, 15 insertions(+), 73 deletions(-) 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..fc31a4d94 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) + @Suppress("NewApi") + val certificates = mapOf( + RELEASE_CERT_PUBLIC_KEY_SHA256.toByteArray() to PackageManager.CERT_INPUT_SHA256 ) + val app = App.getApp() + return 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() } /** From 2e53a99361cdc9f380ccd7ace8dd21ed8aa321f4 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Tue, 6 Feb 2024 05:11:13 +0530 Subject: [PATCH 065/123] Convert isReleaseApk to lazy value --- .../main/java/org/schabi/newpipe/NewVersionWorker.kt | 10 ++++------ .../schabi/newpipe/settings/MainSettingsFragment.java | 2 +- .../org/schabi/newpipe/settings/SettingsActivity.java | 2 +- .../java/org/schabi/newpipe/util/ReleaseVersionUtil.kt | 5 ++--- 4 files changed, 8 insertions(+), 11 deletions(-) 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/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/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/util/ReleaseVersionUtil.kt b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt index fc31a4d94..fc1a7d8cc 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt @@ -15,14 +15,13 @@ object ReleaseVersionUtil { private const val RELEASE_CERT_PUBLIC_KEY_SHA256 = "cb84069bd68116bafae5ee4ee5b08a567aa6d898404e7cb12f9e756df5cf5cab" - @JvmStatic - fun isReleaseApk(): Boolean { + val isReleaseApk by lazy { @Suppress("NewApi") val certificates = mapOf( RELEASE_CERT_PUBLIC_KEY_SHA256.toByteArray() to PackageManager.CERT_INPUT_SHA256 ) val app = App.getApp() - return try { + try { PackageInfoCompat.hasSignatures(app.packageManager, app.packageName, certificates, false) } catch (e: PackageManager.NameNotFoundException) { createNotification( From 5bdb6f18d60783b38043f547f0131104531e70e5 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Wed, 20 Mar 2024 06:49:09 +0530 Subject: [PATCH 066/123] Use hexToByteArray() extension --- .../main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 fc1a7d8cc..3ea19fa4f 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt @@ -15,10 +15,11 @@ object ReleaseVersionUtil { private const val RELEASE_CERT_PUBLIC_KEY_SHA256 = "cb84069bd68116bafae5ee4ee5b08a567aa6d898404e7cb12f9e756df5cf5cab" + @OptIn(ExperimentalStdlibApi::class) val isReleaseApk by lazy { @Suppress("NewApi") val certificates = mapOf( - RELEASE_CERT_PUBLIC_KEY_SHA256.toByteArray() to PackageManager.CERT_INPUT_SHA256 + RELEASE_CERT_PUBLIC_KEY_SHA256.hexToByteArray() to PackageManager.CERT_INPUT_SHA256 ) val app = App.getApp() try { From 2e318b8b03db66a204f77b4a473cdf08ebfb82de Mon Sep 17 00:00:00 2001 From: CloudyRowly <76801836+CloudyRowly@users.noreply.github.com> Date: Thu, 21 Mar 2024 19:18:55 +1100 Subject: [PATCH 067/123] Added "free memory" check before downloading [Android N / API 24+] (#10505) Added "free space" check before downloading eliminating bugs related to out-of-memory on Android N / API 24+ --- .../newpipe/download/DownloadDialog.java | 23 +++++++++- .../streams/io/StoredDirectoryHelper.java | 45 +++++++++++++++++++ .../java/us/shandian/giga/util/Utility.java | 18 +++++++- 3 files changed, 82 insertions(+), 4 deletions(-) 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 1375d661e..fa312e55e 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -16,6 +16,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; @@ -147,7 +148,6 @@ public class DownloadDialog extends DialogFragment registerForActivityResult( new StartActivityForResult(), this::requestDownloadPickVideoFolderResult); - /*////////////////////////////////////////////////////////////////////////// // Instance creation //////////////////////////////////////////////////////////////////////////*/ @@ -565,7 +565,6 @@ public class DownloadDialog extends DialogFragment } } - /*////////////////////////////////////////////////////////////////////////// // Listeners //////////////////////////////////////////////////////////////////////////*/ @@ -784,6 +783,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 @@ -795,6 +795,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"; @@ -807,6 +808,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(); @@ -816,6 +818,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; } @@ -871,6 +874,22 @@ public class DownloadDialog extends DialogFragment return; } + // Check for free memory space (for api 24 and up) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + final long freeSpace = mainStorage.getFreeMemory(); + 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); 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..0fe2e0408 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 @@ -5,11 +5,15 @@ import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; +import android.os.Build; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import android.provider.DocumentsContract; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.documentfile.provider.DocumentFile; import org.schabi.newpipe.settings.NewPipeSettings; @@ -23,6 +27,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -30,6 +35,8 @@ 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 us.shandian.giga.util.Utility; + public class StoredDirectoryHelper { private static final String TAG = StoredDirectoryHelper.class.getSimpleName(); public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -168,6 +175,44 @@ public class StoredDirectoryHelper { return docTree == null; } + /** + * Get free memory of the storage partition (root of the directory). + * @return amount of free memory in the volume of current directory (bytes) + */ + @RequiresApi(api = Build.VERSION_CODES.N) // Necessary for `getStorageVolume()` + public long getFreeMemory() { + final Uri uri = getUri(); + final StorageManager storageManager = (StorageManager) context. + getSystemService(Context.STORAGE_SERVICE); + final List volumes = storageManager.getStorageVolumes(); + + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + if (split.length > 0) { + final String volumeId = split[0]; + + for (final StorageVolume volume : volumes) { + // if the volume is an internal system volume + if (volume.isPrimary() && volumeId.equalsIgnoreCase("primary")) { + return Utility.getSystemFreeMemory(); + } + + // if the volume is a removable volume (normally an SD card) + if (volume.isRemovable() && !volume.isPrimary()) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + try { + final String sdCardUUID = volume.getUuid(); + return storageManager.getAllocatableBytes(UUID.fromString(sdCardUUID)); + } catch (final Exception e) { + // do nothing + } + } + } + } + } + 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/us/shandian/giga/util/Utility.java b/app/src/main/java/us/shandian/giga/util/Utility.java index 3cfa22bd9..c75269757 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 { @@ -40,6 +40,20 @@ public class Utility { UNKNOWN } + /** + * Get amount of free system's memory. + * @return free memory (bytes) + */ + public static long getSystemFreeMemory() { + try { + final StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); + return statFs.getAvailableBlocksLong() * statFs.getBlockSizeLong(); + } catch (final Exception e) { + // do nothing + } + return -1; + } + public static String formatBytes(long bytes) { Locale locale = Locale.getDefault(); if (bytes < 1024) { From 206d1b6db4f9b60f652fd02ac16cad5727e75ffc Mon Sep 17 00:00:00 2001 From: TobiGr Date: Thu, 21 Mar 2024 11:56:10 +0100 Subject: [PATCH 068/123] Add separate message when download is rejected due to insufficient storage --- .../main/java/us/shandian/giga/ui/adapter/MissionAdapter.java | 2 +- app/src/main/res/values-ar-rLY/strings.xml | 2 +- app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-az/strings.xml | 2 +- app/src/main/res/values-b+ast/strings.xml | 2 +- app/src/main/res/values-b+uz+Latn/strings.xml | 2 +- app/src/main/res/values-be/strings.xml | 2 +- app/src/main/res/values-bg/strings.xml | 2 +- app/src/main/res/values-bn-rBD/strings.xml | 2 +- app/src/main/res/values-bn-rIN/strings.xml | 2 +- app/src/main/res/values-bn/strings.xml | 2 +- app/src/main/res/values-ca/strings.xml | 2 +- app/src/main/res/values-ckb/strings.xml | 2 +- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values-da/strings.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-el/strings.xml | 2 +- app/src/main/res/values-eo/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-et/strings.xml | 2 +- app/src/main/res/values-eu/strings.xml | 2 +- app/src/main/res/values-fa/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-gl/strings.xml | 2 +- app/src/main/res/values-he/strings.xml | 2 +- app/src/main/res/values-hi/strings.xml | 2 +- app/src/main/res/values-hr/strings.xml | 2 +- app/src/main/res/values-hu/strings.xml | 2 +- app/src/main/res/values-in/strings.xml | 2 +- app/src/main/res/values-is/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-ka/strings.xml | 2 +- app/src/main/res/values-kmr/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-ku/strings.xml | 2 +- app/src/main/res/values-lt/strings.xml | 2 +- app/src/main/res/values-lv/strings.xml | 2 +- app/src/main/res/values-mk/strings.xml | 2 +- app/src/main/res/values-ml/strings.xml | 2 +- app/src/main/res/values-nb-rNO/strings.xml | 2 +- app/src/main/res/values-ne/strings.xml | 2 +- app/src/main/res/values-nl-rBE/strings.xml | 2 +- app/src/main/res/values-nl/strings.xml | 2 +- app/src/main/res/values-nqo/strings.xml | 2 +- app/src/main/res/values-or/strings.xml | 2 +- app/src/main/res/values-pa/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-pt-rPT/strings.xml | 2 +- app/src/main/res/values-pt/strings.xml | 2 +- app/src/main/res/values-ro/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-ryu/strings.xml | 2 +- app/src/main/res/values-sat/strings.xml | 2 +- app/src/main/res/values-sc/strings.xml | 2 +- app/src/main/res/values-sk/strings.xml | 2 +- app/src/main/res/values-sl/strings.xml | 2 +- app/src/main/res/values-so/strings.xml | 2 +- app/src/main/res/values-sq/strings.xml | 2 +- app/src/main/res/values-sr/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-ur/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rHK/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 3 ++- 71 files changed, 72 insertions(+), 71 deletions(-) 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/res/values-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml index aca74e743..c57ead36a 100644 --- a/app/src/main/res/values-ar-rLY/strings.xml +++ b/app/src/main/res/values-ar-rLY/strings.xml @@ -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 ce62198cd..d69564493 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -407,7 +407,7 @@ لا يمكن الكتابة فوق الملف هناك تنزيل معلق بهذا الاسم تم إغلاق NewPipe أثناء العمل على الملف - لم يتبقى مساحة في الجهاز + لم يتبقى مساحة في الجهاز تم فقد التقدم بسبب حذف الملف انتهى وقت الاتصال هل تريد محو سجل التنزيل، أم تريد حذف جميع الملفات التي تم تنزيلها؟ diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index da08069ac..c0ac7e6d5 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -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 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 3bd940d11..c3c187891 100644 --- a/app/src/main/res/values-b+uz+Latn/strings.xml +++ b/app/src/main/res/values-b+uz+Latn/strings.xml @@ -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 ed24aac81..c41a9e6bb 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -399,7 +399,7 @@ немагчыма перазапісаць файл Ў чарзе ўжо ёсць загрузка з такім імем NewPipe была зачынена падчас працы над файлам - Скончылася вольнае месца на прыладзе + Скончылася вольнае месца на прыладзе Прагрэс страчаны, так як файл быў выдалены Час злучэння выйшла Вы ўпэўнены\? diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index fc845fdf9..78dcd0a93 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 секунди diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index 4f27d60ce..7ac77e353 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 @@ বন্ধ করুন ডাউনলোড করা ফাইলগুলো ডিলিট করুন ডাওন লোড ইতিহাস মুছুন - ডিভাইস এ স্পেস নেই + ডিভাইস এ স্পেস নেই পাওয়া যায় নি সার্ভার পাওয়া যায় নি এরর দেখান diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml index cb5c0e595..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 @@ অ্যাপ এর ভাষা বন্ধ করুন ডাওন লোড ইতিহাস মুছুন - ডিভাইস এ স্পেস নেই + ডিভাইস এ স্পেস নেই পাওয়া যায় নি সার্ভার পাওয়া যায় নি ডাউন লোড হয় নি diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 13bda43ae..c0851f9d6 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -10,7 +10,7 @@ বন্ধ করুন ডাউনলোড করা ফাইলগুলো ডিলিট করুন ডাওন লোড ইতিহাস মুছুন - ডিভাইস এ স্পেস নেই + ডিভাইস এ স্পেস নেই পাওয়া যায় নি সার্ভার পাওয়া যায় নি এরর দেখান diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 6f4724f3b..19a74923f 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -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\? diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index 3cfa5ef7b..7c60b33c0 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -21,7 +21,7 @@ هێڵەکی به‌رده‌وامبوون له‌ (به‌بێ دووباره‌كردنه‌وه‌) نۆبه‌تی کارپێکەر به‌پێی په‌خشی هاوشێوه‌ سنووردانانی نۆرەی دابەزاندن - بیرگەی ناوەکیت پڕ بووە + بیرگەی ناوەکیت پڕ بووە ژمارەی بەژداری نادیارە ناتوانرێت لەسەر ئەو فایله‌وه‌ جێگیر بکرێت په‌ڕه‌ هەڵبژێرە diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 1a5b83e52..02f6486c9 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í diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index f0ffba311..c3842e3b8 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -633,7 +633,7 @@ Skift service, nuværende valg: Kommentarer er slået fra Ingen apps på din enhed kan åbne dette - Ingen ledig plads på enheden + Ingen ledig plads på enheden App sprog Ja, og delvist sete videoer Fejl ved indlæsning af feed diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7fd1d4fed..c7506c649 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -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 diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 881f91b78..18ab48318 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -395,7 +395,7 @@ δεν είναι δυνατή η αντικατάσταση του αρχείου Υπάρχει μια εκκρεμής λήψη με αυτό το όνομα Το NewPipe τερματίστηκε ενώ επεξεργάζονταν το αρχείο - Δεν υπάρχει αρκετός χώρος στη συσκευή + Δεν υπάρχει αρκετός χώρος στη συσκευή Η πρόοδος χάθηκε, επειδή το αρχείο διαγράφηκε Λήξη χρονικού ορίου σύνδεσης Θέλετε να διαγράψετε το ιστορικό λήψεων σας ή να διαγράψετε όλα τα αρχεία που έχετε λάβει; diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 90f5fd2ec..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: diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2da61b8d5..b17c4d169 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -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 diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 1a9c5daac..1234aec5e 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -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 diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 69a42d1c2..3c36a85ae 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 diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 0dc26070a..01d26857e 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -384,7 +384,7 @@ ناتوانی در بازنویسی پرونده یک بارگیری دیگر با همین نام در صف قرار دارد نیوپایپ در خلال کار روی پرونده، بسته شد - فضایی روی دستگاه باقی نمانده است + فضایی روی دستگاه باقی نمانده است پیشرفت کار متوفق شد زیرا پرونده پاک شده است پایان زمان اتصال می‌خواهید تاریخچه بارگیری را پاک کنید یا همه پرونده‌هایی که بارگیری شده‌اند؟ diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index a8ee1093e..c27115689 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 diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 81ab11b88..51b572dce 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 diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index bfc8aa6e1..93a43ddd2 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 diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 5357e38fd..6cae41316 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -401,7 +401,7 @@ לא ניתן לשכתב את הקובץ כבר יש הורדה ממתינה בשם הזה NewPipe נסגר בזמן העבודה על הקובץ - לא נשאר מקום במכשיר + לא נשאר מקום במכשיר התהליך אבד כיוון שהקובץ נמחק החיבור המתין זמן רב מדי למחוק את היסטוריית ההורדות שלך או למחוק את כל הקבצים שהורדת\? diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index d155fab95..a425a9b04 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -379,7 +379,7 @@ फाइल को ओवरराइट नहीं कर सकते इस नाम का एक डाउनलोड बाकी है फ़ाइल पर कार्य करते समय न्यूपाइप बंद किया गया - डिवाइस पर जगह समाप्त + डिवाइस पर जगह समाप्त प्रगति खो गई, क्योंकि फ़ाइल मिटा दी गई थी कनेक्शन का समय समाप्त क्या आप अपना डाउनलोड इतिहास मिटाना चाहते हैं या सभी डाउनलोड की गई फ़ाइलों को हटाना चाहते हैं\? diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 330ae2d5c..8f077fdea 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -426,7 +426,7 @@ Uvijek aktualiziraj Uključi brzi način Isključi brzi način - Memorija uređaja je popunjena + Memorija uređaja je popunjena Najomiljeniji Pritisni „Gotovo” kad je riješeno Gotovo diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 58428df59..13a488f07 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 diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 1bab98066..1ca35cfb8 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -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 diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index ee90a9333..c35d7a685 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -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 17cdb1c4b..3d8162880 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 diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 93b52997c..03117abad 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -390,7 +390,7 @@ ファイルを上書きできません 同じファイル名のダウンロードが既に進行中です ファイルの作業中に NewPipe が閉じられました - デバイスに空き容量がありません + デバイスに空き容量がありません ファイルが削除されたため、進行状況が失われました ダウンロード履歴、またはダウンロードしたファイルを消去しますか\? ダウンロード キューの制限 diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 9f6227388..cbeb27d9d 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -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-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e8651d966..d03be0920 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -389,7 +389,7 @@ 파일을 덮어쓰기할 수 없습니다 해당 이름으로 대기된 다운로드가 있습니다 파일 작업 중에 Newpipe가 종료되었습니다 - 남은 저장공간이 없습니다 + 남은 저장공간이 없습니다 파일이 삭제되어 진행이 중지되었습니다 연결시간 초과 모든 다운로드 기록과 파일을 삭제합니다.확실합니까\? 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..84699cf6d 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 diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 9bf252ccb..ab3f7b94c 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 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-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index c1cb3db93..6f232ba06 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -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\? 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 b08e20202..edb822366 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -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 diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml index 060021846..6956d6b78 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -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 89715cd84..331c3248c 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -378,7 +378,7 @@ ପ୍ରଗତି ହଜିଗଲା, କାରଣ ଫାଇଲ୍ ଡିଲିଟ୍ ହେଲା ପରବର୍ତ୍ତୀ ପ୍ରକ୍ରିୟାକରଣ ବିଫଳ ହେଲା ଫାଇଲରେ କାମ କରିବାବେଳେ ନ୍ୟୁ ପାଇପ୍ ବନ୍ଦ ହୋଇଯାଇଥିଲା - ଡିଭାଇସରେ କୌଣସି ସ୍ଥାନ ବାକି ନାହିଁ + ଡିଭାଇସରେ କୌଣସି ସ୍ଥାନ ବାକି ନାହିଁ ଡିସ୍କରୁ ସମସ୍ତ ଡାଉନଲୋଡ୍ ହୋଇଥିବା ଫାଇଲଗୁଡ଼ିକୁ ଲିଭାନ୍ତୁ\? ବନ୍ଦ କର ସର୍ବାଧିକ ପୁନଃଚେଷ୍ଟା diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index ee5b4aacc..e34469eb1 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -395,7 +395,7 @@ ਫਾਈਲ ਓਵਰਰਾਈਟ ਨਹੀਂ ਹੋ ਸਕਦੀ ਇਸ ਨਾਮ ਦਾ ਇੱਕ ਡਾਊਨਲੋਡ ਬਕਾਇਆ ਹੈ ਫਾਈਲ ਤੇ ਕੰਮ ਕਰਦੇ ਸਮੇਂ ਨਿਊਪਾਈਪ ਬੰਦ ਕੀਤੀ ਗਈ - ਡਿਵਾਈਸ ਤੇ ਕੋਈ ਜਗ੍ਹਾ ਨਹੀਂ ਬਚੀ ਹੈ + ਡਿਵਾਈਸ ਤੇ ਕੋਈ ਜਗ੍ਹਾ ਨਹੀਂ ਬਚੀ ਹੈ ਪ੍ਰਗਤੀ ਖਤਮ ਹੋ ਗਈ, ਕਿਉਂਕਿ ਫਾਈਲ ਮਿਟਾਈ ਗਈ ਕੁਨੈਕਸ਼ਨ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਡਾਊਨਲੋਡ ਇਤਿਹਾਸ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ ਜਾਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਸਾਰੀਆਂ ਫ਼ਾਈਲਾਂ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ\? diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 2a7d80396..0212da91f 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 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index cca254aea..c0c20636b 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\? diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 0dae4fc78..3a6fece0a 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 diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 799166a89..554082a10 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -398,7 +398,7 @@ 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\? diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index bc3b81e1e..49a6b592b 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -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 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 32454ae72..bd7e5ce20 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -405,7 +405,7 @@ не удаётся перезаписать файл В очереди уже есть загрузка с таким именем NewPipe была закрыта во время работы над файлом - Закончилось свободное место на устройстве + Закончилось свободное место на устройстве Прогресс потерян, так как файл был удалён Действительно удалить историю загрузок и загруженные файлы\? Ограничить очередь загрузки diff --git a/app/src/main/res/values-ryu/strings.xml b/app/src/main/res/values-ryu/strings.xml index dab5815db..a531d14bc 100644 --- a/app/src/main/res/values-ryu/strings.xml +++ b/app/src/main/res/values-ryu/strings.xml @@ -392,7 +392,7 @@ ファイルうわがきなやびらん いぬファイルめいぬダウンロードぬしでぃにしんこうちゅうやいびーん ファイルぬさぎょうちゅうにNewPipeぬくーららりやびたん - デバイスんかいにりらりょうぬあいびらん + デバイスんかいにりらりょうぬあいびらん ファイルぬさちゅるじょさったるたみ、しんこうじょうちゅーがうしならりやびたん ダウンロードりりき、あらんでぃダウンロードさるファイルしーょうきょさびーが? ダウンロードキューぬせいぎん diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index 65dbffbae..a5959086e 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -708,7 +708,7 @@ ᱟᱱᱰᱨᱚᱭᱮᱰ ᱨᱮ ᱞᱤᱵᱨᱮ ᱞᱟᱭᱤᱴᱣᱮᱴ ᱥᱴᱨᱤᱢᱤᱝ ᱴᱮᱜᱥ ᱥᱚᱨᱠᱟᱨᱤ - ᱚᱱᱚᱞ ᱨᱮ ᱡᱟᱭᱜᱟ ᱵᱟᱹᱱᱩᱜᱼᱟ + ᱚᱱᱚᱞ ᱨᱮ ᱡᱟᱭᱜᱟ ᱵᱟᱹᱱᱩᱜᱼᱟ ᱪᱮᱱᱮᱞ ᱨᱮᱱᱟᱜ ᱟᱵᱟᱛᱟᱨ ᱛᱷᱩᱱᱤᱠᱟ ᱥᱮᱨᱮᱧ ᱞᱤᱥᱴᱤ ᱥᱟᱦᱴᱟ ᱵᱟᱝ diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index b9178cf87..b39a041ee 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 diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index a76bc1d4d..231de5fc4 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -398,7 +398,7 @@ 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äť + Máš plnú pamäť 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\? 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 66659c77a..fc68ce5eb 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -237,7 +237,7 @@ Није могуће опоравити ово преузимање Веза је истекла Напредак је изгубљен, јер је фајл избрисан - Недовољно меморије на уређају + Недовољно меморије на уређају NewPipe је затворен док је радио на фајлу Накнадна обрада није успела Није пронађено diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 2f66aa0de..be8e2894c 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 diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 056434a4d..dfac4fb47 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -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\? diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 76813a04f..9348c448d 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -398,7 +398,7 @@ не можу перезаписати файл Завантаження з такою назвою вже додано в чергу NewPipe був закритий під час роботи над файлом - На пристрої не залишилося вільного місця + На пристрої не залишилося вільного місця Прогрес втрачено через видалення файлу Час очікування з\'єднання вичерпано Очистити історію завантажень чи завантажені файли\? 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 bac361395..cdd6f6e31 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -392,7 +392,7 @@ 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 + 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ề\? diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f1d3fbf61..89c215943 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 在处理文件时被关闭 - 设备上没有剩余储存空间 + 设备上没有剩余储存空间 进度丢失,文件已被删除 连接超时 是否清空下载记录或删除所有下载的文件? diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index e9eb5bf12..c91731314 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -454,7 +454,7 @@ 建立唔到呢個目的地資料夾 建立唔到安全連線 喺幕後開始播放 - 部機冇晒位 + 部機冇晒位 頂櫳重試幾多次 若然有機會用到流動數據嘅時候,可能會用得著,雖則有啲下載冇得暫停 輪住嚟下載 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 382a95479..f2eb503ab 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 在處理檔案時被關閉 - 裝置上沒有剩餘的空間 + 裝置上沒有剩餘的空間 進度遺失,因為檔案已被刪除 您想要清除您的下載歷史記錄,還是刪除所有已下載的檔案? 限制下載佇列 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 281df95a4..30a85f3c7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -616,7 +616,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 From bc7c3f48ad6293f960b4cff25d6fc940ff60d490 Mon Sep 17 00:00:00 2001 From: Mohammed Anas Date: Tue, 26 Mar 2024 19:53:45 +0000 Subject: [PATCH 069/123] Fix GitHub question discussion form The name and description fields are both invalid here. --- .github/DISCUSSION_TEMPLATE/questions.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml index befb6280e..bb7894625 100644 --- a/.github/DISCUSSION_TEMPLATE/questions.yml +++ b/.github/DISCUSSION_TEMPLATE/questions.yml @@ -1,5 +1,3 @@ -name: Question -description: Ask about anything NewPipe-related labels: [question] body: - type: markdown From 1732584e5e60a1c50bed4d67aaf50ea51d86e824 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 20 Mar 2024 21:26:58 +0100 Subject: [PATCH 070/123] Translated using Weblate (Danish) Currently translated at 100.0% (729 of 729 strings) Co-authored-by: cat <158170307+cultcats@users.noreply.github.com> --- app/src/main/res/values-da/strings.xml | 434 +++++++++++++------------ 1 file changed, 231 insertions(+), 203 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index c3842e3b8..f48ed7b3b 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -3,7 +3,7 @@ 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). + Ingen streamafspiller blev fundet (du kan installere VLC for at afspille den). Installér Annullér Åbn i browser @@ -39,16 +39,16 @@ Hentede lydfiler gemmes her Vælg lagringsmappe til lydfiler Standardopløsning - Standardopløsning for popup + Standardopløsning til popup Vis højere opløsninger Kun nogle enheder kan afspille 2K-/4K-videoer Afspil med Kodi 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 Lys Mørk @@ -56,11 +56,11 @@ 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-cache + 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 slettet + 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 @@ -113,57 +113,57 @@ Fil 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 popup Skift til hovedafspiller Importér database Eksportér database - Overskriver din nuværende historik, abonnementer, spillelister og (hvis det ønskes) indstillinger - Eksportér historik, abonnementer, spillelister og indstillinger + 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 + Visningshistorik slettet Ryd søgehistorik - Sletter historikken for søgeord - Slet hele søgehistorikken\? - Søgehistorikken blev slettet + 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,55 +316,55 @@ 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-URL\'er understøttes @@ -410,7 +410,7 @@ Vis ikke Bland Vis beskrivelse - Åbn hjemmeside + Åbn webside Sprog Lav kvalitet (mindre) Start afspilning automatisk — %s @@ -424,10 +424,10 @@ %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 hovedafspiller i fuld skærm - Downloadmappe endnu ikke valgt. Vælg standardmappen nu + 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+) @@ -441,15 +441,15 @@ 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 notifikation Afspiller-notifikation - En fejl opstod, se notifikationen - Slå fra for at skjule videobeskrivelsen og yderligere information + 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 @@ -462,31 +462,31 @@ 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? - Videohashfunktion notifikation - Notifikationer om videohashfunktioners status + 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 blev slettet + Afspilningspositioner slettet 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 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 som kan vises i den lille notifikation! + 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) Nat-tema @@ -495,67 +495,67 @@ \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 + 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. + 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 app på din enhed kan åbne dette Ingen ledig plads på enheden - App sprog + 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 + 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 @@ -715,14 +715,14 @@ Sortér 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 - Brug lydbeskrivelser for personer med nedsat syn, hvis tilgængeligt - 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 brugerdefineres. + 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 @@ -743,7 +743,7 @@ 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. + 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 @@ -771,8 +771,8 @@ Billedkvalitet Indlæs ikke billeder Lav kvalitet - Del playliste - Del playliste med detajler såsom playliste navn og videotitler eller som en simpel liste over video-URL\'er + 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 @@ -785,4 +785,32 @@ Ø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 From 3134f5e747af5897ad1dce34c31f3e1b70603756 Mon Sep 17 00:00:00 2001 From: Mohammed Anas Date: Tue, 26 Mar 2024 20:28:28 +0000 Subject: [PATCH 071/123] Don't add "question" label to question discussions (#10906) They already have a "Questions" category of their own anyway. --- .github/DISCUSSION_TEMPLATE/questions.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml index bb7894625..2d467d5e5 100644 --- a/.github/DISCUSSION_TEMPLATE/questions.yml +++ b/.github/DISCUSSION_TEMPLATE/questions.yml @@ -1,4 +1,3 @@ -labels: [question] body: - type: markdown attributes: From 0d4c1bee3f140a87802d9a3b6252bae1b952dcc7 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Tue, 26 Mar 2024 22:32:29 +0100 Subject: [PATCH 072/123] [CI] Update gradle/wrapper-validation-action to v2 Updates deprecated Node 16 to 20 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4800f8578..c3aff730b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/wrapper-validation-action@v2 - name: create and checkout branch # push events already checked out the branch From c34d30dc17a4944bf3b65fc3bdb8af034fc4e092 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Tue, 26 Mar 2024 22:48:47 +0100 Subject: [PATCH 073/123] [CI] Update sonar job to use cache@v4 Updates deprecated Node 16 to 20 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3aff730b..0d76e1645 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,7 +123,7 @@ jobs: cache: 'gradle' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar From b06a9618d4cbc4e5f7bfcb87be434e55c340488e Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 27 Mar 2024 10:18:04 +0100 Subject: [PATCH 074/123] use GitHub markdown to emphasise warning in all READMEs --- doc/README.asm.md | 7 ++++--- doc/README.de.md | 7 ++++--- doc/README.es.md | 7 ++++--- doc/README.fr.md | 7 ++++--- doc/README.hi.md | 7 ++++--- doc/README.it.md | 7 ++++--- doc/README.ja.md | 7 ++++--- doc/README.ko.md | 7 ++++--- doc/README.pa.md | 7 ++++--- doc/README.pl.md | 7 ++++--- doc/README.pt_BR.md | 7 ++++--- doc/README.ro.md | 7 ++++--- doc/README.ru.md | 7 ++++--- doc/README.ryu.md | 7 ++++--- doc/README.so.md | 7 ++++--- doc/README.sr.md | 7 ++++--- doc/README.tr.md | 7 ++++--- doc/README.zh_TW.md | 7 ++++--- 18 files changed, 72 insertions(+), 54 deletions(-) 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 商店違反了他們的使用者合約。 ## 截圖 From 3a014d8d46a15935b403fe3fe5e4f74fb9976613 Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 27 Mar 2024 16:05:17 +0100 Subject: [PATCH 075/123] Fix not considering nullability when comparing getAudioTrackType --- app/src/main/java/org/schabi/newpipe/util/ListHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 7cb8f1f37..f1904565d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -874,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())); } } From bed351668738cbe57d528f2be77383771789abf9 Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 27 Mar 2024 17:30:23 +0100 Subject: [PATCH 076/123] Fix non-desugared method being used Search for "Not supported at all minSDK levels" here: https://developer.android.com/studio/write/java8-support-table --- .../java/us/shandian/giga/service/DownloadManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 42ff3ca8c..45211211f 100755 --- a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java @@ -411,7 +411,7 @@ public class DownloadManagerService extends Service { mission.threadCount = threads; mission.source = source; mission.nearLength = nearLength; - mission.recoveryInfo = recovery.toArray(MissionRecoveryInfo[]::new); + mission.recoveryInfo = recovery.toArray(new MissionRecoveryInfo[0]); if (ps != null) ps.setTemporalDir(DownloadManager.pickAvailableTemporalDir(this)); From d8a5d5545ddae5f19566a30e949db551b24da732 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Mar 2024 10:56:50 +0100 Subject: [PATCH 077/123] Fix choosing audio format to mux with video-only download --- .../newpipe/util/SecondaryStreamHelper.java | 54 +++++++------------ 1 file changed, 19 insertions(+), 35 deletions(-) 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() { From d3afde8789df76456128207a5f1186727f50237d Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Mar 2024 11:21:29 +0100 Subject: [PATCH 078/123] Remove unused DownloadDialog.onDismissListener --- .../newpipe/download/DownloadDialog.java | 20 ------------------- 1 file changed, 20 deletions(-) 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 fa312e55e..4680e71a7 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; @@ -113,9 +111,6 @@ 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; @@ -195,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 @@ -364,14 +352,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(); From 5d9adce40dce1e10507131c2eb39c348ba2a6c50 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Mar 2024 11:35:21 +0100 Subject: [PATCH 079/123] Fix NPE, since dismissing a dialog still calls onViewCreated() --- .../java/org/schabi/newpipe/download/DownloadDialog.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 4680e71a7..bbdb46292 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -115,7 +115,7 @@ public class DownloadDialog extends DialogFragment 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; @@ -209,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)); @@ -293,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())); From 29e4135aaa168db96e67a9168acbb01da92c65d6 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Mar 2024 12:03:10 +0100 Subject: [PATCH 080/123] Try to fix PR labeler Reference: https://github.com/actions/labeler?tab=readme-ov-file#permissions --- .github/workflows/pr-labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From ddd6c8cbf1e6a019f4df82863e3b446d5fb708fb Mon Sep 17 00:00:00 2001 From: ge78fug Date: Mon, 27 Mar 2023 16:57:30 +0200 Subject: [PATCH 081/123] Changed the landscape layout of list card item Make layout-land/list_stream_card_item a symlink to layout/list_stream_item --- app/src/main/res/layout-land/list_stream_card_item.xml | 1 + 1 file changed, 1 insertion(+) create mode 120000 app/src/main/res/layout-land/list_stream_card_item.xml 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 From cefdefdfd2d82dd461d8736fe497a709457288b0 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Mar 2024 18:51:36 +0100 Subject: [PATCH 082/123] 11111th commit From b85f7a6747eb61ea4b7b7893688ea65f7103de01 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Mar 2024 22:46:19 +0100 Subject: [PATCH 083/123] Some more slight improvements --- .../newpipe/fragments/detail/DescriptionFragment.java | 2 +- .../fragments/list/channel/ChannelAboutFragment.java | 11 +++-------- .../fragments/list/channel/ChannelFragment.java | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) 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 ba233559c..7fc66b42f 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 @@ -26,11 +26,11 @@ public class DescriptionFragment extends BaseDescriptionFragment { @State StreamInfo streamInfo; - public DescriptionFragment(final StreamInfo streamInfo) { this.streamInfo = streamInfo; } + @Nullable @Override protected Description getDescription() { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java index 30c3aac5a..674a13c5b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java @@ -26,10 +26,8 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { @State protected ChannelInfo channelInfo; - public static ChannelAboutFragment getInstance(final @NonNull ChannelInfo channelInfo) { - final ChannelAboutFragment fragment = new ChannelAboutFragment(); - fragment.channelInfo = channelInfo; - return fragment; + ChannelAboutFragment(@NonNull final ChannelInfo channelInfo) { + this.channelInfo = channelInfo; } @@ -42,10 +40,7 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { @Nullable @Override protected Description getDescription() { - return new Description( - channelInfo.getDescription(), - Description.PLAIN_TEXT - ); + return new Description(channelInfo.getDescription(), Description.PLAIN_TEXT); } @NonNull 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)); } } From 1d3a69a29f7f8237a1c6df55a7c39a66a6e435e1 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Thu, 25 Jan 2024 15:50:47 +0100 Subject: [PATCH 084/123] Move text from manual_update_title to check_for_updates --- app/src/main/res/values-ar-rLY/strings.xml | 2 +- app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-az/strings.xml | 2 +- app/src/main/res/values-be/strings.xml | 2 +- app/src/main/res/values-bg/strings.xml | 2 +- app/src/main/res/values-bn-rBD/strings.xml | 2 +- app/src/main/res/values-bn/strings.xml | 2 +- app/src/main/res/values-ca/strings.xml | 2 +- app/src/main/res/values-ckb/strings.xml | 2 +- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values-da/strings.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-el/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-et/strings.xml | 2 +- app/src/main/res/values-eu/strings.xml | 2 +- app/src/main/res/values-fa/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-gl/strings.xml | 2 +- app/src/main/res/values-he/strings.xml | 2 +- app/src/main/res/values-hi/strings.xml | 2 +- app/src/main/res/values-hr/strings.xml | 2 +- app/src/main/res/values-hu/strings.xml | 2 +- app/src/main/res/values-hy/strings.xml | 2 +- app/src/main/res/values-in/strings.xml | 2 +- app/src/main/res/values-is/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-ka/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-lt/strings.xml | 2 +- app/src/main/res/values-lv/strings.xml | 2 +- app/src/main/res/values-nb-rNO/strings.xml | 2 +- app/src/main/res/values-nl/strings.xml | 2 +- app/src/main/res/values-nqo/strings.xml | 2 +- app/src/main/res/values-or/strings.xml | 2 +- app/src/main/res/values-pa/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-pt-rPT/strings.xml | 2 +- app/src/main/res/values-pt/strings.xml | 2 +- app/src/main/res/values-ro/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-ryu/strings.xml | 2 +- app/src/main/res/values-sc/strings.xml | 2 +- app/src/main/res/values-sk/strings.xml | 2 +- app/src/main/res/values-sr/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rHK/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 3 ++- 56 files changed, 57 insertions(+), 56 deletions(-) diff --git a/app/src/main/res/values-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml index c57ead36a..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 فيديو ألف diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d69564493..eefe24390 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -700,7 +700,7 @@ وضع التالي على قائمة الانتظار تم وضع التالي على قائمة الانتظار جاري المعالجة ... قد يستغرق لحظة - التحقق من وجود تحديثات + التحقق من وجود تحديثات التحقق يدويا من وجود إصدارات جديدة جاري التحقق من وجود تحديثات… عناصر تغذية جديدة diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index c0ac7e6d5..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 diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index c41a9e6bb..5990738cf 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -670,7 +670,7 @@ Апавяшчаць аб новых стрымах з падпісак Частата праверкі Патрабуецца падключэнне да сеткі - Праверце наяўнасць абнаўленняў + Праверце наяўнасць абнаўленняў Праверце новыя версіі ўручную Аўтаматычны запуск прайгравання — %s Картка diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 78dcd0a93..bc235446c 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -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 7ac77e353..3f0d65c82 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -401,7 +401,7 @@ কনটেন্টের জন্য পূর্বনির্ধারিত দেশ বাইরের প্লেয়ারসমূহ এ ধরনের লিঙ্কসমূহ সমর্থন করে না হ্যাশ হিসাব করা হচ্ছে - আপডেট চেক করো + আপডেট চেক করো %s জন দেখছে %s জন দেখছে diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index c0851f9d6..853b04b64 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -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 19a74923f..871af187b 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -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 diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index 7c60b33c0..e6e375a4c 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -658,7 +658,7 @@ نیوپایپ تووشی کێشەیەک بوو ، کرتە بکە بۆ سکاڵاکردن پیشاندانی ”کڕاش کردنی لێدەرەکە“ سازاندنی پەیامی کێشەیەک - پشکنین بۆ نوێکردنەوە + پشکنین بۆ نوێکردنەوە کێشە لە سکاڵا کردنی پەیام پەیامەکانی سکاڵاکردن لە کێشەکان بابەتە نوێیەکانی فیید diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 02f6486c9..0a4751261 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -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ě diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index f48ed7b3b..601bc3752 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -526,7 +526,7 @@ Tjekker efter opdateringer… gendanner \"Hurtig feed\"-tilstand oplyser ikke mere info om dette. - Tjek efter opdateringer + Tjek efter opdateringer Fjern sete videoer\? 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. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c7506c649..e94c8f757 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -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 diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 18ab48318..cb5958dc5 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -654,7 +654,7 @@ Επεξεργασία... Μπορεί να πάρει λίγο χρόνο Έλεγχος αναβάθμισης… Χειροκίνητος έλεγχος για νέα έκδοση - Έλεγχος αναβάθμισης + Έλεγχος αναβάθμισης Νέα αντικείμενα τροφοδοσίας Εμφάνιση «Κατάρρευσης αναπαραγωγέα» Εμφανίζει μια επιλογή κατάρρευσης κατά τη χρήση του αναπαραγωγέα diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index b17c4d169..3a8681e44 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -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 diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 1234aec5e..5864d5519 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -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 diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 3c36a85ae..6f94a8f62 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -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 01d26857e..2afeaf286 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -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 c27115689..67350d7ba 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -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ä diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 51b572dce..9994abb4c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -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 » diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 93a43ddd2..efe1b7f5e 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -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 6cae41316..befde2d53 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -676,7 +676,7 @@ נוסף כהבא בתור הוספה כהבא בתור מתבצע עיבוד… נא להמתין רגע קט - איתור עדכונים + איתור עדכונים מתבצע איתור עדכונים… לנסות לאתר גרסאות חדשות ידנית פריטים חדשים בהזנה diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index a425a9b04..756b1878a 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -579,7 +579,7 @@ नेटवर्क कनेक्शन आवश्यक नई स्ट्रीम अधिसूचनाएं कोई भी नेटवर्क - अपडेट के लिए जाँच करें + अपडेट के लिए जाँच करें निम्न गुणवत्ता (छोटा) सीकबार थंमनेल पूर्वावलोकन उच्च गुणवत्ता (बड़ा) diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 8f077fdea..f6c2120f7 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -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 diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 13a488f07..5ca829ed8 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -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\? 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 1ca35cfb8..d715f2232 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -640,7 +640,7 @@ 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 diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index c35d7a685..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 diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 3d8162880..5ddb6c672 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -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 diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 03117abad..afcf34429 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -639,7 +639,7 @@ 処理中… 少し時間がかかるかもしれません 新しいバージョンを手動で確認します アップデートを確認中… - アップデートを確認 + アップデートを確認 次をキューに追加 次をキューに追加しました クリエイターの心をこめて diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index cbeb27d9d..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 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d03be0920..d17e7599d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -557,7 +557,7 @@ 확인 빈도 필요한 네트워크 연결 모든 네트워크 - 업데이트 확인 + 업데이트 확인 새로운 버전을 수동으로 확인 자동으로 재생 시작 — %s Wi-Fi에서만 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 84699cf6d..c68e49bdd 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -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 ab3f7b94c..58b9a9d76 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -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-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 6f232ba06..416ebfd02 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -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\" diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index edb822366..9b690f770 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -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 diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml index 6956d6b78..94e7ae2f4 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -482,7 +482,7 @@ ߞߊ߬ ߡߊ߬ߝߘߎ߬ߟߌ ߟߎ߬ ߟߊ߫ ߥߦߏ߬ ߞߎߘߊ ߟߎ߫ ߛߏߓߌ߬ߘߐ߬ߓߏ߲ ߠߎ߬ ߟߊߣߊ߬ ߞߙߏ߬ߝߏ߫ ߛߎ߮ ߓߍ߯ ߟߏ߲ߘߐߦߊߟߌ ߟߎ߬ - ߟߏ߲ߘߐߦߊߟߌ ߟߎ߬ ߡߊߝߍߣߍ߲߫ + ߟߏ߲ߘߐߦߊߟߌ ߟߎ߬ ߡߊߝߍߣߍ߲߫ ߞߊ߬ ߓߐߞߏ߫ ߞߎߘߊ ߟߎ߫ ߕߎ߬ߢߊ߬ߟߐ߲߫ ߓߟߏ ߟߊ߫ ߞߵߊ߬ ߡߊߖߌ߰ ߟߥߊߟߌߟߊ߲ ߘߐߦߟߍߡߊ߲߫ ߕߎߡߊ ߝߏߛߌ߬ diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index 331c3248c..e6daac644 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -410,7 +410,7 @@ ସ୍ଵତଃସୃଷ୍ଟ ବିଷୟବସ୍ତୁ ଡାଉନଲୋଡ୍ ଧାଡି ସୀମିତ କରନ୍ତୁ - ଅଦ୍ୟତନ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ + ଅଦ୍ୟତନ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ ନୂତନ ସଂସ୍କରଣଗୁଡ଼ିକ ପାଇଁ ମାନୁଆଲ ଯାଞ୍ଚ କରନ୍ତୁ ବିଚାରାଧୀନ ଅଛି ଦେଖାଯାଇଥିବା ଭିଡିଓଗୁଡିକ ଅପସାରଣ କରିବେ କି\? diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index e34469eb1..196b5b785 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -669,7 +669,7 @@ ਸਬਸਕ੍ਰਾਈਬ ਕੀਤੇ ਚੈਨਲ ਉੱਪਰ ਨਵੀਂ ਸਟ੍ਰੀਮ ਉੱਪਲਬਧ ਹੋਣ ਤੇ ਨੋਟੀਫਿਕੇਸ਼ਨ ਰਾਹੀਂ ਸੂਚਿਤ ਕਰੋ ਜਾਂਚ ਅਵਧੀ ਕੋਈ ਵੀ ਨੈੱਟਵਰਕ - ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ + ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ ਘੱਟ ਗੁਣਵੱਤਾ (ਛੋਟਾ ਆਕਾਰ) ਡਿਸਕ ਤੋਂ ਸਾਰੀਆਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਫਾਈਲਾਂ ਹਟਾਓ\? diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 0212da91f..6fd61b80e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -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 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c0c20636b..1d241de51 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -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 diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 3a6fece0a..325c79d3c 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -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 diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 554082a10..e69f79a68 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -664,7 +664,7 @@ Seguinte colocado na fila Colocar seguinte na fila A processar… Pode levar algum tempo - Procurar atualizações + Procurar atualizações Verificar manualmente se existe uma nova versão A procurar atualizações… Novos itens diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 49a6b592b..ccc25dc4b 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -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 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index bd7e5ce20..a885631eb 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -675,7 +675,7 @@ Добавить следующим Обработка… Подождите немного Проверить обновления вручную - Проверить обновления + Проверить обновления Проверка обновлений… Новое на канале Уведомления diff --git a/app/src/main/res/values-ryu/strings.xml b/app/src/main/res/values-ryu/strings.xml index a531d14bc..195eb22b3 100644 --- a/app/src/main/res/values-ryu/strings.xml +++ b/app/src/main/res/values-ryu/strings.xml @@ -651,7 +651,7 @@ しーょりちゅう… くーてーんじがんがかかいんかむしりやびらん みーさるバージョンしーゅどうでぃかくにんさびーん アップデートかくにんちゅう… - アップデートかくにん + アップデートかくにん ちぎキューんかいちちが ちぎキューんかいちいからさびたん クリエイターぬちむくみてぃ diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index b39a041ee..a497844ab 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -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-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 231de5fc4..443809526 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -664,7 +664,7 @@ 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 diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index fc68ce5eb..596fbe263 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -715,7 +715,7 @@ Плејлисте које су затамњене већ садрже ову ставку. LeakCanary није доступан Додирните да бисте преузели %s - Провери ажурирања + Провери ажурирања Преглед сличице траке за претрагу Прикажи следеће стримове Прикажи/сакриј стримове diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index be8e2894c..a5c9aaf2b 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -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 diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index dfac4fb47..fd12b6a35 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -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 diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 9348c448d..1227d7db4 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -668,7 +668,7 @@ Заплановано наступним Запланувати наступним Обробка… Трохи заждіть - Перевірити наявність оновлень + Перевірити наявність оновлень Перевірка нових версій вручну Перевірка оновлень… Нові записи стрічки diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index cdd6f6e31..393d3cdba 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -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 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 89c215943..29b1fc302 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -642,7 +642,7 @@ 处理中…可能需要一些时间 手动检查新版本 检查更新中… - 检查更新 + 检查更新 新订阅源条目 显示\"使播放器崩溃\" 在使用播放器时显示一个崩溃选项 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index c91731314..f293773bd 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -288,7 +288,7 @@ 節奏 播放速度控掣 系統預設 - 檢查有冇更新 + 檢查有冇更新 個 App 用咩語言 睇咗嗰啲剷咗佢 係咪要剷走睇咗嘅影片? diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index f2eb503ab..cb9279c41 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -640,7 +640,7 @@ 已將下一個加入佇列 將下一個加入佇列 正在處理……可能需要一點時間 - 檢查更新 + 檢查更新 手動檢查新版本 正在檢查更新…… 新 feed 項目 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b9880282f..5433c88c7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -556,7 +556,8 @@ Updates Show a notification to prompt app update when a new version is available - Check for updates + Check for updates + @string/check_for_updates Manually check for new versions Minimize on app switch From a3bbbf03b411be091c85b01ba3317d7d613f38fd Mon Sep 17 00:00:00 2001 From: Tobi Date: Wed, 27 Mar 2024 21:27:20 +0100 Subject: [PATCH 085/123] Ask for consent before starting update checks NewPipe is contacting its servers without asking for the users' consent. This is categorized as "tracking" by F-Droid (see https://github.com/TeamNewPipe/NewPipe/discussions/10785). This commit disables checking for udpates by default and adds a dialog asking for the user's consent to automatically check for updates if the app version is eligible for them. After upgrading to a version containing this commit the user is asked directly on the first app start. On fresh installs however, showing it on the first app start contributes to a bad onboarding an welcoming experience. Therefore, the dialog is shown at the second app start. Co-authored-by: Stypox --- app/src/main/java/org/schabi/newpipe/App.java | 13 ++++++- .../java/org/schabi/newpipe/MainActivity.java | 7 ++++ .../newpipe/settings/NewPipeSettings.java | 16 +++----- .../newpipe/settings/SettingMigrations.java | 6 +-- .../settings/UpdateSettingsFragment.java | 37 +++++++++++++++++++ app/src/main/res/values/settings_keys.xml | 1 + app/src/main/res/values/strings.xml | 4 ++ app/src/main/res/xml/update_settings.xml | 2 +- 8 files changed, 70 insertions(+), 16 deletions(-) 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 a096e7eaf..346084b62 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -79,6 +79,7 @@ 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; @@ -86,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; @@ -167,6 +169,11 @@ public class MainActivity extends AppCompatActivity { // if this is enabled by the user. NotificationWorker.initialize(this); } + if (!UpdateSettingsFragment.wasUserAskedForConsent(this) + && ReleaseVersionUtil.INSTANCE.isReleaseApk() + && !App.getApp().isFirstRun()) { + UpdateSettingsFragment.askForConsentToUpdateChecks(this); + } } @Override 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 f280324cf..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); @@ -68,7 +63,7 @@ public final class NewPipeSettings { saveDefaultVideoDownloadDirectory(context); saveDefaultAudioDownloadDirectory(context); - disableMediaTunnelingIfNecessary(context, isFirstRun); + disableMediaTunnelingIfNecessary(context); } static void saveDefaultVideoDownloadDirectory(final Context context) { @@ -146,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 = @@ -162,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/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/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/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 3087d1fc2..fb68a464d 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -494,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 5433c88c7..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 @@ -557,8 +559,10 @@ Updates Show a notification to prompt app update when a new version is available 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 diff --git a/app/src/main/res/xml/update_settings.xml b/app/src/main/res/xml/update_settings.xml index a44555edf..becd5c5c9 100644 --- a/app/src/main/res/xml/update_settings.xml +++ b/app/src/main/res/xml/update_settings.xml @@ -4,7 +4,7 @@ android:title="@string/settings_category_updates_title"> Date: Fri, 29 Mar 2024 11:08:33 +0100 Subject: [PATCH 086/123] update_app_key default value should be false --- app/src/main/java/org/schabi/newpipe/MainActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 346084b62..094eb5197 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -183,7 +183,7 @@ 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)) { // Start the worker which is checking all conditions // and eventually searching for a new version. NewVersionWorker.enqueueNewVersionCheckingWork(app, false); From 92402685f81bfc25f37efe4d1d6ff861486c5dd6 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Mar 2024 11:14:30 +0100 Subject: [PATCH 087/123] Improve new version checks before running --- app/src/main/java/org/schabi/newpipe/MainActivity.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 094eb5197..175694125 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -170,8 +170,8 @@ public class MainActivity extends AppCompatActivity { NotificationWorker.initialize(this); } if (!UpdateSettingsFragment.wasUserAskedForConsent(this) - && ReleaseVersionUtil.INSTANCE.isReleaseApk() - && !App.getApp().isFirstRun()) { + && !App.getApp().isFirstRun() + && ReleaseVersionUtil.INSTANCE.isReleaseApk()) { UpdateSettingsFragment.askForConsentToUpdateChecks(this); } } @@ -183,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), false)) { + 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); From bef5907ec3be2f223369492c35dc3ba63c7f56cb Mon Sep 17 00:00:00 2001 From: bg1722 Date: Sat, 10 Apr 2021 23:26:20 +0200 Subject: [PATCH 088/123] show OverallDuration in Playlist earlier only overall amount of videos was shown. Now overall duration is shown there too - as formatted by existing Localization.concatenateStrings() and Localization.getDurationString(). show all videos OverallDuration in local Playlist too refactor to make implementation in LocalPlaylistFragment and PlaylistFragment more obviously similar unfortunately could not refactor upto BaseLocalListFragment revert the changes for online Playlists because they are paginated and may be infinite i.e. correct count may come only from the service->extractor chain which unfortunately does not give overall duration yet next try to improve user-experience with online Playlist just show that duration is longer (">") than the calculated value in case there is more page(s) even more improve user-experience for online Playlist by adding the duration of next items as soon as they are made visible make showing of playlists duration configurable, disabled by default adjusted duration to be handled as long because it comes as long from extractor no idea why I handled it as int earlier Revert "make showing of playlists duration configurable, disabled by default", refactor This reverts commit bc1ba17a20d3dd1763210f81d7ca67c5f1734a3d. Fix rebase Apply review Rename video -> stream Remove unused settings keys --- .../list/playlist/PlaylistFragment.java | 29 +++++++++++++++++-- .../local/playlist/LocalPlaylistFragment.java | 24 ++++++++++----- .../org/schabi/newpipe/util/Localization.java | 21 +++++++++++++- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index ab3963d61..998ea0624 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -89,6 +89,9 @@ 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)) + ); + } + } + } 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..c4fe3b896 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 @@ -502,7 +502,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment { itemListAdapter.clearStreamItemList(); itemListAdapter.addItems(itemsToKeep); - setVideoCount(itemListAdapter.getItemsList().size()); + setStreamCountAndOverallDuration(itemListAdapter.getItemsList()); saveChanges(); hideLoading(); @@ -684,7 +684,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)) + ); } } 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 0485413cc..5d73d21f0 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -238,7 +238,25 @@ public final class Localization { } } + /** + * 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); + } + + /** + * 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 + * @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 String output; final long days = duration / (24 * 60 * 60L); /* greater than a day */ @@ -256,7 +274,8 @@ public final class Localization { } else { output = String.format(Locale.US, "%d:%02d", minutes, seconds); } - return output; + final String durationPostfix = isDurationComplete ? "" : "+"; + return output + durationPostfix; } /** From 4591c096378eb7254092e0692cf76852ceaed766 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Mar 2024 17:59:28 +0100 Subject: [PATCH 089/123] Apply review --- .../schabi/newpipe/database/Migrations.java | 19 ++-- .../playlist/PlaylistDuplicatesEntry.java | 1 + .../database/playlist/PlaylistLocalItem.java | 74 --------------- .../playlist/dao/PlaylistRemoteDAO.java | 2 +- .../playlist/dao/PlaylistStreamDAO.java | 22 +---- .../local/bookmark/BookmarkFragment.java | 24 ++--- .../local/bookmark/MergedPlaylistManager.java | 95 +++++++++++++++++++ .../local/playlist/LocalPlaylistManager.java | 15 +-- .../local/playlist/RemotePlaylistManager.java | 6 +- .../settings/SelectPlaylistFragment.java | 6 +- 10 files changed, 123 insertions(+), 141 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/local/bookmark/MergedPlaylistManager.java 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 fa470c2f2..9d641965d 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -258,19 +258,19 @@ public final class Migrations { + "(`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 DEFAULT 0)"); + + "`display_index` INTEGER NOT NULL)"); database.execSQL("INSERT INTO `playlists_tmp` " - + "(`uid`, `name`, `is_thumbnail_permanent`, `thumbnail_stream_id`) " - + "SELECT `uid`, `name`, `is_thumbnail_permanent`, `thumbnail_stream_id` " + + "(`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. + // 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`"); - // Create index on the new table. - database.execSQL("CREATE INDEX `index_playlists_name` ON `playlists` (`name`)"); - // Update remote_playlists. // Create a temp table to initialize display_index. @@ -285,13 +285,12 @@ public final class Migrations { + "SELECT `uid`, `service_id`, `name`, `url`, `thumbnail_url`, `uploader`, " + "`stream_count` FROM `remote_playlists`"); - // Replace the old table. + // 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 INDEX `index_remote_playlists_name` " - + "ON `remote_playlists` (`name`)"); database.execSQL("CREATE UNIQUE INDEX `index_remote_playlists_service_id_url` " + "ON `remote_playlists` (`service_id`, `url`)"); 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 dcd3b2b6c..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,6 +13,7 @@ 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, 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 efd7120d3..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,12 +1,6 @@ package org.schabi.newpipe.database.playlist; import org.schabi.newpipe.database.LocalItem; -import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; public interface PlaylistLocalItem extends LocalItem { String getOrderingName(); @@ -16,72 +10,4 @@ public interface PlaylistLocalItem extends LocalItem { long getUid(); void setDisplayIndex(long displayIndex); - - /** - * 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 - * @param remotePlaylists remote playlists - * @return merged playlists - */ - static List merge( - final List localPlaylists, - final List remotePlaylists) { - Collections.sort(localPlaylists, - Comparator.comparingLong(PlaylistMetadataEntry::getDisplayIndex)); - Collections.sort(remotePlaylists, - Comparator.comparingLong(PlaylistRemoteEntity::getDisplayIndex)); - - // 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; - } - - 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); - } - - static void addItemsWithSameIndex(final List result, - final List itemsWithSameIndex) { - if (itemsWithSameIndex.size() > 1) { - 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/database/playlist/dao/PlaylistRemoteDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java index 8118bc40f..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 @@ -42,7 +42,7 @@ public interface PlaylistRemoteDAO extends BasicDAO { @Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " ORDER BY " + REMOTE_PLAYLIST_DISPLAY_INDEX) - Flowable> getDisplayIndexOrderedPlaylists(); + Flowable> getPlaylists(); @Query("SELECT " + REMOTE_PLAYLIST_ID + " FROM " + REMOTE_PLAYLIST_TABLE + " WHERE " + REMOTE_PLAYLIST_URL + " = :url " 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 d795e6ea7..4e1c163a4 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 @@ -92,26 +92,6 @@ public interface PlaylistStreamDAO extends BasicDAO { + " ORDER BY " + JOIN_INDEX + " ASC") Flowable> getOrderedStreamsOf(long playlistId); - @Transaction - @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 + "'" - + " ELSE (SELECT " + STREAM_THUMBNAIL_URL - + " FROM " + STREAM_TABLE - + " WHERE " + STREAM_TABLE + "." + STREAM_ID + " = " + PLAYLIST_THUMBNAIL_STREAM_ID - + " ) END AS " + PLAYLIST_THUMBNAIL_URL + ", " - - + "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT - + " FROM " + PLAYLIST_TABLE - + " 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") - Flowable> getPlaylistMetadata(); - @Transaction @Query("SELECT " + PLAYLIST_ID + ", " + PLAYLIST_NAME + ", " + PLAYLIST_THUMBNAIL_PERMANENT + ", " + PLAYLIST_THUMBNAIL_STREAM_ID + ", " @@ -130,7 +110,7 @@ public interface PlaylistStreamDAO extends BasicDAO { + " ON " + PLAYLIST_TABLE + "." + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID + " GROUP BY " + PLAYLIST_ID + " ORDER BY " + PLAYLIST_DISPLAY_INDEX) - Flowable> getDisplayIndexOrderedPlaylistMetadata(); + Flowable> getPlaylistMetadata(); @RewriteQueriesToDropUnusedColumns @Transaction diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 41acd2615..59e2582ff 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.local.bookmark; +import static org.schabi.newpipe.local.bookmark.MergedPlaylistManager.getMergedOrderedPlaylists; import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; import android.content.DialogInterface; @@ -47,7 +48,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; @@ -184,9 +184,7 @@ public final class BookmarkFragment extends BaseLocalListFragment> 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 + */ + private 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/playlist/LocalPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java index e153f0a10..461ac2d0a 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 { @@ -108,10 +107,6 @@ public class LocalPlaylistManager { })).subscribeOn(Schedulers.io()); } - public Flowable> getPlaylists() { - return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io()); - } - public Flowable> getDistinctPlaylistStreams(final long playlistId) { return playlistStreamTable .getStreamsWithoutDuplicates(playlistId).subscribeOn(Schedulers.io()); @@ -129,20 +124,14 @@ public class LocalPlaylistManager { .subscribeOn(Schedulers.io()); } - public Flowable> getDisplayIndexOrderedPlaylists() { - return playlistStreamTable.getDisplayIndexOrderedPlaylistMetadata() - .subscribeOn(Schedulers.io()); + public Flowable> getPlaylists() { + return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io()); } public Flowable> getPlaylistStreams(final long playlistId) { return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io()); } - public Single deletePlaylist(final long playlistId) { - return Single.fromCallable(() -> playlistTable.deletePlaylist(playlistId)) - .subscribeOn(Schedulers.io()); - } - public Maybe renamePlaylist(final long playlistId, final String name) { return modifyPlaylist(playlistId, name, THUMBNAIL_ID_LEAVE_UNCHANGED, false, -1); } 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 45d4ef644..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 @@ -23,11 +23,7 @@ public class RemotePlaylistManager { } public Flowable> getPlaylists() { - return playlistRemoteTable.getAll().subscribeOn(Schedulers.io()); - } - - public Flowable> getDisplayIndexOrderedPlaylists() { - return playlistRemoteTable.getDisplayIndexOrderedPlaylists().subscribeOn(Schedulers.io()); + return playlistRemoteTable.getPlaylists().subscribeOn(Schedulers.io()); } public Flowable> getPlaylist(final PlaylistInfo info) { 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 3e97d42e6..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.getDisplayIndexOrderedPlaylists(), - remotePlaylistManager.getDisplayIndexOrderedPlaylists(), PlaylistLocalItem::merge) + disposable = getMergedOrderedPlaylists(localPlaylistManager, remotePlaylistManager) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::displayPlaylists, this::onError); } From 92e9c3e42eed0d8f5f29210e6527f8351edfc4da Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Mar 2024 20:43:55 +0100 Subject: [PATCH 090/123] Fix DatabaseMigrationTest Complete removal of unneeded index, and remove default value for `remote_playlists.display_index`. --- .../9.json | 25 ++-------- .../newpipe/database/DatabaseMigrationTest.kt | 47 ++++++++++++------- .../schabi/newpipe/database/Migrations.java | 7 +-- .../playlist/model/PlaylistEntity.java | 5 +- .../playlist/model/PlaylistRemoteEntity.java | 1 - 5 files changed, 37 insertions(+), 48 deletions(-) diff --git a/app/schemas/org.schabi.newpipe.database.AppDatabase/9.json b/app/schemas/org.schabi.newpipe.database.AppDatabase/9.json index 0fcd383af..aced06c0a 100644 --- a/app/schemas/org.schabi.newpipe.database.AppDatabase/9.json +++ b/app/schemas/org.schabi.newpipe.database.AppDatabase/9.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 9, - "identityHash": "94596ea2227c63dd78b472ea4a83f1c4", + "identityHash": "7591e8039faa74d8c0517dc867af9d3e", "entities": [ { "tableName": "subscriptions", @@ -362,17 +362,7 @@ "uid" ] }, - "indices": [ - { - "name": "index_playlists_name", - "unique": false, - "columnNames": [ - "name" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_playlists_name` ON `${TABLE_NAME}` (`name`)" - } - ], + "indices": [], "foreignKeys": [] }, { @@ -511,15 +501,6 @@ ] }, "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, @@ -743,7 +724,7 @@ "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, '94596ea2227c63dd78b472ea4a83f1c4')" + "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 f71880366..a34cfece6 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -122,8 +122,10 @@ class DatabaseMigrationTest { ) testHelper.runMigrationsAndValidate( - AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, - true, Migrations.MIGRATION_5_6 + AppDatabase.DATABASE_NAME, + Migrations.DB_VER_9, + true, + Migrations.MIGRATION_8_9 ) val migratedDatabaseV3 = getMigratedDatabase() @@ -209,6 +211,11 @@ class DatabaseMigrationTest { 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() @@ -220,25 +227,27 @@ class DatabaseMigrationTest { @Test fun migrateDatabaseFrom8to9() { - val databaseInV5 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_5) + val databaseInV8 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_8) val localUid1: Long val localUid2: Long val remoteUid1: Long val remoteUid2: Long - databaseInV5.run { + databaseInV8.run { localUid1 = insert( "playlists", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("name", DEFAULT_NAME + "1") - put("thumbnail_url", DEFAULT_THUMBNAIL) + put("is_thumbnail_permanent", false) + put("thumbnail_stream_id", -1) } ) localUid2 = insert( "playlists", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { put("name", DEFAULT_NAME + "2") - put("thumbnail_url", DEFAULT_THUMBNAIL) + put("is_thumbnail_permanent", false) + put("thumbnail_stream_id", -1) } ) delete( @@ -267,33 +276,35 @@ class DatabaseMigrationTest { } testHelper.runMigrationsAndValidate( - AppDatabase.DATABASE_NAME, Migrations.DB_VER_6, - true, Migrations.MIGRATION_5_6 + AppDatabase.DATABASE_NAME, + Migrations.DB_VER_9, + true, + Migrations.MIGRATION_8_9 ) - val migratedDatabaseV6 = getMigratedDatabase() - var localListFromDB = migratedDatabaseV6.playlistDAO().all.blockingFirst() - var remoteListFromDB = migratedDatabaseV6.playlistRemoteDAO().all.blockingFirst() + 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(0, localListFromDB[0].displayIndex) + assertEquals(-1, localListFromDB[0].displayIndex) assertEquals(1, remoteListFromDB.size) assertEquals(remoteUid1, remoteListFromDB[0].uid) - assertEquals(0, remoteListFromDB[0].displayIndex) + assertEquals(-1, remoteListFromDB[0].displayIndex) - val localUid3 = migratedDatabaseV6.playlistDAO().insert( - PlaylistEntity(DEFAULT_NAME + "3", DEFAULT_THUMBNAIL, -1) + val localUid3 = migratedDatabaseV9.playlistDAO().insert( + PlaylistEntity(DEFAULT_NAME + "3", false, -1, -1) ) - val remoteUid3 = migratedDatabaseV6.playlistRemoteDAO().insert( + val remoteUid3 = migratedDatabaseV9.playlistRemoteDAO().insert( PlaylistRemoteEntity( DEFAULT_THIRD_SERVICE_ID, DEFAULT_NAME, DEFAULT_THIRD_URL, DEFAULT_THUMBNAIL, DEFAULT_UPLOADER_NAME, -1, 10 ) ) - localListFromDB = migratedDatabaseV6.playlistDAO().all.blockingFirst() - remoteListFromDB = migratedDatabaseV6.playlistRemoteDAO().all.blockingFirst() + localListFromDB = migratedDatabaseV9.playlistDAO().all.blockingFirst() + remoteListFromDB = migratedDatabaseV9.playlistRemoteDAO().all.blockingFirst() assertEquals(2, localListFromDB.size) assertEquals(localUid3, localListFromDB[1].uid) assertEquals(-1, localListFromDB[1].displayIndex) 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 9d641965d..c9f630869 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -278,12 +278,13 @@ public final class Migrations { + "(`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 DEFAULT 0," + + "`display_index` INTEGER NOT NULL," + "`stream_count` INTEGER)"); database.execSQL("INSERT INTO `remote_playlists_tmp` (`uid`, `service_id`, " - + "`name`, `url`, `thumbnail_url`, `uploader`, `stream_count`)" + + "`name`, `url`, `thumbnail_url`, `uploader`, `display_index`, " + + "`stream_count`)" + "SELECT `uid`, `service_id`, `name`, `url`, `thumbnail_url`, `uploader`, " - + "`stream_count` FROM `remote_playlists`"); + + "-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. 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 cb18027d0..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 @@ -3,17 +3,14 @@ package org.schabi.newpipe.database.playlist.model; import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.Ignore; -import androidx.room.Index; 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://" 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 50c3899f1..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 { From e66e1b542c937a2df7e658102580225aededec4b Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Mar 2024 20:55:24 +0100 Subject: [PATCH 091/123] Also sort playlist duplicates by display index --- .../schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4e1c163a4..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 @@ -154,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); } From 90979e2a8130655aacaa36b024816b832fbcbc06 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 29 Mar 2024 20:58:07 +0100 Subject: [PATCH 092/123] Fix PlaylistLocalItemTest --- .../local/bookmark/MergedPlaylistManager.java | 2 +- .../playlist/PlaylistLocalItemTest.java | 25 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) 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 index 6b0eda132..25eb2f652 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/MergedPlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/MergedPlaylistManager.java @@ -39,7 +39,7 @@ public final class MergedPlaylistManager { * @param remotePlaylists remote playlists, already sorted by display index * @return merged playlists */ - private static List merge( + public static List merge( final List localPlaylists, final List remotePlaylists) { diff --git a/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java b/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java index ab6315d91..847c54aa8 100644 --- a/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java +++ b/app/src/test/java/org/schabi/newpipe/database/playlist/PlaylistLocalItemTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; +import org.schabi.newpipe.local.bookmark.MergedPlaylistManager; import java.util.ArrayList; import java.util.List; @@ -15,7 +16,7 @@ public class PlaylistLocalItemTest { final List localPlaylists = new ArrayList<>(); final List remotePlaylists = new ArrayList<>(); final List mergedPlaylists = - PlaylistLocalItem.merge(localPlaylists, remotePlaylists); + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); assertEquals(0, mergedPlaylists.size()); } @@ -24,11 +25,11 @@ public class PlaylistLocalItemTest { public void onlyLocalPlaylists() { final List localPlaylists = new ArrayList<>(); final List remotePlaylists = new ArrayList<>(); - localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 0, 1)); - localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 1, 1)); - localPlaylists.add(new PlaylistMetadataEntry(3, "name3", "", 3, 1)); + 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 = - PlaylistLocalItem.merge(localPlaylists, remotePlaylists); + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); assertEquals(3, mergedPlaylists.size()); assertEquals(0, mergedPlaylists.get(0).getDisplayIndex()); @@ -47,7 +48,7 @@ public class PlaylistLocalItemTest { remotePlaylists.add(new PlaylistRemoteEntity( 3, "name3", "url3", "", "", 4, 1L)); final List mergedPlaylists = - PlaylistLocalItem.merge(localPlaylists, remotePlaylists); + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); assertEquals(3, mergedPlaylists.size()); assertEquals(1, mergedPlaylists.get(0).getDisplayIndex()); @@ -59,14 +60,14 @@ public class PlaylistLocalItemTest { public void sameIndexWithDifferentName() { final List localPlaylists = new ArrayList<>(); final List remotePlaylists = new ArrayList<>(); - localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 0, 1)); - localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 1, 1)); + 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 = - PlaylistLocalItem.merge(localPlaylists, remotePlaylists); + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); assertEquals(4, mergedPlaylists.size()); assertTrue(mergedPlaylists.get(0) instanceof PlaylistMetadataEntry); @@ -83,14 +84,14 @@ public class PlaylistLocalItemTest { public void sameNameWithDifferentIndex() { final List localPlaylists = new ArrayList<>(); final List remotePlaylists = new ArrayList<>(); - localPlaylists.add(new PlaylistMetadataEntry(1, "name1", "", 1, 1)); - localPlaylists.add(new PlaylistMetadataEntry(2, "name2", "", 3, 1)); + 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 = - PlaylistLocalItem.merge(localPlaylists, remotePlaylists); + MergedPlaylistManager.merge(localPlaylists, remotePlaylists); assertEquals(4, mergedPlaylists.size()); assertTrue(mergedPlaylists.get(0) instanceof PlaylistRemoteEntity); From 3cc0205def80a6732ea374655a272ac7d3457503 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Mar 2024 14:14:31 +0100 Subject: [PATCH 093/123] Fix inconsistencies when removing playlist Remove checkDisplayIndexModified because it was causing more problems than it solved. Now when adding new playlists they won't necessarily appear at the top, but will get sorted alphabetically along with the other playlists with index -1. This will be the case until any playlist is sorted, at which point all indices are assigned and newly added playlists will appear at the top again. --- .../local/bookmark/BookmarkFragment.java | 31 +++---------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 59e2582ff..922429382 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -56,7 +56,7 @@ public final class BookmarkFragment extends BaseLocalListFragment> deletedItems; @@ -259,7 +260,6 @@ public final class BookmarkFragment extends BaseLocalListFragment subscriptions) { if (debounceSaver == null || !debounceSaver.getIsModified()) { - checkDisplayIndexModified(subscriptions); handleResult(subscriptions); isLoadingComplete.set(true); } @@ -349,30 +349,9 @@ public final class BookmarkFragment extends BaseLocalListFragment result) { - if (debounceSaver != null && debounceSaver.getIsModified()) { - return; - } - - // Check if the display index does not match the actual index in the list. - // This may happen when a new list is created - // or on the first run after database migration - // or display index is not continuous for some reason - // or the user changes the display index. - boolean isDisplayIndexModified = false; - for (int i = 0; i < result.size(); i++) { - final PlaylistLocalItem item = result.get(i); - if (item.getDisplayIndex() != i) { - isDisplayIndexModified = true; - break; - } - } - - if (debounceSaver != null && isDisplayIndexModified) { + if (debounceSaver != null) { debounceSaver.setHasChangesToSave(); + saveImmediate(); } } @@ -482,7 +461,7 @@ public final class BookmarkFragment extends BaseLocalListFragment Date: Sat, 30 Mar 2024 14:36:31 +0100 Subject: [PATCH 094/123] Fix warnings and allow moving only up and down even in grid --- .../newpipe/local/bookmark/BookmarkFragment.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 922429382..a366723e0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.bookmark; import static org.schabi.newpipe.local.bookmark.MergedPlaylistManager.getMergedOrderedPlaylists; -import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; import android.content.DialogInterface; import android.os.Bundle; @@ -244,7 +243,7 @@ public final class BookmarkFragment extends BaseLocalListFragment> getPlaylistsSubscriber() { - return new Subscriber>() { + return new Subscriber<>() { @Override public void onSubscribe(final Subscription s) { showLoading(); @@ -276,7 +275,6 @@ public final class BookmarkFragment extends BaseLocalListFragment Date: Sat, 30 Mar 2024 14:46:13 +0100 Subject: [PATCH 095/123] Undo some unneeded changes to LocalPlaylistManager --- .../local/playlist/LocalPlaylistManager.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) 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 461ac2d0a..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 @@ -133,18 +133,13 @@ public class LocalPlaylistManager { } public Maybe renamePlaylist(final long playlistId, final String name) { - return modifyPlaylist(playlistId, name, THUMBNAIL_ID_LEAVE_UNCHANGED, false, -1); + return modifyPlaylist(playlistId, name, THUMBNAIL_ID_LEAVE_UNCHANGED, false); } public Maybe changePlaylistThumbnail(final long playlistId, final long thumbnailStreamId, final boolean isPermanent) { - return modifyPlaylist(playlistId, null, thumbnailStreamId, isPermanent, -1); - } - - public Maybe updatePlaylistDisplayIndex(final long playlistId, - final long displayIndex) { - return modifyPlaylist(playlistId, null, THUMBNAIL_ID_LEAVE_UNCHANGED, false, displayIndex); + return modifyPlaylist(playlistId, null, thumbnailStreamId, isPermanent); } public long getPlaylistThumbnailStreamId(final long playlistId) { @@ -168,8 +163,7 @@ public class LocalPlaylistManager { private Maybe modifyPlaylist(final long playlistId, @Nullable final String name, final long thumbnailStreamId, - final boolean isPermanent, - final long displayIndex) { + final boolean isPermanent) { return playlistTable.getPlaylist(playlistId) .firstElement() .filter(playlistEntities -> !playlistEntities.isEmpty()) @@ -182,9 +176,6 @@ public class LocalPlaylistManager { playlist.setThumbnailStreamId(thumbnailStreamId); playlist.setIsThumbnailPermanent(isPermanent); } - if (displayIndex != -1) { - playlist.setDisplayIndex(displayIndex); - } return playlistTable.update(playlist); }).subscribeOn(Schedulers.io()); } From 5e7ad6ffd160633f15d5ad02f651ee3207539224 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Mar 2024 15:14:18 +0100 Subject: [PATCH 096/123] Fix fragments without empty constructor --- .../schabi/newpipe/fragments/detail/DescriptionFragment.java | 3 +++ .../newpipe/fragments/list/channel/ChannelAboutFragment.java | 2 ++ 2 files changed, 5 insertions(+) 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 7fc66b42f..6a7c5ca73 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 @@ -30,6 +30,9 @@ public class DescriptionFragment extends BaseDescriptionFragment { this.streamInfo = streamInfo; } + public DescriptionFragment() { + } + @Nullable @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java index 674a13c5b..a67d37dc1 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java @@ -30,6 +30,8 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { this.channelInfo = channelInfo; } + public ChannelAboutFragment() { + } @Override protected void initViews(final View rootView, final Bundle savedInstanceState) { From e42c9abdde72e8af8fcbdaef5a46ce329a5152d4 Mon Sep 17 00:00:00 2001 From: pratyaksh1610 Date: Tue, 20 Dec 2022 01:44:42 +0530 Subject: [PATCH 097/123] moved player notification to notification section --- app/src/main/res/xml/appearance_settings.xml | 8 -------- app/src/main/res/xml/notifications_settings.xml | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) 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" /> - - + + Date: Sun, 31 Dec 2023 16:28:21 +0100 Subject: [PATCH 098/123] Move import export manager to separate folder --- .../BackupRestoreSettingsFragment.java | 5 ++-- .../ImportExportManager.kt} | 5 ++-- ...agerTest.kt => ImportExportManagerTest.kt} | 25 ++++++++++--------- 3 files changed, 19 insertions(+), 16 deletions(-) rename app/src/main/java/org/schabi/newpipe/settings/{ContentSettingsManager.kt => export/ImportExportManager.kt} (96%) rename app/src/test/java/org/schabi/newpipe/settings/{ContentSettingsManagerTest.kt => ImportExportManagerTest.kt} (87%) diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java index bc24fbe81..842023e50 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -24,6 +24,7 @@ import androidx.preference.PreferenceManager; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorUtil; +import org.schabi.newpipe.settings.export.ImportExportManager; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.StoredFileHelper; import org.schabi.newpipe.util.NavigationHelper; @@ -42,7 +43,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { private final SimpleDateFormat exportDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US); - private ContentSettingsManager manager; + private ImportExportManager manager; private String importExportDataPathKey; private final ActivityResultLauncher requestImportPathLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), @@ -57,7 +58,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { @Nullable final String rootKey) { final File homeDir = ContextCompat.getDataDir(requireContext()); Objects.requireNonNull(homeDir); - manager = new ContentSettingsManager(new NewPipeFileLocator(homeDir)); + manager = new ImportExportManager(new NewPipeFileLocator(homeDir)); manager.deleteSettingsFile(); importExportDataPathKey = getString(R.string.import_export_data_path); diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt similarity index 96% rename from app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt rename to app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index df56de516..9954a2b36 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -1,8 +1,9 @@ -package org.schabi.newpipe.settings +package org.schabi.newpipe.settings.export import android.content.SharedPreferences import android.util.Log import org.schabi.newpipe.MainActivity.DEBUG +import org.schabi.newpipe.settings.NewPipeFileLocator import org.schabi.newpipe.streams.io.SharpOutputStream import org.schabi.newpipe.streams.io.StoredFileHelper import org.schabi.newpipe.util.ZipHelper @@ -11,7 +12,7 @@ import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.util.zip.ZipOutputStream -class ContentSettingsManager(private val fileLocator: NewPipeFileLocator) { +class ImportExportManager(private val fileLocator: NewPipeFileLocator) { companion object { const val TAG = "ContentSetManager" } diff --git a/app/src/test/java/org/schabi/newpipe/settings/ContentSettingsManagerTest.kt b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt similarity index 87% rename from app/src/test/java/org/schabi/newpipe/settings/ContentSettingsManagerTest.kt rename to app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt index ec41a77f8..7b219df18 100644 --- a/app/src/test/java/org/schabi/newpipe/settings/ContentSettingsManagerTest.kt +++ b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt @@ -17,6 +17,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.Mockito.withSettings import org.mockito.junit.MockitoJUnitRunner +import org.schabi.newpipe.settings.export.ImportExportManager import org.schabi.newpipe.streams.io.StoredFileHelper import us.shandian.giga.io.FileStream import java.io.File @@ -25,10 +26,10 @@ import java.nio.file.Files import java.util.zip.ZipFile @RunWith(MockitoJUnitRunner::class) -class ContentSettingsManagerTest { +class ImportExportManagerTest { companion object { - private val classloader = ContentSettingsManager::class.java.classLoader!! + private val classloader = ImportExportManager::class.java.classLoader!! } private lateinit var fileLocator: NewPipeFileLocator @@ -54,7 +55,7 @@ class ContentSettingsManagerTest { val output = File.createTempFile("newpipe_", "") `when`(storedFileHelper.stream).thenReturn(FileStream(output)) - ContentSettingsManager(fileLocator).exportDatabase(sharedPreferences, storedFileHelper) + ImportExportManager(fileLocator).exportDatabase(sharedPreferences, storedFileHelper) val zipFile = ZipFile(output) val entries = zipFile.entries().toList() @@ -77,7 +78,7 @@ class ContentSettingsManagerTest { val settings = File.createTempFile("newpipe_", "") `when`(fileLocator.settings).thenReturn(settings) - ContentSettingsManager(fileLocator).deleteSettingsFile() + ImportExportManager(fileLocator).deleteSettingsFile() assertFalse(settings.exists()) } @@ -87,7 +88,7 @@ class ContentSettingsManagerTest { val settings = File("non_existent") `when`(fileLocator.settings).thenReturn(settings) - ContentSettingsManager(fileLocator).deleteSettingsFile() + ImportExportManager(fileLocator).deleteSettingsFile() assertFalse(settings.exists()) } @@ -98,7 +99,7 @@ class ContentSettingsManagerTest { Assume.assumeTrue(dir.delete()) `when`(fileLocator.dbDir).thenReturn(dir) - ContentSettingsManager(fileLocator).ensureDbDirectoryExists() + ImportExportManager(fileLocator).ensureDbDirectoryExists() assertTrue(dir.exists()) } @@ -107,7 +108,7 @@ class ContentSettingsManagerTest { val dir = Files.createTempDirectory("newpipe_").toFile() `when`(fileLocator.dbDir).thenReturn(dir) - ContentSettingsManager(fileLocator).ensureDbDirectoryExists() + ImportExportManager(fileLocator).ensureDbDirectoryExists() assertTrue(dir.exists()) } @@ -124,7 +125,7 @@ class ContentSettingsManagerTest { val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(zip)) - val success = ContentSettingsManager(fileLocator).extractDb(storedFileHelper) + val success = ImportExportManager(fileLocator).extractDb(storedFileHelper) assertTrue(success) assertFalse(dbJournal.exists()) @@ -143,7 +144,7 @@ class ContentSettingsManagerTest { val emptyZip = File(classloader.getResource("settings/empty.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) - val success = ContentSettingsManager(fileLocator).extractDb(storedFileHelper) + val success = ImportExportManager(fileLocator).extractDb(storedFileHelper) assertFalse(success) assertTrue(dbJournal.exists()) @@ -159,7 +160,7 @@ class ContentSettingsManagerTest { val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(zip)) - val contains = ContentSettingsManager(fileLocator).extractSettings(storedFileHelper) + val contains = ImportExportManager(fileLocator).extractSettings(storedFileHelper) assertTrue(contains) } @@ -171,7 +172,7 @@ class ContentSettingsManagerTest { val emptyZip = File(classloader.getResource("settings/empty.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) - val contains = ContentSettingsManager(fileLocator).extractSettings(storedFileHelper) + val contains = ImportExportManager(fileLocator).extractSettings(storedFileHelper) assertFalse(contains) } @@ -185,7 +186,7 @@ class ContentSettingsManagerTest { val editor = Mockito.mock(SharedPreferences.Editor::class.java) `when`(preferences.edit()).thenReturn(editor) - ContentSettingsManager(fileLocator).loadSharedPreferences(preferences) + ImportExportManager(fileLocator).loadSharedPreferences(preferences) verify(editor, atLeastOnce()).putBoolean(anyString(), anyBoolean()) verify(editor, atLeastOnce()).putString(anyString(), anyString()) From 235fb926382cfed5a6353106c732e85a9140d6da Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 31 Dec 2023 18:11:35 +0100 Subject: [PATCH 099/123] Make checkstyle accept javadocs with long links --- checkstyle/checkstyle.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/checkstyle/checkstyle.xml b/checkstyle/checkstyle.xml index 3377e3b84..ee091fa9f 100644 --- a/checkstyle/checkstyle.xml +++ b/checkstyle/checkstyle.xml @@ -39,11 +39,13 @@ - + + + From d75a6eaa4164d3fc0ac67bc35048b021b85e9b6f Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 31 Dec 2023 18:21:48 +0100 Subject: [PATCH 100/123] Fix vulnerability with whitelist-aware ObjectInputStream Only a few specific classes are now allowed. --- .../settings/export/ImportExportManager.kt | 5 +- .../export/PreferencesObjectInputStream.java | 58 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 9954a2b36..298841c5f 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -8,7 +8,6 @@ import org.schabi.newpipe.streams.io.SharpOutputStream import org.schabi.newpipe.streams.io.StoredFileHelper import org.schabi.newpipe.util.ZipHelper import java.io.IOException -import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.util.zip.ZipOutputStream @@ -78,7 +77,9 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) { try { val preferenceEditor = preferences.edit() - ObjectInputStream(fileLocator.settings.inputStream()).use { input -> + PreferencesObjectInputStream( + fileLocator.settings.inputStream() + ).use { input -> preferenceEditor.clear() @Suppress("UNCHECKED_CAST") val entries = input.readObject() as Map diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java b/app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java new file mode 100644 index 000000000..0d11b0b61 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java @@ -0,0 +1,58 @@ +package org.schabi.newpipe.settings.export; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.util.Set; + +/** + * An {@link ObjectInputStream} that only allows preferences-related types to be deserialized, to + * prevent injections. The only allowed types are: all primitive types, all boxed primitive types, + * null, strings. HashMap, HashSet and arrays of previously defined types are also allowed. Sources: + * + * cmu.edu + * , + * + * OWASP cheatsheet + * , + * + * Apache's {@code ValidatingObjectInputStream} + * + */ +public class PreferencesObjectInputStream extends ObjectInputStream { + + /** + * Primitive types, strings and other built-in types do not pass through resolveClass() but + * instead have a custom encoding; see + * + * official docs. + */ + private static final Set CLASS_WHITELIST = Set.of( + "java.lang.Boolean", + "java.lang.Byte", + "java.lang.Character", + "java.lang.Short", + "java.lang.Integer", + "java.lang.Long", + "java.lang.Float", + "java.lang.Double", + "java.lang.Void", + "java.util.HashMap", + "java.util.HashSet" + ); + + public PreferencesObjectInputStream(final InputStream in) throws IOException { + super(in); + } + + @Override + protected Class resolveClass(final ObjectStreamClass desc) + throws ClassNotFoundException, IOException { + if (CLASS_WHITELIST.contains(desc.getName())) { + return super.resolveClass(desc); + } else { + throw new ClassNotFoundException("Class not allowed: " + desc.getName()); + } + } +} From d8668ed226dccf36666fc325204acdb29a078f6d Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 27 Mar 2024 15:02:27 +0100 Subject: [PATCH 101/123] Show snackbar error when settings import fails --- .../org/schabi/newpipe/error/UserAction.java | 1 + .../BackupRestoreSettingsFragment.java | 17 ++++- .../settings/export/ImportExportManager.kt | 69 ++++++++----------- 3 files changed, 45 insertions(+), 42 deletions(-) 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 c8701cd77..6ca66e0d2 100644 --- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java @@ -6,6 +6,7 @@ package org.schabi.newpipe.error; public enum UserAction { USER_REPORT("user report"), UI_ERROR("ui error"), + DATABASE_IMPORT_EXPORT("database import or export"), SUBSCRIPTION_CHANGE("subscription change"), SUBSCRIPTION_UPDATE("subscription update"), SUBSCRIPTION_GET("get subscription"), diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java index 842023e50..1d00ef287 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -23,7 +23,9 @@ import androidx.preference.PreferenceManager; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; +import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; +import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.settings.export.ImportExportManager; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.StoredFileHelper; @@ -166,7 +168,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { Toast.makeText(requireContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT) .show(); } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(this, "Exporting database", e); + showErrorSnackbar(e, "Exporting database and settings"); } } @@ -202,7 +204,12 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { final Context context = requireContext(); final SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(context); - manager.loadSharedPreferences(prefs); + try { + manager.loadSharedPreferences(prefs); + } catch (IOException | ClassNotFoundException e) { + showErrorSnackbar(e, "Importing preferences"); + return; + } cleanImport(context, prefs); finishImport(importDataUri); }) @@ -211,7 +218,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { finishImport(importDataUri); } } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(this, "Importing database", e); + showErrorSnackbar(e, "Importing database and settings"); } } @@ -269,4 +276,8 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { .putString(importExportDataPathKey, importExportDataUri.toString()); editor.apply(); } + + private void showErrorSnackbar(final Throwable e, final String request) { + ErrorUtil.showSnackbar(this, new ErrorInfo(e, UserAction.DATABASE_IMPORT_EXPORT, request)); + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 298841c5f..b4503bdd6 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -73,50 +73,41 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) { /** * Remove all shared preferences from the app and load the preferences supplied to the manager. */ + @Throws(IOException::class, ClassNotFoundException::class) fun loadSharedPreferences(preferences: SharedPreferences) { - try { - val preferenceEditor = preferences.edit() + val preferenceEditor = preferences.edit() - PreferencesObjectInputStream( - fileLocator.settings.inputStream() - ).use { input -> - preferenceEditor.clear() - @Suppress("UNCHECKED_CAST") - val entries = input.readObject() as Map - for ((key, value) in entries) { - when (value) { - is Boolean -> { - preferenceEditor.putBoolean(key, value) - } - is Float -> { - preferenceEditor.putFloat(key, value) - } - is Int -> { - preferenceEditor.putInt(key, value) - } - is Long -> { - preferenceEditor.putLong(key, value) - } - is String -> { - preferenceEditor.putString(key, value) - } - is Set<*> -> { - // There are currently only Sets with type String possible - @Suppress("UNCHECKED_CAST") - preferenceEditor.putStringSet(key, value as Set?) - } + PreferencesObjectInputStream( + fileLocator.settings.inputStream() + ).use { input -> + preferenceEditor.clear() + @Suppress("UNCHECKED_CAST") + val entries = input.readObject() as Map + for ((key, value) in entries) { + when (value) { + is Boolean -> { + preferenceEditor.putBoolean(key, value) + } + is Float -> { + preferenceEditor.putFloat(key, value) + } + is Int -> { + preferenceEditor.putInt(key, value) + } + is Long -> { + preferenceEditor.putLong(key, value) + } + is String -> { + preferenceEditor.putString(key, value) + } + is Set<*> -> { + // There are currently only Sets with type String possible + @Suppress("UNCHECKED_CAST") + preferenceEditor.putStringSet(key, value as Set?) } } - preferenceEditor.commit() - } - } catch (e: IOException) { - if (DEBUG) { - Log.e(TAG, "Unable to loadSharedPreferences", e) - } - } catch (e: ClassNotFoundException) { - if (DEBUG) { - Log.e(TAG, "Unable to loadSharedPreferences", e) } + preferenceEditor.commit() } } } From 6afdbd6fd39cd1c5020dafde65215cfa66fa781f Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 27 Mar 2024 15:12:57 +0100 Subject: [PATCH 102/123] Add test: vulnerable settings should fail importing --- .../settings/ImportExportManagerTest.kt | 19 ++++++++++++++++++ .../settings/vulnerable_serialization.zip | Bin 0 -> 3536 bytes 2 files changed, 19 insertions(+) create mode 100644 app/src/test/resources/settings/vulnerable_serialization.zip diff --git a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt index 7b219df18..2743ba098 100644 --- a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt +++ b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt @@ -3,6 +3,7 @@ package org.schabi.newpipe.settings import android.content.SharedPreferences import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse +import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue import org.junit.Assume import org.junit.Before @@ -192,4 +193,22 @@ class ImportExportManagerTest { verify(editor, atLeastOnce()).putString(anyString(), anyString()) verify(editor, atLeastOnce()).putInt(anyString(), anyInt()) } + + @Test + fun `Importing preferences with a serialization injected class should fail`() { + val settings = File.createTempFile("newpipe_", "") + `when`(fileLocator.settings).thenReturn(settings) + + val emptyZip = File(classloader.getResource("settings/vulnerable_serialization.zip")?.file!!) + `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) + Assume.assumeTrue(ImportExportManager(fileLocator).extractSettings(storedFileHelper)) + + val preferences = Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly()) + val editor = Mockito.mock(SharedPreferences.Editor::class.java) + `when`(preferences.edit()).thenReturn(editor) + + assertThrows(ClassNotFoundException::class.java) { + ImportExportManager(fileLocator).loadSharedPreferences(preferences) + } + } } diff --git a/app/src/test/resources/settings/vulnerable_serialization.zip b/app/src/test/resources/settings/vulnerable_serialization.zip new file mode 100644 index 0000000000000000000000000000000000000000..d57a5f8d0150cd11dec35d7c29ed2bcb9e65a774 GIT binary patch literal 3536 zcmZ`+XHb*d)(#yhDpd?dLKO(Th$2W8L3-~>umhOGllAlo0>`Py*abY>n3H!!d^x1XY9> zKt&K94gp@yUJgR`w!depA9d<5lFz4)>zE6Cwx-L-&Ghs7PO(j7@yJ`~0mnvm#O)a2 z`Glju#p3xzO@3G-B}eksQpU^+c6&LjT4BPvgCTQ3***@=FVrFO#cZLJSCR5FdTD!u z_h4w^?2bQkGeZbcW(ixT{Ydzo|SAS^O!U?L2Jw5Kj;v?jOsY zYuaa52x3+Ig6ss2|6AMsh5#V}fHo1z-7O$n{?qF1-Ct+t3}<^U3G2})o^by8;WNVR zhRU5@II!m|ZCMwTFAvW7v5oi(KkmZ+`y$czqR7jW3;!yf5Vc)&o>=E9I4q=O1J_M! zYCx4;76RJIELx)MSPDHl6U&ma~fu`(A%b&q?J|teYAl11FlAcgR)2?bt0h5J;CLrBt|wE;6Chqy+!Dt0I4n zZMHU8d~SQ1Bx=vd5{3>oa8#i)V zMDS@FrS71*x_Ao5^^*GeQ4&_k_AHLxxq8x8#TgE9-cBtDwC2FVH!5Rky~|Hhr~*>T zyfEII2ExG4^@#C$zXS)zHRbO#0dOxDxXw=vwfOXT8TxV6xRLBGy>68fz7F&8D~K0w zAs-7NYwkZ9*@d!3HC0M1eh!J$t&~XaE=yX(6usMUOVK2O7Hk$|0%7$>Pb5qzjPJte z62FQ&AiYO_UY-6O&rGi2@Sr{>uf z3NzNk`9qZX>J1f=6CIB%&1fH7&6!GPMr~_>mvNO}Ffp16F4iNg^zc>AVrOd4Rd*s& zvp4~uw75v>y$2)kaX3=ZeGSUBv``zsAi?s`l4G5liV<~^bhD&M$s(o8T)9goo#&t~ zfIXnD+}jH}c0>AwVD$W(qsZ<{411==Is4C^36YO>9{l9mn@0BlA&w*SKc)Lp_Li%H zb<4{1`e}MveMBrd{1#~aZGVlqe)pvN6nU$yI~HXMY4v}ofT(D&soi87>Ja;_U*Sn5 zHuGA=jbb3Xz3D0Hi`#KmSR1+cpXldLTc73Rxr$pjfoHDzwKtz-JP}fqg#A!nAzeGs z_gnpF&~TjxV%rOOBdf{XwmfJs+ZTgmG$b=rf71lcoj&dH7PuE8Sc}L(MUa2-7#>QM z6|WaL;DP=SJv3ZXUt3+7{*LWEn;dp3TCv>;UVO?SyRM6!DQ^+q)Mv6kXqfkhlAAZ^ zb@M}PN^^VR0#Bs*)nL|4(3;RRQ{gC^pee4Xs;Cp3`&o3hWwrR!Wc>&{z|+|0l486k z_;Imo+J+JhB}>Ktkt~Nq_jbsQ{Q+;!|5VL3;!d7h?GXesojDB>bx|>iK(A8U zJ*n){y>ehG6PdsGP*oIej+o@F-h@g%JUUtf!rI?6RjMrMD=c_;e#phLR5Cd7Gtj;O=Jxi5K!V9?Rh)Q5icXF)%Q<*tv6kU6NkgmUS@>@E=1#GW z5wgoj2{tQ#ix}@eSxKHVy_sV%DX@Eee>#X2#ew3{DW5Dh8T^X5Ce&@pCR+Pa9txH8 z5Xc|2T=g4YY?(vK4b88&7oAC-`ny%$Kl<=4>r19mvnSgyaDW)7f#I{8(M0TmXNQ6Q zWdTMODgJa;Q8qJZ?n||Qf?372hKgQ&ERxP6QS^KLBu%i&tyAM#V56)CdZB_dt35v8 z+h%I_&D3PTuibVs5S@aMMw4|@TA{*yZP`1HbW#I68n@@v=NCSKUnh`Q0X9jl{Knz* zF}Fiz#E?OtL2Wm9>v};^9*XCkf%8RkG&CCe!z%-;z%Xj8$td#YOnW zcR$|=2MxK~9P~&v_||3fQ){vue7nBxMfcjSm00Kho(FNoc1D9-BwUrG0D$i$0N^U& zNA%b8z}Lag&)LJtH`efBFM|SOi1WdYd6l&=8rfw2#8+H-Lh>3$eb5$?$ zaFCHIG$XdqA^xYd9*>1nA><=Bx^3Cxndv%DlJm=|H#} zTuncf5(E`49@GqKr8i5FQIC2MOCv!y1e)av1NKK^bcM2mRXPe~Y|KiIyS{q)QRXI( zfuxjss#GmEI`0?`+I-;%{j&zv{YcM!H+h?bgF8*G$)~PA?L|ubB(l#ZQZ#*~!v^?; z@qxxZKc$IPbmNv- zeIm9I*6lPYcK;1|CN-*1BiiV`X`hB|PDY?e; zpZ&UWn;nubxx~CSY~lN=_+fHO;Hc|p25%EYCQ)=HD?%n#RPR{CR_6JyR_#1y+B8#? zP^2w6y)uFUDXI#UfiQ`*1%36S-bF9GHGbr1CQ9)jt6!3t$aq#>^CR4O{aamTzUee` z^m~r2?|9Q!B)@dndHc=cV0-&N0Y4g&nG9P@bo`@ch_asHF7>}S!iTA&^^+!6uTA5W zbR(T|nCMv6;kS4vvg@2#QIC7=F%Dpl1bW(G^~CCdaEhd;-px#6Ti49^nx50!cY^N= zV))WGk03mU&nACAz$$7idn*}0<-?|WTL-cO@@o9N!iBR>s4cu{ZRcodIhSscc1=j< zF~+>24K2L)$$j9!%`zo!Hs(MMzxUIHwr--2gFFrj~P5<4RzQdQnEzA14e(#z`nS<=+ zG;~0tj_xdJWF|dwhe3B9`yk=@SdlFfpICYm$dWZZP3a|Np9M1fpuEsaCZ~$S6?#3` zIRDbqma`o|$dZIAr5^HKc98{L0xSv-8RxWCZ|!? zu3N)oLsaeTv24ms+2XmdWE``0>E+SLKCR(w&=NgEbM@*(XUK0kK52TjV%~S<%lconx2lu*~evpwDlPE0yHj>RTF&Z#XY1eFkbD8TSh^4((R-X^$W|_jcd2nKBow=EXu%VMQ>O{nHfd4i9a1k2ZIYKYiznh)CEfoNKVIu+%#tzpW zekIMx^V#HI_$T-OZ=HW}g9$lolz(&o)kyzY;d4F2oLcL@RUjijFG)=J5Cn;(IoJLJ D$x)Jd literal 0 HcmV?d00001 From 974167fcb80a3d582da03652767847784dd3b82e Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sat, 30 Mar 2024 16:17:31 +0100 Subject: [PATCH 103/123] Add comment that empty constructors are needed for IcePick See 5e7ad6ffd160633f15d5ad02f651ee3207539224 and https://github.com/TeamNewPipe/NewPipe/pull/10781#discussion_r1545351144 --- .../org/schabi/newpipe/fragments/detail/DescriptionFragment.java | 1 + .../newpipe/fragments/list/channel/ChannelAboutFragment.java | 1 + 2 files changed, 2 insertions(+) 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 6a7c5ca73..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 @@ -31,6 +31,7 @@ public class DescriptionFragment extends BaseDescriptionFragment { } public DescriptionFragment() { + // keep empty constructor for IcePick when resuming fragment from memory } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java index a67d37dc1..0dc2fb65a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java @@ -31,6 +31,7 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { } public ChannelAboutFragment() { + // keep empty constructor for IcePick when resuming fragment from memory } @Override From d8423499dc695f06e30209047c9274ba6b6d21f2 Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 27 Mar 2024 12:31:16 +0100 Subject: [PATCH 104/123] Use JSON for settings imports/exports --- .../BackupRestoreSettingsFragment.java | 20 +- .../newpipe/settings/NewPipeFileLocator.kt | 21 --- .../settings/export/BackupFileLocator.kt | 28 +++ .../settings/export/ImportExportManager.kt | 154 +++++++++++----- .../org/schabi/newpipe/util/ZipHelper.java | 172 +++++++++++++----- app/src/main/res/values/strings.xml | 1 + .../settings/ImportExportManagerTest.kt | 60 ++---- .../test/resources/settings/newpipe.settings | Bin 2445 -> 0 bytes 8 files changed, 292 insertions(+), 164 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/settings/NewPipeFileLocator.kt create mode 100644 app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt delete mode 100644 app/src/test/resources/settings/newpipe.settings diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java index 1d00ef287..f4080acd3 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -21,11 +21,14 @@ import androidx.core.content.ContextCompat; import androidx.preference.Preference; import androidx.preference.PreferenceManager; +import com.grack.nanojson.JsonParserException; + import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; +import org.schabi.newpipe.settings.export.BackupFileLocator; import org.schabi.newpipe.settings.export.ImportExportManager; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.StoredFileHelper; @@ -60,8 +63,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { @Nullable final String rootKey) { final File homeDir = ContextCompat.getDataDir(requireContext()); Objects.requireNonNull(homeDir); - manager = new ImportExportManager(new NewPipeFileLocator(homeDir)); - manager.deleteSettingsFile(); + manager = new ImportExportManager(new BackupFileLocator(homeDir)); importExportDataPathKey = getString(R.string.import_export_data_path); @@ -192,9 +194,13 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { } // if settings file exist, ask if it should be imported. - if (manager.extractSettings(file)) { + final boolean hasJsonPrefs = manager.exportHasJsonPrefs(file); + if (hasJsonPrefs || manager.exportHasSerializedPrefs(file)) { new androidx.appcompat.app.AlertDialog.Builder(requireContext()) .setTitle(R.string.import_settings) + .setMessage(hasJsonPrefs ? null : requireContext() + .getString(R.string.import_settings_vulnerable_format)) + .setOnDismissListener(dialog -> finishImport(importDataUri)) .setNegativeButton(R.string.cancel, (dialog, which) -> { dialog.dismiss(); finishImport(importDataUri); @@ -205,8 +211,12 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { final SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(context); try { - manager.loadSharedPreferences(prefs); - } catch (IOException | ClassNotFoundException e) { + if (hasJsonPrefs) { + manager.loadJsonPrefs(file, prefs); + } else { + manager.loadSerializedPrefs(file, prefs); + } + } catch (IOException | ClassNotFoundException | JsonParserException e) { showErrorSnackbar(e, "Importing preferences"); return; } diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeFileLocator.kt b/app/src/main/java/org/schabi/newpipe/settings/NewPipeFileLocator.kt deleted file mode 100644 index c2f93d15f..000000000 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeFileLocator.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.schabi.newpipe.settings - -import java.io.File - -/** - * Locates specific files of NewPipe based on the home directory of the app. - */ -class NewPipeFileLocator(private val homeDir: File) { - - val dbDir by lazy { File(homeDir, "/databases") } - - val db by lazy { File(homeDir, "/databases/newpipe.db") } - - val dbJournal by lazy { File(homeDir, "/databases/newpipe.db-journal") } - - val dbShm by lazy { File(homeDir, "/databases/newpipe.db-shm") } - - val dbWal by lazy { File(homeDir, "/databases/newpipe.db-wal") } - - val settings by lazy { File(homeDir, "/databases/newpipe.settings") } -} diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt b/app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt new file mode 100644 index 000000000..c864e4a0d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt @@ -0,0 +1,28 @@ +package org.schabi.newpipe.settings.export + +import java.io.File + +/** + * Locates specific files of NewPipe based on the home directory of the app. + */ +class BackupFileLocator(private val homeDir: File) { + companion object { + const val FILE_NAME_DB = "newpipe.db" + @Deprecated( + "Serializing preferences with Java's ObjectOutputStream is vulnerable to injections", + replaceWith = ReplaceWith("FILE_NAME_JSON_PREFS") + ) + const val FILE_NAME_SERIALIZED_PREFS = "newpipe.settings" + const val FILE_NAME_JSON_PREFS = "preferences.json" + } + + val dbDir by lazy { File(homeDir, "/databases") } + + val db by lazy { File(dbDir, FILE_NAME_DB) } + + val dbJournal by lazy { File(dbDir, "$FILE_NAME_DB-journal") } + + val dbShm by lazy { File(dbDir, "$FILE_NAME_DB-shm") } + + val dbWal by lazy { File(dbDir, "$FILE_NAME_DB-wal") } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index b4503bdd6..339ebf644 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -2,8 +2,10 @@ package org.schabi.newpipe.settings.export import android.content.SharedPreferences import android.util.Log -import org.schabi.newpipe.MainActivity.DEBUG -import org.schabi.newpipe.settings.NewPipeFileLocator +import com.grack.nanojson.JsonArray +import com.grack.nanojson.JsonParser +import com.grack.nanojson.JsonParserException +import com.grack.nanojson.JsonWriter import org.schabi.newpipe.streams.io.SharpOutputStream import org.schabi.newpipe.streams.io.StoredFileHelper import org.schabi.newpipe.util.ZipHelper @@ -11,9 +13,9 @@ import java.io.IOException import java.io.ObjectOutputStream import java.util.zip.ZipOutputStream -class ImportExportManager(private val fileLocator: NewPipeFileLocator) { +class ImportExportManager(private val fileLocator: BackupFileLocator) { companion object { - const val TAG = "ContentSetManager" + const val TAG = "ImportExportManager" } /** @@ -23,27 +25,41 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) { @Throws(Exception::class) fun exportDatabase(preferences: SharedPreferences, file: StoredFileHelper) { file.create() - ZipOutputStream(SharpOutputStream(file.stream).buffered()) - .use { outZip -> - ZipHelper.addFileToZip(outZip, fileLocator.db.path, "newpipe.db") + ZipOutputStream(SharpOutputStream(file.stream).buffered()).use { outZip -> + try { + // add the database + ZipHelper.addFileToZip( + outZip, + BackupFileLocator.FILE_NAME_DB, + fileLocator.db.path, + ) - try { - ObjectOutputStream(fileLocator.settings.outputStream()).use { output -> + // add the legacy vulnerable serialized preferences (will be removed in the future) + ZipHelper.addFileToZip( + outZip, + BackupFileLocator.FILE_NAME_SERIALIZED_PREFS + ) { byteOutput -> + ObjectOutputStream(byteOutput).use { output -> output.writeObject(preferences.all) output.flush() } - } catch (e: IOException) { - if (DEBUG) { - Log.e(TAG, "Unable to exportDatabase", e) - } } - ZipHelper.addFileToZip(outZip, fileLocator.settings.path, "newpipe.settings") + // add the JSON preferences + ZipHelper.addFileToZip( + outZip, + BackupFileLocator.FILE_NAME_JSON_PREFS + ) { byteOutput -> + JsonWriter + .indent("") + .on(byteOutput) + .`object`(preferences.all) + .done() + } + } catch (e: Exception) { + Log.e(TAG, "Unable to export serialized settings", e) } - } - - fun deleteSettingsFile() { - fileLocator.settings.delete() + } } /** @@ -56,7 +72,12 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) { } fun extractDb(file: StoredFileHelper): Boolean { - val success = ZipHelper.extractFileFromZip(file, fileLocator.db.path, "newpipe.db") + val success = ZipHelper.extractFileFromZip( + file, + BackupFileLocator.FILE_NAME_DB, + fileLocator.db.path, + ) + if (success) { fileLocator.dbJournal.delete() fileLocator.dbWal.delete() @@ -66,48 +87,81 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) { return success } - fun extractSettings(file: StoredFileHelper): Boolean { - return ZipHelper.extractFileFromZip(file, fileLocator.settings.path, "newpipe.settings") + @Deprecated( + "Serializing preferences with Java's ObjectOutputStream is vulnerable to injections", + replaceWith = ReplaceWith("exportHasJsonPrefs") + ) + fun exportHasSerializedPrefs(zipFile: StoredFileHelper): Boolean { + return ZipHelper.zipContainsFile(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) + } + + fun exportHasJsonPrefs(zipFile: StoredFileHelper): Boolean { + return ZipHelper.zipContainsFile(zipFile, BackupFileLocator.FILE_NAME_JSON_PREFS) } /** * Remove all shared preferences from the app and load the preferences supplied to the manager. */ + @Deprecated( + "Serializing preferences with Java's ObjectOutputStream is vulnerable to injections", + replaceWith = ReplaceWith("loadJsonPrefs") + ) @Throws(IOException::class, ClassNotFoundException::class) - fun loadSharedPreferences(preferences: SharedPreferences) { - val preferenceEditor = preferences.edit() + fun loadSerializedPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) { + ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) { + PreferencesObjectInputStream(it).use { input -> + val editor = preferences.edit() + editor.clear() + @Suppress("UNCHECKED_CAST") + val entries = input.readObject() as Map + for ((key, value) in entries) { + when (value) { + is Boolean -> editor.putBoolean(key, value) + is Float -> editor.putFloat(key, value) + is Int -> editor.putInt(key, value) + is Long -> editor.putLong(key, value) + is String -> editor.putString(key, value) + is Set<*> -> { + // There are currently only Sets with type String possible + @Suppress("UNCHECKED_CAST") + editor.putStringSet(key, value as Set?) + } + } + } - PreferencesObjectInputStream( - fileLocator.settings.inputStream() - ).use { input -> - preferenceEditor.clear() - @Suppress("UNCHECKED_CAST") - val entries = input.readObject() as Map - for ((key, value) in entries) { + if (!editor.commit()) { + Log.e(TAG, "Unable to loadSerializedPrefs") + } + } + } + } + + /** + * Remove all shared preferences from the app and load the preferences supplied to the manager. + */ + @Throws(JsonParserException::class) + fun loadJsonPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) { + ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_JSON_PREFS) { + val editor = preferences.edit() + editor.clear() + + val jsonObject = JsonParser.`object`().from(it) + for ((key, value) in jsonObject) { when (value) { - is Boolean -> { - preferenceEditor.putBoolean(key, value) - } - is Float -> { - preferenceEditor.putFloat(key, value) - } - is Int -> { - preferenceEditor.putInt(key, value) - } - is Long -> { - preferenceEditor.putLong(key, value) - } - is String -> { - preferenceEditor.putString(key, value) - } - is Set<*> -> { - // There are currently only Sets with type String possible - @Suppress("UNCHECKED_CAST") - preferenceEditor.putStringSet(key, value as Set?) + is Boolean -> editor.putBoolean(key, value) + is Float -> editor.putFloat(key, value) + is Int -> editor.putInt(key, value) + is Long -> editor.putLong(key, value) + is String -> editor.putString(key, value) + is JsonArray -> { + editor.putStringSet(key, value.mapNotNull { e -> e as? String }.toSet()) } } } - preferenceEditor.commit() + + if (!editor.commit()) { + Log.e(TAG, "Unable to loadJsonPrefs") + } } } } diff --git a/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java b/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java index bc08e6197..b2aebac42 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java @@ -1,18 +1,21 @@ package org.schabi.newpipe.util; import org.schabi.newpipe.streams.io.SharpInputStream; +import org.schabi.newpipe.streams.io.StoredFileHelper; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; -import org.schabi.newpipe.streams.io.StoredFileHelper; - /** * Created by Christian Schabesberger on 28.01.18. * Copyright 2018 Christian Schabesberger @@ -34,73 +37,154 @@ import org.schabi.newpipe.streams.io.StoredFileHelper; */ public final class ZipHelper { - private ZipHelper() { } private static final int BUFFER_SIZE = 2048; + @FunctionalInterface + public interface InputStreamConsumer { + void acceptStream(InputStream inputStream) throws IOException; + } + + @FunctionalInterface + public interface OutputStreamConsumer { + void acceptStream(OutputStream outputStream) throws IOException; + } + + + private ZipHelper() { } + + /** - * This function helps to create zip files. - * Caution this will override the original file. + * This function helps to create zip files. Caution this will overwrite the original file. * - * @param outZip The ZipOutputStream where the data should be stored in - * @param file The path of the file that should be added to zip. - * @param name The path of the file inside the zip. - * @throws Exception + * @param outZip the ZipOutputStream where the data should be stored in + * @param nameInZip the path of the file inside the zip + * @param fileOnDisk the path of the file on the disk that should be added to zip */ - public static void addFileToZip(final ZipOutputStream outZip, final String file, - final String name) throws Exception { + public static void addFileToZip(final ZipOutputStream outZip, + final String nameInZip, + final String fileOnDisk) throws IOException { + try (FileInputStream fi = new FileInputStream(fileOnDisk)) { + addFileToZip(outZip, nameInZip, fi); + } + } + + /** + * This function helps to create zip files. Caution this will overwrite the original file. + * + * @param outZip the ZipOutputStream where the data should be stored in + * @param nameInZip the path of the file inside the zip + * @param streamConsumer will be called with an output stream that will go to the output file + */ + public static void addFileToZip(final ZipOutputStream outZip, + final String nameInZip, + final OutputStreamConsumer streamConsumer) throws IOException { + final byte[] bytes; + try (ByteArrayOutputStream byteOutput = new ByteArrayOutputStream()) { + streamConsumer.acceptStream(byteOutput); + bytes = byteOutput.toByteArray(); + } + + try (ByteArrayInputStream byteInput = new ByteArrayInputStream(bytes)) { + ZipHelper.addFileToZip(outZip, nameInZip, byteInput); + } + } + + /** + * This function helps to create zip files. Caution this will overwrite the original file. + * + * @param outZip the ZipOutputStream where the data should be stored in + * @param nameInZip the path of the file inside the zip + * @param inputStream the content to put inside the file + */ + public static void addFileToZip(final ZipOutputStream outZip, + final String nameInZip, + final InputStream inputStream) throws IOException { final byte[] data = new byte[BUFFER_SIZE]; - try (FileInputStream fi = new FileInputStream(file); - BufferedInputStream inputStream = new BufferedInputStream(fi, BUFFER_SIZE)) { - final ZipEntry entry = new ZipEntry(name); + try (BufferedInputStream bufferedInputStream = + new BufferedInputStream(inputStream, BUFFER_SIZE)) { + final ZipEntry entry = new ZipEntry(nameInZip); outZip.putNextEntry(entry); int count; - while ((count = inputStream.read(data, 0, BUFFER_SIZE)) != -1) { + while ((count = bufferedInputStream.read(data, 0, BUFFER_SIZE)) != -1) { outZip.write(data, 0, count); } } } /** - * This will extract data from ZipInputStream. - * Caution this will override the original file. + * This will extract data from ZipInputStream. Caution this will overwrite the original file. * - * @param zipFile The zip file - * @param file The path of the file on the disk where the data should be extracted to. - * @param name The path of the file inside the zip. + * @param zipFile the zip file to extract from + * @param nameInZip the path of the file inside the zip + * @param fileOnDisk the path of the file on the disk where the data should be extracted to * @return will return true if the file was found within the zip file - * @throws Exception */ - public static boolean extractFileFromZip(final StoredFileHelper zipFile, final String file, - final String name) throws Exception { + public static boolean extractFileFromZip(final StoredFileHelper zipFile, + final String nameInZip, + final String fileOnDisk) throws IOException { + return extractFileFromZip(zipFile, nameInZip, input -> { + // delete old file first + final File oldFile = new File(fileOnDisk); + if (oldFile.exists()) { + if (!oldFile.delete()) { + throw new IOException("Could not delete " + fileOnDisk); + } + } + + final byte[] data = new byte[BUFFER_SIZE]; + try (FileOutputStream outFile = new FileOutputStream(fileOnDisk)) { + int count; + while ((count = input.read(data)) != -1) { + outFile.write(data, 0, count); + } + } + }); + } + + /** + * This will extract data from ZipInputStream. + * + * @param zipFile the zip file to extract from + * @param nameInZip the path of the file inside the zip + * @param streamConsumer will be called with the input stream from the file inside the zip + * @return will return true if the file was found within the zip file + */ + public static boolean extractFileFromZip(final StoredFileHelper zipFile, + final String nameInZip, + final InputStreamConsumer streamConsumer) + throws IOException { + try (ZipInputStream inZip = new ZipInputStream(new BufferedInputStream( + new SharpInputStream(zipFile.getStream())))) { + ZipEntry ze; + while ((ze = inZip.getNextEntry()) != null) { + if (ze.getName().equals(nameInZip)) { + streamConsumer.acceptStream(inZip); + return true; + } + } + + return false; + } + } + + /** + * @param zipFile the zip file + * @param fileInZip the filename to check + * @return whether the provided filename is in the zip; only the first level is checked + */ + public static boolean zipContainsFile(final StoredFileHelper zipFile, final String fileInZip) + throws Exception { try (ZipInputStream inZip = new ZipInputStream(new BufferedInputStream( new SharpInputStream(zipFile.getStream())))) { - final byte[] data = new byte[BUFFER_SIZE]; - boolean found = false; ZipEntry ze; while ((ze = inZip.getNextEntry()) != null) { - if (ze.getName().equals(name)) { - found = true; - // delete old file first - final File oldFile = new File(file); - if (oldFile.exists()) { - if (!oldFile.delete()) { - throw new Exception("Could not delete " + file); - } - } - - try (FileOutputStream outFile = new FileOutputStream(file)) { - int count = 0; - while ((count = inZip.read(data)) != -1) { - outFile.write(data, 0, count); - } - } - - inZip.closeEntry(); + if (ze.getName().equals(fileInZip)) { + return true; } } - return found; + return false; } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4ad8b1d9..56140441c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -856,4 +856,5 @@ Show more Show less + The settings in the export being imported use a vulnerable format that was deprecated since NewPipe 0.27.0. Make sure the export being imported is from a trusted source, and prefer using only exports obtained from NewPipe 0.27.0 or newer in the future. Support for importing settings in this vulnerable format will soon be removed completely, and then old versions of NewPipe will not be able to import settings of exports from new versions anymore. diff --git a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt index 2743ba098..70420801c 100644 --- a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt +++ b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt @@ -1,6 +1,7 @@ package org.schabi.newpipe.settings import android.content.SharedPreferences +import com.grack.nanojson.JsonParser import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertThrows @@ -18,6 +19,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.Mockito.withSettings import org.mockito.junit.MockitoJUnitRunner +import org.schabi.newpipe.settings.export.BackupFileLocator import org.schabi.newpipe.settings.export.ImportExportManager import org.schabi.newpipe.streams.io.StoredFileHelper import us.shandian.giga.io.FileStream @@ -33,21 +35,19 @@ class ImportExportManagerTest { private val classloader = ImportExportManager::class.java.classLoader!! } - private lateinit var fileLocator: NewPipeFileLocator + private lateinit var fileLocator: BackupFileLocator private lateinit var storedFileHelper: StoredFileHelper @Before fun setupFileLocator() { - fileLocator = Mockito.mock(NewPipeFileLocator::class.java, withSettings().stubOnly()) + fileLocator = Mockito.mock(BackupFileLocator::class.java, withSettings().stubOnly()) storedFileHelper = Mockito.mock(StoredFileHelper::class.java, withSettings().stubOnly()) } @Test fun `The settings must be exported successfully in the correct format`() { val db = File(classloader.getResource("settings/newpipe.db")!!.file) - val newpipeSettings = File.createTempFile("newpipe_", "") `when`(fileLocator.db).thenReturn(db) - `when`(fileLocator.settings).thenReturn(newpipeSettings) val expectedPreferences = mapOf("such pref" to "much wow") val sharedPreferences = @@ -60,7 +60,7 @@ class ImportExportManagerTest { val zipFile = ZipFile(output) val entries = zipFile.entries().toList() - assertEquals(2, entries.size) + assertEquals(3, entries.size) zipFile.getInputStream(entries.first { it.name == "newpipe.db" }).use { actual -> db.inputStream().use { expected -> @@ -72,26 +72,11 @@ class ImportExportManagerTest { val actualPreferences = ObjectInputStream(actual).readObject() assertEquals(expectedPreferences, actualPreferences) } - } - @Test - fun `Settings file must be deleted`() { - val settings = File.createTempFile("newpipe_", "") - `when`(fileLocator.settings).thenReturn(settings) - - ImportExportManager(fileLocator).deleteSettingsFile() - - assertFalse(settings.exists()) - } - - @Test - fun `Deleting settings file must do nothing if none exist`() { - val settings = File("non_existent") - `when`(fileLocator.settings).thenReturn(settings) - - ImportExportManager(fileLocator).deleteSettingsFile() - - assertFalse(settings.exists()) + zipFile.getInputStream(entries.first { it.name == "preferences.json" }).use { actual -> + val actualPreferences = JsonParser.`object`().from(actual) + assertEquals(expectedPreferences, actualPreferences) + } } @Test @@ -156,38 +141,29 @@ class ImportExportManagerTest { @Test fun `Contains setting must return true if a settings file exists in the zip`() { - val settings = File.createTempFile("newpipe_", "") - `when`(fileLocator.settings).thenReturn(settings) - val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(zip)) - val contains = ImportExportManager(fileLocator).extractSettings(storedFileHelper) - - assertTrue(contains) + assertTrue(ImportExportManager(fileLocator).exportHasSerializedPrefs(storedFileHelper)) } @Test fun `Contains setting must return false if a no settings file exists in the zip`() { - val settings = File.createTempFile("newpipe_", "") - `when`(fileLocator.settings).thenReturn(settings) - val emptyZip = File(classloader.getResource("settings/empty.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) - val contains = ImportExportManager(fileLocator).extractSettings(storedFileHelper) - - assertFalse(contains) + assertFalse(ImportExportManager(fileLocator).exportHasSerializedPrefs(storedFileHelper)) } @Test fun `Preferences must be set from the settings file`() { - val settings = File(classloader.getResource("settings/newpipe.settings")!!.path) - `when`(fileLocator.settings).thenReturn(settings) + val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!) + `when`(storedFileHelper.stream).thenReturn(FileStream(zip)) val preferences = Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly()) val editor = Mockito.mock(SharedPreferences.Editor::class.java) `when`(preferences.edit()).thenReturn(editor) + `when`(editor.commit()).thenReturn(true) - ImportExportManager(fileLocator).loadSharedPreferences(preferences) + ImportExportManager(fileLocator).loadSerializedPrefs(storedFileHelper, preferences) verify(editor, atLeastOnce()).putBoolean(anyString(), anyBoolean()) verify(editor, atLeastOnce()).putString(anyString(), anyString()) @@ -196,19 +172,15 @@ class ImportExportManagerTest { @Test fun `Importing preferences with a serialization injected class should fail`() { - val settings = File.createTempFile("newpipe_", "") - `when`(fileLocator.settings).thenReturn(settings) - val emptyZip = File(classloader.getResource("settings/vulnerable_serialization.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) - Assume.assumeTrue(ImportExportManager(fileLocator).extractSettings(storedFileHelper)) val preferences = Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly()) val editor = Mockito.mock(SharedPreferences.Editor::class.java) `when`(preferences.edit()).thenReturn(editor) assertThrows(ClassNotFoundException::class.java) { - ImportExportManager(fileLocator).loadSharedPreferences(preferences) + ImportExportManager(fileLocator).loadSerializedPrefs(storedFileHelper, preferences) } } } diff --git a/app/src/test/resources/settings/newpipe.settings b/app/src/test/resources/settings/newpipe.settings deleted file mode 100644 index 56e6c5d5dd53ba66b0144215b928f6137f67fc30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2445 zcmaJ@zi%8x7#-VjLL8FV`Qea26rcba>tWC@AwsfT$@bXs8f9v`AFEZ+6$YOH59>lXiFZn>X*h?|t{^FSQuFTKUxM znYEg2zINM$?2f4xkNp1A&#Mo9T~PD4)KczDddnp2{FXY(nWvDsJRMi>zNh5#VDub* zAL4tR)oUR!Icat}?AFGUwuKqux74L$JFtmi>&mddYi`Qn+I8o0GUYEX`-i`M_wV2H zYVH%YxM%X3#?>6F3)rzPNV`&-I<-y@Tv7);G`0-8>T|WASe>bZ>_TJU)PZP6@6bdZ z9%l6-@Q$>=Kq2-vVM=x4N~V(AB`+la;Pq}zHOZra^+iNFU8ZtXU7UAfw;Se;JYs1z#%U6?Ueu%DpT+pJEuT!Of|jCT)*Nnk2iU7A*1hui^( zD=HDGC;QI&U<;BA$)Ec3`yc-K;;T*gV5{?iSIg0*(Jyyv5#Y;5-(EcS%^zRSD>bg* zKDgjyR2nW|y=PN$t-8x{N_n#u6JT3b`v}iIK30IkWq}DhEse*!hq^MHv3f&jeP_L0 zxK2f#gT!>bw|>X%SqkfSX>`w4w9e{^t8Q-p483ZdsB*vt+ynCUU$oh43)KX42h z!4@eLKP2YDYl(LuXn0`c{EqGrcCQ_eRY9_--rYPe-2qm}mnLthiEbp{GPG*)96gee zsdQQwl`V%;ws?k$m!pbO;ErW9c(hCS))im5}2_^Pw<68fK2z8dISMv0Drk&AjoMs#>b9it&?BwVX2r$E{;3Vfb9jr!2~|;gdEOfJ zRg-wLmebHAlWVyrlTL29au110>3z4cwz<}8C4S|ZE8h>UY!@zahID4Nlp3^hdvU(< zG$8MR!&QWtmk5#u=!(flW)dW#+6HDd+uY5B6oU7g`vA$VNV({1Y1^Z4NS7l(@kHy2 zpk0{7RK@(2>B#D6b6HdytS;sTafKF5b%USEgn|Y=0qEttZ%)w|ni~KpkK%laVjRSU^ zjkP0)wMJ5$@#E%1cYR0S!jG8J9etsl{&8xzDqo6d(dI)d@yix%W`<6)%&E>_ Date: Sat, 30 Mar 2024 18:42:11 +0100 Subject: [PATCH 105/123] Add test zips and extensive tests for ImportExportManager Now all possible combinations of files in the zip (present or not) are checked at the same time --- .../settings/export/ImportExportManager.kt | 20 +- .../settings/ImportAllCombinationsTest.kt | 184 ++++++++++++++++++ .../settings/ImportExportManagerTest.kt | 16 +- app/src/test/resources/settings/README.md | 4 + .../test/resources/settings/db_noser_json.zip | Bin 0 -> 5428 bytes .../resources/settings/db_noser_nojson.zip | Bin 0 -> 4040 bytes .../test/resources/settings/db_ser_json.zip | Bin 0 -> 7243 bytes .../test/resources/settings/db_ser_nojson.zip | Bin 0 -> 5807 bytes .../resources/settings/db_vulnser_json.zip | Bin 0 -> 6752 bytes .../resources/settings/db_vulnser_nojson.zip | Bin 0 -> 5364 bytes app/src/test/resources/settings/newpipe.zip | Bin 8317 -> 0 bytes .../resources/settings/nodb_noser_json.zip | Bin 0 -> 1410 bytes .../{empty.zip => nodb_noser_nojson.zip} | Bin .../test/resources/settings/nodb_ser_json.zip | Bin 0 -> 3177 bytes .../resources/settings/nodb_ser_nojson.zip | Bin 0 -> 1789 bytes .../resources/settings/nodb_vulnser_json.zip | Bin 0 -> 2734 bytes .../settings/nodb_vulnser_nojson.zip | Bin 0 -> 1346 bytes .../settings/vulnerable_serialization.zip | Bin 3536 -> 0 bytes 18 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 app/src/test/java/org/schabi/newpipe/settings/ImportAllCombinationsTest.kt create mode 100644 app/src/test/resources/settings/README.md create mode 100644 app/src/test/resources/settings/db_noser_json.zip create mode 100644 app/src/test/resources/settings/db_noser_nojson.zip create mode 100644 app/src/test/resources/settings/db_ser_json.zip create mode 100644 app/src/test/resources/settings/db_ser_nojson.zip create mode 100644 app/src/test/resources/settings/db_vulnser_json.zip create mode 100644 app/src/test/resources/settings/db_vulnser_nojson.zip delete mode 100644 app/src/test/resources/settings/newpipe.zip create mode 100644 app/src/test/resources/settings/nodb_noser_json.zip rename app/src/test/resources/settings/{empty.zip => nodb_noser_nojson.zip} (100%) create mode 100644 app/src/test/resources/settings/nodb_ser_json.zip create mode 100644 app/src/test/resources/settings/nodb_ser_nojson.zip create mode 100644 app/src/test/resources/settings/nodb_vulnser_json.zip create mode 100644 app/src/test/resources/settings/nodb_vulnser_nojson.zip delete mode 100644 app/src/test/resources/settings/vulnerable_serialization.zip diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 339ebf644..5558a1b37 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -9,6 +9,7 @@ import com.grack.nanojson.JsonWriter import org.schabi.newpipe.streams.io.SharpOutputStream import org.schabi.newpipe.streams.io.StoredFileHelper import org.schabi.newpipe.util.ZipHelper +import java.io.FileNotFoundException import java.io.IOException import java.io.ObjectOutputStream import java.util.zip.ZipOutputStream @@ -110,10 +111,12 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { fun loadSerializedPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) { ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) { PreferencesObjectInputStream(it).use { input -> - val editor = preferences.edit() - editor.clear() @Suppress("UNCHECKED_CAST") val entries = input.readObject() as Map + + val editor = preferences.edit() + editor.clear() + for ((key, value) in entries) { when (value) { is Boolean -> editor.putBoolean(key, value) @@ -133,19 +136,24 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { Log.e(TAG, "Unable to loadSerializedPrefs") } } + }.let { fileExists -> + if (!fileExists) { + throw FileNotFoundException(BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) + } } } /** * Remove all shared preferences from the app and load the preferences supplied to the manager. */ - @Throws(JsonParserException::class) + @Throws(IOException::class, JsonParserException::class) fun loadJsonPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) { ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_JSON_PREFS) { + val jsonObject = JsonParser.`object`().from(it) + val editor = preferences.edit() editor.clear() - val jsonObject = JsonParser.`object`().from(it) for ((key, value) in jsonObject) { when (value) { is Boolean -> editor.putBoolean(key, value) @@ -162,6 +170,10 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { if (!editor.commit()) { Log.e(TAG, "Unable to loadJsonPrefs") } + }.let { fileExists -> + if (!fileExists) { + throw FileNotFoundException(BackupFileLocator.FILE_NAME_JSON_PREFS) + } } } } diff --git a/app/src/test/java/org/schabi/newpipe/settings/ImportAllCombinationsTest.kt b/app/src/test/java/org/schabi/newpipe/settings/ImportAllCombinationsTest.kt new file mode 100644 index 000000000..862ac3b80 --- /dev/null +++ b/app/src/test/java/org/schabi/newpipe/settings/ImportAllCombinationsTest.kt @@ -0,0 +1,184 @@ +package org.schabi.newpipe.settings + +import android.content.SharedPreferences +import org.junit.Assert +import org.junit.Test +import org.mockito.Mockito +import org.schabi.newpipe.settings.export.BackupFileLocator +import org.schabi.newpipe.settings.export.ImportExportManager +import org.schabi.newpipe.streams.io.StoredFileHelper +import us.shandian.giga.io.FileStream +import java.io.File +import java.io.IOException +import java.nio.file.Files + +class ImportAllCombinationsTest { + + companion object { + private val classloader = ImportExportManager::class.java.classLoader!! + } + + private enum class Ser(val id: String) { + YES("ser"), + VULNERABLE("vulnser"), + NO("noser"); + } + + private data class FailData( + val containsDb: Boolean, + val containsSer: Ser, + val containsJson: Boolean, + val filename: String, + val throwable: Throwable, + ) + + private fun testZipCombination( + containsDb: Boolean, + containsSer: Ser, + containsJson: Boolean, + filename: String, + runTest: (test: () -> Unit) -> Unit, + ) { + val zipFile = File(classloader.getResource(filename)?.file!!) + val zip = Mockito.mock(StoredFileHelper::class.java, Mockito.withSettings().stubOnly()) + Mockito.`when`(zip.stream).then { FileStream(zipFile) } + + val fileLocator = Mockito.mock( + BackupFileLocator::class.java, + Mockito.withSettings().stubOnly() + ) + val db = File.createTempFile("newpipe_", "") + val dbJournal = File.createTempFile("newpipe_", "") + val dbWal = File.createTempFile("newpipe_", "") + val dbShm = File.createTempFile("newpipe_", "") + Mockito.`when`(fileLocator.db).thenReturn(db) + Mockito.`when`(fileLocator.dbJournal).thenReturn(dbJournal) + Mockito.`when`(fileLocator.dbShm).thenReturn(dbShm) + Mockito.`when`(fileLocator.dbWal).thenReturn(dbWal) + + if (containsDb) { + runTest { + Assert.assertTrue(ImportExportManager(fileLocator).extractDb(zip)) + Assert.assertFalse(dbJournal.exists()) + Assert.assertFalse(dbWal.exists()) + Assert.assertFalse(dbShm.exists()) + Assert.assertTrue("database file size is zero", Files.size(db.toPath()) > 0) + } + } else { + runTest { + Assert.assertFalse(ImportExportManager(fileLocator).extractDb(zip)) + Assert.assertTrue(dbJournal.exists()) + Assert.assertTrue(dbWal.exists()) + Assert.assertTrue(dbShm.exists()) + Assert.assertEquals(0, Files.size(db.toPath())) + } + } + + val preferences = Mockito.mock(SharedPreferences::class.java, Mockito.withSettings().stubOnly()) + var editor = Mockito.mock(SharedPreferences.Editor::class.java) + Mockito.`when`(preferences.edit()).thenReturn(editor) + Mockito.`when`(editor.commit()).thenReturn(true) + + when (containsSer) { + Ser.YES -> runTest { + Assert.assertTrue(ImportExportManager(fileLocator).exportHasSerializedPrefs(zip)) + ImportExportManager(fileLocator).loadSerializedPrefs(zip, preferences) + + Mockito.verify(editor, Mockito.times(1)).clear() + Mockito.verify(editor, Mockito.times(1)).commit() + Mockito.verify(editor, Mockito.atLeastOnce()) + .putBoolean(Mockito.anyString(), Mockito.anyBoolean()) + Mockito.verify(editor, Mockito.atLeastOnce()) + .putString(Mockito.anyString(), Mockito.anyString()) + Mockito.verify(editor, Mockito.atLeastOnce()) + .putInt(Mockito.anyString(), Mockito.anyInt()) + } + Ser.VULNERABLE -> runTest { + Assert.assertTrue(ImportExportManager(fileLocator).exportHasSerializedPrefs(zip)) + Assert.assertThrows(ClassNotFoundException::class.java) { + ImportExportManager(fileLocator).loadSerializedPrefs(zip, preferences) + } + + Mockito.verify(editor, Mockito.never()).clear() + Mockito.verify(editor, Mockito.never()).commit() + } + Ser.NO -> runTest { + Assert.assertFalse(ImportExportManager(fileLocator).exportHasSerializedPrefs(zip)) + Assert.assertThrows(IOException::class.java) { + ImportExportManager(fileLocator).loadSerializedPrefs(zip, preferences) + } + + Mockito.verify(editor, Mockito.never()).clear() + Mockito.verify(editor, Mockito.never()).commit() + } + } + + // recreate editor mock so verify() behaves correctly + editor = Mockito.mock(SharedPreferences.Editor::class.java) + Mockito.`when`(preferences.edit()).thenReturn(editor) + Mockito.`when`(editor.commit()).thenReturn(true) + + if (containsJson) { + runTest { + Assert.assertTrue(ImportExportManager(fileLocator).exportHasJsonPrefs(zip)) + ImportExportManager(fileLocator).loadJsonPrefs(zip, preferences) + + Mockito.verify(editor, Mockito.times(1)).clear() + Mockito.verify(editor, Mockito.times(1)).commit() + Mockito.verify(editor, Mockito.atLeastOnce()) + .putBoolean(Mockito.anyString(), Mockito.anyBoolean()) + Mockito.verify(editor, Mockito.atLeastOnce()) + .putString(Mockito.anyString(), Mockito.anyString()) + Mockito.verify(editor, Mockito.atLeastOnce()) + .putInt(Mockito.anyString(), Mockito.anyInt()) + } + } else { + runTest { + Assert.assertFalse(ImportExportManager(fileLocator).exportHasJsonPrefs(zip)) + Assert.assertThrows(IOException::class.java) { + ImportExportManager(fileLocator).loadJsonPrefs(zip, preferences) + } + + Mockito.verify(editor, Mockito.never()).clear() + Mockito.verify(editor, Mockito.never()).commit() + } + } + } + + @Test + fun `Importing all possible combinations of zip files`() { + val failedAssertions = mutableListOf() + for (containsDb in listOf(true, false)) { + for (containsSer in Ser.entries) { + for (containsJson in listOf(true, false)) { + val filename = "settings/${if (containsDb) "db" else "nodb"}_${ + containsSer.id}_${if (containsJson) "json" else "nojson"}.zip" + testZipCombination(containsDb, containsSer, containsJson, filename) { test -> + try { + test() + } catch (e: Throwable) { + failedAssertions.add( + FailData( + containsDb, containsSer, containsJson, + filename, e + ) + ) + } + } + } + } + } + + if (failedAssertions.isNotEmpty()) { + for (a in failedAssertions) { + println( + "Assertion failed with containsDb=${a.containsDb}, containsSer=${ + a.containsSer}, containsJson=${a.containsJson}, filename=${a.filename}:" + ) + a.throwable.printStackTrace() + println() + } + Assert.fail("${failedAssertions.size} assertions failed") + } + } +} diff --git a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt index 70420801c..a524f64f3 100644 --- a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt +++ b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt @@ -109,7 +109,7 @@ class ImportExportManagerTest { `when`(fileLocator.dbShm).thenReturn(dbShm) `when`(fileLocator.dbWal).thenReturn(dbWal) - val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!) + val zip = File(classloader.getResource("settings/db_ser_json.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(zip)) val success = ImportExportManager(fileLocator).extractDb(storedFileHelper) @@ -128,7 +128,7 @@ class ImportExportManagerTest { val dbShm = File.createTempFile("newpipe_", "") `when`(fileLocator.db).thenReturn(db) - val emptyZip = File(classloader.getResource("settings/empty.zip")?.file!!) + val emptyZip = File(classloader.getResource("settings/nodb_noser_nojson.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) val success = ImportExportManager(fileLocator).extractDb(storedFileHelper) @@ -141,21 +141,21 @@ class ImportExportManagerTest { @Test fun `Contains setting must return true if a settings file exists in the zip`() { - val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!) + val zip = File(classloader.getResource("settings/db_ser_json.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(zip)) assertTrue(ImportExportManager(fileLocator).exportHasSerializedPrefs(storedFileHelper)) } @Test - fun `Contains setting must return false if a no settings file exists in the zip`() { - val emptyZip = File(classloader.getResource("settings/empty.zip")?.file!!) + fun `Contains setting must return false if no settings file exists in the zip`() { + val emptyZip = File(classloader.getResource("settings/nodb_noser_nojson.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) assertFalse(ImportExportManager(fileLocator).exportHasSerializedPrefs(storedFileHelper)) } @Test fun `Preferences must be set from the settings file`() { - val zip = File(classloader.getResource("settings/newpipe.zip")?.file!!) + val zip = File(classloader.getResource("settings/db_ser_json.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(zip)) val preferences = Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly()) @@ -172,12 +172,10 @@ class ImportExportManagerTest { @Test fun `Importing preferences with a serialization injected class should fail`() { - val emptyZip = File(classloader.getResource("settings/vulnerable_serialization.zip")?.file!!) + val emptyZip = File(classloader.getResource("settings/db_vulnser_json.zip")?.file!!) `when`(storedFileHelper.stream).thenReturn(FileStream(emptyZip)) val preferences = Mockito.mock(SharedPreferences::class.java, withSettings().stubOnly()) - val editor = Mockito.mock(SharedPreferences.Editor::class.java) - `when`(preferences.edit()).thenReturn(editor) assertThrows(ClassNotFoundException::class.java) { ImportExportManager(fileLocator).loadSerializedPrefs(storedFileHelper, preferences) diff --git a/app/src/test/resources/settings/README.md b/app/src/test/resources/settings/README.md new file mode 100644 index 000000000..13a3440e1 --- /dev/null +++ b/app/src/test/resources/settings/README.md @@ -0,0 +1,4 @@ +`*.zip` files in this folder are NewPipe database exports, in all possible configurations: +- `db` / `nodb` indicates if there is a `newpipe.db` database included or not +- `ser` / `vulnser` / `noser` indicates if there is a `newpipe.settings` Java-serialized preferences file included, if it is included and contains an injection attack, of if it is not included +- `json` / `nojson` indicates if there is a `preferences.json` JSON preferences file included or not diff --git a/app/src/test/resources/settings/db_noser_json.zip b/app/src/test/resources/settings/db_noser_json.zip new file mode 100644 index 0000000000000000000000000000000000000000..4bbb7523cff0acc4bbd2746598f6673285897cbd GIT binary patch literal 5428 zcmZ{IXIN9+wlyMx1rY>AI)XGok=_$PiZp2g0#X#|JwT`d5oywU?^UVNBPAfc_ujz} zB9Kr61d?3O(f2#gcb-w!8CPZwJk%ct+(?JYSwFd!gg`%>|2JpD8Mwho;2)LHMiMX4v*6jf;Mc~K~8 z*EpyoDhyyJiH{G?w4}p-YI`d8g9IKW&XU_twrrEq*aTwqCdeN&(9$(r8I15~Um;(N z-P36H-VVu|!&6d4#_y4?FBa|gX;vMkpA$Zb1o)0q{?;M$R)Sqx3p8zLu zg=JU&_=#fT+wh()XrZM55TP9>0{O?E^Jvt+=j|! z#s>p~;Y4H>`ykmxP`UIAALQ`SH4Y6Dz0D)}hfjFzCBASYM+rmA0ygB{>IN!4Du`}epnU@@ zRv{w#&}(^m1Yz8~Zvkmot3qWrnmO8!+8U`a?V(p!R@z#g^Si=@p?m|uct<&EH-pAX zzRD7uTUz(w-l9;P4v(gn3VgbyC2Mv4sOIs#*)#6eo*QB%Jr)PA*r1dVLvt${6KV*B zlL@*C6Uht+MJxDz#G_T%1CvzsVT#1fAbeiY+1eYIJoTFO`MwqZ2{@C+<8rzS2Nhhg z-3dZe=n-$?(E|KrVnNTN*1enSmQ?Uw>VvE(1&hf=i|}xG)^NwL&U;^%$2HXt!qwsV z331_jVGrmSjGIAlMa9;;;k0B0lW|e-=@TS!&;4Lw&gG>>m=JV>G%dr>G~L9aJAirr z4mtKxE+^m-@=gr)YQYs8u5@*@ZQA0ZyScg0;0{_Trvd@!%vdsvB-sdtr@_OR>*^9m z)j)BhedjH?`=41SB*Mz~v2O|$68We^0PJ6|Fd1RxrEi&q39(^-8xnw#1v_aki(Fxy zxy-p_C%&RihP+)+t$4<-Q>{(LtQV2j8iL2{z?N)WpDJ(PNl7VrZsafhD?&PeUvZQ1 z_!_BSp!oz)wo{7!L1p*(@Ud6``eb<@>J$QbGHtz6R-MG^TQuyrdO>Wc$bG$Mws?{s zdrwe7zib_~x)2$uF%tZIe(pP?QQvS#du^?kxI*7@+@SaN>s zy4h2!GrD~EPja4gb|=DlWUh@)9E_(b&Tn({zJm5sT9JB~xkLKGgcxL+ySSd`^Dj8L zZHu*OQ|+d%h|$Auu)^3U;SOcEy!PDK+rffu?wn7~NIU&un%Bw0+O%GfhkOjI2JUL^ zJfU8=#k^M-m^Yp`RvNGmGIudgf*kp*8hi)WTE7(FyS?}-XHTMD@coVKdeF@FZT@Hl z;<)L!cMh8)3%?G*U~~3b8v2Af0m)SkZ?*$OsQi_cUaokdj=*qMM{$3hyesO|)2RZ3<;J`_;1nW@6+~{=pRetLt;6-qsD@GE*WprvFxWmN8!K6_ETGC6lP3; zfh)d?t?6+#%_eQ^v?>WE^rVu{;)g#I_hY->-YpI+&SUH+C2n(&q-3U4FhT~FGuvF4 z=8};nTv<@FEw{}Lxbykd<*?LS#mT>^5sG7wCS&stNN+7W(iw3TrO$C)sAse6**HtzgO#xKE2t{0!5lFtM*1PjN8Wr_dnRnxA4}Px-e}~=#*xk1dD|x zdF&-|)d}uB?^VXUw6R&Y!Y=e}wfk7@V$JiLOiWE2?;Gj{Gow-^1FShp&Pq}^E!MlR z?+RO5c$Q)()-w!6K$WKm;p(eBsgj9>2jn42WW@FD{s%*g0hrEZ4*fg3emQ%)5b*4e zs3B~06Y5snYxx8xop+ymsZ^GMejg&g2va z`5=XdoYn^+nO)I(WEb=ogR2oTw6+$!d=~;Jw|VVKnou-Uv}zK#2(V7D$|!z4ArW}~wU z4VFCaqDwz(96puY^U2SE?3{R#6ipU$EeRjgt2ujCM`IemI2ef+!n)@u0JTVdDufDM zl0j89p*cJUNQKz* z1Gp&%!q__>K4cgn3IC1wp5w0f=g$+ZY5%~o{MkJB1W6#U%n<%kRoveBC)3V^o&f)N zWp_IN6TVI${7vUJkk|iA>j341zk3_8;Bo&e4*j2CX~N%n!v^Ao|5qvX+s3f_mn?s# zxhHRN|IhnUl8M|JZPn!L2`So1N>~*?Xyi(k!sl+*T;MP)(5sbHp`-Y#zK!@neRGkm z=TAU3dWU%PHQ?J0xUI%*jSxQj^|sk{kVM?}CfnV)EO? z1*!0h{<-%KfjLoJ=kwi2TWpeDh8T}XFVf{;>Bp4%`?tX`Dq{)3S1D)VXYvkqAk2QU zssBS!D&w$WC5!MN{=Aj%US$r66Fr8R46`t6;rKA69t)olF9%7h0lL|kYV%KT?ZA&E z?xkg$ezEN7*#TUv?HB=}=>$sP?BW5N)LG$Zvu4J?%#DHZx_bbaO3nt7}DL9dlCDMAwiZ3G}dt>|6QAYBVimgn7O0^=~ z1~0;|8>uD^D(+pO|BC91sarNhy7+9YSkVT4*T4RHlFKI4lcBY3c<-=lq$X)Bug5_5 zv3A3wxNX{sn)oU>n9+65zNG-Vu2*1-i8%RyH1TkzRz$HU z4G-JfQ3GmHBQ=L7a8~bXRR%>6YqbpZ&DzWF9p-CdIBmT_;?4JU980EhpXut8Xv9ZE z0gDn-g2}Zh&6bq6gEf)#Rm_0pjHeajlA=Sxog$rd_r# zTNS$U;S{=!MIY<2E3=tTH4~%n%4|i;lPC@`W&To{NJlz}D+n)C&1mo`POrR6^x-vF zjO=(T_GXVQ`E~`5fk~)@yLwzhCjNw?dm|O~&0x|Jct&5@uYI=NqXidH*g68o-&_g=U2x5Sygnfkb8z}k&mj9x8*g> z&7XHQygg+^QxHF{VVv@O)ZIAvZYY~XOwVG*v$l_0O_y<>syS>vo?Mnqb82V~K1Udb z>>3=edDU=Qa(r9pya_7q;OJ@Yl9U0k@Y1_deWp_^uFzvmm-Q{w%4smp*EJh38yd`y zQC`5A`TtMTx3xl8@Z2dER}g$TL|AG>I21e9?Q6_3EXT+jJNTnr7`$QA1bsJfD6#-)gpC(>JcDahIjMqwPe-I>6XIMWPg4V9X7S12nsj zAm_tGGBjD{VZ_Qw07CHSM4E1u$JYB5IsLlC@-dZFxh?4C_cJ`;1Tw!3B`aDPsFL0} zJjT>CCwOFG)|Th3m=05#_nF^l3`67R{4#|CGM>|_!s$}DKCJPh-sqk@l~!?ga;k?M_ANN5vE24%+`%X5&3%UDPN?v+Mx^IoXRg6<0O;DNCR#5gm8zkw63I%sK%;><%}n0 zK}_~Hpz-%Up6dvTn<-?TzVVNDoNIYKKN@Vj8egmmgiZ&PJ1a@=?NcWtskwTP@d7H% zHp-LTS6!8s#_75AC@cJAP4kt%iyBBUTw5jr)mTbo$`^unN!_&=_ZL&F1Zne+-gwec z=;EQQN(&|%S#Q$PhSw{%-!EB|oCZL=4Vy9YM(`|s+Ibwl-q%EY@`_faGv?;CQ?RE_vw<)1vuO}2W&avHwlud1vU3)Zkkon9G zEiW6rF`ie(3A=Tl-c)xY)xMM4wC}vkVNsLkFTUQOuRjY$Lho@_w37Q;^|q-FvS*c$ z+OscP9Z1wtvQk`UEnKDu=Y94|rANGPX(6yJD36HZQv+zt$bZqe}5*bm+`m!Rm0~m9nB&6N-)1-&D1=mO`7=#^YyT;kCBHQk_WdUO{b3 zUQVYQPCAOG^mmQqnka(B`a*}q&?~Lk0w~$ITRuZcJmjd4Plcy}<+-#+apSID%b+NB zl|8=aGVvq2Q+Bu3{fCaIWn?=dzOf`jze?SCtMzPE{&-LzeJwdPP{P}9CzMWz!Et21Au-veufS~x{usFlY!pkt3uV3Q>p7FxuOOGQ#Eu9#&qFZBKrOW^ z#56?zcc=1Cb^XuypT6aP_(cjC}%kh?j9e(Zwh!+bvwWX3OEHOAa=nA^td?DXX{LTfGu$IonO9AX! zMhzgHbj=|$ifM2A&`>q@n~}fvptsDm^eHxXROdR&MVD}r**M#k7H*yv#^E@>-UYVV zWW0Vy;CfW?G}*bgp~l+W_XKG!C6yp<+`voMX(J9?9>E3@5;?+jw<3$b~0+1Z@mVC3tH#U4MS&_DrF6S0eYYm)uy8;X;O_+QjFc?2jACIX+yx{PSpuUX>P41JOjHXe>XGSCcDt9s!J*F=nLDEnU}r|q{ixZ5}M+b(thjOfA5Ck)BE!k(WTJ+Mvro@4HyCHeyqyF~7toFY|^(iycN zQcn+6UTyXhq@WzGo3~=Gc(v&TTWTGU?yuDI*|*Eb8@5}T*ox+U3WVt} zFuWRYJlKWstzNc=v@F#T3R<6exFcOHb@=hb#f63LP7Udo7&*AqP$bPm{eq80Ypqmm z1=T0JZwEgsn_?_s5TJvZ=Bg8sNJR&#%x zr^Z@27sB2Jz8rULvFD0)mfi?^`f3ENxcqSGsaLUH!}83hh0t*fzy6JC9tuc@3Uk}X z-jTf~nZAq0?2n_({2G1x#LlWj;2wrUy0Sjl%%D^ z;$yDx@LF|vW3;ro&c||}Dn+FvU?%o)IK1yRdfMxeevB-9g(W-piA|2ReP5Ws76%*o zSiLap8jd50e4><*O08T(c2C$-`Z_wwEI5ew3u~|dMDE;^c;=NzOg1J)ps6Vxsq3AB z{B+b=wDsnXaiy5*E%MVc&2%X)d4R}Ua*vvv_T2ltvb5wFz-cAGDB4Xmz`jV%csg%7 z(^IM(!CSoP-Kd!R{lIXQFaJUO$(G15H?X6y*z4MJ98hS5hGnSg_c+xsX{}Yhy^|~< z;kM(zy9kx5S8Dr?M)nlKi2L(faL*{n%?am?s``vSg3Cud7AZ_mw8UBar(h^)@}=9F z=2gptMRa_;{%EAe%yc`S<)@L1fw$6)< zhdd>iZ)_4dBA)c4_#zkMlpcw?l#o^AWlf`7-15ymwxAsJxB_HC9M&xH<~xSXwbR+A zt#Q>v-csm&Pg(NKSWp$UxVI?zT;%O;-|IIISrDN;2CQr`-G&d?qW%i62W}c{+`NE3 zD}XNxFaA%=c7jQOVZ>6UHJWj`_~<2yW$Q5NPemgBKig%W}VhfeXR2 zT{$T(9oF3<+&XF2S6P(*OdWZ{G)VsV{5&iiR?IiZ!qg2?<`m%6w8Vv13%F2hicYbl zF`{){t6hsC7_Y9sI9`4s2EH&)w&lb%=2t|7uL4*iRKn-R(8fn3Z?{kh?Kj7YMmU#X z6JyZ?gRx?B@xe1P(I1aI|4L+5InI;1M6`cBF?oG8+aa`HAr~%SzUHY{^Epv!R6%K1 zKO?ZxnRfV?)Ly6P6I>R85G*vPD7oh?!kuH9SvSbFHbq~fQj1v9PJH-cYb$pJt+L-k zqd^zvyKs}GSMiO3#Dg;}op9W<`G*5>ykFc?A_uSFOY8%Uzfx>EH4&;JD6m3wh95pd ztm!shV?dkq$i-#ZiH!cV)*IxsNwzI%v$nDJxctN)(qU93%)-eY zF?D}vcNdd8a?2ccM(t~FVomj7?SL4}ad9ok*IO&6y*s_&iS)p=cI(*#{u_KtpyR zynq;5Bt?ZF4F@hSl>SJr|5(7LvNd~4O_u>(jNXF)EAMO86+Ct$f>0N*E-Q$SM6mMS zN1mL+){z5cd{9X)WMki-xf}%(o0Hpa)_vh$pGax}Q+t>Lu+IIvVT4(>16e}!oEo97 zjp)^5koJpY>oC7)*uSNP=Fbz0CNH{PbK9Wb5izo&tHf)1P+usVZ{F{zDGjC4)uJeZ zgq}7IxS$Vksrz4li7_YV832@q-uU_q*^H}m<|DheT8H_~Zjwk~Dv>_h#=%4A!JYlZ z*TjtFQL*WnnhNmBut!h zC`JS!B*Z()9Q(tgz0lX}=g%`N8U0Nb{29DxA4jJPPSSr-)tuY-t+bQT`@o;*-3`RQ z*O%$^UvlmOdHj3X1gNI}ift)KqyASv;=h+w>A&g?7f1l@Kc&Dwe52D_U)!P)UOD6SZ8?NZN_uR@$(+t^AsXQ|D?Q zLi;6cZ+nVwX}Riip?BanA#kh74W~~YtD4?ToiqbM&6^IV4s9m^F zrcCO0C!6sH55?Vgxa3*oh9KWPQ+)!XqNWu*UG6CxT=hVS6HW0HF%f&zdkTd`?!n-+ z11P*OLF{Oz59dnGaLZMYkRQN#ZO{GvRnOshWDl2>(rr`dVeH{OkefGYE7K-a=nj`v z%*cKF*a+$3h4z3dQ2KcPlRVz39%s4KnEUlP6Nm;-o z5ynNeY)oR_!Q|fFO~g;YXgjRIsjVvn-#}O=2;+!ictUy2rzYg!DAr65pFB=$wtdXf z>(pVct>XSx-eF-LinbQ0gHq*3=UAFOYTk}U;m5Y>6D;p>YIX77zTYS>Zt)N~=xq^5ZroLA7wf)jUmyE*G)l$cAjKW|s}2!7||XhX(WaleJh4WpK8DeK%d z4XJe)FyBkOduJ(p*{sx+6u18hXB9vswLL9pbe>Uolxt~IL;bQ)S(6}=F*4%rb^*|k z6>l&yPIaTyfBq_%OE}RyxLmgJ!Y-X{CVH+kLTb#6*cA1b(8v1S`!O)=|Pefv+*CED?G&u zXP&E(u&|C+^3_Xe$)oMF_pM|R-dmt7?+R8Q1WF>6o)#RP1&MklK3A%F^Tu?%%4a=Y z7OIq_yq&1{;AZXG&78c~KHv&020D}B7E&eh=HZ>;GVw{VSt+hs$a@0X+{6^=OaA&^m(4u&b-k#|U;D>Z6W8?(hBdHOQJWTfO92hn z9Yx=x5of$%A4K~*J}RpL1SPM&c&5nQWIR4lnVQQ|aJx*D8P-nuDR{h@nb%{k1<9ja7GEVU8`rT{v8M{zST^0@|r zGd)b&831}Day;9l&Tq|OLEXG5y?RV%QGE@*+I~m_>_cYO;it+MhU!!|cJ@dO9cg~~ zq^0>8C;lC1$CkiT{SkQTbV#0TSgr>5Lktg8%sZzhuO`VRf@`%=A2I36>=FV4Li#aE zkQ_*<5732-l#M>&cl>Zy-N0zrZfE)n_2s-VSu?MgTlEoAvLZ=UBSCh1W|Szyo8)R^ zyxqODBXu-Agw>;(2%XHH^q^;z~9ITIiHFCYF)P=fpPrn%dffT8C=)(QAqIIUs@4w7x@ z@$RN%-_mXDI)abN*6SN88?SbpMV7m}QTZ(pT2T?*75rf*UUxnW<-v~ytT8zAe7Gj> z_or7zj4|h%vh8y}(v1IJ$VwL1C?(8Lmyzir!~gERf9l2W@!w9|Q1|3NJt`vuBZK0P KvMmWt&;TKgyAy&t1cJL0oZ#*b4I#MGxVr|2V8KIhclR_Bq;c==n|*fn zJLjJ7y;r~HSF6SxbFCkv)|jQLfQUqZfQE*KpcEPK0pULaB*Xt zJ;^>0^-L&{nrV1p*v6i|2|ve_kr#iKT1t+9&>LGyt-)R{S#soI_W9NMovLu;Pc<)@ zVOI{?8X&g@Aq}!y(WX{Qtr?16$qAV=P;=93$d}fUtwHLyDZ?PX{ zM@bK=!H2#FI0}I)8ASr=qtjvzvwV92A4!WIQ2)G?u;>#L5O2~y(I_Shc_4Z`^XYE$ z)!)yy;FU=+FnbtH7!2j_%%_DNQwMT&MK=1YYq42qaV6rPUE&kFU|!A;uC+i{cn_2Z z_)k8Mb~0e#<82$EdIC=(GdMJ-quBNrdVA+!XP)nf#@@0WppC{wk8Ft}FOqs-}K+wB3#A3|?TUs4B$Q>rlF5g%-~rK<>xmZw!5 zQ`Z+QK-P_!)Kd(+(wR2bzZ5p3FeOHo7|Av%7$~+gL|JVGiQ>ZaOVBtTkm&w~YRalT|K|I+KdG9C*H? zBWH8_vVoa!@s6=`0G+RF!0cR;x(zpaY-vMjMqyv_dInH!D3}Gr?(}>WO}p9s+Av*l z96Nb87*i#(T%r&HbF28|q$1DuN4>K#?aAuK^_b?XF-csm_;?M5slx zpjhzy_2J=(`}xX}v$9eISKAIsMwYHorlDDXAo&>%=EIYCULfruPW%I6(K9Htd~>pE z-t4izy}eim2O^zUy)W9AwWb?`v=a)>07sC2{hB$J|W`QHU0PFSA2nx z>-DoX$1ot6^5&fr%MP8a7_g3D8A#&0a|0EOV6m`s^8j_~`Kd~0=3 zH&ZRhe|Pegq}m%Io~CXS`^=RYKHxlKX%kb_FEFM}hm}!lM@ zCqDrzl(Wm7p6w2$FQ8im4Kt!kRTeYsb5NbwvC0t}{>lsTlj5L)>4K^9z%y49XOk4* zrSGQBH_t{ZWe!%XRn5E;fhJCK^xP)bg##@1ILYUrd6237?!?Nk3r|lInnnWRq^}%; zn{+7i66b!m?aO{yJS+KFc02H1P4VGzN z3B$q?=zU_Rf|;I3u>cfjneR_w-x7xLEwYNU<~+qV{8l?NLDubtT{J{;NruEIf^QSY zqn-~x{P_5?G^n(IbQtA%m%Sh^Ij*GMVNeyh^`lWfDoWC`6$P6roBZHs#0O4K+fEFg z`0EcQxQC57Wx+waqA0;aL2FYh>i0LUwgF5sQBVA8ht_@?>U6OxQw7?L!x$XlKkgkr z#~}$lWwR1O%tGcD=yx;B0|xlA+Q_x{92Kj_;#ep61TK|Qe9En0cOP!P)$+Uf75n#5 z7cV%hxX4^j=Op7}@nAQ?zwzG73Tx)Lkx$6)I z(gl-?&oRTKQJ**U1e}kp2Hy0o(`n-z`{$h;13eeN$BsS3wVu8JeUL~}@aL5Avs1?_ z-k}SK#EyL~2CPGCHmJei8cFYx7Zt4=?K{8Ux(h2W)v+||Ta+HTyaZ>BztJwm5Si<$ifS-h5z}u$aLvk007V0DM*FCdj;q@bZ`fpKpR*Vk7Wyc3%HAfny;1aA zT>ll`$AX5~&DEVMa0YmHpQqrpWNq#TTZsKE{=W52-w%kV;MEU6=3UG9t>B4oVHWV{ z+6$>IBO)b!y(F_I2YC%%_W*WJ}c0Z_O|*dxVhcPmaCg`3z`*T3;h zQ+iK*-E%0@gXJ z1pj*d1nPoWwfSK}|7k~gqWi`TTwDbO&YxoLg14(ELr}PA3IiE&MM#HH_6n)*%QS-XZi^JKUrl#GE6IoWvi1H zu;eHuX_NoFg&|c4mcQHZ2)&_bL#)9IpM)X$wZKNTO?dX+TXyXB_sO`xuFe-px3(+C z@?3p>QhBy$Fk22e?9yqCyb$t{n^*r*id&GNmsYYPUYcBC`FfRi<%(-M^aCwmm&mGY zkI3w4?%3Ntqx-riysLlE>RvzAv$fy2!(wHBfew~x!v`^MnZ0bIO65rWWoa}cE6-?m zfyaZ!Jp%mYnCRvc7PiFaXe}YALhLZh2+=%wWS}vdt$%z(;g%g`Uw~6H?Jn|8!rs>P<}B4HfQlDSKVn?kEHaqAVB?#2 zrG4_ufNnO)Vz(7{VubX7neT+Ry`bd?U@^YVB;=#5C$j)yMy^qX#lXOk=;PLro>*HZ z99L{{^_*Jhu6VLtC2M2>J*})ZzH`+n8>4AJokzT}1Jd9LbOUXG<4MBozLauT zyo)=TFNIy8`6agOoVxpzcG!b0W)XBBLr6ua%qSco%2dICjs~+@GcD+VsJbDs7VJsta$?s} z)V8fvWOEaJ{pnEO8*Y#=EIO+(y&w4uPN-9{s+n{Q!v)+G zLCx8)YwZ4=^wWBsS-p1@RkuDYlL7|0_b=?}9OFI;R7XW=&Q!V`By*(+#0#Fs@yoK+ z?6GBLhq!r`?Ss>nQ*8Y!X`-=nZF70Z@bzvKwOzOBp48nZ_ZSr52A8w9C=0XhFZbzx(tMTc=EG}d4}MV!nuzGj zq)t;vJ^%7ew7PO%Z8g`?UCt*VSV!hZ(T(+-#729bn8Ln1$4#sb_Jep5G@jY7MjXOl zNIYL?xmdL82P$bfrl{)ex)!kt)VE6$D2Ej3Gm3#k+ubJ)mm_$xR4A6apUb3(!b2x# zGBj#E_RKfLwZA4;P04MF@3rlIyMu{d1D6llP)jyOYK4z3u5KFIlRR>6w$_&|$u82` z&&Umw#@iB?{Ij_Nv)&WQg8^v_u9?-@)$vBbc)N{CeGBeL*8ZhpzyYuTFcVnhCT9IG z$rX<1@g#pKuA(+-da*POby-(`kjyS15>}pMDT^0=AIo*VJOSY#iMKIQKkwSQV7*@& z*;j;WP$6e5IKWd)*JC=mTK6L|pd8^}JbymKW{yF;L^|I9XMA+ZCo+EBd{OaZU5AD~ z>=2Rne_i7w6brzzHv-vF594EnJ=X}p=-!PSo>zfCZK$Jl8;xEG!8 zAM}wzF?-wtxMD<}(jU0r9hLv|Nh>Q0@AT`vh*4M%oOK}E7u`d!0`$`Ic)WE^iBQLl zDn7^hEi+zM{yXV-6OVza3aDs4d8$3?f3*YL=Lo9*PbZH1yWk%u?q=ccZsTa>=Ak2E zPb&2~XQ_U;c9-8Xn?F4yf)c}>*F~}rjVXjyn{^xMqYh>I*vZ5?*2ens*w{wAnkwU` z _vl#&_blHuiyjEvS2!Rq*u;gbE2mR`TbMuAqSovU$ktOEjs00Ztqlyv*z0w`As zQw0d;9Who{2oC4C;sc%z?{bV8mVXEe;o9KUMEBS<_TIFof|^T;>avV{a;Ce5a)5V0 z3b7VaziAZe#5Eg7h~;T@7^H>iH?jD9VMA^taBz(`<8p5`9qgr7fK{!O zm&zj*GQXqB{N~#)$)g|F{;E*Olaj!)%wHfwx=@Fx7rRA>xxX2t_}TWVU$#GLs=_QP z{5yKCF2e4Tgecc*2@L$0yhL=W5mZo)bE3?MU|zI;_p-AV4Y z?^=c2{~qc^L=QMB&3V6dWZ%05jAQdlsN`@i;B|Q)wIt|i>b_y+`IH>-3jl257S!s9 zfloXOB~xUJL)Tr*AbG@-j%lPT3ok_r%uA~=8| ztwIv4iKOM$3*|Xxz#1|_stslIrj9Zc7yT)3o6lV8<#_VzntHA6@@Q37n_8$s-U9C4 zQ%S3&8;JPpxYOB&hppbR;InFKu#f(lW!PCC*uv_A_$$PsYGm%3A~i=STlA!IB6Z5* zQ2pg{Lg@!*{>juhXlGRKE_KwwTlHe$u^mmiaoY-BTFoz1hEv4yjqryyD(P8tX`-77 zPRE+!OET1^JVYJpRRtrg}ovOXQ#+ z=>*GHa$i_K$i~{66tnki1dk-&)w0DrCt0mpMpE3^l-A%>i&8B92r}z>b`?NQUO*L4 z^x_-2-#3pp@j#Y|^K1kCQ0&~Q2i7~n?X~uX#Bi0y@Tw6U?Wr|-1h+U#0tm!XUF_Pb1=pxMauF_P!d_WW#psCO5h2?qW`gEZ=~vT-W0*) zL0q<6opH%s1b)Y*f;WsPGRU>>dqIO3L+oJuH%01&wm260y?IA<133mh#r4Tg1z|Rb zh0Dp7r{{~Cu*-9+v`@dJBbkvmEbT8iLR#mp)niyBg!eD=vO=1phSUw<)<&!c8`NM7 z%)RizgDJpgRszOXnq*-vqT1-h5r&&B!*2x9DSllIy#S}If_nU3pd;8dqGz?w(1N_T0>PS1Kr-#%6isBA*IqM3^Nwb*0 zl?AIc)e|pVbr6!xTuBj@;eMz(hu=>y-3kb8?5h{adB#>>LyLAiz_U?!WZq=LH3<^J z!~D^*muIa}c~LmcTqo`hL{;XOCHG+1?9^Ouq4=1wP(vK4a1-RlB)I->08l1JPLKC^n}9g58D= zjXa<8aXn8g;g_c+G5cXsbE-uRs)N9`I0DZL$H>eT!@of0u27}Y0iatfjoPZ3#F^Y7 zvgsrOI$Sw+5F^vzjbVFdo6qjTjW%GZlH1qst28INOnZ69{w-EZ-Z2dIuf`QO-H7S; z`>HhSw@>^3H!f#a3rh=E3r8~xw>P$KPL4qBRYw8=T;M*J^bQssqIlg;toka?5({02 z3ltF8QD^eQZZnB7XGZ?hV{XHvAh$4XDX@ZBCr7ZXOKdenkND+ucz2YWO&%T6%4l+KWsUopC#BXqeQ5NQMUvZ5?EN&)L+dH|)PUx%n%gN{2Bf1QtoOgo^;#%6NEsnPWeeR3mZC-L1>)*%+nkeVJdGe6g;suLp>7@|bwYT1am8&{ugRn$+g}4%HMcoLQITei7)W7`DXdsL>=v+D~Qt zNa>Ajscl}*VlxgVvqNu7B>a4@&dF~qnSE;zkm#_~@nLx~RDUzER9>uYKCsG3TKMD) zKPg4Q#RHW^w8nU+D%E|{MS5+Tm_ZA-+W(zVq0Bd49RU*Lbp+Q23xRBjV$Wj~cU97} z)ig^^qQXl9F95a%tc_B7#c(IbAR}XZyXL@r&8+M;5a^@Zej}j=&e0}1lOz9hdSc9Y z#TQIzP{``O?1LL3CerN-_j5YEuM~4s<5Cz@_Tolq&FKAj&;VdGf;XQGgr82+9dsSO zOK*9%MLTt#{ni#D@h)y>x}cK2`^76_BaNAKyFNyvuWya=DEJkMI7kVHmCNTmS58yk zI-&iXQ?0t)A)OJcQU0L2Omg1Q&~hA_f+*q&2@C%Lv}Ivw(w)*txQ*a7W{kLlr7-dP zOSOW_rZ6aURM_|Oobpa{U4wkHU3i1-=@Yj|&kCvgnK>+rOIp2tu?_}*`B^j(PDo$f ziRouK*d;$olT(IbN3(8uF3^ZeiH$~CypA2o^7fb90RPb1N>EpD0RnbNv+I_lzuk66 zE2`q{M7d~|&Z?q=TN6?t=5t`N1zF+MmMa?gtcMXzIM4_keV z-2h~F%Lp~kelI4K8HU|t_SO@~V^>U$-T6pbUaV-!NO`xOQd0&P$#*tlt5I5e{0u5a z?kX-sVClw9m+QpKE?;x=*;CfeFu8sJC?$PdHVSc_qZ+XRNkD(<7g~izr=67~u-qbw z6U)spVf*%#tJsPW8u#78?N91af{=6yVC(B;AiYdqtpa;|0?m-Biq@T!1E-?gU0Z*5 zhUPj0(RzQ;>E46pj{+BACC=l^eb5&JRmvAl!bO>Q-CW5((Yag*<&}N1U*IEcj*(&) zxk2jun`^Tp-Ag)a%lm18Nor@K}r8#2xts~tw_~xNh0t?sB?2w(~<3_4%bIvDJ(=3Xr)HL2?iaPa< zs!0mNsA>Guqf2e+i0?XH%0q5~Y)NyZ4%6*B#MHKd=g+4|A2iVdnr@6ld3M7{m*Nqc zZ9Y4p`SU27?)oYd0}q1pWoW_yr*1tRPPGL&aDQyu=OC6GGT}ZUGm(w>a{aN063D|t zB)1u)A!gAaSaU4h`xhB%dW3$b3cl;RL#_^4OfQs38~G~hG%dCj@P@t+b@S(k{5zz9 z*IZ!A3I7Ds=uD#isIiRe%hmh8UajFO5~Hz>Sk_Q1@h{}v?1s4$qpk+uwSIRvO)S(FAUINOpUX{ST71j)9I`-(l;E zdTvT;!PMceq(%#_9`)x9l^?hiILib)ca%S$Kz>W67=W?3=b>ojgNfFQ9lGCw-lV?t zPEH5Rq2N6+>}GRkA^cYE^P=e5-Y5wc=Rqr{?T^{clMaq+I2Bg&SlZ)^JZY!G1sUJ- zt}cwI%+eq@r?^!v1MRyzc0$CiN%(y8M4p+C$3dc;b9VyAuA!FnLj;&*!Yx19ey zX$vNEx)oX%j|IPlAoPv%#B)Xv4)_icEi(@@!_>&)yd=6tJv!XoN=^!6n-^(c$(E?% z;k_HMx;O#TZ{D{6H?7qizX>ZS7T|cSkrkT6s?C2*EEA6*9(r0O0}XpHnsTNv%#Z}3=Utv@h6}fD5T|DvnP!++^g$R8$w^Vy za=8$;UGg{-LE#MyrfhYhbH)PQ*Va~KNRCv>t=b0mW-c2=6K;gS)8XNaA3r8dsJlTY z2Cv%l4kI5;NrYD(qFxm#B=J&;0$IyZT{0rd%XL{riSgk;5((h=qP?`YMV^T6eAax5 zGjDM(UH-mXgLvkT3++w%Y?WxDrjSW{kQEC@aP?jC)YQ_aF9W21L`g#g6gTNliHQ9J z&8B4k?v;A@pt|pB>{JYbJX<+zbq)pd&DiW!)FwajD;{%NMdKSOa^C8nE14ER(FiFR zRIDGYE=EUdj)y#5nEyooa&RnpASz-eMlhe3#x8!>bc&ei)*lSHvr?v`d==;E0*?p}&KB~n# zjEJJZ{IB_wWspNRGZ(XD@QLrL;U`do%`-vXyG#1H2#H1^OOl*Mx7nS$05(? zF)xV4l|w}U_ZqVQCVdcWqt$6!u*yx zGN86?CY`Lbs);5Ki6#F^7>mRoLVbC2uOzS}pMHoKztd5Yf{{Yusw+!eP%WP#d*mBmW8ja;07n3;A zO!g_W!Ct;N|5PLH?pNgB%ap5CS|IPnN}HjdQZq!kHHX{6VDg9)33|SonTb4DlsfCe zVpCUFI(BCZA0irjkQcX`+FN&Duc!?~(S3D*h73JG6j=D^enXqKEA&dUPJ_h4l06a0 z93O=cPY0CGpV`{3TcZ{Sx4M0;_fcjAEheTWPWO%ULKqLyBq28JrI)3tk1f`}pv;Tf z+qstGr`9u#MBS<{wnb_Q`qLznijGJ_m2ToUb_X1dEw}^NPV43VtVr z1z$7s1?j-UPyQ5qfXi^q)WumPjvz55ksZ z4r*GEE?ruQzeNr&v-3v0nwqd)fQUuZsvVpC9_|UNjxkOuTEPkPR_tEmSyxqI00t)$ zie@_KY9ViBa+h7}{12aN(%NPLn2KH8`}TUGaks{X9_7$%0`XkhKR*XykhrB5a=<~? z?w#Ly9|^YsQWjq0*p6vU+{d$PXeZXD)sGbO$BuG=+pp_|h4b@W-bG&qtyew@CKhHb zfN)X_hqLxFGt-R|M*L)a%Jq2u>(Aq@>3l{p{n|Y5Y!`=Pn#KK=s=B-P$7yH7&SZa~ zfA97FCw(1<`)%j8;1_?EAAyy)-+RAg!eahU9`ZlQ(zxH}4O_4`_TNXTpD~F#yk`2f z%sYF7`5*L|Bm=1p(z@lX7r1yYIdN6|sF@>03Y)iCkH(xcwGvcQhD~4z{F<>N24OQ-*N_)%fK6JP(k)tx>u>wsj_F-z-C>=512$L2F`$;d*X>q|+q3N06gx4b zW%Kk&D|3oq!VhcHDHSzNhhs|7TOz~YAI=HxKB1xWV$Qbml3EX)ra6)N2s8()#dIAS zO)R1^G;iq`m>a`!wa~X~hf2236yp*d*mXHt{`^hD@=Zt=rLlyNe(Gh!rM#oP+xcON zX#n#RO5^Y`C5wn4fqd8}?+VAHseYp@y16bJk%Vxiehc4mZ%0Y%VZdBmty%CJdl0t- zO?r-LxmADv9uU2@_fobs14kh{w{*lJby+mgrjh{Z}}xOZyb2|l(aj-t)}se(82UF%$8Ohjkv(s z>%>O07sTDxZ3fCx4&|bju$9zB6ULfUjOh3>aiddJ`FJQ|@~}4Mr96dV2ZNAOgD9t= zO2n;~YDpuCh#L<-91O;NTru5s@!fz~(*}Mrxbrs^A%QB6~mf5 zHs)YY1*}ht)*73_SerMfj)-orHPAJ->8w~fF4V_8w)1flZ@aJSRQfINFFgZ7&4j2J z+2W+s5K={|#M%qE0;q=SDnJO=QcqSxXJrL5*@e?fiw z`#nt`FPSH)+n$=FY!n@0@m+u3&;|*=^sSU02LJ*3=q#x@{b~ zZ+N=qUH{mMy>7Ahwp&RLdw<&(Nf{s$&qG(rNPuEV)$>OgfBO|_=QbG^=)E3(Jvvel z&(S~Nr`05t#=q63u^sQQ44$|OOmskh05|eEqFLIy{Zpl8T$@aQSBZ(T<>S6P{kddP zGW=Q)S>s(%{E|U8=I0dpF;7%CNyqQiKkd6#p#2vLo%Pzs=)!N9Bvs^SygeSL6n;fn!U3g zgtq=AvfzHW1ULg+=q_uEnh*{n@Vr(#k<-!{F+ZOFig8`hMJZ(E(@H;^;3HmN)jjH5JLbKbAKuo$=rI$e&pP2!%vYk@J6pEH(=mc!A3)zxF&U#sFPRR0 z@RW~X*?9Vo^AB2nyepUx)CMhM-^XPk8rgumjwUL5)2^yBJoBfWM=*dz_JAMHwI2V( z28D41<3+p~F4{pKvTPD<7mwr4Xjjhqm_mSzOfFq}qfVR!7^B zU?e!;GFa27H#XqWVnK=o&5ZNyrA6x9Z^Cf_*SnY5uQ?XJNJ>%IQC3HF+cor@x1~Uv ziVJHqO?|Szc1dM}FTqT*&Gdd>iCGes?VOR;pH-^OzkWdmb1asJ-VWB2tizbFLPZ?j z+A^p@uig5Wafk{CF2l?Z_D0W%(yBgFXdtE5;=Xz{6@R_99c)t0+DEVKJ3l%c;pqr$ z_!zdok+iJV+UsRmlP0yJAd?U~Aej`4ph=&O&HhXyne{Ame!Q%EDE_9TtB`A-*95V6 z;<}e`x@v|!Cl$Ka(0oGivm~R^@|o_?5_b}cE2vaA+nNg$W%VjG)VPty=K~*61GSS| zoQ1GQi}^s$V|8rhVp)kAVFCBw%o(42`xHctW83Z*NP!+vTbKGvq$?E|()N%wOL6x# zK{ejnpZ2NrMNXDmM23AP$uYv)oR`ZRK05@hZH zvjJTvh5rD68$~3aw@2f~Z-g*t@WqlC&85?!DV3rd*h|AoZb5QWt1SlPS5(WA_~I04 zA4yIVEIY(9aD|hn*hDa8H_7Z-MsqPyg}{&Z?gL&u_);HFv=Le>rI=&Q<)l9ch+%~! zdxwpX`*6P+sk?}(5~!&pl&p?m6L}dTy3cWU(3G$ygwvZP(nJpUUClm^yTr>G@#BnT zx%FgkNyUIgs$9(q=h0okrfCEr__5#NVkgR1=bZmuJ0-}+c-cDiuorG+^Fr=EL17h< zNOhr(Glnl};#mSq^4wtE$wGX|3m5T;lvqqhWX~o`Y}r((4LhHqr1L zY5{-m6w`6jfH_$gnw*Yy5E9-q(&vXvmSizZ(yzc|M;n9(n2QlibYU zrpQ6vS2$Z!-W?bV{5I)U*uc&t;5{!j=Y4&KP*9xi`dX?!mi}1~ zNaZz2-ax0;9*`>qtd&W5D3SC{aS|r%g|9s+NIk&5yR^hpjqnevRQ@62C zpMW00YvsL|>~@a#S$NV5t%I0r~Cp zreYcZdU02;rm{L=Drb=4Ya%r}g{lCQlWXAIq^+aXXLI)405D%6;_LTONr*(bt*m|f z;%;;9KK7=z`VD+)y#Iel{Hw9~bNypnmgfJ>`)`Z;!@$Gy{n-)y;r%f@ZFQo5MzQ#K O_;{e7sjUBuYX1c!>QLnX literal 0 HcmV?d00001 diff --git a/app/src/test/resources/settings/db_vulnser_json.zip b/app/src/test/resources/settings/db_vulnser_json.zip new file mode 100644 index 0000000000000000000000000000000000000000..64444acdda9f674ff0e0828f59316da73dba438c GIT binary patch literal 6752 zcmZ`;Wmr^QyGB7oq(PAoq(MNWq(P97k_Kr6>Fyj5k&^E2p_J|pDXF2ma~NWV9AIE( zj_<3U@4CM0ob_YxwVwUl>)w0+xSv?6ikMhr7#J8h7|P-P@4a(WyVMDPEs5@8;QqQf zn0q=|JDI=uXnM5lXh#JG+&>nHr)L|S9kjM%t|!ehWqn0DMK2>y_Q;DsMywyhcB*X;o6IT@t~H7Gq=B5G;m&Y`d)F%7QtXj(tM^_= z-aNvxtESvg+YJ-G92XB|Rj;8!%QwLW9*=^(q0xi^W3FRjV_8SNw{<{bad!55l3PJa z0%o<`m8YUTF9{G+!_<4_I}o2eLdAf^v;v{j;Ry-*DS=)857Y&S`@cTqpCKxK%LX7G z^NTl%%(&MdF`9mRwf6Y;qjmK6gh-SfiXjAr{oC?scGJ|JMqP=M`TRm+3JLs<`P+^W z6W@XIbb|3N2e_cSkotYlYm%LC2ae_o)65dg1v{+uK9w zLI4K>sIA71f*qV3Ens9-PlOP?!%uU2a9x(exUJnf`YnTLH2GH`UG~MhWPoxt`JUtdq4OgVHKR49 zS|N9GTku^7y14^SfYW%O#XlWIip{T@AS)%%o<45=HVD7@r2r zFV6tfTaV2E4eOQA>_%gI+c9fB`KL#)wbj**wl`ePJABO?L%}F}Nm5sx#tM##Vx((Y z-|5j3Z=5=tikJM(Oj}#l+QwPU^G9=6tnK{|1d993PBiJ8i6TbkSCuCf!O|C#6y=7( z832NIkH-;=Yn@LFQ{tqqwn?M;6&!fM4-4 zvL~J`?p#bRYPr|C^|3q@2|h}Fk`*OwHnn6H9=?+`+C8fN(bwsDP1TcdrJekQxbUN} zClplrtu8wyv>`~X&Jgk>4s)~0W`;ic!*oc zoB&1$VGIJZ;0_X5vNqN+V|LTm+6vSmgvsWVgT;F?mUSbsHiLK4cEV`t>JrBkUE;=m zUbp2Qe|s5gv3FMHlki;xqRyLne)jG z9ECkpd512wf*Jdls$0}qZzJzD1drQznA5X-slX>pNhyA#=P$Y+AsWCXvqgP=58E%$ zWK!aFj|kqm4(RL1Tr2gsPPWQ8zpb&y(rPcr`RhAP?9vw}1pqUwMfGf7_jmBD+boTm zYPU4__2{ZQfw7$7cHfbCUAeLN!8{#q%$!%)J^r04ICx_nNY_qyvF2Be*zAby0bdK63^Qm82-#&&|f|8juPoiDqRZ1ByYf# zn|qfk!JL!pAw~W%`9QJuxhN_mmf1UMJc#X<8lQspIQ|h|5PcWp!7JeB&`t|N0qW1$yQD9LX^&s}$4m)8Y8X{>uNMIR_)s0c^ee8rg zi;C7|*117k$;eUwyk~_;oAA&t=jagNG4~^C1QFc? zeHiy%DnZeYN6yz)ofx>uC8CcUL2FGElUZ|&!D$vxZ#G%Wj9wo&v0;XUo%!EfG#xMKnMt3TzkDxW9>ao z5<95i8xbg1v&6<~!7p+BcX%BN8xk~>x2vF8(49RV!WYt&+3#&&c2lHatIeKXn1}Fr zFGc!QmC2WvyEF=E{La~77wP4$Lb-KmedH_M#BL2r9(K}0|`SfH4#!kJ0TDM z@YP{~DMZXXL8$gAWpv%eZCnS+ve_38`8OQph=Yx5czN^koIXWf1#Of)`-08OkRQNG zG!(|rLq|si!V3R|XvuNY`u*oI*42I>Xn${>djUbCX=l-YsLJsV{zlr_&n1o^q;-;Xi>=j69@Z`vncvczF`IMLj99U{Tkzl<6GL_)7*;> z$p5vz6Ml+k3A1Q&^aK~{Ll%Py_+qARqcZ@}_eKgy%2&$!Ke5j@L;O12h&eQcaM zYo<%)j^DR1ntZFkYI{n|kHa?v*msC`^$rP{7jUqW5>}zKpJIh+9^J4om`zpR++g#) z9i0DY7nl>pa=p+8u|_1>WC*bF4?vtwmVZnuef$vINvto#qnUCQekEmR;{rcUHu9%? zMXVn-Dr*)V#Fe+&;`QAwak5`GlWMNhk}p0?w%^PLj!!g&FN?&iMgc{dXX#OSgT6L(CmYh;>wuzCC5zM`hInO$=WtpjR(Lr zZgnRHI>{tsq!IS5h_Cc@^wCTa>J-N}zHt-k=3Ufgw_`qqp?mX%T8!?aIZHh{aP~H# z-sC-Ymqn|#oQQ2H|Hsvpltn|D$`m9&Xc@cSzPuC^ax{Ki6{ROdB-8$sN4A!qRp)Iu zj-Eo|u*}gN%5Tu0F?B0O5GS9_RSWXK7HypOQ!G}Yo>c7}qerK`pqixdynY>x=V}d% zaeL(DHSv`@9@Nf9wrvH?8(Ia{@Q8~~5PdHgyvZQD)^b7MU51`fIdWpPq&$itX>`=q zhE%*JHBx1C5@}&tD?iK+UazI9Z&h3QXtz)k!))#CBG~#^-M)A__oaq5mU4VVltf`- zN-&-p0jxnMez7tPb=~YjXGjKYWAap_Uvht{xt6>yQe^iB z0fQs@Wc+eA9m7x|H>J3SOwdo(Ot8KA2B_z>yPr5CFks~19`igy! zBwl_8QoUDFN=TdbhQhKe>sqzh8Ik7WRV!|@V%a&S3#M@&5aQb@;c9#*$^l;J(f_3R zINR0xm6_en!=gA)SWh~AiemD~w-)j8?_jm1YzH@a@7N$6x!wY})wI-VYmS5>Sb-ZJ zr9*&_OhCpw_pOkG3FcGI26sK#+wn z{tOk`h0eQjN#f|>vB@-zO84E5tCHGviDl#RYm&RoTP;^8@e9DhUi1CJ)uBq!gVS?( zO>2UC7JPkW!Q$y@O6xI=f%0f`{JdW#Z$QQya)lj=6c(5C^33uWqafm~TIHTuH!Lf^ zA_+kMju0RnP~a+Ig&5-v#dN<_IFnRS8#XvA}W0Z<|)?x0x;>V zQvyochksM?9!dD)+Znz+m~xh%VV^(mI9a7I%Np=yztwm#wn?NP zPIDP+f{wJ+M7R~4fcJW+k$7Eh{=AW5x2Xue*9Rrr-YLb!q3yoir;&;)0aNx*!Q#6Z zmK5EL{O&KElET!9z5-A1f60vJx!-0gG3X{h_20_-?yt%_y2MO-lo6A$3kw6o^}(;= z`&X#=TY0;hySZ6ASh_}O8Cp?mk%`}AFh?B~JIC8!!lA(J~W*nY}i#ywh5@oG>QqKjbCX^XbtiQU<50m02x{#FC#Qogo z3LAkkv+47`^t1pipJjEzGFz{x7zKl}2-u6jSzzU1dI$qhsR^W{11t`EIeacpcF0AY z*CKef+geO(KzYlJJweTA_@jgq>wH&LjP#r3{(f4UPwZ!zS|KZsGzMxlXNZ~?F?~mb z#}xh{?m)FydP<3T;lN?dAhj=zGBTmbkL~8@U`(1WK8QdqUqy?KKjWe4fo9ih21_yv z*$)=lA1sjl8*@m)A8SJJ4I$47G%I#Eg%$sGDp$#<@~s4BhEjHMZ@w&n`WII@f$-dZ zzG~5-dv>Gp^N}n{?7Z>@N^<(K?QPH|x)udRf`-}CI)%el;x4~>`NTSDc?n6c2XQ3I zYk=FqqcK46BZ|<+!*uG=KeUu>u zx6Xh$!?0liXLfBFN%Wjyzmw+UEQsXUh-uD&{TW0$AU7wq4Bs!O&> zn(;fmkPjj4z6I+f@b)3Dg&iHFhFTanFBoUUy_+=AwrZgce;;NU?=2Lz^f5aGPGT;9 z)|FW%yF@-KJ<9^|3#7KL2c7bxsM%JOSb#6{U zT)F=8TFu`MR|7u>UJ2bf5wcYEt}eJUE8IQe>qiQ$S8u?BSw3sKy;sH;wl%vKTBMj(GVSg%uJ`6g?vHN`D?l|S%>Ya} zelxMcidO+G;1kRwN7D}xEZS4zC7M(BlQtjRk$=i7xxk5Y#y9B*sw+L2ES)koSi4x* z$z7!qYG*EG73xo6&=8o%C)FIN0|l3+NzqguT|0G*NKLg(_Ox7rUOhL7>hEUU`+gtC z|Ef!b%{aah4_Sjz4QK))fl3%*1{m`8A#RJdxdG{sK3-)N@OhJze(3Pu3mt{`gF`_;uTHJYTA%0N z3@M|7>CKn?g>OI?DKW4I@g+qGo&88Q6>NXIlaF(|jTKoq-6XzqF}t7Y7>0Co+8sTS z>pHQyB6li`u3Iq)IR{89Sbn_YW+QCybjjN*rKzqr7Oo#{o=9S&VXTp@M+QzRg^C&dewLis<+i}nt zd%x8{Wz3V7dwY{zb0f?rN>l_WeXf%wT-+hC^hJ-7WFq{SQ(i@?(IvD~`#rgPfh|RS z?FZMDx1@W0wNb^fdL$s+v_y}5XLnpvmijCZuF51GsXnSF%- zk4$5?&3tQt$($2Wdj$iVnh4ckKg7oVtA0r3uhh3? z9Gh0)mg#PDMSB?|EacmK*Sz@%@oJ&ys>^1l1O^WySY1-p_qa#RuEV_AFt}Q;81*x3 zCS)GkWKA~vxh}1=;2uE*{sLkoc%pn)No$@=L6%MZb@Y{xx22v`vt5Hi@)SGsmYOVy zZ0M7I@~j*-{-=#p@ZASX{8II5)4mWj#ZxDadHH=Z{UpQ2NF6nXgmAm@wBDrdsK&~M zl?+bfpr^JEn&TgRzETc1wNbuY%PSors(ULKE;Z&tnNmQHLu@xy>f@yp3m)?P zGXqZw0u5C2GucJM%`Ahow9$=U9C|Hfq4M+W<&kP_clU`qALKl4*}cX!1)(ob74V zvdc~CNr{Sg?{ycbPTFc34}+61#av(^p}hbbcJ_MRah=%9uvf;cVOOXmHbFm`rk&z( zTv{C!uH78R9B8&npiidrs~|h(_;u>zeEPoU+!nyXCeMA2fuL{O1)$JJ%;oKPz7_)= z3d0Op#n`qCD;6h0wM5SdaGn8I2*TN4?#uTJ{#;%R>e!aW=yu2UFgAh{LXc^ zHZFyHDf~zI7glcH`=7r$h~HkLIx}J#H>bcE`p##t0m6WGbVksnZsFD7pyh|CRf^0` zIgy5`A3$okK@>c_yP8aapmc2H%3IpvbWu}I#;XZR|IFV=p}m$s?U|MPb|COx2e3pv zl68Pb&77Ut;ems?%q1m>o@5h2u)xpI5dqk0dp0-pb=*Urkt8-e=;s%FGZJOF$Zr&Pc^xcSlsyCO3Z*A^f2FW|K#0LZ8HNTIsWDi2~$P zZXl^oN$GHxp#?6j`SDM@A6&+-k_Qau?u5l&J7<4BDGEl4Ns06h4N^DTktjcZtb(|6C@X0*W`ZLu7FzYEKG~}uHyKN?il#~WfXNc&pU|j zE3Y=4!C|90V~56Iq6ioBs(cDObILcq-1q?0Ib=(sQ6K@un`Qd5|4K@`98bg?0pfLB z0Km_GspXx!WElVdxyes9Nw`Qv<-uw6L1QP=jqwbe$GQ~gs{{w0%Bb5LE literal 0 HcmV?d00001 diff --git a/app/src/test/resources/settings/db_vulnser_nojson.zip b/app/src/test/resources/settings/db_vulnser_nojson.zip new file mode 100644 index 0000000000000000000000000000000000000000..56d58a22e2d2568311cd041e4d6619b8224a60b1 GIT binary patch literal 5364 zcmZ`-XCPc%x7H#eI*BOZ)d?bkh&CdK2%-~R^d7wr5z(Ue9(|DLy^{!{jWT){VGL$Q z9n6@SoA*m{zd!e`AN%aR&U*H<&-wAJwYHWTF$n_!0Rb6-Moh@7fC8;P9qM088X^Mn zUk^81KX*rW+xs?F@Rc_%EKuO}Lzxs#f#JDfM;G2^`g|+?NAwU*WmSe-{#44^4KAu_ zN+W0p(dp5JmTb&VZ9kP^Pf_l)c?xGp*Dfi8Ll{bbn&M6iBU20EXsl1)8pU!FT%$8! zH?n8}ZQs{WZD!z2j9N)fLGbG|u;5kOkVCh}p#g{l>d*k4hkcbjx}*}y;NvPxh)&MB02Em@QcMD_M! zCFGXB13#9vE977TVdE#)cr-}#w@#jN2n#w(Rr13o@OM{&D9*VMB5gsZlz-@g>13_h ztyoqW-Mtrs7Q-=ws8kf2FCOp~9w)c3W{Itp$NNe3j(K-`d4f(#5?YrSP31wV1O#t~ zY|l@CtXsFOfh`-gh>xunuFeyV#;SMW$n~|go-RcZk8SaGp^*rjs{*~3VQY<0O*z&p zd*B$pES9Vzpy{u=J=@ilzrK0WaPQXq1%LP84axFB>mxnRcAD6+g*A<7HK@|rG*h*i zbS{vp+xK=X_j>Ogvn=&-s7)VGUMlVjk~ zcbHgAIz6|QmAmO;7_XN=lH<2$&tNdP_tDaV`%8^zvGyON*||pMIcC-ap=^iL6zD63 zf>3T4bt0O$gb<6ZSfA*bwZ;r|c9t4aBcByiLjnD{D@Ji7KO(lXx1-sbn$jlJJd-EB zUv?E9e)wlvD!S?rZCa|7CPXU%;HpIT%89G2)aR9^CPf2oNCCi0PO|>ih2lC3c?%hC zLS_9dMF*aZk8}6VwYFIEpT}KmiI{ZqwdLe{UqeZqnOUxA93s0HD;p}Jyv2HYjWjsS za$5dLzYO!8nt{vlQ^`=|+3I1tTO?3;)_(tUUHU&kW#g{v7$PHOezL)N5JUuh>!Fgt z=S{@=Qe2z{I6`r8p@Y@<`*`|LY|L!DXpzt@$0V5fG$}jTA^sHo=J@DTdU2A>;=bJl zQ}Oms3V|FhH~eK>p@U9xuRv{b@Ycj_CGGo+63xAqE;&onlE7JhP_w{?y=@A|E_?H~ z#w}e5V~+aX(j?&+m(SRuzQQERh=)Diyuufx{UN=YWE9aoTF)sW--Xr5A876i(=Xj* zgO`RCO%+X6gdTcYx?83LPXgBsJA51MUy2G*F6$M*rJ5hw-1yk+Ik!tGlAuJCJezFg zvISn+JNEUpAVU8jK*O4?#Z<|}1JL4$3>ckjYi2B7FNaPfH5z;PMS)% zq~=)VSQt*oy!+;A^9`Rb{VLj$2G*Eg9v-#@APtuZTbW$axkPz7g$Ss8xO%J&a|qJa z;fdFrEHdC9CKrwRcIoymfkfsiPv{oX`u*%2?^d==$e?6yJDb6dn|k$Ff)H3z>O>j$TIXFzwDfeg}&!Buyp$GG%EgerHMMtQUvN zTwm>m&6P028v~H%BFktH2!&r^O;p2=&y`3-GV+SMhn zk~F=UYb4=Wa}E`+BOc6>PAfg4h<%!fyWI zYa)R%$yob=aorFNeAC%gau3eFJ%|GPHysrKpcV~cVns#nZ{se)H>>`6Pb$V$9Li5K z63x}m!NCG1iTQ=-DDc+*{pSfbw7;U+e{WuR2E*go=kR~1swwyXX4<)^Gx^`%C;R>X z4d2A$|D0n7H&Q`a9Kwen@i;0m`IFjy3OJ8=zd)C7(=s1-MAU@5VqXxFth z(68cwJ3X3b-Prs*R^S=%lf$=F_nzH%*DW6Xk()9Bs+G4aX-(j@AnmO=y$?iSti|QE|vcEKm_iY=n=(1gc&hg+f93_!9t&o#; zjttS1Jv;O1=P&r3k7*^y#7BU8ZYf>?k&z3MZVpeSHJM!@{75}GhL6h{Qjfuqia#G- zuyF}1i08Xp9Dq5Z)17i91tf-G?nf(MXVh(8NA%K~NIlfcyokAY>f+>yI?ON+;dn%A z5p-x-i?rPc(|lr`<@?u8zW1bVWq&lU zv>hDW2Vge#jpf^O@HF!C%SW6t7o`)Onz6T6q+PRRwb|D*lfnLdL z+lh#29%Xag=uaT=$|ni%SrG1+EGVV*2f{l5)a9~m3n4JVyl0i;u(9Q9#)r*ar8Zl> zBJH#5G*FRou9UD@Tg_ZDW2?=?N`O~Ln_a6b!IALE!@79mr!>mlcOO1$l;Agf9z$mQ zB5hO|PRRTT@jbC=)g0y?_+!nEF|5OY>=lI1A z8nWUv$EUG&R*kBo63~rCmgY|FRU4PZhD2V+0MEyrw{=|0X9^$a8jxtD#Ky~)rDaA? zXj36u3{#eBqj8t*o*ZTjrCmIJYMjeHclFjYR^~>CR;M3pRfo@`>VDom;H=!K)_od7 zrQ2Het{J^HpLbs~ErCvMC*diH^4Q(Hy=T)oFt^7_;!Cx28bZpmYgTE2f`-d+UtUX^ z!Z|Z2s|5_rqNKdllUwp|XH)|}vJmx#5aTE8Rp$YM6H=xhFK@c=xFx)ns{ZgnZ~C*> zZkkx8RHF1z!sF+{H9NvNdGEb^%c0wui|J0mpSeD~cr;qdKgYK$L|Y@+^@@S~C!I!s zpWLHN=+|o~x1vCGFCL*ADrT{>^Qfu(-P3OKRqXpZ9*1{lSD$BjH8e*Rp(c?BhNm0; z4ZOBI^-KNao}e#0gPq@`~;jztgD>O>$lVPF#kiI%7Ton}uC4oSl8anKH8;EoOkr)KvLOaNzD>0hNs0ea%pL z6PN@M=({I}K8;@dBh@VmFkOSfQe6 z74XQ~W&0*5864f8!XVP!SY5Qcdz{&M$Y!cB-k!1$oF^8VtH}6bn<oxVo}6CS1(Q!G_^;qju)n|9;-TNrMx0W*_RW7<%zP2@nT1dV5BHZ zqNBOaQP0M)(B;AiR2{3!aV>k!6`x_X8rRj+293_fibj3%{fUmx8AJNXb>9b02N+g> zv-eP+G=qdzu@UGWjBEoQ3TsLl#5oc0{8FgT5TcsjY@WT<<~wI|TMj zU$`ZF9=NAR>(G2CJ)-<2Gk&MRKeA}Sm{6_%blyb2I`4!EYlCraV(vZ?0s^laznbq~ zsp4O&7^hYvWFVqo)PEuut|N@tsVc*P2y0Qt z)V5#&419k=i6bSPO1oH7pF<+|rq#Y)-xDr-2D@jk z?F?SqVFx$ovD9DJrBItvep7gU(ikQOiOVdJigArAdE#94kLBNrpHb<&_fnCJDI659 zmmRt0GOoH1$EPkRrfRCLVv^L|g=pjGc%eqsGI!kc;-HhZFSuDXtw~8$UcvuHGM&ac z@M;)70gSlC6m@%)WAO1*M8CMhZJuWV!}S#Fz*EsA!~L(krn>$rBu#O~yHNfqHHPFM z=F-9*SbA>;n?D8?b)&NWh-Ax9k|Ma}FQlInSxF?X*2QI7{+dw>P@^%jw zDGX87Z~t_#7WOXeLW=N6%3dp=zJzcRFw_|DF)8}7Pc#D|K~ie zj&n}_=ccq9i40YSIveWnw!$5Q(hq`#Q-&MXk-g?n_p$Ltz_Y+p( zC{mCyKKe zxW*#Y&0EPYHJHhzE4e^PuQ${Lj;PFj%2o@%bnh8^3hA2e?>GlPx@Q?b_=S7-^L4V2 zi#{0vi`1_X@ZsuXXF``ep^O-huXgCB|)>V zs@xTzkQvDVAV?T(e?2OS9T3+q=8nsd%euR_pKT12s%w>2=lUe=cMX0gGixEf@@#Mp zWx>js{C?!?ljfG8+kz0H9Or|SAFEDL#%?1?X;hW3dqQ@j#!-XNC~&ENul{*s;Jr^~ z%n1>kw#y;X81PwUBJxH`1t_(55G$Zb8sc(fGr!YXlTY@AE@(b+=WP?$h@oNk8$axZ zVN#(CVU77^Co#3~5Pc2b*O#IK)GdBON;6WEo9MkhFEhNnmKq@u1Hu1))A+kO{yY9v vKO3umaq`3r1pl8-{za6A1^z0l|Kk3trk2{Z|J>&h{lfeH3U&DR00jR7OC^Z( literal 0 HcmV?d00001 diff --git a/app/src/test/resources/settings/newpipe.zip b/app/src/test/resources/settings/newpipe.zip deleted file mode 100644 index 1ce8431feb3d4f2b14e770ac7f8a3b7efac07436..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8317 zcmZ{Jby!s0_b-Z+gmj0*(A|w99Yc#qN!QTbAl)xRcej9mGz{IPl0yvLjRHf?_50qR ze!cfTcmHvov(AcVpR+&vbM{(GT^acqArd+|I#Rb+k_OUW(834=_kZ~;)UxX)>lo8KJn0OlvfdIP@} z>8q=P&%42I1s?@AR!#c$!?CWjw<7C#&sDb{mP85uyjF*EZiXi6d3dvBAxuzMX)qxv za3ccsw;HmkLXm9sW0iZ2=Jz9{Y!1uKe^BafK<}v#YPwi(wExj`Bl#h+oqM^ja!Ia| zN@>t_8KOv5>pp)437$5J=z%f!s3&Gg_MQ*ampA=$={VYMV{AW5VXz2#zhpgOH#Q97 z>-}7>V2Nu;%ZJ4!xu~M=vdort6J6|3%<&D=$g%00`IyFJ7nN~EScQkpSlyRaoz>VH zZHgZJMwsOfQV8ugot`Gp2|omHj2YV_q!} zKcHYE#+)D#{FM<9|4BMHarwTMTXkToqs>%9`j z*Av)Y3Rsm9YQDlo6&PqQW~PO`x7JUoASWQ(Wy0juVgp-{?G7C>%W9d2;FB^mrf7;( zxWIWn0pe3-wY3F1LaY`0d6cJ#I*J5-#z&FbQlB74k)xvtt18y37tbhIvDUw2t?kPC zvIH|}SbxRHoXNt&AMb;lDB>8aqT9ka;wqboUn0Ns4lf~C@KDNjR>^rminzj5~jC|r4LW6(=*#7I>atuRLt2$ zAcleh{}?#U#o|oGZ=QEuNX<~tJ-*KPL3utgiTOvLjpx`Enq*a$yo(TGx({bL?9T%# z^eZPx$@!TdXeF&?-YmHJT)1kj@}0X**Q#cgAzW@VD!P~!GPowbSh4r-mPv9qh(53p zJSv8#XS#JkX;A)+-BAE4mhy>QMJ#n@$d4k*c5H^Dgl6j9G2Cj0t;mcQrQ-FG$J5Gse zwvdhk4vXR@rmUsR@7HAFu;El=SPJWMaShh2d)e6hqN4o$MraL|xO|#4WXnU4ih4dJ z7-}xzY-d~hPeT0uDDG>;FYC>1X3%0i-EbubmMMqDQJg4>Sx?}u$AMc7XjN}Ke$UP_ z^Ke937!MnSrrO4vygBl&vhMBL;jD@OY&*CN)VRF3h${3Yh)?{A2H7h)cx6YZS^wV1 zP3_?>u&-6<4*%)z^&Q=Tr7;H!$J};I1-2fPN`( zyN$8_Kt_ndJ=@0)UQS$1@w!ABb^6q=xj*HBrO0cpV3|P6DJO4 z{F(MCLuppu;CWUlg>a`yTm1B4shx1AWn1)gRVkZrr)gX4^cW*1kfT&txYIndZ$EpV z4#<^h=`3p37Br1n`a@X4Q8Z%OmGNm=s10fvU#U$tFssx_)FQKgzu4w!89}LYc6`>L zlc*){<_w5@V87hvSs6Ve0nqv5w8=Cs@ci@)-g0q|U^LMhL>SXUr0)UeyZw3V-w;+& zY6M-c@1LK?cpV|M`V;VJ7y{D0uoLbnp2=&YM=uO+KBD6>z+~As$NzjYGCo`0cnU|D zHj|J;dD7wh>`0^C!PbF}yUTX8Xse%I&O7#lt?vMLUZFA|r;#>OL&L#VFN(X%V~41S zCol&$R5T+)gK2vDz}!|TM+I1!6oui=D_H{^9Gr=QegewL&yEa7it=R4jGTQIB_rp} z7^I0qRH1I@_X|ZLWc|Y6Z2wsb+DGJbrhXc{Jb>gIzi7DohHjKGqa@HT8BVsr6cza9 z9{rpv>YAVy?-X!_p0Va^`Fs9I;-=)(LNIMGg_4V*VhziP4xqJ=AuXl<*Cp8;sL07W z@(FC94*sF!0#rl?WN3hG02!KKML>o+*cm{kh2H>%kF){Zg>+$2#>zfUnQH0a4dmCW z6Q5;gG?O}l4zfcp{NilqNj30)(BYF~#a*cIaA7v~4G_%i1^Taq{AaIr^juMBS&l8O zI{3N2E_^P)p!Brxz+0oRxMrU3QTuBa2AHgt3!j0;nndDdUf|DM7Ri@=bt>G1Dv5Po z;jtFFu~)wc&YE}mjPOA!hFz)+sS*Ci!r(-gWJcgJOY`z!*W?GzY9 zuy|ASw0&2N29=%?^O?{HTA7u9KH+1&uZ`#M&I^d$GAJWY2qI^l(6ipQa>{r|9RbNY z_GDnrd>j8pLjvTViWw=NoLnZ7k&?UdDre#1&}Z2wo3yciHx54twFM&I z1A7Q~t_$aBoJ)w!;OMa%eAvzn39P5_687BSK7+m!&N%knQ?s0*_R^5xmybyaVzC?V z5;YOjuQB8c2-I=i8t5DG>N2Z#P@JPSn&6@BgsO;B_T3cryp!|&AYgGVc5-lH`7z7T zn?bI!00xbqOBX(yEZ*vPkJVq|!PhftTg>Xj=_yVIhvx~YapwxO)Pt!FmJXz>OY*(D&6$Qb z*0;+$mZJhRq~|j{T7*y-e&#yLx_v2%kQcF{YWl^2#|ZIa^zS_& z(ords>%yN`w8tcm(vopmckI0MTX=NHqfDo;>f7V5*>$IsR0jeLW{)RbwTaq&X_%U( zr}u?hJK7Jwx!+2tqA}!y03`w4PVCtjzVwOJeHlI7_W=jt(0U5~ldeJ=q*>u)6yQ#K zX5Br`$(Jp)qg|Ur*nww>cV?U?`RKIEPci8qwGcn->(q~f*PUJlFQ}6cUo3=fXX3Ry ziW>Hub#s{J3u_i#zSK#2tIcH>xwlu1pFiT%Kso>ipp)D35^_I-gxi^03n}}3)jku) zyOgRHGxzp(vyzEn49#2ur=c^iol7R2%OY{6!!Pec64EA5yAGqFE_Y4mFWm|+sJq&( zTB2``4bu|1nigBJ9^hBNI~yQpE(vEA2TWx1u`%h};k1i^KWKq^3IC_W=IJ{*4z9_Z z_kfups)1z( z{n1RTQ|`K_0ZUX~o{%cc+T-?X8Zpm=#)OFDm6f|2|BH`MushlHL*|;)?aPFdQnxnz z1YmxEGS`(DewkybtHN!y>&D4Kz(ohNw>1_mNo{eUD5HhAl5fWSu3FSrX(b?9HSdv( z-#dA$Ip{xo3h-d%ftC2 zRkbXRW+xF9-DA82Bix6>k!EPGyMT#*X0`YB9YX6(9^d%=`p;8Uf76RT0vbxk*s$?k z`KgS@)3dx6{*!P=Y^m#=_(7dFQ=L#>B-jDYq}l}O;NJ35gpqJM6-0v9D3QU)Z>U$g z0opOYW3I@V@5`@iZ2iAa*@{M&sl1v?NfY*Ar~J4hg{b%nc(kd-3r*T43%q(}m682i z1HRWM_MylZRl3+%c&lb@$&~!HL*LW7@(`F-+n5v&A4ndrS2{jx##C6d zXj0-_Oifc%qAP-_@=}eP_tvT3HVLwtCEXSlvRH6yBu0-Il(*;`s9BmSV|5PIx)vyj zyscy!JH5|Z6(dV|IHk}nG@Yj+$}Li+HFgop!l~7SGuYDAkJq82(K#UkbQ|G@{!hMw zoVcx2Si10{_$g-_$vNO*_ESxLd3vy-Il(s*Ka3wHa&6a1>79_SCUVe)b19!#CSCSd zS9D5ClK0UF9{~yYx$^h0_w~eE_1gr-N>wYI+6=yh9`m2% z>@__*qw*OR&-7R7K)fOPX}CJN!17SzidQHZ6^iCj1jEm@n8*@;%5^TmV|&+?2i}-R zvus+HbvtXSEz4!!Zj`e4b6aI}WyEYReH*+5LI~}wB=%dhRg=rri*p$QHXfAC6K!L1 zoH{$sPN}I4$aE&_>rbq8T$H^%rr&xfEVI`9+Q)%QXP;tAd?le!)e8}M(pQqbyy((T;3kv$U@g+A1uGvGQF_mrH+HX<#wa*5I1qvM zhF%FI-e%U2wp20QTJLYgLy>y27DJ>ZZyV}lSPkY~bzh;L^LG9Ya(nq2-Rq#PL*800 zHC^47=QBiee8B}08lmbn>S~PW&>{%^etiLNv-fE`ywY@)&_ib`3_i|ZOp8JhIwLJ8 z|I**-Pqs|Ao`jjR_Z=(H$k>`+7Mqbj%Eg1io}Kd;b> z0fy?O)#KWF&&_(-;ZDl$#7(E9Lw-UvIZ0MVGQn!oNdZfB*7o?O#&xIbFea)r z_Q^nh4)cPSq|T?>#Z4XCZ){@~93#ew$=y2((gK0OPe7PwVAYdMK;+ z=-aXCYI`S>dS)l_1Pb>O%g^PCw**jMrh~y@uKk!0%l42~YQ5{{_B@>mCFKUt6q_>_ z+KvOGs!^6MPtaw=u@a@I$v<7Ui+#vh8W?@PVkYmX$$T|Q7wiLE+Xqf%uQMak931JW zqau2jS|0q2yyafGlz7)8ES6S$p}Ds$5N?}MDiwmI_Y*DC6CW5?@j=vr#4r;IztS8- z*f~Q);8n;_nmt8SEn%<8giTi0F!Lq?W10ku9x?JWEMrO~5OjdXlo54GmJr90@VI%# z^`g^s3p=BXCOYo%s9W9>3F%A*ZHH`|Zf$-=DzdE?5a?_#G$oL5intdhR#M(*DuQv{wT{dxD1uC z!-b!4gzqb=Wf*~SeRUsL{eRI$Gwq;&=t}s2Mg5dhpoUeLC@iAqSv9e#*;0lQ3%G!Q z;*!0Km~*`cYO6WQAfNY8T@(=VCJtuOY_fC8`{D)Xq>V#v=kdrl{f1-ykCQ=Foc1ip z3v}|~ifz5lbZrTJ$qMbm;R+7KEyNjePeab^?EPXz?{qRXQ3>*TmGtX{41yI}N4Jgx z=2jLJ81`EnJ^Pe9?z>1DP4w>P@+8F5A?2ZfN-_37HI=foT|0D7E?LF3Md6)GK{5~v zQ@Rg+^onO*ps}$ye-@33I4M(?RwMNMYfGZFS5Z8bBO`}bxdF2Q}UgpvfJ#bkCsb%RtEJIgzI)~{bm+T=SUTFgb5$?<}j@TO$2zPm;IxKp;O zdOM-t4(L4%%{Lu^PC=@K2ScT_$)MPk17CY5=O4`XkiFX&q92cPR|03g{N zZg*TheXfTc__>BF>ln_QEcnKz@V9(HwgSAz*K$Q(GsLpOj8lt-f>|zh>~YrOpLH`o zi1!Y}ab@J6rB6ZyzW6s!792YVhg7^8aq8bLqKE^T2sPL=?CO`4bY5xNdp75Gv|jYU z&Q$`|jV!U+JH?z^BowOR@7%T@9oYT_qHwo=oqPK5urHfc> zxC@{j#I2yVL>mipwQPy=N*A>n4r%IDbpRV4UY+E_xX>B$04C}a2 zsMo<2$GHZIoKQ7zQX3BYN()ImDE6g8LFqReGh8YG)^t+2WUg5g(_N32*Big`=pdo) zo=ys--2;g>FOHTJlj>*lMjck$ZOwAC#@WZ z25@%b!N{#8NNKgV>LsuZrL3^d9hbgS^XP%%8{@#^^3}DOpA=hNHy67!)CR3J-mfZ( zgfbg%U3y+AqWkPo0@j7$A~mIw7xL0k1P6r5^p}YURvk9?lMW256?Z$EJ;zHFMXi|+ zZAIQ{FIn`mCKib|T^2!LE{*cJ)TW1H^&fth%|4Ftv4*~F8mC~6AYx?>8gLR$)>5hD zS0Fb{kNQ63?kqmP)a-5k0!arSrcmu(+=3+_5TfQGEf_R` zr&A-Vy-+(put3b(_fpU_EC~0ky&+LWxY93JnCOCq{=;QMsYR|XwdF0Fc6Imy?*SyQ z^K;b?WxSyQEZZ6UVM5Yyu=uiz--t2j_!CJ*Hq|Wt_PJ+3u@Glpnt(C(H@vjcEgLR@ zR9#b8wHwu}?Ocm{2wsZsqDoo8Nn6rS8-I#I7Jc4;M=N*kN@bhtLk&26xT zVC2&aj+yqD#SQ2$HM)Eb4()Rs&(Dk$r9JmXHb>GWUEYGTJ=K-x58EseoPjGDzkSRO zC}LBe+QUpPUG90ty60X~&7)Y^+99pT=F-WslqzWMIiZ?n6*vpi%6ODgS4Kl0;^@hk zBmY%-`S27;{r}qy!~JFOyBp?e;pS%JVCCws)8$AhOGu4qc_zl-Y&z5v&iv{LKb*5) zh8SD;Z6T=%!2mJJgt^1@%CS>B-(iN(H9g96Y{D;JHPqF25|A{s)W53J`dBBHdo?up z!s6mMZi;iff9zzP`kwkuJ(_aZEu>8KtAm7-_Xm+pk|YwFClQa+Q&kCpIc5#lP}53T zKVzBl2~Pd){vcI1@`NAPQXd{q&-YMn{gn`&iJd0&iBIJxjuQz`TgGMb0*G|UE!5x&45xGFp0xU>dOD61xrT^@Gd1(tZHxUB zv*HNvE_;HU=dVr3E|g@Du%ukp zx*aczN1hj7^0!VJ0!4U-+dj0Us0HmVzNP&!%!wh-^+L}U?R6AqBDl=-JJGiweL{5c z6k>Za7m1V%ncV%F;_i3(Ym@k<=JUOi=lFOP*8Z0_#lgk+!#-2HJg?gE)^&!9WJotq zvFgcy3Wn6axO7u?3blh@2{>i*RLUj^f!UD(MoJ&Xht&SGGo9h^XPbX5= zaDeoHTZeH8`YruK5ef}eIzWUaF+UWEJD+OI&5+{(|}!k@{aFeFNysH`(tyS=xt=@9x}AqZ25 z_ZAH~_9X8g*Eo_9b6W{-xUpUDq1sS_(Grn}?CnRUDmSQql{a0HZ)Fy6PPq`*iOxx`XGY+Az$Y$sf zlfjlo+Sqhzx}x|5ha8olwcRW+m0>y8JUZxcxH+aa?ev_;w>G1%qvIdle}2h}A13a# zN_E5!t9aS&lG_b z0&%lWBS!xb+T`Fa3gPKXEHtr^nSO<&107@$60|=Shra(O83tGM`Ua_J+6@4yByvB* zR2T}lISVv;8~cQ*Yi313cJ_9)WiR0WQrw8)&~(W&B&0YDq+b;G~|F_@YG5G{81rT+!Ia@nE) diff --git a/app/src/test/resources/settings/nodb_noser_json.zip b/app/src/test/resources/settings/nodb_noser_json.zip new file mode 100644 index 0000000000000000000000000000000000000000..7881f939b1cbb33e98318906a029fd6356fccc53 GIT binary patch literal 1410 zcmV-|1%3KZO9KQH0000808NR0SZL&>KmY{*0J{tT01yBG0B~|;W@U0^ZewM0E^2dc zZhcnUZWK8XedjYq;teGOL9pZvv8;BbWm#?V29f1%SI=1Ewi{nEnV|gpp0fKoG&~Td zeW@x}ojRw!Kih;_8WPco@GVJCLbq~lAISH!?-H!D`sMqhQx?5WTJ=DN#Ec z$;RnwZAg0YEm0!gY_xB4d`kyLhA5N~@zjyH*NPnctSsVa=epFvWlL>EUIhI}qI03x zi$1YAjh6+0TVr-;$Ow zKwhFpx;9ri8wK3}a;`mD9PrzhW-=F|^IUfs4paWQAV%6<;+WjE45kBSCIFJHeV=+`2LkCIDAF8<)_dJA6HK==AUn$?w)didYo}KgKQ>} z;2iLxBE>2(W~8OKcj_$1gR11C&W9Byt-TC;X2-rt%Ojnq25TeTX7M?*5Fic+Af(+6 zi7PPbNya$H+}eS@gP{Npgtl_vZhULuytoHU_s*z3n^)URZF98*q6`9kq-7nV^x$#` zsz&fc~C?R8C6xyDpb|mmlbLO*=mlxZK!mWfTF?$Dsif%sq`ToC~ zkGJB}=bKw`^Y6zGf4%>kgZEjb_{qsFF zL}v2ZR7XX4xb7k#eqKtwvkp-pXm#X)qy@gDs8Wn5$1+i6D<*CUZc;_}8WLJTxDJIk z$cM}~@7}$hG44Q-3wq3zDGcs{E;Q9Yte_Y3kO$;)2XUv9UB5Q}nUu%a0ZWg0_! zF%5nG^y%Y2w>OjiGo50{#`r>3tqeP5`pz?N*?so(KTt~n0u%rg0000808NR0SZL&> zKmY{*0J{tT01yBG0000000000000000001Ra%E;^a%FB~WpgfSb8l`?O9ci100001 Q0096#0000k1poj504D>Qy#N3J literal 0 HcmV?d00001 diff --git a/app/src/test/resources/settings/empty.zip b/app/src/test/resources/settings/nodb_noser_nojson.zip similarity index 100% rename from app/src/test/resources/settings/empty.zip rename to app/src/test/resources/settings/nodb_noser_nojson.zip diff --git a/app/src/test/resources/settings/nodb_ser_json.zip b/app/src/test/resources/settings/nodb_ser_json.zip new file mode 100644 index 0000000000000000000000000000000000000000..aa8316c6ca38c2c5198f21f7c2fd5ee18a9eb604 GIT binary patch literal 3177 zcmZ`+X*3iL`<=2yNEtNN32E##N{lrz7?MHuC21z>*q4bR>)06~V#o|xVr&_N7^R_P ziGFsKU6vuTl;!Pz{vY1=?VR^{K0N2%bMDuB?_+Aj#0&%g0H*-A62h%h2kSXQSpk4N zE&zb%4~szs`?&d_6#P*E0dANF{()8+p2B+PiKDeW)w3!=`6@Xv@nDVsWnW#=Dd`wV zOSx~%&Q{=@-nG6duIZ_<-rngnq$%W4RJJJ+teY#Ui!XyfAg(&mX1uz1-LBv0V8%|9 zk)&FSQvH|SRtAoN3*U>mW%DLA96V0SR^|JQ;g}c)E)OfEg&!>M5uGlLy@jdqxSgdW zb-2~jc3QFt)Dm({o+Fkx*seyz?cv0<8ihj#S;aFZ-7pMvrvc^O;9Ew_rSbAOju>-T z?V*DN;i`wXtB4`t!0+AU@!E@sKwHtU5$)XIxI7FjW2g7mDtG#%QuM|?JMi(NoRxk zLVde}`&m^Ue+yR1HGJw0;r;!_>R!KICrlJR`O~atLMHQ^#=>W8JHdgURH%3+yd0NwvB8kcXpv{UoFC9wA5_v=yPULW zHDqNaq84-BP&_iNYyxTKZMgg9=WWrj{lj1ZDbUJu;?1usp0uyHRQb^K7gv0Wlznd| zj>3ZO1x!B(I>?Ow4#G8Dh23dQKJGgaD`G64%5F1~EA;y;Jh}d2DgODXh{7in7rM@r zVOgepnw|g{HpDdPkt!k`eKVG$o~A>x^$MAxEeLR0<^T3 z#Rw=xA|fjU|KSW)#{NU?Zc>$EbtN;5k|252Hul=;C9ZBq=IU5ThW_alUw{v>!v%KAp%_Z>mcPqF9Kv~$uIfvxQ;m=xc|Y?|+`^(jG(WeN zlrsB!z5u8g7Lx7Q=NLYs<-=Fd)*8f^r+w%OWlTwM@N-J&R=EPdnS@?;mCL*^h7c3% zb~M7HL=UhC-XEtMYWYmJSRy0g>1ck#3OQ?o6wkZGiXGF@?=$zR<&#eeO;nCC8&7|M zn?sR_#>LJAm)9q@!$n1lE`*a$zY-076{wJglkMBgx3`PsE~xw^x5xKwvZXEquaxe= zM8k(>Kk>Scyw7A|`FX-*7p!7~>%LHaGK8{rvBMvia+|N{b~t*J5ZPQ!mYXDB(u&yn z?GbUz?%||ZHB~c4tFQx&&6J)IlM_3bN$bC4K&pOL^2n19O*l0wx*wC3PTTjmUj4c) z&?V1%dU|}p&U>nf>ihH7-1`s-$qwkO!vshqGStf=&>obd$XOw=)GMziEPDc`%VN^_ z-dSTjVhu17wyH}@Fg>Z91 zWk)Mqsd-1V#}?OS9u&p7F_FeH(d(NdUyimmA3S;V9iAY=GL829aV4f{_@`O2td9EP zR$*QYHL=^w{@B$~ZfRP4o`Z7%PhT1UMacmn{1zf{zM7Wog7FSt8haFA>|)=yyDt+; z-R^3zq&*1ZdzQ!|Lq*#5`6;9n)u||!L0Te>aW)oT^SfN8_d~Gm@9Vm^!a!lA<~71? z@WF0mI5~C623kWB70#1+RT_}VS(a^v!jdY{eNv$qb z*d#j0z={1C{q^k?P3dgh?Yy%as+q%j~&pG90J15o{SclviH3)|lb=-z>PBK$0= z9d_6*MRO)6{go7AD}%e4{V-v380)v0QKEv76mofcm$H6GoKV9!`g$c|lhiA6-xy!P z7~W>n%K;H~xy-98DH)#%xl^c{zmwr1??KRcG7(HB=AhQ5SD+4Wn> zS{HY@8VgsC*i8Q`s9fhtv;iytKnv&p2bGT>3XSqZVO&uD3hw^i7@Xw<2B^w|TU3J2 za9w1&Q8UL?TNzY>vhn)CN|?u3^;^$Ug`A+d&klYU)cuBCRp)t*E0?h%!b)FjO~lv= zo*PWqkT<-o)98n9x3m@rBzu4w>Ye?k40xBi>QhS7ZO`>_Y|U1}iq6?gD2 z-S`K|Oo~<+a2rplg97zdfL@gAP`yT)`)CXK?uZXh8%07IsV3C(ZrR<-;Nhk`QheI; zrMG{W5gR-Xl6iJ>n9wwmD11;UH{-haYM6#H^3ro;J~SZt7mX%q)~cq~>^A#6mI!iW zyv(Y3LEvll|Da^a*g?M45C*eeJS;lC`vqPgVWc zr|zj@AK2^8C1ndH-FbYJi}|W)q1Qy*6vHj zbA(p&=ZH#gxcb@#@8c{Z-#|85O^VY@Wp=<9U-;yp;H5h}RbkqWB>h*)R;ofQQvkm@ zlxn_CanLGjfT{4t#1k}BfV5>F4B|FFY6io{9cGC3xw#*|QI_scx|Hrl;IK9=J36-W zL`#7UL(xa;Yfg~u>(O9)l3c(TmM2C_qy6D=sQ3E*3oQ&%$%t+ee3i8+m*%`w2ZA^r zQ`g?%*9R{yy$)3a z3%TIp1RO6Y36k6}@n5JXJQ0{k=&Xu?TnS~1O2lX$>>st~r8UkBE#}$zZZYU*BHE7g zqIYdd_Ii3AIHyd5^4s;*s6(B_!umZ&J5On~Km>PrfA1bv7yV=0LSHYWXu)7uq1XM3ZGJN9}vzV|v~x~<^Kx;EkT!`>`u z&h;pTtDm(hp9yRc27N=O{8A(g*W_+$Wb~O2yPy6R-n+%Cp-oGw70Yg}P&?yndUFP9 zJZN{$Vc0mzv<$KQ=vgH1!?N-Z9S&#?G1NvkXQ!f*ayH#zzHdR)CRv>&J10^V z8@7T6DG7P?jZu^17Cm1~a2v;)@3*VB9GE4-7&&6Nrt@PsDg8IqMv7_a65Xb^@9gP$ zL5&Ufn!DO_Ev7CBOm&gh7k*j1E%H^r1zp`*Bvja&f=@T7lb@b#SIV4YSMudUm|*iy z^D=+w73L=UGit)9)$@LGt2sgFTOY{*vHJoG_27cdL&Y7NyKI#U*OB%AFz!g5G#dD9 z)4#~Jn<7kunk?HWhqPMtd~h6b!ZgP6B>ACgNFYv>;LXr0T)3m{yM9=i1)hR z?de!najKzy^(lFe;dZvQOpQ*Q1OondvH#QU|6~5H`G2ebcH4hT%l|wJe|xW~5z8MI Q066()!~Wcn)BlJ51y#WA{{R30 literal 0 HcmV?d00001 diff --git a/app/src/test/resources/settings/nodb_ser_nojson.zip b/app/src/test/resources/settings/nodb_ser_nojson.zip new file mode 100644 index 0000000000000000000000000000000000000000..437d30d8bb8b73e9257a4b58a4f3556f246568cc GIT binary patch literal 1789 zcmVaA|O5E^}pcbZKs9 zb9GleY#c=xo!GI-uj4p&oH&?-h#(4dHgQCv2q}alRxIHJVOJoS%G+*rC)vb0I}m9Ru8Y8Fv%&VemXWrlqp+J^d7aU} zJDq*_eIGx!9DC6QB9h!`ZtrHoNUmbh#P5VB+Np*Lgi|`P{aquGG;42Y9Z8Y=a>m^M z?W=$PnqYh0VN-1pWwM(x$Cj{T7E7)ZF0xQ*-q1m2c~hw5F8he>b!6`9kHox&A5oKA+=&Zxl2kOw+(GI7Os&jvV?;de8cNt$XX z6Z^wgV$VgwIvz&c^-{^(DwO&ugQrrRW+`@yWn7nrTeT~Bsx1Yx6m4>kV>5KPV#mQI zOa83dsHRE;qAEZkn!0+$L^WHEyB4B5b|4gHr+hh?Mxs|2!49`OD#f;uOoD3k#Yn}9 z(5YjJhr$V-SpehMLOwGXLJAX%pOOh7$_J61aFR#LI)9TH$BzGhNuI%CcBw&I8Hw2d zhfEv~TEZZ8(m1w<1BWaPDWpi#D%hcHh%9mxOh*}|!m;U#=U3BLR~ukZ`eU2@AkgG9pp*C?tir6}KVszeTcKnox>KT2t zG&)thjQ`F7g&j;SjtN=UnH&2NMvSv1r-!Bpf1j7GW5+_>NvM=9k3|9A^nM;5 zp&@>w3%VYAx^*s(eJjrKoLpOl4xX&~Ecg}S+`F=phcl3C1Hp%TmX~d0QDg&DR@K{1 z_aHb~eaIoH0(h?NCBSoijZYP!Pkv_Dk%76dqb%H`CqIAl`)hyy32R?3t=+KM!Kzys z+2W@UA1ojI;*ZZKNTRQxrGRV|W|0Wk9{Gfokiq$DH?O|Ebz_@v-rm~gTW{UCwsG}M zB)~G`UE3KQfqQLKbzB*kFAS<9=*%re94i4jLIBwXXE+_^^1+PQFk;e+iFXa<1qf(-34`o}ypX;#3om_dOv6TFw zhG_$$*_3AP@z>w}^YLewfa*qxDh1wVZ1ORLxYYGhj4MJs=kuSNgtm&vQe6LNUM%R| zBIODU=7-1V&>4a(6et!{9fol}RtXx2UEjCKE;h#Dkk*8^3Jq3?vd=;RI1uC{t zTF!DVjiItQFQ_QHflPf_RN$P&W8V3Ad1}hC$n$&|e5X^e9KA--$WeZyj_6r3u%aFu zn<~1egHg4y8rJl@6&=#_MHQzISgi5?`OYc#IXm9xQlm+Bjji?lqvCbt(~1!tcpW^h zkU~7kIEzU0@VJVfiVwCk?R0GytYq`0`Q`r8(A2@ALCjw}vW^^1gLX(?382TmPwPG@ zmVHl~5%4*b)J57pIce1$XxwyV&$3m|_uQYq#ur3YGHgn6R<^)fdBrbN17An$jh(#j z4!H^aiSg8Va`BR-I7%cNW5i&t?o}d@u)sN4u!J=l2b_pX~kzP)h*< z6aW+e000O8O^JS3j?uFWdj$g-o literal 0 HcmV?d00001 diff --git a/app/src/test/resources/settings/nodb_vulnser_json.zip b/app/src/test/resources/settings/nodb_vulnser_json.zip new file mode 100644 index 0000000000000000000000000000000000000000..84f97aac0cfd73aad44a0bfdb47e0525e5cd55d6 GIT binary patch literal 2734 zcmZ{mc{CIZ7sjXT+Yo~=MMjJ*YZyCOUW_vKeNxSkosoSAO_ov!ktNJnlk8*3nn)N# zmSjo7SO$?H+o!(&-t&F;o^zjb&vTxCf9EkXp`ir<&H@+#PfBiE4~Wo+w9^6r7$yLK z0{{Scqk??z`l7C2PyqpVyO)A@q&BcO_{D*S%!(b7o&JtFt@-=2pHipx&jF7$BnHhSjC3SuD3!E!(vr zt{}953)GByF$kNvDQUTZ$r9$sgZTB~QIr4!7Q z%xOA{J?vc{#NxR5VtM*5b*l{H-YL5ATs8>ltzwr+N1WPQaks-~Ok*mqqiieU|A{o;zbch^}~F$a6HN`4u+9_k{Tu)@s# zZutNy{8ZLJB68-0V}JSt@~Fx!skX!kR0tM{I~2R=U?z1}k}LTBuLLsBSe4gj8E&+7 z2=F8J%D649qx19Lq=i1OTt^~Q`PEeC9DbQPM9FmjOklX69}EVQa* zfwPgVE%=Cq+!BVLN zWwhO%U9PuK_xkv@ISl-AM?8;A)eGu$}8`mqZrf% z_{PZOMHpE%maWZ1MMv$y&TPS4%|fd1XNEL;Y7+eh$8h8cr=1y=agyMe>@L4FI=@kf z`nXbfOl#qrsyD3~Fdcqzcaulwyfq;~GOu{plK(O^iH37^bn1g)=Ur~&=Dll6IM*O) z!%O)(YW7rIAtP_t+xo->wX?v4Wb9-zoSWr)y+fSXs@qC&MaQ8ZQxELx?023u07;?tV$EmIvvt zg1jn}<@Jdj?i0=Uo8!J5Efo-PgqlEaH`(3mpU^F7gz$`Ktq&&Lgu&n;H1*upXmZ1N z0{F08YTj+BX|w}n;-&9Qd-P8MCV%3QW)II^}?jqCFRoyzoXc!q`^3zvp!e|BLkmf{uY=eM2i}xJL>`LoM zB)+Mi&XPvPT<~COOyQ2+HSa?MBT~F3-r7zM;v8mEij1`yKS!4P7^tsray?5k z@w>+$sab)XFHaAc^)vW7%r9oe@j6`FxxlbV$p$7sHx2NwM!~Xm34v?$0cPOMspn{U z-hyq%AU>eQaifsI7pM6g$IQ%uZxzdTzTPgTMq)$kns;^Wf8<#6ZW>*@PgzGoca&p< z91ElZCPO$LXlb;Co`m^O_FrjvBNR+}t_La7H)M9WELZbEolol5yJIQCmzP_~+8IRc zIgz2QY+3gWUF|3H!}+g7+gNz`oh?3Pc)Wu;*UCD-6jU+6%YnIjua}Glexanx`Y#WI-6bXK?1pTTsBub2Xx!d$nz%~1e#4LA zA!b3F8#0S8eO^*>{i8y&{gh%nMN{U$n+3w}CFIbA!iJy~sh*g!1w1L1Tl94cYZ$tx z)wmCmQ%vsxnMQBHYB+>|j6#HIV7#R4icz=9hp%6vT4VA7zz6mIbKc<|-`+Mbz^SAX z%`BTKxEH35wt)34mVmlYurue+_+ztOl#k0j;B#g~PP=NUJ@nFTB#QUgQ?x!qfcBG9!GO3WWa zd*l%)Nl|YmZ*zSrKc~G;0}!YDA+}$6W2Plnj-s8+655|8&Ps{CqWD>>JdbxfdDt&_ z+CLFuv@Uf`BZXu!>dx{lqJNu9L%ZWqm0)^fnHsx`*|m9j_^<<~(cg!)j08OCD~t*Z96I({fsjclte^ zviRGwE8kDuRDNxHDY?wiOo*jU9slB7n?mXWlY$?&@%4~w7B1S^elQS^*{BS!fAhm% zYAquWZ90Gt!0q!c)(GWp9m(w4-DD_VR7TW{ZCHt(H5vMBH@L(=O$2wyUnkos1;4fF z?Qww^0Q-woLe8d_jIjav#OzflN5X=vAa8` zv^;ZGi{}5g!_$KJcL-5`>B)|j!0CNcd01yBG0B&V>aA|O5E^}pcbZKs9 zbB$G9XdG1(KKq*{jqRohR*BKXl+={En;(B#7SuE*ab%N9x5koK@b1jb?v%;Q^xiw0 zSp`MV_Mr+2*0=sVD6|;0FMY8OMa)aJ`1hiSLNyTeK}1vvg2i+1%@OUlx;XRZyQd#q8vHQ^ z(HtaQ!l*3-^yW}K6RKzG>Y2QHkb*_CIcRp7BOFn2=}~wQBEE}u(+%tj$FX>tm#Af> z$LzAn9D5#3{gb$SDf{i~&qN`TgAP%mPgNl`n+sy=RKP$ZXB1{eVP;lgX8p$D*ZmK| z-#PZ-Id{?jCQ1hkA==;tWWOB9iN8l<4U>6O2h9(eov zd-|ggn}L*$BGWEve9V^lF*N8XQkyavw9?5_W)cg-sV76l8iu3%aedcah|4!$iksYp z2s)NhL0ZHvrh^Q44BBUNb!lEOG@X6_>*=3=?RsG%is}jv!|3~8B9AaeDu3Sp5c%Ez zBa5+UkhG{>6r~&_i2NsDTTm=%D-(Et^@`~h*0F8XFBN81c@D~bF4tHDTwa&SkmSp4 zo?N#mH>=yi;G^jG&;}W}V}c4nS>AHQrGL-&^6oAxs(P`gAicS;1c$| z$}idFSN&5_i07e&2+VcC6O?PP*|ZJn?`M*EIf&b2o`&`kWl8hEoUBCTwc`R}bIyS5 z@$LrJC+U2FvVD$!=#kEquqXE!9$Fq%b&(?+M*j@DA3)jvc)-d)OEoPy)Aoq;0BGy0T;W};vI}Vmi z9`^7SK^Rt~iYuU}(Nr2GD3AjZ>(txGN5W|pO1t<&=Y?l?Ek_}tK|5OUS(TrLH!Ay65o&m2YBU zizimoKB!dF2bJOp0ZAP*LD?+`^EA?L&a|oV(y7y5T;KKC<)GdhIlU&SC^^QLBU3ln z4HHzyw9bX8nL-|~?fG!N%tF(=(!;v<&NeDwjmE(w=&J_^e)zyUDPNMe49-02(SxM8 z)RObIUs*YcXPgEJL5i9Y^pq=L*!2pQsdwX9C-6~rOrv|%SgdeC=hM_T#eJqN`ez4+ zhaWzS!!vOHmOYpq>g$N$@nqNLx2~NC!4iAdkLG~1Bg$7-y>EB@;8&2KI{~g<`MU!TEyokC zC&ErKDG#=HEJV~f(Bb;(z;j*yKTt~n0zU&300ICA0FJ0*R?Q>>B)|j!0CNcd01yBG z00000000000HlEc0001PWp{9CaAhuYWps3DZfA2)O9ci1000010096#0002)1ONa4 E01*6)NdN!< literal 0 HcmV?d00001 diff --git a/app/src/test/resources/settings/vulnerable_serialization.zip b/app/src/test/resources/settings/vulnerable_serialization.zip deleted file mode 100644 index d57a5f8d0150cd11dec35d7c29ed2bcb9e65a774..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3536 zcmZ`+XHb*d)(#yhDpd?dLKO(Th$2W8L3-~>umhOGllAlo0>`Py*abY>n3H!!d^x1XY9> zKt&K94gp@yUJgR`w!depA9d<5lFz4)>zE6Cwx-L-&Ghs7PO(j7@yJ`~0mnvm#O)a2 z`Glju#p3xzO@3G-B}eksQpU^+c6&LjT4BPvgCTQ3***@=FVrFO#cZLJSCR5FdTD!u z_h4w^?2bQkGeZbcW(ixT{Ydzo|SAS^O!U?L2Jw5Kj;v?jOsY zYuaa52x3+Ig6ss2|6AMsh5#V}fHo1z-7O$n{?qF1-Ct+t3}<^U3G2})o^by8;WNVR zhRU5@II!m|ZCMwTFAvW7v5oi(KkmZ+`y$czqR7jW3;!yf5Vc)&o>=E9I4q=O1J_M! zYCx4;76RJIELx)MSPDHl6U&ma~fu`(A%b&q?J|teYAl11FlAcgR)2?bt0h5J;CLrBt|wE;6Chqy+!Dt0I4n zZMHU8d~SQ1Bx=vd5{3>oa8#i)V zMDS@FrS71*x_Ao5^^*GeQ4&_k_AHLxxq8x8#TgE9-cBtDwC2FVH!5Rky~|Hhr~*>T zyfEII2ExG4^@#C$zXS)zHRbO#0dOxDxXw=vwfOXT8TxV6xRLBGy>68fz7F&8D~K0w zAs-7NYwkZ9*@d!3HC0M1eh!J$t&~XaE=yX(6usMUOVK2O7Hk$|0%7$>Pb5qzjPJte z62FQ&AiYO_UY-6O&rGi2@Sr{>uf z3NzNk`9qZX>J1f=6CIB%&1fH7&6!GPMr~_>mvNO}Ffp16F4iNg^zc>AVrOd4Rd*s& zvp4~uw75v>y$2)kaX3=ZeGSUBv``zsAi?s`l4G5liV<~^bhD&M$s(o8T)9goo#&t~ zfIXnD+}jH}c0>AwVD$W(qsZ<{411==Is4C^36YO>9{l9mn@0BlA&w*SKc)Lp_Li%H zb<4{1`e}MveMBrd{1#~aZGVlqe)pvN6nU$yI~HXMY4v}ofT(D&soi87>Ja;_U*Sn5 zHuGA=jbb3Xz3D0Hi`#KmSR1+cpXldLTc73Rxr$pjfoHDzwKtz-JP}fqg#A!nAzeGs z_gnpF&~TjxV%rOOBdf{XwmfJs+ZTgmG$b=rf71lcoj&dH7PuE8Sc}L(MUa2-7#>QM z6|WaL;DP=SJv3ZXUt3+7{*LWEn;dp3TCv>;UVO?SyRM6!DQ^+q)Mv6kXqfkhlAAZ^ zb@M}PN^^VR0#Bs*)nL|4(3;RRQ{gC^pee4Xs;Cp3`&o3hWwrR!Wc>&{z|+|0l486k z_;Imo+J+JhB}>Ktkt~Nq_jbsQ{Q+;!|5VL3;!d7h?GXesojDB>bx|>iK(A8U zJ*n){y>ehG6PdsGP*oIej+o@F-h@g%JUUtf!rI?6RjMrMD=c_;e#phLR5Cd7Gtj;O=Jxi5K!V9?Rh)Q5icXF)%Q<*tv6kU6NkgmUS@>@E=1#GW z5wgoj2{tQ#ix}@eSxKHVy_sV%DX@Eee>#X2#ew3{DW5Dh8T^X5Ce&@pCR+Pa9txH8 z5Xc|2T=g4YY?(vK4b88&7oAC-`ny%$Kl<=4>r19mvnSgyaDW)7f#I{8(M0TmXNQ6Q zWdTMODgJa;Q8qJZ?n||Qf?372hKgQ&ERxP6QS^KLBu%i&tyAM#V56)CdZB_dt35v8 z+h%I_&D3PTuibVs5S@aMMw4|@TA{*yZP`1HbW#I68n@@v=NCSKUnh`Q0X9jl{Knz* zF}Fiz#E?OtL2Wm9>v};^9*XCkf%8RkG&CCe!z%-;z%Xj8$td#YOnW zcR$|=2MxK~9P~&v_||3fQ){vue7nBxMfcjSm00Kho(FNoc1D9-BwUrG0D$i$0N^U& zNA%b8z}Lag&)LJtH`efBFM|SOi1WdYd6l&=8rfw2#8+H-Lh>3$eb5$?$ zaFCHIG$XdqA^xYd9*>1nA><=Bx^3Cxndv%DlJm=|H#} zTuncf5(E`49@GqKr8i5FQIC2MOCv!y1e)av1NKK^bcM2mRXPe~Y|KiIyS{q)QRXI( zfuxjss#GmEI`0?`+I-;%{j&zv{YcM!H+h?bgF8*G$)~PA?L|ubB(l#ZQZ#*~!v^?; z@qxxZKc$IPbmNv- zeIm9I*6lPYcK;1|CN-*1BiiV`X`hB|PDY?e; zpZ&UWn;nubxx~CSY~lN=_+fHO;Hc|p25%EYCQ)=HD?%n#RPR{CR_6JyR_#1y+B8#? zP^2w6y)uFUDXI#UfiQ`*1%36S-bF9GHGbr1CQ9)jt6!3t$aq#>^CR4O{aamTzUee` z^m~r2?|9Q!B)@dndHc=cV0-&N0Y4g&nG9P@bo`@ch_asHF7>}S!iTA&^^+!6uTA5W zbR(T|nCMv6;kS4vvg@2#QIC7=F%Dpl1bW(G^~CCdaEhd;-px#6Ti49^nx50!cY^N= zV))WGk03mU&nACAz$$7idn*}0<-?|WTL-cO@@o9N!iBR>s4cu{ZRcodIhSscc1=j< zF~+>24K2L)$$j9!%`zo!Hs(MMzxUIHwr--2gFFrj~P5<4RzQdQnEzA14e(#z`nS<=+ zG;~0tj_xdJWF|dwhe3B9`yk=@SdlFfpICYm$dWZZP3a|Np9M1fpuEsaCZ~$S6?#3` zIRDbqma`o|$dZIAr5^HKc98{L0xSv-8RxWCZ|!? zu3N)oLsaeTv24ms+2XmdWE``0>E+SLKCR(w&=NgEbM@*(XUK0kK52TjV%~S<%lconx2lu*~evpwDlPE0yHj>RTF&Z#XY1eFkbD8TSh^4((R-X^$W|_jcd2nKBow=EXu%VMQ>O{nHfd4i9a1k2ZIYKYiznh)CEfoNKVIu+%#tzpW zekIMx^V#HI_$T-OZ=HW}g9$lolz(&o)kyzY;d4F2oLcL@RUjijFG)=J5Cn;(IoJLJ D$x)Jd From 7da1d3001037ec91baf7a99980deeadf7d0f1c9e Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Mar 2024 18:47:20 +0100 Subject: [PATCH 106/123] Expose all import/export errors to the user --- .../settings/export/ImportExportManager.kt | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 5558a1b37..85370449a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -27,38 +27,34 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { fun exportDatabase(preferences: SharedPreferences, file: StoredFileHelper) { file.create() ZipOutputStream(SharpOutputStream(file.stream).buffered()).use { outZip -> - try { - // add the database - ZipHelper.addFileToZip( - outZip, - BackupFileLocator.FILE_NAME_DB, - fileLocator.db.path, - ) + // add the database + ZipHelper.addFileToZip( + outZip, + BackupFileLocator.FILE_NAME_DB, + fileLocator.db.path, + ) - // add the legacy vulnerable serialized preferences (will be removed in the future) - ZipHelper.addFileToZip( - outZip, - BackupFileLocator.FILE_NAME_SERIALIZED_PREFS - ) { byteOutput -> - ObjectOutputStream(byteOutput).use { output -> - output.writeObject(preferences.all) - output.flush() - } + // add the legacy vulnerable serialized preferences (will be removed in the future) + ZipHelper.addFileToZip( + outZip, + BackupFileLocator.FILE_NAME_SERIALIZED_PREFS + ) { byteOutput -> + ObjectOutputStream(byteOutput).use { output -> + output.writeObject(preferences.all) + output.flush() } + } - // add the JSON preferences - ZipHelper.addFileToZip( - outZip, - BackupFileLocator.FILE_NAME_JSON_PREFS - ) { byteOutput -> - JsonWriter - .indent("") - .on(byteOutput) - .`object`(preferences.all) - .done() - } - } catch (e: Exception) { - Log.e(TAG, "Unable to export serialized settings", e) + // add the JSON preferences + ZipHelper.addFileToZip( + outZip, + BackupFileLocator.FILE_NAME_JSON_PREFS + ) { byteOutput -> + JsonWriter + .indent("") + .on(byteOutput) + .`object`(preferences.all) + .done() } } } @@ -133,7 +129,7 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { } if (!editor.commit()) { - Log.e(TAG, "Unable to loadSerializedPrefs") + throw IOException("Unable to commit loadSerializedPrefs") } } }.let { fileExists -> @@ -168,7 +164,7 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { } if (!editor.commit()) { - Log.e(TAG, "Unable to loadJsonPrefs") + throw IOException("Unable to commit loadJsonPrefs") } }.let { fileExists -> if (!fileExists) { From 2756ef6d2fd998cf30b689db3a60fdc32ccf3c3b Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Mar 2024 18:53:45 +0100 Subject: [PATCH 107/123] Show notification when failing to import settings --- .../newpipe/settings/BackupRestoreSettingsFragment.java | 8 +++++++- .../schabi/newpipe/settings/export/ImportExportManager.kt | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java index f4080acd3..20af8c150 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -217,7 +217,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { manager.loadSerializedPrefs(file, prefs); } } catch (IOException | ClassNotFoundException | JsonParserException e) { - showErrorSnackbar(e, "Importing preferences"); + createErrorNotification(e, "Importing preferences"); return; } cleanImport(context, prefs); @@ -290,4 +290,10 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { private void showErrorSnackbar(final Throwable e, final String request) { ErrorUtil.showSnackbar(this, new ErrorInfo(e, UserAction.DATABASE_IMPORT_EXPORT, request)); } + private void createErrorNotification(final Throwable e, final String request) { + ErrorUtil.createNotification( + requireContext(), + new ErrorInfo(e, UserAction.DATABASE_IMPORT_EXPORT, request) + ); + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 85370449a..843806b80 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -1,7 +1,6 @@ package org.schabi.newpipe.settings.export import android.content.SharedPreferences -import android.util.Log import com.grack.nanojson.JsonArray import com.grack.nanojson.JsonParser import com.grack.nanojson.JsonParserException From 0b28c688c69a1ccc264c91fc0bc8cd5e6baf46e1 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Mon, 1 Apr 2024 13:38:40 +0200 Subject: [PATCH 108/123] Translated using Weblate (Estonian) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Hungarian) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Odia) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Vietnamese) Currently translated at 46.7% (36 of 77 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Czech) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Spanish) Currently translated at 99.7% (736 of 738 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Polish) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (German) Currently translated at 99.7% (736 of 738 strings) Translated using Weblate (Korean) Currently translated at 31.1% (24 of 77 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Korean) Currently translated at 98.9% (726 of 734 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Polish) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Czech) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Italian) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Polish) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (734 of 734 strings) Translated using Weblate (German) Currently translated at 99.7% (732 of 734 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Belarusian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Romanian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Russian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Slovak) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (German) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Hungarian) Currently translated at 16.8% (13 of 77 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Polish) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Czech) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Italian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Hungarian) Currently translated at 100.0% (730 of 730 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (730 of 730 strings) Co-authored-by: Agnieszka C Co-authored-by: Alex25820 Co-authored-by: Apious Co-authored-by: Eduardo Malaspina Co-authored-by: Fjuro Co-authored-by: GET100PERCENT Co-authored-by: Ghost of Sparta Co-authored-by: Hosted Weblate Co-authored-by: Ihor Hordiichuk Co-authored-by: Jeff Huang Co-authored-by: Linerly Co-authored-by: Michael Moroni Co-authored-by: Milan Co-authored-by: NEXI Co-authored-by: Oğuz Ersen Co-authored-by: Philip Goto Co-authored-by: Pi-Cla Co-authored-by: Priit Jõerüüt Co-authored-by: Rex_sa Co-authored-by: Tim Trek Co-authored-by: TobiGr Co-authored-by: Tấn Lực Trương Co-authored-by: gallegonovato Co-authored-by: kuragehime Co-authored-by: rehork Co-authored-by: Макар Разин Co-authored-by: 大王叫我来巡山 Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hu/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ko/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/vi/ Translation: NewPipe/Metadata --- app/src/main/res/values-ar/strings.xml | 11 ++ app/src/main/res/values-be/strings.xml | 1 + app/src/main/res/values-cs/strings.xml | 11 ++ app/src/main/res/values-de/strings.xml | 6 + app/src/main/res/values-es/strings.xml | 11 ++ app/src/main/res/values-et/strings.xml | 11 ++ app/src/main/res/values-hu/strings.xml | 11 ++ app/src/main/res/values-in/strings.xml | 11 ++ app/src/main/res/values-it/strings.xml | 7 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 5 +- app/src/main/res/values-nl/strings.xml | 11 ++ app/src/main/res/values-or/strings.xml | 11 ++ app/src/main/res/values-pl/strings.xml | 11 ++ app/src/main/res/values-ro/strings.xml | 19 +- app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-sk/strings.xml | 13 +- app/src/main/res/values-sr/strings.xml | 7 + app/src/main/res/values-sv/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 15 +- app/src/main/res/values-uk/strings.xml | 3 +- app/src/main/res/values-vi/strings.xml | 177 ++++++++++-------- app/src/main/res/values-zh-rCN/strings.xml | 11 ++ app/src/main/res/values-zh-rHK/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 11 ++ .../metadata/android/hu/short_description.txt | 2 +- .../metadata/android/ko/changelogs/65.txt | 27 +++ .../metadata/android/ko/changelogs/66.txt | 29 +++ .../metadata/android/ko/changelogs/68.txt | 31 +++ .../metadata/android/ko/changelogs/69.txt | 19 ++ .../metadata/android/ko/changelogs/70.txt | 25 +++ .../metadata/android/ko/changelogs/71.txt | 10 + .../metadata/android/ko/changelogs/730.txt | 2 + .../metadata/android/ko/changelogs/740.txt | 23 +++ .../metadata/android/ko/changelogs/750.txt | 22 +++ .../metadata/android/ko/changelogs/760.txt | 43 +++++ .../metadata/android/ko/changelogs/770.txt | 4 + .../metadata/android/ko/changelogs/780.txt | 12 ++ .../metadata/android/ko/changelogs/790.txt | 14 ++ .../metadata/android/ko/changelogs/800.txt | 27 +++ .../metadata/android/ko/changelogs/810.txt | 19 ++ .../metadata/android/vi/changelogs/65.txt | 32 +++- .../metadata/android/vi/changelogs/66.txt | 33 ++++ .../metadata/android/vi/changelogs/68.txt | 31 +++ .../metadata/android/vi/changelogs/69.txt | 19 ++ .../metadata/android/vi/changelogs/820.txt | 2 +- 46 files changed, 697 insertions(+), 107 deletions(-) create mode 100644 fastlane/metadata/android/ko/changelogs/65.txt create mode 100644 fastlane/metadata/android/ko/changelogs/66.txt create mode 100644 fastlane/metadata/android/ko/changelogs/68.txt create mode 100644 fastlane/metadata/android/ko/changelogs/69.txt create mode 100644 fastlane/metadata/android/ko/changelogs/70.txt create mode 100644 fastlane/metadata/android/ko/changelogs/71.txt create mode 100644 fastlane/metadata/android/ko/changelogs/730.txt create mode 100644 fastlane/metadata/android/ko/changelogs/740.txt create mode 100644 fastlane/metadata/android/ko/changelogs/750.txt create mode 100644 fastlane/metadata/android/ko/changelogs/760.txt create mode 100644 fastlane/metadata/android/ko/changelogs/770.txt create mode 100644 fastlane/metadata/android/ko/changelogs/780.txt create mode 100644 fastlane/metadata/android/ko/changelogs/790.txt create mode 100644 fastlane/metadata/android/ko/changelogs/800.txt create mode 100644 fastlane/metadata/android/ko/changelogs/810.txt create mode 100644 fastlane/metadata/android/vi/changelogs/66.txt create mode 100644 fastlane/metadata/android/vi/changelogs/68.txt create mode 100644 fastlane/metadata/android/vi/changelogs/69.txt diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index eefe24390..82173d758 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -869,4 +869,15 @@ عرض المزيد عرض أقل قم بتحرير كل إجراء إعلام أدناه من خلال النقر عليه. يتم تعيين الإجراءات الثلاثة الأولى (تشغيل/إيقاف مؤقت، السابق والتالي) بواسطة النظام ولا يمكن تخصيصها. + لا توجد مساحة خالية كافية على الجهاز + اعادة ضبط الإعداداتِ + النسخ الاحتياطيُّ والاستعادة + أعيدوا جميع الإعدادات إلى قيمهم الافتراضية + ستؤدي إعادة ضبط جميع الإعدادات إلى تجاهل جميع إعداداتك المفضلة وإعادة تشغيل التطبيق. +\n +\nهل انت متأكد انك تريد المتابعة؟ + نعم + يمكن لـ NewPipe البحث تلقائيًا عن الإصدارات الجديدة من وقت لآخر وإعلامك بمجرد توفرها. +\nهل تريد تمكين هذا؟ + لا \ No newline at end of file diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 5990738cf..dceaaf6c5 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -839,4 +839,5 @@ Паказаць больш Паказаць менш Адрэдагуйце кожнае дзеянне апавяшчэння, націснуўшы на яго. Першыя тры дзеянні (прайграванне/паўза, папярэдняе і наступнае) задаюцца сістэмай і не могуць быць зменены. + Недастаткова вольнага месца на прыладзе \ 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 0a4751261..34537446c 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -827,4 +827,15 @@ 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-de/strings.xml b/app/src/main/res/values-de/strings.xml index e94c8f757..3a308fb25 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -813,4 +813,10 @@ 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-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3a8681e44..ce0b32603 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -830,4 +830,15 @@ 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 5864d5519..1d3fcdcb8 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -813,4 +813,15 @@ 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-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5ca829ed8..5a402c94c 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -813,4 +813,15 @@ 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-in/strings.xml b/app/src/main/res/values-in/strings.xml index d715f2232..71900400e 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -799,4 +799,15 @@ %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-it/strings.xml b/app/src/main/res/values-it/strings.xml index 5ddb6c672..35a722a57 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -827,4 +827,11 @@ 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 afcf34429..6cbc3c084 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -799,4 +799,5 @@ もっと見る 表示を少なくする 以下の通知アクションをタップして編集します。 最初の3つのアクション (再生/一時停止、前へ、次へ)はシステムによって設定されており、カスタマイズすることはできません。 + デバイスの空き容量が不足しています \ 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 d17e7599d..939d97441 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -505,8 +505,8 @@ 설명 표시하기 한 플레이어에서 다른 플레이어로 전환하면 대기열이 대체될 수 있습니다 어두운 테마 - 최대 3개까지 축소 알림에 표시될 항목을 고를 수 있습니다! - 아래의 각 알림 작업을 눌러 편집하세요. 오른쪽에 있는 확인란을 사용하여 압축 알림에 표시할 항목을 최대 3개까지 선택 + 축소된 알림에서 최대 3개까지 표시될 항목을 고를 수 있습니다! + 아래의 각 알림 작업을 눌러 편집하세요 오른쪽에 있는 확인란을 사용하여 압축 알림에 표시할 항목을 최대 3개까지 선택 YouTube의 \'제한 모드\' 켜기 비디오 해싱 진행 알림 새로운 스트림 @@ -793,4 +793,5 @@ 채널 이전 스트림 실시간 + 아래의 각 알림 작업을 탭하여 편집하세요. 처음 세 가지 작업(재생/일시 중지, 이전 및 다음)은 시스템에 의해 설정되며 사용자 정의할 수 없습니다. \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 9b690f770..2a5206699 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -813,4 +813,15 @@ 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-or/strings.xml b/app/src/main/res/values-or/strings.xml index e6daac644..f9faa8324 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -813,4 +813,15 @@ %s ଉତ୍ତରଗୁଡ଼ିକ କମ୍ ଦର୍ଶାନ୍ତୁ + ଉପକରଣରେ ପର୍ଯ୍ୟାପ୍ତ ଖାଲି ସ୍ଥାନ ନାହିଁ + ବ୍ୟାକଅପ୍ ଏବଂ ପୁନରୁଦ୍ଧାର କରନ୍ତୁ + ହଁ + ନା + NewPipe ସ୍ୱୟଂଚାଳିତ ଭାବରେ ସମୟ ସମୟରେ ନୂତନ ସଂସ୍କରଣଗୁଡିକ ଯାଞ୍ଚ କରିପାରିବ ଏବଂ ଥରେ ଉପଲବ୍ଧ ହେବା ପରେ ଆପଣଙ୍କୁ ସୂଚିତ କରିପାରିବ । +\nଆପଣ ଏହାକୁ ସକ୍ଷମ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? + ସେଟିଂସମୂହ ପୁନଃସେଟ୍ କରନ୍ତୁ + ସମସ୍ତ ସେଟିଂସମୂହକୁ ସେମାନଙ୍କର ଡିଫଲ୍ଟ ମୂଲ୍ୟରେ ପୁନଃସେଟ୍ କରନ୍ତୁ + ସମସ୍ତ ସେଟିଂସମୂହକୁ ପୁନଃ ସେଟ୍ କରିବା ଦ୍ବାରା ଆପଣଙ୍କର ସମସ୍ତ ପସନ୍ଦିତ ସେଟିଂସମୂହ ପରିତ୍ୟାଗ ହେବ ଏବଂ ଆପ୍ ପୁନଃ ସେଟ୍ଆରମ୍ଭ ହେବ । +\n +\nଆପଣ ଆଗକୁ ବଢିବାକୁ ଚାହୁଁଛନ୍ତି କି? \ 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 6fd61b80e..01f242cd9 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -836,4 +836,15 @@ 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-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index ccc25dc4b..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! @@ -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ă @@ -828,4 +828,5 @@ 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 a885631eb..df566853b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -832,4 +832,5 @@ %s ответов Отредактируйте каждое действие уведомления ниже, нажав на него. Первые три действия (воспроизведение/пауза, предыдущее и следующее) задаются системой и не подлежат настройке. + Недостаточно свободного места на устройстве \ 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 443809526..e3aa5b6bc 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -398,7 +398,7 @@ 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\? @@ -827,4 +827,15 @@ - %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-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 596fbe263..996b3fa2e 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -827,4 +827,11 @@ Прикажи више Прикажи мање Измените сваку радњу обавештења у наставку тако што ћете је додирнути. Прве три радње (пусти/паузирај, претходни и следећи) поставља систем и не могу се прилагодити. + Нема довољно слободног меморијског простора на уређају + Прављење резервне копије и враћање + Ресетуј подешавања + Ресетујте сва подешавања на подразумеване вредности + Ресетовање свих подешавања ће одбацити сва жељена подешавања и поново покренути апликацију. +\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 a5c9aaf2b..54aab0cdf 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -813,4 +813,5 @@ 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-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index fd12b6a35..adecbf3ff 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -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\? @@ -813,4 +813,15 @@ %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 1227d7db4..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 був закритий під час роботи над файлом - На пристрої не залишилося вільного місця + На пристрої не залишилося вільного простору Прогрес втрачено через видалення файлу Час очікування з\'єднання вичерпано Очистити історію завантажень чи завантажені файли\? @@ -832,4 +832,5 @@ %s відповідей Показати менше + Недостатньо вільного простору на пристрої \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 393d3cdba..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,13 +385,13 @@ 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 + 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 @@ -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 @@ -539,7 +539,7 @@ 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,7 +550,7 @@ Đã 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 + 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 @@ -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 @@ -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,7 +787,7 @@ 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 @@ -799,4 +799,15 @@ %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 29b1fc302..fa8d31022 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -799,4 +799,15 @@ 显示较多 显示较少 轻按下方的每个通知操作进行编辑。头三个动作(播放/暂停、上一个和下一个)是系统设置的,不能自定义。 + 设备剩余空间不足 + 备份和还原 + 重置设置 + 将所有设置重置为默认值 + 重置所有设置会取消你所有的偏好设置并重启应用。 +\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 f293773bd..a59da9c4e 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -799,4 +799,5 @@ 摺埋 拉開 撳下面嘅掣去更改對應嘅通知動作。頭三個動作 (播放/暫停、上一個、下一個) 系統預設咗,冇得揀。 + 部機冇晒位 \ 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 cb9279c41..c404edeca 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -799,4 +799,15 @@ 顯示更多 顯示較少 透過點擊下面的每個通知操作來編輯它。前三個動作(播放/暫停、上一個與下一個)由系統設定,無法自訂。 + 裝置上沒有足夠的空間 + 備份與還原 + 重設設定 + 將所有設定重設為預設值 + 重設所有設定將會放棄您的所有偏好設定並重新啟動應用程式。 +\n +\n您確定您想要繼續嗎? + + + NewPipe 可以隨時自動檢查新版本,並在新版本可用時通知您。 +\n您想要啟用此功能嗎? \ No newline at end of file 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/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/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. From c915b6e68b4613d414ce97acc1e58eb2f9a91410 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 1 Apr 2024 14:16:51 +0200 Subject: [PATCH 109/123] Add changelog for v0.27.0 (997) --- .../metadata/android/en-US/changelogs/997.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/997.txt 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 From 7abf0f4886fefc922d0801d079716f48553ca31a Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 1 Apr 2024 14:23:04 +0200 Subject: [PATCH 110/123] Update NewPipeExtractor to YT comments fix PR https://github.com/TeamNewPipe/NewPipeExtractor/pull/1163 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7a3b28661..72cab42de 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,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.Stypox:NewPipeExtractor:aaf3231fc75d7b4177549fec4aa7e672bfe84015' + implementation 'com.github.AudricV:NewPipeExtractor:2eca6d3ae62dde0d0d3933ba01031ec07a1a940e' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From f704721b59200faf35ed358a9dbea7965fc66bb9 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 1 Apr 2024 14:23:48 +0200 Subject: [PATCH 111/123] Release v0.27.0 (997) --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 72cab42de..2c4ea383c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" From f0db2aa43cbfa4d0523e8571042807427c2be2f6 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Thu, 4 Apr 2024 11:38:57 +0200 Subject: [PATCH 112/123] Improve documentation --- .../settings/BackupRestoreSettingsFragment.java | 4 +++- .../newpipe/settings/export/ImportExportManager.kt | 12 +++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java index 20af8c150..97df1549b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -187,6 +187,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { throw new IOException("Could not create databases dir"); } + // replace the current database if (!manager.extractDb(file)) { Toast.makeText(requireContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG) @@ -265,7 +266,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { } /** - * Save import path and restart system. + * Save import path and restart app. * * @param importDataUri The import path to save */ @@ -290,6 +291,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { private void showErrorSnackbar(final Throwable e, final String request) { ErrorUtil.showSnackbar(this, new ErrorInfo(e, UserAction.DATABASE_IMPORT_EXPORT, request)); } + private void createErrorNotification(final Throwable e, final String request) { ErrorUtil.createNotification( requireContext(), diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 843806b80..9a0d842a4 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -67,11 +67,17 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { return fileLocator.dbDir.exists() || fileLocator.dbDir.mkdir() } + /** + * Extracts the database from the given file to the app's database directory. + * The current app's database will be overwritten. + * @param file the .zip file to extract the database from + * @return true if the database was successfully extracted, false otherwise + */ fun extractDb(file: StoredFileHelper): Boolean { val success = ZipHelper.extractFileFromZip( - file, - BackupFileLocator.FILE_NAME_DB, - fileLocator.db.path, + file, + BackupFileLocator.FILE_NAME_DB, + fileLocator.db.path, ) if (success) { From 13baaa31cd94831688df5c15838c7a3703e7306e Mon Sep 17 00:00:00 2001 From: bg1722 Date: Sat, 6 Apr 2024 07:58:05 +0200 Subject: [PATCH 113/123] add an intuitive prefix for the duration of lists on UI, and avoid using the new prefix for single videos --- .../fragments/list/playlist/PlaylistFragment.java | 2 +- .../newpipe/local/playlist/LocalPlaylistFragment.java | 3 ++- .../main/java/org/schabi/newpipe/util/Localization.java | 9 ++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 998ea0624..9afb06344 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -506,7 +506,7 @@ public class PlaylistFragment extends BaseListInfoFragment Date: Sun, 7 Apr 2024 23:44:22 +0200 Subject: [PATCH 114/123] Update NewPipeExtractor --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 2c4ea383c..a4fde4ccc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,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.AudricV:NewPipeExtractor:2eca6d3ae62dde0d0d3933ba01031ec07a1a940e' + implementation 'com.github.AudricV:NewPipeExtractor:f4dcab16004e566f1969bc6f627facf198573fee' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From 3738e309495ae7de404268b20a85b9ebe87cb4d7 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 9 Apr 2024 20:18:21 +0200 Subject: [PATCH 115/123] Fix NPE when avatarUrl is empty --- .../newpipe/database/subscription/SubscriptionEntity.java | 4 +++- .../org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt | 2 +- .../schabi/newpipe/local/subscription/SubscriptionManager.kt | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) 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/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/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 } From 67b41b970da9e6f039b5d877220ef2c557c75b3f Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 10 Apr 2024 10:52:47 +0200 Subject: [PATCH 116/123] Fix not saving comment replies state on config change --- .../fragments/list/comments/CommentRepliesFragment.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index a816b149f..304eaf55a 100644 --- 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 @@ -30,6 +30,7 @@ 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; @@ -38,7 +39,8 @@ public final class CommentRepliesFragment public static final String TAG = CommentRepliesFragment.class.getSimpleName(); - private CommentsInfoItem commentsInfoItem; // the comment to show replies of + @State + CommentsInfoItem commentsInfoItem; // the comment to show replies of private final CompositeDisposable disposables = new CompositeDisposable(); From 00770fc63484b9946a53a1a800fe9fe9add3573b Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 20 Apr 2024 13:11:08 +0200 Subject: [PATCH 117/123] Update NewPipeExtractor --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index a4fde4ccc..1f3d1f759 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,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.AudricV:NewPipeExtractor:f4dcab16004e566f1969bc6f627facf198573fee' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:fbe9e6223aceac8d6f6b352afaed4cb61aed1c79' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From c3c39a7b24af5c3b062999103e90be3e56547850 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 23 Apr 2024 12:16:06 +0200 Subject: [PATCH 118/123] Fix free storage space check for all APIs See https://stackoverflow.com/q/31171838 See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html --- .../newpipe/download/DownloadDialog.java | 25 +++--- .../streams/io/StoredDirectoryHelper.java | 77 +++++++++---------- .../java/us/shandian/giga/util/Utility.java | 14 ---- 3 files changed, 50 insertions(+), 66 deletions(-) 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 bbdb46292..db2066b27 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -859,20 +859,19 @@ public class DownloadDialog extends DialogFragment return; } - // Check for free memory space (for api 24 and up) - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { - final long freeSpace = mainStorage.getFreeMemory(); - 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 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 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 0fe2e0408..8dd819293 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,24 +1,29 @@ 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.Build; -import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; +import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStatVfs; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; 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; @@ -27,16 +32,9 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.UUID; 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; - -import us.shandian.giga.util.Utility; - public class StoredDirectoryHelper { private static final String TAG = StoredDirectoryHelper.class.getSimpleName(); public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -45,6 +43,7 @@ public class StoredDirectoryHelper { private Path ioTree; private DocumentFile docTree; + // will be `null` for non-SAF files, i.e. files that use `ioTree` private Context context; private final String tag; @@ -176,41 +175,41 @@ public class StoredDirectoryHelper { } /** - * Get free memory of the storage partition (root of the directory). - * @return amount of free memory in the volume of current directory (bytes) + * 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 */ - @RequiresApi(api = Build.VERSION_CODES.N) // Necessary for `getStorageVolume()` - public long getFreeMemory() { - final Uri uri = getUri(); - final StorageManager storageManager = (StorageManager) context. - getSystemService(Context.STORAGE_SERVICE); - final List volumes = storageManager.getStorageVolumes(); + public long getFreeStorageSpace() { + try { + final StructStatVfs stat; - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - if (split.length > 0) { - final String volumeId = split[0]; + 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()); - for (final StorageVolume volume : volumes) { - // if the volume is an internal system volume - if (volume.isPrimary() && volumeId.equalsIgnoreCase("primary")) { - return Utility.getSystemFreeMemory(); - } - - // if the volume is a removable volume (normally an SD card) - if (volume.isRemovable() && !volume.isPrimary()) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - try { - final String sdCardUUID = volume.getUuid(); - return storageManager.getAllocatableBytes(UUID.fromString(sdCardUUID)); - } catch (final Exception e) { - // do nothing - } + } 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 IOException | ErrnoException e) { + return Long.MAX_VALUE; } - return Long.MAX_VALUE; } /** 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 c75269757..86a08c57f 100644 --- a/app/src/main/java/us/shandian/giga/util/Utility.java +++ b/app/src/main/java/us/shandian/giga/util/Utility.java @@ -40,20 +40,6 @@ public class Utility { UNKNOWN } - /** - * Get amount of free system's memory. - * @return free memory (bytes) - */ - public static long getSystemFreeMemory() { - try { - final StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); - return statFs.getAvailableBlocksLong() * statFs.getBlockSizeLong(); - } catch (final Exception e) { - // do nothing - } - return -1; - } - public static String formatBytes(long bytes) { Locale locale = Locale.getDefault(); if (bytes < 1024) { From 23a087c498c8c897059195df005ffae1865addf0 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 23 Apr 2024 18:00:52 +0200 Subject: [PATCH 119/123] Translated using Weblate (Romanian) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Croatian) Currently translated at 99.5% (735 of 738 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (French (Louisiana)) Currently translated at 0.2% (2 of 738 strings) Translated using Weblate (Portuguese (Portugal)) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Portuguese (Portugal)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (738 of 738 strings) Added translation using Weblate (French (Louisiana)) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (French) Currently translated at 99.7% (736 of 738 strings) Added translation using Weblate (Arabic (Tunisian)) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (ryu (generated) (ryu)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Kannada) Currently translated at 5.8% (43 of 738 strings) Translated using Weblate (Kannada) Currently translated at 5.1% (4 of 78 strings) Translated using Weblate (Italian) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Russian) Currently translated at 99.8% (737 of 738 strings) Translated using Weblate (German) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (German) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (ryu (generated) (ryu)) Currently translated at 99.0% (731 of 738 strings) Translated using Weblate (Portuguese) Currently translated at 99.7% (736 of 738 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Russian) Currently translated at 99.7% (736 of 738 strings) Translated using Weblate (Dutch) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Vietnamese) Currently translated at 50.0% (39 of 78 strings) Translated using Weblate (Sardinian) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Greek) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Japanese) Currently translated at 99.4% (734 of 738 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Italian) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Punjabi) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Punjabi) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (738 of 738 strings) Translated using Weblate (Slovak) Currently translated at 21.7% (17 of 78 strings) Translated using Weblate (Czech) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 62.8% (49 of 78 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (78 of 78 strings) Translated using Weblate (Polish) Currently translated at 61.5% (48 of 78 strings) Translated using Weblate (Chinese (Traditional, Hong Kong)) Currently translated at 23.0% (18 of 78 strings) Co-authored-by: Agnieszka C Co-authored-by: Ajeje Brazorf Co-authored-by: Alex25820 Co-authored-by: AudricV Co-authored-by: Fjuro Co-authored-by: Flavian <3zorro.1@gmail.com> Co-authored-by: Hosted Weblate Co-authored-by: Ihor Hordiichuk Co-authored-by: Jonatan Nyberg Co-authored-by: Jose Delvani Co-authored-by: Linerly Co-authored-by: MS-PC Co-authored-by: Milan Co-authored-by: Milo Ivir Co-authored-by: NEXI Co-authored-by: Philip Goto Co-authored-by: Random Co-authored-by: Ray Co-authored-by: Rex_sa Co-authored-by: Sergio Marques Co-authored-by: ShareASmile Co-authored-by: Tấn Lực Trương Co-authored-by: Vasilis K Co-authored-by: VfBFan Co-authored-by: abhijithkjg Co-authored-by: gallegonovato Co-authored-by: kaajjo Co-authored-by: kuragehime Co-authored-by: ngocanhtve Co-authored-by: ssantos Co-authored-by: yosrinajar Co-authored-by: zeineb-b Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ar/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/cs/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/de/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/es/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hi/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/id/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/it/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/kn/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pa/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pl/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pt/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pt_PT/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sk/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sv/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/uk/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/vi/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/zh_Hant/ Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/zh_Hant_HK/ Translation: NewPipe/Metadata --- app/src/main/res/values-aeb/strings.xml | 2 + app/src/main/res/values-de/strings.xml | 5 + app/src/main/res/values-el/strings.xml | 11 + app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 11 + app/src/main/res/values-frc/strings.xml | 20 + app/src/main/res/values-hi/strings.xml | 11 + app/src/main/res/values-hr/strings.xml | 16 +- app/src/main/res/values-it/strings.xml | 4 + app/src/main/res/values-ja/strings.xml | 10 + app/src/main/res/values-kn/strings.xml | 2 + app/src/main/res/values-nl/strings.xml | 6 +- app/src/main/res/values-pa/strings.xml | 11 + app/src/main/res/values-pt-rBR/strings.xml | 423 +++++++++--------- app/src/main/res/values-pt-rPT/strings.xml | 11 + app/src/main/res/values-pt/strings.xml | 11 + app/src/main/res/values-ro/strings.xml | 15 +- app/src/main/res/values-ru/strings.xml | 7 + app/src/main/res/values-ryu/strings.xml | 11 + app/src/main/res/values-sc/strings.xml | 20 +- app/src/main/res/values-sr/strings.xml | 4 + app/src/main/res/values-sv/strings.xml | 10 + app/src/main/res/values-uk/strings.xml | 10 + app/src/main/res/values-vi/strings.xml | 46 +- app/src/main/res/values-zh-rHK/strings.xml | 10 + .../metadata/android/ar/changelogs/997.txt | 17 + .../metadata/android/cs/changelogs/997.txt | 17 + .../metadata/android/de/changelogs/990.txt | 1 + .../metadata/android/de/changelogs/997.txt | 17 + .../metadata/android/es/changelogs/810.txt | 8 +- .../metadata/android/es/changelogs/961.txt | 4 +- .../metadata/android/es/changelogs/977.txt | 2 +- .../metadata/android/es/changelogs/997.txt | 17 + .../metadata/android/hi/changelogs/997.txt | 17 + .../metadata/android/id/changelogs/997.txt | 15 + .../metadata/android/it/changelogs/997.txt | 17 + .../metadata/android/kn-IN/changelogs/830.txt | 1 + .../metadata/android/kn-IN/changelogs/850.txt | 1 + .../android/kn-IN/full_description.txt | 1 + .../android/kn-IN/short_description.txt | 1 + .../metadata/android/pa/changelogs/997.txt | 17 + .../metadata/android/pl/changelogs/997.txt | 17 + .../metadata/android/pt-PT/changelogs/996.txt | 2 + .../metadata/android/pt-PT/changelogs/997.txt | 16 + .../metadata/android/pt/changelogs/997.txt | 16 + .../metadata/android/sk/changelogs/997.txt | 17 + .../metadata/android/sv/changelogs/997.txt | 17 + .../metadata/android/uk/changelogs/997.txt | 17 + .../metadata/android/vi/changelogs/70.txt | 25 ++ .../metadata/android/vi/changelogs/985.txt | 1 + .../metadata/android/vi/changelogs/997.txt | 17 + .../android/zh-Hant/changelogs/997.txt | 17 + .../android/zh_Hant_HK/changelogs/997.txt | 17 + 53 files changed, 775 insertions(+), 246 deletions(-) create mode 100644 app/src/main/res/values-aeb/strings.xml create mode 100644 app/src/main/res/values-frc/strings.xml create mode 100644 fastlane/metadata/android/ar/changelogs/997.txt create mode 100644 fastlane/metadata/android/cs/changelogs/997.txt create mode 100644 fastlane/metadata/android/de/changelogs/997.txt create mode 100644 fastlane/metadata/android/es/changelogs/997.txt create mode 100644 fastlane/metadata/android/hi/changelogs/997.txt create mode 100644 fastlane/metadata/android/id/changelogs/997.txt create mode 100644 fastlane/metadata/android/it/changelogs/997.txt create mode 100644 fastlane/metadata/android/kn-IN/changelogs/830.txt create mode 100644 fastlane/metadata/android/kn-IN/changelogs/850.txt create mode 100644 fastlane/metadata/android/kn-IN/full_description.txt create mode 100644 fastlane/metadata/android/kn-IN/short_description.txt create mode 100644 fastlane/metadata/android/pa/changelogs/997.txt create mode 100644 fastlane/metadata/android/pl/changelogs/997.txt create mode 100644 fastlane/metadata/android/pt-PT/changelogs/996.txt create mode 100644 fastlane/metadata/android/pt-PT/changelogs/997.txt create mode 100644 fastlane/metadata/android/pt/changelogs/997.txt create mode 100644 fastlane/metadata/android/sk/changelogs/997.txt create mode 100644 fastlane/metadata/android/sv/changelogs/997.txt create mode 100644 fastlane/metadata/android/uk/changelogs/997.txt create mode 100644 fastlane/metadata/android/vi/changelogs/70.txt create mode 100644 fastlane/metadata/android/vi/changelogs/985.txt create mode 100644 fastlane/metadata/android/vi/changelogs/997.txt create mode 100644 fastlane/metadata/android/zh-Hant/changelogs/997.txt create mode 100644 fastlane/metadata/android/zh_Hant_HK/changelogs/997.txt diff --git a/app/src/main/res/values-aeb/strings.xml b/app/src/main/res/values-aeb/strings.xml new file mode 100644 index 000000000..a6b3daec9 --- /dev/null +++ b/app/src/main/res/values-aeb/strings.xml @@ -0,0 +1,2 @@ + + \ 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 3a308fb25..fcc38bf0b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -819,4 +819,9 @@ Ja Nein Sichern und Wiederherstellen + NewPipe kann von Zeit zu Zeit automatisch nach neuen Versionen suchen und dich benachrichtigen, sobald sie verfügbar sind. +\nMöchtest du dies aktivieren? + Wenn du alle Einstellungen zurücksetzt, werden alle deine bevorzugten Einstellungen verworfen und die App wird neu gestartet. +\n +\nMöchtest du wirklich fortfahren? \ 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 cb5958dc5..6e9525459 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -813,4 +813,15 @@ Εμφάνιση περισσοτέρων Εμφάνιση λιγότερων Επεξεργαστείτε κάθε ενέργεια ειδοποίησης παρακάτω πατώντας σε αυτήν. Οι τρεις πρώτες ενέργειες (αναπαραγωγή/παύση, προηγούμενηο και επόμενο) ορίζονται από το σύστημα και δεν μπορούν να τροποποιηθούν. + Δεν υπάρχει αρκετός ελεύθερος χώρος στη συσκευή + Όχι + Ναι + Αντίγραφο ασφαλείας και επαναφορά + Το NewPipe μπορεί να ελέγχει αυτόματα για νέες εκδόσεις και να σας ειδοποιεί μόλις είναι διαθέσιμες. +\nΘέλετε να το ενεργοποιήσετε; + Επαναφορά ρυθμίσεων + Επαναφορά όλων των ρυθμίσεων στις αρχικές τιμές τους + Η επαναφορά όλων των ρυθμίσεων θα απορρίψει όλες τις τροποποιημένες ρυθμίσεις σας και θα επανεκκινήσει την εφαρμογή. +\n +\nΕίστε βέβαιοι ότι θέλετε να συνεχίσετε; \ 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 ce0b32603..c7b780a1f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -782,7 +782,7 @@ Suscriptores Qué pestañas se muestran en las páginas de los canales Pestañas del canal - Cortos + Shorts Cargando los metadatos… Recuperar las fichas del canal Acerca de diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9994abb4c..66418e7cf 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -829,4 +829,15 @@ 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 + Réinitialiser tous les paramètres à leurs valeurs par défaut + Non + La réinitialisation de tous les paramètres va supprimer toutes vos préférences de paramètres et redémarrer l\'application. +\n +\nÊtes-vous sûr de vouloir poursuivre ? + Sauvegarde et restauration + Oui + NewPipe peut automatiquement vérifier la disponibilité de nouvelles versions de temps en temps et vous notifier lorsqu\'elles sont disponibles. +\nVoulez-vous activer cette vérification ? + Réinitialiser les paramètres + Pas assez d\'espace disponible sur l\'appareil \ No newline at end of file diff --git a/app/src/main/res/values-frc/strings.xml b/app/src/main/res/values-frc/strings.xml new file mode 100644 index 000000000..5b919711c --- /dev/null +++ b/app/src/main/res/values-frc/strings.xml @@ -0,0 +1,20 @@ + + + aucun streamer trouvé . Installez VLC? + non + ouvrir dans le browser + ouvrir dans le popup mode + ouvrir avec + partagez + installer le fichier stream + chercher + parameters + installer + Installer + marquer comme vu + "publié le %1$s" + aucun joueur de stream n\'est trouvé ( vous pouvez installez VLC pour jouer) + Annuler + OK + Oui + \ 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 756b1878a..51455fafb 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -813,4 +813,15 @@ और दिखाओ नीचे दी गई प्रत्येक अधिसूचना कार्रवाई पर टैप करके उसे संपादित करें। पहली तीन क्रियाएँ (चलाएँ/रोकें, पिछली और अगली) सिस्टम द्वारा निर्धारित की जाती हैं और इन्हें अनुकूलित नहीं किया जा सकता है। कम दिखाएं + न्यूपाइप समय-समय पर स्वचालित रूप से नए संस्करणों की जांच कर सकती है और उपलब्ध होने पर आपको सूचित कर सकती है। +\nक्या आप इसे सक्षम करना चाहते हैं? + सेटिंग्स रीसेट करें + सभी सेटिंग्स को उनके डिफ़ॉल्ट मानों पर रीसेट करें + सभी सेटिंग्स को रीसेट करने से आपकी सभी पसंदीदा सेटिंग्स खारिज हो जाएंगी और ऐप पुनः प्रारंभ हो जाएगा। +\n +\nक्या आप सुनिश्चित रूप से आगे बढ़ना चाहते हैं? + हाँ + नहीं + डिवाइस पर पर्याप्त खाली स्थान नहीं है + बैकअप और रिस्टोर \ 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 f6c2120f7..419f4619e 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -574,7 +574,7 @@ \nYouTube je primjer usluge koja nudi ovaj brzi način sa svojim RSS feedom. \n \nDakle, izbor se svodi na ono što više voliš: brzinu ili precizne informacije.
    - Izračunavanje šifriranja + Izračunavanje šifre Obavijest šifriranja videa Obavijesti o napretku šifriranja videa Nedavni @@ -733,7 +733,7 @@ Najava Razvrstaj Koristi razervnu funkciju ExoPlayer dekodera - Ignoriranje hardverskih medijskih gumba + Ignoriraj događaje 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 @@ -821,4 +821,16 @@ %s odgovora Tuneliranje medija je standardno deaktivirano na tvom uređaju jer je poznato da model tvog uređaja to ne podržava. + Da + Ne + Aktiviraj ovu opciju ako imaš problema s inicijaliziranjem dekodera, što vraća dekodere nižeg prioriteta ako inicijaliziranje primarnih dekodera ne uspije. To može rezultirati lošijim performansama reprodukcije u odnosu na korištenje primarnih dekodera + Nedovoljno memorije na uređaju + Spremanje sigurnosne kopije i obnavljanje + NewPipe može automatski tražiti nove verzije i obavijestiti te. +\nŽeliš li aktivirati tu mogućnost? + Obnovi postavke + Obnovi sve postavke na zadane vrijednosti + Obnavljanje svih postavki odbacit će sve tvoje postavljene postavke i aplikacija će se ponovo pokrenuti. +\n +\nStvarno želiš nastaviti? \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 35a722a57..2a5ac16d3 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -834,4 +834,8 @@ \n \nSei sicuro di voler procedere?
    Azzera tutte le impostazioni ai loro valori predefiniti + + No + NewPipe può cercare automaticamente nuove versioni di tanto in tanto e avvisarti quando sono disponibili. +\nVuoi attivarlo? \ 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 6cbc3c084..74924125a 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -800,4 +800,14 @@ 表示を少なくする 以下の通知アクションをタップして編集します。 最初の3つのアクション (再生/一時停止、前へ、次へ)はシステムによって設定されており、カスタマイズすることはできません。 デバイスの空き容量が不足しています + バックアップと復元 + はい + いいえ + NewPipe は定期的に新しいバージョンを自動的にチェックし、更新可能になると通知します。 +\n有効にしますか? + 設定をリセット + 全ての設定をデフォルト状態にリセットします + 全ての設定をリセットすると、優先設定が全て破棄され、アプリが再起動します。 +\n +\n続行しますか? \ No newline at end of file diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index ec598114d..2ba73e178 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -40,4 +40,6 @@ ಟ್ಯಾಬ್ ಆಯ್ಕೆಮಾಡಿ ಹಿನ್ನೆಲೆ ಅನ್‌ಸಬ್‌ಸ್ಕ್ರೈಬ್ ಮಾಡಿ + ಹೌದು + ಇಲ್ಲ \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 2a5206699..b4629a03f 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -205,8 +205,8 @@ Pop-upspeler Bezig met ophalen van informatie… Bezig met laden van gevraagde inhoud - Databank importeren - Databank exporteren + Data­base importeren + Data­base exporteren Dit overschrijft je huidige geschiedenis, abonnementen, afspeellijsten en instellingen Exporteer geschiedenis, abonnementen, afspeellijsten en instellingen Geëxporteerd @@ -637,7 +637,7 @@ Reacties zijn uitgeschakeld Zoeksuggesties op afstand Lokale zoeksuggesties - Markeer als bekeken + Markeren als bekeken %1$s download verwijderd %1$s downloads verwijderd diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 196b5b785..814cdb885 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -813,4 +813,15 @@ ਹੋਰ ਵਿਖਾਓ ਇਸ \'ਤੇ ਟੈਪ ਕਰਕੇ ਹੇਠਾਂ ਹਰੇਕ ਸੂਚਨਾ ਕਾਰਵਾਈ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰੋ। ਪਹਿਲੀਆਂ ਤਿੰਨ ਕਾਰਵਾਈਆਂ (ਪਲੇ/ਪੌਜ਼, ਪਿਛਲਾ ਅਤੇ ਅਗਲਾ) ਸਿਸਟਮ ਦੁਆਰਾ ਸੈੱਟ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ ਅਤੇ ਉਹਨਾਂ ਨੂੰ ਅਨੁਕੂਲਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ। ਘੱਟ ਦਿਖਾਓ + ਹਾਂ + ਨਿਊਪਾਈਪ ਸਮੇਂ-ਸਮੇਂ \'ਤੇ ਨਵੇਂ ਸੰਸਕਰਣਾਂ ਦੀ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਜਾਂਚ ਕਰ ਸਕਦੀ ਹੈ ਅਤੇ ਇੱਕ ਵਾਰ ਉਪਲਬਧ ਹੋਣ \'ਤੇ ਤੁਹਾਨੂੰ ਸੂਚਿਤ ਕਰ ਸਕਦੀ ਹੈ। +\nਕੀ ਤੁਸੀਂ ਇਸਨੂੰ ਇਨੇਬਲ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? + ਬੈਕਅੱਪ ਅਤੇ ਰੀਸਟੋਰ + ਸੈਟਿੰਗਾਂ ਨੂੰ ਰੀਸੈਟ ਕਰੋ + ਸਾਰੀਆਂ ਸੈਟਿੰਗਾਂ ਨੂੰ ਉਹਨਾਂ ਦੇ ਡਿਫ਼ਾਲਟ ਮੁੱਲਾਂ \'ਤੇ ਰੀਸੈਟ ਕਰੋ + ਸਾਰੀਆਂ ਸੈਟਿੰਗਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨ ਨਾਲ ਤੁਹਾਡੀਆਂ ਸਾਰੀਆਂ ਤਰਜੀਹੀ ਸੈਟਿੰਗਾਂ ਰੱਦ ਹੋ ਜਾਣਗੀਆਂ ਅਤੇ ਐਪ ਰੀਸਟਾਰਟ ਹੋ ਜਾਵੇਗਾ। +\n +\nਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ \'ਤੇ ਅੱਗੇ ਵਧਣਾ ਚਾਹੁੰਦੇ ਹੋ? + ਡਿਵਾਈਸ \'ਤੇ ਲੋੜੀਂਦੀ ਖਾਲੀ ਥਾਂ ਨਹੀਂ ਹੈ + ਨਹੀਂ \ 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 1d241de51..5a203e0f2 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -17,8 +17,8 @@ Excluir Não gostei Curtidas - Baixar - Baixar + Download + Download Detalhes: Relatar por e-mail Relatório de erro @@ -31,7 +31,7 @@ Erro Copiado para a área de transferência Nome do arquivo - Toque para detalhes + Toque para mais detalhes O NewPipe está baixando Por favor, espere… Erro de rede @@ -57,30 +57,30 @@ Downloads Downloads Você quis dizer \"%1$s\"\? - Aplicativo/IU parou + O aplicativo travou Reproduzindo em segundo plano O menu de download não pôde ser configurado Reproduzir vídeo, duração: - Miniatura do avatar do uploader + Foto de perfil do autor Escolha a pasta de download para arquivos de áudio Áudios baixados são salvos aqui Pasta para áudios baixados Escolha a pasta de download para arquivos de vídeo Vídeos baixados são salvos aqui Pasta para vídeos baixados - Instalar o aplicativo Kore\? + Instalar Kore? Toque na lupa para começar. - Threads + Processos Por favor, defina uma pasta de download depois nas configurações - Sem reprodutor de transmissão. Instalar VLC? + Player de vídeo não encontrado. Instalar VLC? O site não pôde ser analisado Áudio - Reproduzir com Kodi + Reproduzir no Kodi Pesquisar - Mostra uma opção para ver vídeo pelo media center do Kodi - Usar reprodutor de áudio externo - Usar reprodutor de vídeo externo - Mostrar opção \"Reproduzir com Kodi\" + Mostrar opção para reproduzir o vídeo no Kodi + Usar player de áudio externo + Usar player de vídeo externo + Mostrar opção \"Reproduzir no Kodi\" O que aconteceu:\\nPedido:\\nIdioma do conteúdo:\\nPaís do conteúdo:\\nIdioma do aplicativo:\\nServiço:\\nHora GMT:\\nPacote:\\nVersão:\\nVersão do SO: Abrir no modo Popup Resolução padrão do Popup @@ -88,11 +88,11 @@ Apenas alguns dispositivos suportam vídeos em 2K/4K Formato de vídeo padrão Reproduzindo em modo Popup - Tudo + Todos Desativado - k - M - Bi + mil + mi + bi Essa permissão é necessária \npara abrir em modo Popup Limpar @@ -100,7 +100,7 @@ Segundo plano Lembrar propriedades do Popup Lembrar último tamanho e posição do Popup - Remove o som em algumas resoluções + Remove áudio em algumas resoluções Sugestões de pesquisa Escolha as sugestões a serem exibidas enquanto estiver buscando Melhor resolução @@ -115,7 +115,7 @@ Colaborar © %1$s %2$s protegido pela licença %3$s Sobre o NewPipe - Baixar + Download Caracteres permitidos em nome de arquivos Os caracteres inválidos são substituídos por este valor Caractere de substituição @@ -124,20 +124,20 @@ Inscrever-se Inscrito Inscrição cancelada - Não foi possível alterar inscrição - Não foi possível atualizar inscrição + Não foi possível alterar a inscrição + Não foi possível atualizar a inscrição Inscrições Novidades Continuar reprodução Continua vídeo após interrupções (ex: ligações) Histórico de pesquisa Armazena histórico de pesquisa localmente - Histórico de vídeo - Armazena histórico de vídeos assistidos + Histórico de exibição + Mantenha o controle dos vídeos assistidos Histórico Histórico Notificação do NewPipe - Notificações para o reprodutor do NewPipe + Notificações para o player NewPipe Comportamento Histórico e cache Desfazer @@ -160,29 +160,29 @@ %s vídeos %s vídeos - Reprodutor + Player Nada aqui além de grilos Deseja excluir este item do histórico de busca\? Conteúdo da página inicial Página em branco - Página do Quiosque - Página de canais + Página do Kiosk + Página do canal Selecione um canal Nenhuma inscrição ainda - Selecione uma banca + Selecione um Kiosk Em Alta Top 50 Novos e tendências Mostrar dica \"Segure para pôr na fila\" Mostra dica ao tocar no botão segundo plano ou Popup em \"Detalhes:\" do vídeo Reproduzir tudo - Não é possível reproduzir esta transmissão - Ocorreu um erro irrecuperável no reprodutor - Se recuperando do erro do reprodutor + Não é possível reproduzir este vídeo + Ocorreu um erro irrecuperável na reprodução + Se recuperando de um erro durante a reprodução Remover Detalhes Configurações de áudio - Segure para pôr na fila + Toque longo para pôr na fila [Desconhecido] Reproduzir em segundo plano Reproduzir em um Popup @@ -191,52 +191,52 @@ Retribuir Site oficial Visite o site do NewPipe para mais informações e novidades. - Reprodutor de transmissão não encontrado (pode instalar o VLC para assistir). + Player de vídeo não encontrado (você pode instalar o VLC para reproduzi-lo). País padrão do conteúdo Sempre - Uma vez + Apenas uma vez Mudar para segundo plano Mudar para Popup Mudar para principal - Reprodutores externos não suportam estes tipos de URL + Players externos não suportam estes tipos de URL Nenhuma transmissão de vídeo encontrada Nenhuma transmissão de áudio encontrada - Reprodutor de vídeo - Reprodutor em segundo plano - Reprodutor Popup + Player de vídeo + Reprodução em segundo plano + Reprodução em Popup Obtendo informação… Carregando conteúdo solicitado Importar base de dados Exportar base de dados - Substitui seu histórico atual, inscrições, listas de reprodução e (opcionalmente) configurações - Exporta histórico, inscrições, listas de reprodução e configurações + Substitui seu histórico atual, inscrições, playlists e (opcionalmente) configurações + Exporta histórico, inscrições, playlists e configurações Exportado Importado Nenhum arquivo ZIP válido Aviso: Não foi possível importar todos os arquivos. Isso irá sobrescrever suas configurações atuais. - Baixar arquivo de transmissão + Baixar arquivo Mostrar informação - Listas de reprodução favoritas + Playlists favoritas Adicionar a Arraste para ordenar Criar - Dispensar + Descartar Renomear - Último reproduzido - Mais reproduzido + Última reprodução + Mais assistidos Sempre perguntar - Nova lista de reprodução + Nova playlist Renomear Nome - Adicionar à lista de reprodução - Definir como miniatura da lista de reprodução - Favoritar lista de reprodução + Adicionar à playlist + Definir como miniatura da playlist + Salvar como playlist favorita Remover dos favoritos - Excluir esta lista de reprodução? - lista de reprodução criada - Adicionado à lista de reprodução - Miniatura da lista de reprodução alterada. + Excluir esta playlist? + Playlist criada + Adicionado à playlist + Miniatura da playlist alterada. Sem legendas Ajustar Preencher @@ -246,11 +246,11 @@ Reportar erros de out-of-lifecycle Forçar entrega de relatórios de erros Rx fora de um fragmento ou atividade de lifecycle após o descarte Usar busca de posição rápida (inexata) - A busca inexata permite que o reprodutor de vídeo ache posições mais rápido com a precisão reduzida. Não funciona para voltar ou avançar 5, 15 ou 25 segundos + A busca inexata permite que o player de vídeo ache posições mais rápido com a precisão reduzida. Não funciona para voltar ou avançar 5, 15 ou 25 segundos Enfileirar a próxima transmissão automaticamente Continua a reprodução da fila (sem repetição) adicionando mais transmissões similares Arquivo - Pasta não existe + Essa pasta não existe Arquivo/fonte do conteúdo não existe O arquivo não existe ou não há permissão para leitura ou escrita O nome do arquivo não pode ficar vazio @@ -262,78 +262,78 @@ Exportando… Importar arquivo Exportação anterior - Não foi possível importar inscrições - Não foi possível exportar inscrições - Importe inscrições do YouTube pelo Google takeout: + Não foi possível importar as inscrições + Não foi possível exportar as inscrições + Importar inscrições do YouTube em Google Takeout: \n \n1. Acesse este URL: %1$s \n2. Faça login quando solicitado -\n3. Clique em \"Todos os dados incluídos\", depois em \"Desmarcar todos\", em seguida, selecione apenas \"assinaturas\" e clique em \"OK\" -\n4. Clique em \"Próximo passo\" e em seguida, em \"Criar exportação\" -\n5. Clique no botão \"Baixar\" quando ele aparecer +\n3. Clique em \"Todos os dados incluídos\", depois em \"Desmarcar todos\", selecione apenas \"Inscrições\" e clique em \"OK\" +\n4. Clique em \"Próxima etapa\" e, em seguida, em \"Criar exportação\" +\n5. Clique no botão \"Download\" quando ele aparecer \n6. Clique em IMPORTAR ARQUIVO abaixo e selecione o arquivo .zip baixado -\n7. Caso a importação do arquivo .zip falhe: Extraia o arquivo .csv (geralmente em \"YouTube e YouTube Music/subscriptions/subscriptions.csv\", clique em IMPORTAR ARQUIVO abaixo e selecione o arquivo csv extraído - Importe um perfil do SoundCloud digitando o URL ou seu ID: +\n7. [Se a importação do .zip falhar] Extraia o arquivo .csv (geralmente em \"YouTube e YouTube Music/subscriptions/subscriptions.csv\"), clique em IMPORTAR ARQUIVO abaixo e selecione o arquivo csv extraído + Importar um perfil do SoundCloud digitando o URL ou seu ID: \n -\n1. Ative o \"modo desktop\" em um navegador (o site não está disponível em aparelhos celulares) +\n1. Ative o \"Modo desktop (computador)\" em um navegador da Web (o site não está disponível para dispositivos móveis) \n2. Acesse este URL: %1$s \n3. Faça login quando solicitado -\n4. Copie o URL do perfil que você foi redirecionado. +\n4. Copie o URL do perfil para o qual você foi redirecionado. seuID, soundcloud.com/seuid Tenha em mente que esta operação poderá consumir muitos dados. \n \nVocê deseja continuar\? - Cache de imagens limpo + Cache de imagens removidos Limpar cache de metadados Remove todos os dados de páginas em cache - Cache de metadados limpo - Controles de velocidade de reprodução + Cache de metadados removidos + Controles para velocidade de reprodução Velocidade Afinação Desvincular (pode causar distorção) Ação de \'abrir\' preferida Ação padrão ao abrir conteúdo — %s - Nenhuma transmissão disponível para baixar + Nenhum vídeo disponível para download Abrir gaveta Fechar gaveta Legendas Mudar tamanho da legenda e estilos de plano de fundo. Requer reiniciar o aplicativo para ter efeito - Excluir histórico de vídeo - Exclui o histórico de transmissões exibidas e as posições de reprodução - Excluir todo o histórico de vídeo\? - Histórico de vídeos excluído - Excluir histórico de pesquisa - Exclui o histórico de palavras-chave de pesquisa - Excluir todo o histórico de pesquisa\? - Histórico de busca limpo + Limpar histórico de exibição + Remove histórico de vídeos assistidos e as posições de reprodução + Remover todo o histórico de exibição? + Histórico de exibição removido + Remover histórico de pesquisas + Remove histórico de pesquisas + Remover todo histórico de pesquisas? + Histórico de pesquisa removido 1 item excluído. NewPipe é um copyleft de software livre: Você pode usar, estudar, compartilhar e melhorar a seu gosto. Especificamente você pode redistribuir e/ou modificá-lo sob os termos da GNU General Public License como publicado pela Fundação de Software Livre, na versão 3 da Licença, ou (a seu critério) qualquer versão posterior. Você também quer importar as configurações? Política de privacidade do NewPipe O projeto NewPipe leva sua privacidade muito a sério. Por isso, o aplicativo não coleta nenhum dado sem seu consentimento. \nA política de privacidade do NewPipe explica em detalhes quais dados são envidados e salvos quando você manda um relatório de erro. - Ler a política de privacidade + Ver política de privacidade A fim de cumprir com o Regulamento Geral sobre a Proteção de Dados da UE (RGPD), chamamos sua atenção para a política de privacidade do NewPipe. Por favor, leia com atenção. \nVocê deve aceitá-la para nos enviar o relatório de erros. Aceitar Recusar Ilimitado - Limitar a resolução quando estiver usando dados móveis - Minimizar ao trocar entre aplicativos - Ação ao mudar para outro aplicativo a partir do reprodutor de vídeo principal — %s + Limitar resolução de vídeos ao usar dados móveis + Minimizar ao mudar de aplicativos + Ação ao mudar de aplicativo a partir do player principal - %s Nenhum - Minimizar para segundo plano - Minimizar para reprodutor Popup + Minimizar reprodução para o modo de segundo plano + Minimizar reprodução para o modo Popup Avançar durante o silêncio Passo Redefinir Canais - Listas de reprodução + Playlists Faixas Usuários Cancelar inscrição - Selecionar aba - Debug + Escolha a guia + Depuração Atualizações Eventos Arquivo excluído @@ -341,26 +341,26 @@ Notificações para novas versões do NewPipe Armazenamento externo indisponível Não é possível baixar para o cartão SD externo. Redefinir o local da pasta de download\? - Não foi possível carregar as abas salvas, carregando as abas padrão - Restaurar padrões - Deseja restaurar padrões\? + Não foi possível ler as guias salvas, portanto, usamos as guias padrão + Restaurar configurações + Deseja restaurar os padrões? Número de inscritos indisponível - Que abas são visíveis na página inicial + Quais guias são exibidas na página inicial Conferências Atualizações Notificar quando uma nova versão do aplicativo estiver disponível - Modo de exibição em lista + Modo de exibição da lista Lista Grade Automático - Atualização do NewPipe disponível! + Uma atualização do NewPipe está disponível! Finalizado pausado na fila pós-processamento - Fila + Colocar na fila Ação negada pelo sistema - O download falhou + Download falhou Gerar nome único Sobrescrever Um arquivo baixado com esse nome já existe @@ -368,11 +368,11 @@ Mostrar erro O arquivo não pode ser criado A pasta de destino não pode ser criada - Uma conexão segura não pôde ser estabelecida - O servidor não pôde ser encontrado + Não foi possível estabelecer uma conexão segura + Não foi possível encontrar o servidor Não foi possível se conectar ao servidor O servidor não envia dados - O servidor não aceita downloads em multi-thread, tente de novo com @string/msg_threads = 1 + O servidor não aceita downloads multi-processo, tente novamente com @string/msg_threads = 1 Não encontrado Falha no pós-processamento Parar @@ -392,7 +392,7 @@ Posições em listas Mostra indicadores de posição de reprodução em listas Excluir dados - Posições de reprodução limpas + Posições de reprodução removidas Arquivo movido ou excluído Já existe um arquivo com este nome O arquivo não pode ser sobrescrito @@ -403,24 +403,24 @@ Tempo limite de conexão Excluir todo o histórico de downloads ou excluir todos os arquivos baixados\? Limitar fila de downloads - Faz downloads um de cada vez + Permitir apenas um download de cada vez Iniciar downloads Pausar downloads Perguntar onde salvar o arquivo Você será questionado onde salvar cada download. \nAtive o seletor de pasta do sistema (SAF) se você quiser baixar em um cartão SD externo Usar o seletor de pastas do sistema (SAF) - O \'Storage Access Framework\' permite baixar em um cartão SD externo - Excluir posição das reproduções - Exclui todas as posições de reprodução - Excluir todas as posições de reprodução\? - Alternar serviço, selecionados: - Quiosque Padrão - Ninguém está vendo + A \"Estrutura de acesso ao armazenamento\" permite baixar em 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? + Alternar serviço, atualmente selecionado: + Kiosk padrão + Ninguém está assistindo %s assistindo %s assistindo - %s estão vendo + %s assistindo Ninguém está ouvindo @@ -429,14 +429,14 @@ %s ouvintes O idioma será alterado após reiniciar o aplicativo - Duração do salto para avançar/retroceder - Instâncias do PeerTube - Escolha suas instâncias do PeerTube favoritas + Duração de avanço/retrocesso rápido + Instâncias PeerTube + Selecione suas instâncias favoritas do PeerTube Encontre as instâncias que gosta em %s Adicionar instância Insira o URL da instância Erro ao validar a instância - Apenas os URL HTTPS são suportados + Somente URL HTTPS são compatíveis A instância já existe Local Adicionado recentemente @@ -447,7 +447,7 @@ Escolha uma instância Limpar histórico de downloads Excluir arquivos baixados - Dar permissão para mostrar por cima de outros aplicativos + Obter permissão para exibir sobre outros aplicativos Idioma do aplicativo Padrão do sistema Toque em \"Pronto\" ao resolver @@ -521,34 +521,34 @@ Este vídeo tem restrição de idade. \n \nAtive \"%1$s\" nas configurações se quiser vê-lo. - Sim, e vídeos parcialmente vistos - Os vídeos que foram vistos antes e depois de terem sidos adicionados à lista de reprodução serão removidos. + Sim, e vídeos parcialmente assistidos + Os vídeos que foram assistidos antes e depois de terem sidos adicionados à playlist serão removidos. \nTem certeza? Esta ação não pode ser desfeita! - Remover vídeos vistos\? - Remover vistos + Remover vídeos assistidos? + Remover assistidos Textos originais dos serviços serão visíveis nos itens de transmissão Mostrar tempo original nos itens Ativar o \"Modo Restrito\" do YouTube Por %s Criado por %s - Miniatura do avatar do canal + Foto de perfil do canal Mostrar apenas inscrições não agrupadas Mostrando resultados para: %s - Ainda não há listas de reprodução favoritas - Página da lista de reprodução - Selecione uma lista de reprodução - Por favor verifique se uma issue discutindo este problema já existe. Ao criar tickets duplicados, você tira de nós um tempo no qual poderíamos estar usando para corrigir um bug real. + Ainda não há playlist favoritas + Página da playlist + Selecione uma playlist + Verifique se o erro já foi informado. Ao informar erros duplicados, você nos toma o tempo que poderíamos dedicar a outras correções de erros. Reporte no GitHub Copiar relatório formatado Nunca - Apenas no Wi-Fi + Apenas em Wi-Fi Iniciar reprodução automaticamente — %s - Reproduzir fila + Fila de reprodução Não foi possível reconhecer a URL. Abrir com outro aplicativo\? Pôr na fila automaticamente - A fila do reprodutor ativo será substituída - Mudar de um reprodutor de vídeo para outro pode substituir sua fila - Pedir confirmação antes de limpar uma fila + A fila de reprodução atual será substituída + Mudar de um player para outro pode substituir sua fila + Pedir confirmação antes de limpar a fila Aleatório Carregando Nada @@ -560,8 +560,8 @@ Terceiro botão de ação Segundo botão de ação Primeiro botão de ação - Cortar a miniatura do vídeo mostrada na notificação da proporção 16:9 para 1:1 - Cortar a miniatura para a proporção de 1:1 + Ajustar miniatura de vídeo mostrada na notificação de 16:9 para 1:1 + Ajustar miniatura para a proporção de 1:1 Mostrar vazamentos de memória Na fila Pôr na fila @@ -575,8 +575,8 @@ Usar miniatura para o plano de fundo da tela de bloqueio e notificações Mostrar miniatura Calculando hash - Notificações para o progresso do hash do vídeo - Notificação de hash do vídeo + Notificações sobre o progresso do hashing de vídeo + Notificar hash de vídeo Desative para ocultar as caixas de informações de metadados com informações adicionais sobre o criador, conteúdo da transmissão ou uma solicitação de pesquisa Mostrar informação de metadados Recente @@ -605,8 +605,8 @@ Automático (tema do dispositivo) Tema noturno Mostrar detalhes do canal - Desative o tunelamento de mídia se aparecer uma tela preta ou se tiver engasgos durante a reprodução do vídeo. - Desativar tunelamento de mídia + Desative o túnel de mídia se aparecer uma tela preta ou se tiver travamento durante a reprodução do vídeo. + Desativar túnel de mídia Interno Privado Não Listado @@ -631,52 +631,52 @@ \nDeseja cancelar a inscrição neste canal\? Não foi possível carregar o feed para \'%s\'. Erro ao carregar o feed - O \'Storage Access Framework\' é compatível apenas com versões a partir do Android 10 + A \"Estrutura de acesso ao armazenamento\" é compatível apenas com versões a partir do Android 10 Você será questionado onde salvar cada download Nenhuma pasta de download definida ainda, escolha a pasta de download padrão agora - Desligado - Ligado + Desativado + Ativado Modo tablet Não mostrar - Baixa qualidade (menor) - Alta qualidade (maior) + Baixa qualidade (pior) + Alta qualidade (melhor) Pré visualização da miniatura da barra de busca Os comentários estão desabilitados - Marcar como visto + Marcar como assistido Curtido pelo criador Exibir fitas coloridas no topo das imagens indicando sua fonte: vermelho para rede, azul para disco e verde para memória - %1$s download apagado - %1$s downloads apagados - %1$s downloads apagados + %1$s download excluído + %1$s downloads excluídos + %1$s downloads excluídos %s download concluído %s downloads concluídos %s downloads concluídos - Exibir indicadores com imagem + Mostrar indicadores de imagem Adicionado na próxima posição da fila Enfileira a próxima - Deslize items para remove-los - Não inicia os vídeos no reprodutor reduzido, mas muda direto para o modo de tela cheia, se a rotação automática estiver travada. Você ainda consegue acessar o reprodutor reduzido saindo da tela cheia - Iniciar o reprodutor principal em tela cheia + Deslize os itens para remove-los + Não inicie os vídeos no mini player, mas vá diretamente para o modo de tela cheia, se a rotação automática estiver bloqueada. Você ainda pode acessar o mini player saindo da tela cheia + Iniciar player principal em tela cheia Sugestões de busca remotas Sugestões de busca locais Processando… Pode demorar um pouco - Procurar por atualizações - Procurar manualmente por novas versões - Procurando por atualizações… - Travar o reprodutor de vídeo - Mostrar \"Fechar o reprodutor\" - Mostra uma opção de travamento ao usar o reprodutor + Buscar atualizações + Verificar manualmente se há novas versões + Buscando atualizações… + Travar reprodução + Mostrar \"Travar reprodução\" + Mostra uma opção para travar a reprodução Novos itens do feed Notificação de relatório de erro Notificações para reportar erros - O NewPipe encontrou um erro, toque para reportar + O NewPipe encontrou um erro, toque para relatar Crie uma notificação de erro Nenhum gerenciador de arquivos apropriado foi encontrado para esta ação. -\nInstale um gerenciador de arquivos compatível com o Storage Access Framework +\nInstale um gerenciador de arquivos compatível com a \"Estrutura de acesso ao armazenamento\" Ocorreu um erro, consulte a notificação Mostrar um snackbar de erro Nenhum gerenciador de arquivos apropriado foi encontrado para esta ação. @@ -684,36 +684,36 @@ Comentário fixado O LeakCanary não está disponível ExoPlayer padrão - Notificação do reprodutor - Configurar a notificação da reprodução da transmissão atual + Notificação de reprodução + Configurar notificação da reprodução do vídeo atual Notificações - Novas transmissões - Notificações sobre novas transmissões para inscrições - Notificações de novas transmissões - Notificar sobre novas transmissões de inscrições + Novos vídeos + Notificações sobre novos vídeos de inscrições + Notificações sobre novos vídeos + Notificar sobre novos vídeos de inscrições Frequência de verificação Nenhuma rede - Excluir todos os arquivos baixados do disco\? + Excluir todos os arquivos baixados? Agora você se inscreveu neste canal Alternar tudo , Carregando detalhes da transmissão… - %s nova transmissão - %s novas transmissões - %s novas transmissões + %s novo vídeo + %s novos vídeos + %s novos vídeos - Verifica por novas transmissões + Buscar novos vídeos Conexão de rede necessária As notificações estão desativadas Seja notificado Por cento Semitom - A transmissão selecionada não é compatível com reprodutores externos - Nenhum transmissão de áudio está disponível para reprodutores externos - Transmissões que ainda não são suportadas pelo baixador não são exibidos - Nenhum vídeo de transmissão está disponível para reprodutores externos - Selecione a qualidade para reprodutores externos + O vídeo selecionado não é compatível com players externos + Nenhuma transmissão de áudio está disponível para players externos + Os vídeos que ainda não são suportados pelo assistente de download não são exibidos + Nenhuma transmissão de vídeo está disponível para players externos + Selecione a qualidade para players externos Formato desconhecido Qualidade desconhecida Tamanho do intervalo de carregamento da reprodução @@ -722,32 +722,32 @@ Perguntas frequentes Classificar Modo rápido - Importar ou exportar inscrições do menu de 3 pontos + Importar ou exportar inscrições no menu com 3 pontos Toque para baixar %s - Você está executando a versão mais recente do NewPipe + Você já possui a atualização mais recente do NewPipe Esta opção só está disponível se %s for selecionado para Tema Desativar miniatura permanente Cartão Falha ao copiar para a área de transferência Duplicata adicionada %d vez(es) - As listas de reprodução em cinza já contêm este item. + As playlists em cinza já contêm este item. Ignorar eventos de botão de mídia de hardware Útil, por exemplo, se você estiver usando um fone de ouvido com botões físicos quebrados Remover duplicados Remover duplicados\? - Deseja remover todos as transmissões duplicadas nesta lista de reprodução? - Mostrar as transmissões seguintes - Mostrar/ocultar transmissões + Deseja remover todos os vídeos duplicados nesta playlist? + Mostrar próximos vídeos + Mostrar/ocultar vídeos Parcialmente assistido Em breve Totalmente assistido - Escolha o gesto da mão esquerda da tela do reprodutor - Ação do gesto esquerdo - Escolha o gesto da mão direita da tela do reprodutor + Escolha o gesto para a parte esquerda na tela de reprodução + Ação para o gesto à esquerda + Escolha o gesto para a parte direita na tela de reprodução Brilho Volume Nenhum - Ação do gesto direito + Ação para o gesto à direita Altere o tamanho do intervalo de carregamento (atualmente %s). Um valor menor pode acelerar o carregamento inicial do vídeo Dar preferência ao áudio original Selecionar o áudio original e independentemente do idioma @@ -755,69 +755,69 @@ Selecionar um áudio com descrição para pessoas com dificuldades de visão, se disponível Áudio: %s Faixa de áudio - Seleciona faixa de áudio para reprodutor externo + Selecione a faixa de áudio para players externo Desconhecido - Configurações de ExoPlayer - Gerenciar algumas configurações de ExoPlayer. É necessário reiniciar o reprodutor para aplicar as mudanças + Configurar ExoPlayer + Gerenciar algumas configurações do ExoPlayer. É necessário reiniciar o player para aplicar as mudanças %1$s %2$s original dublado descritivo Esta solução alternativa libera os codificadores de vídeo quando ocorre uma alteração de superfície, no lugar de definir a superfície para o Codec diretamente. Já usado pelo ExoPlayer em alguns dispositivos com esse problema, essa configuração só tem efeito no Android 6 e superior \n -\nAtivar esta opção pode evitar erros de reprodução ao alternar o reprodutor de vídeo atual ou alternar para tela cheia - Uma faixa de áudio já deve estar presente nesta transmissão - Utilizar a contingência do decodificador do ExoPlayer - Sempre utilizar o configuração de saída de vídeo alternativa do ExoPlayer +\nAtivar esta opção pode evitar erros de reprodução ao alternar o player de vídeo atual ou alternar para tela cheia + Uma faixa de áudio já deve estar presente neste vídeo + Usar decodificador alternativo do ExoPlayer + Usar sempre a solução alternativa de configuração da superfície de saída de vídeo do ExoPlayer Habilite essa opção se você tiver problemas de inicialização do decodificador, que retorna codificadores de baixa prioridade se o decodificador primário falhar. Isso pode resultar em pior desempenho de reprodução - Mova o seletor da aba principal para a parte inferior - Posição das abas principais - Nenhuma transmissão + Mover o seletor da guia principal para a parte inferior + Posição de guias principais + Nenhum conteúdo Nenhuma transmissão ao vivo - O tunelamento de mídia foi desabilitado por padrão em seu dispositivo porque seu modelo é conhecido por não suportá-lo. + O túnel de mídia foi desabilitado por padrão em seu dispositivo porque seu modelo é conhecido por não suportá-lo. Vídeos Inscritos - Quais guias são mostradas nas páginas do canal - Guias de canal + Quais guias são mostradas na página do canal + Guias do canal Shorts Carregando metadados… Buscar guias de canal Sobre Álbuns Guias a serem buscadas ao atualizar o feed. Esta opção não tem efeito se um canal for atualizado usando o modo rápido. - Listas de reprodução + Playlists Faixas Canais Ao vivo Qualidade da imagem \? - Compartilhar lista URL - Compartilhar com Títulos + Compartilhar URL + Compartilhar com título %1$s \n%2$s - Alterna a orientação da tela + Alternar orientação da tela Baixa qualidade Alternar tela cheia - Avatares - Próxima transmissão - Avatares do subcanal - Abrir a fila de reprodução + Fotos + Próxima vídeo + Fotos de perfil do subcanal + Abrir fila de reprodução Não carregar imagens Alta qualidade - Compartilhar Lista de Reprodução + Compartilhar playlist Avançar Retroceder - Repete - Compartilhar lista de reprodução com detalhes como nome da lista de reprodução e títulos de vídeo ou como uma lista simples dos URL de vídeos + Repetir + Compartilhar playlist com detalhes como o nome da playlist e títulos de vídeo ou como uma lista simples dos URL de vídeos Qualidade média - Avatares do carregador + Fotos de perfil do autor - %1$s: %2$s - Escolha a qualidade das imagens ou se deve carregá-las como estão, para reduzir o uso de dados e memória. Alterações limpam a memória e o cache de imagem no disco — %s + Escolha a qualidade das imagens e se as imagens devem ser carregadas, para reduzir o uso de dados e memória. As alterações limpam o cache de imagens na memória e no disco - %s Reproduzir Mais opções Miniaturas Duração - Transmissão anterior + Vídeo 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. @@ -827,4 +827,15 @@ %s respostas Mostrar menos + Não há espaço livre suficiente no dispositivo + Sim + Não + Backup e restauração + O NewPipe pode verificar automaticamente se há novas versões de tempos em tempos e notificá-lo quando elas estiverem disponíveis. +\nDeseja ativar essa opção? + Restaurar configurações + Restaurar todas as configurações para seus valores padrão + A restauração de todas as configurações descartará todas as suas configurações preferidas e reiniciará o aplicativo. +\n +\nTem certeza de que deseja continuar? \ 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 325c79d3c..498a49a53 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -827,4 +827,15 @@ %s respostas Mostrar menos + O NewPipe pode verificar automaticamente se há novas versões de tempos em tempos e notificá-lo quando elas estiverem disponíveis. +\nDeseja ativar essa opção? + Repor valores originais de todas as definições + A restauração de todas as configurações descartará todas as suas configurações preferidas e reiniciará a app. +\n +\nTem certeza que deseja continuar? + Sim + Não + Backup e restauro + Repor definições + Não há espaço suficiente no aparelho \ 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 e69f79a68..5534dbf0b 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -827,4 +827,15 @@ 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. + Não há espaço suficiente no dispositivo + Sim + Não + Repor valores originais de todas as definições + Backup e restauro + Repor definições + A restauração de todas as configurações descartará todas as suas configurações preferidas e reiniciará a app. +\n +\nTem certeza que deseja continuar? + O NewPipe pode verificar automaticamente se há novas versões de tempos em tempos e notificá-lo quando elas estiverem disponíveis. +\nDeseja ativar essa opção? \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index c9eaaf2c9..bcef4d952 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -765,15 +765,14 @@ Schimbați pe ecran complet Preluați filele canalului Avatare - Activați această opțiune dacă aveți probleme cu inițializarea decodorului care trece înapoi la decodoare cu prioritate mai scăzută dacă inițializarea decodoarelor principale eșuează. Asta poate duce la performanță de redare mai slabă decât atunci când se utilizează decodoarele principale. + Activați această opțiune dacă aveți probleme cu inițializarea decodorului care trece înapoi la decodoare cu prioritate mai scăzută dacă inițializarea decodoarelor principale eșuează. Asta poate duce la performanță de redare mai slabă decât atunci când se utilizează decodoarele principale Acțiunea gestului din dreapta %1$s %2$s Utilizați întotdeauna configurarația suprafeței de ieșire video din ExoPlayer ca soluție alternativă Transmisia viitoare Tunelizarea media a fost dezactivată în mod implicit pe dispozitivul dumneavoastră deoarece se cunoaște despre acest model de dispozitiv că nu o suportă. Avatarele subcanalelor - Această soluție alternativă eliberează și reinstanțează codecurile video când se întamplă o schimbare a suprafeței, în loc de a seta suprafața pentru codec direct. Deja -\n folosită de ExoPlayer pe unele dispozitive cu această problemă, această setare are efect doar pe Android 6 sau mai mare + Această soluție alternativă eliberează și reinstanțează codecurile video când se întamplă o schimbare a suprafeței, în loc de a seta suprafața pentru codec direct. Deja folosită de ExoPlayer pe unele dispozitive cu această problemă, această setare are efect doar pe Android 6 sau mai mare \n \nActivarea acestei opțiuni poate preveni erorile de redare când se schimbă playerul video curent sau se trece pe ecran complet O coloană sonoră ar trebui să fie deja prezentă în această transmisie @@ -829,4 +828,14 @@ Arată mai puține Piste Nu este suficient spațiu liber pe dispozitiv + Backup și restabilire + NewPipe poate verifica automat pentru versiuni noi din când în când și te poate notifica când acestea sunt disponibile. +\nDoriți să activați acest lucru? + Resetează toate setările la valorile inițiale + Da + Nu + Resetează setări + Resetarea tuturor setărilor va elimina toate setările tale preferate și va reporni aplicația. +\n +\nSigur doriți să continuați? \ 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 df566853b..9e79b165f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -833,4 +833,11 @@ Отредактируйте каждое действие уведомления ниже, нажав на него. Первые три действия (воспроизведение/пауза, предыдущее и следующее) задаются системой и не подлежат настройке. Недостаточно свободного места на устройстве + Сбросить все настройки на их значения по умолчанию + Да + Нет + Резервное копирование и восстановление + Сбросить настройки + NewPipe может автоматически проверять наличие обновлений и уведомить вас, когда они будут доступны. +\nЖелаете включить эту функцию? \ 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 195eb22b3..5a4f35de5 100644 --- a/app/src/main/res/values-ryu/strings.xml +++ b/app/src/main/res/values-ryu/strings.xml @@ -813,4 +813,15 @@ なーふぃんんーじゅん ひょうじいきらくすん いかぬちうちアクションタップしへんしゅうさびーん。さいしょぬみーちぬアクション (さいせい/いちじていし、めーんかい、ちぎんかい)ーシステムにゆってぃしっていさりてぃうぅい、カスタマイズすしぇーなやびらん。 + はい + NewPipeーてぃんじちーがみーさるバージョンじちゃーてぃきんかいチェックしー、こうしんがのうないるとぅちうちさびーん。 +\nゆうこうなさびーが? + デバイスぬあきゆういょうがふすくそーいびーん + うぅーうぅー + バックアップとぅふくぎん + しっていリセット + まじりぬしっていデフォルトじょうたいんかいリセットさびーん + まじりぬしっていリセットしーねー、ゆーいるしんしっていぬまじりはちされい、アプリぬさいきちゃーさびーん。 +\n +\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 a497844ab..6b89b32c3 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -539,7 +539,7 @@ Colende dae unu riproduidore a s\'àteru dias pòdere remplasare sa lista tua Pedi una cunfirma in antis de iscantzellare una lista Òrdine casuale - Modìfica cada atzione de notìfica inoghe in suta incarchende·la. Ischerta·nde finas a tres de ammustrare in sa notìfica cumpata impreende sas casellas de controllu a destra + Modìfica cada atzione de notìfica inoghe in suta incarchende·la. Ischerta·nde finas a tres de ammustrare in sa notìfica cumpata impreende sas casellas de controllu a destra. Sega sa miniadura ammustrada in sa notìfica dae su formadu in 16:9 a cussu 1:1 Nudda Carrighende @@ -806,4 +806,22 @@ Canales Flussu antepostu Diretas + Modìfica cada atzione de notìfica inoghe in suta tochende·la. Sas primas tres atziones (riprodutzione/pàusa, antepostu e imbeniente) sunt impostadas dae su sistema e non si podent personalizare. + Non b\'at ispàtziu lìberu bastante in su dispositivu + Mustra de mancu + NewPipe podet chircare in automàticu versiones noas cada tantu e notificare·ti cando sunt a disponimentu. +\nLu boles abilitare? + Nono + Còpia de seguresa e riprìstinu + + %s risposta + %s rispostas + + Mustra de prus + Eja + Reseta sas impostatziones + Reseta totu sas impostatziones a sos valores predefinidos issoro + Resetende totu sas impostatziones as a iscartare totu sas impostatziones preferidas tuas e a torrare a allùghere s\'aplicatzione. +\n +\nSes seguru de bòlere sighire? \ No newline at end of file diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 996b3fa2e..5f14a0e51 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -834,4 +834,8 @@ Ресетовање свих подешавања ће одбацити сва жељена подешавања и поново покренути апликацију. \n \nЖелите ли заиста да наставите? + Не + Да + NewPipe може аутоматски да проверава да ли постоје нове верзије с времена на време и да вас обавести када буду доступне. +\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 54aab0cdf..8cf57f605 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -814,4 +814,14 @@ 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 + Nej + Säkerhetskopiering och återställning + Ja + NewPipe kan automatiskt söka efter nya versioner då och då och meddela dig när de är tillgängliga. +\nVill du aktivera detta? + Återställ inställningar + Återställ alla inställningar till deras standardvärden + Om du återställer alla inställningar försvinner alla dina föredragna inställningar och appen startas om. +\n +\nÄr du säker på att du vill fortsätta? \ 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 fa63499ea..c34708609 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -833,4 +833,14 @@ Показати менше Недостатньо вільного простору на пристрої + Скинути налаштування + Резервне копіювання і відновлення + NewPipe може час від часу автоматично перевіряти наявність нових версій і сповіщати вас про їх появу. +\nХочете увімкнути цю функцію? + Так + Ні + Скинути всі налаштування до усталених значень + Скидання всіх налаштувань призведе до скидання всіх вибраних вами налаштувань і перезапуску застосунку. +\n +\nВи впевнені, що хочете продовжити? \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index dcb8d2f84..0d71173af 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -8,7 +8,7 @@ Mở trong trình duyệt Mở trong chế độ bật lên Chia sẻ - Tải về + Tải xuống Tìm kiếm Cài đặt Ý bạn là \"%1$s\"\? @@ -16,7 +16,7 @@ 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 + Bật lên 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 @@ -43,7 +43,7 @@ Ghi nhớ kích thước và vị trí cuối cùng của cửa sổ bật lên Đề xuất tìm kiếm Chọn các đề xuất để hiển thị khi tìm kiếm - Tải về + Tải xuống Hiện các video \"Tiếp theo\" và \"Tương tự\" URL không hỗ trợ Vẻ ngoài @@ -105,12 +105,12 @@ Giới thiệu về NewPipe Giấy phép của bên thứ ba © %1$s bởi %2$s dưới %3$s - Thông tin & FAQ + Giới thiệu & Câu hỏi thường gặp Giấy phép Phát trực tuyến nhẹ tự do trên Android. Xem trên GitHub Giấy phép của NewPipe - Sự đóng góp của bạn luôn được hoan nghênh – kể cả khi bạn dịch, thay đổi giao diện, dọn code, thêm tính năng hay thay đổi những thứ khác, sự giúp đỡ của bạn vẫn đáng được trân trọng. Bạn càng làm nhiều, ứng dụng này sẽ càng tốt hơn bao giờ hết ! + Cho dù bạn có ý tưởng về: dịch thuật, thay đổi thiết kế, dọn mã hoặc thay đổi mã thực sự nhiều— sự trợ giúp luôn được hoan nghênh. Làm càng nhiều thì càng tốt! Đọc giấy phép Đóng góp Ngôn ngữ nội dung ưu tiên @@ -134,7 +134,7 @@ 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 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 tất cả dữ liệu trang web được lưu trong bộ nhớ cache Đã 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 @@ -212,9 +212,9 @@ Ký tự thay thế Chỉ chữ cái và chữ số Hầu hết các ký tự đặc biệt - Đóng góp - NewPipe được phát triển bởi các tình nguyện viên dành thời gian và tâm huyết của mình để mang lại cho bạn trải nghiệm tốt nhất. Đóng góp một chút xiền để giúp chúng tôi làm NewPipe tốt hơn nữa (Nếu bạn muốn). - Đôn Nét + Quyên tặng + NewPipe được phát triển bởi các tình nguyện viên dành thời gian rảnh rỗi để mang lại cho bạn trải nghiệm người dùng tốt nhất. Hãy đền đáp để giúp các nhà phát triển làm cho NewPipe thậm chí còn tốt hơn nữa trong khi họ thưởng thức một tách cà phê. + Đền đáp Trang web Truy cập website chính thức của NewPipe để biết thêm thông tin và tin tức. Chính sách bảo mật của NewPipe @@ -283,7 +283,7 @@ Nhập từ Xuất sang Đang nhập… - Đang xuất … + Đang xuất… Nhập tệp Xuất trước Không thể nhập đăng ký @@ -310,7 +310,7 @@ Tốc độ Độ cao Bỏ gắn (có thể gây méo) - Tua nhanh trong im lặng + Chuyển nhanh qua khoảng lặng Bước Đặt lại Để tuân thủ Quy định bảo vệ dữ liệu chung của châu Âu (GDPR), chúng tôi sẽ thu hút sự chú ý của bạn đến chính sách bảo mật của NewPipe. Vui lòng đọc kỹ. @@ -318,7 +318,7 @@ Chấp nhận Từ chối Không giới hạn - Giới hạn độ phân giải khi sử dụng 3G, 4G + Giới hạn độ phân giải khi sử dụng dữ liệu di động 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 video chính — %s Không @@ -353,7 +353,7 @@ đang xử lý Xếp hàng Thao tác bị từ chối bởi hệ thống - Tải về không thành công + Tải xuống thất bại Tạo tên riêng biệt Ghi đè Có một tệp đã tải về trùng tên @@ -426,10 +426,10 @@ %d giây 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. + Những video đã 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! - Xóa video đã xem? - Xóa đã xem + Xóa các video đã xem? + Loại bỏ đã xem Mặc định hệ thống Ngôn ngữ ứng dụng \'Khung truy cập lưu trữ\' cho phép tải xuống một thẻ SD bên ngoài @@ -563,7 +563,7 @@ Bình luận 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 + Tắt để ẩn các hộp siêu dữ liệu 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ả @@ -624,7 +624,7 @@ Bình luận đã bị tắt Đã được chủ kênh thả \"thính\" Đánh dấu là đã xem - Hiện ruy băng được tô màu Picasso ở trên cùng các hình ảnh và chỉ ra nguồn của chúng: đỏ đối với mạng, xanh lam đối với ổ đĩa và xanh lá đối với bộ nhớ + Hiển thị các dải băng màu Picasso trên đầu các hình ảnh cho biết nguồn của chúng: màu đỏ cho mạng, màu lam cho đĩa và màu lục cho bộ nhớ Hiện dấu chỉ hình ảnh Đề xuất tìm kiếm trên mạng Đề xuất tìm kiếm cục bộ @@ -634,7 +634,7 @@ %s lượt tải xuống đã hoàn tất - Vuốt các mục để xóa chúng + Vuốt các mục để loại bỏ chúng 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 @@ -683,7 +683,7 @@ Phần trăm , 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ị + Các luồng chưa được trình tải xuống hỗ trợ sẽ không được hiển thị 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 @@ -705,9 +705,9 @@ Chế độ tăng tốc Danh sách phát màu xám thì đã chứa mục này. Hữu ích trong trường hợp phím bấm âm lượng trên tai nghe hoặc thiết bị của bạn bị hỏng - Không nhận phím điều khiển âm lượng vật lý - Loại bỏ mục trùng lặp - Loại bỏ mục trùng lặp\? + Bỏ qua nhận nút phương tiện vật lý + Loại bỏ các bản trùng lặp + Loại bỏ các bản trùng lặp? Bạn có muốn loại bỏ mọi luồng trùng lặp trong danh sách phát này\? Hiện/Ẩn luồng phát Hiển thị các luồng phát sau diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index a59da9c4e..518a1290f 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -800,4 +800,14 @@ 拉開 撳下面嘅掣去更改對應嘅通知動作。頭三個動作 (播放/暫停、上一個、下一個) 系統預設咗,冇得揀。 部機冇晒位 + + NewPipe 可以周不時自動睇過有冇新版本,一出咗就通知您。 +\n您想唔想啟用呢個功能? + 推翻所有設定 + 推翻所有設定就會全盤抹走晒您喜好過嘅設定,然後重新開過個 app 個囉噃。 +\n +\n您確定要不惜一切推倒重來? + 備份與還原 + 唔使喇 + 顛覆所有設定,光復成預設值重新開始 \ No newline at end of file diff --git a/fastlane/metadata/android/ar/changelogs/997.txt b/fastlane/metadata/android/ar/changelogs/997.txt new file mode 100644 index 000000000..f338558a1 --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/997.txt @@ -0,0 +1,17 @@ +جديد +• إضافة ردود التعليق +• السماح بإعادة ترتيب قوائم التشغيل +• عرض وصف قائمة التشغيل ومدتها +• السماح بإعادة ضبط الإعدادات + +تحسين +• [Android 13+] استعادة إجراءات الإشعارات المخصصة +• طلب الموافقة للتحقق من التحديث +• السماح بتشغيل/إيقاف الإشعارات مؤقتًا أثناء التخزين المؤقت +• إعادة ترتيب بعض الإعدادات + +مُثَبَّت +• [YouTube] إصلاح مشكلة عدم تحميل التعليقات، بالإضافة إلى إصلاحات وتحسينات أخرى +• حل مشكلة عدم الحصانة في إعدادات الاستيراد والتحويل إلى JSON +• إصلاحات التنزيل المختلفة +• تقليم نص البحث diff --git a/fastlane/metadata/android/cs/changelogs/997.txt b/fastlane/metadata/android/cs/changelogs/997.txt new file mode 100644 index 000000000..c2fbe35a4 --- /dev/null +++ b/fastlane/metadata/android/cs/changelogs/997.txt @@ -0,0 +1,17 @@ +Nové +• Odpovědi na komentáře +• Změna pořadí playlistů +• Zobrazení popisu a trvání playlistu +• Možnost resetu nastavení + +Vylepšeno +• [Android 13+] Obnovení vlastních akcí oznámení +• Žádost o souhlas pro kontrolu aktualizací +• Povolení přehrání/pozastavení oznámení při načítání +• Změna pořadí některých nastavení + +Opraveno +• [YouTube] Opraveno nenačítání komentářů a další opravy a vylepšení +• Oprava závislosti v importu nastavení a přechod na JSON +• Různé opravy stahování +• Trim vyhledávaného textu diff --git a/fastlane/metadata/android/de/changelogs/990.txt b/fastlane/metadata/android/de/changelogs/990.txt index 6e56a8e70..d3c926d8f 100644 --- a/fastlane/metadata/android/de/changelogs/990.txt +++ b/fastlane/metadata/android/de/changelogs/990.txt @@ -11,3 +11,4 @@ Verbessert: Behoben: • Verschiedene Probleme mit Player-Benachrichtigung: veraltete/fehlende Medieninfos, verzerrte Miniaturansicht +… diff --git a/fastlane/metadata/android/de/changelogs/997.txt b/fastlane/metadata/android/de/changelogs/997.txt new file mode 100644 index 000000000..f55e72b05 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/997.txt @@ -0,0 +1,17 @@ +Neu +• Antwort auf Kommentar +• Wiedergabelisten umordnen +• Wiedergabelisten-Beschreibung und -Dauer +• Rücksetzen der Einstellungen + +Verbessert +• [Android 13+] Wiederherstellen benutzerdef. Benachrichtigungsaktionen +• Zustimmung zur Update-Prüfung +• Während Pufferung Abspielen/Pause über Benachrichtigung +• Neuordnung einiger Einstellungen + +Behoben +• [YouTube] Kommentare wurden nicht geladen, weitere Korrekturen und Verbesserungen +• Sicherheitslücke beim Einstellungsimport und Umstellung auf JSON +• Verschiedene Download-Korrekturen +• Suchtext gekürzt diff --git a/fastlane/metadata/android/es/changelogs/810.txt b/fastlane/metadata/android/es/changelogs/810.txt index 2f569dc5b..caacfec03 100644 --- a/fastlane/metadata/android/es/changelogs/810.txt +++ b/fastlane/metadata/android/es/changelogs/810.txt @@ -8,12 +8,12 @@ Mejorado - Añadir una miniatura ficticia para listas de reproducción locales vacías - Usar la extensión de archivos *.opus en lugar de *.webm y mostrar "opus" en etiqueta de formato en lugar de "WebM Opus" en menú desplegable de descargas - Añadir un botón para eliminar archivos descargados o el historial de descargas en "Descargas" -- YouTube] Añadir soporte a los enlaces de canal /c/shortened_url +- [YouTube] Añadir soporte a los enlaces de canal /c/shortened_url Corregidos - Corregidos múltiples problemas al compartir un video a NewPipe y al descargar sus secuencias directamente - Corregido el acceso al reproductor fuera de su hilo de creación - Corregida la paginación de resultados de búsqueda -- YouTube] Corregido el cambio a nulo que causaba NPE -- YouTube] Corregida la visualización de comentarios al abrir una url de invidio.us -- SoundCloud] Actualizado client_id +- [YouTube] Corregido el cambio a nulo que causaba NPE +- [YouTube] Corregida la visualización de comentarios al abrir una url de invidio.us +- [SoundCloud] Actualizado client_id diff --git a/fastlane/metadata/android/es/changelogs/961.txt b/fastlane/metadata/android/es/changelogs/961.txt index ce3a21b58..56230f940 100644 --- a/fastlane/metadata/android/es/changelogs/961.txt +++ b/fastlane/metadata/android/es/changelogs/961.txt @@ -8,5 +8,5 @@ - Se ha corregido la posibilidad de compartir vídeos desde el reproductor - Corregida la vista web de ReCaptcha en blanco - Corregido el fallo que se producía al eliminar un stream de una lista -- PeerTube] Corregidos los flujos relacionados -- YouTube] Corregida la búsqueda de música en YouTube +- [PeerTube] Corregidos los flujos relacionados +- [YouTube] Corregida la búsqueda de música en YouTube diff --git a/fastlane/metadata/android/es/changelogs/977.txt b/fastlane/metadata/android/es/changelogs/977.txt index d5b4dad2a..a8869fbcb 100644 --- a/fastlane/metadata/android/es/changelogs/977.txt +++ b/fastlane/metadata/android/es/changelogs/977.txt @@ -1,5 +1,5 @@ • Añade botón de "reproducir siguiente" al menú presionado -• Añadido prefijo de ruta de Youtube shorts +• Añadido prefijo de ruta de YouTube Shorts • Corregida importación de ajustes • Intercambio entre barra y botones en pantalla de cola • Correcciones relacionadas con MediasessionManager diff --git a/fastlane/metadata/android/es/changelogs/997.txt b/fastlane/metadata/android/es/changelogs/997.txt new file mode 100644 index 000000000..ee9a61d8c --- /dev/null +++ b/fastlane/metadata/android/es/changelogs/997.txt @@ -0,0 +1,17 @@ +Nuevo +• Añadir respuestas a comentarios +• Reordenar listas de reproducción +• Mostrar descripción y duración de listas de reproducción +• Restaurar ajustes + +Mejorado +• [Android 13+] acciones de notificación personalizadas +• Permiso para comprobar actualizaciones +• Reproducir/pausar notificaciones al almacenar en búfer +• Algunos ajustes + +Corregido +• [YouTube] Corregir comentarios que no se cargan y mejoras +• Fallo al importar de ajustes y JSON +• Corrección de varias descargas +• texto de búsqueda diff --git a/fastlane/metadata/android/hi/changelogs/997.txt b/fastlane/metadata/android/hi/changelogs/997.txt new file mode 100644 index 000000000..652935884 --- /dev/null +++ b/fastlane/metadata/android/hi/changelogs/997.txt @@ -0,0 +1,17 @@ +नया +• टिप्पणियों के उत्तर जोड़ें +• प्लेलिस्ट को पुन: व्यवस्थित करने की अनुमति दें +• प्लेलिस्ट विवरण और अवधि दिखाएं +• सेटिंग्स रीसेट करने की अनुमति दें + +सुधार +• [एंड्रॉइड 13+] कस्टम अधिसूचना क्रियाएं पुनर्स्थापित करें +• अपडेटस की जांच के लिए सहमति का अनुरोध करें +• बफ़रिंग के दौरान नोटीफिकेशन से चलाने/रोकने की अनुमति दें +• कुछ सेटिंग्स पुनः व्यवस्थित करें + +ठीक किए +• [यूट्यूब] लोड न होने वाली टिप्पणियों को ठीक करें, साथ ही अन्य फिक्स और सुधार भी +• सेटिंग्स आयात में भेद्यता को हल करें और JSON पर स्विच करें +• विभिन्न डाउनलोड सुधार +• खोज पाठ को ट्रिम करें diff --git a/fastlane/metadata/android/id/changelogs/997.txt b/fastlane/metadata/android/id/changelogs/997.txt new file mode 100644 index 000000000..7895e84e3 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/997.txt @@ -0,0 +1,15 @@ +Baru +• Tambah balasan komentar +• Izinkan susunan ulg playlist +• Tampilkan deskripsi & durasi daftar putar +• Izinkan setelan ulg +• [Android 13+] Pulihkan tindakan notifikasi khusus +• Minta persetujuan utk pemeriksaan update +• Izinkan pemutaran notifikasi sementara buffering +• Susun ulg bbrp setelan + +Perbaikan +• [YouTube] Perbaiki komentar yg tdk dapat dimuat, ditambah perbaikan & peningkatan +• Atasi kerentanan pengimporan pengaturan & ubah ke JSON +• Perbaikan unduhan +• Pangkas teks pencarian diff --git a/fastlane/metadata/android/it/changelogs/997.txt b/fastlane/metadata/android/it/changelogs/997.txt new file mode 100644 index 000000000..f88855128 --- /dev/null +++ b/fastlane/metadata/android/it/changelogs/997.txt @@ -0,0 +1,17 @@ +Novità +• Risposte ai commenti +• Riordinamento playlist +• Mostra descrizione e durata playlist +• Consenti ripristino impostazioni + +Migliorie +• [Android 13+] Ripristina azioni di notifica personalizzate +• Richiedi consenso per ricerca aggiornamenti +• Notifiche avvio/pausa durante il buffer +• Riordinate alcune impostazioni + +Correzioni +• [YouTube] I commenti non si caricavano, altre correzioni +• Vulnerabilità in importa impostazioni e passaggio a JSON +• Correzioni sui download +• Trim delle ricerche diff --git a/fastlane/metadata/android/kn-IN/changelogs/830.txt b/fastlane/metadata/android/kn-IN/changelogs/830.txt new file mode 100644 index 000000000..266c384e2 --- /dev/null +++ b/fastlane/metadata/android/kn-IN/changelogs/830.txt @@ -0,0 +1 @@ +SoundCloud ಸಮಸ್ಯೆಗಳನ್ನು ಸರಿಪಡಿಸಲು SoundCloud client_id ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ. diff --git a/fastlane/metadata/android/kn-IN/changelogs/850.txt b/fastlane/metadata/android/kn-IN/changelogs/850.txt new file mode 100644 index 000000000..ca21e7472 --- /dev/null +++ b/fastlane/metadata/android/kn-IN/changelogs/850.txt @@ -0,0 +1 @@ +ಈ ಬಿಡುಗಡೆಯ ಯೂಟ್ಯೂಬ್ ವೆಬ್‌ಸೈಟ್ ಆವೃತ್ತಿಯನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ. ಹಳೆಯ ವೆಬ್‌ಸೈಟ್ ಆವೃತ್ತಿಯು ಮಾರ್ಚ್‌ನಲ್ಲಿ ಸ್ಥಗಿತಗೊಳ್ಳಲಿದೆ ಮತ್ತು ಆದ್ದರಿಂದ ನೀವು ನ್ಯೂಪೈಪ್ ಅನ್ನು ಅಪ್‌ಗ್ರೇಡ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ. diff --git a/fastlane/metadata/android/kn-IN/full_description.txt b/fastlane/metadata/android/kn-IN/full_description.txt new file mode 100644 index 000000000..58a5e2550 --- /dev/null +++ b/fastlane/metadata/android/kn-IN/full_description.txt @@ -0,0 +1 @@ +ನ್ಯೂಪೈಪ್ ಯಾವುದೇ ಗೂಗಲ್ ಫ್ರೇಮ್‌ವರ್ಕ್ ಲೈಬ್ರರಿಗಳನ್ನು ಅಥವಾ ಯೂಟ್ಯೂಬ್‌ ಏಪಿಐ ಅನ್ನು ಬಳಸುವುದಿಲ್ಲ. ಇದು ಅಗತ್ಯವಿರುವ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲು ವೆಬ್‌ಸೈಟ್ ಅನ್ನು ಮಾತ್ರ ಪಾರ್ಸ್ ಮಾಡುತ್ತದೆ. ಆದ್ದರಿಂದ ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಗೂಗಲ್ ಸೇವೆಗಳನ್ನು ಸ್ಥಾಪಿಸದೆಯೇ ಸಾಧನಗಳಲ್ಲಿ ಬಳಸಬಹುದು. ಅಲ್ಲದೆ, ನ್ಯೂಪೈಪ್ ಅನ್ನು ಬಳಸಲು ನಿಮಗೆ ಯೂಟ್ಯೂಬ್ ಖಾತೆಯ ಅಗತ್ಯವಿಲ್ಲ ಮತ್ತು ಅದು FLOSS ಆಗಿದೆ. diff --git a/fastlane/metadata/android/kn-IN/short_description.txt b/fastlane/metadata/android/kn-IN/short_description.txt new file mode 100644 index 000000000..e8caefb88 --- /dev/null +++ b/fastlane/metadata/android/kn-IN/short_description.txt @@ -0,0 +1 @@ +ಆಂಡ್ರಾಯ್ಡ್‌ಗಾಗಿ ತಯಾರಿಸಿರುವ ಯೂಟ್ಯೂಬ್ ಆಧಾರಿತ ಉಚಿತ ಕಿರುತಂತ್ರಾಂಶ. diff --git a/fastlane/metadata/android/pa/changelogs/997.txt b/fastlane/metadata/android/pa/changelogs/997.txt new file mode 100644 index 000000000..8e0cb85f2 --- /dev/null +++ b/fastlane/metadata/android/pa/changelogs/997.txt @@ -0,0 +1,17 @@ +ਨਵਾਂ +• ਟਿੱਪਣੀਆਂ ਦੇ ਜਵਾਬ ਸ਼ਾਮਿਲ ਕਰੋ +• ਪਲੇਲਿਸਟਸ ਨੂੰ ਮੁੜ ਕ੍ਰਮਬੱਧ ਕਰਨ ਦਿਓ +• ਪਲੇਲਿਸਟ ਵਰਣਨ ਅਤੇ ਮਿਆਦ ਦਿਖਾਓ +• ਸੈਟਿੰਗਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨ ਦਿਓ + +ਸੁਧਾਰ +• [ਐਂਡਰੌਇਡ 13+] ਕਸਟਮ ਨੋਟੀਫਿਕੇਸ਼ਨ ਕਿਰਿਆਵਾਂ ਨੂੰ ਮੁੜ ਲਾਗੂ ਕਰੋ +• ਅੱਪਡੇਟ ਜਾਂਚ ਲਈ ਸਹਿਮਤੀ ਦੀ ਬੇਨਤੀ ਕਰੋ +• ਬਫਰਿੰਗ ਦੌਰਾਨ ਨੋਟੀਫਿਕੇਸ਼ਨ ਤੋਂ ਚਲਾਉਣ/ਰੋਕਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ +• ਕੁਝ ਸੈਟਿੰਗਾਂ ਨੂੰ ਮੁੜ ਕ੍ਰਮਬੱਧ ਕਰੋ + +ਠੀਕ ਕੀਤਾ +• [ਯੂਟਿਊਬ] ਲੋਡ ਨਾ ਹੋਣ ਵਾਲੀਆਂ ਟਿੱਪਣੀਆਂ ਨੂੰ ਠੀਕ ਕਰੋ, ਨਾਲ ਹੀ ਹੋਰ ਫਿਕਸ ਅਤੇ ਸੁਧਾਰ +• ਸੈਟਿੰਗਾਂ ਆਯਾਤ ਕਰਨ ਵਿੱਚ ਕਮਜ਼ੋਰੀ ਨੂੰ ਹੱਲ ਕਰੋ ਅਤੇ JSON 'ਤੇ ਸਵਿੱਚ ਕਰੋ +• ਕਈ ਡਾਊਨਲੋਡ ਫਿਕਸ +• ਖੋਜ ਟੈਕਸਟ ਨੂੰ ਟਰਿੱਮ ਕਰੋ diff --git a/fastlane/metadata/android/pl/changelogs/997.txt b/fastlane/metadata/android/pl/changelogs/997.txt new file mode 100644 index 000000000..24a300ec9 --- /dev/null +++ b/fastlane/metadata/android/pl/changelogs/997.txt @@ -0,0 +1,17 @@ +Nowe +• Odpowiadanie na komentarze +• Zmiana kolejności playlist +• Pokazyw. opisu i czasu trwania playlist +• Reset ustawień + +Ulepszone +• [Android 13+] Przywrócono niestand. akcje powiadomień +• Prośba o zgodę na sprawdzanie aktualizacji +• Odtw./wstrzym. powiadomień podczas bufor. +• Zmiana kolejność niektórych ustawień + +Naprawione +• [YouTube] Nieładujące się komentarze i inne poprawki i ulepszenia +• Luka w imporcie ustawień i przełączaniu na JSON +• Różne poprawki pobierania +• Przycięto wyszuk. tekst diff --git a/fastlane/metadata/android/pt-PT/changelogs/996.txt b/fastlane/metadata/android/pt-PT/changelogs/996.txt new file mode 100644 index 000000000..230e76b54 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/996.txt @@ -0,0 +1,2 @@ +Um NullPointerException foi corrigido ao abrir um canal / conferência em media.ccc.de. +O Grinch tentou estragar o nosso presente de Natal para si, mas corrigimo-lo. diff --git a/fastlane/metadata/android/pt-PT/changelogs/997.txt b/fastlane/metadata/android/pt-PT/changelogs/997.txt new file mode 100644 index 000000000..3e9da2ade --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/997.txt @@ -0,0 +1,16 @@ +Novo +• Adicionar respostas de comentários +• Permitir reordenar listas de reprodução +... + +Melhorado +• [Android 13+] Restaurar ações de notificação personalizadas +• Solicitar consentimento para verificação de atualização +• Permitir reprodução/pausa de notificação durante o buffer +... + +Fixado +• [Tube] Corrigir comentários que não carregam, além de outras correções e melhorias +• Resolver vulnerabilidade nas configurações importação e mudar para JSON +• Várias correções de descargas +... diff --git a/fastlane/metadata/android/pt/changelogs/997.txt b/fastlane/metadata/android/pt/changelogs/997.txt new file mode 100644 index 000000000..3e9da2ade --- /dev/null +++ b/fastlane/metadata/android/pt/changelogs/997.txt @@ -0,0 +1,16 @@ +Novo +• Adicionar respostas de comentários +• Permitir reordenar listas de reprodução +... + +Melhorado +• [Android 13+] Restaurar ações de notificação personalizadas +• Solicitar consentimento para verificação de atualização +• Permitir reprodução/pausa de notificação durante o buffer +... + +Fixado +• [Tube] Corrigir comentários que não carregam, além de outras correções e melhorias +• Resolver vulnerabilidade nas configurações importação e mudar para JSON +• Várias correções de descargas +... diff --git a/fastlane/metadata/android/sk/changelogs/997.txt b/fastlane/metadata/android/sk/changelogs/997.txt new file mode 100644 index 000000000..98bd604bb --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/997.txt @@ -0,0 +1,17 @@ +Nové +• Pridané odpovede na komentáre +• Umožnená zmena poradia zoznamov skladieb +• Zobrazenie popisu a trvania playlistu +• Umožnené resetovanie nastavení + +Vylepšené +• [Android 13+] Obnovenie vlastných akcií upozornenia +• Vyžiadanie súhlasu na kontrolu aktualizácie +• Povolenie oznámenia prehrávania/pozastavenia počas načítania +• Zmena poradia niektorých nastavení + +Opravené +• [YouTube] Oprava nenačítania komentárov, ďalšie opravy a vylepšenia +• Vyriešenie zraniteľnosti pri importe nastavení a prepnutie na JSON +• Rôzne opravy sťahovania +• Orezanie textu vyhľadávania diff --git a/fastlane/metadata/android/sv/changelogs/997.txt b/fastlane/metadata/android/sv/changelogs/997.txt new file mode 100644 index 000000000..f5c18754d --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/997.txt @@ -0,0 +1,17 @@ +Nytt +• La till kommentars svar +• Tillåt omordnande av spellistor +• Visa spellistors beskrivning och varaktighet +• Tillåt återställning av inställningar + +Förbättrat +• [Android 13+] Återställ anpassade aviserings åtgärder +• Begär samtycke för uppdateringskontroll +• Tillåt spela/pausa från avisering under buffring +• Omordnade några inställningar + +Fixat +• [YouTube] Fixade kommentarer som inte laddas +• Lös sårbarhet i inställnings import och byt till JSON +• Olika hämtnings fixar +• Trimma sök text diff --git a/fastlane/metadata/android/uk/changelogs/997.txt b/fastlane/metadata/android/uk/changelogs/997.txt new file mode 100644 index 000000000..4c591d278 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/997.txt @@ -0,0 +1,17 @@ +Нове +• Додано коментарі відповідей +• Дозвіл перевпор. плейлістів +• Показ опису й тривалості плейлістів +• Дозвіл скидати налаштування + +Удосконалено +• [Android 13+] Відновлено власні дії сповіщень +• Запит згоди на пошук оновлень +• Дозвіл відтвор./паузи зі сповіщень під час буферизації +• Перевпор. деякі налаштування + +Виправлено +• [YouTube] Усунуто незавантаж. коментарів та інші виправлення +• Усунуто вразливість імпорту налаштув. і переходу на JSON +• Виправлення завантажень +• Обрізання тексту пошуку diff --git a/fastlane/metadata/android/vi/changelogs/70.txt b/fastlane/metadata/android/vi/changelogs/70.txt new file mode 100644 index 000000000..7e52b5287 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/70.txt @@ -0,0 +1,25 @@ +LƯU Ý: Phiên bản này có thể là một lỗi nghiêm trọng, giống như phiên bản trước. Tuy nhiên do đã tắt hoàn toàn kể từ ngày 17. một phiên bản bị hỏng còn tốt hơn là không có phiên bản nào. Phải không? ¯\_(ツ)_/¯ + +### Cải tiến +* bây giờ có thể mở các tệp đã tải xuống bằng một cú nhấp chuột #1879 +* bỏ hỗ trợ cho android 4.1 - 4.3 #1884 +* xóa trình phát cũ #1884 +* xóa luồng khỏi hàng phát hiện tại bằng cách vuốt chúng sang phải #1915 +* xóa luồng được xếp hàng tự động khi luồng mới được xếp hàng thủ công #1878 +* Xử lý hậu kỳ để tải xuống và triển khai các tính năng còn thiếu #1759 của @kapodamy +* Cơ sở hạ tầng hậu xử lý +* Xử lý lỗi thích hợp "cơ sở hạ tầng" (dành cho người tải xuống) +* Xếp hàng thay vì tải xuống nhiều lần +* Di chuyển các lượt tải xuống đang chờ xử lý nối tiếp (tệp `.giga`) sang dữ liệu ứng dụng +* Triển khai thử tải xuống tối đa +* Tạm dừng tải xuống đa luồng thích hợp +* Dừng tải xuống khi chuyển sang mạng di động (không bao giờ hoạt động, xem điểm thứ 2) +* Lưu số lượng chủ đề cho lần tải tiếp theo +* Đã sửa rất nhiều lỗi không mạch lạc + +### Đã sửa +* Khắc phục sự cố với độ phân giải mặc định được đặt thành độ phân giải dữ liệu di động tốt nhất và hạn chế #1835 +* Đã sửa lỗi trình phát bật lên #1874 +* NPE khi cố mở trình phát nền #1901 +* Sửa lỗi chèn luồng mới khi bật xếp hàng tự động #1878 +* Đã khắc phục sự cố tắt máy giải mã diff --git a/fastlane/metadata/android/vi/changelogs/985.txt b/fastlane/metadata/android/vi/changelogs/985.txt new file mode 100644 index 000000000..d2086b62c --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/985.txt @@ -0,0 +1 @@ +Đã sửa lỗi YouTube không phát bất kỳ luồng nào diff --git a/fastlane/metadata/android/vi/changelogs/997.txt b/fastlane/metadata/android/vi/changelogs/997.txt new file mode 100644 index 000000000..d143823d3 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/997.txt @@ -0,0 +1,17 @@ +Mới + • Thêm câu trả lời nhận xét + • Cho phép sắp xếp lại danh sách phát + • Hiển thị mô tả và thời lượng danh sách phát + • Cho phép đặt lại cài đặt + + Cải thiện + • [Android 13+] Khôi phục các tác vụ thông báo tùy chỉnh + • Yêu cầu sự đồng ý để kiểm tra cập nhật + • Cho phép phát/tạm dừng thông báo trong khi lưu vào bộ đệm + • Sắp xếp lại một số cài đặt + + Đã sửa + • [YouTube] Khắc phục lỗi không tải được bình luận, cùng với các bản sửa lỗi và cải tiến khác + • Giải quyết lỗ hổng trong cài đặt nhập và chuyển sang JSON + • Nhiều bản sửa lỗi tải xuống khác nhau + • Cắt bớt văn bản tìm kiếm diff --git a/fastlane/metadata/android/zh-Hant/changelogs/997.txt b/fastlane/metadata/android/zh-Hant/changelogs/997.txt new file mode 100644 index 000000000..e5df621ab --- /dev/null +++ b/fastlane/metadata/android/zh-Hant/changelogs/997.txt @@ -0,0 +1,17 @@ +新增 +• 顯示留言回覆 +• 允許重新排序播放清單 +• 顯示播放清單描述與總時長 +• 允許重設所有設定 + +改進 +• [Android 13+] 還原自訂通知動作 +• 更新檢查徵求同意 +• 緩衝時允許自通知中播放/暫停 +• 重新排序部分設定 + +修正 +• [YouTube] 修正留言未能載入,以及其他修正與改進 +• 解決設定匯入的漏洞並改用 JSON +• 若干下載修正 +• 修剪搜尋文字 diff --git a/fastlane/metadata/android/zh_Hant_HK/changelogs/997.txt b/fastlane/metadata/android/zh_Hant_HK/changelogs/997.txt new file mode 100644 index 000000000..009bd31fc --- /dev/null +++ b/fastlane/metadata/android/zh_Hant_HK/changelogs/997.txt @@ -0,0 +1,17 @@ +新嘢 +• 留言睇埋回覆 +• 播放清單有得執排位 +• 騷埋播放清單描述同總片長 +• 設定有得推倒重來 + +進步 +• [Android 13+] 自訂通知動作回歸 +• 徵得同意至睇過有冇更新 +• 緩衝緊通知都照撳得播放/暫停 +• 一啲設定執過位 + +修正 +• [YouTube] 執返掂啲留言 lo 唔到,同埋其他執漏同進步 +• 解決設定匯入漏洞兼轉會用 JSON +• 若干下載執漏 +• 搵嘢飛起頭尾啲空格 From caa3812e13d888f31b23487505462eea85f12e96 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 23 Apr 2024 18:05:31 +0200 Subject: [PATCH 120/123] Ignore all errors when getting free storage space It's not a critical check that needs to be perfomed, so in case something does not work on some device/version, let's just ignore the error. --- .../org/schabi/newpipe/streams/io/StoredDirectoryHelper.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 8dd819293..3f6bf37ea 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 @@ -11,7 +11,6 @@ import android.database.Cursor; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract; -import android.system.ErrnoException; import android.system.Os; import android.system.StructStatVfs; import android.util.Log; @@ -207,7 +206,9 @@ public class StoredDirectoryHelper { // this is the same formula used inside the FsStat class return stat.f_bavail * stat.f_frsize; - } catch (final IOException | ErrnoException e) { + } catch (final Throwable e) { + // ignore any error + Log.e(TAG, "Could not get free storage space", e); return Long.MAX_VALUE; } } From b8daf16b920e90ed98d3a06b4c6ddf30a21171f4 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 23 Apr 2024 18:39:56 +0200 Subject: [PATCH 121/123] Update app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java Co-authored-by: Tobi --- .../org/schabi/newpipe/streams/io/StoredDirectoryHelper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 3f6bf37ea..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 @@ -42,7 +42,10 @@ public class StoredDirectoryHelper { private Path ioTree; private DocumentFile docTree; - // will be `null` for non-SAF files, i.e. files that use `ioTree` + /** + * Context is `null` for non-SAF files, i.e. files that use `ioTree`. + */ + @Nullable private Context context; private final String tag; From 83ca6b9468e34a1bf88df2e61a2f7c4493ca60eb Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 23 Apr 2024 19:25:13 +0200 Subject: [PATCH 122/123] Update NewPipeExtractor to v0.24.0 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1f3d1f759..28a208195 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,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:fbe9e6223aceac8d6f6b352afaed4cb61aed1c79' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.0' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From 9828586762997ef1d0ada61cf8a8cd42c0f18800 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 23 Apr 2024 20:16:04 +0200 Subject: [PATCH 123/123] Fix indentation for ktlint --- .../schabi/newpipe/settings/export/ImportExportManager.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 9a0d842a4..93c1bfb81 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -75,9 +75,9 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { */ fun extractDb(file: StoredFileHelper): Boolean { val success = ZipHelper.extractFileFromZip( - file, - BackupFileLocator.FILE_NAME_DB, - fileLocator.db.path, + file, + BackupFileLocator.FILE_NAME_DB, + fileLocator.db.path, ) if (success) {