mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2026-03-27 18:19:43 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da76ddbf19 | ||
|
|
75ba70ab41 | ||
|
|
6d50fe79b8 | ||
|
|
f8c9ab8b11 | ||
|
|
949f0f3448 | ||
|
|
af08ddcf04 | ||
|
|
09c96159d1 | ||
|
|
2dda39205d | ||
|
|
3112eefb99 | ||
|
|
cf0d7016ec | ||
|
|
b0f2d509e6 | ||
|
|
db61af15e8 | ||
|
|
80a47be218 | ||
|
|
179a713561 | ||
|
|
fa6412a9aa | ||
|
|
3f8d26d33e | ||
|
|
2fec3a3c58 | ||
|
|
08326c64cb | ||
|
|
831425a742 | ||
|
|
fbf3b7a905 | ||
|
|
515bb6e94d | ||
|
|
349000857a | ||
|
|
f1c608b396 | ||
|
|
5f1a270ca4 | ||
|
|
aba2a385fd | ||
|
|
71a3bf2855 | ||
|
|
ebb937934a | ||
|
|
223b240299 | ||
|
|
668af4fc3e | ||
|
|
bfcc31ec89 | ||
|
|
6fa97e17f5 | ||
|
|
0d65733e53 | ||
|
|
9cc6f9fd68 | ||
|
|
79767f95f7 | ||
|
|
d5f941ff3d | ||
|
|
05f09c94d1 | ||
|
|
47624a575a | ||
|
|
3b3348e7a1 | ||
|
|
521f60af85 | ||
|
|
dda219a9e9 | ||
|
|
b8ec9bf412 | ||
|
|
e173bf4252 | ||
|
|
9f45aa571c | ||
|
|
0cdf40cd5f | ||
|
|
0020a02a28 | ||
|
|
e358867da8 | ||
|
|
e6daf45c83 |
@@ -11,6 +11,7 @@ plugins {
|
||||
alias(libs.plugins.jetbrains.kotlin.kapt)
|
||||
alias(libs.plugins.google.ksp)
|
||||
alias(libs.plugins.jetbrains.kotlin.parcelize)
|
||||
alias(libs.plugins.jetbrains.kotlinx.serialization)
|
||||
alias(libs.plugins.sonarqube)
|
||||
checkstyle
|
||||
}
|
||||
@@ -246,6 +247,12 @@ dependencies {
|
||||
implementation(libs.google.android.material)
|
||||
implementation(libs.androidx.webkit)
|
||||
|
||||
// Coroutines interop
|
||||
implementation(libs.kotlinx.coroutines.rx3)
|
||||
|
||||
// Kotlinx Serialization
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
/** Third-party libraries **/
|
||||
implementation(libs.livefront.bridge)
|
||||
implementation(libs.evernote.statesaver.core)
|
||||
|
||||
15
app/proguard-rules.pro
vendored
15
app/proguard-rules.pro
vendored
@@ -44,3 +44,18 @@
|
||||
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
|
||||
<fields>;
|
||||
}
|
||||
|
||||
## Keep Kotlinx Serialization classes
|
||||
-keepclassmembers class kotlinx.serialization.json.** {
|
||||
*** Companion;
|
||||
}
|
||||
-keepclasseswithmembers class kotlinx.serialization.json.** {
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
-keep,includedescriptorclasses class org.schabi.newpipe.**$$serializer { *; }
|
||||
-keepclassmembers class org.schabi.newpipe.** {
|
||||
*** Companion;
|
||||
}
|
||||
-keepclasseswithmembers class org.schabi.newpipe.** {
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
@@ -96,14 +96,6 @@
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_about" />
|
||||
|
||||
<service
|
||||
android:name=".local.subscription.services.SubscriptionsImportService"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
|
||||
<service
|
||||
android:name=".local.subscription.services.SubscriptionsExportService"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
|
||||
<service
|
||||
android:name=".local.feed.service.FeedLoadService"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
|
||||
@@ -254,13 +254,6 @@ class AboutActivity : AppCompatActivity() {
|
||||
"ByteHamster",
|
||||
"https://github.com/ByteHamster/SearchPreference",
|
||||
StandardLicenses.MIT
|
||||
),
|
||||
SoftwareComponent(
|
||||
"FreeDroidWarn",
|
||||
"2026",
|
||||
"woheller69",
|
||||
"https://github.com/woheller69/FreeDroidWarn",
|
||||
StandardLicenses.APACHE2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -216,9 +216,9 @@ public abstract class BaseDescriptionFragment extends BaseFragment {
|
||||
|| image.getWidth() != Image.WIDTH_UNKNOWN
|
||||
// if even the resolution level is unknown, ?x? will be shown
|
||||
|| image.getEstimatedResolutionLevel() == Image.ResolutionLevel.UNKNOWN) {
|
||||
urls.append(imageSizeToText(image.getHeight()));
|
||||
urls.append('x');
|
||||
urls.append(imageSizeToText(image.getWidth()));
|
||||
urls.append('x');
|
||||
urls.append(imageSizeToText(image.getHeight()));
|
||||
} else {
|
||||
switch (image.getEstimatedResolutionLevel()) {
|
||||
case LOW -> urls.append(getString(R.string.image_quality_low));
|
||||
|
||||
@@ -1,41 +1,63 @@
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.os.BundleCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.work.Constraints;
|
||||
import androidx.work.ExistingWorkPolicy;
|
||||
import androidx.work.NetworkType;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.OutOfQuotaPolicy;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
import com.livefront.bridge.Bridge;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput;
|
||||
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportWorker;
|
||||
|
||||
public class ImportConfirmationDialog extends DialogFragment {
|
||||
protected Intent resultServiceIntent;
|
||||
private static final String EXTRA_RESULT_SERVICE_INTENT = "extra_result_service_intent";
|
||||
private static final String INPUT = "input";
|
||||
|
||||
public static void show(@NonNull final Fragment fragment,
|
||||
@NonNull final Intent resultServiceIntent) {
|
||||
final ImportConfirmationDialog confirmationDialog = new ImportConfirmationDialog();
|
||||
final Bundle args = new Bundle();
|
||||
args.putParcelable(EXTRA_RESULT_SERVICE_INTENT, resultServiceIntent);
|
||||
confirmationDialog.setArguments(args);
|
||||
public static void show(@NonNull final Fragment fragment, final SubscriptionImportInput input) {
|
||||
final var confirmationDialog = new ImportConfirmationDialog();
|
||||
final var arguments = new Bundle();
|
||||
arguments.putParcelable(INPUT, input);
|
||||
confirmationDialog.setArguments(arguments);
|
||||
confirmationDialog.show(fragment.getParentFragmentManager(), null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
|
||||
return new AlertDialog.Builder(requireContext())
|
||||
final var context = requireContext();
|
||||
return new AlertDialog.Builder(context)
|
||||
.setMessage(R.string.import_network_expensive_warning)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
requireContext().startService(resultServiceIntent);
|
||||
final var constraints = new Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build();
|
||||
final var input = BundleCompat.getParcelable(requireArguments(), INPUT,
|
||||
SubscriptionImportInput.class);
|
||||
|
||||
final var req = new OneTimeWorkRequest.Builder(SubscriptionImportWorker.class)
|
||||
.setInputData(input.toData())
|
||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||
.setConstraints(constraints)
|
||||
.build();
|
||||
|
||||
WorkManager.getInstance(context)
|
||||
.enqueueUniqueWork(SubscriptionImportWorker.WORK_NAME,
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE, req);
|
||||
|
||||
dismiss();
|
||||
})
|
||||
.create();
|
||||
@@ -45,7 +67,7 @@ public class ImportConfirmationDialog extends DialogFragment {
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
resultServiceIntent = requireArguments().getParcelable(EXTRA_RESULT_SERVICE_INTENT);
|
||||
Bridge.restoreInstanceState(this, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package org.schabi.newpipe.local.subscription
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
@@ -15,8 +13,6 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
@@ -27,9 +23,6 @@ import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.Section
|
||||
import com.xwray.groupie.viewbinding.GroupieViewHolder
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.GROUP_ALL_ID
|
||||
import org.schabi.newpipe.databinding.DialogTitleBinding
|
||||
@@ -53,13 +46,6 @@ import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem
|
||||
import org.schabi.newpipe.local.subscription.item.GroupsHeader
|
||||
import org.schabi.newpipe.local.subscription.item.Header
|
||||
import org.schabi.newpipe.local.subscription.item.ImportSubscriptionsHintPlaceholderItem
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE
|
||||
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.OnClickGesture
|
||||
import org.schabi.newpipe.util.ServiceHelper
|
||||
@@ -72,6 +58,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
|
||||
private lateinit var viewModel: SubscriptionViewModel
|
||||
private lateinit var subscriptionManager: SubscriptionManager
|
||||
private lateinit var importExportHelper: SubscriptionsImportExportHelper
|
||||
private val disposables: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
private val groupAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>()
|
||||
@@ -80,11 +67,6 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
private lateinit var feedGroupsSortMenuItem: GroupsHeader
|
||||
private val subscriptionsSection = Section()
|
||||
|
||||
private val requestExportLauncher =
|
||||
registerForActivityResult(StartActivityForResult(), this::requestExportResult)
|
||||
private val requestImportLauncher =
|
||||
registerForActivityResult(StartActivityForResult(), this::requestImportResult)
|
||||
|
||||
@State
|
||||
@JvmField
|
||||
var itemsListState: Parcelable? = null
|
||||
@@ -104,6 +86,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
subscriptionManager = SubscriptionManager(requireContext())
|
||||
importExportHelper = SubscriptionsImportExportHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
@@ -143,7 +126,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
// -- Import --
|
||||
val importSubMenu = menu.addSubMenu(R.string.import_from)
|
||||
|
||||
addMenuItemToSubmenu(importSubMenu, R.string.previous_export) { onImportPreviousSelected() }
|
||||
addMenuItemToSubmenu(importSubMenu, R.string.previous_export) { importExportHelper.onImportPreviousSelected() }
|
||||
.setIcon(R.drawable.ic_backup)
|
||||
|
||||
for (service in ServiceList.all()) {
|
||||
@@ -161,7 +144,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
// -- Export --
|
||||
val exportSubMenu = menu.addSubMenu(R.string.export_to)
|
||||
|
||||
addMenuItemToSubmenu(exportSubMenu, R.string.file) { onExportSelected() }
|
||||
addMenuItemToSubmenu(exportSubMenu, R.string.file) { importExportHelper.onExportSelected() }
|
||||
.setIcon(R.drawable.ic_save)
|
||||
}
|
||||
|
||||
@@ -197,51 +180,10 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
NavigationHelper.openSubscriptionsImportFragment(fragmentManager, serviceId)
|
||||
}
|
||||
|
||||
private fun onImportPreviousSelected() {
|
||||
NoFileManagerSafeGuard.launchSafe(
|
||||
requestImportLauncher,
|
||||
StoredFileHelper.getPicker(activity, JSON_MIME_TYPE),
|
||||
TAG,
|
||||
requireContext()
|
||||
)
|
||||
}
|
||||
|
||||
private fun onExportSelected() {
|
||||
val date = SimpleDateFormat("yyyyMMddHHmm", Locale.ENGLISH).format(Date())
|
||||
val exportName = "newpipe_subscriptions_$date.json"
|
||||
|
||||
NoFileManagerSafeGuard.launchSafe(
|
||||
requestExportLauncher,
|
||||
StoredFileHelper.getNewPicker(activity, exportName, JSON_MIME_TYPE, null),
|
||||
TAG,
|
||||
requireContext()
|
||||
)
|
||||
}
|
||||
|
||||
private fun openReorderDialog() {
|
||||
FeedGroupReorderDialog().show(parentFragmentManager, null)
|
||||
}
|
||||
|
||||
private fun requestExportResult(result: ActivityResult) {
|
||||
if (result.data != null && result.resultCode == Activity.RESULT_OK) {
|
||||
activity.startService(
|
||||
Intent(activity, SubscriptionsExportService::class.java)
|
||||
.putExtra(SubscriptionsExportService.KEY_FILE_PATH, result.data?.data)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestImportResult(result: ActivityResult) {
|
||||
if (result.data != null && result.resultCode == Activity.RESULT_OK) {
|
||||
ImportConfirmationDialog.show(
|
||||
this,
|
||||
Intent(activity, SubscriptionsImportService::class.java)
|
||||
.putExtra(KEY_MODE, PREVIOUS_EXPORT_MODE)
|
||||
.putExtra(KEY_VALUE, result.data?.data)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Views
|
||||
// ////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.schabi.newpipe.local.subscription
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Pair
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
@@ -51,23 +50,16 @@ class SubscriptionManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun upsertAll(infoList: List<Pair<ChannelInfo, List<ChannelTabInfo>>>): List<SubscriptionEntity> {
|
||||
val listEntities = subscriptionTable.upsertAll(
|
||||
infoList.map { SubscriptionEntity.from(it.first) }
|
||||
)
|
||||
fun upsertAll(infoList: List<Pair<ChannelInfo, ChannelTabInfo>>) {
|
||||
val listEntities = infoList.map { SubscriptionEntity.from(it.first) }
|
||||
subscriptionTable.upsertAll(listEntities)
|
||||
|
||||
database.runInTransaction {
|
||||
infoList.forEachIndexed { index, info ->
|
||||
info.second.forEach {
|
||||
feedDatabaseManager.upsertAll(
|
||||
listEntities[index].uid,
|
||||
it.relatedItems.filterIsInstance<StreamInfoItem>()
|
||||
)
|
||||
}
|
||||
val streams = info.second.relatedItems.filterIsInstance<StreamInfoItem>()
|
||||
feedDatabaseManager.upsertAll(listEntities[index].uid, streams)
|
||||
}
|
||||
}
|
||||
|
||||
return listEntities
|
||||
}
|
||||
|
||||
fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url)
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.schabi.newpipe.local.subscription
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||
import androidx.fragment.app.Fragment
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionFragment.Companion.JSON_MIME_TYPE
|
||||
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
|
||||
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput
|
||||
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
|
||||
import org.schabi.newpipe.streams.io.StoredFileHelper
|
||||
|
||||
/**
|
||||
* This class has to be created in onAttach() or onCreate().
|
||||
*
|
||||
* It contains registerForActivityResult calls and those
|
||||
* calls are only allowed before a fragment/activity is created.
|
||||
*/
|
||||
class SubscriptionsImportExportHelper(
|
||||
val fragment: Fragment
|
||||
) {
|
||||
val context: Context = fragment.requireContext()
|
||||
|
||||
companion object {
|
||||
val TAG: String =
|
||||
SubscriptionsImportExportHelper::class.java.simpleName + "@" + Integer.toHexString(
|
||||
hashCode()
|
||||
)
|
||||
}
|
||||
|
||||
private val requestExportLauncher =
|
||||
fragment.registerForActivityResult(StartActivityForResult(), this::requestExportResult)
|
||||
private val requestImportLauncher =
|
||||
fragment.registerForActivityResult(StartActivityForResult(), this::requestImportResult)
|
||||
|
||||
private fun requestExportResult(result: ActivityResult) {
|
||||
val data = result.data?.data
|
||||
if (data != null && result.resultCode == Activity.RESULT_OK) {
|
||||
SubscriptionExportWorker.schedule(context, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestImportResult(result: ActivityResult) {
|
||||
val data = result.data?.dataString
|
||||
if (data != null && result.resultCode == Activity.RESULT_OK) {
|
||||
ImportConfirmationDialog.show(
|
||||
fragment,
|
||||
SubscriptionImportInput.PreviousExportMode(data)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onExportSelected() {
|
||||
val date = SimpleDateFormat("yyyyMMddHHmm", Locale.ENGLISH).format(Date())
|
||||
val exportName = "newpipe_subscriptions_$date.json"
|
||||
|
||||
NoFileManagerSafeGuard.launchSafe(
|
||||
requestExportLauncher,
|
||||
StoredFileHelper.getNewPicker(
|
||||
context,
|
||||
exportName,
|
||||
JSON_MIME_TYPE,
|
||||
null
|
||||
),
|
||||
TAG,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
fun onImportPreviousSelected() {
|
||||
NoFileManagerSafeGuard.launchSafe(
|
||||
requestImportLauncher,
|
||||
StoredFileHelper.getPicker(context, JSON_MIME_TYPE),
|
||||
TAG,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
@@ -37,7 +33,7 @@ import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
|
||||
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput;
|
||||
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
|
||||
import org.schabi.newpipe.streams.io.StoredFileHelper;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
@@ -168,10 +164,8 @@ public class SubscriptionsImportFragment extends BaseFragment {
|
||||
}
|
||||
|
||||
public void onImportUrl(final String value) {
|
||||
ImportConfirmationDialog.show(this, new Intent(activity, SubscriptionsImportService.class)
|
||||
.putExtra(KEY_MODE, CHANNEL_URL_MODE)
|
||||
.putExtra(KEY_VALUE, value)
|
||||
.putExtra(Constants.KEY_SERVICE_ID, currentServiceId));
|
||||
ImportConfirmationDialog.show(this,
|
||||
new SubscriptionImportInput.ChannelUrlMode(currentServiceId, value));
|
||||
}
|
||||
|
||||
public void onImportFile() {
|
||||
@@ -186,16 +180,10 @@ public class SubscriptionsImportFragment extends BaseFragment {
|
||||
}
|
||||
|
||||
private void requestImportFileResult(final ActivityResult result) {
|
||||
if (result.getData() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.getResultCode() == Activity.RESULT_OK && result.getData().getData() != null) {
|
||||
final String data = result.getData() != null ? result.getData().getDataString() : null;
|
||||
if (result.getResultCode() == Activity.RESULT_OK && data != null) {
|
||||
ImportConfirmationDialog.show(this,
|
||||
new Intent(activity, SubscriptionsImportService.class)
|
||||
.putExtra(KEY_MODE, INPUT_STREAM_MODE)
|
||||
.putExtra(KEY_VALUE, result.getData().getData())
|
||||
.putExtra(Constants.KEY_SERVICE_ID, currentServiceId));
|
||||
new SubscriptionImportInput.InputStreamMode(currentServiceId, data));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Mauricio Colli <mauriciocolli@outlook.com>
|
||||
* BaseImportExportService.java is part of NewPipe
|
||||
*
|
||||
* License: GPL-3.0+
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.ServiceCompat;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
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.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.processors.PublishProcessor;
|
||||
|
||||
public abstract class BaseImportExportService extends Service {
|
||||
protected final String TAG = this.getClass().getSimpleName();
|
||||
|
||||
protected final CompositeDisposable disposables = new CompositeDisposable();
|
||||
protected final PublishProcessor<String> notificationUpdater = PublishProcessor.create();
|
||||
|
||||
protected NotificationManagerCompat notificationManager;
|
||||
protected NotificationCompat.Builder notificationBuilder;
|
||||
protected SubscriptionManager subscriptionManager;
|
||||
|
||||
private static final int NOTIFICATION_SAMPLING_PERIOD = 2500;
|
||||
|
||||
protected final AtomicInteger currentProgress = new AtomicInteger(-1);
|
||||
protected final AtomicInteger maxProgress = new AtomicInteger(-1);
|
||||
protected final ImportExportEventListener eventListener = new ImportExportEventListener() {
|
||||
@Override
|
||||
public void onSizeReceived(final int size) {
|
||||
maxProgress.set(size);
|
||||
currentProgress.set(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemCompleted(final String itemName) {
|
||||
currentProgress.incrementAndGet();
|
||||
notificationUpdater.onNext(itemName);
|
||||
}
|
||||
};
|
||||
|
||||
protected Toast toast;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(final Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
subscriptionManager = new SubscriptionManager(this);
|
||||
setupNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
disposeAll();
|
||||
}
|
||||
|
||||
protected void disposeAll() {
|
||||
disposables.clear();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Notification Impl
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected abstract int getNotificationId();
|
||||
|
||||
@StringRes
|
||||
public abstract int getTitle();
|
||||
|
||||
protected void setupNotification() {
|
||||
notificationManager = NotificationManagerCompat.from(this);
|
||||
notificationBuilder = createNotification();
|
||||
startForeground(getNotificationId(), notificationBuilder.build());
|
||||
|
||||
final Function<Flowable<String>, Publisher<String>> throttleAfterFirstEmission = flow ->
|
||||
flow.take(1).concatWith(flow.skip(1)
|
||||
.throttleLast(NOTIFICATION_SAMPLING_PERIOD, TimeUnit.MILLISECONDS));
|
||||
|
||||
disposables.add(notificationUpdater
|
||||
.filter(s -> !s.isEmpty())
|
||||
.publish(throttleAfterFirstEmission)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::updateNotification));
|
||||
}
|
||||
|
||||
protected void updateNotification(final String text) {
|
||||
notificationBuilder
|
||||
.setProgress(maxProgress.get(), currentProgress.get(), maxProgress.get() == -1);
|
||||
|
||||
final String progressText = currentProgress + "/" + maxProgress;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
notificationBuilder.setContentText(text + " (" + progressText + ")");
|
||||
}
|
||||
} else {
|
||||
notificationBuilder.setContentInfo(progressText);
|
||||
notificationBuilder.setContentText(text);
|
||||
}
|
||||
|
||||
if (notificationManager.areNotificationsEnabled()) {
|
||||
notificationManager.notify(getNotificationId(), notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected void stopService() {
|
||||
postErrorResult(null, null);
|
||||
}
|
||||
|
||||
protected void stopAndReportError(final Throwable throwable, final String request) {
|
||||
stopService();
|
||||
ErrorUtil.createNotification(this, new ErrorInfo(
|
||||
throwable, UserAction.SUBSCRIPTION_IMPORT_EXPORT, request));
|
||||
}
|
||||
|
||||
protected void postErrorResult(final String title, final String text) {
|
||||
disposeAll();
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE);
|
||||
stopSelf();
|
||||
|
||||
if (title == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String textOrEmpty = text == null ? "" : text;
|
||||
notificationBuilder = new NotificationCompat
|
||||
.Builder(this, getString(R.string.notification_channel_id))
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContentTitle(title)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(textOrEmpty))
|
||||
.setContentText(textOrEmpty);
|
||||
|
||||
if (notificationManager.areNotificationsEnabled()) {
|
||||
notificationManager.notify(getNotificationId(), notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected NotificationCompat.Builder createNotification() {
|
||||
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
|
||||
.setOngoing(true)
|
||||
.setProgress(-1, -1, true)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContentTitle(getString(getTitle()));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Toast
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void showToast(@StringRes final int message) {
|
||||
showToast(getString(message));
|
||||
}
|
||||
|
||||
protected void showToast(final String message) {
|
||||
if (toast != null) {
|
||||
toast.cancel();
|
||||
}
|
||||
|
||||
toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Error handling
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void handleError(@StringRes final int errorTitle, @NonNull final Throwable error) {
|
||||
String message = getErrorMessage(error);
|
||||
|
||||
if (TextUtils.isEmpty(message)) {
|
||||
final String errorClassName = error.getClass().getName();
|
||||
message = getString(R.string.error_occurred_detail, errorClassName);
|
||||
}
|
||||
|
||||
showToast(errorTitle);
|
||||
postErrorResult(getString(errorTitle), message);
|
||||
}
|
||||
|
||||
protected String getErrorMessage(final Throwable error) {
|
||||
String message = null;
|
||||
if (error instanceof SubscriptionExtractor.InvalidSourceException) {
|
||||
message = getString(R.string.invalid_source);
|
||||
} else if (error instanceof FileNotFoundException) {
|
||||
message = getString(R.string.invalid_file);
|
||||
} else if (ExceptionUtils.isNetworkRelated(error)) {
|
||||
message = getString(R.string.network_error);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
public interface ImportExportEventListener {
|
||||
/**
|
||||
* Called when the size has been resolved.
|
||||
*
|
||||
* @param size how many items there are to import/export
|
||||
*/
|
||||
void onSizeReceived(int size);
|
||||
|
||||
/**
|
||||
* Called every time an item has been parsed/resolved.
|
||||
*
|
||||
* @param itemName the name of the subscription item
|
||||
*/
|
||||
void onItemCompleted(String itemName);
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Mauricio Colli <mauriciocolli@outlook.com>
|
||||
* ImportExportJsonHelper.java is part of NewPipe
|
||||
*
|
||||
* License: GPL-3.0+
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.grack.nanojson.JsonAppendableWriter;
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonWriter;
|
||||
|
||||
import org.schabi.newpipe.BuildConfig;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.InvalidSourceException;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A JSON implementation capable of importing and exporting subscriptions, it has the advantage
|
||||
* of being able to transfer subscriptions to any device.
|
||||
*/
|
||||
public final class ImportExportJsonHelper {
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Json implementation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private static final String JSON_APP_VERSION_KEY = "app_version";
|
||||
private static final String JSON_APP_VERSION_INT_KEY = "app_version_int";
|
||||
|
||||
private static final String JSON_SUBSCRIPTIONS_ARRAY_KEY = "subscriptions";
|
||||
|
||||
private static final String JSON_SERVICE_ID_KEY = "service_id";
|
||||
private static final String JSON_URL_KEY = "url";
|
||||
private static final String JSON_NAME_KEY = "name";
|
||||
|
||||
private ImportExportJsonHelper() { }
|
||||
|
||||
/**
|
||||
* Read a JSON source through the input stream.
|
||||
*
|
||||
* @param in the input stream (e.g. a file)
|
||||
* @param eventListener listener for the events generated
|
||||
* @return the parsed subscription items
|
||||
*/
|
||||
public static List<SubscriptionItem> readFrom(
|
||||
final InputStream in, @Nullable final ImportExportEventListener eventListener)
|
||||
throws InvalidSourceException {
|
||||
if (in == null) {
|
||||
throw new InvalidSourceException("input is null");
|
||||
}
|
||||
|
||||
final List<SubscriptionItem> channels = new ArrayList<>();
|
||||
|
||||
try {
|
||||
final JsonObject parentObject = JsonParser.object().from(in);
|
||||
|
||||
if (!parentObject.has(JSON_SUBSCRIPTIONS_ARRAY_KEY)) {
|
||||
throw new InvalidSourceException("Channels array is null");
|
||||
}
|
||||
|
||||
final JsonArray channelsArray = parentObject.getArray(JSON_SUBSCRIPTIONS_ARRAY_KEY);
|
||||
|
||||
if (eventListener != null) {
|
||||
eventListener.onSizeReceived(channelsArray.size());
|
||||
}
|
||||
|
||||
for (final Object o : channelsArray) {
|
||||
if (o instanceof JsonObject) {
|
||||
final JsonObject itemObject = (JsonObject) o;
|
||||
final int serviceId = itemObject.getInt(JSON_SERVICE_ID_KEY, 0);
|
||||
final String url = itemObject.getString(JSON_URL_KEY);
|
||||
final String name = itemObject.getString(JSON_NAME_KEY);
|
||||
|
||||
if (url != null && name != null && !url.isEmpty() && !name.isEmpty()) {
|
||||
channels.add(new SubscriptionItem(serviceId, url, name));
|
||||
if (eventListener != null) {
|
||||
eventListener.onItemCompleted(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final Throwable e) {
|
||||
throw new InvalidSourceException("Couldn't parse json", e);
|
||||
}
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the subscriptions items list as JSON to the output.
|
||||
*
|
||||
* @param items the list of subscriptions items
|
||||
* @param out the output stream (e.g. a file)
|
||||
* @param eventListener listener for the events generated
|
||||
*/
|
||||
public static void writeTo(final List<SubscriptionItem> items, final OutputStream out,
|
||||
@Nullable final ImportExportEventListener eventListener) {
|
||||
final JsonAppendableWriter writer = JsonWriter.on(out);
|
||||
writeTo(items, writer, eventListener);
|
||||
writer.done();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #writeTo(List, OutputStream, ImportExportEventListener)
|
||||
* @param items the list of subscriptions items
|
||||
* @param writer the output {@link JsonAppendableWriter}
|
||||
* @param eventListener listener for the events generated
|
||||
*/
|
||||
public static void writeTo(final List<SubscriptionItem> items,
|
||||
final JsonAppendableWriter writer,
|
||||
@Nullable final ImportExportEventListener eventListener) {
|
||||
if (eventListener != null) {
|
||||
eventListener.onSizeReceived(items.size());
|
||||
}
|
||||
|
||||
writer.object();
|
||||
|
||||
writer.value(JSON_APP_VERSION_KEY, BuildConfig.VERSION_NAME);
|
||||
writer.value(JSON_APP_VERSION_INT_KEY, BuildConfig.VERSION_CODE);
|
||||
|
||||
writer.array(JSON_SUBSCRIPTIONS_ARRAY_KEY);
|
||||
for (final SubscriptionItem item : items) {
|
||||
writer.object();
|
||||
writer.value(JSON_SERVICE_ID_KEY, item.getServiceId());
|
||||
writer.value(JSON_URL_KEY, item.getUrl());
|
||||
writer.value(JSON_NAME_KEY, item.getName());
|
||||
writer.end();
|
||||
|
||||
if (eventListener != null) {
|
||||
eventListener.onItemCompleted(item.getName());
|
||||
}
|
||||
}
|
||||
writer.end();
|
||||
|
||||
writer.end();
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Mauricio Colli <mauriciocolli@outlook.com>
|
||||
* SubscriptionsExportService.java is part of NewPipe
|
||||
*
|
||||
* License: GPL-3.0+
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.IntentCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.App;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
|
||||
import org.schabi.newpipe.streams.io.SharpOutputStream;
|
||||
import org.schabi.newpipe.streams.io.StoredFileHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class SubscriptionsExportService extends BaseImportExportService {
|
||||
public static final String KEY_FILE_PATH = "key_file_path";
|
||||
|
||||
/**
|
||||
* A {@link LocalBroadcastManager local broadcast} will be made with this action
|
||||
* when the export is successfully completed.
|
||||
*/
|
||||
public static final String EXPORT_COMPLETE_ACTION = App.PACKAGE_NAME + ".local.subscription"
|
||||
+ ".services.SubscriptionsExportService.EXPORT_COMPLETE";
|
||||
|
||||
private Subscription subscription;
|
||||
private StoredFileHelper outFile;
|
||||
private OutputStream outputStream;
|
||||
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
if (intent == null || subscription != null) {
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
final Uri path = IntentCompat.getParcelableExtra(intent, KEY_FILE_PATH, Uri.class);
|
||||
if (path == null) {
|
||||
stopAndReportError(new IllegalStateException(
|
||||
"Exporting to a file, but the path is null"),
|
||||
"Exporting subscriptions");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
try {
|
||||
outFile = new StoredFileHelper(this, path, "application/json");
|
||||
// truncate the file before writing to it, otherwise if the new content is smaller than
|
||||
// the previous file size, the file will retain part of the previous content and be
|
||||
// corrupted
|
||||
outputStream = new SharpOutputStream(outFile.openAndTruncateStream());
|
||||
} catch (final IOException e) {
|
||||
handleError(e);
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
startExport();
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNotificationId() {
|
||||
return 4567;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTitle() {
|
||||
return R.string.export_ongoing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeAll() {
|
||||
super.disposeAll();
|
||||
if (subscription != null) {
|
||||
subscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void startExport() {
|
||||
showToast(R.string.export_ongoing);
|
||||
|
||||
subscriptionManager.subscriptionTable().getAll().take(1)
|
||||
.map(subscriptionEntities -> {
|
||||
final List<SubscriptionItem> result =
|
||||
new ArrayList<>(subscriptionEntities.size());
|
||||
for (final SubscriptionEntity entity : subscriptionEntities) {
|
||||
result.add(new SubscriptionItem(entity.getServiceId(), entity.getUrl(),
|
||||
entity.getName()));
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.map(exportToFile())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getSubscriber());
|
||||
}
|
||||
|
||||
private Subscriber<StoredFileHelper> getSubscriber() {
|
||||
return new Subscriber<StoredFileHelper>() {
|
||||
@Override
|
||||
public void onSubscribe(final Subscription s) {
|
||||
subscription = s;
|
||||
s.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(final StoredFileHelper file) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "startExport() success: file = " + file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Throwable error) {
|
||||
Log.e(TAG, "onError() called with: error = [" + error + "]", error);
|
||||
handleError(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
LocalBroadcastManager.getInstance(SubscriptionsExportService.this)
|
||||
.sendBroadcast(new Intent(EXPORT_COMPLETE_ACTION));
|
||||
showToast(R.string.export_complete_toast);
|
||||
stopService();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Function<List<SubscriptionItem>, StoredFileHelper> exportToFile() {
|
||||
return subscriptionItems -> {
|
||||
ImportExportJsonHelper.writeTo(subscriptionItems, outputStream, eventListener);
|
||||
return outFile;
|
||||
};
|
||||
}
|
||||
|
||||
protected void handleError(final Throwable error) {
|
||||
super.handleError(R.string.subscriptions_export_unsuccessful, error);
|
||||
}
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Mauricio Colli <mauriciocolli@outlook.com>
|
||||
* SubscriptionsImportService.java is part of NewPipe
|
||||
*
|
||||
* License: GPL-3.0+
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||
import static org.schabi.newpipe.streams.io.StoredFileHelper.DEFAULT_MIME;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.IntentCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.App;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabInfo;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
import org.schabi.newpipe.streams.io.SharpInputStream;
|
||||
import org.schabi.newpipe.streams.io.StoredFileHelper;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Notification;
|
||||
import io.reactivex.rxjava3.functions.Consumer;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class SubscriptionsImportService extends BaseImportExportService {
|
||||
public static final int CHANNEL_URL_MODE = 0;
|
||||
public static final int INPUT_STREAM_MODE = 1;
|
||||
public static final int PREVIOUS_EXPORT_MODE = 2;
|
||||
public static final String KEY_MODE = "key_mode";
|
||||
public static final String KEY_VALUE = "key_value";
|
||||
|
||||
/**
|
||||
* A {@link LocalBroadcastManager local broadcast} will be made with this action
|
||||
* when the import is successfully completed.
|
||||
*/
|
||||
public static final String IMPORT_COMPLETE_ACTION = App.PACKAGE_NAME + ".local.subscription"
|
||||
+ ".services.SubscriptionsImportService.IMPORT_COMPLETE";
|
||||
|
||||
/**
|
||||
* How many extractions running in parallel.
|
||||
*/
|
||||
public static final int PARALLEL_EXTRACTIONS = 8;
|
||||
|
||||
/**
|
||||
* Number of items to buffer to mass-insert in the subscriptions table,
|
||||
* this leads to a better performance as we can then use db transactions.
|
||||
*/
|
||||
public static final int BUFFER_COUNT_BEFORE_INSERT = 50;
|
||||
|
||||
private Subscription subscription;
|
||||
private int currentMode;
|
||||
private int currentServiceId;
|
||||
@Nullable
|
||||
private String channelUrl;
|
||||
@Nullable
|
||||
private InputStream inputStream;
|
||||
@Nullable
|
||||
private String inputStreamType;
|
||||
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
if (intent == null || subscription != null) {
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
currentMode = intent.getIntExtra(KEY_MODE, -1);
|
||||
currentServiceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, Constants.NO_SERVICE_ID);
|
||||
|
||||
if (currentMode == CHANNEL_URL_MODE) {
|
||||
channelUrl = intent.getStringExtra(KEY_VALUE);
|
||||
} else {
|
||||
final Uri uri = IntentCompat.getParcelableExtra(intent, KEY_VALUE, Uri.class);
|
||||
if (uri == null) {
|
||||
stopAndReportError(new IllegalStateException(
|
||||
"Importing from input stream, but file path is null"),
|
||||
"Importing subscriptions");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
try {
|
||||
final StoredFileHelper fileHelper = new StoredFileHelper(this, uri, DEFAULT_MIME);
|
||||
inputStream = new SharpInputStream(fileHelper.getStream());
|
||||
inputStreamType = fileHelper.getType();
|
||||
|
||||
if (inputStreamType == null || inputStreamType.equals(DEFAULT_MIME)) {
|
||||
// mime type could not be determined, just take file extension
|
||||
final String name = fileHelper.getName();
|
||||
final int pointIndex = name.lastIndexOf('.');
|
||||
if (pointIndex == -1 || pointIndex >= name.length() - 1) {
|
||||
inputStreamType = DEFAULT_MIME; // no extension, will fail in the extractor
|
||||
} else {
|
||||
inputStreamType = name.substring(pointIndex + 1);
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
handleError(e);
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMode == -1 || currentMode == CHANNEL_URL_MODE && channelUrl == null) {
|
||||
final String errorDescription = "Some important field is null or in illegal state: "
|
||||
+ "currentMode=[" + currentMode + "], "
|
||||
+ "channelUrl=[" + channelUrl + "], "
|
||||
+ "inputStream=[" + inputStream + "]";
|
||||
stopAndReportError(new IllegalStateException(errorDescription),
|
||||
"Importing subscriptions");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
startImport();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNotificationId() {
|
||||
return 4568;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTitle() {
|
||||
return R.string.import_ongoing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeAll() {
|
||||
super.disposeAll();
|
||||
if (subscription != null) {
|
||||
subscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Imports
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void startImport() {
|
||||
showToast(R.string.import_ongoing);
|
||||
|
||||
Flowable<List<SubscriptionItem>> flowable = null;
|
||||
switch (currentMode) {
|
||||
case CHANNEL_URL_MODE:
|
||||
flowable = importFromChannelUrl();
|
||||
break;
|
||||
case INPUT_STREAM_MODE:
|
||||
flowable = importFromInputStream();
|
||||
break;
|
||||
case PREVIOUS_EXPORT_MODE:
|
||||
flowable = importFromPreviousExport();
|
||||
break;
|
||||
}
|
||||
|
||||
if (flowable == null) {
|
||||
final String message = "Flowable given by \"importFrom\" is null "
|
||||
+ "(current mode: " + currentMode + ")";
|
||||
stopAndReportError(new IllegalStateException(message), "Importing subscriptions");
|
||||
return;
|
||||
}
|
||||
|
||||
flowable.doOnNext(subscriptionItems ->
|
||||
eventListener.onSizeReceived(subscriptionItems.size()))
|
||||
.flatMap(Flowable::fromIterable)
|
||||
|
||||
.parallel(PARALLEL_EXTRACTIONS)
|
||||
.runOn(Schedulers.io())
|
||||
.map((Function<SubscriptionItem, Notification<Pair<ChannelInfo,
|
||||
List<ChannelTabInfo>>>>) subscriptionItem -> {
|
||||
try {
|
||||
final ChannelInfo channelInfo = ExtractorHelper
|
||||
.getChannelInfo(subscriptionItem.getServiceId(),
|
||||
subscriptionItem.getUrl(), true)
|
||||
.blockingGet();
|
||||
return Notification.createOnNext(new Pair<>(channelInfo,
|
||||
Collections.singletonList(
|
||||
ExtractorHelper.getChannelTab(
|
||||
subscriptionItem.getServiceId(),
|
||||
channelInfo.getTabs().get(0), true).blockingGet()
|
||||
)));
|
||||
} catch (final Throwable e) {
|
||||
return Notification.createOnError(e);
|
||||
}
|
||||
})
|
||||
.sequential()
|
||||
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnNext(getNotificationsConsumer())
|
||||
|
||||
.buffer(BUFFER_COUNT_BEFORE_INSERT)
|
||||
.map(upsertBatch())
|
||||
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getSubscriber());
|
||||
}
|
||||
|
||||
private Subscriber<List<SubscriptionEntity>> getSubscriber() {
|
||||
return new Subscriber<>() {
|
||||
@Override
|
||||
public void onSubscribe(final Subscription s) {
|
||||
subscription = s;
|
||||
s.request(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(final List<SubscriptionEntity> successfulInserted) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "startImport() " + successfulInserted.size()
|
||||
+ " items successfully inserted into the database");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Throwable error) {
|
||||
Log.e(TAG, "Got an error!", error);
|
||||
handleError(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
LocalBroadcastManager.getInstance(SubscriptionsImportService.this)
|
||||
.sendBroadcast(new Intent(IMPORT_COMPLETE_ACTION));
|
||||
showToast(R.string.import_complete_toast);
|
||||
stopService();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Consumer<Notification<Pair<ChannelInfo,
|
||||
List<ChannelTabInfo>>>> getNotificationsConsumer() {
|
||||
return notification -> {
|
||||
if (notification.isOnNext()) {
|
||||
final String name = notification.getValue().first.getName();
|
||||
eventListener.onItemCompleted(!TextUtils.isEmpty(name) ? name : "");
|
||||
} else if (notification.isOnError()) {
|
||||
final Throwable error = notification.getError();
|
||||
final Throwable cause = error.getCause();
|
||||
if (error instanceof IOException) {
|
||||
throw error;
|
||||
} else if (cause instanceof IOException) {
|
||||
throw cause;
|
||||
} else if (ExceptionUtils.isNetworkRelated(error)) {
|
||||
throw new IOException(error);
|
||||
}
|
||||
|
||||
eventListener.onItemCompleted("");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Function<List<Notification<Pair<ChannelInfo, List<ChannelTabInfo>>>>,
|
||||
List<SubscriptionEntity>> upsertBatch() {
|
||||
return notificationList -> {
|
||||
final List<Pair<ChannelInfo, List<ChannelTabInfo>>> infoList =
|
||||
new ArrayList<>(notificationList.size());
|
||||
for (final Notification<Pair<ChannelInfo, List<ChannelTabInfo>>> n : notificationList) {
|
||||
if (n.isOnNext()) {
|
||||
infoList.add(n.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return subscriptionManager.upsertAll(infoList);
|
||||
};
|
||||
}
|
||||
|
||||
private Flowable<List<SubscriptionItem>> importFromChannelUrl() {
|
||||
return Flowable.fromCallable(() -> NewPipe.getService(currentServiceId)
|
||||
.getSubscriptionExtractor()
|
||||
.fromChannelUrl(channelUrl));
|
||||
}
|
||||
|
||||
private Flowable<List<SubscriptionItem>> importFromInputStream() {
|
||||
Objects.requireNonNull(inputStream);
|
||||
Objects.requireNonNull(inputStreamType);
|
||||
|
||||
return Flowable.fromCallable(() -> NewPipe.getService(currentServiceId)
|
||||
.getSubscriptionExtractor()
|
||||
.fromInputStream(inputStream, inputStreamType));
|
||||
}
|
||||
|
||||
private Flowable<List<SubscriptionItem>> importFromPreviousExport() {
|
||||
return Flowable.fromCallable(() -> ImportExportJsonHelper.readFrom(inputStream, null));
|
||||
}
|
||||
|
||||
protected void handleError(@NonNull final Throwable error) {
|
||||
super.handleError(R.string.subscriptions_import_unsuccessful, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2018 Mauricio Colli <mauriciocolli@outlook.com>
|
||||
* ImportExportJsonHelper.java is part of NewPipe
|
||||
*
|
||||
* License: GPL-3.0+
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.local.subscription.workers
|
||||
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import kotlinx.serialization.json.encodeToStream
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.InvalidSourceException
|
||||
|
||||
/**
|
||||
* A JSON implementation capable of importing and exporting subscriptions, it has the advantage
|
||||
* of being able to transfer subscriptions to any device.
|
||||
*/
|
||||
object ImportExportJsonHelper {
|
||||
private val json = Json { encodeDefaults = true }
|
||||
|
||||
/**
|
||||
* Read a JSON source through the input stream.
|
||||
*
|
||||
* @param in the input stream (e.g. a file)
|
||||
* @return the parsed subscription items
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(InvalidSourceException::class)
|
||||
fun readFrom(`in`: InputStream?): List<SubscriptionItem> {
|
||||
if (`in` == null) {
|
||||
throw InvalidSourceException("input is null")
|
||||
}
|
||||
|
||||
try {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
return json.decodeFromStream<SubscriptionData>(`in`).subscriptions
|
||||
} catch (e: Throwable) {
|
||||
throw InvalidSourceException("Couldn't parse json", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the subscriptions items list as JSON to the output.
|
||||
*
|
||||
* @param items the list of subscriptions items
|
||||
* @param out the output stream (e.g. a file)
|
||||
*/
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JvmStatic
|
||||
fun writeTo(
|
||||
items: List<SubscriptionItem>,
|
||||
out: OutputStream
|
||||
) {
|
||||
json.encodeToStream(SubscriptionData(items), out)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.schabi.newpipe.local.subscription.workers
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.schabi.newpipe.BuildConfig
|
||||
|
||||
@Serializable
|
||||
class SubscriptionData(
|
||||
val subscriptions: List<SubscriptionItem>
|
||||
) {
|
||||
@SerialName("app_version")
|
||||
private val appVersion = BuildConfig.VERSION_NAME
|
||||
|
||||
@SerialName("app_version_int")
|
||||
private val appVersionInt = BuildConfig.VERSION_CODE
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class SubscriptionItem(
|
||||
@SerialName("service_id")
|
||||
val serviceId: Int,
|
||||
val url: String,
|
||||
val name: String
|
||||
)
|
||||
@@ -0,0 +1,119 @@
|
||||
package org.schabi.newpipe.local.subscription.workers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.OutOfQuotaPolicy
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.reactive.awaitFirst
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.schabi.newpipe.BuildConfig
|
||||
import org.schabi.newpipe.NewPipeDatabase
|
||||
import org.schabi.newpipe.R
|
||||
|
||||
class SubscriptionExportWorker(
|
||||
appContext: Context,
|
||||
params: WorkerParameters
|
||||
) : CoroutineWorker(appContext, params) {
|
||||
// This is needed for API levels < 31 (Android S).
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
return createForegroundInfo(applicationContext.getString(R.string.export_ongoing))
|
||||
}
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
return try {
|
||||
val uri = inputData.getString(EXPORT_PATH)!!.toUri()
|
||||
val table = NewPipeDatabase.getInstance(applicationContext).subscriptionDAO()
|
||||
val subscriptions =
|
||||
table.getAll()
|
||||
.awaitFirst()
|
||||
.map { SubscriptionItem(it.serviceId, it.url ?: "", it.name ?: "") }
|
||||
|
||||
val qty = subscriptions.size
|
||||
val title = applicationContext.resources.getQuantityString(R.plurals.export_subscriptions, qty, qty)
|
||||
setForeground(createForegroundInfo(title))
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
// Truncate file if it already exists
|
||||
applicationContext.contentResolver.openOutputStream(uri, "wt")?.use {
|
||||
ImportExportJsonHelper.writeTo(subscriptions, it)
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.i(TAG, "Exported $qty subscriptions")
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast
|
||||
.makeText(applicationContext, R.string.export_complete_toast, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error while exporting subscriptions", e)
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast
|
||||
.makeText(applicationContext, R.string.subscriptions_export_unsuccessful, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
return Result.failure()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createForegroundInfo(title: String): ForegroundInfo {
|
||||
val notification =
|
||||
NotificationCompat
|
||||
.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setOngoing(true)
|
||||
.setProgress(-1, -1, true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
.setContentTitle(title)
|
||||
.build()
|
||||
val serviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0
|
||||
return ForegroundInfo(NOTIFICATION_ID, notification, serviceType)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SubscriptionExportWork"
|
||||
private const val NOTIFICATION_ID = 4567
|
||||
private const val NOTIFICATION_CHANNEL_ID = "newpipe"
|
||||
private const val WORK_NAME = "exportSubscriptions"
|
||||
private const val EXPORT_PATH = "exportPath"
|
||||
|
||||
fun schedule(
|
||||
context: Context,
|
||||
uri: Uri
|
||||
) {
|
||||
val data = workDataOf(EXPORT_PATH to uri.toString())
|
||||
val workRequest =
|
||||
OneTimeWorkRequestBuilder<SubscriptionExportWorker>()
|
||||
.setInputData(data)
|
||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||
.build()
|
||||
|
||||
WorkManager
|
||||
.getInstance(context)
|
||||
.enqueueUniqueWork(WORK_NAME, ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package org.schabi.newpipe.local.subscription.workers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Build
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.Data
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.rx3.await
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.schabi.newpipe.BuildConfig
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.extractor.NewPipe
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||
import org.schabi.newpipe.util.ExtractorHelper
|
||||
|
||||
class SubscriptionImportWorker(
|
||||
appContext: Context,
|
||||
params: WorkerParameters
|
||||
) : CoroutineWorker(appContext, params) {
|
||||
// This is needed for API levels < 31 (Android S).
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
return createForegroundInfo(applicationContext.getString(R.string.import_ongoing), null, 0, 0)
|
||||
}
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val subscriptions =
|
||||
try {
|
||||
loadSubscriptionsFromInput(SubscriptionImportInput.fromData(inputData))
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error while loading subscriptions from path", e)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast
|
||||
.makeText(applicationContext, R.string.subscriptions_import_unsuccessful, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
val mutex = Mutex()
|
||||
var index = 1
|
||||
val qty = subscriptions.size
|
||||
var title =
|
||||
applicationContext.resources.getQuantityString(R.plurals.load_subscriptions, qty, qty)
|
||||
|
||||
val channelInfoList =
|
||||
try {
|
||||
withContext(Dispatchers.IO.limitedParallelism(PARALLEL_EXTRACTIONS)) {
|
||||
subscriptions
|
||||
.map {
|
||||
async {
|
||||
val channelInfo =
|
||||
ExtractorHelper.getChannelInfo(it.serviceId, it.url, true).await()
|
||||
val channelTab =
|
||||
ExtractorHelper.getChannelTab(it.serviceId, channelInfo.tabs[0], true).await()
|
||||
|
||||
val currentIndex = mutex.withLock { index++ }
|
||||
setForeground(createForegroundInfo(title, channelInfo.name, currentIndex, qty))
|
||||
|
||||
channelInfo to channelTab
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error while loading subscription data", e)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(applicationContext, R.string.subscriptions_import_unsuccessful, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
title = applicationContext.resources.getQuantityString(R.plurals.import_subscriptions, qty, qty)
|
||||
setForeground(createForegroundInfo(title, null, 0, 0))
|
||||
index = 0
|
||||
|
||||
val subscriptionManager = SubscriptionManager(applicationContext)
|
||||
for (chunk in channelInfoList.chunked(BUFFER_COUNT_BEFORE_INSERT)) {
|
||||
withContext(Dispatchers.IO) {
|
||||
subscriptionManager.upsertAll(chunk)
|
||||
}
|
||||
index += chunk.size
|
||||
setForeground(createForegroundInfo(title, null, index, qty))
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(applicationContext, R.string.import_complete_toast, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private suspend fun loadSubscriptionsFromInput(input: SubscriptionImportInput): List<SubscriptionItem> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
when (input) {
|
||||
is SubscriptionImportInput.ChannelUrlMode ->
|
||||
NewPipe.getService(input.serviceId).subscriptionExtractor
|
||||
.fromChannelUrl(input.url)
|
||||
.map { SubscriptionItem(it.serviceId, it.url, it.name) }
|
||||
|
||||
is SubscriptionImportInput.InputStreamMode ->
|
||||
applicationContext.contentResolver.openInputStream(input.url.toUri())?.use {
|
||||
val contentType =
|
||||
MimeTypeMap.getFileExtensionFromUrl(input.url).ifEmpty { DEFAULT_MIME }
|
||||
NewPipe.getService(input.serviceId).subscriptionExtractor
|
||||
.fromInputStream(it, contentType)
|
||||
.map { SubscriptionItem(it.serviceId, it.url, it.name) }
|
||||
}
|
||||
|
||||
is SubscriptionImportInput.PreviousExportMode ->
|
||||
applicationContext.contentResolver.openInputStream(input.url.toUri())?.use {
|
||||
ImportExportJsonHelper.readFrom(it)
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createForegroundInfo(
|
||||
title: String,
|
||||
text: String?,
|
||||
currentProgress: Int,
|
||||
maxProgress: Int
|
||||
): ForegroundInfo {
|
||||
val notification =
|
||||
NotificationCompat
|
||||
.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setOngoing(true)
|
||||
.setProgress(maxProgress, currentProgress, currentProgress == 0)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.addAction(
|
||||
R.drawable.ic_close,
|
||||
applicationContext.getString(R.string.cancel),
|
||||
WorkManager.getInstance(applicationContext).createCancelPendingIntent(id)
|
||||
).apply {
|
||||
if (currentProgress > 0 && maxProgress > 0) {
|
||||
val progressText = "$currentProgress/$maxProgress"
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
setSubText(progressText)
|
||||
} else {
|
||||
setContentInfo(progressText)
|
||||
}
|
||||
}
|
||||
}.build()
|
||||
val serviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0
|
||||
|
||||
return ForegroundInfo(NOTIFICATION_ID, notification, serviceType)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Log tag length is limited to 23 characters on API levels < 24.
|
||||
private const val TAG = "SubscriptionImport"
|
||||
|
||||
private const val NOTIFICATION_ID = 4568
|
||||
private const val NOTIFICATION_CHANNEL_ID = "newpipe"
|
||||
private const val DEFAULT_MIME = "application/octet-stream"
|
||||
private const val PARALLEL_EXTRACTIONS = 8
|
||||
private const val BUFFER_COUNT_BEFORE_INSERT = 50
|
||||
|
||||
const val WORK_NAME = "SubscriptionImportWorker"
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SubscriptionImportInput : Parcelable {
|
||||
@Parcelize
|
||||
data class ChannelUrlMode(val serviceId: Int, val url: String) : SubscriptionImportInput()
|
||||
|
||||
@Parcelize
|
||||
data class InputStreamMode(val serviceId: Int, val url: String) : SubscriptionImportInput()
|
||||
|
||||
@Parcelize
|
||||
data class PreviousExportMode(val url: String) : SubscriptionImportInput()
|
||||
|
||||
fun toData(): Data {
|
||||
val (mode, serviceId, url) = when (this) {
|
||||
is ChannelUrlMode -> Triple(CHANNEL_URL_MODE, serviceId, url)
|
||||
is InputStreamMode -> Triple(INPUT_STREAM_MODE, serviceId, url)
|
||||
is PreviousExportMode -> Triple(PREVIOUS_EXPORT_MODE, null, url)
|
||||
}
|
||||
return workDataOf("mode" to mode, "service_id" to serviceId, "url" to url)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val CHANNEL_URL_MODE = 0
|
||||
private const val INPUT_STREAM_MODE = 1
|
||||
private const val PREVIOUS_EXPORT_MODE = 2
|
||||
|
||||
fun fromData(data: Data): SubscriptionImportInput {
|
||||
val mode = data.getInt("mode", PREVIOUS_EXPORT_MODE)
|
||||
when (mode) {
|
||||
CHANNEL_URL_MODE -> {
|
||||
val serviceId = data.getInt("service_id", -1)
|
||||
if (serviceId == -1) {
|
||||
throw IllegalArgumentException("No service id provided")
|
||||
}
|
||||
val url = data.getString("url")!!
|
||||
return ChannelUrlMode(serviceId, url)
|
||||
}
|
||||
|
||||
INPUT_STREAM_MODE -> {
|
||||
val serviceId = data.getInt("service_id", -1)
|
||||
if (serviceId == -1) {
|
||||
throw IllegalArgumentException("No service id provided")
|
||||
}
|
||||
val url = data.getString("url")!!
|
||||
return InputStreamMode(serviceId, url)
|
||||
}
|
||||
|
||||
PREVIOUS_EXPORT_MODE -> {
|
||||
val url = data.getString("url")!!
|
||||
return PreviousExportMode(url)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown mode: $mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ 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;
|
||||
|
||||
@@ -27,6 +26,7 @@ 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.subscription.SubscriptionsImportExportHelper;
|
||||
import org.schabi.newpipe.settings.export.BackupFileLocator;
|
||||
import org.schabi.newpipe.settings.export.ImportExportManager;
|
||||
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
|
||||
@@ -34,12 +34,10 @@ 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;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@@ -57,18 +55,22 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment {
|
||||
private final ActivityResultLauncher<Intent> requestExportPathLauncher =
|
||||
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
|
||||
this::requestExportPathResult);
|
||||
private SubscriptionsImportExportHelper importExportHelper;
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull final Context context) {
|
||||
super.onAttach(context);
|
||||
importExportHelper = new SubscriptionsImportExportHelper(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(@Nullable final Bundle savedInstanceState,
|
||||
@Nullable final String rootKey) {
|
||||
final File homeDir = ContextCompat.getDataDir(requireContext());
|
||||
Objects.requireNonNull(homeDir);
|
||||
manager = new ImportExportManager(new BackupFileLocator(homeDir));
|
||||
manager = new ImportExportManager(new BackupFileLocator(requireContext()));
|
||||
|
||||
importExportDataPathKey = getString(R.string.import_export_data_path);
|
||||
|
||||
|
||||
addPreferencesFromResourceRegistry();
|
||||
|
||||
final Preference importDataPreference = requirePreference(R.string.import_data);
|
||||
@@ -123,6 +125,21 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment {
|
||||
alertDialog.show();
|
||||
return true;
|
||||
});
|
||||
|
||||
final Preference exportSubsPreference =
|
||||
requirePreference(R.string.export_subscriptions_key);
|
||||
exportSubsPreference.setOnPreferenceClickListener(reference -> {
|
||||
importExportHelper.onExportSelected();
|
||||
return true;
|
||||
});
|
||||
|
||||
final Preference importSubsPreference =
|
||||
requirePreference(R.string.import_subscriptions_key);
|
||||
importSubsPreference.setOnPreferenceClickListener(preference -> {
|
||||
importExportHelper.onImportPreviousSelected();
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void requestExportPathResult(final ActivityResult result) {
|
||||
@@ -181,9 +198,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment {
|
||||
}
|
||||
|
||||
try {
|
||||
if (!manager.ensureDbDirectoryExists()) {
|
||||
throw new IOException("Could not create databases dir");
|
||||
}
|
||||
manager.ensureDbDirectoryExists();
|
||||
|
||||
// replace the current database
|
||||
if (!manager.extractDb(file)) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.schabi.newpipe.settings.export
|
||||
|
||||
import java.io.File
|
||||
import android.content.Context
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.div
|
||||
|
||||
/**
|
||||
* Locates specific files of NewPipe based on the home directory of the app.
|
||||
*/
|
||||
class BackupFileLocator(private val homeDir: File) {
|
||||
class BackupFileLocator(context: Context) {
|
||||
companion object {
|
||||
const val FILE_NAME_DB = "newpipe.db"
|
||||
|
||||
@@ -17,13 +19,8 @@ class BackupFileLocator(private val homeDir: File) {
|
||||
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") }
|
||||
val db: Path = context.getDatabasePath(FILE_NAME_DB).toPath()
|
||||
val dbJournal: Path = db.resolveSibling("$FILE_NAME_DB-journal")
|
||||
val dbShm: Path = db.resolveSibling("$FILE_NAME_DB-shm")
|
||||
val dbWal: Path = db.resolveSibling("$FILE_NAME_DB-wal")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.ObjectOutputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.io.path.createParentDirectories
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import org.schabi.newpipe.streams.io.SharpOutputStream
|
||||
import org.schabi.newpipe.streams.io.StoredFileHelper
|
||||
import org.schabi.newpipe.util.ZipHelper
|
||||
@@ -28,11 +30,8 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) {
|
||||
// previous file size, the file will retain part of the previous content and be corrupted
|
||||
ZipOutputStream(SharpOutputStream(file.openAndTruncateStream()).buffered()).use { outZip ->
|
||||
// add the database
|
||||
ZipHelper.addFileToZip(
|
||||
outZip,
|
||||
BackupFileLocator.FILE_NAME_DB,
|
||||
fileLocator.db.path
|
||||
)
|
||||
val name = BackupFileLocator.FILE_NAME_DB
|
||||
ZipHelper.addFileToZip(outZip, name, fileLocator.db)
|
||||
|
||||
// add the legacy vulnerable serialized preferences (will be removed in the future)
|
||||
ZipHelper.addFileToZip(
|
||||
@@ -61,11 +60,10 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) {
|
||||
|
||||
/**
|
||||
* Tries to create database directory if it does not exist.
|
||||
*
|
||||
* @return Whether the directory exists afterwards.
|
||||
*/
|
||||
fun ensureDbDirectoryExists(): Boolean {
|
||||
return fileLocator.dbDir.exists() || fileLocator.dbDir.mkdir()
|
||||
@Throws(IOException::class)
|
||||
fun ensureDbDirectoryExists() {
|
||||
fileLocator.db.createParentDirectories()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,16 +73,13 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) {
|
||||
* @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
|
||||
)
|
||||
val name = BackupFileLocator.FILE_NAME_DB
|
||||
val success = ZipHelper.extractFileFromZip(file, name, fileLocator.db)
|
||||
|
||||
if (success) {
|
||||
fileLocator.dbJournal.delete()
|
||||
fileLocator.dbWal.delete()
|
||||
fileLocator.dbShm.delete()
|
||||
fileLocator.dbJournal.deleteIfExists()
|
||||
fileLocator.dbWal.deleteIfExists()
|
||||
fileLocator.dbShm.deleteIfExists()
|
||||
}
|
||||
|
||||
return success
|
||||
|
||||
@@ -6,12 +6,12 @@ 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.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
@@ -37,9 +37,6 @@ import java.util.zip.ZipOutputStream;
|
||||
*/
|
||||
|
||||
public final class ZipHelper {
|
||||
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface InputStreamConsumer {
|
||||
void acceptStream(InputStream inputStream) throws IOException;
|
||||
@@ -55,17 +52,17 @@ public final class ZipHelper {
|
||||
|
||||
|
||||
/**
|
||||
* This function helps to create zip files. Caution this will overwrite 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 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
|
||||
* @param path the path of the file on the disk that should be added to zip
|
||||
*/
|
||||
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);
|
||||
final Path path) throws IOException {
|
||||
try (var inputStream = Files.newInputStream(path)) {
|
||||
addFileToZip(outZip, nameInZip, inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,13 +77,13 @@ public final class ZipHelper {
|
||||
final String nameInZip,
|
||||
final OutputStreamConsumer streamConsumer) throws IOException {
|
||||
final byte[] bytes;
|
||||
try (ByteArrayOutputStream byteOutput = new ByteArrayOutputStream()) {
|
||||
try (var byteOutput = new ByteArrayOutputStream()) {
|
||||
streamConsumer.acceptStream(byteOutput);
|
||||
bytes = byteOutput.toByteArray();
|
||||
}
|
||||
|
||||
try (ByteArrayInputStream byteInput = new ByteArrayInputStream(bytes)) {
|
||||
ZipHelper.addFileToZip(outZip, nameInZip, byteInput);
|
||||
try (var byteInput = new ByteArrayInputStream(bytes)) {
|
||||
addFileToZip(outZip, nameInZip, byteInput);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,49 +94,26 @@ public final class ZipHelper {
|
||||
* @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 (BufferedInputStream bufferedInputStream =
|
||||
new BufferedInputStream(inputStream, BUFFER_SIZE)) {
|
||||
final ZipEntry entry = new ZipEntry(nameInZip);
|
||||
outZip.putNextEntry(entry);
|
||||
int count;
|
||||
while ((count = bufferedInputStream.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
outZip.write(data, 0, count);
|
||||
}
|
||||
}
|
||||
private static void addFileToZip(final ZipOutputStream outZip,
|
||||
final String nameInZip,
|
||||
final InputStream inputStream) throws IOException {
|
||||
outZip.putNextEntry(new ZipEntry(nameInZip));
|
||||
inputStream.transferTo(outZip);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will extract data from ZipInputStream. Caution this will overwrite the original file.
|
||||
* This will extract data from ZipInputStream. Caution, this will overwrite the original file.
|
||||
*
|
||||
* @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
|
||||
* @param path 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
final Path path) throws IOException {
|
||||
return extractFileFromZip(zipFile, nameInZip, input ->
|
||||
Files.copy(input, path, StandardCopyOption.REPLACE_EXISTING));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -186,7 +186,15 @@ object ImageStrategy {
|
||||
fun dbUrlToImageList(url: String?): List<Image> {
|
||||
return when (url) {
|
||||
null -> listOf()
|
||||
else -> listOf(Image(url, -1, -1, ResolutionLevel.UNKNOWN))
|
||||
|
||||
else -> listOf(
|
||||
Image(
|
||||
url,
|
||||
Image.HEIGHT_UNKNOWN,
|
||||
Image.WIDTH_UNKNOWN,
|
||||
ResolutionLevel.UNKNOWN
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="main_bg_subtitle">Uka luparu ch’allt’aña qalltañataki.</string>
|
||||
</resources>
|
||||
@@ -823,9 +823,26 @@
|
||||
<string name="player_http_403">Oynadarkən serverdən alınan HTTP xətası 403, çox güman ki, yayım URL-si müddətinin bitməsi və ya IP qadağası ilə bağlıdır</string>
|
||||
<string name="player_http_invalid_status">HTTP xətası %1$s oynadarkən serverdən alındı</string>
|
||||
<string name="youtube_player_http_403">HTTP xətası 403 oynadarkən serverdən alındı, ehtimal ki, IP qadağası və ya yayım URL-nin deobfuscation problemləri ilə bağlıdır</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s sorğuçunun bot olmadığını təsdiqləmək üçün giriş tələb edərək data təmin etməkdən imtina etdi.\n\nIP-niz %1$s tərəfindən müvəqqəti şəkildə qadağan oluna bilər, bir müddət gözləyə və ya başqa IP-yə keçə bilərsiniz (məsələn, VPN-i açıb/qapatmaqla və ya WiFi-dan mobil dataya keçməklə).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s sorğuçunun bot olmadığını təsdiqləmək üçün giriş tələb edərək data təmin etməkdən imtina etdi.\n\nIP-niz %1$s tərəfindən müvəqqəti şəkildə qadağan oluna bilər, bir müddət gözləyə və ya başqa IP-yə keçə bilərsiniz (məsələn, VPN-i açıb/qapatmaqla və ya WiFi-dan mobil dataya keçməklə).\n\nDaha çox məlumat üçün xahiş olunur <a href="%2$s">bu Tez-tez Verilən Suallar qeydinə</a> baxın.</string>
|
||||
<string name="unsupported_content_in_country">Bu məzmun hazırda seçilən məzmun ölkəsi üçün əlçatan deyil. \n\nSeçiminizi \"Tənzimləmələr > Məzmun > İlkin məzmun ölkəsi\"- dən dəyişin.</string>
|
||||
<string name="kao_dialog_warning">2025 avqustunda, Google 2026-cı ilin sentyabrından etibarən tətbiqlərin quraşdırılması Play Store xaricində quraşdırılanlar daxil olmaqla, sertifikatlaşdırılan cihazlardakı bütün Android tətbiqləri üçün tərtibatçı təsdiqlənməsini tələb edəcək deyə bəyan etdi. NewPipe tərtibatçıları bu tələblə razılaşmadığı üçün NewPipe bu vaxtdan sonra artıq sertifikatlaşdırılan Android cihazlarında işləməyəcək.</string>
|
||||
<string name="kao_dialog_more_info">Təfərrüatlar</string>
|
||||
<string name="kao_solution">Həll olunma</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">%d abunəlik ixrac olunur…</item>
|
||||
<item quantity="other">%d abunəlik ixrac olunur…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">%d abunəlik yüklənilir…</item>
|
||||
<item quantity="other">%d abunəlik yüklənilir…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">%d abunəlik idxal olunur…</item>
|
||||
<item quantity="other">%d abunəlik idxal olunur…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Abunəlikləri idxal et</string>
|
||||
<string name="export_subscriptions_title">Abunəlikləri ixrac et</string>
|
||||
<string name="import_subscriptions_summary">Əvvəlki .json ixracından abunəlikləri idxal et</string>
|
||||
<string name="export_subscriptions_summary">Abunəliklərinizi .json faylına köçürün</string>
|
||||
<string name="import_from_previous_export">Əvvəlki köçürmədən idxal et</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
@@ -830,9 +830,26 @@
|
||||
<string name="player_http_403">HTTP грешка 403, получена от сървъра по време на възпроизвеждане, вероятно причинена от изтичане на URL адреса за стрийминг или забрана на IP адреса</string>
|
||||
<string name="player_http_invalid_status">HTTP грешка %1$s получена от сървъра по време на възпроизвеждане</string>
|
||||
<string name="youtube_player_http_403">HTTP грешка 403, получена от сървъра по време на възпроизвеждане, вероятно причинена от забрана на IP адреса или проблеми с деобфускацията на URL адреси за стрийминг</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s отказа да предостави данни, като поиска вход, за да потвърди, че заявителят не е бот.\n\nВашият IP адрес може да е временно забранен от %1$s. Можете да изчакате известно време или да превключите към друг IP адрес (например като включите/изключите VPN или като превключите от WiFi към мобилни данни).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s отказа да предостави данни, като поиска вход, за да потвърди, че заявителят не е бот.\n\nВашият IP адрес може да е временно забранен от %1$s. Можете да изчакате известно време или да превключите към друг IP адрес (например като включите/изключите VPN или като превключите от WiFi към мобилни данни).\n\nМоля, вижте <a href="%2$s">тази статия с ЧЗВ</a> за повече информация.</string>
|
||||
<string name="unsupported_content_in_country">Това съдържание не е налично за текущо избраната държава на съдържанието.\n\nПроменете избора си от \"Настройки > Съдържание > Държава на съдържанието по подразбиране\".</string>
|
||||
<string name="kao_dialog_warning">През август 2025 г. Google обяви, че от септември 2026 г. инсталирането на приложения ще изисква проверка от разработчика за всички приложения за Android на сертифицирани устройства, включително тези, инсталирани извън Play Store. Тъй като разработчиците на NewPipe не са съгласни с това изискване, NewPipe вече няма да работи на сертифицирани устройства с Android след този период.</string>
|
||||
<string name="kao_dialog_more_info">Детайли</string>
|
||||
<string name="kao_solution">Решение</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Изнасяне на %d абонамент…</item>
|
||||
<item quantity="other">Изнасяне на %d абонаменти…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Зареждане на %d абонамент…</item>
|
||||
<item quantity="other">Зареждане на %d абонаменти…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Внасяне на %d абонамент…</item>
|
||||
<item quantity="other">Внасяне на %d абонаменти…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Внасяне на абонаменти</string>
|
||||
<string name="export_subscriptions_title">Изнасяне на абонаменти</string>
|
||||
<string name="import_subscriptions_summary">Внасяне на абонаменти от предишен .json файл</string>
|
||||
<string name="export_subscriptions_summary">Изнесете абонаментите си в .json файл</string>
|
||||
<string name="import_from_previous_export">Внасяне от предишно изнасяне</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -859,9 +859,32 @@
|
||||
<string name="player_http_403">Během přehrávání byla ze serveru přijata chyba HTTP 403, pravděpodobně způsobená vypršením platnosti streamingové adresy URL nebo zákazem IP adresy</string>
|
||||
<string name="player_http_invalid_status">Chyba HTTP %1$s obdržená ze serveru během přehrávání</string>
|
||||
<string name="youtube_player_http_403">Chyba HTTP 403 obdržená od serveru během přehrávání, pravděpodobně způsobená zákazem IP adresy nebo problémy s deobfuskací streamovací adresy URL</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s odmítl poskytnout data, žádá o přihlášení k potvrzení, že žadatel není bot.\n\nVaše IP adresa mohla být dočasně zakázána %1$s, můžete nějakou dobu počkat nebo přepnout na jinou IP adresu (například zapnutím/vypnutím VPN nebo přepnutím z WiFi na mobilní data).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s odmítl poskytnout data, žádá o přihlášení k potvrzení, že žadatel není bot.\n\nVaše IP adresa mohla být dočasně zablokována službou %1$s, můžete nějakou dobu počkat nebo přepnout na jinou IP adresu (například zapnutím/vypnutím VPN nebo přepnutím z Wi-Fi na mobilní data).\n\nPro více informací si přečtěte <a href="%2$s">tento záznam v FAQ</a>.</string>
|
||||
<string name="unsupported_content_in_country">Tento obsah není pro aktuálně vybranou zemi obsahu dostupný.\n\nZměňte výběr v nabídce \"Nastavení > Obsah > Výchozí země obsahu\".</string>
|
||||
<string name="kao_dialog_warning">Společnost Google oznámila, že od roku 2026/2027 budou všechny aplikace na certifikovaných zařízeních Android vyžadovat, aby vývojář odeslal své osobní identifikační údaje přímo společnosti Google. Jelikož vývojáři této aplikace s tímto požadavkem nesouhlasí, aplikace po tomto datu přestane na certifikovaných zařízeních Android fungovat.</string>
|
||||
<string name="kao_dialog_more_info">Podrobnosti</string>
|
||||
<string name="kao_solution">Řešení</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Exportování %d odběru…</item>
|
||||
<item quantity="few">Exportování %d odběrů…</item>
|
||||
<item quantity="many">Exportování %d odběrů…</item>
|
||||
<item quantity="other">Exportování %d odběrů…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Načítání %d odběru…</item>
|
||||
<item quantity="few">Načítání %d odběrů…</item>
|
||||
<item quantity="many">Načítání %d odběrů…</item>
|
||||
<item quantity="other">Načítání %d odběrů…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importování %d odběru…</item>
|
||||
<item quantity="few">Importování %d odběrů…</item>
|
||||
<item quantity="many">Importování %d odběrů…</item>
|
||||
<item quantity="other">Importování %d odběrů…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Importovat odběry</string>
|
||||
<string name="export_subscriptions_title">Exportovat odběry</string>
|
||||
<string name="import_subscriptions_summary">Importovat odběry z předchozího exportu .json</string>
|
||||
<string name="export_subscriptions_summary">Exportovat odběry do souboru .json</string>
|
||||
<string name="import_from_previous_export">Importovat z předchozího exportu</string>
|
||||
</resources>
|
||||
|
||||
@@ -242,8 +242,8 @@
|
||||
<string name="caption_auto_generated">Automatisch erzeugt</string>
|
||||
<string name="import_from">Import von</string>
|
||||
<string name="export_to">Export nach</string>
|
||||
<string name="import_ongoing">Importiere …</string>
|
||||
<string name="export_ongoing">Exportiere …</string>
|
||||
<string name="import_ongoing">Wird importiert …</string>
|
||||
<string name="export_ongoing">Wird exportiert …</string>
|
||||
<string name="import_file_title">Datei importieren</string>
|
||||
<string name="previous_export">Vorheriger Export</string>
|
||||
<string name="import_network_expensive_warning">Beachte, dass diese Aktion das Netzwerk stark belasten kann.
|
||||
@@ -845,9 +845,26 @@
|
||||
<string name="player_http_403">HTTP-Fehler 403 vom Server während der Wiedergabe erhalten, wahrscheinlich verursacht durch Ablauf der Streaming-URL oder eine IP-Sperre</string>
|
||||
<string name="player_http_invalid_status">HTTP-Fehler %1$s vom Server während der Wiedergabe erhalten</string>
|
||||
<string name="youtube_player_http_403">HTTP-Fehler 403 vom Server während der Wiedergabe erhalten, wahrscheinlich verursacht durch eine IP-Sperre oder Probleme beim Entschlüsseln der Streaming-URL</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s hat die Datenbereitstellung verweigert und verlangt eine Anmeldung, um zu bestätigen, dass es sich bei dem Anfragenden nicht um einen Bot handelt.\n\nDeine IP-Adresse wurde möglicherweise vorübergehend von %1$s gesperrt. Du kannst einige Zeit warten oder zu einer anderen IP-Adresse wechseln (z. B. durch Ein- und Ausschalten eines VPNs oder durch Wechseln von WLAN zu mobilen Daten).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s hat die Datenbereitstellung verweigert und verlangt eine Anmeldung, um zu bestätigen, dass es sich bei dem Anfragenden nicht um einen Bot handelt.\n\nDeine IP-Adresse wurde möglicherweise vorübergehend von %1$s gesperrt. Du kannst einige Zeit warten oder zu einer anderen IP-Adresse wechseln (z. B. durch Ein- und Ausschalten eines VPNs oder durch Wechseln von WLAN zu mobilen Daten).\n\nWeitere Informationen findest du unter <a href="%2$s">diesem FAQ-Eintrag</a>.</string>
|
||||
<string name="unsupported_content_in_country">Dieser Inhalt ist für das aktuell ausgewählte Land des Inhalts nicht verfügbar.\n\nÄndere die Auswahl unter „Einstellungen > Inhalt > Bevorzugtes Land des Inhalts“.</string>
|
||||
<string name="kao_dialog_warning">Im August 2025 gab Google bekannt, dass ab September 2026 für die Installation von Apps eine Entwicklerüberprüfung für alle Android-Apps auf zertifizierten Geräten erforderlich sein wird, einschließlich derjenigen, die außerhalb des Play Store installiert wurden. Da die Entwickler von NewPipe dieser Forderung nicht nachkommen, wird NewPipe nach diesem Zeitpunkt auf zertifizierten Android-Geräten nicht mehr funktionieren.</string>
|
||||
<string name="kao_dialog_more_info">Details</string>
|
||||
<string name="kao_solution">Lösung</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">%d Abonnement wird exportiert …</item>
|
||||
<item quantity="other">%d Abonnements werden exportiert …</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">%d Abonnement wird geladen …</item>
|
||||
<item quantity="other">%d Abonnements werden geladen …</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">%d Abonnement wird importiert …</item>
|
||||
<item quantity="other">%d Abonnements werden importiert …</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Abonnements importieren</string>
|
||||
<string name="export_subscriptions_title">Abonnements exportieren</string>
|
||||
<string name="import_subscriptions_summary">Importieren von Abonnements aus einem früheren .json-Export</string>
|
||||
<string name="export_subscriptions_summary">Exportiere deine Abonnements in eine .json-Datei</string>
|
||||
<string name="import_from_previous_export">Aus vorherigem Export importieren</string>
|
||||
</resources>
|
||||
|
||||
@@ -845,9 +845,26 @@
|
||||
<string name="player_http_403">Σφάλμα HTTP 403 που ελήφθη από τον διακομιστή κατά την αναπαραγωγή, πιθανώς λόγω λήξης διεύθυνσης URL ροής ή αποκλεισμού IP</string>
|
||||
<string name="player_http_invalid_status">Σφάλμα HTTP %1$s ελήφθη από τον διακομιστή κατά την αναπαραγωγή</string>
|
||||
<string name="youtube_player_http_403">Σφάλμα HTTP 403 ελήφθη από τον διακομιστή κατά την αναπαραγωγή, πιθανώς λόγω αποκλεισμού IP ή προβλημάτων απεμπλοκής URL ροής</string>
|
||||
<string name="sign_in_confirm_not_bot_error">Ο %1$s αρνήθηκε να παράσχει δεδομένα, ζητώντας σύνδεση για να επιβεβαιώσει ότι ο αιτών δεν είναι bot.\n\nΗ IP σας ενδέχεται να έχει αποκλειστεί προσωρινά από τον %1$s. Μπορείτε να περιμένετε λίγο ή να αλλάξετε IP (για παράδειγμα, ενεργοποιώντας/απενεργοποιώντας ένα VPN ή αλλάζοντας από WiFi σε δεδομένα κινητής τηλεφωνίας).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">Ο %1$s αρνήθηκε να παράσχει δεδομένα, ζητώντας σύνδεση για να επιβεβαιώσει ότι ο αιτών δεν είναι bot.\n\nΗ IP σας ενδέχεται να έχει αποκλειστεί προσωρινά από τον %1$s. Μπορείτε να περιμένετε λίγο ή να αλλάξετε IP (για παράδειγμα, ενεργοποιώντας/απενεργοποιώντας ένα VPN ή αλλάζοντας από WiFi σε δεδομένα κινητής τηλεφωνίας).\n\nΑνατρέξτε σε<a href="%2$s">αυτήν την καταχώρηση στις Συχνές Ερωτήσεις</a> για περισσότερες πληροφορίες.</string>
|
||||
<string name="unsupported_content_in_country">Αυτό το περιεχόμενο δεν είναι διαθέσιμο για την τρέχουσα επιλεγμένη χώρα περιεχομένου.\n\nΑλλάξτε την επιλογή σας από \"Ρυθμίσεις > Περιεχόμενο > Προεπιλεγμένη χώρα περιεχομένου\".</string>
|
||||
<string name="kao_dialog_more_info">Λεπτομέρειες</string>
|
||||
<string name="kao_solution">Λύση</string>
|
||||
<string name="kao_dialog_warning">Τον Αύγουστο του 2025, η Google ανακοίνωσε ότι από τον Σεπτέμβριο του 2026, η εγκατάσταση εφαρμογών θα απαιτεί επαλήθευση προγραμματιστή για όλες τις εφαρμογές Android σε πιστοποιημένες συσκευές, συμπεριλαμβανομένων εκείνων που είναι εγκατεστημένες εκτός του Play Store. Δεδομένου ότι οι προγραμματιστές του NewPipe δεν συμφωνούν με αυτήν την απαίτηση, το NewPipe δεν θα λειτουργεί πλέον σε πιστοποιημένες συσκευές Android μετά από αυτό το χρονικό διάστημα.</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Εξαγωγή %d συνδρομής…</item>
|
||||
<item quantity="other">Εξαγωγή %d συνδρομών…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Φόρτωση %d συνδρομής…</item>
|
||||
<item quantity="other">Φόρτωση %d συνδρομών…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Εισαγωγή %d συνδρομής…</item>
|
||||
<item quantity="other">Εισαγωγή %d συνδρομών…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Εισαγωγή συνδρομών</string>
|
||||
<string name="export_subscriptions_title">Εξαγωγή συνδρομών</string>
|
||||
<string name="import_subscriptions_summary">Εισαγωγή συνδρομών από προηγούμενη εξαγωγή</string>
|
||||
<string name="export_subscriptions_summary">Εξαγωγή των συνδρομών σας σε αρχείο .json</string>
|
||||
<string name="import_from_previous_export">Εισαγωγή από προηγούμενη εξαγωγή</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -831,8 +831,25 @@
|
||||
<string name="player_http_invalid_status">Esitamise ajal lisas server andmevoogu HTTP oleku %1$s</string>
|
||||
<string name="youtube_player_http_403">Esitamise ajal lisas server andmevoogu HTTP oleku 403 ning tavaliselt tähendab see, et sinu seadme IP-aadress on keelatud või voogedastuse võrguaadressi hägustamisvastastes meetmetes on viga</string>
|
||||
<string name="unsupported_content_in_country">See sisu pole saadaval hetkel kehtvas riigis.\n\nRiiki saad muuta: Seadistused > Sisu > Sisu vaikimisi riik.</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s keeldus andmete edastamisest ning eeldab sisselogimist tuvastamaks, et tegemist pole robotiga.\n\nLisaks võib olla juhtunud, et %1$s on lisanud sinu seadme ip-aadressi ajutisse keelunimekirja. Sa võid oodata natuke aega või vahetada võrguühendus viisi (näiteks lülitades VPN sisse/välja või kasutades WiFi asemel mobiilset internetiühendust).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s keeldus andmete edastamisest ning eeldab sisselogimist tuvastamaks, et tegemist pole robotiga.\n\nLisaks võib olla juhtunud, et %1$s on lisanud sinu seadme ip-aadressi ajutisse keelunimekirja. Sa võid oodata natuke aega või vahetada võrguühendus viisi (näiteks lülitades VPN sisse/välja või kasutades WiFi asemel mobiilset internetiühendust).\n\nLisateavet leiad <a href="%2$s">siit korduva kippumate küsimuste</a> alajaotusest.</string>
|
||||
<string name="kao_dialog_warning">2025. aasta augustis teatas Google, et alates septembrist 2026 uute rakenduste paigaldamine kõikides uutes Androidi seadmetes eeldab arendajate verifitseerimist, sealhulgas juhtudel, kui selline rakendus on paigaldatud väljastpoolt Google Play rakendustepoodi. Kuna NewPipe\'i arendajad pole sellise nõudmisega nõus, siis sellise aja saabumisel NewPipe enam ei toimi sertifitseeritud Androidi seadmetes.</string>
|
||||
<string name="kao_dialog_more_info">Üksikasjad</string>
|
||||
<string name="kao_solution">Lahendus</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Eksportimisel on %d tellimus…</item>
|
||||
<item quantity="other">Eksportimisel on %d tellimust…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Laadimisel on %d tellimus…</item>
|
||||
<item quantity="other">Laadimisel on %d tellimust…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importimisel on %d tellimus…</item>
|
||||
<item quantity="other">Importimisel on %d tellimust…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Impordi tellimusi</string>
|
||||
<string name="export_subscriptions_title">Ekspordi tellimusi</string>
|
||||
<string name="import_subscriptions_summary">Impordi tellimusi varasemeksporditud json-failist</string>
|
||||
<string name="export_subscriptions_summary">Ekspirdi oma tellimused json-faili</string>
|
||||
<string name="import_from_previous_export">Impordi tellimusi varasemeksporditud failist</string>
|
||||
</resources>
|
||||
|
||||
@@ -861,9 +861,29 @@
|
||||
<string name="player_http_403">Erreur HTTP 403 reçue du serveur pendant la lecture, probablement causée par l\'expiration de l\'URL de streaming ou une interdiction d\'IP</string>
|
||||
<string name="player_http_invalid_status">Erreur HTTP %1$s reçue du serveur pendant la lecture</string>
|
||||
<string name="youtube_player_http_403">Erreur HTTP 403 reçue du serveur pendant la lecture, probablement causée par un bannissement d\'IP ou des problèmes de désobfuscation de l\'URL de streaming</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s a refusé de fournir des données et a demandé un identifiant pour confirmer que le demandeur n\'est pas un robot.\n\nVotre adresse IP a peut-être été temporairement bannie par %1$s. Vous pouvez patienter un peu ou changer d\'adresse IP (par exemple en activant/désactivant un VPN, ou en passant du Wi-Fi aux données mobiles).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s a refusé de fournir des données et a demandé un identifiant pour confirmer que l\'auteur de la requête n\'est pas un robot.\n\nVotre adresse IP a peut-être été temporairement bloquée par %1$s. Vous pouvez patienter ou essayer une autre adresse IP (par exemple, en activant/désactivant un VPN ou en passant du Wi-Fi aux données mobiles).\n\nPour plus d\'informations, veuillez consulter <a href="%2$s">cette FAQ</a>.</string>
|
||||
<string name="unsupported_content_in_country">Ce contenu n\'est pas disponible pour le pays actuellement sélectionné.\n\nModifiez votre sélection dans « Paramètres > Contenu > Pays par défaut ».</string>
|
||||
<string name="kao_dialog_warning">En août 2025, Google a annoncé qu\'à compter de septembre 2026, l\'installation d\'applications nécessiterait une vérification par le développeur pour toutes les applications Android sur les appareils certifiés, y compris celles installées en dehors du Play Store. Les développeurs de NewPipe refusant cette exigence, NewPipe ne fonctionnera plus sur les appareils Android certifiés après cette date.</string>
|
||||
<string name="kao_dialog_more_info">Détails</string>
|
||||
<string name="kao_solution">Solution</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Exportation de l\'abonnement %d…</item>
|
||||
<item quantity="many">Exportation de %d abonnements…</item>
|
||||
<item quantity="other">Exportation de %d abonnements…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Chargement de l\'abonnement %d…</item>
|
||||
<item quantity="many">Chargement de %d abonnements…</item>
|
||||
<item quantity="other">Chargement de %d abonnements…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importation de l\'abonnement %d…</item>
|
||||
<item quantity="many">Importation de %d abonnements…</item>
|
||||
<item quantity="other">Importation de %d abonnements…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Importer des abonnements</string>
|
||||
<string name="export_subscriptions_title">Exporter les abonnements</string>
|
||||
<string name="import_subscriptions_summary">Importer des abonnements à partir d\'une exportation .json précédente</string>
|
||||
<string name="export_subscriptions_summary">Exportez vos abonnements dans un fichier .json</string>
|
||||
<string name="import_from_previous_export">Importer à partir d\'une exportation précédente</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="no_player_found">aucun streamer trouvé . Installez VLC?</string>
|
||||
<string name="no">non</string>
|
||||
<string name="open_in_browser">ouvrir dans le browser</string>
|
||||
<string name="open_in_popup_mode">ouvrir dans le popup mode</string>
|
||||
<string name="open_with">ouvrir avec</string>
|
||||
<string name="share">partagez</string>
|
||||
<string name="controls_download_desc">installer le fichier stream</string>
|
||||
<string name="search">chercher</string>
|
||||
<string name="settings">parameters</string>
|
||||
<string name="download">installer</string>
|
||||
<string name="install">Installer</string>
|
||||
<string name="mark_as_watched">marquer comme vu</string>
|
||||
<string name="upload_date_text">"publié le %1$s"</string>
|
||||
<string name="no_player_found_toast">aucun joueur de stream n\'est trouvé ( vous pouvez installez VLC pour jouer)</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="yes">Oui</string>
|
||||
</resources>
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
@@ -369,7 +369,7 @@
|
||||
<string name="feed_notification_loading">Cargando transmisión…</string>
|
||||
<string name="feed_subscription_not_loaded_count">Non se cargou: %d</string>
|
||||
<string name="feed_oldest_subscription_update">Actualizada a última información: %s</string>
|
||||
<string name="feed_groups_header_title">Grupos da canle</string>
|
||||
<string name="feed_groups_header_title">Grupos da canles</string>
|
||||
<plurals name="days">
|
||||
<item quantity="one">%d día</item>
|
||||
<item quantity="other">%d días</item>
|
||||
@@ -388,7 +388,7 @@
|
||||
</plurals>
|
||||
<string name="new_seek_duration_toast">Debido ás restricións de ExoPlayer, a duración da busca estableceuse en %d segundos</string>
|
||||
<string name="remove_watched_popup_partially_watched_streams">Si, e visualizou parcialmente estes vídeos</string>
|
||||
<string name="remove_watched_popup_warning">Eliminaranse os vídeos vistos antes e despois de seren engadidos á lista de reprodución. \nEstás seguro? Isto non se pode desfacer.!</string>
|
||||
<string name="remove_watched_popup_warning">Eliminaranse as emisións vistas antes e despois de seren engadidas á lista de reprodución.\nEstás seguro?</string>
|
||||
<string name="remove_watched_popup_title">Borrar todos os vídeos vistos?</string>
|
||||
<string name="remove_watched">Eliminar o visto</string>
|
||||
<string name="systems_language">Sistema predeterminado</string>
|
||||
@@ -812,4 +812,11 @@
|
||||
<string name="channel_tab_tracks">Pistas</string>
|
||||
<string name="feed_fetch_channel_tabs_summary">Lapelas a recuperar ao actualizar o feed. Esta opción non ten efecto se a canle se actualiza no modo rápido.</string>
|
||||
<string name="always_use_exoplayer_set_output_surface_workaround_summary">Esta solución alternativa libera os códecs de video e os re-instancia cando muda a máscara, no canto de configurar a máscara directamente no códec. ExoPlayer xa emprega esta configuración nalgúns dispositivos con este problema e só afecta a Android 6 e versións posteriores.\n\nActivar esta opción pode minimizar erros de reprodución ao mudar o reprodutor de video actual ou mudar ao modo de pantalla completa</string>
|
||||
<string name="short_thousand">%sK</string>
|
||||
<string name="short_million">%sM</string>
|
||||
<string name="short_billion">%sMM</string>
|
||||
<string name="search_with_service_name">Procurar %1$s</string>
|
||||
<string name="search_with_service_name_and_filter">Procurar %1$s (%2$s)</string>
|
||||
<string name="tab_bookmarks_short">Listas de reprodución</string>
|
||||
<string name="feed_group_page_summary">Páxina do grupo de canles</string>
|
||||
</resources>
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
<item quantity="one">%d सेकेंड</item>
|
||||
<item quantity="other">%d सेकंड्स</item>
|
||||
</plurals>
|
||||
<string name="remove_watched_popup_title">देखे गए वीडियो हटायें?</string>
|
||||
<string name="remove_watched_popup_title">देखे गए स्ट्रीम्स हटाएँ?</string>
|
||||
<string name="remove_watched">देखे गए को हटा दें</string>
|
||||
<string name="systems_language">सिस्टम डिफ़ॉल्ट</string>
|
||||
<string name="app_language_title">ऐप की भाषा</string>
|
||||
@@ -529,7 +529,7 @@
|
||||
<string name="auto_device_theme_title">ऑटोमैटिक (डिवाइस थीम)</string>
|
||||
<string name="night_theme_summary">अपनी पसंदीदा नाइट थीम चुने — %s</string>
|
||||
<string name="select_night_theme_toast">आप अपनी पसंदीदा नाइट थीम नीचे चुन सकते हैं</string>
|
||||
<string name="download_has_started">डाउनलोड शुरू हो गया है</string>
|
||||
<string name="download_has_started">डाउनलोड शुरू हुआ</string>
|
||||
<string name="restricted_video_no_stream">यह वीडियो आयु-प्रतिबंधित है।
|
||||
\nयूट्यूब की नई नीतियों के कारण न्यूपाइप किसी भी आयु प्रतिबंधित वीडियो स्ट्रीम का इस्तेमाल नहीं कर सकता है और इस कारण इसे वीडियो को प्ले करने में असमर्थ है।</string>
|
||||
<string name="peertube_instance_url_title">पियरट्यूब इंसटैंस</string>
|
||||
@@ -669,7 +669,7 @@
|
||||
<string name="show_channel_details">चैनल विवरण दिखाएं</string>
|
||||
<string name="show_original_time_ago_title">आइटम्स का असल अपलोड समय दिखाएं</string>
|
||||
<string name="show_original_time_ago_summary">सेवाओं से मूल पाठ स्ट्रीम आइटम में दिखाई देंगे</string>
|
||||
<string name="remove_watched_popup_warning">प्लेलिस्ट में शामिल, पहले और बाद में देखे जा चुके वीडियो हटा दिए जाएंगे। \nक्या यक़ीनन आप ऐसा चाह्ते हैं? इसे असंपादित नहीं किया जा सकेगा!</string>
|
||||
<string name="remove_watched_popup_warning">जो स्ट्रीम्स प्लेलिस्ट में जोड़ने से पहले या बाद में देखी जा चुकी हैं, उन्हें हटा दिया जाएगा।\nक्या यक़ीनन आप ऐसा चाह्ते हैं?</string>
|
||||
<plurals name="minutes">
|
||||
<item quantity="one">%d मिनट</item>
|
||||
<item quantity="other">%d मिनट्स</item>
|
||||
@@ -845,9 +845,9 @@
|
||||
<string name="player_http_403">पले करते समय सर्वर से HTTP error 403 मिला, शायद स्ट्रीमिंग URL एक्सपायर होने या IP बैन की वजह से हुआ</string>
|
||||
<string name="player_http_invalid_status">पले करते समय सर्वर से HTTP error %1$s मिला</string>
|
||||
<string name="youtube_player_http_403">पले करते समय सर्वर से HTTP error 403 मिला, जो शायद IP बैन या स्ट्रीमिंग URL डीओबफस्केशन की दिक्कतों की वजह से हुआ है</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s ने डेटा देने से मना कर दिया, और यह कन्फर्म करने के लिए लॉगिन मांगा कि रिक्वेस्ट करने वाला बोट नहीं है।\n\nहो सकता है कि %1$s ने आपके IP को कुछ समय के लिए बैन कर दिया हो, आप कुछ समय इंतज़ार कर सकते हैं या किसी दूसरे IP पर स्विच कर सकते हैं (जैसे VPN ऑन/ऑफ करके, या WiFi से मोबाइल डेटा पर स्विच करके)।</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s ने डेटा देने से मना कर दिया, और यह कन्फर्म करने के लिए लॉगिन मांगा कि रिक्वेस्ट करने वाला बोट नहीं है।\n\nहो सकता है कि %1$s ने आपके IP को कुछ समय के लिए बैन कर दिया हो, आप कुछ समय इंतज़ार कर सकते हैं या किसी दूसरे IP पर स्विच कर सकते हैं (जैसे VPN ऑन/ऑफ करके, या WiFi से मोबाइल डेटा पर स्विच करके)।\n\nअधिक जानकारी के लिए कृपया <a href="%2$s">यह FAQ एंट्री</a> देखें।</string>
|
||||
<string name="unsupported_content_in_country">यह कंटेंट अभी चुने गए देश के कंटेंट के लिए उपलब्ध नहीं है।\n\n\"सेटिंग्स > कंटेंट > डिफ़ॉल्ट कंटेंट देश\" से अपना चुनाव बदलें।</string>
|
||||
<string name="kao_dialog_warning">Google ने घोषणा की है कि 2026/2027 से, प्रमाणित Android डिवाइसों पर सभी ऐप्स के लिए डेवलपर्स को अपनी व्यक्तिगत पहचान संबंधी जानकारी सीधे Google को जमा करनी होगी। चूँकि इस ऐप के डेवलपर्स इस आवश्यकता से सहमत नहीं हैं, यह ऐप उस समय के बाद प्रमाणित Android डिवाइसों पर काम नहीं करेगा।</string>
|
||||
<string name="kao_dialog_warning">अगस्त 2025 में, Google ने घोषणा की कि सितंबर 2026 से, सर्टिफाइड डिवाइस पर सभी Android ऐप्स जिनमें Play Store के बाहर से इंस्टॉल किए गए ऐप्स भी शामिल हैं, को इंस्टॉल करने के लिए डेवलपर वेरिफिकेशन ज़रूरी होगा। चूंकि NewPipe के डेवलपर्स इस शर्त से सहमत नहीं हैं, इसलिए उस समय के बाद NewPipe सर्टिफाइड Android डिवाइस पर काम नहीं करेगा।</string>
|
||||
<string name="kao_dialog_more_info">विवरण</string>
|
||||
<string name="kao_solution">समाधान</string>
|
||||
</resources>
|
||||
|
||||
@@ -816,9 +816,26 @@
|
||||
<string name="player_http_403">A lejátszás közben a kiszolgáló 403-as HTTP-hibát adott vissza, valószínűleg a közvetítési hivatkozás érvényessége lejárt vagy a IP-tiltás miatt</string>
|
||||
<string name="player_http_invalid_status">HTTP-hiba (%1$s) érkezett a kiszolgálótól a lejátszás során</string>
|
||||
<string name="youtube_player_http_403">HTTP 403-as hiba érkezett a kiszolgálótól a lejátszás közben, valószínűleg IP-tiltás vagy a közvetítési hivatkozás feloldási problémák miatt</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s visszautasította az adatok szolgáltatását, és bejelentkezést kér annak megerősítésére, hogy a kérés nem robot által érkezik.\n\nElőfordulhat, hogy az IP-címét ideiglenesen letiltotta %1$s, várhat egy keveset, vagy váltson egy másik IP-címre (például VPN be-/kikapcsolásával, vagy Wi-Fi-ről mobiladat-forgalomra váltva).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s visszautasította az adatok szolgáltatását, és bejelentkezést kér annak megerősítésére, hogy a kérés nem robot által érkezik.\n\nElőfordulhat, hogy az IP-címét ideiglenesen letiltotta %1$s, várhat egy keveset, vagy váltson egy másik IP-címre (például VPN be-/kikapcsolásával, vagy Wi-Fi-ről mobiladat-forgalomra váltva).\n\nTovábbi információért tekintse meg <a href="%2$s">ezt a GYIK-bejegyzést</a>.</string>
|
||||
<string name="unsupported_content_in_country">Ez a tartalom a jelenleg kiválasztott tartalom országában nem elérhető.\n\nVáltoztassa meg a „Beállítások > Tartalom >Tartalom alapértelmezett országa” menüpontban.</string>
|
||||
<string name="kao_dialog_warning">2025 augusztusában a Google bejelentette, hogy 2026 szeptemberétől az alkalmazások telepítéséhez fejlesztői ellenőrzésre lesz szükség a tanúsított eszközökön található összes Android-alkalmazáshoz, beleértve a Play Áruházon kívül telepített alkalmazásokat is. Mivel a NewPipe fejlesztői nem értenek egyet ezzel a követelménnyel, a NewPipe ezután nem fog működni a tanúsított Android-eszközökön.</string>
|
||||
<string name="kao_dialog_more_info">Részletek</string>
|
||||
<string name="kao_solution">Megoldás</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">%d feliratkozás exportálása…</item>
|
||||
<item quantity="other">%d feliratkozások exportálása…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">%d feliratkozás betöltése…</item>
|
||||
<item quantity="other">%d feliratkozások betöltése…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">%d feliratkozás importálása…</item>
|
||||
<item quantity="other">%d feliratkozások importálása…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Feliratkozások importálása</string>
|
||||
<string name="export_subscriptions_title">Feliratkozások exportálása</string>
|
||||
<string name="import_subscriptions_summary">Feliratkozások importálása korábbi .json-fájlból</string>
|
||||
<string name="export_subscriptions_summary">Feliratkozások exportálása .json-fájlba</string>
|
||||
<string name="import_from_previous_export">Importálás korábbi exportból</string>
|
||||
</resources>
|
||||
|
||||
@@ -434,7 +434,7 @@
|
||||
<string name="delete_downloaded_files">Hapus berkas yang diunduh</string>
|
||||
<string name="permission_display_over_apps">Izinkan untuk ditampilkan di atas aplikasi lain</string>
|
||||
<string name="app_language_title">Bahasa apl</string>
|
||||
<string name="systems_language">Default sistem</string>
|
||||
<string name="systems_language">Bawaan sistem</string>
|
||||
<string name="done">Selesai</string>
|
||||
<string name="seek_duration_title">Durasi maju/mundur cepat</string>
|
||||
<string name="subtitle_activity_recaptcha">Tekan \"Selesai\" saat selesai</string>
|
||||
@@ -746,7 +746,7 @@
|
||||
<string name="show_channel_tabs">Tab saluran</string>
|
||||
<string name="channel_tab_shorts">Shorts</string>
|
||||
<string name="loading_metadata_title">Memuat Metadata…</string>
|
||||
<string name="feed_fetch_channel_tabs">Dapatjan tab saluran</string>
|
||||
<string name="feed_fetch_channel_tabs">Dapatkan tab saluran</string>
|
||||
<string name="channel_tab_about">Tentang</string>
|
||||
<string name="channel_tab_albums">Album</string>
|
||||
<string name="feed_fetch_channel_tabs_summary">Tab untuk didapatkan ketika memperarui umpan. Opsi ini tidak memiliki efek jika saluran diperbarui menggunakan mode cepat.</string>
|
||||
@@ -828,9 +828,12 @@
|
||||
<string name="player_http_403">Kesalahan HTTP 403 diterima dari server saat memutar, dapat disebabkan oleh URL streaming kedaluwarsa atau pemblokiran IP</string>
|
||||
<string name="player_http_invalid_status">Kesalahan HTTP %1$s diterima dari server saat memutar</string>
|
||||
<string name="youtube_player_http_403">Kesalahan HTTP 403 diterima dari server saat memutar, dapat disebabkan oleh pemblokiran IP atau masalah deobfuskasi URL streaming</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s menolak memberikan data, meminta login untuk memastikan peminta bukan bot.\n\nAlamat IP Anda mungkin telah diblokir sementara oleh %1$s, Anda dapat menunggu beberapa saat atau beralih ke alamat IP yang berbeda (misalnya dengan mengaktifkan/menonaktifkan VPN, atau beralih dari WiFi ke data seluler).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s menolak memberikan data, meminta login untuk memastikan peminta bukan bot.\n\nAlamat IP Anda mungkin telah diblokir sementara oleh %1$s, Anda dapat menunggu beberapa saat atau beralih ke alamat IP yang berbeda (misalnya dengan mengaktifkan/menonaktifkan VPN, atau beralih dari WiFi ke data seluler).\n\nHarap lihat <a href=\"%2$s\"></a>entri Pertanyaan ini</a> untuk informasi lebih lanjut.</string>
|
||||
<string name="unsupported_content_in_country">Konten ini tidak tersedia untuk negara konten yang saat ini dipilih.\n\nUbah pilihan Anda dari “Pengaturan > Konten > Negara konten bawaan”.</string>
|
||||
<string name="short_thousand">%sK</string>
|
||||
<string name="short_million">%sM</string>
|
||||
<string name="short_billion">%sB</string>
|
||||
<string name="kao_dialog_warning">Pada Agustus 2025, Google mengumumkan bahwa mulai September 2026, pemasangan aplikasi akan memerlukan verifikasi pengembang untuk semua aplikasi Android pada perangkat bersertifikasi, termasuk yang dipasang di luar Play Store. Karena pengembang NewPipe tidak menyetujui persyaratan ini, NewPipe tidak akan lagi berfungsi pada perangkat Android bersertifikasi setelah waktu tersebut.</string>
|
||||
<string name="kao_dialog_more_info">Rincian</string>
|
||||
<string name="kao_solution">Solusi</string>
|
||||
</resources>
|
||||
|
||||
@@ -859,9 +859,29 @@
|
||||
<string name="player_http_403">Errore HTTP 403 ricevuto dal server durante la riproduzione, probabilmente causato dalla scadenza dell\'URL in streaming o da un divieto dell\'IP</string>
|
||||
<string name="player_http_invalid_status">Errore HTTP %1$s ricevuto dal server durante la riproduzione</string>
|
||||
<string name="youtube_player_http_403">Errore HTTP 403 ricevuto dal server durante la riproduzione, probabilmente causato da un divieto dell\'IP o problemi di de-offuscamento dell\'URL in streaming</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s ha rifiutato di fornire i dati, chiedendo un accesso per confermare che il richiedente non sia un bot.\n\nIl tuo IP potrebbe essere stato temporaneamente vietato da %1$s, puoi aspettare un po\' di tempo o passare ad un IP diverso (ad esempio accendendo/spegnendo una VPN, o passando dal WiFi ai dati mobili).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s ha rifiutato di fornire i dati, chiedendo un accesso per confermare che il richiedente non sia un bot.\n\nIl tuo IP potrebbe essere stato temporaneamente vietato da %1$s, puoi aspettare un po\' di tempo o passare ad un IP diverso (ad esempio accendendo/spegnendo una VPN, o passando dal WiFi ai dati mobili).\n\nLeggi <a href="%2$s">questa voce nelle FAQ</a> per maggiori informazioni.</string>
|
||||
<string name="unsupported_content_in_country">Questo contenuto non è disponibile per il Paese dei contenuti attualmente selezionato.\n\nModifica la selezione da \"Impostazioni > Contenuti > Paese dei contenuti predefinito\".</string>
|
||||
<string name="kao_dialog_warning">Ad agosto 2025, Google ha annunciato che a partire da settembre 2026, l\'installazione di app richiederà la verifica dello sviluppatore per tutte le app Android su dispositivi certificati, compresi quelli installati al di fuori del Play Store. Poiché gli sviluppatori di NewPipe non sono d\'accordo con questo requisito, NewPipe non funzionerà più su dispositivi Android certificati dopo quel mese.</string>
|
||||
<string name="kao_dialog_more_info">Dettagli</string>
|
||||
<string name="kao_solution">Soluzione</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Esportazione di %d iscrizione…</item>
|
||||
<item quantity="many">Esportazione di %d iscrizioni…</item>
|
||||
<item quantity="other">Esportazione di %d iscrizioni…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Caricamento di %d iscrizione…</item>
|
||||
<item quantity="many">Caricamento di %d iscrizioni…</item>
|
||||
<item quantity="other">Caricamento di %d iscrizioni…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importazione di %d iscrizione…</item>
|
||||
<item quantity="many">Importazione di %d iscrizioni…</item>
|
||||
<item quantity="other">Importazione di %d iscrizioni…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Importa iscrizioni</string>
|
||||
<string name="export_subscriptions_title">Esporta iscrizioni</string>
|
||||
<string name="import_subscriptions_summary">Importa iscrizioni da un\'esportazione .json precedente</string>
|
||||
<string name="export_subscriptions_summary">Esporta le tue iscrizioni su un file .json</string>
|
||||
<string name="import_from_previous_export">Importa da un\'esportazione precedente</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
@@ -223,4 +223,12 @@
|
||||
<string name="metadata_tags">Tibzimin</string>
|
||||
<string name="blank_page_summary">Asebter d ilem</string>
|
||||
<string name="settings_category_exoplayer_title">Iɣewwaṛen n ExoPlayer</string>
|
||||
<plurals name="days">
|
||||
<item quantity="one">%d n wass</item>
|
||||
<item quantity="other">%d n wussan</item>
|
||||
</plurals>
|
||||
<plurals name="videos">
|
||||
<item quantity="one">%s n tvidyut</item>
|
||||
<item quantity="other">%s n tvidyutin</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -831,9 +831,23 @@
|
||||
<string name="player_http_403">재생 중 서버에서 HTTP 403 오류를 수신했으며, 스트리밍 URL이 만료되었거나 IP 차단으로 인해 발생했을 수 있습니다</string>
|
||||
<string name="player_http_invalid_status">재생 중 서버에서 HTTP %1$s 오류를 수신했습니다</string>
|
||||
<string name="youtube_player_http_403">재생 중 서버에서 HTTP 403 오류를 수신했으며, 스트리밍 URL 역난독화 문제나 IP 차단 때문일 수 있습니다</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s에서 데이터 제공을 거부하고, 요청자가 봇이 아닌지 확인하기 위해 로그인을 요청하고 있습니다.\n\n아마 IP가 %1$s에서 임시 차단되었을 것이며, 잠시 기다리거나 다른 IP로 전환할 수 있습니다 (예를 들자면 VPN을 켜/끄거나, WiFi를 모바일 데이터로 바꾸세요).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s에서 데이터 제공을 거부하고, 요청자가 봇이 아닌지 확인하기 위해 로그인을 요청하고 있습니다.\n\n아마 IP가 %1$s에서 임시 차단되었을 것이며, 잠시 기다리거나 다른 IP로 전환할 수 있습니다 (예를 들어 VPN을 켜/끄거나, WiFi를 모바일 데이터로 바꿔 보세요).\n\n자세한 정보는 <a href="%2$s">여기 FAQ</a>를 확인하세요.</string>
|
||||
<string name="unsupported_content_in_country">이 콘텐츠는 현재 선택한 콘텐츠 지역에서 이용할 수 없습니다.\n\n\"설정 > 콘텐츠 > 기본 콘텐츠 국가\"에서 지역을 바꾸세요.</string>
|
||||
<string name="kao_dialog_warning">2025년 8월, Google은 2026년 9월부터 인증된 기기에 앱을 설치하려면 Play 스토어 외 앱을 포함한 모든 Android 앱에 대해 개발자 인증을 받아야 한다고 발표했습니다. NewPipe 개발자는 이 요구 사항에 동의하지 않으므로, 이 이후 NewPipe는 더 이상 인증된 Android 기기에서 동작하지 않을 것입니다.</string>
|
||||
<string name="kao_dialog_more_info">자세히</string>
|
||||
<string name="kao_solution">해결책</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="other">구독 %d건 내보내는 중…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="other">구독 %d건 불러오는 중…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="other">구독 %d건 가져오는 중…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">구독 가져오기</string>
|
||||
<string name="export_subscriptions_title">구독 내보내기</string>
|
||||
<string name="import_subscriptions_summary">이전에 내보낸 .json에서 구독을 가져옵니다</string>
|
||||
<string name="export_subscriptions_summary">구독 현황을 .json 파일로 내보냅니다</string>
|
||||
<string name="import_from_previous_export">이전 내보내기에서 가져오기</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -2,8 +2,8 @@
|
||||
<resources>
|
||||
<string name="caption_none">Nav Subtitri</string>
|
||||
<string name="playlist_thumbnail_change_success">Atskaņošanas saraksta attēls nomainīts.</string>
|
||||
<string name="playlist_creation_success">Atskaņošanas saraksts radīts</string>
|
||||
<string name="delete_playlist_prompt">Dzēst atskaņošanas sarakstu\?</string>
|
||||
<string name="playlist_creation_success">Atskaņošanas saraksts izveidots</string>
|
||||
<string name="delete_playlist_prompt">Vai tiešām vēlaties dzēst šo atskaņošanas sarakstu?</string>
|
||||
<string name="set_as_playlist_thumbnail">Iestatīt, kā atskaņošanas saraksta attēlu</string>
|
||||
<string name="add_to_playlist">Pievienot atskaņošanas sarakstam</string>
|
||||
<string name="name">Nosaukums</string>
|
||||
@@ -29,10 +29,10 @@
|
||||
<string name="new_and_hot">Jauns un populārs</string>
|
||||
<string name="top_50">Top 50</string>
|
||||
<string name="error_unable_to_load_comments">Nevarēja ielādēt komentārus</string>
|
||||
<string name="import_settings">Vai jūs vēlaties ievietot arī iestatījumus?</string>
|
||||
<string name="import_settings">Vai vēlaties ievietot arī iestatījumus?</string>
|
||||
<string name="override_current_data">Pašreizējie dati tiks aizstāti.</string>
|
||||
<string name="could_not_import_all_files">Uzmanību: Ne visas datnes varēja ievietot.</string>
|
||||
<string name="no_valid_zip_file">Nav derīgs ZIP fails</string>
|
||||
<string name="no_valid_zip_file">Nederīga ZIP datne</string>
|
||||
<string name="import_complete_toast">Ievietošana pabeigta</string>
|
||||
<string name="export_complete_toast">Eksportēts</string>
|
||||
<string name="select_a_kiosk">Atlasiet kiosku</string>
|
||||
@@ -48,7 +48,7 @@
|
||||
<string name="main_page_content">Galvenā lapa</string>
|
||||
<string name="title_most_played">Visvairāk Atskaņotais</string>
|
||||
<string name="title_last_played">Pēdējais atskaņotais</string>
|
||||
<string name="delete_item_search_history">Vai jūs tiešām vēlaties izdzēst šo vaicājumu no meklēšanas vēstures?</string>
|
||||
<string name="delete_item_search_history">Vai tiešām vēlaties izdzēst šo vaicājumu no meklēšanas vēstures?</string>
|
||||
<string name="action_history">Vēsture</string>
|
||||
<string name="title_activity_history">Vēsture</string>
|
||||
<string name="read_full_license">Izlasīt licenci</string>
|
||||
@@ -80,7 +80,7 @@
|
||||
<string name="recaptcha_request_toast">reCAPTCHA izaicinājums dots</string>
|
||||
<string name="subtitle_activity_recaptcha">Nospiediet \"Pabeigts\", kad to atrisinat</string>
|
||||
<string name="title_activity_recaptcha">reCAPTCHA izaicinājums</string>
|
||||
<string name="one_item_deleted">1 lieta izdzēsta.</string>
|
||||
<string name="one_item_deleted">1 vienums dzēsts.</string>
|
||||
<string name="msg_popup_permission">Šī atļauja ir nepieciešama, lai
|
||||
\natvērtu popup režīmā</string>
|
||||
<string name="no_available_dir">Lūdzu nosakiet lejupielādes mapi iestatījumos vēlāk</string>
|
||||
@@ -91,13 +91,13 @@
|
||||
<string name="msg_running">NewPipe lejupielādē</string>
|
||||
<string name="msg_error">Kļūda</string>
|
||||
<string name="msg_threads">Procesi</string>
|
||||
<string name="msg_name">Faila nosaukums</string>
|
||||
<string name="msg_name">Datnes nosaukums</string>
|
||||
<string name="ok">Labi</string>
|
||||
<string name="rename">Pārsaukt</string>
|
||||
<string name="dismiss">Noraidīt</string>
|
||||
<string name="checksum">Kontrolsumma</string>
|
||||
<string name="delete">Izdzēst</string>
|
||||
<string name="create">Radīt</string>
|
||||
<string name="delete">Dzēst</string>
|
||||
<string name="create">Izveidot / Saglabāt</string>
|
||||
<string name="pause">Pauzēt</string>
|
||||
<string name="start">Sākt</string>
|
||||
<string name="no_comments">Nav komentāru</string>
|
||||
@@ -168,16 +168,16 @@
|
||||
<string name="error_report_button_text">Ziņojiet pa e-pastu</string>
|
||||
<string name="sorry_string">Piedotiet, tam nevajadzēja notikt.</string>
|
||||
<string name="permission_display_over_apps">Dot atļauju rādīt pāri citām aplikācijām</string>
|
||||
<string name="restore_defaults_confirmation">Vai jūs tiešām vēlaties atjaunot noklusējuma vērtības?</string>
|
||||
<string name="restore_defaults_confirmation">Vai tiešām vēlaties atjaunot noklusējuma vērtības?</string>
|
||||
<string name="restore_defaults">Atjaunot noklusējuma vērtības</string>
|
||||
<string name="saved_tabs_invalid_json">Nevarēja nolasīt saglabātās cilnes, tādēļ izmanto noklusējuma</string>
|
||||
<string name="no_streams_available_download">Neviens video nav pieejams lejupielādei</string>
|
||||
<string name="error_occurred_detail">Notika kļūda: %1$s</string>
|
||||
<string name="file_name_empty_error">Faila nosaukums nevar būt tukšs</string>
|
||||
<string name="file_name_empty_error">Datnes nosaukums nevar būt tukšs</string>
|
||||
<string name="invalid_file">Datne neeksistē vai nav dota atļauja to lasīt vai rakstīt</string>
|
||||
<string name="invalid_source">Tāds fails/saturs neeksistē</string>
|
||||
<string name="invalid_source">Tāda datne/saturs neeksistē</string>
|
||||
<string name="invalid_directory">Tāda mape neeksistē</string>
|
||||
<string name="missing_file">Fails pārvietots vai izdzēsts</string>
|
||||
<string name="missing_file">Datne pārvietota vai dzēsta</string>
|
||||
<string name="audio_streams_empty">Netika atrasts audio</string>
|
||||
<string name="video_streams_empty">Netika atrasti video</string>
|
||||
<string name="external_player_unsupported_link_type">Ārējie atskaņotāj neatbalsta šāda tipa saites</string>
|
||||
@@ -190,26 +190,26 @@
|
||||
<string name="parsing_error">Nevarēja apstrādāt mājaslapu</string>
|
||||
<string name="could_not_load_thumbnails">Nevarēja ielādēt visus video attēlus</string>
|
||||
<string name="network_error">Tīkla kļūda</string>
|
||||
<string name="download_to_sdcard_error_message">Lejupielādēt uz SD karti nav iespējams. Atiestatīt lejupielāžu mapes lokāciju\?</string>
|
||||
<string name="download_to_sdcard_error_message">Lejupielāde ārējā SD kartē nav iespējama. Vai tiešām vēlaties atiestatīt lejupielāžu mapi?</string>
|
||||
<string name="download_to_sdcard_error_title">Ārējā krātuve nepieejama</string>
|
||||
<string name="general_error">Kļūda</string>
|
||||
<string name="search_history_deleted">Meklēšanas vēsture izdzēsta</string>
|
||||
<string name="delete_search_history_alert">Vai tiešām izdzēst visu meklēšanas vēsturi?</string>
|
||||
<string name="clear_search_history_summary">Izdzēš meklēto vārdu vēsturi</string>
|
||||
<string name="search_history_deleted">Meklēšanas vēsture dzēsta</string>
|
||||
<string name="delete_search_history_alert">Vai tiešām vēlaties izdzēst visu meklēšanas vēsturi?</string>
|
||||
<string name="clear_search_history_summary">Izdzēš visus meklēšanas vaicājumus</string>
|
||||
<string name="clear_search_history_title">Notīrīt meklēšanas vēsturi</string>
|
||||
<string name="watch_history_states_deleted">Atskaņošanas pozīcikas izdzēstas</string>
|
||||
<string name="delete_playback_states_alert">Izdzēst visas atskaņošanas pozīcijas\?</string>
|
||||
<string name="watch_history_states_deleted">Atskaņošanas pozīcijas dzēstas</string>
|
||||
<string name="delete_playback_states_alert">Vai tiešām vēlaties dzēst visas atskaņošanas pozīcijas?</string>
|
||||
<string name="clear_playback_states_summary">Izdzēš visas atskaņošanas pozīcijas</string>
|
||||
<string name="clear_playback_states_title">Izdzēst atskaņošanas pozīcijas</string>
|
||||
<string name="watch_history_deleted">Skatīšanās vēsture izdzēsta</string>
|
||||
<string name="delete_view_history_alert">Izdzēst visu skatīšanās vēsturi\?</string>
|
||||
<string name="clear_views_history_summary">Izdzēš atskaņoto videoklipu un atskaņošanas pozīciju vēsturi</string>
|
||||
<string name="clear_playback_states_title">Notīrīt atskaņošanas pozīcijas</string>
|
||||
<string name="watch_history_deleted">Skatīšanās vēsture dzēsta</string>
|
||||
<string name="delete_view_history_alert">Vai tiešām vēlaties dzēst visu skatīšanās vēsturi?</string>
|
||||
<string name="clear_views_history_summary">Izdzēš atskaņoto video / audio un atskaņošanas pozīciju vēsturi</string>
|
||||
<string name="clear_views_history_title">Notīrīt skatīšanās vēsturi</string>
|
||||
<string name="clear_cookie_summary">Notīrīt sīkfailus , kurus NewPipe saglabā, kad jūs atrisinat reCAPTCHA</string>
|
||||
<string name="clear_cookie_summary">Izdzēš sīkdatnes, kuras NewPipe uzglabā, kad jūs atrisiniet reCAPTCHA</string>
|
||||
<string name="export_data_summary">Eksportēt vēsturi, abonementus, atskaņošanas sarakstus un iestatījumus</string>
|
||||
<string name="import_data_summary">Aizstās jūsu pašreizējo vēsturi, abonementus, atskaņošanas sarakstus un (pēc izvēles) iestatījumus</string>
|
||||
<string name="recaptcha_cookies_cleared">reCAPTCHA sīkfaili tika izdzēsti</string>
|
||||
<string name="clear_cookie_title">Izdzēst reCAPTCHA sīkfailus</string>
|
||||
<string name="recaptcha_cookies_cleared">reCAPTCHA sīkdatnes dzēstas</string>
|
||||
<string name="clear_cookie_title">Notīrīt reCAPTCHA sīkdatnes</string>
|
||||
<string name="export_data_title">Eksportēt datubāzi</string>
|
||||
<string name="import_data_title">Ievietot datubāzi</string>
|
||||
<string name="switch_to_main">Pārslēgt uz Galveno</string>
|
||||
@@ -217,14 +217,14 @@
|
||||
<string name="switch_to_background">Pārslēgt uz Fonu</string>
|
||||
<string name="unknown_content">[Nezināms]</string>
|
||||
<string name="app_update_notification_channel_description">Paziņojumi par jaunām NewPipe versijām</string>
|
||||
<string name="app_update_notification_channel_name">Aplikācijas atjauninājuma paziņojums</string>
|
||||
<string name="app_update_notification_channel_name">Lietotnes atjauninājuma paziņojums</string>
|
||||
<string name="notification_channel_description">Paziņojumi priekš NewPipe atskaņotāja</string>
|
||||
<string name="notification_channel_name">NewPipe paziņojums</string>
|
||||
<string name="file">Fails</string>
|
||||
<string name="file">Datne</string>
|
||||
<string name="just_once">Tikai Vienreiz</string>
|
||||
<string name="always">Vienmēr</string>
|
||||
<string name="play_all">Atskaņot Visu</string>
|
||||
<string name="file_deleted">Fails izdzēsts</string>
|
||||
<string name="file_deleted">Datne dzēsta</string>
|
||||
<string name="undo">Atsaukt</string>
|
||||
<string name="best_resolution">Labākā izšķirtspēja</string>
|
||||
<string name="clear">Notīrīt</string>
|
||||
@@ -329,7 +329,7 @@
|
||||
<string name="new_seek_duration_toast">ExoPlayer ierobežojumu dēļ tīšanas solis tika iestatīts uz %d sekundēm</string>
|
||||
<string name="remove_watched_popup_partially_watched_streams">Jā, un daļēji skatītos</string>
|
||||
<string name="remove_watched_popup_warning">Tiešraides, kas iepriekš noskatītas un pēc tam pievienotas atskaņošanas sarakstam, tiks noņemtas. \nVai tiešām turpināt?</string>
|
||||
<string name="remove_watched_popup_title">Vai tiešām noņemt skatītās tiešraides?</string>
|
||||
<string name="remove_watched_popup_title">Vai tiešām vēlaties noņemt skatītās tiešraides?</string>
|
||||
<string name="remove_watched">Noņemt skatīto</string>
|
||||
<string name="systems_language">System default</string>
|
||||
<string name="app_language_title">Lietotnes valoda</string>
|
||||
@@ -349,17 +349,17 @@
|
||||
<string name="max_retry_desc">Maksimālais mēģinājumu skaits pirms lejupielādes atcelšanas</string>
|
||||
<string name="max_retry_msg">Maksimālais atkārtoto mēģinājumu skaits</string>
|
||||
<string name="stop">Stop</string>
|
||||
<string name="delete_downloaded_files">Dzēst lejupielādētos failus</string>
|
||||
<string name="confirm_prompt">Vai vēlaties notīrīt lejupielāžu vēsturi vai izdzēst visus lejupielādētos failus\?</string>
|
||||
<string name="delete_downloaded_files">Dzēst lejupielādētās datnes</string>
|
||||
<string name="confirm_prompt">Vai tiešām vēlaties dzēst lejupielāžu vēsturi un visas lejupielādētās datnes?</string>
|
||||
<string name="clear_download_history">Notīrīt lejupielāžu vēsturi</string>
|
||||
<string name="error_download_resource_gone">Nevar atgūt šo lejupielādi</string>
|
||||
<string name="error_timeout">Savienojums pārtraukts</string>
|
||||
<string name="error_progress_lost">Progress zaudēts, jo fails tika izdzēsts</string>
|
||||
<string name="error_progress_lost">Progress zaudēts, jo datne tika dzēsta</string>
|
||||
<string name="error_insufficient_storage_left">Ierīcē nav vietas</string>
|
||||
<string name="error_postprocessing_stopped">Strādājot ar failu, NewPipe tika aizvērts</string>
|
||||
<string name="error_postprocessing_failed">Pēcapstrāde neizdevās</string>
|
||||
<string name="error_http_not_found">Nav atrasts</string>
|
||||
<string name="error_http_unsupported_range">Serveris nepieņem vairāku procesu lejupielādes, mēģiniet vēlreiz ar @ string / msg_threads = 1</string>
|
||||
<string name="error_http_unsupported_range">Serveris nepieņem daudzpavedienu lejupielādes, mēģiniet vēlreiz ar @string/msg_threads = 1</string>
|
||||
<string name="error_http_no_content">Serveris nesūta datus</string>
|
||||
<string name="error_connect_host">Nevar izveidot savienojumu ar serveri</string>
|
||||
<string name="error_unknown_host">Nevarēja atrast serveri</string>
|
||||
@@ -370,8 +370,8 @@
|
||||
<string name="download_already_pending">Ir gaidāma lejupielāde ar šo nosaukumu</string>
|
||||
<string name="download_already_running">Notiek lejupielāde ar šo nosaukumu</string>
|
||||
<string name="overwrite_failed">nevar pārrakstīt datni</string>
|
||||
<string name="overwrite_finished_warning">Lejupielādēts fails ar šo nosaukumu jau pastāv</string>
|
||||
<string name="overwrite_unrelated_warning">Fails ar šo nosaukumu jau pastāv</string>
|
||||
<string name="overwrite_finished_warning">Lejupielādētā datne ar šādu nosaukumu jau pastāv</string>
|
||||
<string name="overwrite_unrelated_warning">Datne ar šādu nosaukumu jau pastāv</string>
|
||||
<string name="overwrite">Pārrakstīt</string>
|
||||
<string name="generate_unique_name">Ģenerēt unikālu nosaukumu</string>
|
||||
<string name="permission_denied">Darbību noraidīja sistēma</string>
|
||||
@@ -385,9 +385,7 @@
|
||||
<string name="playback_step">Solis</string>
|
||||
<string name="skip_silence_checkbox">Klusuma brīžos patīt uz priekšu</string>
|
||||
<string name="unhook_checkbox">Atvienot (var izraisīt traucējumus)</string>
|
||||
<string name="import_network_expensive_warning">Paturiet prātā, ka šī darbība var pieprasīt lielu datu daudzumu
|
||||
\n
|
||||
\nVai vēlaties turpināt\?</string>
|
||||
<string name="import_network_expensive_warning">Paturiet prātā, ka šī darbība var pieprasīt lielu datu daudzumu\n\nVai vēlaties turpināt?</string>
|
||||
<string name="drawer_close">Aizvērt Atvilkni</string>
|
||||
<string name="drawer_open">Atvērt Atvilkni</string>
|
||||
<string name="most_liked">Vispopulārākais</string>
|
||||
@@ -420,7 +418,7 @@
|
||||
<string name="paused">Pausēts</string>
|
||||
<string name="missions_header_pending">Gaida</string>
|
||||
<string name="missions_header_finished">Pabeigts</string>
|
||||
<string name="app_update_available_notification_title">Ir pieejams Newpipe atjauninājums!</string>
|
||||
<string name="app_update_available_notification_title">Pieejama jauna NewPipe versija!</string>
|
||||
<string name="auto">Automātiski</string>
|
||||
<string name="grid">Tīkls</string>
|
||||
<string name="list">Saraksts</string>
|
||||
@@ -452,7 +450,7 @@
|
||||
<string name="show_original_time_ago_title">Rādīt oriģinālo laiku uz lietām</string>
|
||||
<string name="show_memory_leaks">Rādīt atmiņas noplūdes</string>
|
||||
<string name="caption_setting_title">Subtitri</string>
|
||||
<string name="caption_auto_generated">Automātiski radīti</string>
|
||||
<string name="caption_auto_generated">Automātiski izveidots</string>
|
||||
<string name="resize_zoom">Pietuvināt</string>
|
||||
<string name="resize_fill">Piepildīt</string>
|
||||
<string name="resize_fit">Pielāgot</string>
|
||||
@@ -557,7 +555,7 @@
|
||||
<string name="use_external_video_player_summary">Dažās izšķirtspējās nav pieejami skaņas celiņi</string>
|
||||
<string name="use_external_video_player_title">Izmantot ārējo video atskaņotāju</string>
|
||||
<string name="share_dialog_title">Kopīgot ar</string>
|
||||
<string name="search_showing_result_for">Tiek rādīti %s rezultāti</string>
|
||||
<string name="search_showing_result_for">Tiek rādīti %s vaicājuma rezultāti</string>
|
||||
<string name="did_you_mean">Varbūt jūs gribējāt meklēt \"%1$s\"?</string>
|
||||
<string name="settings">Iestatījumi</string>
|
||||
<string name="search">Meklēt</string>
|
||||
@@ -588,7 +586,7 @@
|
||||
<string name="metadata_language">Valoda</string>
|
||||
<string name="metadata_age_limit">Vecuma ierobežojums</string>
|
||||
<string name="metadata_licence">License</string>
|
||||
<string name="metadata_tags">Tagi</string>
|
||||
<string name="metadata_tags">Birkas</string>
|
||||
<string name="metadata_category">Kategorija</string>
|
||||
<string name="downloads_storage_ask_summary_no_saf_notice">Jums tiks jautāts, kur saglabāt katru lejupielādi</string>
|
||||
<string name="dont_show">Nerādīt</string>
|
||||
@@ -606,13 +604,13 @@
|
||||
<string name="local_search_suggestions">Lokālos meklēšanas ieteikumus</string>
|
||||
<string name="high_quality_larger">Augstas kvalitātes (lielāks)</string>
|
||||
<string name="check_for_updates">Pārbaudīt atjauninājumus</string>
|
||||
<string name="manual_update_description">Pašrocīgi pārbaudīt jaunas versijas pieejamību</string>
|
||||
<string name="manual_update_description">Pašrocīgi veikt jaunas versijas pārbaudi</string>
|
||||
<string name="seekbar_preview_thumbnail_title">Video atskaņošanas joslas sīktēla priekšskatījums</string>
|
||||
<string name="checking_updates_toast">Pārbauda, vai ir atjauninājumi…</string>
|
||||
<string name="checking_updates_toast">Notiek atjauninājumu pārbaude…</string>
|
||||
<string name="downloads_storage_use_saf_summary_api_29">Sākot ar Android 10, tikai“Krātuves Piekļuves Sistēma” ir atbalstīta</string>
|
||||
<string name="feed_load_error_account_info">Nevarēja ielādēt straumi priekš \'%s\'.</string>
|
||||
<string name="feed_load_error">Kļūda lādējot plūsmu</string>
|
||||
<string name="feed_load_error_terminated">Autora konts tika slēgts.\nNewPipe turpmāk vairs nevarēs ielādēt šī kanāla plūsmas saturu.\nVai tiešām atteikties no šī kanāla abonēšanas?</string>
|
||||
<string name="feed_load_error_terminated">Autora konts tika slēgts.\nNewPipe turpmāk vairs nevarēs ielādēt šī kanāla saturu.\nVai tiešām vēlaties atteikties no šī kanāla abonēšanas?</string>
|
||||
<string name="feed_load_error_fast_unknown">Ātrās straumes režīms nesniedz vairāk informācijas par šo.</string>
|
||||
<string name="description_select_disable">Izslēgt teksta atlasīšanu video aprakstā</string>
|
||||
<string name="metadata_privacy_internal">Iekšeji</string>
|
||||
@@ -627,16 +625,16 @@
|
||||
<string name="mark_as_watched">Atzīmēt kā noskatītu</string>
|
||||
<string name="processing_may_take_a_moment">Apstrādā... Var aizņemt kādu laiku</string>
|
||||
<plurals name="deleted_downloads_toast">
|
||||
<item quantity="zero">Izdzēsa %1$s lejupielāžu</item>
|
||||
<item quantity="one">Izdzēsa %1$s lejupielādi</item>
|
||||
<item quantity="other">Izdzēsa %1$s lejupielādes</item>
|
||||
<item quantity="zero">Dzēstas %1$s lejupielādes</item>
|
||||
<item quantity="one">Dzēsta %1$s lejupielāde</item>
|
||||
<item quantity="other">Dzēstas %1$s lejupielādes</item>
|
||||
</plurals>
|
||||
<plurals name="download_finished_notification">
|
||||
<item quantity="zero">%s lejupielādes pabeigtas</item>
|
||||
<item quantity="one">%s lejupielāde pabeigta</item>
|
||||
<item quantity="other">%s lejupielādes pabeigtas</item>
|
||||
</plurals>
|
||||
<string name="description_select_note">Tagad varat atlasīt tekstu video aprakstā.</string>
|
||||
<string name="description_select_note">Tagad variet atlasīt tekstu video aprakstā.</string>
|
||||
<string name="notifications">Paziņojumi</string>
|
||||
<string name="crash_the_player">Avarēt atskaņotāju</string>
|
||||
<string name="settings_category_player_notification_summary">Pielāgojiet pašlaik atskaņotās plūsmas paziņojumu</string>
|
||||
@@ -648,7 +646,7 @@
|
||||
<item quantity="one">%s jauna tiešraide</item>
|
||||
<item quantity="other">%s jaunas tiešraides</item>
|
||||
</plurals>
|
||||
<string name="streams_notification_channel_description">Paziņojumi par jaunām tiešraidēm abonementos</string>
|
||||
<string name="streams_notification_channel_description">\@string/enable_streams_notifications_summary</string>
|
||||
<string name="faq_title">Bieži uzdotie jautājumi</string>
|
||||
<string name="error_report_channel_description">Paziņojumi, lai ziņotu par kļūdām</string>
|
||||
<string name="error_report_channel_name">Kļūdas ziņojuma paziņojums</string>
|
||||
@@ -665,11 +663,11 @@
|
||||
<string name="leak_canary_not_available">LeakCanary nav pieejams</string>
|
||||
<string name="create_error_notification">Izveidot kļūdas paziņojumu</string>
|
||||
<string name="any_network">Jebkurš tīkls</string>
|
||||
<string name="app_update_unavailable_toast">Jums ir jaunākā NewPipe versija</string>
|
||||
<string name="app_update_unavailable_toast">Jūs jau izmantojiet jaunāko NewPipe versiju</string>
|
||||
<string name="ignore_hardware_media_buttons_summary">Noder, piemēram, kad lietojiet austiņas ar bojātām pogām</string>
|
||||
<string name="prefer_descriptive_audio_summary">Atskaņos skaņas celiņu ar audio aprakstiem vājredzīgajiem, ja tāds ir pieejams</string>
|
||||
<string name="ignore_hardware_media_buttons_title">Ignorēt pieslēgtās ierīces multimēdiju pogas</string>
|
||||
<string name="delete_downloaded_files_confirm">Izdzēst visus lejupielādētos failus\?</string>
|
||||
<string name="delete_downloaded_files_confirm">Vai tiešām vēlaties dzēst visas lejupielādētās datnes?</string>
|
||||
<string name="feed_new_items">Jaunumi kanālā</string>
|
||||
<string name="prefer_original_audio_title">Dot priekšroku oriģinālajai skaņai</string>
|
||||
<string name="prefer_original_audio_summary">Atskaņos oriģinālo skaņas celiņu neatkarīgi no iestatītās valodas</string>
|
||||
@@ -689,15 +687,15 @@
|
||||
<string name="playlist_add_stream_success_duplicate">Dublikāts pievienots %d reizi(-es)</string>
|
||||
<string name="show_crash_the_player_title">Rādīt \"avarēt atskaņotāju\"</string>
|
||||
<string name="card">Karte</string>
|
||||
<string name="app_update_available_notification_text">Spiediet, lai lejupielādētu %s</string>
|
||||
<string name="remove_duplicates_title">Dzēst dublikātus\?</string>
|
||||
<string name="remove_duplicates_message">Vai vēlaties dzēst visus tiešraižu dublikātus šajā sarakstā\?</string>
|
||||
<string name="app_update_available_notification_text">Nospiediet, lai lejupielādētu %s</string>
|
||||
<string name="remove_duplicates_title">Vai tiešām vēlaties dzēst dublikātus?</string>
|
||||
<string name="remove_duplicates_message">Vai tiešām vēlaties noņemt visus dublētos vienumus šajā atskaņošanas sarakstā?</string>
|
||||
<string name="feed_show_hide_streams">Rādīt/slēpt tiešraides</string>
|
||||
<string name="fast_mode">Ātrais režīms</string>
|
||||
<string name="enable_streams_notifications_summary">Informē par jaunām abonementu tiešraidēm</string>
|
||||
<string name="enable_streams_notifications_summary">Informē, ja pieejami jauni video / audio abonementos</string>
|
||||
<string name="percent">Procenti</string>
|
||||
<string name="semitone">Pustonis</string>
|
||||
<string name="enable_streams_notifications_title">Paziņojumi par jaunām tiešraidēm</string>
|
||||
<string name="enable_streams_notifications_title">Paziņot par jauniem video / audio</string>
|
||||
<string name="streams_notifications_interval_title">Pārbaužu biežums</string>
|
||||
<string name="main_tabs_position_summary">Galvenās cilnes atlasītāja pārvietošana uz apakšu</string>
|
||||
<string name="notification_actions_summary_android13">Rediģējiet katru zemāk redzamo paziņojuma darbību, pieskaroties tai. Pirmās trīs darbības (atskaņot/pauze, iepriekšējais un nākamais) ir sistēmas iestatītas, un tās nevar pielāgot.</string>
|
||||
@@ -714,16 +712,14 @@
|
||||
<string name="channel_tab_about">Par</string>
|
||||
<string name="metadata_thumbnails">Sīkattēli</string>
|
||||
<string name="sort">Kārtot</string>
|
||||
<string name="auto_update_check_description">NewPipe pati var automātiski pārbaudīt jaunas versijas pieejamību laiku pa laikam un informēt jūs, kad tā ir pieejama.\nVai jūs tiešām gribiet ieslēgt šo funkciju?</string>
|
||||
<string name="auto_update_check_description">NewPipe pati var veikt jaunas versijas pārbaudi laiku pa laikam un informēt jūs, kad tā ir pieejama.\nVai tiešām vēlaties ieslēgt šo funkciju?</string>
|
||||
<string name="no_appropriate_file_manager_message">Netika atrasts atbilstošs failu pārvaldnieks šai darbībai.
|
||||
\nLūdzu instalējiet failu pārvaldnieku vai pamēģiniet atspējot \'%s\' lejuplādēšanas iestatījumos</string>
|
||||
<string name="audio_track_type_original">oriģinālais</string>
|
||||
<string name="streams_notifications_network_title">Nepieciešams tīkla savienojums</string>
|
||||
<string name="reset_settings_summary">Atiestata visus iestatījumus uz to sākotnējām vērtībām</string>
|
||||
<string name="reset_settings_title">Atiestatīt iestatījumus</string>
|
||||
<string name="reset_all_settings">Visu iestatījumu atiestatošana atmetīs visus jūsu izvēlētos iestatījumus un restartēs aplikāciju.
|
||||
\n
|
||||
\nVai jūs esat droši, ka vēlaties turpināt?</string>
|
||||
<string name="reset_all_settings">Atiestatot iestatījumus, visi jūsu iestatītie iestatījumi tiks atmesti uz to noklusētajām vērtībām un lietotne palaista pa jaunu.\n\nVai tiešām vēlaties turpināt?</string>
|
||||
<string name="night_theme_available">Šī opcija ir pieejama tikai, ja %s ir izvēlēts kā motīvs</string>
|
||||
<string name="detail_pinned_comment_view_description">Piespraustais komentārs</string>
|
||||
<string name="notifications_disabled">Paziņojumi ir atspējoti</string>
|
||||
@@ -836,4 +832,5 @@
|
||||
<string name="kao_dialog_more_info">Detalizētāka informācija</string>
|
||||
<string name="kao_solution">Risinājums</string>
|
||||
<string name="migration_info_6_7_message">SoundCloud likvidēja oriģinālos Top 50 topus. Atbilstošā cilne noņemta no jūsu galvenās lapas.</string>
|
||||
<string name="permission_display_over_apps_message">Lai varētu izmantot uznirstošo atskaņotāju, lūdzu, atlasiet %1$s šajā Android iestatījumu izvēlnē un iespējojiet %2$s.</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -847,4 +847,9 @@
|
||||
<string name="youtube_player_http_403">HTTP-fout 403 ontvangen van de server tijdens het afspelen, waarschijnlijk veroorzaakt door een ip-blokkade of problemen met de deobfuscatie van de streaming-url</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s weigerde gegevens te verstrekken en vroeg om een login om te bevestigen dat de aanvrager geen bot is.\n\nUw ip-adres is mogelijk tijdelijk geblokkeerd door %1$s. U kunt even wachten of overschakelen naar een ander ip-adres (bijvoorbeeld door een vpn in of uit te schakelen, of door over te schakelen van wifi naar mobiele data).</string>
|
||||
<string name="unsupported_content_in_country">Deze inhoud is niet beschikbaar voor het momenteel geselecteerde inhoudsland.\n\nWijzig uw selectie via ‘Instellingen > Inhoud > Standaardland voor inhoud’.</string>
|
||||
<string name="kao_dialog_more_info">Details</string>
|
||||
<string name="kao_solution">Oplossing</string>
|
||||
<string name="import_subscriptions_title">Abonnementen importeren</string>
|
||||
<string name="export_subscriptions_title">Abonnementen exporteren</string>
|
||||
<string name="import_from_previous_export">Importeren vanuit vorige export</string>
|
||||
</resources>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<string name="subscription_change_failed">ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ</string>
|
||||
<string name="show_info">ਜਾਣਕਾਰੀ ਵਿਖਾਓ</string>
|
||||
<string name="subscription_update_failed">ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਅਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ</string>
|
||||
<string name="tab_subscriptions">ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ</string>
|
||||
<string name="tab_subscriptions">ਸਬਸਕ੍ਰਿਪਸ਼ਨਜ਼</string>
|
||||
<string name="tab_bookmarks">ਬੁੱਕਮਾਰਕ ਕੀਤੀਆਂ ਪਲੇਲਿਸਟਾਂ</string>
|
||||
<string name="fragment_feed_title">ਨਵਾਂ ਕੀ ਹੈ</string>
|
||||
<string name="controls_background_title">ਬੈਕਗ੍ਰਾਊਂਡ</string>
|
||||
@@ -104,10 +104,10 @@
|
||||
<string name="switch_to_background">ਬੈਕਗ੍ਰਾਊਂਡ ਮੋਡ ਵਿੱਚ ਚਲਾਓ</string>
|
||||
<string name="switch_to_popup">ਪੌਪ-ਅਪ ਮੋਡ ਵਿੱਚ ਚਲਾਓ</string>
|
||||
<string name="switch_to_main">ਮੇਨ ਤੇ ਚਲਾਓ</string>
|
||||
<string name="import_data_title">ਡਾਟਾਬੇਸ ਆਯਾਤ ਕਰੋ</string>
|
||||
<string name="export_data_title">ਡਾਟਾਬੇਸ ਨਿਰਯਾਤ ਕਰੋ</string>
|
||||
<string name="import_data_title">ਡਾਟਾਬੇਸ ਇੰਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="export_data_title">ਡਾਟਾਬੇਸ ਐਕਸਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="import_data_summary">ਤੁਹਾਡੇ ਮੌਜੂਦਾ ਇਤਿਹਾਸ, ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ, ਪਲੇਲਿਸਟਾਂ ਅਤੇ (ਚੋਣਵੇਂ ਤੌਰ \'ਤੇ) ਸੈਟਿੰਗਾਂ ਨੂੰ ਨਵੀਆਂ ਨਾਲ ਬਦਲ ਦਿੰਦਾ ਹੈ</string>
|
||||
<string name="export_data_summary">ਇਤਿਹਾਸ, ਸੁਬਸਕ੍ਰਿਪਸ਼ਨਾਂ, ਪਲੇਲਿਸਟਾਂ ਅਤੇ ਸੈਟਿੰਗਾਂ ਨਿਰਯਾਤ ਕਰੋ</string>
|
||||
<string name="export_data_summary">ਇਤਿਹਾਸ, ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ, ਪਲੇਲਿਸਟਾਂ ਅਤੇ ਸੈਟਿੰਗਾਂ ਐਕਸਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="clear_views_history_title">ਵੇਖੇ ਗਏ ਵੀਡੀਓਜ਼ ਦੀ ਸੂਚੀ ਮਿਟਾਓ</string>
|
||||
<string name="clear_views_history_summary">ਚਲਾਈਆਂ ਗਈਆਂ ਸਟ੍ਰੀਮਾਂ ਦੇ ਇਤਿਹਾਸ ਅਤੇ ਪਲੇ-ਸਥਿਤੀਆਂ ਨੂੰ ਮਿਟਾਉਂਦਾ ਹੈ</string>
|
||||
<string name="delete_view_history_alert">ਕੀ ਵੇਖੇ ਗਏ ਵੀਡੀਓਜ਼ ਦਾ ਇਤਿਹਾਸ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇ\?</string>
|
||||
@@ -273,7 +273,7 @@
|
||||
<string name="export_to">ਤੇ ਐਕਸਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="import_ongoing">ਇੰਪੋਰਟ ਹੋ ਰਿਹਾ ਹੈ…</string>
|
||||
<string name="export_ongoing">ਐਕਸਪੋਰਟ ਹੋ ਰਿਹਾ ਹੈ…</string>
|
||||
<string name="import_file_title">ਇੰਪੋਰਟ ਫਾਈਲ</string>
|
||||
<string name="import_file_title">ਫ਼ਾਈਲ ਇੰਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="previous_export">ਪਿੱਛਲਾ ਐਕਸਪੋਰਟ</string>
|
||||
<string name="subscriptions_import_unsuccessful">ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਇੰਪੋਰਟ ਨਹੀਂ ਹੋ ਸਕੀਆਂ</string>
|
||||
<string name="subscriptions_export_unsuccessful">ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਐਕਸਪੋਰਟ ਨਹੀਂ ਹੋ ਸਕੀਆਂ</string>
|
||||
@@ -430,7 +430,7 @@
|
||||
<string name="description_select_disable">ਵੇਰਵੇ \'ਚੋਂ ਲਿਖਤ ਚੁਣਨਾ ਬੰਦ ਕਰੋ</string>
|
||||
<string name="description_select_enable">ਵੇਰਵੇ \'ਚੋਂ ਲਿਖਤ ਚੁਣਨਾ ਚਾਲੂ ਕਰੋ</string>
|
||||
<string name="description_select_note">ਤੁਸੀਂ ਹੁਣ ਵੇਰਵੇ \'ਚੋਂ ਲਿਖਤ ਨੂੰ ਚੁਣ ਸਕਦੇ ਹੋ। ਨੋਟ ਕਰੋ ਕਿ ਪੰਨਾ ਜਗ-ਬੁੱਝ ਸਕਦਾ ਹੈ ਅਤੇ ਚੋਣ ਮੋਡ ਵਿੱਚ ਹੋਣ ਵੇਲੇ ਲਿੰਕ ਕਲਿੱਕ ਕਰਨ ਯੋਗ ਨਹੀਂ ਹੋ ਸਕਦੇ ਹਨ।</string>
|
||||
<string name="download_has_started">ਡਾਊਨਲੋਡ ਸ਼ੁਰੂ ਹੋ ਗਿਐ</string>
|
||||
<string name="download_has_started">ਡਾਊਨਲੋਡ ਸ਼ੁਰੂ ਹੋਇਆ</string>
|
||||
<string name="select_night_theme_toast">ਤੁਸੀਂ ਆਪਣੀ ਪਸੰਦੀਦਾ ਰਾਤ ਦੀ ਥੀਮ ਹੇਠਾਂ ਚੁਣ ਸਕਦੇ ਹੋ</string>
|
||||
<string name="night_theme_summary">ਆਪਣੀ ਪਸੰਦੀਦਾ ਰਾਤ ਦੀ ਥੀਮ ਚੁਣੋ — %s</string>
|
||||
<string name="auto_device_theme_title">ਆਟੋਮੈਟਿਕ (ਡਿਵਾਈਸ ਥੀਮ)</string>
|
||||
@@ -494,8 +494,8 @@
|
||||
<item quantity="other">%d ਸਕਿੰਟ</item>
|
||||
</plurals>
|
||||
<string name="remove_watched_popup_partially_watched_streams">ਹਾਂ, ਅਤੇ ਅੱਧ-ਪਚੱਧੀਆਂ ਵੇਖੀਆਂ ਹੋਈਆਂ ਵੀ</string>
|
||||
<string name="remove_watched_popup_warning">ਪਲੇਲਿਸਟ ਵਿੱਚ ਸ਼ਾਮਿਲ ਪਹਿਲਾਂ ਤੇ ਬਾਅਦ ਵਿੱਚ ਵੇਖੇ ਜਾ ਚੁੱਕੇ ਵੀਡੀਓ ਹਟਾ ਦਿੱਤੇ ਜਾਣਗੇ। \nਕੀ ਵਾਕਿਆ ਹੀ ਤੁਸੀਂ ਇਹਨਾਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਸ ਕਾਰਵਾਈ ਨੂੰ ਵਾਪਸ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਣਾ!</string>
|
||||
<string name="remove_watched_popup_title">ਵੇਖੇ ਹੋਏ ਵੀਡੀਓ ਹਟਾ ਦੇਈਏ?</string>
|
||||
<string name="remove_watched_popup_warning">ਪਲੇਲਿਸਟ ਵਿੱਚ ਸ਼ਾਮਿਲ ਪਹਿਲਾਂ ਤੇ ਬਾਅਦ ਵਿੱਚ ਵੇਖੀਆਂ ਸਟ੍ਰੀਮਾਂ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।\nਕੀ ਤੁਸੀਂ ਯਕੀਨਨ ਇਹ ਚਾਹੁੰਦੇ ਹੋ?</string>
|
||||
<string name="remove_watched_popup_title">ਕੀ ਵੇਖੀਆਂ ਸਟ੍ਰੀਮਾਂ ਨੂੰ ਹਟਾਉਣਾ ਹੈ?</string>
|
||||
<string name="remove_watched">ਵੇਖੇ ਹੋਏ ਨੂੰ ਹਟਾਓ</string>
|
||||
<string name="systems_language">ਸਿਸਟਮ ਡਿਫ਼ਾਲਟ</string>
|
||||
<string name="app_language_title">ਐਪ ਦੀ ਭਾਸ਼ਾ</string>
|
||||
@@ -651,8 +651,7 @@
|
||||
<item quantity="other">%1$s ਡਾਊਨਲੋਡ ਹਟਾਏ</item>
|
||||
</plurals>
|
||||
<string name="detail_sub_channel_thumbnail_view_description">ਚੈਨਲ ਦਾ ਅਵਤਾਰ ਥੰਮਨੇਲ</string>
|
||||
<string name="no_appropriate_file_manager_message_android_10">ਇਸ ਕਾਰਜ ਲਈ ਕੋਈ ਢੁਕਵਾਂ ਫਾਈਲ ਮੈਨੇਜਰ ਨਹੀਂ ਮਿਲਿਆ।
|
||||
\nਕ੍ਰਿਪਾ ਕਰਕੇ ਸਟੋਰੇਜ ਐਕਸਿਸ ਫਰੇਮਵਰਕ SAF ਅਨੁਕੂਲ ਫਾਈਲ ਮੈਨੇਜਰ ਇੰਨਸਟਾਲ ਕਰੋ</string>
|
||||
<string name="no_appropriate_file_manager_message_android_10">ਇਸ ਕਾਰਜ ਲਈ ਕੋਈ ਢੁਕਵਾਂ ਫਾਈਲ ਮੈਨੇਜਰ ਨਹੀਂ ਮਿਲਿਆ।\nਕ੍ਰਿਪਾ ਕਰਕੇ ਸਟੋਰੇਜ ਐਕਸਿਸ ਫਰੇਮਵਰਕ SAF ਅਨੁਕੂਲ ਫਾਈਲ ਮੈਨੇਜਰ ਸਥਾਪਤ ਕਰੋ</string>
|
||||
<string name="tablet_mode_title">ਟੈਬਲੇਟ ਮੋਡ</string>
|
||||
<string name="notifications_disabled">ਨੋਟੀਫਿਕੇਸ਼ਨ ਬੰਦ ਕੀਤੇ ਹੋਏ ਹਨ</string>
|
||||
<string name="toggle_all">ਸਭ ਨੂੰ ਟੌਗਲ ਕਰੋ</string>
|
||||
@@ -684,7 +683,7 @@
|
||||
\n
|
||||
\nਤੁਹਾਡੀ ਚੋਣ ਇਸ ਗੱਲ ਤੇ ਮੁਨੱਸਰ ਕਰਦੀ ਹੈ ਕਿ ਤੁਸੀਂ ਗਤੀ ਤੇ ਸਟੀਕਤਾ ਵਿੱਚੋਂ ਕਿਸ ਨੂੰ ਪ੍ਰਾਥਮਿਕਤਾ ਦਿੰਦੇ ਹੋ।</string>
|
||||
<string name="fast_mode">ਤੇਜ ਮੋਡ</string>
|
||||
<string name="import_subscriptions_hint">3-ਡੌਟ ਮੀਨੂ ਤੋਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਨੂੰ ਆਯਾਤ ਜਾਂ ਨਿਰਯਾਤ ਕਰੋ</string>
|
||||
<string name="import_subscriptions_hint">3-ਡੌਟ ਮੀਨੂ ਤੋਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਨੂੰ ਇੰਪੋਰਟ ਜਾਂ ਐਕਸਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="app_update_unavailable_toast">ਤੁਸੀਂ ਨਿਊਪਾਈਪ ਦਾ ਨਵੀਨਤਮ ਸੰਸਕਰਣ ਚਲਾ ਰਹੇ ਹੋ</string>
|
||||
<string name="app_update_available_notification_text">%s ਨੂੰ ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ</string>
|
||||
<string name="night_theme_available">ਇਹ ਵਿਕਲਪ ਤਾਂ ਹੀ ਉਪਲਬਧ ਹੁੰਦਾ ਹੈ ਜੇਕਰ %s ਨੂੰ ਥੀਮ ਲਈ ਚੁਣਿਆ ਜਾਂਦਾ ਹੈ</string>
|
||||
@@ -827,6 +826,26 @@
|
||||
<string name="player_http_403">ਪਲੇਅ ਕਰਦੇ ਸਮੇਂ ਸਰਵਰ ਤੋਂ HTTP error 403 ਪ੍ਰਾਪਤ ਹੋਇਆ, ਜੋ ਸ਼ਾਇਦ ਸਟ੍ਰੀਮਿੰਗ URL ਦੀ ਮਿਆਦ ਪੁੱਗਣ ਜਾਂ IP ਦੀ ਪਾਬੰਦੀ ਕਾਰਨ ਹੋਈ ਹੈ</string>
|
||||
<string name="player_http_invalid_status">ਚਲਾਉਣ ਦੌਰਾਨ ਸਰਵਰ ਤੋਂ HTTP error %1$s ਪ੍ਰਾਪਤ ਹੋਇਆ</string>
|
||||
<string name="youtube_player_http_403">ਪਲੇਅ ਕਰਦੇ ਸਮੇਂ ਸਰਵਰ ਤੋਂ HTTP error 403 ਪ੍ਰਾਪਤ ਹੋਇਆ, ਜੋ ਸ਼ਾਇਦ IP ਬੈਨ ਜਾਂ ਸਟ੍ਰੀਮਿੰਗ URL ਡੀਔਬਫਸਕੇਸ਼ਨ ਸਮੱਸਿਆਵਾਂ ਕਾਰਨ ਹੋਈ ਹੈ</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s ਨੇ ਡੇਟਾ ਪ੍ਰਦਾਨ ਕਰਨ ਤੋਂ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ, ਅਤੇ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਲੌਗਇਨ ਕਰਨ ਲਈ ਕਿਹਾ ਕਿ ਬੇਨਤੀਕਰਤਾ ਬੋਟ ਨਹੀਂ ਹੈ।\n\nਹੋ ਸਕਦਾ ਹੈ ਕਿ %1$s ਨੇ ਤੁਹਾਡੇ IP ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਪਾਬੰਦੀ ਲਗਾਈ ਹੋਵੇ, ਤੁਸੀਂ ਕੁਝ ਸਮਾਂ ਉਡੀਕ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਕਿਸੇ ਵੱਖਰੇ IP \'ਤੇ ਸਵਿੱਚ ਕਰ ਸਕਦੇ ਹੋ (ਉਦਾਹਰਣ ਵਜੋਂ VPN ਨੂੰ ਚਾਲੂ/ਬੰਦ ਕਰਕੇ, ਜਾਂ WiFi ਤੋਂ ਮੋਬਾਈਲ ਡੇਟਾ \'ਤੇ ਸਵਿੱਚ ਕਰਕੇ)।</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s ਨੇ ਡੇਟਾ ਪ੍ਰਦਾਨ ਕਰਨ ਤੋਂ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ, ਅਤੇ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਲੌਗਇਨ ਕਰਨ ਲਈ ਕਿਹਾ ਕਿ ਬੇਨਤੀਕਰਤਾ ਬੋਟ ਨਹੀਂ ਹੈ।\n\nਹੋ ਸਕਦਾ ਹੈ ਕਿ %1$s ਨੇ ਤੁਹਾਡੇ IP ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਪਾਬੰਦੀ ਲਗਾਈ ਹੋਵੇ, ਤੁਸੀਂ ਕੁਝ ਸਮਾਂ ਉਡੀਕ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਕਿਸੇ ਵੱਖਰੇ IP \'ਤੇ ਸਵਿੱਚ ਕਰ ਸਕਦੇ ਹੋ (ਉਦਾਹਰਣ ਵਜੋਂ VPN ਨੂੰ ਚਾਲੂ/ਬੰਦ ਕਰਕੇ, ਜਾਂ WiFi ਤੋਂ ਮੋਬਾਈਲ ਡੇਟਾ \'ਤੇ ਸਵਿੱਚ ਕਰਕੇ)।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਕਿਰਪਾ ਕਰਕੇ <a href="%2$s">ਇਹ FAQ ਐਂਟਰੀ</a> ਵੇਖੋ।</string>
|
||||
<string name="unsupported_content_in_country">ਇਹ ਸਮੱਗਰੀ ਵਰਤਮਾਨ ਵਿੱਚ ਚੁਣੇ ਗਏ ਦੇਸ਼ ਦੀ ਸਮੱਗਰੀ ਲਈ ਉਪਲੱਬਧ ਨਹੀਂ ਹੈ।\n\n\"ਸੈਟਿੰਗਾਂ > ਸਮੱਗਰੀ > ਡਿਫ਼ਾਲਟ ਸਮੱਗਰੀ ਦੇਸ਼\" ਤੋਂ ਆਪਣੀ ਚੋਣ ਬਦਲੋ।</string>
|
||||
<string name="kao_dialog_warning">ਅਗਸਤ 2025 ਵਿੱਚ, ਗੂਗਲ ਨੇ ਐਲਾਨ ਕੀਤਾ ਕਿ ਸਤੰਬਰ 2026 ਤੋਂ, ਐਪਸ ਨੂੰ ਸਥਾਪਿਤ ਕਰਨ ਲਈ ਪ੍ਰਮਾਣਿਤ ਡਿਵਾਈਸਾਂ \'ਤੇ ਸਾਰੇ ਐਂਡਰਾਇਡ ਐਪਸ ਲਈ ਡਿਵੈਲਪਰ ਤਸਦੀਕ ਦੀ ਲੋੜ ਹੋਵੇਗੀ, ਜਿਸ ਵਿੱਚ ਪਲੇ ਸਟੋਰ ਤੋਂ ਬਾਹਰ ਸਥਾਪਤ ਕੀਤੇ ਐਪਸ ਵੀ ਸ਼ਾਮਲ ਹਨ। ਕਿਉਂਕਿ ਨਿਊਪਾਈਪ ਦੇ ਡਿਵੈਲਪਰ ਇਸ ਲੋੜ ਨਾਲ ਸਹਿਮਤ ਨਹੀਂ ਹਨ, ਇਸ ਲਈ ਨਿਊਪਾਈਪ ਉਸ ਸਮੇਂ ਤੋਂ ਬਾਅਦ ਪ੍ਰਮਾਣਿਤ ਐਂਡਰਾਇਡ ਡਿਵਾਈਸਾਂ \'ਤੇ ਕੰਮ ਨਹੀਂ ਕਰੇਗੀ।</string>
|
||||
<string name="kao_dialog_more_info">ਵੇਰਵੇ</string>
|
||||
<string name="kao_solution">ਹੱਲ</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">%d ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਐਕਸਪੋਰਟ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…</item>
|
||||
<item quantity="other">%d ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਐਕਸਪੋਰਟ ਕੀਤੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">%d ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…</item>
|
||||
<item quantity="other">%d ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਲੋਡ ਕੀਤੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">%d ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਇੰਪੋਰਟ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…</item>
|
||||
<item quantity="other">%d ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਇੰਪੋਰਟ ਕੀਤੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਇੰਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="export_subscriptions_title">ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਐਕਸਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="import_subscriptions_summary">ਪਹਿਲਾਂ ਕੀਤੇ .json ਐਕਸਪੋਰਟ ਤੋਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਨੂੰ ਇੰਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="export_subscriptions_summary">ਆਪਣੀਆਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਨੂੰ .json ਫਾਈਲ ਵਿੱਚ ਐਕਸਪੋਰਟ ਕਰੋ</string>
|
||||
<string name="import_from_previous_export">ਪਹਿਲਾਂ ਕੀਤੇ ਐਕਸਪੋਰਟ ਤੋਂ ਇੰਪੋਰਟ ਕਰੋ</string>
|
||||
</resources>
|
||||
|
||||
@@ -868,9 +868,32 @@
|
||||
<string name="player_http_403">Podczas odtwarzania otrzymano od serwera błąd HTTP 403, prawdopodobnie spowodowany wygaśnięciem adresu URL strumienia lub blokadą adresu IP.</string>
|
||||
<string name="player_http_invalid_status">Podczas odtwarzania otrzymano od serwera błąd HTTP %1$s.</string>
|
||||
<string name="youtube_player_http_403">Podczas odtwarzania otrzymano od serwera błąd HTTP 403, prawdopodobnie spowodowany blokadą adresu IP lub problemami z odszyfrowaniem adresu URL strumienia.</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s odmówił dostarczenia danych, prosząc o zalogowanie się w celu potwierdzenia, że nie jest się botem.\n\nTwoje IP mogło zostać tymczasowo zablokowane przez %1$s. Możesz chwilę poczekać lub zmienić adres IP (na przykład włączając/wyłączając VPN lub przełączając się z sieci Wi-Fi na dane komórkowe).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s odmówił dostarczenia danych, prosząc o zalogowanie się w celu potwierdzenia, że nie jest się botem.\n\nTwoje IP mogło zostać tymczasowo zablokowane przez %1$s. Możesz chwilę poczekać lub zmienić adres IP (na przykład włączając/wyłączając VPN lub przełączając się z sieci Wi-Fi na dane komórkowe).\n\nZobacz <a href="%2$s">ten wpis w FAQ</a>, aby uzyskać więcej informacji.</string>
|
||||
<string name="unsupported_content_in_country">Ta treść nie jest dostępna dla aktualnie wybranego kraju treści.\n\nZmień swój wybór w „Ustawienia > Zawartość > Domyślny kraj treści”.</string>
|
||||
<string name="kao_dialog_warning">W sierpniu 2025 r. Google ogłosił, że od września 2026 r. instalowanie aplikacji będzie wymagać weryfikacji ich twórców w przypadku wszystkich aplikacji na Androida na certyfikowanych urządzeniach, w tym tych zainstalowanych poza sklepem Google Play. Ponieważ programiści NewPipe nie zgadzają się z tym wymogiem, NewPipie nie będzie już działać na certyfikowanych urządzeniach z Androidem po tym czasie.</string>
|
||||
<string name="kao_dialog_more_info">Szczegóły</string>
|
||||
<string name="kao_solution">Rozwiązanie</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Eksportowanie %d subskrypcji…</item>
|
||||
<item quantity="few">Eksportowanie %d subskrypcji…</item>
|
||||
<item quantity="many">Eksportowanie %d subskrypcji…</item>
|
||||
<item quantity="other">Eksportowanie %d subskrypcji…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Ładowanie %d subskrypcji…</item>
|
||||
<item quantity="few">Ładowanie %d subskrypcji…</item>
|
||||
<item quantity="many">Ładowanie %d subskrypcji…</item>
|
||||
<item quantity="other">Ładowanie %d subskrypcji…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importowanie %d subskrypcji…</item>
|
||||
<item quantity="few">Importowanie %d subskrypcji…</item>
|
||||
<item quantity="many">Importowanie %d subskrypcji…</item>
|
||||
<item quantity="other">Importowanie %d subskrypcji…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Importuj subskrypcje</string>
|
||||
<string name="export_subscriptions_title">Eksportuj subskrypcje</string>
|
||||
<string name="import_subscriptions_summary">Zaimportuj subskrypcje z poprzedniego eksportu do .json</string>
|
||||
<string name="export_subscriptions_summary">Wyeksportuj swoje subskrypcje do pliku .json</string>
|
||||
<string name="import_from_previous_export">Importuj z poprzedniego eksportu</string>
|
||||
</resources>
|
||||
|
||||
@@ -859,9 +859,29 @@
|
||||
<string name="player_http_403">Erro HTTP 403 recebido do servidor durante a reprodução, provavelmente causado por URL de streaming expirado ou IP banido</string>
|
||||
<string name="player_http_invalid_status">Erro HTTP %1$s recebido do servidor durante reprodução</string>
|
||||
<string name="youtube_player_http_403">Erro HTTP 403 recebido do servidor durante a reprodução, provavelmente causado por um banimento de IP ou problemas de desofuscação de URL de streaming</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s se recusou a fornecer dados, solicitando um login para confirmar que o solicitante não é um bot.\n\nSeu IP pode ter sido temporariamente banido por %1$s. Você pode esperar um pouco ou mudar para um IP diferente (por exemplo, ativando/desativando uma VPN ou alternando de Wi-Fi para dados móveis).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s se recusou a fornecer dados, solicitando um login para confirmar que o solicitante não é um bot.\n\nSeu IP pode ter sido temporariamente banido por %1$s. Você pode esperar um pouco ou mudar para um IP diferente (por exemplo, ativando/desativando uma VPN ou alternando de Wi-Fi para dados móveis).\n\nConfira <a href="%2$s">este artigo do FAQ</a> para mais informações.</string>
|
||||
<string name="unsupported_content_in_country">Este conteúdo não está disponível para o país selecionado atualmente.\n\nAltere sua seleção acessando “Configurações > Conteúdo > País padrão do conteúdo”.</string>
|
||||
<string name="kao_dialog_warning">Em agosto de 2025, o Google anunciou que, a partir de setembro de 2026, a instalação de aplicativos exigirá a verificação do desenvolvedor para todos os aplicativos Android em dispositivos certificados, incluindo aqueles instalados fora da Play Store. Como os desenvolvedores do NewPipe não concordam com esse requisito, o NewPipe não funcionará mais em dispositivos Android certificados após essa data.</string>
|
||||
<string name="kao_dialog_more_info">Detalhes</string>
|
||||
<string name="kao_solution">Solução</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Exportando %d inscrição…</item>
|
||||
<item quantity="many">Exportando %d inscrições…</item>
|
||||
<item quantity="other">Exportando %d inscrições…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Carregando %d inscrição…</item>
|
||||
<item quantity="many">Carregando %d inscrições…</item>
|
||||
<item quantity="other">Carregando %d inscrições…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importando %d inscrição…</item>
|
||||
<item quantity="many">Importando %d inscrições…</item>
|
||||
<item quantity="other">Importando %d inscrições…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Importar inscrições</string>
|
||||
<string name="export_subscriptions_title">Exportar inscrições</string>
|
||||
<string name="import_subscriptions_summary">Importar inscrições do arquivo .json exportado anterior</string>
|
||||
<string name="export_subscriptions_summary">Exportar inscrições para arquivo .json</string>
|
||||
<string name="import_from_previous_export">Importar da exportação anterior</string>
|
||||
</resources>
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
<string name="entry_deleted">Entrada apagada</string>
|
||||
<string name="account_terminated_service_provides_reason">Conta terminada\n\n%1$s fornece esta razão: %2$s</string>
|
||||
<string name="player_http_invalid_status">Erro HTTP %1$s recebido do servidor ao reproduzir</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s recusou fornecer dados, pedindo por um login para confirmar que o solicitante não é um bot.\n\nO seu IP pode ter sido temporariamente banido por %1$s, pode esperar algum tempo ou mudar para um IP diferente (por exemplo, a ligar / desligar uma VPN, ou a alternar de Wi-Fi para dados móveis).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s recusou fornecer dados, pedindo por um login para confirmar que o solicitante não é um bot.\n\nO seu IP pode ter sido temporariamente banido por %1$s, você pode esperar algum tempo ou mudar para um IP diferente (por exemplo, a ligar/desligar uma VPN, ou a alternar de Wi-Fi para dados móveis).\n\nConfira <a href="%2$s">este artigo do FAQ</a> para mais informações.</string>
|
||||
<string name="unsupported_content_in_country">Este conteúdo não está disponível para o país de conteúdo atualmente selecionado.\n\nAltere a sua seleção de \"Configurações > Conteúdo > País predefinido de conteúdo\".</string>
|
||||
<string name="player_http_403">Erro HTTP 403 recebido do servidor durante a reprodução, provavelmente causado pela URL de streaming expirado ou IP banido</string>
|
||||
<string name="youtube_player_http_403">Erro HTTP 403 recebido do servidor durante a reprodução, provavelmente causado por um bloqueio de IP ou problemas de desofuscação da URL de streaming</string>
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
<string name="entry_deleted">Entrada apagada</string>
|
||||
<string name="account_terminated_service_provides_reason">Conta terminada\n\n%1$s fornece esta razão: %2$s</string>
|
||||
<string name="player_http_invalid_status">Erro HTTP %1$s recebido do servidor ao reproduzir</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s recusou fornecer dados, pedindo por um login para confirmar que o solicitante não é um bot.\n\nO seu IP pode ter sido temporariamente banido por %1$s, pode esperar algum tempo ou mudar para um IP diferente (por exemplo, a ligar / desligar uma VPN, ou a alternar de Wi-Fi para dados móveis).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s recusou fornecer dados, pedindo por um login para confirmar que o solicitante não é um bot.\n\nO seu IP pode ter sido temporariamente banido por %1$s, você pode esperar algum tempo ou mudar para um IP diferente (por exemplo, a ligar/desligar uma VPN, ou a alternar de Wi-Fi para dados móveis).\n\nConfira <a href="%2$s">este artigo do FAQ</a> para mais informações.</string>
|
||||
<string name="unsupported_content_in_country">Este conteúdo não está disponível para o país de conteúdo atualmente selecionado.\n\nAltere a sua seleção de \"Configurações > Conteúdo > País predefinido de conteúdo\".</string>
|
||||
<string name="player_http_403">Erro HTTP 403 recebido do servidor durante a reprodução, provavelmente causado pela URL de streaming expirado ou IP banido</string>
|
||||
<string name="youtube_player_http_403">Erro HTTP 403 recebido do servidor durante a reprodução, provavelmente causado por um bloqueio de IP ou problemas de desofuscação da URL de streaming</string>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -860,9 +860,32 @@
|
||||
<string name="player_http_403">Počas prehrávania bola zo servera prijatá chyba HTTP 403, pravdepodobne spôsobená vypršaním platnosti streamingovej adresy URL alebo zákazom IP adresy</string>
|
||||
<string name="player_http_invalid_status">Chyba HTTP %1$s prijatá zo servera počas prehrávania</string>
|
||||
<string name="youtube_player_http_403">Chyba HTTP 403 prijatá zo servera počas prehrávania, pravdepodobne spôsobená zákazom IP adresy alebo problémami s deobfuskáciou streamingovej URL adresy</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s odmietol poskytnúť údaje, žiada o prihlásenie na potvrdenie, že žiadateľ nie je bot.\n\nVaša IP adresa mohla byť dočasne zakázaná %1$s, môžete nejaký čas počkať alebo prejsť na inú IP adresu (napríklad zapnutím/vypnutím VPN alebo prepnutím z WiFi na mobilné dáta).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s odmietol poskytnúť údaje, žiada o prihlásenie na potvrdenie, že žiadateľ nie je bot.\n\nVaša IP adresa mohla byť dočasne zakázaná %1$s, môžete nejaký čas počkať alebo prejsť na inú IP adresu (napríklad zapnutím/vypnutím VPN alebo prepnutím z WiFi na mobilné dáta).\n\nPozrite si <a href="%2$s">túto časť FAQ</a> pre viac informácií.</string>
|
||||
<string name="unsupported_content_in_country">Tento obsah nie je dostupný pre aktuálne zvolenú krajinu obsahu.\n\nZmeňte výber v ponuke \"Nastavenia > Obsah > Predvolená krajina obsahu\".</string>
|
||||
<string name="kao_dialog_warning">V auguste 2025 spoločnosť Google oznámila, že od septembra 2026 bude inštalácia aplikácií vyžadovať overenie vývojára pre všetky aplikácie Android na certifikovaných zariadeniach, vrátane tých, ktoré sú inštalované mimo obchodu Play Store. Keďže vývojári NewPipe s touto požiadavkou nesúhlasia, NewPipe po tomto termíne nebude na certifikovaných zariadeniach Android fungovať.</string>
|
||||
<string name="kao_dialog_more_info">Podrobnosti</string>
|
||||
<string name="kao_solution">Riešenie</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Exportovanie %d odberu…</item>
|
||||
<item quantity="few">Exportovanie %d odberov…</item>
|
||||
<item quantity="many">Exportovanie %d odberov…</item>
|
||||
<item quantity="other">Exportovanie %d odberov…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Načítanie %d odberu…</item>
|
||||
<item quantity="few">Načítanie %d odberov…</item>
|
||||
<item quantity="many">Načítanie %d odberov…</item>
|
||||
<item quantity="other">Načítanie %d odberov…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importovanie %d odberu…</item>
|
||||
<item quantity="few">Importovanie %d odberov…</item>
|
||||
<item quantity="many">Importovanie %d odberov…</item>
|
||||
<item quantity="other">Importovanie %d odberov…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Importovať odbery</string>
|
||||
<string name="export_subscriptions_title">Exportovať odbery</string>
|
||||
<string name="import_subscriptions_summary">Import odberov z predchádzajúceho exportu .json</string>
|
||||
<string name="export_subscriptions_summary">Export vašich odberov do súboru .json</string>
|
||||
<string name="import_from_previous_export">Importovať z predchádzajúceho exportu</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="use_inexact_seek_title">Dhaafinta dagdaga ah</string>
|
||||
<string name="use_inexact_seek_title">Isticmaal Dhaafinta dagdaga ah</string>
|
||||
<string name="popup_remember_size_pos_summary">Xusuusnow meeshii iyo cabirkii udambeeyay ee daaqada</string>
|
||||
<string name="popup_remember_size_pos_title">Xusuusnow fadhiga daaqada</string>
|
||||
<string name="dark_theme_title">Madow</string>
|
||||
<string name="dark_theme_title">Mugdi</string>
|
||||
<string name="theme_title">Nashqada</string>
|
||||
<string name="default_video_format_title">Nooca muuqaalka</string>
|
||||
<string name="default_audio_format_title">Nooca dhagaysiga</string>
|
||||
<string name="default_audio_format_title">Nooca Maqaalka</string>
|
||||
<string name="notification_colorize_summary">Androidka hakuu baddalo midabka ogaysiiska galka waxa daaran asagoo kusalaynaya midabka galka shayga daaran (aalladahoo dhan looma wada heli karo nidaamkan)</string>
|
||||
<string name="notification_colorize_title">Midabbee ogaysiiska</string>
|
||||
<string name="notification_action_buffering">Soo Kicinaya</string>
|
||||
@@ -14,25 +14,25 @@
|
||||
<string name="notification_action_repeat">Ku celi</string>
|
||||
<string name="notification_actions_at_most_three">Ugu badnaan waxad dooran kartaa sadex shay iney ka muuqdaan ogaysiiska yar!</string>
|
||||
<string name="notification_actions_summary">Wax ka baddal hawsha ogaysiiska adigoo dushooda ku dhufanaya. Dooro ilaa sadex kamida si ay uga muuqdaan ogaysiiska yar adigoo saxaya santuuqa dhanka midig kaga yaala</string>
|
||||
<string name="notification_action_4_title">Batoonka hawsha shanaad</string>
|
||||
<string name="notification_action_3_title">Batoonka hawsha afraad</string>
|
||||
<string name="notification_action_2_title">Batoonka hawsha sadexaad</string>
|
||||
<string name="notification_action_1_title">Batoonka hawsha labaad</string>
|
||||
<string name="notification_action_0_title">Batoonka hawsha koowaad</string>
|
||||
<string name="notification_action_4_title">Batoonka shanaad</string>
|
||||
<string name="notification_action_3_title">Batoonka afraad</string>
|
||||
<string name="notification_action_2_title">Batoonka saddexaad</string>
|
||||
<string name="notification_action_1_title">Batoonka labaad</string>
|
||||
<string name="notification_action_0_title">Batoonka koowaad</string>
|
||||
<string name="notification_scale_to_square_image_summary">La ekaysii galka muuqaalka xaga ogaysiisyada ka muuqda cabirka 1:1 ayadoo laga soo baddalayo 16:9</string>
|
||||
<string name="notification_scale_to_square_image_title">Galka la ekaysii cabirka 1:1</string>
|
||||
<string name="show_play_with_kodi_summary">Soo bandhig istikhyaar ah in muuqaalka lagu furo xarunta madadaalada Kodi</string>
|
||||
<string name="show_play_with_kodi_summary">Soo bandhig istikhyaar ah in muuqaalka lagu furo Kodi</string>
|
||||
<string name="show_play_with_kodi_title">Soodhig istikhyaarka \"Ku fur Kodi\"</string>
|
||||
<string name="kore_not_found">Kushub appka maqan ee Kore\?</string>
|
||||
<string name="kore_not_found">Soo deji appka maqan ee Kore?</string>
|
||||
<string name="play_with_kodi_title">Ku fur Kodi</string>
|
||||
<string name="show_higher_resolutions_summary">Aalladaha qaar kaliya ayaa furi kara muuqaalada 2K/4K ga ah</string>
|
||||
<string name="show_higher_resolutions_title">Tus tayooyinka kasii sareeeya</string>
|
||||
<string name="show_higher_resolutions_title">Tus tayooyinka sare</string>
|
||||
<string name="default_popup_resolution_title">Tayada muuqaalka daaqada</string>
|
||||
<string name="default_resolution_title">Tayada muuqaalka</string>
|
||||
<string name="download_path_audio_dialog_title">Dooro khaanada dhagaysiga lasoo dajiyo</string>
|
||||
<string name="download_path_dialog_title">Dooro khaanada muuqaalada lasoo dajiyo</string>
|
||||
<string name="download_path_audio_summary">Dhagaysiyada lasoo dajiyay halkan ayaa lagu kaydiyaa</string>
|
||||
<string name="download_path_summary">Muuqaalada lasoo dajiyo halkan ayaa lagu kaydiyaa</string>
|
||||
<string name="download_path_audio_dialog_title">Dooro Khaanada soo dejinta ee faylasha maqalka ah</string>
|
||||
<string name="download_path_dialog_title">Dooro khaanada Muuqaallada lagu soo dejinayo</string>
|
||||
<string name="download_path_audio_summary">Dhagaysiyada lasoo dajiyay halkan ayaa lagu kaydiyey</string>
|
||||
<string name="download_path_summary">Muuqaalada lasoo dajiyo halkan ayaa lagu kaydiyey</string>
|
||||
<string name="download_path_audio_title">Khaanada dajinta dhagaysiga</string>
|
||||
<string name="download_path_title">Khaanada dajinta muuqaalada</string>
|
||||
<string name="controls_add_to_playlist_title">Ku Dar</string>
|
||||
@@ -41,17 +41,17 @@
|
||||
<string name="tab_choose">Dooro Daaqada</string>
|
||||
<string name="tab_bookmarks">La calaamadsaday</string>
|
||||
<string name="tab_subscriptions">Rukunka</string>
|
||||
<string name="show_info">Faahfaahinta</string>
|
||||
<string name="show_info">Tus Faahfaahinta</string>
|
||||
<string name="subscription_update_failed">Lama cusbooneysiin karo rukunka</string>
|
||||
<string name="subscription_change_failed">Lama baddali karo rukunka</string>
|
||||
<string name="channel_unsubscribed">Kanaalka waad iskajoojisay</string>
|
||||
<string name="unsubscribe">Iskajooji Rukunka</string>
|
||||
<string name="subscribed_button_title">Rukuntay</string>
|
||||
<string name="subscribe_button_title">Rukumo</string>
|
||||
<string name="use_external_audio_player_title">Isticmaal dhagaysi daare dibada ah</string>
|
||||
<string name="use_external_audio_player_title">Isticmaal dhagaysi Daare dibada ah</string>
|
||||
<string name="use_external_video_player_summary">Codka ayuu ka saaraa tayada muuqaalada qaar</string>
|
||||
<string name="use_external_video_player_title">Isticmaal muuqaal daare dibada ah</string>
|
||||
<string name="share_dialog_title">La wadaag</string>
|
||||
<string name="share_dialog_title">Ku La wadaag</string>
|
||||
<string name="search_showing_result_for">Kutusaya natiijooyinka: %s</string>
|
||||
<string name="did_you_mean">Ma waxaad ka waday \"%1$s\"\?</string>
|
||||
<string name="settings">Fadhiga</string>
|
||||
@@ -64,7 +64,7 @@
|
||||
<string name="no_player_found_toast">Wax fura lama helin shaygan. (waxaad Ku shuban kartaa VLC si aad u furto).</string>
|
||||
<string name="no_player_found">Wax fura lama helin shaygan. Ku shubo VLC\?</string>
|
||||
<string name="upload_date_text">Lasoo galiyay: %1$s</string>
|
||||
<string name="main_bg_subtitle">Ku dhufo waynaysada 🔍 si aad wax uraadiso.</string>
|
||||
<string name="main_bg_subtitle">Ku dhufo raadinta si aad wax uraadiso.</string>
|
||||
<string name="overwrite_unrelated_warning">Shay magacan leh ayaa horay ujiray</string>
|
||||
<string name="overwrite">Ku badal</string>
|
||||
<string name="generate_unique_name">Usamee magac gaar ah</string>
|
||||
@@ -262,7 +262,7 @@
|
||||
<string name="error_report_open_github_notice">Fadlan hubi in arin cilladdan ka hadlaya horay loo wariyay. Marka wax horay u jiray la wariyo markale, wakhti ayaad naga qaadaysaa wakhtigaas oo aan cilada ku sixi la hayn.</string>
|
||||
<string name="error_report_open_issue_button_text">Ku wari xaga GitHub-ka</string>
|
||||
<string name="copy_for_github">Koobiyee warka oo diyaarsan</string>
|
||||
<string name="error_report_button_text">Khaladkan email ahaan ku warceli</string>
|
||||
<string name="error_report_button_text">Khaladkan email ahaan uga war bixi</string>
|
||||
<string name="sorry_string">Waan ka xunahay, sidaa inay dhacdo ma ahayn.</string>
|
||||
<string name="permission_display_over_apps">U ogolow appka inuu dul fuulo applicationada kale</string>
|
||||
<string name="restore_defaults_confirmation">Ma rabtaa inaad sidii hore kuceliso\?</string>
|
||||
@@ -400,11 +400,11 @@
|
||||
<string name="show_comments_title">Tus faallooyinka</string>
|
||||
<string name="clear_queue_confirmation_description">Hormada daareha hadda wax shidaya waa la baddali doonaa</string>
|
||||
<string name="clear_queue_confirmation_summary">Kala baddalka daareha waxay badali kartaa hormada sidaas darteed waydii in la xaqiijiyo intaan hormada la tirtirin</string>
|
||||
<string name="clear_queue_confirmation_title">Xaqiijinta tirtirka hormada</string>
|
||||
<string name="clear_queue_confirmation_title">Weydii xaqiijin ka hor intaadan tirtirin saf-ka</string>
|
||||
<string name="seek_duration_title">Wakhtiga horay udhaafinta/dibucelinta</string>
|
||||
<string name="black_theme_title">Mugdi</string>
|
||||
<string name="light_theme_title">Caddaan</string>
|
||||
<string name="play_audio">Dhagaysi</string>
|
||||
<string name="black_theme_title">Madow</string>
|
||||
<string name="light_theme_title">Iftiin</string>
|
||||
<string name="play_audio">Maqal</string>
|
||||
<string name="notification_action_nothing">Waxba</string>
|
||||
<string name="search">Raadi</string>
|
||||
<string name="download">Daji</string>
|
||||
@@ -491,7 +491,7 @@
|
||||
</plurals>
|
||||
<string name="new_seek_duration_toast">Ayadooy ugu wacantahay xayiraad xaga ExoPlayer-ka ah xadka dhaaf-dhaafinta waa %d ilbiriqsi</string>
|
||||
<string name="remove_watched_popup_partially_watched_streams">Haa, sidoo kale ku dar muuqaalada qayb laga daawaday</string>
|
||||
<string name="remove_watched_popup_warning">Muuqaalada la daawaday kahor iyo kadib markii xulka lagu daray waa la saari doonaa. \nMa hubtaa? Arrinkan dib looma soocelin karo!</string>
|
||||
<string name="remove_watched_popup_warning">Muuqaalada la daawaday kahor iyo kadib markii xulka lagu daray waa la saari doonaa. \nMa hubtaa?</string>
|
||||
<string name="remove_watched_popup_title">Saar muuqaalada la daawaday?</string>
|
||||
<string name="remove_watched">Saar kuwa la daawaday</string>
|
||||
<string name="systems_language">Aaladu saytahay</string>
|
||||
@@ -520,7 +520,7 @@
|
||||
<string name="error_postprocessing_failed">Habayntii way guuldareystay</string>
|
||||
<string name="error_http_not_found">Lama helin</string>
|
||||
<string name="error_http_unsupported_range">Martigaliyuhu ma aqbalo dajinta qaybaha badan leh, iskula day @string/msg_threads = 1</string>
|
||||
<string name="error_connect_host">Kuma xidhi karo martigaliyaha</string>
|
||||
<string name="error_connect_host">Kuma xidhmi karo server-ka</string>
|
||||
<string name="error_unknown_host">Lama heli karo martigaliyaha</string>
|
||||
<string name="error_ssl_exception">Lama samayn karo iskuxidh amni ah</string>
|
||||
<string name="error_path_creation">khaanadii la rabay lama samayn karo</string>
|
||||
@@ -630,7 +630,7 @@
|
||||
<string name="remote_search_suggestions">Soojeedinada raadinta banaanka</string>
|
||||
<string name="local_search_suggestions">Soojeedinada raadinta gudaha</string>
|
||||
<string name="progressive_load_interval_title">Cabirka soodaarida udhexeeya</string>
|
||||
<string name="crash_the_player">Jabi Daareha</string>
|
||||
<string name="crash_the_player">Jabi Daaraha</string>
|
||||
<string name="yes">Haa</string>
|
||||
<string name="no">Maya</string>
|
||||
<string name="search_with_service_name">Raadi %1$s</string>
|
||||
|
||||
@@ -845,9 +845,26 @@
|
||||
<string name="player_http_403">Oynatırken sunucudan HTTP 403 hatası alındı, akış URL’si bitmiş ya da IP engellenmiş olabilir</string>
|
||||
<string name="player_http_invalid_status">Oynatırken sunucudan HTTP %1$s hatası alındı</string>
|
||||
<string name="youtube_player_http_403">Oynatırken sunucudan HTTP 403 hatası alındı, IP engeli ya da akış URL’si çözme sorunları olabilir</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s veri sağlamayı geri çevirdi, istekçinin robot olmadığını doğrulaması için oturum açmasını istiyor.\n\n%1$s, IP adresinizi geçici olarak engellemiş olabilir, bir süre bekleyebilir ya da başka IP\'ye geçebilirsiniz (örneğin VPN\'i açıp/kapatarak ya da WiFi\'den mobil veriye geçerek).</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s veri sağlamıyor, istekçinin robot olmadığını doğrulaması için oturum açmasını istiyor.\n\n%1$s, IP adresinizi geçici olarak engellemiş olabilir, bir süre bekleyebilir ya da başka IP\'ye geçebilirsiniz (örneğin VPN\'i açıp/kapatarak ya da WiFi\'den mobil veriye geçerek).\n\nDaha çok bilgi için <a href="%2$s">bu SSS girdisine</a> bakın.</string>
|
||||
<string name="unsupported_content_in_country">Bu içerik şu anda seçili içerik ülkesinde kullanılamıyor.\n\nSeçiminizi \"Ayarlar > İçerik > Öntanımlı içerik ülkesi\"nden değiştirin.</string>
|
||||
<string name="kao_dialog_warning">Google, Eylül 2026\'dan itibaren sertifikalı Android cihazlardaki Play Store harici olmak üzere tüm uygulamaların, geliştiricilerin kişisel kimlik bilgilerini doğrudan Google’a göndermesini gerektireceğini duyurdu. NewPipe geliştiricileri bu zorunluluğu kabul etmediğinden, NewPipe bu tarihten sonra sertifikalı Android cihazlarda çalışmayacaktır.</string>
|
||||
<string name="kao_dialog_more_info">Detaylar</string>
|
||||
<string name="kao_dialog_warning">Google, Eylül 2026\'dan sonra sertifikalı Android aygıtlarda Play Store dışından kurulmuşlarla birlikte tüm uygulamaların, geliştiricilerin kişisel kimlik bilgilerini doğrudan Google’a göndermesini gerektireceğini duyurdu. NewPipe geliştiricileri bu zorunluluğu kabul etmediğinden, NewPipe bu tarihten sonra sertifikalı Android aygıtlarda çalışmayacaktır.</string>
|
||||
<string name="kao_dialog_more_info">Ayrıntılar</string>
|
||||
<string name="kao_solution">Çözüm</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">%d abonelik dışa aktarılıyor…</item>
|
||||
<item quantity="other">%d abonelik dışa aktarılıyor…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">%d abonelik yükleniyor…</item>
|
||||
<item quantity="other">%d abonelik yükleniyor…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">%d abonelik içeri aktarılıyor…</item>
|
||||
<item quantity="other">%d abonelik içeri aktarılıyor…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">Abonelikleri içeri aktar</string>
|
||||
<string name="export_subscriptions_title">Abonelikleri dışarı aktar</string>
|
||||
<string name="import_subscriptions_summary">Abonelikleri önceki dışarı aktarmadaki .json dosyası ile içeri aktar</string>
|
||||
<string name="export_subscriptions_summary">Aboneliklerini bir .json dosyasında dışarı aktar</string>
|
||||
<string name="import_from_previous_export">Önceki dışarı aktarmadan içe aktar</string>
|
||||
</resources>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<string name="general_error">Lỗi</string>
|
||||
<string name="network_error">Lỗi kết nối mạng</string>
|
||||
<string name="could_not_load_thumbnails">Không thể tải tất cả hình thu nhỏ</string>
|
||||
<string name="parsing_error">Không thể phân tích cú pháp trang web vì trang này đã ngừng hoạt động vào 21/07/2025.</string>
|
||||
<string name="parsing_error">Không thể phân tích cú pháp web.</string>
|
||||
<string name="content_not_available">Nội dung không khả dụng</string>
|
||||
<string name="could_not_setup_download_menu">Không thể thiết lập menu tải về</string>
|
||||
<string name="app_ui_crash">Ứng dụng/Giao diện người dùng bị lỗi</string>
|
||||
@@ -821,8 +821,8 @@
|
||||
<string name="trending_movies">Phim và chương trình đang thịnh hành</string>
|
||||
<string name="trending_music">Âm nhạc đang thịnh hành</string>
|
||||
<string name="player_http_403">Đã xảy ra lỗi HTTP 403 trong khi phát, có thể do URL phát sóng đã hết hạn hoặc bị ban IP</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s đã từ chối cung cấp dữ liệu, cần phải đăng nhập để xác nhận yêu cầu viên ko phải là bot.\n\nIP này có vẻ đã bị ban tạm thời bởi %1$s, bạn có thể đợi một lúc hoặc chuyển sang IP khác (ví dụ như việc tắt / bật lại VPN, hoặc là chuyển mạng từ WIFI sang 4G/5G).</string>
|
||||
<string name="unsupported_content_in_country">Nội dung này không được hỗ trợ tại quốc gia mà bạn chọn.\n\nHãy đổi quốc gia trong phần \"Cài đặt > Nội dung > Nội dung quốc gia mặc định\".</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s đã từ chối cung cấp dữ liệu, cần phải đăng nhập để xác nhận requester của NewPipe ko phải là bot.\n\nIP này có vẻ đã bị ban tạm thời bởi %1$s, bạn có thể đợi một lúc hoặc chuyển sang IP khác (ví dụ như việc tắt / bật lại VPN, hoặc là chuyển mạng từ WIFI sang 4G/5G).\n\nXem qua <a href="%2$s">phần FAQ này</a> đẻ biết thêm chi tiết</string>
|
||||
<string name="unsupported_content_in_country">Nội dung này không được hỗ trợ tại quốc gia mà bạn chọn.\n\nHãy đổi quốc gia trong phần \"Cài đặt > Nội dung > Quốc gia nội dung mặc định\".</string>
|
||||
<string name="permission_display_over_apps_message">Để sử dụng tính năng phát video nổi, hãy chọn %1$s trong Cài đặt Android và bật tính năng %2$s.</string>
|
||||
<string name="youtube_player_http_403">Đã xảy ra lỗi HTTP 403 trong khi phát, có thể IP này đã bị ban hoặc vấn đề phát URL deobfuscation</string>
|
||||
<string name="permission_display_over_apps_permission_name">\"Cho phép hiển thị trên ứng dụng khác\"</string>
|
||||
@@ -831,4 +831,7 @@
|
||||
<string name="account_terminated_service_provides_reason">Tài khoản bị vô hiệu hóa. \n\n%1$s cung cấp lý do này: %2$s</string>
|
||||
<string name="entry_deleted">Entry đã xóa</string>
|
||||
<string name="player_http_invalid_status">Đã xảy ra lỗi HTTP %1$s trong khi phát</string>
|
||||
<string name="kao_dialog_warning">Từ tháng 8 năm 2025, Google đã thông báo rằng kể từ tháng 9 năm 2026, việc cài app sẽ yêu cầu các nhà phát triển phải xác minh trên những thiết bị đã chứng nhận, kể cả đối với các ứng dụng ngoài Play Store. Vì các nhà phát triển của NewPipe không đồng ý với điều kiện này, nên có lẽ NewPipe sẽ không còn hoạt động trên các máy Android được chứng nhận sau thời điểm ấy.</string>
|
||||
<string name="kao_dialog_more_info">Chi tiết</string>
|
||||
<string name="kao_solution">Giải pháp</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="open_in_browser">im brüscher öffn</string>
|
||||
<string name="yes">passd scho</string>
|
||||
<string name="ok">passd scho</string>
|
||||
<string name="cancel">stoarnieren</string>
|
||||
<string name="install">iser</string>
|
||||
<string name="no">net</string>
|
||||
<string name="share">tealn</string>
|
||||
</resources>
|
||||
@@ -831,9 +831,23 @@
|
||||
<string name="player_http_403">播放时从服务器收到 HTTP 403 错误,可能因串流 URL 过期或 IP 封锁导致</string>
|
||||
<string name="player_http_invalid_status">播放时从服务器收到 HTTP %1$s 错误</string>
|
||||
<string name="youtube_player_http_403">播放时从服务器收到 HTTP 403 错误,可能因 IP 封锁或串流 URL 解密问题导致</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s 拒绝提供数据, 要求登录确认请求方不是机器人。\n\n你的 IP 可能已经暂时被 %1$s 封禁,你可以等待一段时间或切换到不同 IP (比如开/关 VPN, 或者从 WiFi 连接切换到移动数据)。</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s 拒绝提供数据, 要求登录确认请求方不是机器人。\n\n你的 IP 可能已经暂时被 %1$s 封禁,你可以等待一段时间或切换到不同 IP (比如开/关 VPN, 或者从 WiFi 连接切换到移动数据)。\n\n请见 <a href="%2$s">此 FAQ条目</a> 获取更多信息。</string>
|
||||
<string name="unsupported_content_in_country">此内容对当前选中的内容地区不可用。\n\n要更改选择,请前往 “设置 > 内容 > 默认内容地区”。</string>
|
||||
<string name="kao_dialog_warning">2025 年 8 月,Google 宣布自 2026 年 9 月起,在已认证设备上安装所有安卓应用都需要开发者验证身份,包括在 Play 商店之外安装的应用。由于 NewPipe 开发者反对此要求,NewPipe 在此时间点后不会再在已认证设备上工作。</string>
|
||||
<string name="kao_dialog_more_info">详情</string>
|
||||
<string name="kao_solution">解决方案</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="other">正在导出 %d 个订阅…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="other">正在加载 %d 个订阅…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="other">正在导入 %d 个订阅…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">导入订阅</string>
|
||||
<string name="export_subscriptions_title">导出订阅</string>
|
||||
<string name="import_subscriptions_summary">从之前的 .json 导出文件导入订阅</string>
|
||||
<string name="export_subscriptions_summary">导出订阅到 .json 文件</string>
|
||||
<string name="import_from_previous_export">从之前的导出文件导入</string>
|
||||
</resources>
|
||||
|
||||
@@ -343,7 +343,7 @@
|
||||
<string name="generate_unique_name">生成獨特的名稱</string>
|
||||
<string name="overwrite">覆寫</string>
|
||||
<string name="overwrite_finished_warning">有已下載的同名檔案</string>
|
||||
<string name="download_already_running">已有進行中的下載與此同名</string>
|
||||
<string name="download_already_running">已有正在進行的下載與此同名</string>
|
||||
<string name="show_error">顯示錯誤</string>
|
||||
<string name="error_file_creation">無法建立檔案</string>
|
||||
<string name="error_path_creation">無法建立目的地資料夾</string>
|
||||
@@ -377,7 +377,7 @@
|
||||
<string name="missing_file">檔案已被移動或刪除</string>
|
||||
<string name="overwrite_unrelated_warning">同名的檔案已存在</string>
|
||||
<string name="overwrite_failed">無法覆寫檔案</string>
|
||||
<string name="download_already_pending">已有擱置中的下載與此同名</string>
|
||||
<string name="download_already_pending">已有正在擱置的下載與此同名</string>
|
||||
<string name="error_postprocessing_stopped">NewPipe 在處理檔案時被關閉</string>
|
||||
<string name="error_insufficient_storage_left">裝置上沒有剩餘的空間</string>
|
||||
<string name="error_progress_lost">進度遺失,因為檔案已被刪除</string>
|
||||
@@ -811,6 +811,23 @@
|
||||
<string name="player_http_403">播放時收到來自伺服器的 HTTP 錯誤 403,可能因串流網址過期或 IP 封鎖所致</string>
|
||||
<string name="player_http_invalid_status">播放時收到來自伺服器的 HTTP 錯誤 %1$s</string>
|
||||
<string name="youtube_player_http_403">播放時收到來自伺服器的 HTTP 錯誤 403,可能因 IP 封鎖或串流網址去混淆化問題所致</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s 拒絕提供資料,要求登入以確認請求者並非機器人。\n\n您的 IP 位址可能已被 %1$s 暫時封鎖,您可稍候片刻或切換至其他 IP 位址(例如開啟/關閉 VPN,或從 Wi-Fi 切換至行動數據)。</string>
|
||||
<string name="sign_in_confirm_not_bot_error">%1$s 拒絕提供資料,要求登入以確認請求者並非機器人。\n\n您的 IP 位址可能已被 %1$s 暫時封鎖,您可稍候片刻或切換至其他 IP 位址(例如開啟/關閉 VPN,或從 Wi-Fi 切換至行動數據)。\n\n更多資訊請參見<a href="%2$s">此 FAQ 條目</a>。</string>
|
||||
<string name="unsupported_content_in_country">此內容目前無法於您所選的國家/地區提供。\n\n請至「設定」→「內容」→「預設內容國家」變更您的選擇。</string>
|
||||
<string name="kao_dialog_warning">2025年8月,Google 宣佈自2026年9月起,所有經認證裝置上的 Android 應用程式(包含不是透過 Play 商店安裝的應用程式)皆須通過開發者驗證方可安裝。由於 NewPipe 開發團隊不接受此項要求,該應用程式屆時將無法在經認證的 Android 裝置上運作。</string>
|
||||
<string name="kao_dialog_more_info">詳細資訊</string>
|
||||
<string name="kao_solution">解決方案</string>
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="other">正在匯出 %d 個訂閱…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="other">正在載入 %d 個訂閱…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="other">正在匯入 %d 個訂閱…</item>
|
||||
</plurals>
|
||||
<string name="import_subscriptions_title">匯入訂閱</string>
|
||||
<string name="export_subscriptions_title">匯出訂閱</string>
|
||||
<string name="export_subscriptions_summary">匯出您的訂閱至 .json 檔案</string>
|
||||
<string name="import_subscriptions_summary">從先前匯出的 .json 匯入訂閱</string>
|
||||
<string name="import_from_previous_export">從先前的匯出檔案匯入</string>
|
||||
</resources>
|
||||
|
||||
@@ -413,6 +413,8 @@
|
||||
<string name="import_export_data_path">import_export_data_path</string>
|
||||
<string name="import_data">import_data</string>
|
||||
<string name="export_data">export_data</string>
|
||||
<string name="import_subscriptions_key">import_subscriptions_key</string>
|
||||
<string name="export_subscriptions_key">export_subscriptions_key</string>
|
||||
|
||||
<string name="clear_cookie_key">clear_cookie</string>
|
||||
|
||||
@@ -1213,7 +1215,6 @@
|
||||
<item>fi</item>
|
||||
<item>fil</item>
|
||||
<item>fr</item>
|
||||
<item>frc</item>
|
||||
<item>gl</item>
|
||||
<item>gu</item>
|
||||
<item>he</item>
|
||||
@@ -1314,7 +1315,6 @@
|
||||
<item>Suomen kieli</item>
|
||||
<item>Wikang Filipino</item>
|
||||
<item>Français</item>
|
||||
<item>Français (Louisiana)</item>
|
||||
<item>Galego</item>
|
||||
<item>ગુજરાતી</item>
|
||||
<item>עברית</item>
|
||||
|
||||
@@ -501,6 +501,18 @@
|
||||
<string name="show_error_snackbar">Show an error snackbar</string>
|
||||
<string name="create_error_notification">Create an error notification</string>
|
||||
<!-- Subscriptions import/export -->
|
||||
<plurals name="export_subscriptions">
|
||||
<item quantity="one">Exporting %d subscription…</item>
|
||||
<item quantity="other">Exporting %d subscriptions…</item>
|
||||
</plurals>
|
||||
<plurals name="load_subscriptions">
|
||||
<item quantity="one">Loading %d subscription…</item>
|
||||
<item quantity="other">Loading %d subscriptions…</item>
|
||||
</plurals>
|
||||
<plurals name="import_subscriptions">
|
||||
<item quantity="one">Importing %d subscription…</item>
|
||||
<item quantity="other">Importing %d subscriptions…</item>
|
||||
</plurals>
|
||||
<string name="import_title">Import</string>
|
||||
<string name="import_from">Import from</string>
|
||||
<string name="export_to">Export to</string>
|
||||
@@ -508,6 +520,11 @@
|
||||
<string name="export_ongoing">Exporting…</string>
|
||||
<string name="import_file_title">Import file</string>
|
||||
<string name="previous_export">Previous export</string>
|
||||
<string name="import_subscriptions_title">Import subscriptions"</string>
|
||||
<string name="export_subscriptions_title">Export subscriptions</string>
|
||||
<string name="import_subscriptions_summary">Import subscriptions from a previous .json export"</string>
|
||||
<string name="export_subscriptions_summary">Export your subscriptions to a .json file</string>
|
||||
<string name="import_from_previous_export">Import from previous export</string>
|
||||
<string name="subscriptions_import_unsuccessful">Could not import subscriptions</string>
|
||||
<string name="subscriptions_export_unsuccessful">Could not export subscriptions</string>
|
||||
<string name="import_youtube_instructions">Import YouTube subscriptions from Google takeout:
|
||||
|
||||
@@ -22,4 +22,18 @@
|
||||
android:summary="@string/reset_settings_summary"
|
||||
app:singleLineTitle="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="@string/export_subscriptions_key"
|
||||
android:title="@string/export_subscriptions_title"
|
||||
android:summary="@string/export_subscriptions_summary"
|
||||
app:singleLineTitle="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="@string/import_subscriptions_key"
|
||||
android:title="@string/import_subscriptions_title"
|
||||
android:summary="@string/import_subscriptions_summary"
|
||||
app:singleLineTitle="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
</PreferenceScreen>
|
||||
@@ -5,11 +5,11 @@ import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
|
||||
import org.schabi.newpipe.local.subscription.workers.ImportExportJsonHelper;
|
||||
import org.schabi.newpipe.local.subscription.workers.SubscriptionItem;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -23,26 +23,22 @@ public class ImportExportJsonHelperTest {
|
||||
final String emptySource =
|
||||
"{\"app_version\":\"0.11.6\",\"app_version_int\": 47,\"subscriptions\":[]}";
|
||||
|
||||
final List<SubscriptionItem> items = ImportExportJsonHelper.readFrom(
|
||||
new ByteArrayInputStream(emptySource.getBytes(StandardCharsets.UTF_8)), null);
|
||||
final var items = ImportExportJsonHelper.readFrom(
|
||||
new ByteArrayInputStream(emptySource.getBytes(StandardCharsets.UTF_8)));
|
||||
assertTrue(items.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidSource() {
|
||||
final List<String> invalidList = Arrays.asList(
|
||||
"{}",
|
||||
"",
|
||||
null,
|
||||
"gibberish");
|
||||
final var invalidList = Arrays.asList("{}", "", null, "gibberish");
|
||||
|
||||
for (final String invalidContent : invalidList) {
|
||||
try {
|
||||
if (invalidContent != null) {
|
||||
final byte[] bytes = invalidContent.getBytes(StandardCharsets.UTF_8);
|
||||
ImportExportJsonHelper.readFrom((new ByteArrayInputStream(bytes)), null);
|
||||
ImportExportJsonHelper.readFrom(new ByteArrayInputStream(bytes));
|
||||
} else {
|
||||
ImportExportJsonHelper.readFrom(null, null);
|
||||
ImportExportJsonHelper.readFrom(null);
|
||||
}
|
||||
|
||||
fail("didn't throw exception");
|
||||
@@ -58,38 +54,24 @@ public class ImportExportJsonHelperTest {
|
||||
@Test
|
||||
public void ultimateTest() throws Exception {
|
||||
// Read from file
|
||||
final List<SubscriptionItem> itemsFromFile = readFromFile();
|
||||
final var itemsFromFile = readFromFile();
|
||||
|
||||
// Test writing to an output
|
||||
final String jsonOut = testWriteTo(itemsFromFile);
|
||||
|
||||
// Read again
|
||||
final List<SubscriptionItem> itemsSecondRead = readFromWriteTo(jsonOut);
|
||||
final var itemsSecondRead = readFromWriteTo(jsonOut);
|
||||
|
||||
// Check if both lists have the exact same items
|
||||
if (itemsFromFile.size() != itemsSecondRead.size()) {
|
||||
if (!itemsFromFile.equals(itemsSecondRead)) {
|
||||
fail("The list of items were different from each other");
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemsFromFile.size(); i++) {
|
||||
final SubscriptionItem item1 = itemsFromFile.get(i);
|
||||
final SubscriptionItem item2 = itemsSecondRead.get(i);
|
||||
|
||||
final boolean equals = item1.getServiceId() == item2.getServiceId()
|
||||
&& item1.getUrl().equals(item2.getUrl())
|
||||
&& item1.getName().equals(item2.getName());
|
||||
|
||||
if (!equals) {
|
||||
fail("The list of items were different from each other");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<SubscriptionItem> readFromFile() throws Exception {
|
||||
final InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
|
||||
"import_export_test.json");
|
||||
final List<SubscriptionItem> itemsFromFile = ImportExportJsonHelper.readFrom(
|
||||
inputStream, null);
|
||||
final var inputStream = getClass().getClassLoader()
|
||||
.getResourceAsStream("import_export_test.json");
|
||||
final var itemsFromFile = ImportExportJsonHelper.readFrom(inputStream);
|
||||
|
||||
if (itemsFromFile.isEmpty()) {
|
||||
fail("ImportExportJsonHelper.readFrom(input) returned a null or empty list");
|
||||
@@ -98,10 +80,10 @@ public class ImportExportJsonHelperTest {
|
||||
return itemsFromFile;
|
||||
}
|
||||
|
||||
private String testWriteTo(final List<SubscriptionItem> itemsFromFile) throws Exception {
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ImportExportJsonHelper.writeTo(itemsFromFile, out, null);
|
||||
final String jsonOut = out.toString("UTF-8");
|
||||
private String testWriteTo(final List<SubscriptionItem> itemsFromFile) {
|
||||
final var out = new ByteArrayOutputStream();
|
||||
ImportExportJsonHelper.writeTo(itemsFromFile, out);
|
||||
final String jsonOut = out.toString(StandardCharsets.UTF_8);
|
||||
|
||||
if (jsonOut.isEmpty()) {
|
||||
fail("JSON returned by writeTo was empty");
|
||||
@@ -111,10 +93,8 @@ public class ImportExportJsonHelperTest {
|
||||
}
|
||||
|
||||
private List<SubscriptionItem> readFromWriteTo(final String jsonOut) throws Exception {
|
||||
final ByteArrayInputStream inputStream = new ByteArrayInputStream(
|
||||
jsonOut.getBytes(StandardCharsets.UTF_8));
|
||||
final List<SubscriptionItem> secondReadItems = ImportExportJsonHelper.readFrom(
|
||||
inputStream, null);
|
||||
final var inputStream = new ByteArrayInputStream(jsonOut.getBytes(StandardCharsets.UTF_8));
|
||||
final var secondReadItems = ImportExportJsonHelper.readFrom(inputStream);
|
||||
|
||||
if (secondReadItems.isEmpty()) {
|
||||
fail("second call to readFrom returned an empty list");
|
||||
|
||||
@@ -3,7 +3,9 @@ package org.schabi.newpipe.settings
|
||||
import android.content.SharedPreferences
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import kotlin.io.path.createTempFile
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.fileSize
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
@@ -47,10 +49,10 @@ class ImportAllCombinationsTest {
|
||||
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_", "")
|
||||
val db = createTempFile("newpipe_", "")
|
||||
val dbJournal = createTempFile("newpipe_", "")
|
||||
val dbWal = createTempFile("newpipe_", "")
|
||||
val dbShm = createTempFile("newpipe_", "")
|
||||
Mockito.`when`(fileLocator.db).thenReturn(db)
|
||||
Mockito.`when`(fileLocator.dbJournal).thenReturn(dbJournal)
|
||||
Mockito.`when`(fileLocator.dbShm).thenReturn(dbShm)
|
||||
@@ -62,7 +64,7 @@ class ImportAllCombinationsTest {
|
||||
Assert.assertFalse(dbJournal.exists())
|
||||
Assert.assertFalse(dbWal.exists())
|
||||
Assert.assertFalse(dbShm.exists())
|
||||
Assert.assertTrue("database file size is zero", Files.size(db.toPath()) > 0)
|
||||
Assert.assertTrue("database file size is zero", db.fileSize() > 0)
|
||||
}
|
||||
} else {
|
||||
runTest {
|
||||
@@ -70,7 +72,7 @@ class ImportAllCombinationsTest {
|
||||
Assert.assertTrue(dbJournal.exists())
|
||||
Assert.assertTrue(dbWal.exists())
|
||||
Assert.assertTrue(dbShm.exists())
|
||||
Assert.assertEquals(0, Files.size(db.toPath()))
|
||||
Assert.assertEquals(0, db.fileSize())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,15 @@ import android.content.SharedPreferences
|
||||
import com.grack.nanojson.JsonParser
|
||||
import java.io.File
|
||||
import java.io.ObjectInputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.zip.ZipFile
|
||||
import kotlin.io.path.createTempDirectory
|
||||
import kotlin.io.path.createTempFile
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.fileSize
|
||||
import kotlin.io.path.inputStream
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertThrows
|
||||
@@ -46,7 +53,7 @@ class ImportExportManagerTest {
|
||||
|
||||
@Test
|
||||
fun `The settings must be exported successfully in the correct format`() {
|
||||
val db = File(classloader.getResource("settings/newpipe.db")!!.file)
|
||||
val db = Paths.get(classloader.getResource("settings/newpipe.db")!!.toURI())
|
||||
`when`(fileLocator.db).thenReturn(db)
|
||||
|
||||
val expectedPreferences = mapOf("such pref" to "much wow")
|
||||
@@ -81,29 +88,29 @@ class ImportExportManagerTest {
|
||||
|
||||
@Test
|
||||
fun `Ensuring db directory existence must work`() {
|
||||
val dir = Files.createTempDirectory("newpipe_").toFile()
|
||||
Assume.assumeTrue(dir.delete())
|
||||
`when`(fileLocator.dbDir).thenReturn(dir)
|
||||
val path = createTempDirectory("newpipe_") / BackupFileLocator.FILE_NAME_DB
|
||||
Assume.assumeTrue(path.parent.deleteIfExists())
|
||||
`when`(fileLocator.db).thenReturn(path)
|
||||
|
||||
ImportExportManager(fileLocator).ensureDbDirectoryExists()
|
||||
assertTrue(dir.exists())
|
||||
assertTrue(path.parent.exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Ensuring db directory existence must work when the directory already exists`() {
|
||||
val dir = Files.createTempDirectory("newpipe_").toFile()
|
||||
`when`(fileLocator.dbDir).thenReturn(dir)
|
||||
val path = createTempDirectory("newpipe_") / BackupFileLocator.FILE_NAME_DB
|
||||
`when`(fileLocator.db).thenReturn(path)
|
||||
|
||||
ImportExportManager(fileLocator).ensureDbDirectoryExists()
|
||||
assertTrue(dir.exists())
|
||||
assertTrue(path.parent.exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The database must be extracted from the zip file`() {
|
||||
val db = File.createTempFile("newpipe_", "")
|
||||
val dbJournal = File.createTempFile("newpipe_", "")
|
||||
val dbWal = File.createTempFile("newpipe_", "")
|
||||
val dbShm = File.createTempFile("newpipe_", "")
|
||||
val db = createTempFile("newpipe_", "")
|
||||
val dbJournal = createTempFile("newpipe_", "")
|
||||
val dbWal = createTempFile("newpipe_", "")
|
||||
val dbShm = createTempFile("newpipe_", "")
|
||||
`when`(fileLocator.db).thenReturn(db)
|
||||
`when`(fileLocator.dbJournal).thenReturn(dbJournal)
|
||||
`when`(fileLocator.dbShm).thenReturn(dbShm)
|
||||
@@ -117,15 +124,15 @@ class ImportExportManagerTest {
|
||||
assertFalse(dbJournal.exists())
|
||||
assertFalse(dbWal.exists())
|
||||
assertFalse(dbShm.exists())
|
||||
assertTrue("database file size is zero", Files.size(db.toPath()) > 0)
|
||||
assertTrue("database file size is zero", db.fileSize() > 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Extracting the database from an empty zip must not work`() {
|
||||
val db = File.createTempFile("newpipe_", "")
|
||||
val dbJournal = File.createTempFile("newpipe_", "")
|
||||
val dbWal = File.createTempFile("newpipe_", "")
|
||||
val dbShm = File.createTempFile("newpipe_", "")
|
||||
val db = createTempFile("newpipe_", "")
|
||||
val dbJournal = createTempFile("newpipe_", "")
|
||||
val dbWal = createTempFile("newpipe_", "")
|
||||
val dbShm = createTempFile("newpipe_", "")
|
||||
`when`(fileLocator.db).thenReturn(db)
|
||||
|
||||
val emptyZip = File(classloader.getResource("settings/nodb_noser_nojson.zip")?.file!!)
|
||||
@@ -136,7 +143,7 @@ class ImportExportManagerTest {
|
||||
assertTrue(dbJournal.exists())
|
||||
assertTrue(dbWal.exists())
|
||||
assertTrue(dbShm.exists())
|
||||
assertEquals(0, Files.size(db.toPath()))
|
||||
assertEquals(0, db.fileSize())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -9,5 +9,6 @@ plugins {
|
||||
alias(libs.plugins.jetbrains.kotlin.kapt) apply false
|
||||
alias(libs.plugins.google.ksp) apply false
|
||||
alias(libs.plugins.jetbrains.kotlin.parcelize) apply false
|
||||
alias(libs.plugins.jetbrains.kotlinx.serialization) apply false
|
||||
alias(libs.plugins.sonarqube) apply false
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
اندرویددا یوتیوب اوچون بیر اؤزگور و یونگول قاپاخ.
|
||||
@@ -1 +1,10 @@
|
||||
फिक्स्ड YouTube कोई स्ट्रीम नहीं चला रहा है
|
||||
इस हॉटफ़िक्स रिलीज़ से "Content not available" एरर ठीक हो गया है: अब YouTube वीडियो फिर से चलाए जा सकते हैं!
|
||||
|
||||
इससे 0.28.1 में आई कुछ और गड़बड़ियाँ भी ठीक हो गई हैं:
|
||||
• प्लेलिस्ट आइटम को खींचकर सिर्फ़ आस-पास की जगहों पर ही ले जा पाना
|
||||
• मौजूदा और पिछले वीडियो के बीच टाइटल/कमेंट्स का बार-बार बदलना
|
||||
• "Start main player in fullscreen" ऑप्शन का काम न करना
|
||||
|
||||
दूसरे सुधार:
|
||||
• [YouTube] लाइवस्ट्रीम को 4 घंटे तक पीछे ले जाने की सुविधा फिर से चालू
|
||||
• बैकग्राउंड में चलते समय लाइवस्ट्रीम वीडियो लोड न करना
|
||||
|
||||
4
fastlane/metadata/android/hi/changelogs/1008.txt
Normal file
4
fastlane/metadata/android/hi/changelogs/1008.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
∙ स्ट्रीम को पिछली प्लेबैक स्थिति से फिर से शुरू करने की समस्या ठीक की गई
|
||||
∙ [YouTube] ज़्यादा चैनल URL फ़ॉर्मैट के लिए सपोर्ट जोड़ा गया
|
||||
∙ [YouTube] ज़्यादा वीडियो मेटा-इन्फ़ो फ़ॉर्मैट के लिए सपोर्ट जोड़ा गया
|
||||
∙ अनुवाद अपडेट किए गए
|
||||
14
fastlane/metadata/android/hi/changelogs/1009.txt
Normal file
14
fastlane/metadata/android/hi/changelogs/1009.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
ज़रूरी
|
||||
'Keep Android Open' कैंपेन के बारे में जानकारी और कार्रवाई के लिए अपील जोड़ी गई: https://www.keepandroidopen.org/
|
||||
|
||||
बेहतर
|
||||
[फ़ीड] पुरानी सब्सक्रिप्शन के अपडेट होने का क्रम बदला गया
|
||||
कमेंट पेज एक के ऊपर एक न दिखें
|
||||
वीडियो डिटेल पेज पर क्लिक करने पर, क्लिक इवेंट नीचे के व्यूज़ तक न पहुँचें
|
||||
|
||||
ठीक किया गया
|
||||
कमेंट रिप्लाई हेडर लेआउट में अवतार इमेज न दिखना
|
||||
प्लेयर से जुड़े कई UI सुधार
|
||||
[SoundCloud] लंबी ID वाले स्ट्रीम को ठीक किया गया
|
||||
|
||||
और भी कई सुधार और बेहतर बदलाव!
|
||||
16
fastlane/metadata/android/id/changelogs/1006.txt
Normal file
16
fastlane/metadata/android/id/changelogs/1006.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
# Peningkatan
|
||||
Pertahankan pemutar saat ini ketika mengklik stempel waktu
|
||||
Cobalah untuk memulihkan misi pengunduhan yang tertunda jika memungkinkan
|
||||
Tambahkan opsi untuk menghapus unduhan tanpa ikut menghapus berkas
|
||||
Izin Overlay: menampilkan dialog penjelasan untuk Android > R
|
||||
Mendukung tautan on.soundcloud saat membuka
|
||||
Banyak perbaikan dan optimasi kecil.
|
||||
|
||||
# Telah diperbaiki
|
||||
Memperbaiki format penghitungan pendek untuk versi Android di bawah 7
|
||||
Memperbaiki notifikasi berhantu
|
||||
Memperbaiki berkas SRT
|
||||
Memperbaiki banyak sekali kerusakan
|
||||
|
||||
# Pengembangan
|
||||
Modernisasi kode internal
|
||||
4
fastlane/metadata/android/id/changelogs/1008.txt
Normal file
4
fastlane/metadata/android/id/changelogs/1008.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
∙ Memperbaiki masalah melanjutkan pemutaran pengaliran dari posisi pemutaran terakhir
|
||||
∙ [YouTube] Menambahkan dukungan untuk lebih banyak format Tautan saluran
|
||||
∙ [YouTube] Menambahkan dukungan untuk lebih banyak format metainfo video
|
||||
∙ Pembaruan terjemahan
|
||||
14
fastlane/metadata/android/id/changelogs/1009.txt
Normal file
14
fastlane/metadata/android/id/changelogs/1009.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Penting
|
||||
Informasi dan ajakan untuk bertindak terkait kampanye Keep Android Open telah ditambahkan: https://www.keepandroidopen.org/
|
||||
|
||||
Improved
|
||||
[Feed] Acak urutan pembaruan langganan yang sudah kedaluwarsa di
|
||||
Jangan menumpuk halaman komentar
|
||||
Jangan meneruskan peristiwa klik ke tampilan yang mendasarinya saat mengklik halaman detail video.
|
||||
|
||||
Telah Diperbaiki
|
||||
Tata letak header balasan komentar tanpa gambar avatar
|
||||
Perbaikan antarmuka pengguna terkait pemain ganda
|
||||
[SoundCloud] Perbaiki aliran data dengan ID yang panjang
|
||||
|
||||
dan masih banyak perbaikan dan peningkatan lainnya!
|
||||
4
fastlane/metadata/android/it/changelogs/1008.txt
Normal file
4
fastlane/metadata/android/it/changelogs/1008.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
∙ Corretto il problema di ripresa degli streaming dall'ultima posizione di riproduzione
|
||||
∙ [YouTube] Aggiunto il supporto per più formati di URL dei canali
|
||||
∙ [YouTube] Aggiunto il supporto per più formati di metainformazioni video
|
||||
∙ Traduzioni aggiornate
|
||||
14
fastlane/metadata/android/it/changelogs/1009.txt
Normal file
14
fastlane/metadata/android/it/changelogs/1009.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Importante
|
||||
Informazioni e invito all'azione per la campagna Keep Android Open aggiunte: https://www.keepandroidopen.org/
|
||||
|
||||
Migliorato
|
||||
[Feed] Riordina l'ordine in cui vengono aggiornati gli abbonamenti obsoleti
|
||||
Non sovrapporre le pagine dei commenti
|
||||
Non passare gli eventi clic alle viste sottostanti quando si fa clic sulla pagina dei dettagli del video
|
||||
|
||||
Corretto
|
||||
Layout dell'intestazione delle risposte ai commenti senza immagine avatar
|
||||
Correzioni multiple all'interfaccia utente relative al player
|
||||
[SoundCloud] Correzione degli stream con ID lunghi
|
||||
|
||||
e altre correzioni e miglioramenti!
|
||||
4
fastlane/metadata/android/pa/changelogs/1008.txt
Normal file
4
fastlane/metadata/android/pa/changelogs/1008.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
∙ ਫਿਕਸਡ ਆਖਰੀ ਪਲੇਬੈਕ ਸਥਿਤੀ 'ਤੇ ਸਟ੍ਰੀਮਾਂ ਨੂੰ ਰਿਜ਼ਿਊਮ ਕਰਨਾ ਠੀਕ ਕੀਤਾ ਗਿਆ
|
||||
∙ [YouTube] ਹੋਰ ਚੈਨਲ URL ਫਾਰਮੈਟਾਂ ਲਈ ਸਮਰਥਨ ਜੁਟਾਇਆ ਗਿਆ
|
||||
∙ [YouTube] ਹੋਰ ਵੀਡੀਓ ਮੈਟਾਇਨਫੋ ਫਾਰਮੈਟਾਂ ਲਈ ਸਮਰਥਨ ਜੁਟਾਇਆ ਗਿਆ
|
||||
∙ ਅੱਪਡੇਟ ਕੀਤੇ ਅਨੁਵਾਦ
|
||||
15
fastlane/metadata/android/pa/changelogs/1009.txt
Normal file
15
fastlane/metadata/android/pa/changelogs/1009.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
ਮਹੱਤਵਪੂਰਨ
|
||||
ਕੀਪ ਐਂਡਰਾਇਡ ਓਪਨ ਮੁਹਿੰਮ ਲਈ ਜਾਣਕਾਰੀ ਅਤੇ ਕਾਰਵਾਈ ਲਈ ਸੱਦਾ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ: https://www.keepandroidopen.org/
|
||||
|
||||
ਫਿਕਸਡ
|
||||
[ਫੀਡ] ਪੁਰਾਣੀਆਂ ਗਾਹਕੀਆਂ ਨੂੰ ਅੱਪਡੇਟ ਕੀਤੇ ਜਾਣ ਦੇ ਕ੍ਰਮ ਨੂੰ ਸ਼ਫਲ ਕਰੋ
|
||||
|
||||
ਟਿੱਪਣੀ ਪੰਨਿਆਂ ਨੂੰ ਸਟੈਕ ਨਾ ਕਰੋ
|
||||
ਵੀਡੀਓ ਵੇਰਵੇ ਪੰਨੇ 'ਤੇ ਕਲਿੱਕ ਕਰਦੇ ਸਮੇਂ ਕਲਿੱਕ ਇਵੈਂਟਾਂ ਨੂੰ ਅੰਡਰਲਾਈੰਗ ਵਿਯੂਜ਼ ਵਿੱਚ ਨਾ ਭੇਜੋ
|
||||
|
||||
ਸਥਿਰ
|
||||
ਅਵਤਾਰ ਚਿੱਤਰ ਤੋਂ ਬਿਨਾਂ ਟਿੱਪਣੀ ਜਵਾਬਾਂ ਦਾ ਸਿਰਲੇਖ ਲੇਆਉਟ
|
||||
ਮਲਟੀਪਲ ਪਲੇਅਰ-ਸਬੰਧਤ UI ਫਿਕਸ
|
||||
[ਸਾਊਂਡ ਕਲਾਉਡ] ਲੰਬੇ ਆਈਡੀ ਨਾਲ ਸਟ੍ਰੀਮਾਂ ਨੂੰ ਫਿਕਸ ਕਰੋ
|
||||
|
||||
ਅਤੇ ਹੋਰ ਫਿਕਸ ਅਤੇ ਸੁਧਾਰ!
|
||||
11
fastlane/metadata/android/ro/changelogs/1000.txt
Normal file
11
fastlane/metadata/android/ro/changelogs/1000.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Îmbunătățiri
|
||||
• Descrierea listei de redare poate fi extinsă sau restrânsă la clic
|
||||
• Gestionare automată a linkurilor subscribeto.me pentru PeerTube
|
||||
• Redarea unui singur element pornește doar din ecranul Istoric
|
||||
|
||||
Remedieri
|
||||
• Corectată vizibilitatea butonului RSS
|
||||
• Rezolvată blocarea previzualizării în bara de căutare
|
||||
• Corectată adăugarea elementelor fără miniatură
|
||||
• Reparat pop-up-ul pentru elemente corelate
|
||||
• Corectată ordinea în „Adaugă la lista de redare”
|
||||
6
fastlane/metadata/android/ro/changelogs/1001.txt
Normal file
6
fastlane/metadata/android/ro/changelogs/1001.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Îmbunătățit
|
||||
• Se permite întotdeauna modificarea preferințelor de notificare ale jucătorului pe Android 13+
|
||||
|
||||
Remediat
|
||||
• S-a remediat problema care împiedica exportul bazei de date/abonamentelor să trunchieze un fișier deja existent, ceea ce putea duce la un export corupt
|
||||
• S-a remediat problema reluării playerului de la început la clic pe un timestamp
|
||||
4
fastlane/metadata/android/ro/changelogs/1002.txt
Normal file
4
fastlane/metadata/android/ro/changelogs/1002.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
S-a remediat eroarea care împiedica YouTube să redea niciun stream.
|
||||
|
||||
Această versiune rezolvă doar cea mai urgentă eroare care împiedică încărcarea detaliilor videoclipurilor YouTube.
|
||||
Știm că există și alte probleme și vom lansa în curând o versiune separată pentru a le rezolva.
|
||||
6
fastlane/metadata/android/ro/changelogs/1003.txt
Normal file
6
fastlane/metadata/android/ro/changelogs/1003.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Aceasta este o actualizare rapidă care corectează erori YouTube:
|
||||
• [YouTube] Repară încărcarea informațiilor video, erorile HTTP 403 la redare și redarea unor videoclipuri cu restricție de vârstă
|
||||
• Corectează problema dimensiunii subtitrărilor care nu se actualiza
|
||||
• Corectează descărcarea dublă a informațiilor la deschiderea unui stream
|
||||
• [SoundCloud] Elimină streamurile protejate DRM care nu puteau fi redate
|
||||
• Traduceri actualizate
|
||||
3
fastlane/metadata/android/ro/changelogs/1004.txt
Normal file
3
fastlane/metadata/android/ro/changelogs/1004.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Această versiune remediază problema YouTube care oferea doar un flux video la 360p.
|
||||
|
||||
Rețineți că soluția utilizată în această versiune este probabil temporară și, pe termen lung, protocolul video SABR trebuie implementat, însă membrii TeamNewPipe sunt ocupați în prezent, așa că orice ajutor ar fi foarte apreciat! https://github.com/TeamNewPipe/NewPipe/issues/12248
|
||||
@@ -1,17 +1,2 @@
|
||||
Yeni
|
||||
• Android Auto desteği eklendi
|
||||
• Akış gruplarının ana ekran sekmeleri olarak ayarlanmasına izin verme
|
||||
• [YouTube] Geçici oynatma listesi olarak paylaşma
|
||||
• [SoundCloud] Beğenilen kanal sekmesi
|
||||
|
||||
Geliştirildi
|
||||
• Daha iyi arama çubuğu önerileri
|
||||
• İndirilenler'de indirme tarihini gösterimi
|
||||
• Android 13 uygulama başı dil kullanma
|
||||
|
||||
Düzeltildi
|
||||
• Karanlık modda bozuk metin renkleri düzeltildi
|
||||
• [YouTube] 100'den fazla öğeyi yüklemeyen oynatma listeleri düzeltildi
|
||||
• [YouTube] Eksik önerilen videolar düzeltildi
|
||||
• Geçmiş listesi görünümündeki çökmeler düzeltildi
|
||||
• Yorum yanıtlarındaki zaman damgaları düzeltildi
|
||||
Yeni: Android Auto desteği eklendi. Akış grupları ana ekran sekmesi yapılabilir. [YouTube] Geçici oynatma listesi olarak paylaşma. [SoundCloud] Beğenilen kanallar sekmesi geliştirildi. Daha iyi arama çubuğu önerileri. İndirilenler’de indirme tarihi gösterimi. Android 13 uygulama başı dil kullanımı.
|
||||
Düzeltildi: Karanlık modda bozuk metin renkleri. [YouTube] 100+ öğe yükleyemeyen oynatma listeleri. Eksik önerilen videolar. Geçmiş listesi çökmeleri. Yorum yanıtı zaman damgaları.
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
|
||||
### 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ắt animation của biểu tượng burgermenu #1486
|
||||
- hoàn tác các tải xuống đã xóa #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 =\\).
|
||||
- Đã thêm tùy chọn chia sẻ vào menu nhấn giữ #1454
|
||||
- Thu nhỏ trình phát chính khi thoát #1354
|
||||
- Cập nhật phiên bản thư viện và bản sửa lỗi sao lưu cơ sở dữ liệu #1510
|
||||
- Cập nhật ExoPlayer lên bản 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 số lần nhân đôi khác nhau nhằm thay đổi tốc độ nhanh hơn.
|
||||
- Đã thêm nút chuyển đổi để tuanhanh 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
|
||||
|
||||
@@ -1,31 +1,21 @@
|
||||
# thay đổi của v0.14.1
|
||||
# Thay đổi
|
||||
|
||||
### Đã 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
|
||||
### v0.14.1
|
||||
Đã sửa
|
||||
• Lỗi không giải mã được URL video (#1659)
|
||||
• Lỗi liên kết trong mô tả video (#1657)
|
||||
|
||||
# thay đổi của v0.14.0
|
||||
### v0.14.0
|
||||
Mới
|
||||
• Thiết kế ngăn kéo mới
|
||||
• Trang chủ có thể tùy chỉnh
|
||||
|
||||
### 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
|
||||
• Cử chỉ điều khiển được làm lại
|
||||
• Cách mới để đóng trình phát cửa sổ bật lên
|
||||
|
||||
### 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.
|
||||
Đã sửa
|
||||
• Lỗi hiển thị số lượng người đăng ký
|
||||
• Lỗi danh sách phát YouTube trống
|
||||
• Lỗi tìm kiếm, liên kết mô tả và tải video
|
||||
• Một số lỗi trình phát, hình thu nhỏ và đăng ký
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
### Cải thiện
|
||||
* Thêm thông báo cập nhật ứng dụng cho bản dựng GitHub (#1608 bởi @krtkush)
|
||||
* Nhiều cải tiến cho trình tải xuống (#1944 bởi @kapodamy):
|
||||
* Thêm biểu tượng trắng bị thiếu và sử dụng cách thức cố định để thay đổi màu biểu tượng
|
||||
* Kiểm tra xem trình lặp có được khởi tạo hay không (sửa lỗi #2031)
|
||||
* Cho phép tải xuống lại với lỗi "xử lý hậu kỳ thất bại" trong bộ ghép nối mới
|
||||
* Bộ ghép nối MPEG-4 mới sửa lỗi luồng video và âm thanh không đồng bộ (#2039)
|
||||
* Thêm thông báo cập nhật cho bản build từ GitHub (#1608, @krtkush)
|
||||
* Cải thiện downloader: bổ sung icon, cho phép tải lại khi lỗi hậu xử lý và thêm muxer MPEG-4 mới để đồng bộ video-audio (#1944, #2039, @kapodamy)
|
||||
|
||||
### Sửa lỗi
|
||||
* Luồng trực tiếp YouTube dừng phát sau một thời gian ngắn (#1996 bởi @yausername)
|
||||
* Livestream YouTube dừng phát sau một thời gian ngắn (#1996, @yausername)
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
<h4>Cải tiến</h4>
|
||||
<ul>
|
||||
<li>click được liên kết trong phần bình luận, tăng cỡ chữ</li>
|
||||
<li>nhảy đến khi click vào mốc thời gian ở bình luận</li>
|
||||
<li>hiện tab ưa thích dựa trên trạng thái lựa chọn gần đây</li>
|
||||
<li>thêm danh sách phát vào hàng chờ khi chạm lâu 'Phát nền' trong cửa sổ danh sách phát</li>
|
||||
<li>tìm kiếm từ ngữ chung khi nó không phảiURL</li>
|
||||
<li>thêm "chia sẻ thời gian hiện tại " nút trờ về video chính</li>
|
||||
<li>thêm nút đóng vào trình phát chính khi hàng đợi video kết thúc</li>
|
||||
<li>thêm "Chơi trực tiếp dưới nền" chạm lâu vào menu để xem danh sách video</li>
|
||||
<li>cải thiện bản dịch tiếng Anh cho lệnh Chơi/Thêm vào danh sách </li>
|
||||
<li>cải thiện hiệu năng một xíu</li>
|
||||
<li>xóa bỏ những tệp không dùng đến</li>
|
||||
<li>cập nhật ExoPlayer lên 2.9.6</li>
|
||||
<li>hỗ trợ liên kết Invidious </li>
|
||||
<li>Click được link và mốc thời gian trong bình luận</li>
|
||||
<li>Hiện tab ưa thích theo lựa chọn gần đây</li>
|
||||
<li>Chạm lâu “Phát nền” để thêm playlist vào hàng chờ</li>
|
||||
<li>Tìm kiếm khi nhập không phải URL</li>
|
||||
<li>Thêm nút “Chia sẻ thời điểm hiện tại”</li>
|
||||
<li>Thêm nút đóng khi hàng phát kết thúc</li>
|
||||
<li>Cải thiện hiệu năng</li>
|
||||
<li>Cập nhật ExoPlayer 2.9.6</li>
|
||||
<li>Hỗ trợ liên kết Invidious</li>
|
||||
</ul>
|
||||
|
||||
<h4>Vá lỗi</h4>
|
||||
<ul>
|
||||
<li>sửa w/ bình luận và vô hiệu hóa phát luồng liên quan</li>
|
||||
<li>sửa lỗi CheckForNewAppVersionTask bị thực thi khi không mong muốn't</li>
|
||||
<li>sửa lỗi nhập danh sách kênh youtube đăng ký: phớt lờ url không hợp lệ và giữ nó trống với tiêu đề</li>
|
||||
<li>sửa lỗi url youtube không hợp lệ: tên thẻ chữ ký không phải lúc nào cũng là "chữ ký" ngăn luồng tải</li>
|
||||
<li>Sửa lỗi bình luận</li>
|
||||
<li>Sửa kiểm tra cập nhật chạy ngoài ý muốn</li>
|
||||
<li>Sửa nhập danh sách kênh đăng ký</li>
|
||||
<li>Sửa URL YouTube không hợp lệ</li>
|
||||
</ul>
|
||||
|
||||
16
fastlane/metadata/android/vi/changelogs/750.txt
Normal file
16
fastlane/metadata/android/vi/changelogs/750.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
Mới
|
||||
• Tiếp tục phát video từ vị trí đã dừng (#2288)
|
||||
• Cải tiến tải xuống: hỗ trợ SD-card (SAF), muxer MP4 mới, đổi thư mục tải trước khi tải, tôn trọng mạng dữ liệu (#2149)
|
||||
|
||||
Cải thiện
|
||||
• Dọn chuỗi không dùng
|
||||
• Xử lý xoay màn hình tốt hơn
|
||||
• Menu chạm lâu nhất quán hơn
|
||||
|
||||
Đã sửa
|
||||
• Hiển thị đúng tên phụ đề
|
||||
• Không crash khi kiểm tra cập nhật thất bại
|
||||
• Sửa lỗi tải kẹt 99.9%
|
||||
• Cập nhật metadata hàng phát
|
||||
• Sửa lỗi playlist SoundCloud
|
||||
• Sửa lỗi đọc thời lượng YouTube TeamNewPipe/NewPipeExtractor#177
|
||||
@@ -12,10 +12,10 @@ autoservice-google = "1.1.1"
|
||||
autoservice-zacsweers = "1.2.0"
|
||||
bridge = "v2.0.2"
|
||||
cardview = "1.0.0"
|
||||
checkstyle = "13.2.0"
|
||||
coil = "3.3.0"
|
||||
checkstyle = "13.3.0"
|
||||
coil = "3.4.0"
|
||||
constraintlayout = "2.2.1"
|
||||
core = "1.17.0"
|
||||
core = "1.17.0" # Newer versions require minSdk >= 23
|
||||
desugar = "2.1.5"
|
||||
documentfile = "1.1.0"
|
||||
exoplayer = "2.19.1"
|
||||
@@ -24,7 +24,9 @@ groupie = "2.10.1"
|
||||
jsoup = "1.22.1"
|
||||
junit = "4.13.2"
|
||||
junit-ext = "1.3.0"
|
||||
kotlin = "2.3.10"
|
||||
kotlin = "2.3.20"
|
||||
kotlinx-coroutines-rx3 = "1.10.2"
|
||||
kotlinx-serialization-json = "1.10.0"
|
||||
ksp = "2.3.6"
|
||||
ktlint = "1.8.0"
|
||||
leakcanary = "2.14"
|
||||
@@ -33,7 +35,7 @@ localbroadcastmanager = "1.1.0"
|
||||
markwon = "4.6.2"
|
||||
material = "1.11.0" # TODO: update to newer version after bug is fixed. See https://github.com/TeamNewPipe/NewPipe/pull/13018
|
||||
media = "1.7.1"
|
||||
mockitoCore = "5.21.0"
|
||||
mockitoCore = "5.23.0"
|
||||
okhttp = "5.3.2"
|
||||
phoenix = "3.0.0"
|
||||
preference = "1.2.1"
|
||||
@@ -44,7 +46,7 @@ runner = "1.7.0"
|
||||
rxandroid = "3.0.2"
|
||||
rxbinding = "4.0.0"
|
||||
rxjava = "3.1.12"
|
||||
sonarqube = "7.2.2.6593"
|
||||
sonarqube = "7.2.3.7755"
|
||||
statesaver = "1.4.1" # TODO: Drop because it is deprecated and incompatible with KSP2
|
||||
stetho = "1.6.0"
|
||||
swiperefreshlayout = "1.2.0"
|
||||
@@ -110,6 +112,8 @@ jakewharton-phoenix = { module = "com.jakewharton:process-phoenix", version.ref
|
||||
jakewharton-rxbinding = { module = "com.jakewharton.rxbinding4:rxbinding", version.ref = "rxbinding" }
|
||||
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||
junit = { module = "junit:junit", version.ref = "junit" }
|
||||
kotlinx-coroutines-rx3 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-rx3", version.ref = "kotlinx-coroutines-rx3" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
|
||||
lisawray-groupie-core = { module = "com.github.lisawray.groupie:groupie", version.ref = "groupie" }
|
||||
lisawray-groupie-viewbinding = { module = "com.github.lisawray.groupie:groupie-viewbinding", version.ref = "groupie" }
|
||||
livefront-bridge = { module = "com.github.livefront:bridge", version.ref = "bridge" }
|
||||
@@ -136,4 +140,5 @@ google-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
jetbrains-kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } # Needed for statesaver
|
||||
jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
|
||||
jetbrains-kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" }
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
||||
distributionSha256Sum=60ea723356d81263e8002fec0fcf9e2b0eee0c0850c7a3d7ab0a63f2ccc601f3
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
2
gradlew
vendored
2
gradlew
vendored
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
||||
Reference in New Issue
Block a user