diff --git a/app/build.gradle b/app/build.gradle index 02146c5f8..ec7bc3776 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,9 +28,9 @@ android { if (System.properties.containsKey('versionCodeOverride')) { versionCode System.getProperty('versionCodeOverride') as Integer } else { - versionCode 1004 + versionCode 1005 } - versionName "0.27.7" + versionName "0.28.0" if (System.properties.containsKey('versionNameSuffix')) { versionNameSuffix System.getProperty('versionNameSuffix') } diff --git a/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java b/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java index 891824a55..892d1df0f 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java +++ b/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java @@ -12,6 +12,7 @@ import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.exceptions.ParsingException; import java.util.Arrays; +import java.util.Objects; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -23,8 +24,23 @@ import static org.junit.Assert.assertTrue; @LargeTest public class ErrorInfoTest { + /** + * @param errorInfo the error info to access + * @return the private field errorInfo.message.stringRes using reflection + */ + private int getMessageFromErrorInfo(final ErrorInfo errorInfo) + throws NoSuchFieldException, IllegalAccessException { + final var message = ErrorInfo.class.getDeclaredField("message"); + message.setAccessible(true); + final var messageValue = (ErrorInfo.Companion.ErrorMessage) message.get(errorInfo); + + final var stringRes = ErrorInfo.Companion.ErrorMessage.class.getDeclaredField("stringRes"); + stringRes.setAccessible(true); + return (int) Objects.requireNonNull(stringRes.get(messageValue)); + } + @Test - public void errorInfoTestParcelable() { + public void errorInfoTestParcelable() throws NoSuchFieldException, IllegalAccessException { final ErrorInfo info = new ErrorInfo(new ParsingException("Hello"), UserAction.USER_REPORT, "request", ServiceList.YouTube.getServiceId()); // Obtain a Parcel object and write the parcelable object to it: @@ -39,7 +55,7 @@ public class ErrorInfoTest { assertEquals(ServiceList.YouTube.getServiceInfo().getName(), infoFromParcel.getServiceName()); assertEquals("request", infoFromParcel.getRequest()); - assertEquals(R.string.parsing_error, infoFromParcel.getMessageStringId()); + assertEquals(R.string.parsing_error, getMessageFromErrorInfo(infoFromParcel)); parcel.recycle(); } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0ac368898..c788385e5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -435,6 +435,7 @@ diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index 79c390063..41f81f136 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -29,7 +29,7 @@ import okhttp3.ResponseBody; public final class DownloaderImpl extends Downloader { public static final String USER_AGENT = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"; + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0"; public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY = "youtube_restricted_mode_key"; public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000"; diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index aafa0cc9f..1950f7160 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -75,8 +75,8 @@ import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; -import org.schabi.newpipe.settings.SettingMigrations; import org.schabi.newpipe.settings.UpdateSettingsFragment; +import org.schabi.newpipe.settings.migration.MigrationManager; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KioskTranslator; @@ -192,7 +192,7 @@ public class MainActivity extends AppCompatActivity { UpdateSettingsFragment.askForConsentToUpdateChecks(this); } - SettingMigrations.showUserInfoIfPresent(this); + MigrationManager.showUserInfoIfPresent(this); } @Override @@ -260,19 +260,6 @@ public class MainActivity extends AppCompatActivity { */ private void addDrawerMenuForCurrentService() throws ExtractionException { //Tabs - final int currentServiceId = ServiceHelper.getSelectedServiceId(this); - final StreamingService service = NewPipe.getService(currentServiceId); - - int kioskMenuItemId = 0; - - for (final String ks : service.getKioskList().getAvailableKiosks()) { - drawerLayoutBinding.navigation.getMenu() - .add(R.id.menu_tabs_group, kioskMenuItemId, 0, KioskTranslator - .getTranslatedKioskName(ks, this)) - .setIcon(KioskTranslator.getKioskIcon(ks)); - kioskMenuItemId++; - } - drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions) @@ -290,6 +277,20 @@ public class MainActivity extends AppCompatActivity { .add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history) .setIcon(R.drawable.ic_history); + //Kiosks + final int currentServiceId = ServiceHelper.getSelectedServiceId(this); + final StreamingService service = NewPipe.getService(currentServiceId); + + int kioskMenuItemId = 0; + + for (final String ks : service.getKioskList().getAvailableKiosks()) { + drawerLayoutBinding.navigation.getMenu() + .add(R.id.menu_kiosks_group, kioskMenuItemId, 0, KioskTranslator + .getTranslatedKioskName(ks, this)) + .setIcon(KioskTranslator.getKioskIcon(ks)); + kioskMenuItemId++; + } + //Settings and About drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings) @@ -309,10 +310,13 @@ public class MainActivity extends AppCompatActivity { changeService(item); break; case R.id.menu_tabs_group: + tabSelected(item); + break; + case R.id.menu_kiosks_group: try { - tabSelected(item); + kioskSelected(item); } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(this, "Selecting main page tab", e); + ErrorUtil.showUiErrorSnackbar(this, "Selecting drawer kiosk", e); } break; case R.id.menu_options_about_group: @@ -336,7 +340,7 @@ public class MainActivity extends AppCompatActivity { .setChecked(true); } - private void tabSelected(final MenuItem item) throws ExtractionException { + private void tabSelected(final MenuItem item) { switch (item.getItemId()) { case ITEM_ID_SUBSCRIPTIONS: NavigationHelper.openSubscriptionFragment(getSupportFragmentManager()); @@ -353,18 +357,19 @@ public class MainActivity extends AppCompatActivity { case ITEM_ID_HISTORY: NavigationHelper.openStatisticFragment(getSupportFragmentManager()); break; - default: - final StreamingService currentService = ServiceHelper.getSelectedService(this); - int kioskMenuItemId = 0; - for (final String kioskId : currentService.getKioskList().getAvailableKiosks()) { - if (kioskMenuItemId == item.getItemId()) { - NavigationHelper.openKioskFragment(getSupportFragmentManager(), - currentService.getServiceId(), kioskId); - break; - } - kioskMenuItemId++; - } + } + } + + private void kioskSelected(final MenuItem item) throws ExtractionException { + final StreamingService currentService = ServiceHelper.getSelectedService(this); + int kioskMenuItemId = 0; + for (final String kioskId : currentService.getKioskList().getAvailableKiosks()) { + if (kioskMenuItemId == item.getItemId()) { + NavigationHelper.openKioskFragment(getSupportFragmentManager(), + currentService.getServiceId(), kioskId); break; + } + kioskMenuItemId++; } } @@ -405,6 +410,7 @@ public class MainActivity extends AppCompatActivity { drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_services_group); drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_tabs_group); + drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_kiosks_group); drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_options_about_group); // Show up or down arrow diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index d434c21ee..3933ade92 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -58,20 +58,10 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService.LinkType; import org.schabi.newpipe.extractor.channel.ChannelInfo; -import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException; -import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; -import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException; -import org.schabi.newpipe.extractor.exceptions.PaidContentException; -import org.schabi.newpipe.extractor.exceptions.PrivateContentException; -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; -import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException; -import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; -import org.schabi.newpipe.ktx.ExceptionUtils; import org.schabi.newpipe.local.dialog.PlaylistDialog; import org.schabi.newpipe.player.PlayerType; import org.schabi.newpipe.player.helper.PlayerHelper; @@ -260,7 +250,8 @@ public class RouterActivity extends AppCompatActivity { showUnsupportedUrlDialog(url); } }, throwable -> handleError(this, new ErrorInfo(throwable, - UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url)))); + UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url, + null, url)))); } /** @@ -269,40 +260,19 @@ public class RouterActivity extends AppCompatActivity { * @param errorInfo the error information */ private static void handleError(final Context context, final ErrorInfo errorInfo) { - if (errorInfo.getThrowable() != null) { - errorInfo.getThrowable().printStackTrace(); - } - - if (errorInfo.getThrowable() instanceof ReCaptchaException) { + if (errorInfo.getRecaptchaUrl() != null) { Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show(); // Starting ReCaptcha Challenge Activity final Intent intent = new Intent(context, ReCaptchaActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.getRecaptchaUrl()); context.startActivity(intent); - } else if (errorInfo.getThrowable() != null - && ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) { - Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) { - Toast.makeText(context, R.string.restricted_video_no_stream, - Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) { - Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof PaidContentException) { - Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof PrivateContentException) { - Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) { - Toast.makeText(context, R.string.soundcloud_go_plus_content, - Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) { - Toast.makeText(context, R.string.youtube_music_premium_content, - Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) { - Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) { - Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show(); - } else { + } else if (errorInfo.isReportable()) { ErrorUtil.createNotification(context, errorInfo); + } else { + // this exception does not usually indicate a problem that should be reported, + // so just show a toast instead of the notification + Toast.makeText(context, errorInfo.getMessage(context), Toast.LENGTH_LONG).show(); } if (context instanceof RouterActivity) { @@ -665,7 +635,8 @@ public class RouterActivity extends AppCompatActivity { startActivity(intent); finish(); }, throwable -> handleError(this, new ErrorInfo(throwable, - UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl))) + UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl, + null, currentUrl))) ); return; } @@ -852,10 +823,10 @@ public class RouterActivity extends AppCompatActivity { }) )), throwable -> runOnVisible(ctx -> handleError(ctx, new ErrorInfo( - throwable, - UserAction.REQUESTED_STREAM, + throwable, UserAction.REQUESTED_STREAM, "Tried to add " + currentUrl + " to a playlist", - ((RouterActivity) ctx).currentService.getServiceId()) + ((RouterActivity) ctx).currentService.getServiceId(), + currentUrl) )) ) ); @@ -995,7 +966,7 @@ public class RouterActivity extends AppCompatActivity { } }, throwable -> handleError(this, new ErrorInfo(throwable, finalUserAction, choice.url + " opened with " + choice.playerChoice, - choice.serviceId))); + choice.serviceId, choice.url))); } } diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 003aa5893..0857fa339 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -389,8 +389,7 @@ public class DownloadDialog extends DialogFragment } }, throwable -> ErrorUtil.showSnackbar(context, new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG, - "Downloading video stream size", - currentInfo.getServiceId())))); + "Downloading video stream size", currentInfo)))); disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(getWrappedAudioStreams()) .subscribe(result -> { if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() @@ -399,8 +398,7 @@ public class DownloadDialog extends DialogFragment } }, throwable -> ErrorUtil.showSnackbar(context, new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG, - "Downloading audio stream size", - currentInfo.getServiceId())))); + "Downloading audio stream size", currentInfo)))); disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(wrappedSubtitleStreams) .subscribe(result -> { if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() @@ -409,8 +407,7 @@ public class DownloadDialog extends DialogFragment } }, throwable -> ErrorUtil.showSnackbar(context, new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG, - "Downloading subtitle stream size", - currentInfo.getServiceId())))); + "Downloading subtitle stream size", currentInfo)))); } private void setupAudioTrackSpinner() { diff --git a/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java b/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java index 4d9966364..90d8f4797 100644 --- a/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java +++ b/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java @@ -36,8 +36,8 @@ public class AcraReportSender implements ReportSender { ErrorUtil.openActivity(context, new ErrorInfo( new String[]{report.getString(ReportField.STACK_TRACE)}, UserAction.UI_ERROR, - ErrorInfo.SERVICE_NONE, "ACRA report", + null, R.string.app_ui_crash)); } } diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java index a07b9b0b5..160dcca4d 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java @@ -115,7 +115,7 @@ public class ErrorActivity extends AppCompatActivity { // normal bugreport buildInfo(errorInfo); - activityErrorBinding.errorMessageView.setText(errorInfo.getMessageStringId()); + activityErrorBinding.errorMessageView.setText(errorInfo.getMessage(this)); activityErrorBinding.errorView.setText(formErrorText(errorInfo.getStackTraces())); // print stack trace once again for debugging: diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt index 6d8c1bd63..609fbb336 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -1,115 +1,304 @@ package org.schabi.newpipe.error +import android.content.Context import android.os.Parcelable import androidx.annotation.StringRes +import androidx.core.content.ContextCompat import com.google.android.exoplayer2.ExoPlaybackException -import kotlinx.parcelize.IgnoredOnParcel +import com.google.android.exoplayer2.upstream.HttpDataSource +import com.google.android.exoplayer2.upstream.Loader import kotlinx.parcelize.Parcelize import org.schabi.newpipe.R import org.schabi.newpipe.extractor.Info +import org.schabi.newpipe.extractor.ServiceList +import org.schabi.newpipe.extractor.ServiceList.YouTube import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException +import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException import org.schabi.newpipe.extractor.exceptions.ExtractionException +import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException +import org.schabi.newpipe.extractor.exceptions.PaidContentException +import org.schabi.newpipe.extractor.exceptions.PrivateContentException +import org.schabi.newpipe.extractor.exceptions.ReCaptchaException +import org.schabi.newpipe.extractor.exceptions.SignInConfirmNotBotException +import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException +import org.schabi.newpipe.extractor.exceptions.UnsupportedContentInCountryException +import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException import org.schabi.newpipe.ktx.isNetworkRelated -import org.schabi.newpipe.util.ServiceHelper +import org.schabi.newpipe.player.mediasource.FailedMediaSource +import org.schabi.newpipe.player.resolver.PlaybackResolver +import java.net.UnknownHostException +/** + * An error has occurred in the app. This class contains plain old parcelable data that can be used + * to report the error and to show it to the user along with correct action buttons. + */ @Parcelize -class ErrorInfo( +class ErrorInfo private constructor( val stackTraces: Array, val userAction: UserAction, - val serviceName: String, val request: String, - val messageStringId: Int + val serviceId: Int?, + private val message: ErrorMessage, + /** + * If `true`, a report button will be shown for this error. Otherwise the error is not something + * that can really be reported (e.g. a network issue, or content not being available at all). + */ + val isReportable: Boolean, + /** + * If `true`, the process causing this error can be retried, otherwise not. + */ + val isRetryable: Boolean, + /** + * If present, indicates that the exception was a ReCaptchaException, and this is the URL + * provided by the service that can be used to solve the ReCaptcha challenge. + */ + val recaptchaUrl: String?, + /** + * If present, this resource can alternatively be opened in browser (useful if NewPipe is + * badly broken). + */ + val openInBrowserUrl: String?, ) : Parcelable { - // no need to store throwable, all data for report is in other variables - // also, the throwable might not be serializable, see TeamNewPipe/NewPipe#7302 - @IgnoredOnParcel - var throwable: Throwable? = null - - private constructor( + @JvmOverloads + constructor( throwable: Throwable, userAction: UserAction, - serviceName: String, - request: String + request: String, + serviceId: Int? = null, + openInBrowserUrl: String? = null, ) : this( throwableToStringList(throwable), userAction, - serviceName, request, - getMessageStringId(throwable, userAction) - ) { - this.throwable = throwable - } + serviceId, + getMessage(throwable, userAction, serviceId), + isReportable(throwable), + isRetryable(throwable), + (throwable as? ReCaptchaException)?.url, + openInBrowserUrl, + ) - private constructor( - throwable: List, + @JvmOverloads + constructor( + throwables: List, userAction: UserAction, - serviceName: String, - request: String + request: String, + serviceId: Int? = null, + openInBrowserUrl: String? = null, ) : this( - throwableListToStringList(throwable), + throwableListToStringList(throwables), userAction, - serviceName, request, - getMessageStringId(throwable.firstOrNull(), userAction) - ) { - this.throwable = throwable.firstOrNull() + serviceId, + getMessage(throwables.firstOrNull(), userAction, serviceId), + throwables.any(::isReportable), + throwables.isEmpty() || throwables.any(::isRetryable), + throwables.firstNotNullOfOrNull { it as? ReCaptchaException }?.url, + openInBrowserUrl, + ) + + // constructor to manually build ErrorInfo when no throwable is available + constructor( + stackTraces: Array, + userAction: UserAction, + request: String, + serviceId: Int?, + @StringRes message: Int + ) : + this( + stackTraces, userAction, request, serviceId, ErrorMessage(message), + true, false, null, null + ) + + // constructor with only one throwable to extract service id and openInBrowserUrl from an Info + constructor( + throwable: Throwable, + userAction: UserAction, + request: String, + info: Info?, + ) : + this(throwable, userAction, request, info?.serviceId, info?.url) + + // constructor with multiple throwables to extract service id and openInBrowserUrl from an Info + constructor( + throwables: List, + userAction: UserAction, + request: String, + info: Info?, + ) : + this(throwables, userAction, request, info?.serviceId, info?.url) + + fun getServiceName(): String { + return getServiceName(serviceId) } - // constructors with single throwable - constructor(throwable: Throwable, userAction: UserAction, request: String) : - this(throwable, userAction, SERVICE_NONE, request) - constructor(throwable: Throwable, userAction: UserAction, request: String, serviceId: Int) : - this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request) - constructor(throwable: Throwable, userAction: UserAction, request: String, info: Info?) : - this(throwable, userAction, getInfoServiceName(info), request) - - // constructors with list of throwables - constructor(throwable: List, userAction: UserAction, request: String) : - this(throwable, userAction, SERVICE_NONE, request) - constructor(throwable: List, userAction: UserAction, request: String, serviceId: Int) : - this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request) - constructor(throwable: List, userAction: UserAction, request: String, info: Info?) : - this(throwable, userAction, getInfoServiceName(info), request) + fun getMessage(context: Context): String { + return message.getString(context) + } companion object { - const val SERVICE_NONE = "none" + @Parcelize + class ErrorMessage( + @StringRes + private val stringRes: Int, + private vararg val formatArgs: String, + ) : Parcelable { + fun getString(context: Context): String { + return if (formatArgs.isEmpty()) { + // use ContextCompat.getString() just in case context is not AppCompatActivity + ContextCompat.getString(context, stringRes) + } else { + // ContextCompat.getString() with formatArgs does not exist, so we just + // replicate its source code but with formatArgs + ContextCompat.getContextForLanguage(context).getString(stringRes, *formatArgs) + } + } + } + + const val SERVICE_NONE = "" + + private fun getServiceName(serviceId: Int?) = + // not using getNameOfServiceById since we want to accept a nullable serviceId and we + // want to default to SERVICE_NONE + ServiceList.all()?.firstOrNull { it.serviceId == serviceId }?.serviceInfo?.name + ?: SERVICE_NONE fun throwableToStringList(throwable: Throwable) = arrayOf(throwable.stackTraceToString()) fun throwableListToStringList(throwableList: List) = throwableList.map { it.stackTraceToString() }.toTypedArray() - private fun getInfoServiceName(info: Info?) = - if (info == null) SERVICE_NONE else ServiceHelper.getNameOfServiceById(info.serviceId) - - @StringRes - private fun getMessageStringId( + fun getMessage( throwable: Throwable?, - action: UserAction - ): Int { + action: UserAction?, + serviceId: Int?, + ): ErrorMessage { return when { - throwable is AccountTerminatedException -> R.string.account_terminated - throwable is ContentNotAvailableException -> R.string.content_not_available - throwable != null && throwable.isNetworkRelated -> R.string.network_error - throwable is ContentNotSupportedException -> R.string.content_not_supported - throwable is ExtractionException -> R.string.parsing_error + // player exceptions + // some may be IOException, so do these checks before isNetworkRelated! throwable is ExoPlaybackException -> { - when (throwable.type) { - ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure - ExoPlaybackException.TYPE_UNEXPECTED -> R.string.player_recoverable_failure - else -> R.string.player_unrecoverable_failure + val cause = throwable.cause + when { + cause is HttpDataSource.InvalidResponseCodeException -> { + if (cause.responseCode == 403) { + if (serviceId == YouTube.serviceId) { + ErrorMessage(R.string.youtube_player_http_403) + } else { + ErrorMessage(R.string.player_http_403) + } + } else { + ErrorMessage(R.string.player_http_invalid_status, cause.responseCode.toString()) + } + } + cause is Loader.UnexpectedLoaderException && cause.cause is ExtractionException -> + getMessage(throwable, action, serviceId) + throwable.type == ExoPlaybackException.TYPE_SOURCE -> + ErrorMessage(R.string.player_stream_failure) + throwable.type == ExoPlaybackException.TYPE_UNEXPECTED -> + ErrorMessage(R.string.player_recoverable_failure) + else -> + ErrorMessage(R.string.player_unrecoverable_failure) } } - action == UserAction.UI_ERROR -> R.string.app_ui_crash - action == UserAction.REQUESTED_COMMENTS -> R.string.error_unable_to_load_comments - action == UserAction.SUBSCRIPTION_CHANGE -> R.string.subscription_change_failed - action == UserAction.SUBSCRIPTION_UPDATE -> R.string.subscription_update_failed - action == UserAction.LOAD_IMAGE -> R.string.could_not_load_thumbnails - action == UserAction.DOWNLOAD_OPEN_DIALOG -> R.string.could_not_setup_download_menu - else -> R.string.general_error + throwable is FailedMediaSource.FailedMediaSourceException -> + getMessage(throwable.cause, action, serviceId) + throwable is PlaybackResolver.ResolverException -> + ErrorMessage(R.string.player_stream_failure) + + // content not available exceptions + throwable is AccountTerminatedException -> + throwable.message + ?.takeIf { reason -> !reason.isEmpty() } + ?.let { reason -> + ErrorMessage( + R.string.account_terminated_service_provides_reason, + getServiceName(serviceId), + reason + ) + } + ?: ErrorMessage(R.string.account_terminated) + throwable is AgeRestrictedContentException -> + ErrorMessage(R.string.restricted_video_no_stream) + throwable is GeographicRestrictionException -> + ErrorMessage(R.string.georestricted_content) + throwable is PaidContentException -> + ErrorMessage(R.string.paid_content) + throwable is PrivateContentException -> + ErrorMessage(R.string.private_content) + throwable is SoundCloudGoPlusContentException -> + ErrorMessage(R.string.soundcloud_go_plus_content) + throwable is UnsupportedContentInCountryException -> + ErrorMessage(R.string.unsupported_content_in_country) + throwable is YoutubeMusicPremiumContentException -> + ErrorMessage(R.string.youtube_music_premium_content) + throwable is SignInConfirmNotBotException -> + ErrorMessage(R.string.sign_in_confirm_not_bot_error, getServiceName(serviceId)) + throwable is ContentNotAvailableException -> + ErrorMessage(R.string.content_not_available) + + // other extractor exceptions + throwable is ContentNotSupportedException -> + ErrorMessage(R.string.content_not_supported) + // ReCaptchas will be handled in a special way anyway + throwable is ReCaptchaException -> + ErrorMessage(R.string.recaptcha_request_toast) + // test this at the end as many exceptions could be a subclass of IOException + throwable != null && throwable.isNetworkRelated -> + ErrorMessage(R.string.network_error) + // an extraction exception unrelated to the network + // is likely an issue with parsing the website + throwable is ExtractionException -> + ErrorMessage(R.string.parsing_error) + + // user actions (in case the exception is null or unrecognizable) + action == UserAction.UI_ERROR -> + ErrorMessage(R.string.app_ui_crash) + action == UserAction.REQUESTED_COMMENTS -> + ErrorMessage(R.string.error_unable_to_load_comments) + action == UserAction.SUBSCRIPTION_CHANGE -> + ErrorMessage(R.string.subscription_change_failed) + action == UserAction.SUBSCRIPTION_UPDATE -> + ErrorMessage(R.string.subscription_update_failed) + action == UserAction.LOAD_IMAGE -> + ErrorMessage(R.string.could_not_load_thumbnails) + action == UserAction.DOWNLOAD_OPEN_DIALOG -> + ErrorMessage(R.string.could_not_setup_download_menu) + else -> + ErrorMessage(R.string.error_snackbar_message) + } + } + + fun isReportable(throwable: Throwable?): Boolean { + return when (throwable) { + // we don't have an exception, so this is a manually built error, which likely + // indicates that it's important and is thus reportable + null -> true + // the service explicitly said that content is not available (e.g. age restrictions, + // video deleted, etc.), there is no use in letting users report it + is ContentNotAvailableException -> false + // we know the content is not supported, no need to let the user report it + is ContentNotSupportedException -> false + // happens often when there is no internet connection; we don't use + // `throwable.isNetworkRelated` since any `IOException` would make that function + // return true, but not all `IOException`s are network related + is UnknownHostException -> false + // by default, this is an unexpected exception, which the user could report + else -> true + } + } + + fun isRetryable(throwable: Throwable?): Boolean { + return when (throwable) { + // we know the content is not available, retrying won't help + is ContentNotAvailableException -> false + // we know the content is not supported, retrying won't help + is ContentNotSupportedException -> false + // by default (including if throwable is null), enable retrying (though the retry + // button will be shown only if a way to perform the retry is implemented) + else -> true } } } diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt index 14ec41148..4ec5f58c3 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt @@ -2,7 +2,6 @@ package org.schabi.newpipe.error import android.content.Context import android.content.Intent -import android.util.Log import android.view.View import android.widget.Button import android.widget.TextView @@ -14,21 +13,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.disposables.Disposable import org.schabi.newpipe.MainActivity import org.schabi.newpipe.R -import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException -import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException -import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException -import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException -import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException -import org.schabi.newpipe.extractor.exceptions.PaidContentException -import org.schabi.newpipe.extractor.exceptions.PrivateContentException -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException -import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException -import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException -import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty import org.schabi.newpipe.ktx.animate -import org.schabi.newpipe.ktx.isInterruptedCaused -import org.schabi.newpipe.ktx.isNetworkRelated -import org.schabi.newpipe.util.ServiceHelper import org.schabi.newpipe.util.external_communication.ShareUtils import java.util.concurrent.TimeUnit @@ -78,64 +63,32 @@ class ErrorPanelHelper( } fun showError(errorInfo: ErrorInfo) { - - if (errorInfo.throwable != null && errorInfo.throwable!!.isInterruptedCaused) { - if (DEBUG) { - Log.w(TAG, "onError() isInterruptedCaused! = [$errorInfo.throwable]") - } - return - } - ensureDefaultVisibility() + errorTextView.text = errorInfo.getMessage(context) - if (errorInfo.throwable is ReCaptchaException) { - errorTextView.setText(R.string.recaptcha_request_toast) - - showAndSetErrorButtonAction( - R.string.recaptcha_solve - ) { + if (errorInfo.recaptchaUrl != null) { + showAndSetErrorButtonAction(R.string.recaptcha_solve) { // Starting ReCaptcha Challenge Activity val intent = Intent(context, ReCaptchaActivity::class.java) - intent.putExtra( - ReCaptchaActivity.RECAPTCHA_URL_EXTRA, - (errorInfo.throwable as ReCaptchaException).url - ) + intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.recaptchaUrl) fragment.startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST) errorActionButton.setOnClickListener(null) } - - errorRetryButton.isVisible = retryShouldBeShown - showAndSetOpenInBrowserButtonAction(errorInfo) - } else if (errorInfo.throwable is AccountTerminatedException) { - errorTextView.setText(R.string.account_terminated) - - if (!isNullOrEmpty((errorInfo.throwable as AccountTerminatedException).message)) { - errorServiceInfoTextView.text = context.resources.getString( - R.string.service_provides_reason, - ServiceHelper.getSelectedService(context)?.serviceInfo?.name ?: "" - ) - errorServiceInfoTextView.isVisible = true - - errorServiceExplanationTextView.text = - (errorInfo.throwable as AccountTerminatedException).message - errorServiceExplanationTextView.isVisible = true - } - } else { - showAndSetErrorButtonAction( - R.string.error_snackbar_action - ) { + } else if (errorInfo.isReportable) { + showAndSetErrorButtonAction(R.string.error_snackbar_action) { ErrorUtil.openActivity(context, errorInfo) } + } - errorTextView.setText(getExceptionDescription(errorInfo.throwable)) + if (errorInfo.isRetryable) { + errorRetryButton.isVisible = retryShouldBeShown + } - if (errorInfo.throwable !is ContentNotAvailableException && - errorInfo.throwable !is ContentNotSupportedException - ) { - // show retry button only for content which is not unavailable or unsupported - errorRetryButton.isVisible = retryShouldBeShown + if (errorInfo.openInBrowserUrl != null) { + errorOpenInBrowserButton.isVisible = true + errorOpenInBrowserButton.setOnClickListener { + ShareUtils.openUrlInBrowser(context, errorInfo.openInBrowserUrl) } - showAndSetOpenInBrowserButtonAction(errorInfo) } setRootVisible() @@ -153,15 +106,6 @@ class ErrorPanelHelper( errorActionButton.setOnClickListener(listener) } - fun showAndSetOpenInBrowserButtonAction( - errorInfo: ErrorInfo - ) { - errorOpenInBrowserButton.isVisible = true - errorOpenInBrowserButton.setOnClickListener { - ShareUtils.openUrlInBrowser(context, errorInfo.request) - } - } - fun showTextError(errorString: String) { ensureDefaultVisibility() @@ -192,27 +136,5 @@ class ErrorPanelHelper( companion object { val TAG: String = ErrorPanelHelper::class.simpleName!! val DEBUG: Boolean = MainActivity.DEBUG - - @StringRes - fun getExceptionDescription(throwable: Throwable?): Int { - return when (throwable) { - is AgeRestrictedContentException -> R.string.restricted_video_no_stream - is GeographicRestrictionException -> R.string.georestricted_content - is PaidContentException -> R.string.paid_content - is PrivateContentException -> R.string.private_content - is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content - is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content - is ContentNotAvailableException -> R.string.content_not_available - is ContentNotSupportedException -> R.string.content_not_supported - else -> { - // show retry button only for content which is not unavailable or unsupported - if (throwable != null && throwable.isNetworkRelated) { - R.string.network_error - } else { - R.string.error_snackbar_message - } - } - } - } } } diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt index e74711b88..b358a5fd2 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt @@ -122,7 +122,7 @@ class ErrorUtil { ) .setSmallIcon(R.drawable.ic_bug_report) .setContentTitle(context.getString(R.string.error_report_notification_title)) - .setContentText(context.getString(errorInfo.messageStringId)) + .setContentText(errorInfo.getMessage(context)) .setAutoCancel(true) .setContentIntent( PendingIntentCompat.getActivity( @@ -156,10 +156,10 @@ class ErrorUtil { // fallback to showing a notification if no root view is available createNotification(context, errorInfo) } else { - Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG) + Snackbar.make(rootView, errorInfo.getMessage(context), Snackbar.LENGTH_LONG) .setActionTextColor(Color.YELLOW) .setAction(context.getString(R.string.error_snackbar_action).uppercase()) { - openActivity(context, errorInfo) + context.startActivity(getErrorActivityIntent(context, errorInfo)) }.show() } } diff --git a/app/src/main/java/org/schabi/newpipe/error/UserAction.java b/app/src/main/java/org/schabi/newpipe/error/UserAction.java index afb880a29..d3af9d32e 100644 --- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java @@ -33,7 +33,9 @@ public enum UserAction { SHARE_TO_NEWPIPE("share to newpipe"), CHECK_FOR_NEW_APP_VERSION("check for new app version"), OPEN_INFO_ITEM_DIALOG("open info item dialog"), - GETTING_MAIN_SCREEN_TAB("getting main screen tab"); + GETTING_MAIN_SCREEN_TAB("getting main screen tab"), + PLAY_ON_POPUP("play on popup"), + SUBSCRIPTIONS("loading subscriptions"); private final String message; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt index e75acedc4..cff37dd5a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt @@ -775,7 +775,7 @@ class VideoDetailFragment : }, { throwable -> showError( - ErrorInfo(throwable, UserAction.REQUESTED_STREAM, url ?: "no url", serviceId) + ErrorInfo(throwable, UserAction.REQUESTED_STREAM, url ?: "no url", serviceId, url) ) } ) @@ -1465,7 +1465,7 @@ class VideoDetailFragment : if (!info.errors.isEmpty()) { showSnackBarError( - ErrorInfo(info.errors, UserAction.REQUESTED_STREAM, info.url, info) + ErrorInfo(info.errors, UserAction.REQUESTED_STREAM, "Some info not extracted: " + info.url, info) ) } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index 7f594734a..848dfe6f5 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -153,7 +153,7 @@ public abstract class BaseListInfoFragment showError(new ErrorInfo(throwable, errorUserAction, - "Start loading: " + url, serviceId))); + "Start loading: " + url, serviceId, url))); } /** @@ -184,7 +184,7 @@ public abstract class BaseListInfoFragment dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable, - errorUserAction, "Loading more items: " + url, serviceId))); + errorUserAction, "Loading more items: " + url, serviceId, url))); } private void forbidDownwardFocusScroll() { @@ -210,7 +210,7 @@ public abstract class BaseListInfoFragment protected void initViews(final View rootView, final Bundle savedInstanceState) { super.initViews(rootView, savedInstanceState); - EmptyStateUtil.setEmptyStateComposable( - binding.emptyStateView, - EmptyStateSpec.Companion.getContentNotSupported() - ); + setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.ContentNotSupported); tabAdapter = new TabAdapter(getChildFragmentManager()); binding.viewPager.setAdapter(tabAdapter); @@ -583,7 +580,7 @@ public class ChannelFragment extends BaseStateFragment isLoading.set(false); handleResult(result); }, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL, - url == null ? "No URL" : url, serviceId))); + url == null ? "No URL" : url, serviceId, url))); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 06f9e30c6..af1da9602 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -3,6 +3,7 @@ package org.schabi.newpipe.fragments.list.search; import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; import static org.schabi.newpipe.extractor.utils.Utils.isBlank; import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable; import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; import static java.util.Arrays.asList; @@ -54,6 +55,7 @@ import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchInfo; import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory; @@ -65,7 +67,6 @@ import org.schabi.newpipe.ktx.ExceptionUtils; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.ui.emptystate.EmptyStateSpec; -import org.schabi.newpipe.ui.emptystate.EmptyStateUtil; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ExtractorHelper; @@ -356,9 +357,7 @@ public class SearchFragment extends BaseListFragment result) { showListFooter(false); infoListAdapter.addInfoItemList(result.getItems()); - nextPage = result.getNextPage(); - if (!result.getErrors().isEmpty() && nextPage != null) { - showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED, - "\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", " - + "pageIds: " + nextPage.getIds() + ", " - + "pageCookies: " + nextPage.getCookies(), - serviceId)); + if (!result.getErrors().isEmpty()) { + // nextPage should be non-null at this point, because it refers to the page + // whose results are handled here, but let's check it anyway + if (nextPage == null) { + showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED, + "\"" + searchString + "\" → nextPage == null", serviceId, + getOpenInBrowserUrlForErrors())); + } else { + showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED, + "\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", " + + "pageIds: " + nextPage.getIds() + ", " + + "pageCookies: " + nextPage.getCookies(), + serviceId, getOpenInBrowserUrlForErrors())); + } } + + // keep the reassignment of nextPage after the error handling to ensure that nextPage + // still holds the correct value during the error handling + nextPage = result.getNextPage(); super.handleNextItems(result); } diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 4f60e36ae..499625332 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -15,6 +15,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.compose.ui.platform.ComposeView; import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; @@ -125,10 +126,8 @@ public final class BookmarkFragment extends BaseLocalListFragment= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0 + setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification, serviceType)) } companion object { diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java index aee7c0003..fbadbb876 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java @@ -85,8 +85,8 @@ public class SubscriptionsImportFragment extends BaseFragment { if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) { ErrorUtil.showSnackbar(activity, new ErrorInfo(new String[]{}, UserAction.SUBSCRIPTION_IMPORT_EXPORT, - ServiceHelper.getNameOfServiceById(currentServiceId), "Service does not support importing subscriptions", + currentServiceId, R.string.general_error)); activity.finish(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index b09593c17..4d6647d12 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -1283,7 +1283,8 @@ public final class Player implements PlaybackListener, Listener { UserAction.PLAY_STREAM, "Loading failed for [" + currentMetadata.getTitle() + "]: " + currentMetadata.getStreamUrl(), - currentMetadata.getServiceId()); + currentMetadata.getServiceId(), + currentMetadata.getStreamUrl()); ErrorUtil.createNotification(context, errorInfo); } @@ -1499,7 +1500,7 @@ public final class Player implements PlaybackListener, Listener { errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM, "Player error[type=" + error.getErrorCodeName() + "] occurred while playing " + currentMetadata.getStreamUrl(), - currentMetadata.getServiceId()); + currentMetadata.getServiceId(), currentMetadata.getStreamUrl()); } ErrorUtil.createNotification(context, errorInfo); } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index a110a80d6..266d65f36 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -33,11 +33,9 @@ import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode; import com.google.android.exoplayer2.ui.CaptionStyleCompat; -import com.google.android.exoplayer2.util.MimeTypes; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.SubtitlesStream; @@ -47,13 +45,14 @@ import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.util.ListHelper; +import org.schabi.newpipe.util.Localization; import java.lang.annotation.Retention; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; -import java.util.Formatter; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -62,11 +61,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; public final class PlayerHelper { - private static final StringBuilder STRING_BUILDER = new StringBuilder(); - private static final Formatter STRING_FORMATTER = - new Formatter(STRING_BUILDER, Locale.getDefault()); - private static final NumberFormat SPEED_FORMATTER = new DecimalFormat("0.##x"); - private static final NumberFormat PITCH_FORMATTER = new DecimalFormat("##%"); + private static final FormattersProvider FORMATTERS_PROVIDER = new FormattersProvider(); @Retention(SOURCE) @IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI, @@ -89,9 +84,11 @@ public final class PlayerHelper { private PlayerHelper() { } - //////////////////////////////////////////////////////////////////////////// - // Exposed helpers - //////////////////////////////////////////////////////////////////////////// + // region Exposed helpers + + public static void resetFormat() { + FORMATTERS_PROVIDER.reset(); + } @NonNull public static String getTimeString(final int milliSeconds) { @@ -100,35 +97,24 @@ public final class PlayerHelper { final int hours = (milliSeconds % 86400000) / 3600000; final int days = (milliSeconds % (86400000 * 7)) / 86400000; - STRING_BUILDER.setLength(0); - return (days > 0 - ? STRING_FORMATTER.format("%d:%02d:%02d:%02d", days, hours, minutes, seconds) - : hours > 0 - ? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds) - : STRING_FORMATTER.format("%02d:%02d", minutes, seconds) - ).toString(); + final Formatters formatters = FORMATTERS_PROVIDER.formatters(); + if (days > 0) { + return formatters.stringFormat("%d:%02d:%02d:%02d", days, hours, minutes, seconds); + } + + return hours > 0 + ? formatters.stringFormat("%d:%02d:%02d", hours, minutes, seconds) + : formatters.stringFormat("%02d:%02d", minutes, seconds); } @NonNull public static String formatSpeed(final double speed) { - return SPEED_FORMATTER.format(speed); + return FORMATTERS_PROVIDER.formatters().speed().format(speed); } @NonNull public static String formatPitch(final double pitch) { - return PITCH_FORMATTER.format(pitch); - } - - @NonNull - public static String subtitleMimeTypesOf(@NonNull final MediaFormat format) { - switch (format) { - case VTT: - return MimeTypes.TEXT_VTT; - case TTML: - return MimeTypes.APPLICATION_TTML; - default: - throw new IllegalArgumentException("Unrecognized mime type: " + format.name()); - } + return FORMATTERS_PROVIDER.formatters().pitch().format(pitch); } @NonNull @@ -219,9 +205,8 @@ public final class PlayerHelper { ? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0)); } - //////////////////////////////////////////////////////////////////////////// - // Settings Resolution - //////////////////////////////////////////////////////////////////////////// + // endregion + // region Resolution public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) { return getPreferences(context) @@ -405,9 +390,8 @@ public final class PlayerHelper { return Integer.parseInt(preferredIntervalBytes) * 1024; } - //////////////////////////////////////////////////////////////////////////// - // Private helpers - //////////////////////////////////////////////////////////////////////////// + // endregion + // region Private helpers @NonNull private static SharedPreferences getPreferences(@NonNull final Context context) { @@ -427,9 +411,8 @@ public final class PlayerHelper { } - //////////////////////////////////////////////////////////////////////////// - // Utils used by player - //////////////////////////////////////////////////////////////////////////// + // endregion + // region Utils used by player @RepeatMode public static int nextRepeatMode(@RepeatMode final int repeatMode) { @@ -503,4 +486,43 @@ public final class PlayerHelper { player.getContext().getString(R.string.seek_duration_key), player.getContext().getString(R.string.seek_duration_default_value)))); } + + // endregion + // region Format + + static class FormattersProvider { + private Formatters formatters; + + public Formatters formatters() { + if (formatters == null) { + formatters = Formatters.create(); + } + return formatters; + } + + public void reset() { + formatters = null; + } + } + + record Formatters( + Locale locale, + NumberFormat speed, + NumberFormat pitch) { + + static Formatters create() { + final Locale locale = Localization.getAppLocale(); + final DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale); + return new Formatters( + locale, + new DecimalFormat("0.##x", dfs), + new DecimalFormat("##%", dfs)); + } + + String stringFormat(final String format, final Object... args) { + return String.format(locale, format, args); + } + } + + // endregion } diff --git a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt index 2948eeaf8..4815965a3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt +++ b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt @@ -17,6 +17,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers import org.schabi.newpipe.MainActivity import org.schabi.newpipe.NewPipeDatabase import org.schabi.newpipe.R +import org.schabi.newpipe.error.ErrorInfo import org.schabi.newpipe.extractor.InfoItem.InfoType import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler @@ -84,7 +85,7 @@ class MediaBrowserPlaybackPreparer( }, { throwable -> Log.e(TAG, "Failed to start playback of media ID [$mediaId]", throwable) - onPrepareError() + onPrepareError(throwable) } ) } @@ -115,9 +116,9 @@ class MediaBrowserPlaybackPreparer( ) } - private fun onPrepareError() { + private fun onPrepareError(throwable: Throwable) { setMediaSessionError.accept( - ContextCompat.getString(context, R.string.error_snackbar_message), + ErrorInfo.getMessage(throwable, null, null).getString(context), PlaybackStateCompat.ERROR_CODE_APP_ERROR ) } diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java index cc3889973..0894d22be 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java @@ -167,19 +167,17 @@ public final class NotificationUtil { && notificationBuilder.mActions.get(2).actionIntent != null); } - public void createNotificationAndStartForeground() { if (notificationBuilder == null) { notificationBuilder = createNotification(); } updateNotification(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - player.getService().startForeground(NOTIFICATION_ID, notificationBuilder.build(), - ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); - } else { - player.getService().startForeground(NOTIFICATION_ID, notificationBuilder.build()); - } + // ServiceInfo constants are not used below Android Q, so 0 is set here + final int serviceType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + ? ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK : 0; + ServiceCompat.startForeground(player.getService(), NOTIFICATION_ID, + notificationBuilder.build(), serviceType); } public void cancelNotificationAndStopForeground() { diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java index 031f0d36c..e794ace72 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -57,9 +57,7 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment { @Override public void onCreatePreferences(@Nullable final Bundle savedInstanceState, @Nullable final String rootKey) { - final var dbDir = requireContext().getDatabasePath(BackupFileLocator.FILE_NAME_DB).toPath() - .getParent(); - manager = new ImportExportManager(new BackupFileLocator(dbDir)); + manager = new ImportExportManager(new BackupFileLocator(requireContext())); importExportDataPathKey = getString(R.string.import_export_data_path); diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 690634d0c..7e1f22776 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -16,6 +16,7 @@ import androidx.preference.Preference; import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.image.ImageStrategy; import org.schabi.newpipe.util.image.PreferredImageQuality; @@ -106,5 +107,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment { NewPipe.setupLocalization( Localization.getPreferredLocalization(context), Localization.getPreferredContentCountry(context)); + PlayerHelper.resetFormat(); } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index 9fe5240cc..5daa3ad82 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -13,6 +13,7 @@ import androidx.preference.PreferenceManager; import org.schabi.newpipe.App; import org.schabi.newpipe.R; +import org.schabi.newpipe.settings.migration.MigrationManager; import org.schabi.newpipe.util.DeviceUtils; import java.io.File; @@ -46,7 +47,7 @@ public final class NewPipeSettings { public static void initSettings(final Context context) { // first run migrations, then setDefaultValues, since the latter requires the correct types - SettingMigrations.runMigrationsIfNeeded(context); + MigrationManager.runMigrationsIfNeeded(context); // readAgain is true so that if new settings are added their default value is set PreferenceManager.setDefaultValues(context, R.xml.main_settings, true); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java index cbd6b0656..2bc5f5396 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -95,8 +95,7 @@ public class SelectChannelFragment extends DialogFragment { progressBar = v.findViewById(R.id.progressBar); emptyView = v.findViewById(R.id.empty_state_view); - EmptyStateUtil.setEmptyStateComposable(emptyView, - EmptyStateSpec.Companion.getNoSubscriptions()); + EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoSubscriptions); progressBar.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); emptyView.setVisibility(View.GONE); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java index 6227d95a9..cb2252b86 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -65,8 +65,7 @@ public class SelectPlaylistFragment extends DialogFragment { recyclerView = v.findViewById(R.id.items_list); emptyView = v.findViewById(R.id.empty_state_view); - EmptyStateUtil.setEmptyStateComposable(emptyView, - EmptyStateSpec.Companion.getNoBookmarkedPlaylist()); + EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoBookmarkedPlaylist); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); final SelectPlaylistAdapter playlistAdapter = new SelectPlaylistAdapter(); recyclerView.setAdapter(playlistAdapter); diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt b/app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt index 0ce2d5f4d..38227e10e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt @@ -1,12 +1,13 @@ package org.schabi.newpipe.settings.export +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(homeDir: Path) { +class BackupFileLocator(context: Context) { companion object { const val FILE_NAME_DB = "newpipe.db" @Deprecated( @@ -17,9 +18,8 @@ class BackupFileLocator(homeDir: Path) { const val FILE_NAME_JSON_PREFS = "preferences.json" } - val dbDir = homeDir / "databases" - val db = homeDir / FILE_NAME_DB - val dbJournal = homeDir / "$FILE_NAME_DB-journal" - val dbShm = dbDir / "$FILE_NAME_DB-shm" - val dbWal = 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") } diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 6b0eb7eb9..cbf860d2c 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -12,7 +12,7 @@ import java.io.FileNotFoundException import java.io.IOException import java.io.ObjectOutputStream import java.util.zip.ZipOutputStream -import kotlin.io.path.createDirectories +import kotlin.io.path.createParentDirectories import kotlin.io.path.deleteIfExists class ImportExportManager(private val fileLocator: BackupFileLocator) { @@ -63,7 +63,7 @@ class ImportExportManager(private val fileLocator: BackupFileLocator) { */ @Throws(IOException::class) fun ensureDbDirectoryExists() { - fileLocator.dbDir.createDirectories() + fileLocator.db.createParentDirectories() } /** diff --git a/app/src/main/java/org/schabi/newpipe/settings/migration/MigrationManager.java b/app/src/main/java/org/schabi/newpipe/settings/migration/MigrationManager.java new file mode 100644 index 000000000..d5b0e783d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/migration/MigrationManager.java @@ -0,0 +1,103 @@ +package org.schabi.newpipe.settings.migration; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.util.Consumer; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.error.ErrorUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * MigrationManager is responsible for running migrations and showing the user information about + * the migrations that were applied. + */ +public final class MigrationManager { + + private static final String TAG = MigrationManager.class.getSimpleName(); + /** + * List of UI actions that are performed after the UI is initialized (e.g. showing alert + * dialogs) to inform the user about changes that were applied by migrations. + */ + private static final List> MIGRATION_INFO = new ArrayList<>(); + + private MigrationManager() { + // MigrationManager is a utility class that is completely static + } + + /** + * Run all migrations that are needed for the current version of NewPipe. + * This method should be called at the start of the application, before any other operations + * that depend on the settings. + * + * @param context Context that can be used to run migrations + */ + public static void runMigrationsIfNeeded(@NonNull final Context context) { + SettingMigrations.runMigrationsIfNeeded(context); + } + + /** + * Perform UI actions informing about migrations that took place if they are present. + * @param context Context that can be used to show dialogs/snackbars/toasts + */ + public static void showUserInfoIfPresent(@NonNull final Context context) { + if (MIGRATION_INFO.isEmpty()) { + return; + } + + try { + MIGRATION_INFO.get(0).accept(context); + } catch (final Exception e) { + ErrorUtil.showUiErrorSnackbar(context, "Showing migration info to the user", e); + // Remove the migration that caused the error and continue with the next one + MIGRATION_INFO.remove(0); + showUserInfoIfPresent(context); + } + } + + /** + * Add a migration info action that will be executed after the UI is initialized. + * This can be used to show dialogs/snackbars/toasts to inform the user about changes that + * were applied by migrations. + * + * @param info the action to be executed + */ + public static void addMigrationInfo(final Consumer info) { + MIGRATION_INFO.add(info); + } + + /** + * This method should be called when the user dismisses the migration info + * to check if there are any more migration info actions to be shown. + * @param context Context that can be used to show dialogs/snackbars/toasts + */ + public static void onMigrationInfoDismissed(@NonNull final Context context) { + MIGRATION_INFO.remove(0); + showUserInfoIfPresent(context); + } + + /** + * Creates a dialog to inform the user about the migration. + * @param uiContext Context that can be used to show dialogs/snackbars/toasts + * @param title the title of the dialog + * @param message the message of the dialog + * @return the dialog that can be shown to the user with a custom dismiss listener + */ + static AlertDialog createMigrationInfoDialog(@NonNull final Context uiContext, + @NonNull final String title, + @NonNull final String message) { + return new AlertDialog.Builder(uiContext) + .setTitle(title) + .setMessage(message) + .setPositiveButton(R.string.ok, null) + .setOnDismissListener(dialog -> + MigrationManager.onMigrationInfoDismissed(uiContext)) + .setCancelable(false) // prevents the dialog from being dismissed accidentally + .create(); + } + +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java similarity index 75% rename from app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java rename to app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java index 2134c7649..92520ec7e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java @@ -1,11 +1,14 @@ -package org.schabi.newpipe.settings; +package org.schabi.newpipe.settings.migration; + +import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; +import static org.schabi.newpipe.extractor.ServiceList.YouTube; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.core.util.Consumer; import androidx.preference.PreferenceManager; @@ -18,34 +21,34 @@ import org.schabi.newpipe.settings.tabs.Tab; import org.schabi.newpipe.settings.tabs.TabsManager; import org.schabi.newpipe.util.DeviceUtils; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; - /** - * In order to add a migration, follow these steps, given P is the previous version:
- * - in the class body add a new {@code MIGRATION_P_P+1 = new Migration(P, P+1) { ... }} and put in - * the {@code migrate()} method the code that need to be run when migrating from P to P+1
- * - add {@code MIGRATION_P_P+1} at the end of {@link SettingMigrations#SETTING_MIGRATIONS}
- * - increment {@link SettingMigrations#VERSION}'s value by 1 (so it should become P+1) + * This class contains the code to migrate the settings from one version to another. + * Migrations are run automatically when the app is started and the settings version changed. + *
+ * In order to add a migration, follow these steps, given {@code P} is the previous version: + *
    + *
  • in the class body add a new {@code MIGRATION_P_P+1 = new Migration(P, P+1) { ... }} and put + * in the {@code migrate()} method the code that need to be run + * when migrating from {@code P} to {@code P+1}
  • + *
  • add {@code MIGRATION_P_P+1} at the end of {@link SettingMigrations#SETTING_MIGRATIONS}
  • + *
  • increment {@link SettingMigrations#VERSION}'s value by 1 + * (so it becomes {@code P+1})
  • + *
+ * Migrations can register UI actions using {@link MigrationManager#addMigrationInfo(Consumer)} + * that will be performed after the UI is initialized to inform the user about changes + * that were applied by migrations. */ public final class SettingMigrations { private static final String TAG = SettingMigrations.class.toString(); private static SharedPreferences sp; - /** - * List of UI actions that are performed after the UI is initialized (e.g. showing alert - * dialogs) to inform the user about changes that were applied by migrations. - */ - private static final List> MIGRATION_INFO = new ArrayList<>(); - private static final Migration MIGRATION_0_1 = new Migration(0, 1) { @Override public void migrate(@NonNull final Context context) { @@ -172,13 +175,48 @@ public final class SettingMigrations { if (tabs.size() != cleanedTabs.size()) { tabsManager.saveTabs(cleanedTabs); // create an AlertDialog to inform the user about the change - MIGRATION_INFO.add((Context uiContext) -> new AlertDialog.Builder(uiContext) - .setTitle(R.string.migration_info_6_7_title) - .setMessage(R.string.migration_info_6_7_message) - .setPositiveButton(R.string.ok, null) - .setCancelable(false) - .create() - .show()); + MigrationManager.addMigrationInfo(uiContext -> + MigrationManager.createMigrationInfoDialog( + uiContext, + uiContext.getString(R.string.migration_info_6_7_title), + uiContext.getString(R.string.migration_info_6_7_message)) + .show()); + } + } + }; + + private static final Migration MIGRATION_7_8 = new Migration(7, 8) { + @Override + protected void migrate(@NonNull final Context context) { + // YouTube remove the combined Trending kiosk, see + // https://github.com/TeamNewPipe/NewPipe/discussions/12445 for more information. + // If the user has a dedicated YouTube/Trending kiosk tab, + // it is removed and replaced with the new live kiosk tab. + // The default trending kiosk tab is not touched + // because it uses the default kiosk provided by the extractor + // and is thus updated automatically. + final TabsManager tabsManager = TabsManager.getManager(context); + final List tabs = tabsManager.getTabs(); + final List cleanedTabs = tabs.stream() + .filter(tab -> !(tab instanceof Tab.KioskTab kioskTab + && kioskTab.getKioskServiceId() == YouTube.getServiceId() + && kioskTab.getKioskId().equals("Trending"))) + .collect(Collectors.toUnmodifiableList()); + if (tabs.size() != cleanedTabs.size()) { + tabsManager.saveTabs(cleanedTabs); + } + + final boolean hasDefaultTrendingTab = tabs.stream() + .anyMatch(tab -> tab instanceof Tab.DefaultKioskTab); + + if (tabs.size() != cleanedTabs.size() || hasDefaultTrendingTab) { + // User is informed about the change + MigrationManager.addMigrationInfo(uiContext -> + MigrationManager.createMigrationInfoDialog( + uiContext, + uiContext.getString(R.string.migration_info_7_8_title), + uiContext.getString(R.string.migration_info_7_8_message)) + .show()); } } }; @@ -196,16 +234,17 @@ public final class SettingMigrations { MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, - MIGRATION_6_7 + MIGRATION_6_7, + MIGRATION_7_8, }; /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - private static final int VERSION = 7; + private static final int VERSION = 8; - public static void runMigrationsIfNeeded(@NonNull final Context context) { + static void runMigrationsIfNeeded(@NonNull final Context context) { // setup migrations and check if there is something to do sp = PreferenceManager.getDefaultSharedPreferences(context); final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); @@ -249,21 +288,6 @@ public final class SettingMigrations { sp.edit().putInt(lastPrefVersionKey, currentVersion).apply(); } - /** - * Perform UI actions informing about migrations that took place if they are present. - * @param context Context that can be used to show dialogs/snackbars/toasts - */ - public static void showUserInfoIfPresent(@NonNull final Context context) { - for (final Consumer consumer : MIGRATION_INFO) { - try { - consumer.accept(context); - } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(context, "Showing migration info to the user", e); - } - } - MIGRATION_INFO.clear(); - } - private SettingMigrations() { } abstract static class Migration { diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java index f667bb900..91621f50e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.settings.preferencesearch; +import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable; + import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -12,7 +14,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding; import org.schabi.newpipe.ui.emptystate.EmptyStateSpec; -import org.schabi.newpipe.ui.emptystate.EmptyStateUtil; import java.util.List; @@ -41,9 +42,7 @@ public class PreferenceSearchFragment extends Fragment { binding = SettingsPreferencesearchFragmentBinding.inflate(inflater, container, false); binding.searchResults.setLayoutManager(new LinearLayoutManager(getContext())); - EmptyStateUtil.setEmptyStateComposable( - binding.emptyStateView, - EmptyStateSpec.Companion.getNoSearchMaxSizeResult()); + setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.NoSearchResult); adapter = new PreferenceSearchAdapter(); adapter.setOnItemClickListener(this::onItemClicked); diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt b/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt index 3bba5dba9..dbb10b9a8 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt @@ -1,12 +1,14 @@ package org.schabi.newpipe.ui.components.about import androidx.annotation.StringRes +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.rememberScrollState @@ -16,7 +18,6 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.NonRestartableComposable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -26,13 +27,12 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp -import androidx.core.content.ContextCompat.getDrawable -import coil3.compose.AsyncImage import my.nanihadesuka.compose.ColumnScrollbar import org.schabi.newpipe.BuildConfig import org.schabi.newpipe.R import org.schabi.newpipe.ui.components.common.defaultThemedScrollbarSettings import org.schabi.newpipe.util.external_communication.ShareUtils +import org.schabi.newpipe.util.image.NewPipeSquircleIcon private val ABOUT_ITEMS = listOf( AboutData(R.string.faq_title, R.string.faq_description, R.string.faq, R.string.faq_url), @@ -83,12 +83,10 @@ fun AboutTab() { .wrapContentSize(Alignment.Center), horizontalAlignment = Alignment.CenterHorizontally ) { - // note: the preview - val context = LocalContext.current - val launcherDrawable = remember { getDrawable(context, R.mipmap.ic_launcher) } - AsyncImage( - model = launcherDrawable, + Image( + imageVector = NewPipeSquircleIcon, contentDescription = stringResource(R.string.app_name), + modifier = Modifier.size(64.dp), ) Spacer(Modifier.height(4.dp)) Text( diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt index bbe4eab4b..8d0646a1d 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt @@ -19,7 +19,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.pluralStringResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import androidx.compose.ui.unit.dp @@ -122,28 +121,23 @@ private fun CommentRepliesDialog( if (comments.itemCount == 0) { item { - val refresh = comments.loadState.refresh - if (refresh is LoadState.Loading) { - LoadingIndicator(modifier = Modifier.padding(top = 8.dp)) - } else if (refresh is LoadState.Error) { - // TODO use error panel instead - EmptyStateComposable( - spec = EmptyStateSpec.DisabledComments.copy( - descriptionText = { - stringResource(R.string.error_unable_to_load_comments) + when (val refresh = comments.loadState.refresh) { + is LoadState.Loading -> { + LoadingIndicator(modifier = Modifier.padding(top = 8.dp)) + } + else -> { + // TODO use error panel instead + EmptyStateComposable( + spec = if (refresh is LoadState.Error) { + EmptyStateSpec.ErrorLoadingComments + } else { + EmptyStateSpec.NoComments }, - ), - modifier = Modifier - .fillMaxWidth() - .heightIn(min = 128.dp) - ) - } else { - EmptyStateComposable( - spec = EmptyStateSpec.NoComments, - modifier = Modifier - .fillMaxWidth() - .heightIn(min = 128.dp) - ) + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 128.dp) + ) + } } } } else { diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt index 3a3fae480..f9c44b80c 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt @@ -81,12 +81,11 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, onCommentAuthorOpened: () -> style = MaterialTheme.typography.titleSmall, ) - Text( - text = Localization.relativeTimeOrTextual( - context, comment.uploadDate, comment.textualUploadDate - ), - style = MaterialTheme.typography.bodySmall, - ) + Localization.relativeTimeOrTextual( + context, comment.uploadDate, comment.textualUploadDate + )?.let { + Text(text = it, style = MaterialTheme.typography.bodySmall) + } } } diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt index b2c9f6b85..a33ffc0ca 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.pluralStringResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -75,7 +74,6 @@ private fun CommentSection( modifier = Modifier .fillMaxWidth() .heightIn(min = 128.dp) - ) } } else if (count == 0) { @@ -111,13 +109,7 @@ private fun CommentSection( is LoadState.Error -> { item { // TODO use error panel instead - EmptyStateComposable( - EmptyStateSpec.DisabledComments.copy( - descriptionText = { - stringResource(R.string.error_unable_to_load_comments) - } - ) - ) + EmptyStateComposable(EmptyStateSpec.ErrorLoadingComments) } } @@ -134,11 +126,7 @@ private fun CommentSection( item { // TODO use error panel instead EmptyStateComposable( - spec = EmptyStateSpec.DisabledComments.copy( - descriptionText = { - stringResource(R.string.error_unable_to_load_comments) - } - ), + spec = EmptyStateSpec.ErrorLoadingComments, modifier = Modifier .fillMaxWidth() .heightIn(min = 128.dp) diff --git a/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt b/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt index 3917c4203..77fa02082 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt @@ -1,6 +1,7 @@ package org.schabi.newpipe.ui.emptystate import android.graphics.Color +import androidx.annotation.StringRes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -22,25 +23,14 @@ import org.schabi.newpipe.ui.theme.AppTheme fun EmptyStateComposable( spec: EmptyStateSpec, modifier: Modifier = Modifier, -) = EmptyStateComposable( - emojiText = spec.emojiText(), - descriptionText = spec.descriptionText(), - modifier = modifier -) - -@Composable -private fun EmptyStateComposable( - emojiText: String, - descriptionText: String, - modifier: Modifier = Modifier, ) { Column( modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { Text( - text = emojiText, + text = spec.emojiText, style = MaterialTheme.typography.titleLarge, textAlign = TextAlign.Center, ) @@ -49,7 +39,7 @@ private fun EmptyStateComposable( modifier = Modifier .padding(top = 6.dp) .padding(horizontal = 16.dp), - text = descriptionText, + text = stringResource(spec.descriptionText), style = MaterialTheme.typography.bodyMedium, textAlign = TextAlign.Center, ) @@ -82,66 +72,48 @@ fun EmptyStateComposableNoCommentPreview() { } } -data class EmptyStateSpec( - val emojiText: @Composable () -> String, - val descriptionText: @Composable () -> String, +enum class EmptyStateSpec( + val emojiText: String, + @field:StringRes val descriptionText: Int, ) { - companion object { - - val GenericError = - EmptyStateSpec( - emojiText = { "¯\\_(ツ)_/¯" }, - descriptionText = { stringResource(id = R.string.empty_list_subtitle) }, - ) - - val NoVideos = - EmptyStateSpec( - emojiText = { "(╯°-°)╯" }, - descriptionText = { stringResource(id = R.string.no_videos) }, - ) - - val NoComments = - EmptyStateSpec( - - emojiText = { "¯\\_(╹x╹)_/¯" }, - descriptionText = { stringResource(id = R.string.no_comments) }, - ) - - val DisabledComments = - NoComments.copy( - descriptionText = { stringResource(id = R.string.comments_are_disabled) }, - ) - - val NoSearchResult = - NoComments.copy( - emojiText = { "╰(°●°╰)" }, - descriptionText = { stringResource(id = R.string.search_no_results) } - ) - - val NoSearchMaxSizeResult = - NoSearchResult - - val ContentNotSupported = - NoComments.copy( - emojiText = { "(︶︹︺)" }, - descriptionText = { stringResource(id = R.string.content_not_supported) }, - ) - - val NoBookmarkedPlaylist = - EmptyStateSpec( - emojiText = { "(╥﹏╥)" }, - descriptionText = { stringResource(id = R.string.no_playlist_bookmarked_yet) }, - ) - - val NoSubscriptionsHint = - EmptyStateSpec( - emojiText = { "(꩜ᯅ꩜)" }, - descriptionText = { stringResource(id = R.string.import_subscriptions_hint) }, - ) - - val NoSubscriptions = - NoSubscriptionsHint.copy( - descriptionText = { stringResource(id = R.string.no_channel_subscribed_yet) }, - ) - } + GenericError( + emojiText = "¯\\_(ツ)_/¯", + descriptionText = R.string.empty_list_subtitle, + ), + NoVideos( + emojiText = "(╯°-°)╯", + descriptionText = R.string.no_videos, + ), + NoComments( + emojiText = "¯\\_(╹x╹)_/¯", + descriptionText = R.string.no_comments, + ), + DisabledComments( + emojiText = "¯\\_(╹x╹)_/¯", + descriptionText = R.string.comments_are_disabled, + ), + ErrorLoadingComments( + emojiText = "¯\\_(╹x╹)_/¯", + descriptionText = R.string.error_unable_to_load_comments, + ), + NoSearchResult( + emojiText = "╰(°●°╰)", + descriptionText = R.string.search_no_results, + ), + ContentNotSupported( + emojiText = "(︶︹︺)", + descriptionText = R.string.content_not_supported, + ), + NoBookmarkedPlaylist( + emojiText = "(╥﹏╥)", + descriptionText = R.string.no_playlist_bookmarked_yet, + ), + NoSubscriptionsHint( + emojiText = "(꩜ᯅ꩜)", + descriptionText = R.string.import_subscriptions_hint, + ), + NoSubscriptions( + emojiText = "(꩜ᯅ꩜)", + descriptionText = R.string.no_channel_subscribed_yet, + ), } diff --git a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java index b8c2ff236..5aa332159 100644 --- a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java +++ b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java @@ -52,6 +52,14 @@ public final class KioskTranslator { return c.getString(R.string.featured); case "Radio": return c.getString(R.string.radio); + case "trending_gaming": + return c.getString(R.string.trending_gaming); + case "trending_music": + return c.getString(R.string.trending_music); + case "trending_movies_and_shows": + return c.getString(R.string.trending_movies); + case "trending_podcasts_episodes": + return c.getString(R.string.trending_podcasts); default: return kioskId; } @@ -77,6 +85,14 @@ public final class KioskTranslator { return R.drawable.ic_stars; case "Radio": return R.drawable.ic_radio; + case "trending_gaming": + return R.drawable.ic_videogame_asset; + case "trending_music": + return R.drawable.ic_music_note; + case "trending_movies_and_shows": + return R.drawable.ic_movie; + case "trending_podcasts_episodes": + return R.drawable.ic_podcasts; default: return 0; } diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index d938fd286..890981e90 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -127,14 +127,13 @@ public final class Localization { } public static String localizeNumber(final double number) { - final NumberFormat nf = NumberFormat.getInstance(getAppLocale()); - return nf.format(number); + return NumberFormat.getInstance(getAppLocale()).format(number); } public static String formatDate(@NonNull final OffsetDateTime offsetDateTime) { return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) - .withLocale(getAppLocale()).format(offsetDateTime - .atZoneSameInstant(ZoneId.systemDefault())); + .withLocale(getAppLocale()) + .format(offsetDateTime.atZoneSameInstant(ZoneId.systemDefault())); } @SuppressLint("StringFormatInvalid") @@ -191,14 +190,20 @@ public final class Localization { final double value = (double) count; if (count >= 1000000000) { - return localizeNumber(round(value / 1000000000)) - + context.getString(R.string.short_billion); + final double shortenedValue = value / 1000000000; + final int scale = shortenedValue >= 100 ? 0 : 1; + return context.getString(R.string.short_billion, + localizeNumber(round(shortenedValue, scale))); } else if (count >= 1000000) { - return localizeNumber(round(value / 1000000)) - + context.getString(R.string.short_million); + final double shortenedValue = value / 1000000; + final int scale = shortenedValue >= 100 ? 0 : 1; + return context.getString(R.string.short_million, + localizeNumber(round(shortenedValue, scale))); } else if (count >= 1000) { - return localizeNumber(round(value / 1000)) - + context.getString(R.string.short_thousand); + final double shortenedValue = value / 1000; + final int scale = shortenedValue >= 100 ? 0 : 1; + return context.getString(R.string.short_thousand, + localizeNumber(round(shortenedValue, scale))); } else { return localizeNumber(value); } @@ -384,9 +389,10 @@ public final class Localization { * {@code parsed != null} and the relevant setting is enabled, {@code textual} will * be appended to the returned string for debugging purposes. */ + @Nullable public static String relativeTimeOrTextual(@Nullable final Context context, @Nullable final DateWrapper parsed, - final String textual) { + @Nullable final String textual) { if (parsed == null) { return textual; } else if (DEBUG && context != null && PreferenceManager @@ -411,8 +417,8 @@ public final class Localization { } } - private static double round(final double value) { - return new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue(); + private static double round(final double value, final int scale) { + return new BigDecimal(value).setScale(scale, RoundingMode.HALF_UP).doubleValue(); } private static String getQuantity(@NonNull final Context context, diff --git a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java index 55193599e..2785afab0 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java @@ -9,9 +9,11 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.provider.Settings; +import android.text.Html; import android.widget.Toast; import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; @@ -113,14 +115,47 @@ public final class PermissionHelper { @RequiresApi(api = Build.VERSION_CODES.M) public static boolean checkSystemAlertWindowPermission(final Context context) { if (!Settings.canDrawOverlays(context)) { - final Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - Uri.parse("package:" + context.getPackageName())); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - context.startActivity(i); - } catch (final ActivityNotFoundException ignored) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + final Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + context.getPackageName())); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + context.startActivity(i); + } catch (final ActivityNotFoundException ignored) { + } + return false; + // from Android R the ACTION_MANAGE_OVERLAY_PERMISSION will only point to the menu, + // so let’s add a dialog that points the user to the right setting. + } else { + final String appName = context.getApplicationInfo() + .loadLabel(context.getPackageManager()).toString(); + final String title = context.getString(R.string.permission_display_over_apps); + final String permissionName = + context.getString(R.string.permission_display_over_apps_permission_name); + final String appNameItalic = "" + appName + ""; + final String permissionNameItalic = "" + permissionName + ""; + final String message = + context.getString(R.string.permission_display_over_apps_message, + appNameItalic, + permissionNameItalic + ); + new AlertDialog.Builder(context) + .setTitle(title) + .setMessage(Html.fromHtml(message, Html.FROM_HTML_MODE_COMPACT)) + .setPositiveButton("OK", (dialog, which) -> { + // we don’t need the package name here, since it won’t do anything on >R + final Intent intent = + new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); + try { + context.startActivity(intent); + } catch (final ActivityNotFoundException ignored) { + } + }) + .setCancelable(true) + .show(); + return false; } - return false; + } else { return true; } diff --git a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java index 6e9ea7a47..05f26f178 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java +++ b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java @@ -121,7 +121,7 @@ public final class SparseItemUtil { callback.accept(result); }, throwable -> ErrorUtil.createNotification(context, new ErrorInfo(throwable, UserAction.REQUESTED_STREAM, - "Loading stream info: " + url, serviceId) + "Loading stream info: " + url, serviceId, url) )); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/image/NewPipeSquircleIcon.kt b/app/src/main/java/org/schabi/newpipe/util/image/NewPipeSquircleIcon.kt new file mode 100644 index 000000000..a4ff131a1 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/image/NewPipeSquircleIcon.kt @@ -0,0 +1,99 @@ +package org.schabi.newpipe.util.image + +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +/** + * Generated with https://github.com/rafaeltonholo/svg-to-compose/ + * based on assets/newpipe_squircle.svg. + */ +val NewPipeSquircleIcon: ImageVector + get() { + val current = _newPipeIcon + if (current != null) return current + + return ImageVector.Builder( + name = "org.schabi.newpipe.ui.theme.AppTheme.NewPipeSquircleIcon", + defaultWidth = 100.0.dp, + defaultHeight = 100.0.dp, + viewportWidth = 100.0f, + viewportHeight = 100.0f, + ).apply { + // M0 50 C0 15 15 0 50 0 s50 15 50 50 -15 50 -50 50 S0 85 0 50 + path( + fill = SolidColor(Color(0xFFCD201F)), + ) { + // M 0 50 + moveTo(x = 0.0f, y = 50.0f) + // C 0 15 15 0 50 0 + curveTo( + x1 = 0.0f, + y1 = 15.0f, + x2 = 15.0f, + y2 = 0.0f, + x3 = 50.0f, + y3 = 0.0f, + ) + // s 50 15 50 50 + reflectiveCurveToRelative( + dx1 = 50.0f, + dy1 = 15.0f, + dx2 = 50.0f, + dy2 = 50.0f, + ) + // s -15 50 -50 50 + reflectiveCurveToRelative( + dx1 = -15.0f, + dy1 = 50.0f, + dx2 = -50.0f, + dy2 = 50.0f, + ) + // S 0 85 0 50 + reflectiveCurveTo( + x1 = 0.0f, + y1 = 85.0f, + x2 = 0.0f, + y2 = 50.0f, + ) + } + // M31.7 19.2 v61.7 l9.7 -5.73 V36 l23.8 14 -17.6 10.35 V71.5 L84 50 + path( + fill = SolidColor(Color(0xFFFFFFFF)), + ) { + // M 31.7 19.2 + moveTo(x = 31.7f, y = 19.2f) + // v 61.7 + verticalLineToRelative(dy = 61.7f) + // l 9.7 -5.73 + lineToRelative(dx = 9.7f, dy = -5.73f) + // V 36 + verticalLineTo(y = 36.0f) + // l 23.8 14 + lineToRelative(dx = 23.8f, dy = 14.0f) + // l -17.6 10.35 + lineToRelative(dx = -17.6f, dy = 10.35f) + // V 71.5 + verticalLineTo(y = 71.5f) + // L 84 50 + lineTo(x = 84.0f, y = 50.0f) + } + }.build().also { _newPipeIcon = it } + } + +@Preview +@Composable +private fun IconPreview() { + Image( + imageVector = NewPipeSquircleIcon, + contentDescription = null, + ) +} + +@Suppress("ObjectPropertyName") +private var _newPipeIcon: ImageVector? = null diff --git a/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java b/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java index 066515d6b..2e4aa320f 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java @@ -1,14 +1,13 @@ package org.schabi.newpipe.util.text; import android.content.Context; -import android.util.Log; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import org.schabi.newpipe.MainActivity; -import org.schabi.newpipe.R; -import org.schabi.newpipe.error.ErrorPanelHelper; +import org.schabi.newpipe.error.ErrorInfo; +import org.schabi.newpipe.error.ErrorUtil; +import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -158,19 +157,13 @@ public final class InternalUrlsHandler { disposables.add(single.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(info -> { - final PlayQueue playQueue = - new SinglePlayQueue(info, seconds * 1000L); + final PlayQueue playQueue = new SinglePlayQueue(info, seconds * 1000L); NavigationHelper.playOnPopupPlayer(context, playQueue, false); }, throwable -> { - if (DEBUG) { - Log.e(TAG, "Could not play on popup: " + url, throwable); - } - new AlertDialog.Builder(context) - .setTitle(R.string.player_stream_failure) - .setMessage( - ErrorPanelHelper.Companion.getExceptionDescription(throwable)) - .setPositiveButton(R.string.ok, null) - .show(); + // This will only show a snackbar if the passed context has a root view: + // otherwise it will resort to showing a notification, so we are safe here. + ErrorUtil.showSnackbar(context, + new ErrorInfo(throwable, UserAction.PLAY_ON_POPUP, url, null, url)); })); return true; } diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManager.java b/app/src/main/java/us/shandian/giga/service/DownloadManager.java index 9b90fa14b..d02f77bc1 100644 --- a/app/src/main/java/us/shandian/giga/service/DownloadManager.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManager.java @@ -265,7 +265,7 @@ public class DownloadManager { } } - public void deleteMission(Mission mission) { + public void deleteMission(Mission mission, boolean alsoDeleteFile) { synchronized (this) { if (mission instanceof DownloadMission) { mMissionsPending.remove(mission); @@ -274,7 +274,9 @@ public class DownloadManager { mFinishedMissionStore.deleteMission(mission); } - mission.delete(); + if (alsoDeleteFile) { + mission.delete(); + } } } diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index 9722a9a1f..f245b3dd9 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -563,16 +563,16 @@ public class MissionAdapter extends Adapter implements Handler.Callb } request.append("]"); - String service; + Integer service; try { - service = NewPipe.getServiceByUrl(mission.source).getServiceInfo().getName(); + service = NewPipe.getServiceByUrl(mission.source).getServiceId(); } catch (Exception e) { - service = ErrorInfo.SERVICE_NONE; + service = null; } ErrorUtil.createNotification(mContext, new ErrorInfo(ErrorInfo.Companion.throwableToStringList(mission.errObject), action, - service, request.toString(), reason)); + request.toString(), service, reason)); } public void clearFinishedDownloads(boolean delete) { @@ -614,7 +614,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb while (i.hasNext()) { Mission mission = i.next(); if (mission != null) { - mDownloadManager.deleteMission(mission); + mDownloadManager.deleteMission(mission, true); mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mission.storage.getUri())); } i.remove(); @@ -667,7 +667,14 @@ public class MissionAdapter extends Adapter implements Handler.Callb shareFile(h.item.mission); return true; case R.id.delete: - mDeleter.append(h.item.mission); + // delete the entry and the file + mDeleter.append(h.item.mission, true); + applyChanges(); + checkMasterButtonsVisibility(); + return true; + case R.id.delete_entry: + // just delete the entry + mDeleter.append(h.item.mission, false); applyChanges(); checkMasterButtonsVisibility(); return true; @@ -676,7 +683,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb final StoredFileHelper storage = h.item.mission.storage; if (!storage.existsAsFile()) { Toast.makeText(mContext, R.string.missing_file, Toast.LENGTH_SHORT).show(); - mDeleter.append(h.item.mission); + mDeleter.append(h.item.mission, true); applyChanges(); return true; } diff --git a/app/src/main/java/us/shandian/giga/ui/common/Deleter.java b/app/src/main/java/us/shandian/giga/ui/common/Deleter.java index 1902076d6..0f285fd74 100644 --- a/app/src/main/java/us/shandian/giga/ui/common/Deleter.java +++ b/app/src/main/java/us/shandian/giga/ui/common/Deleter.java @@ -13,7 +13,9 @@ import com.google.android.material.snackbar.Snackbar; import org.schabi.newpipe.R; import java.util.ArrayList; +import java.util.Optional; +import kotlin.Pair; import us.shandian.giga.get.FinishedMission; import us.shandian.giga.get.Mission; import us.shandian.giga.service.DownloadManager; @@ -30,7 +32,8 @@ public class Deleter { private static final int DELAY_RESUME = 400;// ms private Snackbar snackbar; - private ArrayList items; + // list of missions to be deleted, and whether to also delete the corresponding file + private ArrayList> items; private boolean running = true; private final Context mContext; @@ -51,7 +54,7 @@ public class Deleter { items = new ArrayList<>(2); } - public void append(Mission item) { + public void append(Mission item, boolean alsoDeleteFile) { /* If a mission is removed from the list while the Snackbar for a previously * removed item is still showing, commit the action for the previous item * immediately. This prevents Snackbars from stacking up in reverse order. @@ -60,13 +63,13 @@ public class Deleter { commit(); mIterator.hide(item); - items.add(0, item); + items.add(0, new Pair<>(item, alsoDeleteFile)); show(); } private void forget() { - mIterator.unHide(items.remove(0)); + mIterator.unHide(items.remove(0).getFirst()); mAdapter.applyChanges(); show(); @@ -84,7 +87,19 @@ public class Deleter { private void next() { if (items.size() < 1) return; - String msg = mContext.getString(R.string.file_deleted).concat(":\n").concat(items.get(0).storage.getName()); + final Optional fileToBeDeleted = items.stream() + .filter(Pair::getSecond) + .map(p -> p.getFirst().storage.getName()) + .findFirst(); + + String msg; + if (fileToBeDeleted.isPresent()) { + msg = mContext.getString(R.string.file_deleted) + .concat(":\n") + .concat(fileToBeDeleted.get()); + } else { + msg = mContext.getString(R.string.entry_deleted); + } snackbar = Snackbar.make(mView, msg, Snackbar.LENGTH_INDEFINITE); snackbar.setAction(R.string.undo, s -> forget()); @@ -98,11 +113,13 @@ public class Deleter { if (items.size() < 1) return; while (items.size() > 0) { - Mission mission = items.remove(0); + Pair missionAndAlsoDeleteFile = items.remove(0); + Mission mission = missionAndAlsoDeleteFile.getFirst(); + boolean alsoDeleteFile = missionAndAlsoDeleteFile.getSecond(); if (mission.deleted) continue; mIterator.unHide(mission); - mDownloadManager.deleteMission(mission); + mDownloadManager.deleteMission(mission, alsoDeleteFile); if (mission instanceof FinishedMission) { mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mission.storage.getUri())); @@ -137,7 +154,11 @@ public class Deleter { pause(); - for (Mission mission : items) mDownloadManager.deleteMission(mission); + for (Pair missionAndAlsoDeleteFile : items) { + Mission mission = missionAndAlsoDeleteFile.getFirst(); + boolean alsoDeleteFile = missionAndAlsoDeleteFile.getSecond(); + mDownloadManager.deleteMission(mission, alsoDeleteFile); + } items = null; } } diff --git a/app/src/main/res/drawable/ic_podcasts.xml b/app/src/main/res/drawable/ic_podcasts.xml new file mode 100644 index 000000000..c297e8fd3 --- /dev/null +++ b/app/src/main/res/drawable/ic_podcasts.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/menu/drawer_items.xml b/app/src/main/res/menu/drawer_items.xml index 24c321b8a..8214f3f02 100644 --- a/app/src/main/res/menu/drawer_items.xml +++ b/app/src/main/res/menu/drawer_items.xml @@ -2,5 +2,6 @@ + diff --git a/app/src/main/res/menu/mission.xml b/app/src/main/res/menu/mission.xml index 4273c1ed6..6566252e8 100644 --- a/app/src/main/res/menu/mission.xml +++ b/app/src/main/res/menu/mission.xml @@ -27,7 +27,11 @@ + android:title="@string/delete_file" /> + + الملفات المحملة لا يوجد مثل هذا الملف/مصدر المحتوى الأكثر إعجابًا - بليون تعذر تحميل موجز \'%s\'. ؟ التحقق من وجود تحديثات مثيلات خوادم پيرتيوب +100 فيديو - ألف مثيل الخادم موجود بالفعل طلب تأكيد قبل مسح قائمة الانتظار المشتركون @@ -642,13 +640,11 @@ الصوت : %s خطوة حل - %s يقدم هذا السبب: الدفق المحدد غير مدعوم من قبل المشغلون الخارجيون عن تطبيق نيوپايپ تسريع إلى الأمام/-ترجيع وقت البحث تم رفضها من قبل النظام ليس هناك تعليقات - مليون جاري التحقق من وجود تحديثات… المحتوى اسأل عن مكان التنزيل diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 0b6a70315..e6a0e4342 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -116,9 +116,6 @@ لا شيء هنا سوى الصراصير الصوت إعادة المحاولة - ألف - مليون - بليون ليس هناك مشترِكون %s مشارك @@ -652,7 +649,6 @@ تمكين تحديد نص في الوصف يمكنك الآن تحديد نص داخل الوصف. لاحظ أن الصفحة قد تومض وقد لا تكون الروابط قابلة للنقر أثناء وضع التحديد. فتح الموقع - %s يقدم هذا السبب: تم إنهاء الحساب لا يوفر وضع التغذية السريعة مزيدًا من المعلومات حول هذا الموضوع. حساب منشئ المحتوى قد تم إنهائه. @@ -883,4 +879,9 @@ صفحة مجموعة القناة حدد مجموعة المحتوى لم تنشئ مجموعة محتوى + الإعجابات + البحث %1$s + البحث %1$s (%2$s) + تمت إزالة صفحة أفضل 50 من SoundCloud + أوقفت SoundCloud صفحة أفضل 50 الأصلية. تمت إزالة علامة التبويب المقابلة من صفحتك الرئيسية. diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 256b8a54a..5cb89c160 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -297,9 +297,6 @@ Bəyən Bəyənmə Yenidən sıralamaq üçün sürüklə - min - Mln - Mlrd Xidməti dəyiş, hazırda seçilmiş: Abunəçi yoxdur Baxış yoxdur @@ -502,7 +499,6 @@ Endirmə növbəsini məhdudlaşdır Eyni vaxtda ancaq bir endirmə həyata keçiriləcək Hesab ləğv edildi - %s bu səbəbi təmin edir: Yükləmə başladı Açıqlamadakı mətni seçməyi qeyri-aktiv et Kateqoriya diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml index 51b4fdec0..81b212f80 100644 --- a/app/src/main/res/values-b+ast/strings.xml +++ b/app/src/main/res/values-b+ast/strings.xml @@ -43,9 +43,6 @@ Tarrezmes Formatu de videu predetermináu Prietu - mil - mill. - mil mill. Precísase esti permisu p\'abrir \nnel mou ventanu Retu de reCAPTCHA diff --git a/app/src/main/res/values-b+uz+Latn/strings.xml b/app/src/main/res/values-b+uz+Latn/strings.xml index b556da756..2be37ea7c 100644 --- a/app/src/main/res/values-b+uz+Latn/strings.xml +++ b/app/src/main/res/values-b+uz+Latn/strings.xml @@ -267,9 +267,6 @@ Obunachilar yo\'q Hozirda tanlangan xizmatni yoqish: - B - M - k Qayta Audio Video diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 22f963def..72b08414e 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -55,7 +55,7 @@ Памятаць апошнія памер і пазіцыю ўсплывальнага акна Хуткі пошук пазіцыі Недакладны пошук дазваляе плэеру знаходзіць пазіцыі хутчэй са зніжанай дакладнасцю. Пошук цягам 5, 15 ці 25 секунд пры гэтым немажлівы - Кэш малюнкаў ачышчаны + Кэш відарысаў ачышчаны Ачысціць кэш метаданых Выдаліць усе даныя вэб-старонак у кэшы Кэш метаданых ачышчаны @@ -71,8 +71,8 @@ Працягваць прайграванне пасля перапынкаў (напрыклад, тэлефонных званкоў) Спампаваць «Наступнае» і «Прапанаванае» відэа - Паказваць падказку «Зацісніце, каб дадаць» - Паказваць падказку пры націсканні «У акне» або «У фоне» на старонцы звестак аб відэа + Паказваць падказку «Утрымлівайце, каб дадаць у чаргу» + Паказваць падказку пры націсканні кнопкі «У акне» або «У фоне» на старонцы відэа URL не падтрымліваецца Прадвызначаная краіна кантэнту Прадвызначаная мова кантэнту @@ -137,7 +137,7 @@ Такой папкі не існуе Такога файла або крыніцы кантэнту не існуе Файл не існуе або няма дазволу на яго чытанне ці запіс - Імя файла не можа быць пустым + Назва файла не можа быць пустой Адбылася памылка: %1$s Няма трансляцый, даступных для спампоўвання Прабачце, гэта не павінна было адбыцца. @@ -154,14 +154,11 @@ Спадабалася Не спадабалася Няма вынікаў - Нічога няма, акрамя цвыркуноў + Нічога няма, хоць сабак ганяй Перацягніце, каб змяніць парадак Відэа Аўдыя Паспрабаваць зноў - тыс. - млн - млрд Няма падпісчыкаў %s падпісчык @@ -191,7 +188,7 @@ Адхіліць Перайменаваць ОК - Імя файла + Назва файла Патокі Памылка NewPipe спампоўвае @@ -204,7 +201,7 @@ Запыт reCAPTCHA Запытаны ўвод reCAPTCHA Спампоўванне - Дапушчальныя ў назвах файлаў сімвалы + Сімвалы, дапушчальныя ў назвах файлаў Недапушчальныя сімвалы замяняюцца на гэты Сімвал для замены Літары і лічбы @@ -253,7 +250,7 @@ Выдаліць Падрабязнасці Налады аўдыя - Зацісніце, каб дадаць у чаргу + Утрымлівайце, каб дадаць у чаргу Пачаць прайграванне ў фоне Пачаць прайграванне ў акне Адкрыць бакавую панэль @@ -268,7 +265,7 @@ Загрузка запытанага кантэнту Стварыць плэй-ліст Перайменаваць - Імя + Назва Дадаць у плэй-ліст Зрабіць мініяцюрай плэй-ліста Дадаць плэй-ліст у закладкі @@ -327,7 +324,7 @@ Апавяшчэнні пра новыя версіі NewPipe Знешняе сховішча недаступна Спампоўванне на знешнюю SD-карту немагчыма. Скінуць размяшчэнне папкі спампоўвання? - Памылка чытання захаваных укладак. Выкарыстоўваюцца ўкладкі па змаўчанні + Не ўдалося прачытаць захаваныя ўкладкі, таму выкарыстоўваюцца прадвызначаныя Аднавіць прадвызначаныя значэнні Аднавіць прадвызначаныя значэнні? Колькасць падпісчыкаў недаступна @@ -347,9 +344,9 @@ Дадаць у чаргу Дзеянне забаронена сістэмай Памылка спампоўвання - Стварыць унікальнае імя + Стварыць унікальную назву Перазапісаць - Файл з такім імем ужо спампоўваецца + Файл з такой назвай ўжо спампоўваецца Паказаць тэкст памылкі Немагчыма стварыць папку прызначэння Немагчыма стварыць файл @@ -376,14 +373,14 @@ Працягваць прайграванне Аднаўляць апошнюю пазіцыю Пазіцыі ў спісах - Адлюстроўваць індыкатары пазіцый прагляду ў спісах + Паказваць у спісах пазіцыю прайгравання Ачыстка даных Пазіцыі прайгравання выдалены Файл перамешчаны або выдалены - Файл з такім імем ужо існуе - Файл з такім імем ужо існуе + Файл з такой назвай ужо існуе + Спампаваны файл з такой назвай ужо існуе немагчыма перазапісаць файл - Файл з такім імем ужо дададзены ў чаргу спампоўвання + Файл з такой назвай ужо ў чарзе спампоўвання Праграма NewPipe была закрыта падчас працы з файлам На прыладзе скончылася вольнае месца Прагрэс страчаны, бо файл быў выдалены @@ -542,11 +539,11 @@ Апавяшчэнні пра ход відэахэшавання Стварыць паведамленне пра памылку Выберыце падпіскі - Імпарт ці экспарт падпісак з 3-кропкавага меню + Імпартуйце або экспартуйце падпіскі праз меню з трыма кропкамі ⁝ Забарона вылучэння тэксту ў апісанні Хуткі рэжым Калі ў вас узніклі праблемы з выкарыстаннем праграмы, абавязкова азнаёмцеся з адказамі на частыя пытанні! - Адключыць тунэляванне медыя + Адключыць тунэляванне мультымедыя Мініяцюра з перадпраглядам у паласе перамотвання Высокая якасць (больш) Не паказваць @@ -607,15 +604,15 @@ Дублікат дададзены %d раз(ы) LeakCanary недаступны Паказаць уцечкі памяці - Адключыце мультымедыйнае тунэляванне, калі ў вас з\'яўляецца чорны экран або заіканне падчас прайгравання відэа. + Адключыце тунэляванне мультымедыя, калі відэа прайграецца перарывіста або паказваецца чорны экран. Не ўдалося скапіяваць у буфер абмену Папка спампоўвання яшчэ не зададзена, выберыце папку спампоўвання цяпер Частыя пытанні Перайсці на вэб-сайт - Правядзіце пальцам па элементах, каб выдаліць іх + Каб выдаліць элемент, змахніце яго ўбок Прыбраць пастаянную мініяцюру - Паказваць індыкатары выяў - Паказваць каляровыя стужкі Пікаса на выявах, якія пазначаюць іх крыніцу: чырвоная для сеткі, сіняя для дыска і зялёная для памяці + Паказваць на відарысах указальнікі + Паказваць на відарысах каляровыя меткі Picasso, якія абазначаюць яго крыніцу: чырвоная — сетка, сіняя — дыск, зялёная — памяць Апрацоўка стужкі… Пры кожным спампоўванні вам будзе прапанавана выбраць месца захавання Загрузка канала… @@ -641,10 +638,10 @@ %d выбраных %d выбраных - Пустая назва групы - Выдаліць гэту групу? + Назва групы пустая + Выдаліць групу? Новая - Паказаць толькі разгрупаваныя падпіскі + Паказваць толькі не згрупаваныя падпіскі Запланаваныя Паказваць «Збой плэера» Запусціце праверку новых патокаў @@ -707,7 +704,6 @@ Гэта змесціва з\'яўляецца прыватным, таму NewPipe не можа яго трансляваць або спампоўваць. Гэта відэа даступна толькі для падпісчыкаў YouTube Music Premium, таму NewPipe не можа яго трансляваць або спампоўваць. Уліковы запіс спынены - %s дае наступную прычыну: Вартае ўвагі Унутраная Прагледжаныя цалкам @@ -715,7 +711,7 @@ Даступна для некаторых сэрвісаў, звычайна значна хутчэй, але можа перадаваць абмежаваную колькасць элементаў і не ўсю інфармацыю (можа адсутнічаць працягласць, тып элемента, паказчык трансляцыі) Узроставае абмежаванне Для гэтага дзеяння не знойдзены прыдатны файлавы менеджар. \nУсталюйце файлавы менеджар, сумяшчальны з Storage Access Framework - Ніякая праграма на вашай прыладзе не можа адкрыць гэта + На прыладзе няма праграмы, каб адкрыць гэты файл Стандартнае значэнне ExoPlayer Прагледжаныя часткова Лічыце, што загрузка каналаў адбываецца занадта павольна? Калі так, паспрабуйце ўключыць хуткую загрузку (можна змяніць у наладах або націснуўшы кнопку ніжэй). \n \nNewPipe прапануе два спосабы загрузкі каналаў: \n• Атрыманне ўсяго канала падпіскі. Павольны, але інфармацыя поўная). \n• Выкарыстанне спецыяльнай канчатковай кропкі абслугоўвання. Хуткі, але звычайна інфармацыя няпоўная). \n \nРозніца паміж імі ў тым, што ў хуткім звычайна адсутнічае частка інфармацыі, напрыклад, працягласць або тып (немагчыма адрозніць трансляцыі ад звычайных відэа), і ён можа вяртаць менш элементаў. \n \nYouTube з\'яўляецца прыкладам сэрвісу, які прапануе гэты хуткі метад праз RSS-канал. \n \nТакім чынам, выбар залежыць ад таго, чаму вы аддаяце перавагу: хуткасці або дакладнасці інфармацыя. @@ -747,8 +743,8 @@ У гэтым патоку ўжо павінна быць гукавая дарожка Уключыце гэту опцыю, калі ў вас ёсць праблемы з ініцыялізацыяй дэкодэра, якая вяртаецца да дэкодэраў з больш нізкім прыярытэтам, калі ініцыялізацыя асноўных дэкодэраў не ўдаецца. Гэта можа прывесці да нізкай прадукцыйнасці прайгравання, чым пры выкарыстанні асноўных дэкодэраў Кіраванне некаторымі наладамі ExoPlayer. Каб гэтыя змены ўступілі ў сілу, патрабуецца перазапуск прайгравальніка - Гэты абыходны шлях вызваляе і паўторна стварае відэакодэкі, калі адбываецца змяненне паверхні, замест таго, каб зажаваць паверхню непасрэдна для кодэка. Ужо выкарыстоўваецца ExoPlayer на некаторых прыладах з такой праблемай, гэты параметр ужываецца толькі на прыладах з Android 6 і вышэй\n\nУключэнне параметра можа прадухіліць памылкі прайгравання пры пераключэнні бягучага відэаплэера або пераключэнні ў поўнаэкранны рэжым - Якасць выяў + Гэты абыходны шлях вызваляе і паўторна стварае відэакодэкі, калі адбываецца змяненне паверхні, замест таго, каб задаваць паверхню непасрэдна для кодэка. Ужо выкарыстоўваецца ExoPlayer на некаторых прыладах з такой праблемай, гэты параметр ужываецца толькі на прыладах з Android 6 і вышэй\n\nУключэнне параметра можа прадухіліць памылкі прайгравання пры пераключэнні бягучага відэаплэера або пераключэнні ў поўнаэкранны рэжым + Якасць відарысаў Відэа \? Падпісчыкі @@ -767,12 +763,12 @@ Атрыманне ўкладак канала Аватары Наступны паток - Прадвызначана на вашай прыладзе адключана медыятунэляванне, бо гэтая мадэль прылады яго не падтрымлівае. + Прадвызначана на вашай прыладзе адключана тунэляванне мультымедыя, бо вядома, што гэта мадэль яго не падтрымлівае. Аватары падканалаў Адкрыць чаргу прайгравання - Не загружаць выявы + Не загружаць відарысы Высокая якасць - Аб канале + Пра канал Абагуліць плэй-ліст Пераматаць наперад Альбомы @@ -786,9 +782,9 @@ - %1$s: %2$s Перамясціць панэль укладак уніз Няма жывых трансляцый - Выберыце якасць выяў і ці трэба спампоўваць выявы ўвогуле, каб паменшыць выкарыстанне даных і памяці. Змены ачышчаюць кэш выяў як у памяці, так і на дыску - %s + Выберыце якасць відарысаў ці ўвогуле не загружаць відарысы, каб паменшыць выкарыстанне даных і памяці. Змены ачышчаюць кэш відарысаў у памяці і на дыску (%s) Прайграць - Іншыя опцыі + Іншыя параметры Мініяцюры Трэкі Працягласць @@ -807,7 +803,7 @@ Каб адрэдагаваць кожнае з дзеянняў у апавяшчэнні, націсніце на яго. Першыя тры дзеянні (прайграванне/паўза, папярэдні і наступны) зададзены сістэмай, іх змяніць немагчыма. Недастаткова вольнага месца на прыладзе Так - NewPipe можа аўтаматычна правяраць наяўнасць абнаўленняў і паведаміць вам, калі яны будуць даступны. \nУключыць гэту функцыю? + NewPipe можа час ад часу аўтаматычна правяраць наяўнасць новай версіі і апавяшчаць, калі яна будзе даступна. \nУключыць гэту функцыю? Налады ў імпартаваным экспарце выкарыстоўваюць уразлівы фармат, які састарэў з версіі NewPipe 0.27.0. Пераканайцеся, што імпартаваны экспарт атрыманы з надзейнай крыніцы, і ў будучыні пераважней выкарыстоўваць толькі экспарт, атрыманы з NewPipe 0.27.0 ці навей. Падтрымка імпарту налад у гэтым уразлівым фармаце хутка будзе цалкам выдаленая, і тады старыя версіі NewPipe больш не змогуць імпартаваць наладкі з экспарту з новых версій. Не Рэзервовае капіяванне і аднаўленне @@ -820,4 +816,7 @@ Выберыце групу каналаў Група каналаў яшчэ не створана Старонка групы каналаў + Пошук %1$s + Пошук %1$s (%2$s) + Спадабалася diff --git a/app/src/main/res/values-ber/strings.xml b/app/src/main/res/values-ber/strings.xml index 3912381fc..39247f49b 100644 --- a/app/src/main/res/values-ber/strings.xml +++ b/app/src/main/res/values-ber/strings.xml @@ -45,9 +45,6 @@ ∞ ⵉⴼⵉⴷⵢⵓⵜⵏ 100+ ⵉⴼⵉⴷⵢⵓⵜⵏ - - - ⴰⵎⵙⵍⴰⵢ ⴰⴼⵉⴷⵢⵓ ⵉⵔⵉⵜⵏ diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 813896fea..6ede03bc9 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -183,9 +183,6 @@ Името на файла не може да бъде празно Възникна грешка: %1$s Не са налични източници за изтегляне - хил. - млн. - млрд. Няма абонати Създай Откажи @@ -229,7 +226,7 @@ Съдържание на главната страница Празна страница Страница-павилион - Страница на определен канал + Страница на канал Изберете канал За момента нямате абонаменти Изберете павилион @@ -419,7 +416,6 @@ Страница на плейлиста Глави Лиценз - %s посочва следната причина: Маркери Поверителност Език @@ -816,4 +812,6 @@ Търсене %1$s Търсене %1$s (%2$s) Харесвания + Страница SoundCloud Top 50 е премахната + SoundCloud преустанови оригиналните класации Топ 50. Съответният раздел е премахнат от главната ви страница. diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index ddc32e418..2b0ffe918 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -82,9 +82,6 @@ ভিডিও অডিও পুনরায় চেষ্টা করো - হা - M - বি শুরু বিরতি diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index b61b846ed..b6ef53086 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -104,9 +104,6 @@ কোন ভিডিও নেই কোন ভিউ নেই কোন সাবস্ক্রাইবার নেই - B - M - K পুনরায় চেষ্টা করো অডিও ভিডিও @@ -543,7 +540,6 @@ \'%s\' এর জন্য ফিড প্রক্রিয়া করা যাচ্ছে না। বর্ণনার লেখা নির্বাচন করা নিষ্ক্রিয় করো বর্ণনার লেখা নির্বাচন করা সক্ষম করো - %s এই কারণ বলছে: প্রক্রিয়াকরণ ফিডে ত্রুটি ওয়েবসাইট খুলুন অ্যাকাউন্ট ধ্বংসকৃত diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index b3491b1a4..775c34cac 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -231,9 +231,6 @@ S\'està recuperant el reproductor després de l\'error Bé, és lamentable. Arrossegueu per reordenar la llista - mil - milions - mil milions Inicia Feu un toc aquí per a més detalls Defineix una carpeta de baixades més endavant als paràmetres @@ -615,7 +612,6 @@ Pot seleccionar el seu tema fosc favorit aqui sota Selecciona el teu tema fosc favorit — %s Automàtic (tema del dispositiu) - %s dóna aquesta raó: Usuari suspes El compte de l\'autor ha estat esborrat. \nNewPipe no serà capaç de carregar aquest fil en el futur. diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index 5a9bbbbe2..1e8fee694 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -25,7 +25,6 @@ ژمارەی بەژداری نادیارە ناتوانرێت لەسەر ئەو فایله‌وه‌ جێگیر بکرێت په‌ڕه‌ هەڵبژێرە - ملیۆن +١٠٠ ڤیدیۆیان لێده‌ر هاوردە @@ -46,7 +45,6 @@ ئەمە لەسەر ڕێکخستنەکانی ئێستات جێگیر دەبێت. پەیامەکانی نیوپایپ نیوپایپ لەلایەن چەند خۆبەخشێکەوە دروستکراوە کە کاته‌كانی خۆیان پێ بەخشیووە تاکو باشترین خزمەتگوزاریت پێشکەش بکەن. هیچ نەبێت بە کڕینی کوپێک قاوە یارمەتی گەشەپێدەرەکانمان بدە بۆ ئەوەی کاتی زیاتر تەرخان بکەین بۆ بەرەوپێشبردنی نیوپایپ. - ملیار گەڕانی پێشنیارکراوەکان خێرا فایل سڕایەوە @@ -373,7 +371,6 @@ ناتوانرێت داببه‌زێنرێت ناتوانرێت بە ڕاژەكه‌وە پەیوەست ببیت لێدانی ڤیدیۆ، مه‌ودا: - هەزار زۆرترین بەدڵ سڕینەوە جۆری بنەڕەتی ڤیدیۆ @@ -599,7 +596,6 @@ ڕادیۆ تایبەتکراو ئه‌م بابه‌ته‌ ته‌نیا بۆ ئه‌و كه‌سانه‌ به‌رده‌سته‌ كه‌ پاره‌یان داوه‌ ، بۆیه‌ ناتوانرێت له‌ نیوپایپه‌وه‌ داببه‌زێنرێت. - %s ئه‌م هۆكاره‌ دابین ده‌كات: هه‌ژمار له‌ناوبراوه‌ ئه‌م ڤیدیۆیه‌ ته‌نیا له‌ وه‌شانی نایابی یوتوب میوزیك به‌رده‌سته‌ ، بۆیه‌ ناتوانرێت له‌ نیوپایپه‌وه‌ داببه‌زێنرێت. ئه‌مه‌ تراكی SoundCloud Go+ ه‌ ، لانی كه‌م له‌ وڵاته‌كه‌ی تۆدا، ناتوانرێت له‌لایه‌ن نیوپایپه‌وه‌ داببه‌زێنرێت. diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 4eb1d8b4e..0cea3fb2d 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -57,7 +57,7 @@ Jméno souboru Vlákna Zastavit - Smazat + Odstranit Start Zkusit znovu Video @@ -83,9 +83,7 @@ Určete prosím složku pro stahování později v nastavení Co:\\nŽádost:\\nJazyk obsahu:\\nZemě obsahu:\\nJazyk aplikace:\\nSlužba:\\nČas GMT:\\nBalíček:\\nVerze:\\nVerze OS: Vše - tis. Otevřít ve vyskakovacím okně - mil. Toto oprávnění je vyžadováno \npro otevření ve vyskakovacím okně Odstraňuje zvuk v některých rozlišeních @@ -124,7 +122,6 @@ Oznámení pro NewPipe přehrávač Žádné výsledky Je tu sranda jak v márnici - mld. Žádní odběratelé %s odběratel @@ -234,8 +231,8 @@ Přidat do playlistu Nastavit jako náhled playlistu Přidat playlist do záložek - Smazat záložku - Smazat tento playlist\? + Odstranit záložku + Odstranit tento playlist? Playlist vytvořen V playlistu Náhled playlistu změněn. @@ -411,9 +408,9 @@ Zobrazit pozici přehrávání v seznamech Pozice playbacku smazány Timeout spojení - Smazat pozice playbacku - Smazat všechny pozice playbacku - Smazat všechny pozice playbacku\? + Vymazat pozice přehrávání + Vymaže všechny pozice přehrávání + Vymazat všechny pozice přehrávání? Přepnout službu, právě vybráno: Nikdo nesleduje @@ -444,8 +441,8 @@ obnovuji Toto stahování nelze obnovit Vyberte instanci - Smazat historii stahování - Smazat stažené soubory + Vymazat historii stahování + Odstranit stažené soubory Souhlasit se zobrazením přes jiné aplikace Jazyk aplikace Jazyk systému @@ -488,7 +485,7 @@ %d vybráno Prázdné jméno skupiny - Přejete si smazat tuto skupinu\? + Přejete si odstranit tuto skupinu? Nová Novinky Limit aktualizace novinek @@ -622,7 +619,6 @@ Vypnout výběr textu v popisu Zapnout výběr textu v popisu Nyní můžete vybrat v popisu text. Pamatujte, že v režimu výběru může stránka blikat a odkazy nemusí reagovat na kliknutí. - %s udává teno důvod: Účet uzavřen Režim rychlého feedu o tom neposkytuje více informací. Autorův účet byl uzavřen. @@ -687,7 +683,7 @@ Frekvence kontroly Jakákoli síť Požadované síťové připojení - Smazat všechny stažené soubory z disku\? + Odstranit všechny stažené soubory z disku? Objednali jste si nyní tento kanál Všechny přepnout Nové streamy @@ -844,4 +840,6 @@ Hledat %1$s Hledat %1$s (%2$s) Líbí se + Stránka SoundCloud Top 50 odstraněna + SoundCloud zrušil původní žebříčky Top 50. Příslušná karta byla odstraněna z vaší hlavní stránky. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 2cd839892..f562c87c9 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -305,9 +305,6 @@ Stop Hændelser Ikke andet end fårekyllinger her - t - mio. - mia. %s abonnent %s abonnenter @@ -556,7 +553,6 @@ Dette indhold er privat, så det kan ikke streames eller hentes af NewPipe. Nyligt tilføjede Fremhævede - %s giver denne grund: %s lytter %s lyttere diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3594ebbe6..f92d23cd2 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -69,9 +69,6 @@ Fehlerbericht Löschen Prüfsumme - Tsd. - Mio. - Mrd. Dateiname Fehler Bitte warten … @@ -630,7 +627,6 @@ Du wirst jedes Mal gefragt werden, wohin der Download gespeichert werden soll Fehler beim Laden des Feeds Konnte Feed für \'%s\' nicht laden. - %s gibt diesen Grund an: An Tablet-Modus Aus @@ -827,4 +823,9 @@ Eine Feed-Gruppe auswählen Kanalgruppen-Seite Es wurde noch keine Feed-Gruppe erstellt + Suche %1$s + Suche %1$s (%2$s) + Gefällt mir + SoundCloud-Top-50-Seite entfernt + SoundCloud hat die ursprünglichen Top-50-Charts abgeschafft. Der entsprechende Tab wurde von deiner Hauptseite entfernt. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index bac732578..bd3114485 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -41,7 +41,6 @@ Μικρογραφία εικόνας προφίλ του χρήστη Like Dislike - δισ/ρια Άνοιγμα σε αναδυόμενο παράθυρο Εγγραφή Εγγεγραμμένος @@ -169,8 +168,6 @@ Δεν υπάρχει τίποτα εδώ Σύρετε για ταξινόμηση Προσπάθεια εκ νέου - χιλ. - εκ/ρια Κανένας συνδρομητής %s συνδρομητής @@ -611,7 +608,6 @@ Ενεργοποίηση επιλογής κειμένου στην περιγραφή Τώρα μπορείτε να επιλέξετε κείμενο εντός της περιγραφής. Σημειώστε ότι, η σελίδα μπορεί να παρουσιάζει αστάθεια κατά τη διάρκεια της κατάστασης επιλογής κειμένου. Ανοικτή ιστοσελίδα - Το %s παρέχει αυτή την αιτία: Ο λογαριασμός διαγράφηκε Η κατάσταση γρήγορης τροφοδοσίας δεν παρέχει περισσότερες πληροφορίες. Ο λογαριασμός του δημιουργού έχει διαγραφεί. @@ -827,4 +823,9 @@ Επιλογή ομάδας τροφοδοσίας Δεν δημιουργήθηκε ομάδα τροφοδοσίας ακόμα Σελίδα καναλιού ομάδας + Αναζήτηση %1$s + Αναζήτηση %1$s (%2$s) + Likes + Η σελίδα των SoundCloud Top 50 αφαιρέθηκε + Το SoundCloud έχει καταργήσει τα αρχικά charts με τα Top 50. Η αντίστοιχη καρτέλα έχει αφαιρεθεί από την κύρια σελίδα σας. diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 1da4d536c..3dde69618 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -261,9 +261,6 @@ %s spekto %s spektoj - k - M - Mrd Pri NewPipe Eksteraj permesiloj © %1$s de %2$s sub %3$s @@ -507,7 +504,6 @@ Ŝaltita Etikedoj Elŝutado komenciĝis - %s donas tiun kialon: Tiu enaĵo ne disponeblas en via lando. Freŝaj De %s diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index acecf3b71..8458ad4c2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -81,9 +81,6 @@ Qué:\\nSolicitud:\\nIdioma del contenido:\\nPaís del contenido:\\nIdioma de la aplicación:\\nServicio:\\nMarca de tiempo:\\nPaquete:\\nVersión:\\nVersión del SO: Negro Todo - k - M - MM Abrir en modo emergente Se necesita este permiso \npara abrir en modo emergente @@ -614,7 +611,6 @@ Deshabilitar la selección de texto de la descripción Habilitar la selección de texto de la descripción Ahora puede seleccionar el texto dentro de la descripción. Note que la página puede parpadear y los links no serán cliqueables mientras está en el modo de selección. - %s da esta razón: No fue posible cargar el feed por \'%s\'. Cuenta cancelada El modo de muro rápido no arroja más información sobre esto. diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index fcae21ec6..caf001d8e 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -154,9 +154,6 @@ Video Audio Proovi uuesti - tuh - mln - mld Tellijaid pole %s tellija @@ -217,7 +214,7 @@ Enim esitatud Avalehe sisu Tühi leht - Kioski leht + Kioskivaade Kanali leht Vali kanal Kanaleid pole veel tellitud @@ -565,7 +562,6 @@ Näita pisipilte Kasuta pisipilti nii lukustusvaate kui teavituste taustana Kasutajakonto on suletud - %s toob põhjuseks: Võimalda valida kirjelduse teksti Ära võimalda valida kirjelduse teksti Kategooria @@ -815,4 +811,6 @@ Otsi: %1$s Otsi: %1$s (%2$s) Meeldimisi + SoundCloudi „Top 50“ leht on eemaldatud + SoundCloud on lõpetanud oma algse „Top 50“ edetabeli pidamise. Seega on ka vastav vahekaart meie rakenduse põhivaatest eemaldatud. diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 940db1ddd..03f2e4cf0 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -84,9 +84,6 @@ Bideoa Audioa Saiatu berriro - k - M - MM Hasi Pausatu Ezabatu @@ -618,7 +615,6 @@ Non gorde galdetuko zaizu deskarga bakoitzean Ez da deskargatzeko karpetarik ezarri oraindik, aukeratu lehenetsitako deskargatzeko karpeta orain Pribatutasuna - %s arrazoi hau ematen du: Kontua ezabatu da Jario azkarrak ez du honi buruz informazio gehiagorik ematen. Adin muga diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 023e75a5c..0e2e72677 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -148,9 +148,6 @@ خطایی رخ داد: %1$s جریانی برای بارگیری در دسترس نیست بدون نتیجه - K - M - B %s مشترک %s مشترک @@ -628,7 +625,6 @@ \nنیوپایپ قادر به بار کردن این خوراک در آینده نیست. \nمی‌خواهید اشتراک این کانال را لغو کنید؟ حالت خوراک سریع، اطَلاعات بیش‌تری در این باره نمی‌دهد. - %s این دلیل را آورد: پیش‌نمایش بندانگشتی نوار جویش قلب‌شده به دست ایجادگر پیشنهادهای جست‌وجوی محلّی diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index c88c6a999..4f4833cce 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -102,9 +102,6 @@ Video Ääni Toista uudelleen - t. - milj. - bilj. Ei tilaajia %s tilaaja @@ -595,7 +592,6 @@ Yöteema Poista käytöstä tekstinvalinta kuvauskentän sisältä Voit nyt valita tekstin kuvauskentän sisältä. Huomioithan, että valintatilan aikana sivu voi vilkkua ja linkit eivät ehkä ole klikattavia. - %s tuo tämän syyn: Säätövivun kuvakkeen esikatselu Poista median tunnelointi käytöstä, jos havaitset mustan näyttöruudun tai änkytystä videon toistossa. Poista median tunnelointi käytöstä diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 293e44c06..4d6605fc6 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -87,8 +87,6 @@ Lecture en mode flottant Désactivés Quoi :\\nRequest :\\nContent Language :\\nContent Country :\\nApp Language :\\nService :\\nGMT Time :\\nPackage :\\nVersion :\\nOS version : - k - M Cette autorisation est nécessaire pour \nutiliser le mode flottant Arrière-plan @@ -100,7 +98,6 @@ Mémoriser les propriétés de la fenêtre flottante Mémoriser les dernières taille et position de la fenêtre flottante Effacer - G Le son peut être absent à certaines définitions Suggestions de recherche Sélectionner les suggestions à afficher lors d’une recherche @@ -164,7 +161,7 @@ Caractères spéciaux Voulez-vous supprimer cet élément de l’historique de recherche \? Contenu de la page principale - Page vide + Page blanche Chaîne Sélectionner une chaîne Tendances @@ -623,7 +620,6 @@ Étiquettes Catégorie Vous pouvez maintenant sélectionner du texte à l’intérieur de la description. Notez que la page peut scintiller et que les liens peuvent ne pas être cliquables en mode sélection. - %s indique le motif : Aucun dossier de téléchargement n’est défini pour le moment, sélectionnez le dossier de téléchargement par défaut Ouvrir le site web Compte résilié @@ -846,4 +842,6 @@ Rechercher %1$s Rechercher %1$s (%2$s) Likes + Page SoundCloud Top 50 supprimée + SoundCloud a abandonné le classement original du Top 50. L\'onglet correspondant a été supprimé de votre page d\'accueil. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 0d34e89d8..d6b92ac0c 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -158,9 +158,6 @@ Vídeo Audio Tentar de novo - k - M - B Ningún subscrito %s subscrito @@ -557,7 +554,6 @@ Agora pode seleccionar o texto na descrición. Teña en conta que a páxina pode cintilar e as ligazóns poden non ser clicábeis no modo selección. Automático (Tema do dispositivo) Radio - %s dá este motivo: Este contido non está dispoñíbel no seu país. Capítulos Recentes diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 07f3b24a5..3409406c9 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -112,9 +112,6 @@ סרטון שמע ניסיון חוזר - אלפ. - מיל. - מיליארד אין מנויים מנוי אחד @@ -179,9 +176,9 @@ היסטוריה למחוק את הפריט הזה מהיסטוריית החיפושים\? תוכן הדף הראשי - דף ריק - דף גישה מזדמנת - דף ערוצים + עמוד ריק + עמוד גישה מזדמנת + עמוד הערוץ נא לבחור ערוץ אין עדיין מינויים לערוצים נא לבחור סוג גישה מזדמנת @@ -635,7 +632,6 @@ \nל־NewPipe לא תהיה אפשרות להוריד את ההזנה הזאת בעתיד. \nלהסיר את המינוי מהערוץ הזה\? פתיחת האתר - %s מספק את הסיבה הבאה: החשבון הושמד מצב ההזנה המהירה לא מספק מידע נוסף על כך. לא ניתן לטעון את ההזנה עבור ‚%s’. diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 681bf4ba0..71a2903d6 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -126,9 +126,6 @@ वीडियो ऑडियो फिर से कोशिश करें - हज़ार - मिलियन - अरब कोई सब्सक्राइबर नहीं %s सब्सक्राइबर @@ -657,7 +654,6 @@ मीडिया टनलिंग अक्षम करें \"क्रैश द प्लेयर\" दिखाएं लोड नहीं हुआ: %d - %s इसका कारण प्रदान करता है: टैग लाइसेंस यदि आपको ऐप का उपयोग करने में परेशानी हो रही है, तो सामान्य प्रश्नों के इन उत्तरों को देखना सुनिश्चित करें! @@ -822,4 +818,12 @@ बैकअप और रिस्टोर आयात किए जा रहे निर्यात में सेटिंग्स एक कमजोर प्रारूप का उपयोग करती हैं जिसे न्यूपाइप 0.27.0 के बाद से हटा दिया गया था। सुनिश्चित करें कि आयात किया जा रहा निर्यात किसी विश्वसनीय स्रोत से है, और भविष्य में केवल न्यूपाइप 0.27.0 या नए से प्राप्त निर्यात का उपयोग करना पसंद करें। इस असुरक्षित प्रारूप में सेटिंग्स आयात करने के लिए समर्थन जल्द ही पूरी तरह से हटा दिया जाएगा, और फिर न्यूपाइप के पुराने संस्करण अब नए संस्करणों से निर्यात की सेटिंग्स आयात नहीं कर पाएंगे। सेकेंडरी + %1$s खोजें + %1$s (%2$s) खोजें + प्लेलिस्ट + कृपया एक फ़ीड समूह चुनें + अभी तक कोई फ़ीड समूह नहीं बनाया गया है + चैनल समूह पेज + पसंद + यूट्यूब अस्थायी प्लेलिस्ट के रूप में साझा करें diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 54f5b9b28..4b168c420 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -97,9 +97,6 @@ Video Audio Pokušaj ponovo - tis. - mil - mlrd. Počni Pauziraj Izbriši @@ -642,7 +639,6 @@ Interno Privatnost Sada možeš odabrati tekst u opisu. Napomena: stranica će možda treperiti i možda nećeš moći kliknuti poveznice u načinu rada za odabir teksta. - %s pruža ovaj razlog: Obrada u tijeku … Može malo potrajati Za ukljanjanje stavki povuci ih diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 86f4d76fa..cf8a4a5f3 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -166,9 +166,6 @@ Nincs letölthető adatfolyam Nincs itt semmi pár tücskön kívül Húzza az átrendezéshez - e - m - M Nincs feliratkozó %s feliratkozó @@ -540,7 +537,6 @@ Kapcsolódó elemek Ellenőrizze, hogy létezik-e már olyan jegy, amely az összeomlásával foglalkozik. Ha duplikált jegyet ad fel, akkor olyan időt vesz el tőlünk, amelyet a hiba javítására tudnánk fordítani. Minimalizálás alkalmazásváltáskor - A(z) %s ezt az okot adta meg: Helyi keresési javaslatok Távoli keresési javaslatok A fő lejátszó teljes képernyős indítása @@ -802,4 +798,6 @@ Keresés %1$s Keresés %1$s (%2$s) Kedvelések + SoundCloud Top 50 oldal eltávolítva + A SoundCloud megszüntette az eredeti Top 50-es listákat. A megfelelő lap el lett távolítva a főoldalról. diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 4d269251b..e48398866 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -82,9 +82,6 @@ Meminta kode reCAPTCHA Hitam Semua - r - J - T Buka pada mode sembulan Izin ini dibutuhkan untuk \nmembuka di mode sembul @@ -193,8 +190,8 @@ Terakhir Diputar Sering Diputar Konten halaman utama - Halaman Kosong - Halaman Kedai + Halaman kosong + Halaman kiosk Halaman saluran Pilih saluran Belum ada saluran langganan @@ -602,7 +599,6 @@ Aktifkan dapat memilih teks pada deskripsi Anda sekarang dapat memilih teks di dalam deskripsi. Perhatikan bahwa halaman mungkin berkedip dan tautan tidak dapat diklik saat dalam mode pemilihan. Buka situs web - %s menyediakan alasan ini: Akun dinonaktifkan Mode langganan cepat tidak menyediakan lebih banyak info tentang ini. Akun kreator telah dinonaktifkan. @@ -813,4 +809,9 @@ Halaman grup saluran Belum ada grup umpan yang dibuat Pilih grup umpan + Cari %1$s + Cari %1$s (%2$s) + Suka + Halaman Top 50 SoundCloud dihapus + SoundCloud telah menghentikan dukungan tangga lagu Top 50. Tab terkait telah dihapus dari halaman utama Anda. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 7bef3712e..f64d63fea 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -97,7 +97,6 @@ Hljóðstillingar Spila í bakgrunni Þegar hlekkur er opnaður — %s - þús. Líkar ekki við Reyna aftur Lýsing @@ -217,7 +216,6 @@ Athugasemd þín (á ensku): Engar niðurstöður Myndskeið - ma. Engin áhorf %s áhorf @@ -352,7 +350,6 @@ Flokkur Merki NewPipe er þróað af sjálfboðaliðum sem eyða frítíma sínum í að færa þér bestu notendaupplifunina. Gefðu til baka til að hjálpa forriturum að gera NewPipe enn betri á meðan þeir njóta kaffibolla. - millj. Slökktu á til að fela lýsingu og viðbótarupplýsingar myndskeiðs Villa kom upp: %1$s Þraut reCAPTCHA @@ -576,7 +573,6 @@ Enginn viðeigandi skráarstjóri fannst fyrir þessa aðgerð. \nVinsamlegast settu upp skráarstjóra sem styður Geymsluaðgangsramma (SAF) Þetta efni er ekki fáanlegt í þínu landi. - %s gefur þessa ástæðu: Þetta efni er aðeins í boði fyrir notendur sem hafa greitt — það er ekki hægt að streyma því eða sækja með NewPipe. Sjálfvirk (þema tækis) Veldu uppáhalds næturþemu þína — %s @@ -803,4 +799,12 @@ auka Deila sem YouTube-bráðabirgðaspilunarlista Spilunarlistar + Leita í %1$s + Leita í %1$s (%2$s) + Veldu hóp streyma + Enginn hópur streyma útbúinn ennþá + Síða rásahóps + Líkar við + Topp 50 síða SoundCloud fjarlægð + SoundCloud er hætt með Topp 50 vinsældalistann. Viðkomandi flipi hefur verið fjarlægður af aðalsíðunni þinni. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 5601cead2..71af739ef 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -82,9 +82,6 @@ Risoluzione reCAPTCHA Nero Tutto - k - M - Mrd È richiesta la risoluzione del reCAPTCHA Apri in modalità popup Riproduzione in modalità popup @@ -622,7 +619,6 @@ Attiva la selezione del testo nella descrizione È possibile selezionare il testo all\'interno della descrizione. In modalità selezione la pagina potrebbe sfarfallare e i collegamenti potrebbero non essere cliccabili. Visita il sito - %s fornisce questa motivazione: Account chiuso Il recupero veloce dei feed non fornisce ulteriori informazioni al riguardo. L\'account dell\'autore è stato chiuso. @@ -844,4 +840,6 @@ Cerca %1$s (%2$s) Cerca su %1$s Mi piace + Pagina Top 50 di SoundCloud rimossa + SoundCloud ha dismesso i grafici Top 50 originali. La scheda relativa è stata rimossa dalla pagina principale. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a1f1a4c77..3952afb18 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -83,9 +83,6 @@ reCAPTCHA を要求しました ブラック すべて - k - M - B ポップアップモードで開く ポップアップモードで開くには \n権限の許可が必要です @@ -615,7 +612,6 @@ オフ オン タブレットモード - %s がこの理由を提示: 表示しない 低品質 (小) 高品質 (大) diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 1120673d9..a6e1d9ccb 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -237,9 +237,6 @@ ვიდეო აუდიო ხელახლა სცადეთ - ათასი - მლნ - ბლნ სერვისის გადართვა, ამჟამად არჩეულია: გამოწერები არ არის @@ -547,7 +544,6 @@ ეს ხელმიუწვდომელია თქვენი ქვეყნიდან. ეს მასალა პირადულია, ამიტომაც NewPipe-ს მისი არც მთლიანად და არც თანდათანობით ჩამოწერა არ შეუძლია. ანგარიში შეწყვეტილია - %s იძლევა ამ მიზეზს: ეს მასალა ხელმისაწვდომია მხოლოდ გადამხდელებისთვის, ამიტომაც NewPipe-ს მისი არც მთლიანად და არც თანდათანობით ჩამოწერა არ შეუძლია. გამორჩეული რადიო diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml index 97476fc46..0ded7974c 100644 --- a/app/src/main/res/values-kab/strings.xml +++ b/app/src/main/res/values-kab/strings.xml @@ -82,7 +82,6 @@ Sider Asfaylu udhim Ttu - A Kter Ih Amazray @@ -128,7 +127,6 @@ Snifel isem Asider ur yeddi ara Tamwalit - o Aɣawas n deffir Amazray yesteɛfay diff --git a/app/src/main/res/values-kmr/strings.xml b/app/src/main/res/values-kmr/strings.xml index b5e5235d5..24bc5574e 100644 --- a/app/src/main/res/values-kmr/strings.xml +++ b/app/src/main/res/values-kmr/strings.xml @@ -49,9 +49,6 @@ Ne abone Karûbarê veguheztinê, niha hatî hilbijartin: - B - M - k Dîsa biceribîne Deng Vîdyo diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 22a1bd63c..58100b5fa 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -116,9 +116,6 @@ 무엇:\\n요청:\\n콘텐츠 언어:\\n콘텐츠 국가:\\n앱 언어:\\n서비스:\\nGMT 시간:\\n패키지:\\n버전:\\nOS 버전: 결과 없음 구독할 항목을 추가하세요 - - 백만 - 십억 구독자 없음 구독자 %s명 @@ -646,7 +643,6 @@ 챕터 최근 계정이 해지됨 - %s은(는) 다음과 같은 이유를 제공: 이것은 적어도 귀하의 국가에서 SoundCloud Go+ 트랙이므로 NewPipe에서 스트리밍하거나 다운로드할 수 없습니다. 자동 (장치 테마) 고정된 댓글 diff --git a/app/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml index 2f3934fff..3dc51fcc8 100644 --- a/app/src/main/res/values-ku/strings.xml +++ b/app/src/main/res/values-ku/strings.xml @@ -133,9 +133,6 @@ ڤیدیۆ دەنگ هەوڵدانەوە - هەزار - ملیۆن - بلیۆن هیچ بەشداربوویەک نییە %s بەشداربوو diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 75c05a0cb..c23491dfc 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -139,9 +139,6 @@ Atstatoma po grotuvo klaidos Nėra rezultatų Čia nieko nėra išskyrus svirplius - Tūkst. - Mln. - Mlrd. Nėra prenumeratorių Nėra peržiūrų @@ -626,7 +623,6 @@ Įgalinti teksto pasirinkimą apraše Neleisti pasirinkti teksto apraše Dabar apraše galite pasirinkti tekstą aprašyme. Atminkite, kad puslapis gali mirgėti, o nuorodos gali būti nespustelėjamos, kai veikia pasirinkimo režimas. - %s pateikia šią priežastį: Paskyra anuliuota Greito srauto režimas nesuteikia daugiau informacijos apie tai. Autoriaus paskyra anuliuota. diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 1da203ceb..6aeea0af7 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -136,9 +136,6 @@ Nav abonamentu Izvēlaties pakalpojumu, šobrīd izvēlēts: - B - M - k Atkārtot Audio Video @@ -632,7 +629,6 @@ \nNewPipe turpmāk nevarēs ielādēt šo plūsmu. \nVai vēlaties atteikties no šī kanāla abonēšanas\? Ātrās straumes režīms nesniedz vairāk informācijas par šo. - %s dod šādu pamatojumu: Izslēgt teksta atlasīšanu video aprakstā Iekšeji Autors piekrīt diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 1d3183b10..3a8fa2f07 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -146,9 +146,6 @@ Видео Звук Пробај повторно - илјади - M - милијарди Нема зачленети %s зачленет @@ -434,7 +431,6 @@ Неуспешно вчитување на новинска лента за „%s“. Прикажи / скриј стримови Оваа содржина е приватна, така што не може да биде емитувана или преземена од страна на NewPipe. - %s ја посочува следната причина: Истакнато Радио Автоматски (режим на уредот) @@ -721,4 +717,4 @@ Изберете гестикулација за десната половина од екранот на плеерот Видеата нема да започнат со емитување во миниплеерот, туку директно ќе се вклучат на цел екран, доколку автоматското ротирање е заклучено. Сѐ уште можете да добиете пристап до миниплеерот, кога ќе излезете од целиот екран URL адресата не може да биде распознаена. Да се отвори со друга апликација? - \ No newline at end of file + diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 986c427c8..be358bba0 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -183,9 +183,6 @@ സബ്ക്രൈബേഴ്സ് ഇല്ല സേവനം മാറ്റുക, ഇപ്പോൾ തിരഞ്ഞെടുത്തത്: - B - k - M വീണ്ടും ശ്രമിക്കുക ഓഡിയോ വീഡിയോ @@ -615,7 +612,6 @@ ടാഗുക്കൾ വിഭാഗം താക്കൾക് ഇപ്പോൾ ഡിസ്ക്രിപ്ഷൻ ബോക്സിലെ ടെക്സ്റ്റ്‌ തിരഞ്ഞെടുക്കാൻ സാധിക്കും. ശ്രെദ്ധിക്കുക സെലെക്ഷൻ മോഡിൽ പേജ് ചിലപ്പോൾ മിന്നുകയും ലിങ്കുകൾ ക്ലിക്ക് ചെയ്യാനാകാതെയും വന്നേക്കാം. - ഇതിന്റെ കാരണം %s നൽകും: അക്കൗണ്ട് ഇല്ലാതായിരിക്കുന്നു ഫാസ്റ്റ് ഫീഡ് മോഡ് കൂടുതൽ വിവരങ്ങൾ നൽകില്ല. സൃഷ്ടാവിന്റെ അക്കൗണ്ട് ഇല്ലാതായിരിക്കുന്നു. diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index 304858d84..7a6b2eda4 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -138,11 +138,8 @@ डेबग अपडेट थेट - प्लेलिस्ट - फाईल - के परवाना चेकसम इतिहास diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index bb0527655..e70e61a74 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -171,9 +171,6 @@ Video Audio Cuba semula - K - J - B Tiada pelanggan %s pelanggan diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index e4a67bb9b..fa70a92f8 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -89,9 +89,6 @@ Spiller av i oppsprettsmodus Alle Avskrudd - k - M - Mrd. Denne tilgangen trengs for \nåpning i oppsprettsmodus reCAPTCHA-oppgave forespurt @@ -603,7 +600,6 @@ \nØnsker du å oppheve ditt abonnement på denne kanalen\? Skru av merking av tekst i beskrivelsen Skru på merking av tekst i beskrivelsen - %s oppgav denne grunnen: Konto terminert Kunne ikke laste inn informasjonskanal for «%s». Kunne ikke laste inn informasjonskanal diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index dd570b82e..b40145aa6 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -176,9 +176,6 @@ भिडियो अडियो पुन: प्रयास - हजार - करोड - अर्ब कुनै सदस्यहरू छैनन् %s सदस्य diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml index fbbb38bc0..505351b52 100644 --- a/app/src/main/res/values-nl-rBE/strings.xml +++ b/app/src/main/res/values-nl-rBE/strings.xml @@ -147,9 +147,6 @@ Video Geluid Opnieuw proberen - k - M - mld. Geen abonnees %s abonnee diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index c676dacb9..1c3ac36e8 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -84,9 +84,6 @@ reCAPTCHA-uitdaging gevraagd Openen in pop-upmodus Alles - dznd. - mln. - mld. Deze machtiging is vereist om te \nopenen in pop-upmodus Speelt af in pop-upmodus @@ -615,7 +612,6 @@ Aan Tablet-modus Website openen - %s geeft de volgende reden: Account getermineerd De snelle feed mode levert hierover niet meer informatie. De account van de auteur is getermineerd. diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml index b2a536b5d..c83ced38b 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -178,9 +178,6 @@ ߞߐߝߟߌ߫ ߕߍ߫ ߦߋ߲߬ ߡߍ߲ߕߊ ߞߵߊ߬ ߡߊߛߊ߬ߦߌ߬ - ߥߊ߯ - ߞߋ߲߬ - ߥߟߡ ߞߊ߬ ߥߏ߬ߦߏ߫ ߣߊ߬ߕߊ ߝߙߊ߬ ߕߎ߲߰ߠߌ߲ ߠߊ߫ ߞߍ߲ߖߘߍߡߊߓߟߏ ߡߊ߬ ߞߊ߬ ߕߎ߲߰ߠߌ߲ ߘߐߞߊ߬ߙߊ߲ ߓߟߏߕߎ߰ (ߞߊߣߊ߬ ߡߊߛߊ߬ߦߌ߬) ߥߏ߬ߦߏ߫ ߢߐ߲߰ߘߐ ߟߎ߫ ߟߊ߫ ߕߏߟߏ߲ߟߊ߲߫ ߥߊ߲߬ߥߊ߲ ߣߎߡߊ߲߫ ߕߟߊ ߖߍ߰ߙߍ ߛߎߥߊ߲ߘߌ߫ @@ -628,7 +625,6 @@ ߞߐߕߐ߯ ߡߊߡߙߊߟߊ߲߫ ߛߌ߫ ߡߊ߫ ߛߐ߬ߘߐ߲߬ ߞߋߥߊߟߌ ߣߌ߲߬ ߞߊ߲ߡߊ߬. \nߘߌ߬ߢߍ߬ ߦߋ߫ ߞߐߕߐ߯ ߡߊߡߙߊߟߊ߲ ߘߏ߫ ߡߊߞߍ߫ ߡߍ߲ ߣߌ߫ ߡߙߊ߬ߘߐ߬ߦߊ ߟߊߛߐ߬ߘߐ߲ ߡߎ߬ߙߊ߲߬ߞߊ߲ߞߋ ߘߌ߫ ߓߍ߲߬ ߦߋߡߍ߲ߕߊ ߘߌ߫ ߡߊߛߐ߬ߘߐ߲߬ YouTube Music Premium ߛߌ߲߬ߝߏ߲ ߠߎ߬ ߟߋ߬ ߘߐߙߐ߲߫ ߓߟߏ߫߸ ߏ߬ ߘߐ߫ ߊ߬ ߕߍ߫ ߛߋ߫ ߘߐߛߊߙߌ߫ ߟߊ߫ ߥߟߊ߫ ߞߵߊ߬ ߟߊߖߌ߰ ߣߌߎߔߌߔ ߓߟߏ. - %s ߦߋ߫ ߞߎ߲߭ ߣߌ߲߬ ߠߋ߬ ߝߐ߫ ߟߊ߫: ߛߊ߲ߞߊߥߟߌ ߥߎߢߊ߲ߓߍ߲ ߖߘߍ߬ߢߍ߫ (ߕߙߏߞߏ߫ ߛߊߛߊ) diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index ba1160bb7..bcb3d4ea4 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -437,7 +437,6 @@ ନାପସନ୍ଦ ମନ୍ତବ୍ୟ ଗୁଡିକ ବର୍ଣ୍ଣନା - ନିୟୁତ ସମାଧାନ ପ୍ଲେବେକ୍ ସ୍ପିଡ୍ ନିୟନ୍ତ୍ରଣ ଟେମ୍ପୋ @@ -486,7 +485,6 @@ ସଦସ୍ୟତା ଚୟନ କରନ୍ତୁ କୌଣସି ସଦସ୍ୟତା ଚୟନ ହୋଇନାହିଁ ଦ୍ରୁତ ମୋଡ୍ ସକ୍ଷମ କରନ୍ତୁ - %s ଏହି କାରଣ ପ୍ରଦାନ କରେ: ଚ୍ୟାନେଲର ଅବତାର ଥମ୍ୱନେଲ୍ ବୈଶିଷ୍ଟ୍ୟ ରେଡିଓ @@ -527,7 +525,6 @@ ସମ୍ବନ୍ଧୀୟ ଆଇଟମ୍ ଗୁଡ଼ିକ ପୁନଃ ସଯାଇବାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ ବିରାମ - ଵୃନ୍ଦ କୌଣସି ଗ୍ରାହକ ନାହାଁନ୍ତି ସୃଷ୍ଟି କରନ୍ତୁ ବିବରଣୀ ପାଇଁ ଟ୍ୟାପ୍ କରନ୍ତୁ @@ -608,7 +605,6 @@ ବହିଃ-ଚାଳକ ପାଇଁ ଗୁଣବତ୍ତା ଚୟନ କରନ୍ତୁ ପିନ୍ ହୋଇଥିବା ମନ୍ତବ୍ୟ ୱେବସାଇଟ୍ ଖୋଲନ୍ତୁ - ହଜାର ସୂଚନା ପାଇବା… %s ଗ୍ରାହକ diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 946fd7ea5..b45a2e7c5 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -84,7 +84,7 @@ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ ਪੌਪ-ਅਪ ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ ਸਮੱਗਰੀ - ਉਮਰ-ਮੁਤਾਬਕ-ਪਾਬੰਦੀਸ਼ੁਦਾ ਸਮੱਗਰੀ ਵਿਖਾਓ + ਉਮਰ ਮੁਤਾਬਕ ਪਾਬੰਦੀਸ਼ੁਦਾ ਸਮੱਗਰੀ ਵਿਖਾਓ ਲਾਈਵ ਡਾਊਨਲੋਡਸ ਡਾਊਨਲੋਡਸ @@ -153,9 +153,6 @@ ਵੀਡੀਓ ਆਡੀਓ ਦੋਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ - ਹਜ਼ਾਰ - ਮਿਲੀਅਨ - ਅਰਬ ਕੋਈ ਸਬਸਕ੍ਰਾਈਬਰ ਨਹੀਂ %s ਸਬਸਕ੍ਰਾਈਬਰ @@ -458,7 +455,6 @@ ਰੇਡੀਓ ਫੀਚਰਡ ਇਹ ਸਮੱਗਰੀ ਸਿਰਫ਼ ਉਹਨਾਂ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਉਪਲਬਧ ਹੈ ਜਿੰਨ੍ਹਾਂ ਨੇ ਇਸਦੇ ਲਈ ਕੀਮਤ ਦਿੱਤੀ ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। - %s ਇਸਦਾ ਕਾਰਨ ਪ੍ਰਦਾਨ ਕਰਦਾ ਹੈ: ਖਾਤਾ ਬੰਦ ਕੀਤਾ ਗਿਆ ਇਹ ਵੀਡੀਓ ਸਿਰਫ਼ ਯੂਟਿਊਬ ਮਿਊਜ਼ਿਕ ਦੇ ਪ੍ਰੀਮੀਅਮ ਮੈਂਬਰਾਂ ਲਈ ਉਪਲਬਧ ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਹ ਸਮੱਗਰੀ ਨਿੱਜੀ (ਪ੍ਰਾਈਵੇਟ) ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। @@ -822,6 +818,12 @@ ਨਹੀਂ ਇੰਪੋਰਟ ਕੀਤੇ ਜਾ ਰਹੇ ਐਕਸਪੋਰਟ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਇੱਕ ਕਮਜ਼ੋਰ ਫਾਰਮੈਟ ਦੀ ਵਰਤੋਂ ਕਰਦੀਆਂ ਹਨ ਜੋ ਨਿਊਪਾਈਪ 0.27.0 ਤੋਂ ਬਰਤਰਫ਼ ਕੀਤਾ ਗਿਆ ਸੀ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਇੰਪੋਰਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਨਿਰਯਾਤ ਇੱਕ ਭਰੋਸੇਯੋਗ ਸਰੋਤ ਤੋਂ ਹੈ, ਅਤੇ ਸਿਰਫ਼ ਨਿਊਪਾਈਪ 0.27.0 ਜਾਂ ਇਸਤੋਂ ਨਵੇਂ ਤੋਂ ਪ੍ਰਾਪਤ ਕੀਤੇ ਐਕਸਪੋਰਟ ਦੀ ਵਰਤੋਂ ਕਰਨ ਨੂੰ ਤਰਜੀਹ ਦਿਓ। ਇਸ ਕਮਜ਼ੋਰ ਫਾਰਮੈਟ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਨੂੰ ਆਯਾਤ ਕਰਨ ਲਈ ਸਮਰਥਨ ਜਲਦੀ ਹੀ ਪੂਰੀ ਤਰ੍ਹਾਂ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ ਅਤੇ ਫਿਰ ਨਿਊਪਾਈਪ ਦੇ ਪੁਰਾਣੇ ਸੰਸਕਰਣ ਹੁਣ ਨਵੇਂ ਸੰਸਕਰਣਾਂ ਤੋਂ ਐਕਸਪੋਰਟ ਦੀਆਂ ਸੈਟਿੰਗਾਂ ਨੂੰ ਇੰਪੋਰਟ ਕਰਨ ਦੇ ਯੋਗ ਨਹੀਂ ਹੋਣਗੇ। ਸੈਕੰਡਰੀ - ਟੈਂਪਰੇਰੀ ਯੂਟਿਊਬ ਪਲੇਲਿਸਟ ਵੱਜੋਂ ਸ਼ੇਅਰ ਕਰੋ + ਅਸਥਾਈ ਯੂਟਿਊਬ ਪਲੇਲਿਸਟ ਵਜੋਂ ਸਾਂਝਾ ਕਰੋ ਪਲੇਲਿਸਟਾਂ + %1$s ਦੀ ਖੋਜ ਕਰੋ + %1$s (%2$s) ٪1$s ਦੀ ਖੋਜ ਕਰੋ + ਫੀਡ ਗਰੁੱਪ ਚੁਣੋ + ਅਜੇ ਤੱਕ ਕੋਈ ਫੀਡ ਗਰੁੱਪ ਨਹੀਂ ਬਣਾਇਆ ਗਿਆ + ਚੈਨਲ ਗਰੁੱਪ ਪੰਨਾ + ਪਸੰਦ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 39358eb4a..25b71b36f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -96,10 +96,7 @@ Wszystkie Wyłączone Wyczyść - tys. - mln - mld - To pozwolenie jest wymagane, aby + To pozwolenie jest wymagane, aby \notworzyć w trybie okienkowym Otwórz w trybie okienkowym Tryb okienkowy @@ -626,7 +623,6 @@ Kategoria Otwórz stronę Teraz możesz zaznaczyć tekst wewnątrz opisu. Pamiętaj, że w trybie zaznaczania strona może migotać i linki nie będą klikalne. - %s podaje ten powód: Konto zamknięte Tryb szybki dla ładowania kanału nie dostarcza więcej informacji na ten temat. Konto autora zostało zawieszone. @@ -853,4 +849,6 @@ Szukaj %1$s Szukaj %1$s (%2$s) Polubienia + Usunięto stronę SoundCloud 50 najlepszych + SoundCloud wycofał oryginalną listę 50 najlepszych. Odpowiadająca karta została usunięta ze strony głównej. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index bb16416c9..eda9c96c1 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -90,9 +90,6 @@ Reproduzindo em modo Popup Todos Desativado - mil - mi - bi Essa permissão é necessária \npara abrir em modo Popup Limpar @@ -622,7 +619,6 @@ Ativar seleção de texto na descrição Agora você pode selecionar o texto dentro da descrição. Note que a página pode piscar e os URL podem não ser clicáveis no modo de seleção. Abrir site - %s fornece este motivo: Conta encerrada O modo feed rápido não fornece mais informações sobre isso. A conta do autor foi encerrada. @@ -841,4 +837,9 @@ Selecione um grupo de feeds Nenhum grupo de feeds criado ainda Página do grupo do canal + Pesquisar %1$s + Pesquisar %1$s (%2$s) + Curtidas + Página Top 50 do SoundCloud removida + O SoundCloud descontinuou as paradas originais do Top 50. A aba correspondente foi removida da sua página principal. diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 3a7bee5dc..94547cf52 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -229,7 +229,6 @@ OK Não foi possível atualizar a subscrição Sim e também os vídeos parcialmente vistos - M Ainda não há listas de reprodução favoritas %s ouvinte @@ -423,8 +422,6 @@ Importado Automático Substitui o seu histórico, subscrições, listas de reprodução e (opcionalmente) definições - k - MM Remover marcador Útil ao trocar para dados móveis, mas algumas transferências não podem ser suspensas Toque longo para colocar na fila @@ -626,7 +623,6 @@ Desativar seleção de texto na descrição Ativar seleção de texto na descrição Agora pode selecionar o texto na descrição. Note que a página pode cintilar e as ligações podem não ser clicáveis enquanto estiver no modo de seleção. - %s fornece este motivo: Conta encerrada O modo de feed rápido não fornece mais informações sobre isto. A conta do autor foi encerrada. @@ -841,4 +837,6 @@ Selecione um grupo de feeds Ainda nenhum grupo de feeds criado Página do grupo do canal + Pesquisar %1$s + Pesquisar %1$s (%2$s) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 3c54d11c3..c1dae7426 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -82,9 +82,6 @@ Abrir no modo popup Preto Tudo - K - M - MM Esta permissão é necessária \npara o modo popup Desafio reCAPTCHA @@ -608,7 +605,6 @@ Desativar túnel multimédia Sempre que descarregar um ficheiro, terá que indicar o local para o guardar Ainda não definiu uma pasta para as descargas. Escolha agora a pasta a utilizar - %s fornece este motivo: Conta encerrada O modo de fonte rápida não fornece mais informações sobre isto. A conta do autor foi encerrada. @@ -841,4 +837,6 @@ Selecione um grupo de feeds Ainda nenhum grupo de feeds criado Página do grupo do canal + Pesquisar %1$s + Pesquisar %1$s (%2$s) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index eefdaad48..4284c3d85 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -92,9 +92,6 @@ Dezactivat Aplicația/UI s-a oprit Ce:\\nSolicitare:\\nLimba conținutului:\\nȚara conținutului:\\nLimba aplicației:\\nServiciu:\\nOra GMT:\\nPachet:\\nVersiune:\\nVersiune SO: - k - mil. - mld. Elimină sunetul audio la anumite rezoluții Fundal Pop-up @@ -164,9 +161,9 @@ Istoric Doriți să ștergeți acest element din istoricul de căutare? Conținutul pagini principale - Pagină Goală + Pagină goală Pagina de chioșc - Pagină Canale + Pagină canale Alegeți un canal Nu v-ați abonat la niciun canal deocamdată Alegeți un chioșc @@ -627,7 +624,6 @@ Dezactivați selectarea textului în descriere Activați selectarea textului în descriere Acum puteți selecta text în interiorul descrierii. Rețineți că este posibil ca pagina să pâlpâie, iar linkurile să nu poată fi accesate în modul de selecție. - %s oferă acest motiv: Contul a fost închis Modul rapid nu furnizează mai multe informații în acest sens. Contul autorului a fost închis. @@ -839,4 +835,11 @@ Distribuie ca listă de redare temporară YouTube Liste de redare Pagina grupului de canale + Caută: %1$s + Caută %1$s (%2$s) + Selectează un grup de fluxuri + Încă nu a fost creat niciun grup de fluxuri + Aprecieri + Pagina SoundCloud Top 50 a fost eliminată + SoundCloud a eliminat Top 50. Fila corespunzătoare a fost eliminată din pagina principală. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0312466be..0d1091612 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -94,9 +94,6 @@ Выберите отображаемые предложения поиска Отключено Убирает звук в некоторых разрешениях - млн - млрд - тыс. Разрешение всплывающего окна Помнить последние размер и позицию всплывающего окна Предложения поиска @@ -636,7 +633,6 @@ Не удалось загрузить подписку \'%s\'. Ошибка загрузки подписки Открыть веб-сайт - %s указывает следующую причину: Аккаунт отключён Начиная с Android 10 поддерживается только «Storage Access Framework» Спрашивать, куда сохранять каждую загрузку @@ -846,4 +842,9 @@ Страница группы каналов Выберите группу кормов Группа кормов еще не создана + Поиск %1$s + Поиск %1$s (%2$s) + Лайки + Страница SoundCloud Top 50 удалена + SoundCloud прекратил поддерживать оригинальные чарты Top 50. Соответствующая вкладка была удалена с вашей главной страницы. diff --git a/app/src/main/res/values-ryu/strings.xml b/app/src/main/res/values-ryu/strings.xml index 53b1df7ec..d6db9dd44 100644 --- a/app/src/main/res/values-ryu/strings.xml +++ b/app/src/main/res/values-ryu/strings.xml @@ -83,9 +83,6 @@ reCAPTCHAようきゅうさびたん ブラック まじり - k - M - B ポップアップモードっしふぃらちゅん ポップアップモードっしふぃらちゅんがー \nきんぎんぬきょかがふぃちようでぃす @@ -625,7 +622,6 @@ オフ オン タブレットモード - %sやしがくぬりゆうていじ: ひょうじさん ていふぃんしち(しょう) かんふぃんしち(だい) diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index 4fe7f0787..f9bf2177a 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -216,8 +216,6 @@ ᱡᱟᱦᱟᱱ ᱡᱤᱱᱤᱥ ᱱᱚᱣᱟ ᱨᱮᱫᱚ ᱡᱟᱹᱥᱛᱤ ᱡᱟᱹᱥᱛᱤ ᱠᱨᱤᱠᱮᱴ ᱢᱮᱱᱟᱜᱼᱟ ᱾ ᱵᱷᱤᱰᱤᱭᱳ - k - M ᱥᱮᱞᱮᱫᱤᱭᱟᱹ ᱠᱚᱣᱟᱜ ᱞᱮᱠᱷᱟ ᱵᱟᱭ ᱦᱟᱹᱴᱤᱧ ᱟᱠᱟᱱᱟ ᱵᱟᱱᱩᱜ ᱧᱮᱞ ᱵᱷᱤᱰᱤᱭᱳ ᱵᱟᱹᱱᱩᱜᱼᱟ @@ -332,7 +330,6 @@ ᱪᱮᱯᱴᱟᱨᱥ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ ᱟᱢ ᱢᱤᱫ ᱯᱷᱤᱞ ᱢᱟᱱᱮᱡᱚᱨ ᱤᱱᱥᱴᱚᱞ ᱢᱮ ᱟᱨᱵᱟᱝ ᱰᱟᱩᱱᱞᱚᱰ ᱥᱤᱴᱤᱝ ᱨᱮ ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚ ᱞᱟᱹᱜᱤᱫ ᱯᱨᱚᱵᱷᱟᱣ ᱢᱮ\" ᱱᱚᱶᱟ ᱵᱷᱤᱰᱤᱭᱳ ᱫᱚ ᱭᱩᱴᱭᱩᱵᱽ ᱢᱤᱣᱡᱤᱠ ᱯᱨᱤᱢᱤᱭᱟᱢ ᱥᱮᱞᱮᱫᱤᱭᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱜᱮ ᱧᱟᱢᱚᱜᱼᱟ, ᱚᱱᱟᱛᱮ ᱱᱚᱶᱟ ᱫᱚ ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯ ᱦᱚᱛᱮᱛᱮ ᱵᱟᱝ ᱥᱴᱨᱤᱢ ᱟᱨ ᱵᱟᱝ ᱰᱟᱩᱱᱞᱳᱰ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ - %s ᱫᱚ ᱱᱚᱶᱟ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ: ᱚᱴᱚᱢᱟᱴᱤᱠ (ᱰᱤᱵᱟᱤᱥ ᱛᱷᱮᱢ) ᱟᱢᱟᱜ ᱯᱩᱭᱞᱩ ᱧᱤᱫᱟᱹ ᱛᱷᱤᱢ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ⁇ %s ᱟᱢ ᱞᱟᱛᱟᱨ ᱨᱮ ᱟᱢᱟᱜ ᱧᱤᱫᱟᱹ ᱪᱮᱛᱟᱱ ᱵᱟᱪᱷᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ ᱟ @@ -477,7 +474,6 @@ ᱱᱟᱣᱟ ᱟᱹᱨᱡᱤ ᱞᱟᱹᱜᱤᱫ ᱟᱹᱪᱩᱨ ᱢᱮ ᱚᱰᱤᱭᱳ ᱟᱨᱦᱚᱸ ᱯᱟᱲᱦᱟᱣ ᱢᱮ - ᱵᱤ ᱱᱤᱛᱚᱜ ᱵᱟᱪᱷᱟᱣ ᱟᱠᱟᱱ ᱴᱳᱜᱞ ᱥᱮᱵᱟ: ᱚᱵᱷᱤᱱᱮᱛᱟᱨ ᱵᱟᱹᱱᱩᱜᱼᱟ ᱚᱠᱚᱭ ᱦᱚᱸ ᱵᱟᱝ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 8d9a36f13..7ea235373 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -165,9 +165,6 @@ Perunu iscritu Allughe/istuda su servìtziu. Ischertadu como: - Mrd - Mlln - mìg Torra a proare Àudio Vìdeu @@ -613,7 +610,6 @@ Como podes ischertare su testu in intro de sa descritzione. Ammenta·ti chi sa pàgina diat pòdere trèmere e sos ligàmenes si diant pòdere no abèrrere cando ses in modalidade de ischerta. Incumintzende dae Android 10 petzi sa \'Storage Access Framework\' (Istrutura de Atzessu a s\'Archiviatzione) est suportada Aberi su situ web - %s frunit custa resone: Contu serradu Su recùperu lestru de sos flussos non frunit àteras informatziones in subra de custu. Su contu de s\'autore l\'ant serradu. diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index d0b30b932..f5833ed2e 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -13,12 +13,12 @@ Zdieľať s Použiť externý prehrávač videa Použiť externý prehrávač zvuku - Prevzaté video ukladať do - Prevzaté video súbory sú uložené tu + Adresár stiahnutých videí + Stiahnuté video súbory sú uložené tu Vyberte priečinok pre stiahnuté video Priečinok pre stiahnuté audio Vyberte priečinok pre audio súbory - Prevzaté zvukové súbory sú uložené tu + Stiahnuté zvukové súbory sú uložené tu Štandardné rozlíšenie Prehrať cez Kodi Nainštalovať chýbajúcu aplikáciu Kore\? @@ -74,17 +74,14 @@ Čakajte prosím… Skopírované do schránky Priečinok na sťahovanie zadefinujte prosím neskôr v nastaveniach - Prevzaté - Prevzaté + Sťahované súbory + Stiahnuté Hlásenie o chybe Aplikácia/UP zlyhalo Čo:\\nPožiadavka:\\nJazyk obsahu:\\nKrajina Obsahu:\\nJazyk Aplikácie:\\nSlužba:\\nČas v GMT:\\nBalík:\\nVerzia:\\nOS: Výzva reCAPTCHA Čierna Všetko - k - M - B Požiadavka reCAPTCHA Otvoriť vo vyskakovacom okne Tieto práva sú potrebné pre @@ -165,7 +162,7 @@ Nebol nájdený žiadny prehrávač pre stream (môžete si nainštalovať napr. VLC). Stiahnuť súbor streamu Zobraziť info - Uložené zoznamy + Uložené playlisty Pridať do Zobrazovať tip \"Pridať podržaním\" Zobrazí tip pri stlačení tlačidiel pozadia alebo vyskakovacieho okna videa \"Podrobnosti:\" @@ -179,8 +176,8 @@ Prepnúť na Video Importovať databázu Exportovať databázu - Prepíše aktuálnu históriu, odbery, zoznamy skladieb a (voliteľne) nastavenia - Exportuje históriu, odbery, zoznamy skladieb a nastavenia + Prepíše aktuálnu históriu, odbery, playlisty a (voliteľne) nastavenia + Exportuje históriu, odbery, playlisty a nastavenia Nepodarilo sa prehrať tento stream Pri prehrávaní došlo k chybe a nemožno pokračovať Zotavovanie po chybe v prehrávaní @@ -200,7 +197,7 @@ Naposledy prehrávané Najprehrávanejšie Obsah na hlavnej stránke - Prázdna strana + Prázdna stránka Kiosk Kanál Vyberte si kanál @@ -228,17 +225,17 @@ Vždy sa opýtať Získavajú sa informácie… Načítanie požadované obsahu - Nový zoznam skladieb + Nový playlist Premenovať Názov - Pridať do zoznamu skladieb - Nastaviť ako miniatúru zoznamu skladieb - Záložka zoznamu skladieb - Odstrániť Záložku - Odstrániť tento zoznam skladieb\? - Zoznam skladieb vytvorený + Pridať do playlistu + Nastaviť ako miniatúru playlistu + Pridať playlist medzi záložky + Odstrániť záložku + Odstrániť tento playlist? + Playlist bol vytvorený V playliste - Miniatúra zoznamu skladieb bola zmenená. + Miniatúra playlistu bola zmenená. Bez titulkov Prispôsobiť Vyplniť @@ -321,7 +318,7 @@ Bez limitu Limitovať rozlíšenie pri použití mobilných dát Kanály - Zoznamy skladieb + Playlisty Skladby Používatelia Pretáčať tiché pasáže @@ -513,8 +510,7 @@ \n \nMožno v budúcnosti sa to zmení. Áno aj čiastočne pozreté videá - Pozreté videá, ktoré ste pozreli pred a po ich pridaní do zoznamu, budú odstránené. -\nSte si istí ich odstránením zo zoznamu\? Táto operácia je nezvratná! + Pozreté videá, ktoré ste pozreli pred a po ich pridaní do playlistu, budú odstránené. \nSte si istí ich odstránením z playlistu? Táto operácia je nezvratná! Odstrániť pozreté videá\? Odstrániť pozreté Pôvodné texty zo služieb budú viditeľné v položkách streamu @@ -534,14 +530,14 @@ Nahlásiť na GitHub-e Kopírovať formátované hlásenie Zobrazujú sa výsledky pre: %s - Zoznamy skladieb + Zoznam playlistov Zobraziť iba nezoskupené odbery Nikdy Iba na WiFi Spustí automatické prehrávanie - %s Prehrať zoznam - Zatiaľ bez záložiek zoznamu - Vyberte zoznam skladieb + Žiadne záložky playlistov + Vyberte playlist Skontrolujte prosím, či rovnaká chyba už nie je nahlásená. Vytváranie duplicitných hlásení komplikuje prácu vývojárov. Nemožno rozpoznať URL. Otvoriť pomocou inej aplikácie\? Automatický rad @@ -622,7 +618,6 @@ Povolenie výberu textu v popise Teraz môžete vybrať text vo vnútri popisu. Upozorňujeme, že stránka môže blikať a odkazy nemusia byť klikateľné, keď je v režime výberu. Otvoriť webstránku - %s uvádza tento dôvod: Účet bol zrušený Tento rýchly režim neposkytuje viac informácií. Účet autora bol zrušený. @@ -720,7 +715,7 @@ Ak máte problémy s používaním aplikácie, určite si prečítajte tieto odpovede na časté otázky! Vypnutie trvalého náhľadu Kopírovanie do schránky zlyhalo - Zoznamy zobrazené šedou farbou už obsahujú danú položku. + Playlisty zobrazené šedou farbou už obsahujú danú položku. Karta Dotykom stiahnite %s Duplikát bol pridaný %d-krát @@ -747,7 +742,7 @@ Preferovať prehrávanie popisu Zvuk: %s Zvuková stopa - Chcete odstrániť všetky duplikátne streamy z tohoto zoznamu\? + Chcete odstrániť všetky duplikátne streamy z tohoto playlistu? Zobrazovať nasledovné streamy V tomto streame by už mala byť prítomná zvuková stopa Výber zvukovej stopy pre externé prehrávače @@ -786,7 +781,7 @@ Dozadu Opäť prehrať Karty, ktoré sa majú načítať pri aktualizácii informačného kanála. Táto možnosť nemá žiadny účinok, ak je kanál aktualizovaný pomocou rýchleho režimu. - Zoznamy skladieb + Playlisty Presunie výber hlavnej karty do spodnej časti Žiadne živé prenosy Prehrať @@ -844,4 +839,6 @@ Hľadať %1$s (%2$s) Hľadať %1$s Páči sa + SoundCloud Top 50 stránka odstránená + SoundCloud prestal používať pôvodnú Top 50. Daná stránka bola odstránená z hlavnej stránky. diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index cd41b43ba..474be8127 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -83,9 +83,6 @@ Predmet:\\nZahteva:\\nJezik vsebine:\\nDržava vsebine:\\nJezik aplikacije:\\nStoritev:\\nČas v GMT:\\nPaket:\\nRazličica:\\nRazličica OS: Črna Vse - k - mio - mrd Odpri v pojavnem načinu To dovoljenje je potrebno za odpiranje \nv pojavnem načinu @@ -459,4 +456,4 @@ Označi kot že ogledano Uporabite hitro nenatančno iskanje Sesuj predvajalnik - \ No newline at end of file + diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index 4730785fa..14668add5 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -242,9 +242,6 @@ Dad rukuntay ma jiraan Furo adeega, hada waxaa dooran: - B - K - M ku celi Dhagaysi Muuqaal @@ -611,7 +608,6 @@ Xidh caalamadinta qoraalka Fur caalamadinta qoraalka Hadda waad dooran kartaa qoraalka ku dhexjira faahfaahinta. Ogow markaad caalamdinayso qoraalka boggu wuu boodboodi karaa tixraacyadana waxay noqon karaan kuwo aan lagu dhufan karin. - %s wuxuu sheegayaa sababtan: Akoonka waa lajoojiyay Nidaamka dagdaga ah faahfaahin dheeraad ah uma hayo shaygan. Akoonka soosaaraha waa la joojiyay. diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 162ba5ada..4b9c2ac36 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -295,9 +295,6 @@ Nuk ka abonues Aktivizoje shërbimin, momentalisht e zgjedhur: - B - M - k Riprovo Audio Video @@ -601,7 +598,6 @@ Zgjidhni temën tuaj të preferuar të natës - %s Automatike (tema e pajisjes) Radio - %s e jep këtë arsye: Llogaria është mbyllur Kjo përmbajtje është private, kështu që nuk mund të luhet apo shkarkohet nga NewPipe. Kjo përmbajtje nuk është e disponueshme në shtetin tuaj. diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index fd245ffc4..512ef7469 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -83,9 +83,6 @@ Решите „reCAPTCHA“ задатак Црна Све - хиљ. - мил. - млрд. Отвори у искачућем облику Ова дозвола је потребна за \nотварање у искачућем режиму @@ -622,7 +619,6 @@ Омогући бирање текста унутар описа Онемогући бирање текста унутар описа Сада можете изабрати текст унутар описа. Имајте на уму да страница може треперети и да се на линкове можда неће моћи кликнути док сте у режиму избора. - %s даје овај разлог: Налог укинут Режим брзог фида не пружа више информација о овоме. Налог аутора је укинут. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 9b3efa1fc..b30395eb2 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -112,9 +112,6 @@ Video Ljud Försök igen - t - mn - md Inga prenumeranter %s prenumerant @@ -589,7 +586,6 @@ Hämtningen har startat Radio Detta innehåll är endast tillgängligt för användare som har betalat för det, så det kan inte strömmas eller hämtas av NewPipe. - %s anger detta skäl: Kontot avslutat Denna video är endast tillgänglig för YouTube Music Premium-medlemmar, så den kan inte strömmas eller hämtas av NewPipe. Detta innehåll är privat, så det kan inte strömmas eller hämtas av NewPipe. diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 906249376..f1d388393 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -245,7 +245,6 @@ உம் அபிமான பியர்டியூப் நிகழ்வுகளைத் தேர்ந்தெடு உள்ளடக்க இயல்பிருப்பு மொழி இயக்குதலைத் மறுதொடர் - நி நிகழ்வு ஏற்கனவே உள்ளது யூடியூபின் \"கட்டுப்பாடு பயன்முறை\"ஐ இயக்கு பாடல்கள் @@ -260,8 +259,6 @@ என்ன:\\nகோரிக்கை:\\nஉள்ளடக்க மொழி:\\nஉள்ளடக்க நாடு:\\nசெயலி மொழி:\\nசேவை:\\nGMT நேரம்:\\nசிப்பம்:\\nபதிப்பு:\\nOS பதிப்பு: காணொளியை இயக்கு, காலவளவு: கருத்தளிப்புகள் - - ப.ல இயக்கியைச் சிதை பட்டியல்களில் இயக்கக குறியட நிலைகாட்டிகளைக் காட்டு துணையியக்கியில் காணொளிகளை துவக்காதே, ஆனால் தானாக சுழற்றல் பூட்டப்பட்டிருந்தால் நேரடியாக முழுதிரைக்குத் திரும்பு. முழுதிரையை வெளியேறி நீங்கள் இன்னும் துணையியக்கியை அணுகலாம் @@ -431,7 +428,6 @@ பதிவிறக்க வரிசையை கட்டுப்படுத்துங்கள் ஒரு பதிவிறக்கம் ஒரே நேரத்தில் இயங்கும் உங்கள் சாதனத்தில் எந்த பயன்பாடும் இதைத் திறக்க முடியாது - %s இந்த காரணத்தை வழங்குகிறது: துணை சேனல் அவதாரங்கள் அவதாரங்கள் எக்சோப்ளேயர் இயல்புநிலை diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index d6b1a0169..a422dc996 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -77,9 +77,6 @@ వీడియో ఆడియో మళ్ళీ ప్రయత్నించు - కి - ఎం - బిలియన్ సభ్యులు లేరు %s సభ్యుడు diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index bcbbca0a4..c2cb4669e 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -165,9 +165,6 @@ วิดีโอ เสียง ลองอีกครั้ง - พัน - ล้าน - พันล้าน ไม่มีสมาชิกที่สมัครรับ %s บอกรับ diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml index c6edbe4c3..f25b86cc1 100644 --- a/app/src/main/res/values-ti/strings.xml +++ b/app/src/main/res/values-ti/strings.xml @@ -4,8 +4,8 @@ ኣብ መርበብ-ሓበሬታ ክፉት ውጽኢት ናይ፦ %s ንኽትጅምር ነቲ ምድላይ ምልክት ጠውቆ። - ኣብ %1$s ዝተሓትመ - ናይ ዥረት ተጻዋታይ ኣይተረኽበን። VLC ኣውርድ፧ + ዝተሓትመሉ ዕለት %1$s + ናይ ዥረት ተጻዋታይ ኣይተረኽበን። VLC ይውርድ፧ ሐራይ ቅጥዕታት \"%1$s\" ማለቱ ድዩ፧ @@ -26,7 +26,7 @@ ሰዓበ ናይ ደገ ቪድዮ ተጠቐም መጻወቲ ምውራድ - ኣይትጽንበሩ + ምስዓብ ኣቋርጽ ነቲ ኣብ\'ቲ ምልክታ ዝተርኣየ ናይ ቪድዮ ምስሊ ካብ 16:9 ናብ 1:1 ርሕቐት ኣቀራርባ ቅረጽ ምስ Kodi ተጻወት ድምር ምስ @@ -43,7 +43,7 @@ ናይ ደገ ድምጺ መጻወቲ ተጠቐም Android ሕብሪ ናይቲ መተሓሳሰቢ ብመሰረት እቲ ኣብቲ ንእሽቶ ስእሊ ዘሎ ቀንዲ ሕብሪ ከም ዝጥዕሞ ግበር (እዚ ኣብ ኩሉ መሳርሒታት ከምዘይርከብ ኣስተውዕል) ትሑዝ ፖፕኣፕ ድንቀት - ነፍሲ ወከፍ መፍለጢ ተግባር ኣብ ታሕቲ ብምጥዋቕ ኣርትዖ። ኣብቲ ውህሉል መተሓሳሰቢ ንኽርአ ክሳብ ሰለስተ ካብኣቶም ምረጽ፡ ኣብ የማናይ ሸነኽ ዘሎ ሳጹናት ብምጥቃም + ነፍሲ ወከፍ መፍለጢ ተግባር ኣብ ታሕቲ ብምጥዋቕ ኣርትዖ። ኣብቲ ውህሉል መተሓሳሰቢ ንኽርአ ክሳብ ሰለስተ ካብኣቶም ምረጽ፡ ኣብ የማናይ ሸነኽ ዘሎ ሳጹናት ብምጥቃም። ፖፕኣፕ ትሑዝ ድንቀት ዝወረዱ ናይ ተንቃሳቀሴ-ምስሌ ፋይላት ኣብዚ ይኽዘኑ @@ -56,7 +56,7 @@ ናይ ድምጺ ፋይል ኣራግፍ ምረጽ ዝጎደለ ኮረ ኣፕፕ ኣውራድ፧ ነባሪ ቅርጺ ድምጺ - ምዝገባ + እትስዕቦም Kodi ሚድያ ማእኸል ቪድዮ ንምጽዋት ዝሕግዝ ኣማራጺ ኣብቲ ውሱን መፍለጢ ንምርኣይ እንተበዝሐ ሰለስተ ተግባራት ክትመርጽ ትኽእል ኢኻ! ተንቃሳቀሴ-ምስሌ ፋይል ኣራግፍ @@ -70,7 +70,7 @@ እወ ኣይፋልን ጸሊም - ቆርበት + ቆርበት ኣርእስቲ ለይታው ቆርበት ናይ ፖፕኣፕ ባህርያት ዘክር ናይ ፖፕኣፕ ዝነበሮ ቦታ ዘክር @@ -88,7 +88,7 @@ ኵሉ መስመርት ዝርዝር-ጸወታ - ቅዳሒት + ቪዶ ተጠቀምቲ ፍጻመታት ደርፍታት diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 02794a620..d41b317e8 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -66,5 +66,4 @@ \nBuksan ang \"%1$s\" sa ayos ng app kung gusto mong makita ito. Mga Artista Nakalutang - \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index bb72e1e2b..b5756f943 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -88,9 +88,6 @@ Devre dışı Yorumunuz (İngilizce): Ayrıntılar: - b - M - M Bu izin, açılır pencere kipinde \naçmak için gereklidir reCAPTCHA formu @@ -164,9 +161,9 @@ Kurtarılamayan oynatıcı hatası oluştu Oynatıcı hatasından kurtarılıyor Ana sayfanın içeriği - Boş Sayfa - Kiosk Sayfası - Kanal Sayfası + Boş sayfa + Kiosk sayfası + Kanal sayfası Kanal seçin Henüz kanal aboneliği yok Kiosk seçin @@ -611,7 +608,6 @@ Açıklamadaki metni seçmeyi etkinleştir Artık, açıklamadaki metni seçebilirsiniz. Seçim kipindeyken sayfanın titreyebileceğini ve bağlantıların tıklanamayacağını unutmayın. Web sitesini aç - %s şu nedeni sağlıyor: Hesap sonlandırıldı Hızlı besleme kipi bununla ilgili daha çok bilgi sağlamıyor. Yazarın hesabı sonlandırılmış. @@ -828,4 +824,8 @@ Kanal küme sayfası Besleme kümesi oluşturulmadı Beğeni + %1$s İle Ara + %1$s İle Ara (%2$s) + SoundCloud Top 50 sayfası kaldırıldı + SoundCloud, özgün Top 50 listesini artık yayınlamıyor. İlgili sekme ana sayfanızdan kaldırıldı. diff --git a/app/src/main/res/values-tzm/strings.xml b/app/src/main/res/values-tzm/strings.xml index 31d89bdd5..a62b37fa3 100644 --- a/app/src/main/res/values-tzm/strings.xml +++ b/app/src/main/res/values-tzm/strings.xml @@ -113,7 +113,6 @@ Kkes Senulfu Senti - ifḍ Als-arem Imesli Avidyu @@ -178,4 +177,4 @@ Ilbumen Tilgamin n tɣuri Taɣuri tawurmant - \ No newline at end of file + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index ba1a43fee..ef3d7ca20 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -65,9 +65,6 @@ Відео Аудіо Повторити спробу - тис - млн - млрд Почати Пауза Видалити @@ -218,8 +215,8 @@ Відтворювалося найбільше Вміст на головній сторінці Порожня сторінка - Кіоск-сторінка - Канал + Сторінка кіоску + Сторінка каналу Обрати канал Немає підписок на канали Обрати кіоск @@ -627,7 +624,6 @@ Заборонити виділення тексту в описі Дозволити виділяти текст в описі Тепер можна виділяти текст в описі. Зауважте, що сторінка може мигати і посилання можуть не працювати в режимі виділення. - %s подає таку причину: Неможливо завантажити стрічку для «%s». Помилка завантаження стрічки Обліковий запис автора припинено. @@ -847,4 +843,8 @@ Виберіть групу каналів Групу каналів ще не створено Вподобання + Пошук %1$s + Пошук %1$s (%2$s) + Сторінку SoundCloud Top 50 видалено + SoundCloud припинив підтримку оригінальних чартів Топ-50. Відповідну вкладку видалено з вашої головної сторінки. diff --git a/app/src/main/res/values-und/strings.xml b/app/src/main/res/values-und/strings.xml index 94e4f9dd6..0bd3bf442 100644 --- a/app/src/main/res/values-und/strings.xml +++ b/app/src/main/res/values-und/strings.xml @@ -70,7 +70,6 @@ ویڈیو آڈیو فیر کرو - ہزار بݨاؤ بارے لائیسنس diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 1a434ab63..bb95e2811 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -146,9 +146,6 @@ ویڈیو آڈیو دوبارہ کوشش کریں - ہزار - دہ لاکھ - ارب کوئی صارفین نہیں %s صارف diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index c409e5198..81212a8a1 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -82,9 +82,6 @@ Video Âm thanh Thử lại - nghìn - triệu - tỉ Bắt đầu Dừng Xóa @@ -605,7 +602,6 @@ Tắt chọn văn bản trong mô tả Bật chọn văn bản trong mô tả Bây giờ bạn có thể chọn văn bản trong mô tả. Lưu ý rằng trang có thể nhấp nháy và các liên kết có thể không nhấn vào được trong khi ở chế độ chọn. - %s cung cấp lý do này: Tài khoản đã bị chấm dứt Chế độ nạp nhanh không cung cấp thêm thông tin về điều này. Tài khoản của tác giả đã bị chấm dứt. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b3b9ad384..6c556fb24 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -71,8 +71,6 @@ %s 次观看 - - 百万 开始 暂停 删除 @@ -153,7 +151,6 @@ 详细信息: 播放视频,时长: 视频上传者的头像缩略图 - 十亿 NewPipe 正在下载 请稍后在设置中设定下载目录 使用悬浮窗模式 @@ -198,7 +195,7 @@ 是否删除此条搜索历史记录? 主页面的显示内容 空白页 - 『时下流行』页-自定义 + Kiosk 页面 频道页 选择一个频道 尚未订阅频道 @@ -602,7 +599,6 @@ 启用简介中的文本选择功能 你现在可以选择简介中的文本,注意,在选择模式下,页面可能会闪烁,链接可能无法点击。 打开网站 - %s 提供这个原因: 账号被终止 快速 Feed 模式不提供关于这个的更多信息。 作者账号已被终止。 @@ -814,4 +810,8 @@ 选择一个订阅源组 尚未创建订阅源组 + 搜索%1$s + 搜索 %1$s (%2$s) + 移除了 SoundCloud Top 50 页面 + SoundCloud 已停止发布原创 Top 50 榜单。相应的标签页已从你的主页移除。 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 7d1c80e5f..c634fb74e 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -87,8 +87,6 @@ 全部嘢 App/界面閃退 經過:\\n請求:\\n內容語言:\\n內容國家:\\nApp 語言:\\n服務:\\nGMT 時間:\\n封裝:\\n版本:\\nOS 版本: - - 百萬 reCAPTCHA 考驗 以浮面模式開啟 \n有呢個權限至得 @@ -103,7 +101,6 @@ 不適用 抹走 最佳解像度 - 十億 關於 NewPipe 第三方版權協議 © %1$s %2$s 版權所有,根據 %3$s 嘅條款授權 @@ -489,7 +486,6 @@ NewPipe 仲未支援到呢樣。 \n \n希望未來會喺日後嘅版本支援啦。 - %s 話理由如下: 搵唔到合適嘅檔案總管進行呢個動作。 \n請安裝一個檔案管理程式,又或者試下喺下載設定度停用「%s」 搵唔到合適嘅檔案總管進行呢個動作。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index f04668779..55f0de5eb 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -82,9 +82,6 @@ 影片 音訊 重試 - - 百萬 - 十億 開始 暫停 刪除 @@ -602,7 +599,6 @@ 啟用選取描述中的文字 您現在可以選取描述中的文字了。請注意,在選取模式下,頁面可能會閃爍,連結也可能無法點擊。 開啟網站 - %s 提供了這個理由: 帳號已終止 快速 feed 模式不會提供更多資訊。 作者的帳號已被終止。 @@ -814,4 +810,8 @@ 選取 feed 群組 尚未建立 feed 群組 喜歡 + 搜尋 %1$s + 搜尋 %1$s (%2$s) + 已移除 SoundCloud Top 50 頁面 + SoundCloud 已停止原有的 Top 50 排行榜。對應的標籤已從您的首頁移除。 diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 5928ee193..d95d1270c 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -1192,6 +1192,8 @@ bn bn-bd bn-in + br + bs ca ckb cs @@ -1207,7 +1209,9 @@ fi fil fr + frc gl + gu he hi hr @@ -1219,23 +1223,31 @@ it ja jv + ka kab kmr + kn ko ku + la lt lv mk ml + mn + mr ms nb-no + nn ne nl nl-be + nqo oc or pa + pa-pk pl pt pt-br @@ -1254,7 +1266,11 @@ ta te th + ti + tl + tok tr + tt tzm uk ur @@ -1277,6 +1293,8 @@ বাংলা বাংলা (বাংলাদেশ) বাংলা (भारत) + Brezhoneg + Босански Català کوردیی سۆرانی Čeština @@ -1292,7 +1310,9 @@ Suomen kieli Wikang Filipino Français + Français (Louisiana) Galego + ગુજરાતી עברית हिन्दी Hrvatski @@ -1304,23 +1324,31 @@ Italiano 日本語 ꦧꦱꦗꦮ + ქართული Taqbaylit Kurmancî + ಕನ್ನಡ 한국어 کوردی + Latina Lietuvių kalba latviski македонски јазик മലയാളം + Монгол хэл + मराठी Bahasa Melayu Norsk bokmål - Nनेपाली + Norsk Nynorsk + नेपाली Nederlands (NL) Nederlands (BE) + ߒߞߏ Occitan ଓଡ଼ିଆ ਪੰਜਾਬੀ + ਪੰਜਾਬੀ (PK) Polski Português Português (BR) @@ -1339,7 +1367,11 @@ தமிழ் తెలుగు ไทย + ትግርኛ + Wikang Tagalog + Toki Pona Türkçe + Татар теле Tamaziɣt українська мова اردو diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ef3333a42..927e95dda 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -257,6 +257,8 @@ Restore defaults Do you want to restore defaults? Give permission to display over other apps + In order to use the Popup Player, please select %1$s in the following Android settings menu and enable %2$s. + “Allow display over other apps” NewPipe encountered an error, tap to report An error occurred, see the notification @@ -287,9 +289,9 @@ Video Audio Retry - k - M - B + %sK + %sM + %sB Toggle service, currently selected: No subscribers @@ -333,6 +335,8 @@ Pause Create Delete + Delete file + Delete entry Checksum Dismiss Rename @@ -752,7 +756,7 @@ This content is private, so it cannot be streamed or downloaded by NewPipe. This video is available only to YouTube Music Premium members, so it cannot be streamed or downloaded by NewPipe. Account terminated - %s provides this reason: + Account terminated\n\n%1$s provides this reason: %2$s This content is only available to users who have paid, so it cannot be streamed or downloaded by NewPipe. Featured Radio @@ -883,4 +887,16 @@ Importing %d subscription… Importing %d subscriptions… + YouTube combined trending removed + YouTube has discontinued the combined trending page as of 21st July 2025. NewPipe replaced the default trending page with the trending livestreams.\n\nYou can also select different trending pages in \"Settings > Content > Content of main page\". + Gaming trends + Trending podcasts + Trending movies and shows + Trending music + Entry deleted + HTTP error 403 received from server while playing, likely caused by streaming URL expiration or an IP ban + HTTP error %1$s received from server while playing + HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues + %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data). + This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\". diff --git a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt index f362963db..6f6ba671e 100644 --- a/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt +++ b/app/src/test/java/org/schabi/newpipe/settings/ImportExportManagerTest.kt @@ -25,11 +25,12 @@ import org.schabi.newpipe.streams.io.StoredFileHelper import us.shandian.giga.io.FileStream import java.io.File import java.io.ObjectInputStream +import java.nio.file.Paths import java.util.zip.ZipFile -import kotlin.io.path.Path 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 @@ -52,7 +53,7 @@ class ImportExportManagerTest { @Test fun `The settings must be exported successfully in the correct format`() { - val db = Path(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") @@ -87,21 +88,21 @@ class ImportExportManagerTest { @Test fun `Ensuring db directory existence must work`() { - val dir = createTempDirectory("newpipe_") - Assume.assumeTrue(dir.deleteIfExists()) - `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 = createTempDirectory("newpipe_") - `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 diff --git a/assets/NP logo v2.svg b/assets/NP logo v2.svg index 51fdf95de..88bf3d33d 100644 --- a/assets/NP logo v2.svg +++ b/assets/NP logo v2.svg @@ -1,21 +1 @@ - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/assets/newpipe_squircle.svg b/assets/newpipe_squircle.svg new file mode 100644 index 000000000..91358d421 --- /dev/null +++ b/assets/newpipe_squircle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/pure_logo.svg b/assets/pure_logo.svg index 4455b19c6..c4473eb9b 100644 --- a/assets/pure_logo.svg +++ b/assets/pure_logo.svg @@ -1,67 +1 @@ - - - -image/svg+xml - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/fastlane/metadata/android/ar/changelogs/1005.txt b/fastlane/metadata/android/ar/changelogs/1005.txt new file mode 100644 index 000000000..933c7286a --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/1005.txt @@ -0,0 +1,17 @@ +جديد +• أضف دعمًا لنظام Android Auto +• السماح لإعداد مجموعات التغذية كعلامات شاشة رئيسية +• [يوتيوب] شارك كقائمة تشغيل مؤقتة +• [SoundCloud] Leges Table Tab + +تحسن +• تلميحات شريط بحث أفضل +• عرض تاريخ التنزيل في التنزيلات +• استخدام Android 13 لكل لغة + +مُثَبَّت +• إصلاح ألوان النص المكسورة في الوضع المظلم +• [youtube] إصلاح قوائم التشغيل لا تحميل أكثر من 100 عنصر +• [youtube] إصلاح مقاطع الفيديو الموصى بها مفقودة +• إصلاح حوادث في عرض قائمة التاريخ +• إصلاح الطوابع الزمنية في ردود التعليقات diff --git a/fastlane/metadata/android/cs/changelogs/1005.txt b/fastlane/metadata/android/cs/changelogs/1005.txt new file mode 100644 index 000000000..2059e43de --- /dev/null +++ b/fastlane/metadata/android/cs/changelogs/1005.txt @@ -0,0 +1,17 @@ +Novinky +• Přidána podpora pro Android Auto. +• Možnost nastavit skupiny kanálů jako záložky na hlavní obrazovce. +• [YouTube] Sdílení jako dočasný seznam skladeb. +• [SoundCloud] Záložka Oblíbené kanály + +Vylepšeno +• Lepší nápověda pro vyhledávací lištu +• Zobrazení data stažení v sekci Stažené soubory +• Použití jazyka Android 13 pro jednotlivé aplikace + +Opraveno +• Oprava chybných barev textu v tmavém režimu +• [YouTube] Oprava seznamů skladeb, které nenačtou více než 100 položek +• [YouTube] Oprava chybějících doporučených videí +• Oprava pádů v zobrazení seznamu historie +• Oprava časových značek v odpovědích na komentáře diff --git a/fastlane/metadata/android/de/changelogs/1005.txt b/fastlane/metadata/android/de/changelogs/1005.txt new file mode 100644 index 000000000..ac9000b65 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/1005.txt @@ -0,0 +1,17 @@ +Neu +• Android Auto +• Feed-Gruppen als Hauptbildschirm-Tabs +• [YouTube] Teilen als temporäre Wiedergabeliste +• [SoundCloud] Gefällt-Kanal-Tab + +Verbessert +• Bessere Suchleisten-Hinweise +• Anzeige des Downloaddatums +• App-spezifische Spracheinstellungen (Android 13) + +Behoben +• Fehlerhafte Textfarben im dunklen Modus +• [YouTube] Wiedergabelisten laden nicht mehr als 100 Einträge +• [YouTube] Fehlende empfohlene Videos +• Abstürze in der Verlaufslisten-Ansicht +• Zeitstempel in Kommentarantworten diff --git a/fastlane/metadata/android/et/changelogs/1005.txt b/fastlane/metadata/android/et/changelogs/1005.txt new file mode 100644 index 000000000..685803416 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/1005.txt @@ -0,0 +1,17 @@ +Uus +• Android Auto tugi +• Meedivoogude grupid põhivaates +• [YouTube] Jagamine ajutise esitusloendina +• [SoundCloud] Meeldimiste kanali vahekaart + +Täiendatud +• Paremad otsinguvihjed +• Allalaadimise kuupäev vastavas vaates +• Android 13 puhul rakendusekohane keel + +Parandatud +• Tekstivärvid tumedas kujundused +• [YouTube] Esitusloend ei laadinud üle 100 kirje +• [YouTube] Puuduvad videosoovitused +• Ajaloovaate kookujooksmine +• Ajatemplid kommentaaride vastustes diff --git a/fastlane/metadata/android/fr/changelogs/1005.txt b/fastlane/metadata/android/fr/changelogs/1005.txt new file mode 100644 index 000000000..b7e5ff49f --- /dev/null +++ b/fastlane/metadata/android/fr/changelogs/1005.txt @@ -0,0 +1,17 @@ +Nouveau +• Prise en charge d'Android Auto +• Possibilité de définir des groupes de flux comme onglets de l'écran principal +• [YouTube] Partager comme playlist temporaire +• [SoundCloud] Onglet « J'aime » + +Amélioration +• Amélioration des astuces de la barre de recherche +• Affichage de la date de téléchargement dans Téléchargements +• Utilisation de la langue par application d'Android 13 + +Corrigé +• Correction des couleurs de texte défectueuses en mode sombre +• [YouTube] Correction des playlists ne chargeant pas plus de 100 éléments +• [YouTube] Correction des vidéos recommandées manquantes +• Correction des plantages dans la vue Historique +• Correction des horodatages dans les réponses aux commentaires diff --git a/fastlane/metadata/android/hi/changelogs/1005.txt b/fastlane/metadata/android/hi/changelogs/1005.txt new file mode 100644 index 000000000..495894e76 --- /dev/null +++ b/fastlane/metadata/android/hi/changelogs/1005.txt @@ -0,0 +1,17 @@ +नया ++ • Android Auto के लिए समर्थन जोड़ें ++ • फ़ीड समूहों को मुख्य स्क्रीन टैब के रूप में सेट करने की अनुमति दें ++ • [YouTube] अस्थायी प्लेलिस्ट के रूप में साझा करें ++ • [SoundCloud] "पसंद" चैनल टैब जोङी गई + +बेहतर किए ++ • खोज बार संकेत ++ • डाउनलोडस स्क्रीन में डाउनलोड की तारीख दिखाएं ++ • Android 13+ पर प्रति-ऐप भाषा का उपयोग करें + +फिक्स किए ++ • डार्क मोड में पाठ के रंग ठीक करें ++ • [YouTube] 100 से अधिक आइटम लोड नहीं करने वाली प्लेलिस्ट को ठीक करें ++ • [YouTube] अनुपलब्ध अनुशंसित वीडियो को ठीक करें ++ • इतिहास सूची दृश्य में क्रैश ठीक करें ++ • टिप्पणी के उत्तरों में टाइमस्टैम्प को ठीक करें diff --git a/fastlane/metadata/android/hu/changelogs/1005.txt b/fastlane/metadata/android/hu/changelogs/1005.txt new file mode 100644 index 000000000..6d6974391 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/1005.txt @@ -0,0 +1,17 @@ +Újdonság +- Android Auto támogatás hozzáadása +- A hírfolyamcsoportok főképernyőfülekként való beállítása +- [YouTube] Megosztás ideiglenes lejátszási listaként +- [SoundCloud] „Kedvelések” csatorna lap + +Továbbfejlesztve +- Jobb keresősáv-súgók +- Letöltési dátum megjelenítése a letöltések között +- Android 13 alkalmazásonkénti nyelv használata + +Javítva +- Törött szövegszínek javítása sötét módban +- [YouTube] A lejátszási listák 100-nál több elemet nem töltöttek be. +- [YouTube] Az ajánlott videók hiányának javítása +- Az előzmények listanézetben bekövetkező összeomlások javítása +- A hozzászólásválaszok időbélyegeinek javítása diff --git a/fastlane/metadata/android/it/changelogs/1005.txt b/fastlane/metadata/android/it/changelogs/1005.txt new file mode 100644 index 000000000..e70610077 --- /dev/null +++ b/fastlane/metadata/android/it/changelogs/1005.txt @@ -0,0 +1,17 @@ +Novità +• Ora NewPipe funziona su Android Auto +• Aggiungi feed di gruppi di canali nella schermata principale +• [YouTube] Condividi come playlist temporanea +• [SoundCloud] Mi piace nei canali + +Migliorie +• Indizi nella barra di ricerca +• Mostrata la data di scaricamento nella relativa sezione +• Uso della possibilità di scegliere la lingua dell'app, funzione nativa di Android 13 + +Correzioni +• Sistemati i colori del testo col tema scuro +• [YouTube] Ora si caricano più di 100 video nelle playlist +• [YouTube] Ripristinati i video consigliati +• Sistemati i crash nella cronologia +* Sistemato un errore nell'orario di inserimento di una risposta nei commenti diff --git a/fastlane/metadata/android/pa/changelogs/1005.txt b/fastlane/metadata/android/pa/changelogs/1005.txt new file mode 100644 index 000000000..f1492a1ac --- /dev/null +++ b/fastlane/metadata/android/pa/changelogs/1005.txt @@ -0,0 +1,17 @@ +ਨਵਾਂ ++ • Android Auto ਲਈ ਸਮਰਥਨ ਸ਼ਾਮਿਲ ਕਰੋ ++ • ਫੀਡ ਗਰੁੱਪਾਂ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਟੈਬਾਂ ਵਜੋਂ ਸੈੱਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ ++ • [YouTube] ਅਸਥਾਈ ਪਲੇਲਿਸਟ ਦੇ ਵਜੋਂ ਸਾਂਝਾ ਕਰੋ ++ • [SoundCloud] "ਪਸੰਦ" ਚੈਨਲ ਟੈਬ ਜੋੜੀ ਗਈ + +ਬਿਹਤਰ ਕੀਤੇ ++ • ਖੋਜ ਬਾਰ ਸੰਕੇਤ ++ • ਡਾਊਨਲੋਡਸ ਸੂਚੀ ਵਿੱਚ ਡਾਊਨਲੋਡ ਤਾਰੀਖ ਦਿਖਾਓ ++ • ਐਂਡਰਾਇਡ 13+ 'ਤੇ ਪ੍ਰਤੀ ਐਪ ਭਾਸ਼ਾ ਦੀ ਵਰਤੋਂ ਕਰੋ + +ਦਰੁਸਤ ਕੀਤੇ ++ • ਡਾਰਕ ਥੀਮਾਂ 'ਤੇ ਟੈਕਸਟ ਰੰਗਾਂ ਨੂੰ ਠੀਕ ਕਰੋ ++ • [YouTube] ਪਲੇਲਿਸਟਾਂ ਨੂੰ ਠੀਕ ਕਰੋ ਜੋ 100 ਤੋਂ ਵੱਧ ਆਈਟਮਾਂ ਲੋਡ ਨਹੀਂ ਕਰਦੀਆਂ ++ • [YouTube] ਸਿਫਾਰਸ਼ ਕੀਤੀਆਂ ਵੀਡੀਓਜ਼ ਦੀ ਅਣਉਪਲੱਬਧਤਾ ਨੂੰ ਠੀਕ ਕਰੋ ++ • ਇਤਿਹਾਸ ਸੂਚੀ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਕ੍ਰੈਸ਼ ਨੂੰ ਠੀਕ ਕਰੋ ++ • ਟਿੱਪਣੀਆਂ ਦੇ ਜਵਾਬ ਵਿੱਚ ਟਾਈਮਸਟੈਂਪਾਂ ਨੂੰ ਠੀਕ ਕਰੋ diff --git a/fastlane/metadata/android/pl/changelogs/1005.txt b/fastlane/metadata/android/pl/changelogs/1005.txt new file mode 100644 index 000000000..336aa1fa6 --- /dev/null +++ b/fastlane/metadata/android/pl/changelogs/1005.txt @@ -0,0 +1,17 @@ +Nowe +- Obsł. Android Auto +- Opcja ustawiania grup kanałów jako kart ekranu głównego +- [YouTube] Udostęp. jako tymczasowa playlista +- [SoundCloud] Karta kanału polubień + +Ulepszone +- Lepsze podpow. paska wysz. +- Wyśw. daty pobrania w Pobranych +- Używanie języka aplikacji w Androidzie 13 + +Naprawione +- Uszkodzone kolory tekstu w trybie ciemnym +- [YouTube] Playlisty ładujące maks. 100 pozycji +- [YouTube] Brakujące polecane wideo +- Awarie w widoku listy Historii +- Znaczniki czasu w odp. na komentarze diff --git a/fastlane/metadata/android/ru/changelogs/1005.txt b/fastlane/metadata/android/ru/changelogs/1005.txt new file mode 100644 index 000000000..3dead0f05 --- /dev/null +++ b/fastlane/metadata/android/ru/changelogs/1005.txt @@ -0,0 +1,17 @@ +Новое +• Поддержка Android Auto +• Использование групп лент как вкладок главного экрана +• [YouTube] Возможность поделиться временным плейлистом +• [SoundCloud] Вкладка канала "Лайки" + +Улучшено +• Поисковые подсказки +• Показ даты загрузки в Загрузках +• Установка языка для каждого приложения + +Исправлено +• Цвета текста в тёмном режиме +• [YouTube] Плейлисты не загружали более чем 100 элементов +• [YouTube] Отсутствующие рекомендованные видео +• Вылеты в Истории +• Временные метки в ответах на комментарии diff --git a/fastlane/metadata/android/sk/changelogs/1000.txt b/fastlane/metadata/android/sk/changelogs/1000.txt index 61cabd89d..29e2182f4 100644 --- a/fastlane/metadata/android/sk/changelogs/1000.txt +++ b/fastlane/metadata/android/sk/changelogs/1000.txt @@ -1,13 +1,13 @@ Vylepšené -- Umožnené kliknutie na popis playlistu, aby sa zobrazilo viac/menej obsahu -- [PeerTube] Automatické spracovanie odkazov inštancie `subscribeto.me` -- Spustenie prehrávania iba jednej položky v histórii +- Kliknutím na playlist sa zobrazí viac/menej obsahu +- [PeerTube] Automatické spracovanie odkazov z `subscribeto.me` +- Spustiť prehrávanie iba jednej položky v histórii Opravené -- Oprava viditeľnosti tlačidla RSS -- Oprava pádov náhľadu na paneli vyhľadávania -- Oprava pridania položky bez miniatúry do playlistu -- Oprava ukončenia okna sťahovania pred jeho zobrazením -- Oprava vyskakovacieho okna zoznamu súvisiacich položiek -- Oprava poradia v okne pridania do playlistu -- Úprava rozloženia položiek záložiek playlistu +- Viditeľnosť tlačidla RSS +- Pády pri náhľadoch +- Pridanie položky bez miniatúry do playlistu +- Zatváranie okna sťahovania pred jeho zobrazením +- Vyskakovacie okno zoznamu súvisiacich položiek +- Poradie v okne pridania do playlistu +- Rozloženie položiek záložiek playlistu diff --git a/fastlane/metadata/android/sk/changelogs/1005.txt b/fastlane/metadata/android/sk/changelogs/1005.txt new file mode 100644 index 000000000..8f2bfbfab --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/1005.txt @@ -0,0 +1,18 @@ +Novinky +• Pridaná podpora pre Android Auto +• Možnosť nastaviť skupiny kanálov ako hlavné karty na obrazovke +• [YouTube] Zdieľanie ako dočasný playlist + +• [SoundCloud] Karta „Páči sa“ kanál. + +Vylepšenia +• Lepšia nápoveda v paneli vyhľadávania +• Zobrazenie dátumu stiahnutia v sekcii „Stiahnuté” +• Použitie jazyka Android 13 pre jednotlivé aplikácie + +Opravy +• Oprava chybných farieb textu v tmavom režime +• [YouTube] Oprava playlistov, ktoré nenačítavajú viac ako 100 položiek +• [YouTube] Oprava chýbajúcich odporúčaných videí +• Oprava pádov v zobrazení zoznamu histórie +• Oprava časových značiek v odpovediach na komentáre. diff --git a/fastlane/metadata/android/sk/full_description.txt b/fastlane/metadata/android/sk/full_description.txt index 9de86c677..e1483d4fd 100644 --- a/fastlane/metadata/android/sk/full_description.txt +++ b/fastlane/metadata/android/sk/full_description.txt @@ -1 +1 @@ -NewPipe nepoužíva žiadne Google framework knižnice, ani YouTube API rozhranie. Len analyzuje web, aby získal potrebné informácie. Preto je možné túto aplikáciu používať na zariadeniach bez nainštalovaných Google služieb. Taktiež nepotrebujete účet na YouTube. Appka je FLOSS. +NewPipe nepoužíva Google framework knižnice, ani YouTube API rozhranie. Iba spracováva YouTube stránku aby získal potrebné informácie. Túto aplikáciu je teda možné používať na zariadeniach bez nainštalovaných Google služieb. Taktiež nepotrebujete účet na YouTube. Aplikácia je FLOSS. diff --git a/fastlane/metadata/android/ti/full_description.txt b/fastlane/metadata/android/ti/full_description.txt index f0afa90ab..f58ede5da 100644 --- a/fastlane/metadata/android/ti/full_description.txt +++ b/fastlane/metadata/android/ti/full_description.txt @@ -1 +1 @@ -ኒውፓይፕ ዝዀነ ይኹን ናይ ጎልጋል ቤተ-መጻሕፍቲ ወይ ናይ ዩቱብ ኤፒኢ ኣይጥቀምን ኢዩ። ነቲ ወብ ሳይት ዜድልዮ ሓበሬታ ንምርካብ ጥራይ እዩ ዚምርምሮ ። ስለዚ እዚ ኣፕሊኬሽን እዚ ብዘይ ናይ ጎልጋል ሰርቪስ ኣብ ኤለክትሮኒካዊ መሳርሒታት ክትጥቀመሉ ትኽእል ኢኻ ። ኒውፓይፕ ንምጥቃም እውን ናይ ዩቱብ ሕሳብ ኣየድልየካን ኢዩ እዚ ኸኣ FLOSS ኢዩ። +ኒውፓይፕ ዝዀነ ናይ ጉግል ቤተ-ምስሊታት ወይ ናይ ዩቱብ ኤፒኢይ ኣይጥቀምን ኢዩ። ነቲ መርበብ- ስፍራ ዜድልዮ ሓበሬታ ንምርካብ ጥራይ እዩ ዚምርምሮ። ስለዚ እዚ ኣፕሊኬሽን እዚ ብዘይ ናይ ጉግል ኣገልግሎት ኣብ ኤለክትሮኒካዊ-መሳርሒታት ክትጥቀመሉ ትኽእል ኢኻ። ኒውፓይፕ ንምጥቃም ናይ ዩቱብ ሕሳብ ኣየድልየካን ኢዩ፣ ኒውፓይፕ FLOSS ኢዩ። diff --git a/fastlane/metadata/android/ti/short_description.txt b/fastlane/metadata/android/ti/short_description.txt index f7f2099a1..107cabea0 100644 --- a/fastlane/metadata/android/ti/short_description.txt +++ b/fastlane/metadata/android/ti/short_description.txt @@ -1 +1 @@ -ብናጻ ፈኲስ ናይ ዩቱብ ግንባር ንኣንድሮይድ ። +ነጻ ናይ ዩቱብ ግንባር ንኣንድሮይድ። diff --git a/fastlane/metadata/android/uk/changelogs/1002.txt b/fastlane/metadata/android/uk/changelogs/1002.txt index a90cfff6b..f52bf43af 100644 --- a/fastlane/metadata/android/uk/changelogs/1002.txt +++ b/fastlane/metadata/android/uk/changelogs/1002.txt @@ -1 +1,4 @@ -Виправлено проблему невідтворюваності трансляцій YouTube +Виправлено помилку, через яку YouTube не відтворював жодної трансляції. + +У цьому випуску вирішено лише найактуальнішу помилку, яка перешкоджала завантаженню деталей відео YouTube. +Ми знаємо про інші проблеми, і незабаром випустимо окремий випуск для їх вирішення. diff --git a/fastlane/metadata/android/uk/changelogs/1003.txt b/fastlane/metadata/android/uk/changelogs/1003.txt index a90cfff6b..b969e091c 100644 --- a/fastlane/metadata/android/uk/changelogs/1003.txt +++ b/fastlane/metadata/android/uk/changelogs/1003.txt @@ -1 +1,6 @@ -Виправлено проблему невідтворюваності трансляцій YouTube +Це виправлення, яке виправляє помилки YouTube: +• [YouTube] Виправлено не завантаження інформації про відео, виправлено помилки HTTP 403 під час відтворення відео та відновлено відтворення деяких відео з віковими обмеженнями +• Виправлено незмінні розміри субтитрів +• Виправлено подвійне завантаження інформації під час відкриття потоку +• [Soundcloud] Видалено невідтворювані потоки, захищені DRM +• Оновлені переклади diff --git a/fastlane/metadata/android/uk/changelogs/1004.txt b/fastlane/metadata/android/uk/changelogs/1004.txt index a90cfff6b..1c2ec5d7f 100644 --- a/fastlane/metadata/android/uk/changelogs/1004.txt +++ b/fastlane/metadata/android/uk/changelogs/1004.txt @@ -1 +1,3 @@ -Виправлено проблему невідтворюваності трансляцій YouTube +У цьому випуску виправлено помилку, через яку YouTube відтворював лише 360p-потік. + +Зверніть увагу, що рішення, що використовується в цій версії, ймовірно, тимчасове, і в довгостроковій перспективі потрібно буде впровадити відеопротокол SABR, але учасники TeamNewPipe зараз зайняті, тому будь-яка допомога буде дуже вдячна! https://github.com/TeamNewPipe/NewPipe/issues/12248 diff --git a/fastlane/metadata/android/uk/changelogs/1005.txt b/fastlane/metadata/android/uk/changelogs/1005.txt new file mode 100644 index 000000000..474127f95 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/1005.txt @@ -0,0 +1,17 @@ +Нове +• Додано підтримку Android Auto +• Дозволено налаштування груп стрічок як вкладок головного екрана +• [YouTube] Поділитися як тимчасовий список відтворення +• [SoundCloud] Вкладка каналу «Вподобання» + +Покращено +• Кращі підказки в рядку пошуку +• Показувати дату завантаження в розділі «Завантаження» +• Використовувати мову Android 13 для кожної програми + +Виправлено +• Виправлено пошкоджені кольори тексту в темному режимі +• [YouTube] Виправлено списки відтворення, які не завантажували більше 100 елементів +• [YouTube] Виправлено відсутні рекомендовані відео +• Виправлено збої в перегляді історії +• Виправлено позначки часу у відповідях на коментарі diff --git a/fastlane/metadata/android/zh-Hans/changelogs/1001.txt b/fastlane/metadata/android/zh-Hans/changelogs/1001.txt new file mode 100644 index 000000000..cdebebf55 --- /dev/null +++ b/fastlane/metadata/android/zh-Hans/changelogs/1001.txt @@ -0,0 +1,6 @@ +改进 +- 始终允许在 Android 13以上系统上更改播放器通知首选项 + +修复 +- 修复了导出数据库/订阅时不会截断已存在的文件,从而可能导致导出损坏的问题 +- 修复了点击时间戳时,播放器从头开始恢复的问题 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4705f2298..bea2550ad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -58,7 +58,7 @@ teamnewpipe-nanojson = "e9d656ddb49a412a5a0a5d5ef20ca7ef09549996" # the corresponding commit hash, since JitPack sometimes deletes artifacts. # If there’s already a git hash, just add more of it to the end (or remove a letter) # to cause jitpack to regenerate the artifact. -teamnewpipe-newpipe-extractor = "7adbc48a0aa872c016b8ec089e278d5e12772054" +teamnewpipe-newpipe-extractor = "0023b22095a2d62a60cdfc87f4b5cd85c8b266c3" webkit = "1.9.0" work = "2.10.0"