Merge remote-tracking branch 'upstream/dev' into patch1_ui

This commit is contained in:
Vasiliy 2019-02-12 10:21:03 +02:00
commit 0cb5197ccf
No known key found for this signature in database
GPG Key ID: 9F74C4D2874D7523
147 changed files with 3387 additions and 1765 deletions

View File

@ -19,7 +19,7 @@ hasn't been reported/requested before
* We use English for development. Issues in other languages will be closed and ignored.
* Please only add *one* issue at a time. Do not put multiple issues into one thread.
* When reporting a bug please give us a context, and a description how to reproduce it.
* Issues that only contain a generated bug report, but no describtion might be closed.
* Issues that only contain a generated bug report, but no description might be closed.
## Bug Fixing
* If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to

View File

@ -12,7 +12,7 @@
<a href="https://www.bountysource.com/teams/newpipe" alt="Bountysource bounties"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"></a>
</p>
<hr>
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p>
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#updates">Updates</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p>
<p align="center"><a href="https://newpipe.schabi.org">Website</a> &bull; <a href="https://newpipe.schabi.org/blog/">Blog</a> &bull; <a href="https://newpipe.schabi.org/press/">Press</a></p>
<hr>
@ -73,6 +73,20 @@ NewPipe does not use any Google framework libraries, nor the YouTube API. Websit
* Show comments
* … and many more
## Updates
When a change to the NewPipe code occurs (due to either adding features or bug fixing), eventually a release will occur. These are in the format x.xx.x . In order to get this new version, you can:
* Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods.
* Download the APK from [releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it.
* Update via F-droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, then push the update to users.
When you install an APK from one of these options, it will be incompatible with an APK from one of the other options. This is due to different signing keys being used. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app, and are independent. F-Droid and GitHub use different signing keys, and building an APK debug excludes a key. The signing key issue is being discussed in issue [#1981](https://github.com/TeamNewPipe/NewPipe/issues/1981), and may be fixed by setting up our own repository on F-Droid.
In the meanwhile, if you want to switch sources for some reason (e.g. NewPipe's core functionality was broken and F-Droid doesn't have the update yet), we recommend following this procedure:
1. Back up your data via "Settings>Content>Export Database" so you keep your history, subscriptions, and playlists
2. Uninstall NewPipe
3. Download the APK from the new source and install it
4. Import the data from step 1 via "Settings>Content>Import Database"
## Contribution
Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome.
The more is done the better it gets!

View File

@ -8,18 +8,20 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 19
targetSdkVersion 28
versionCode 69
versionName "0.14.2"
versionCode 71
versionName "0.15.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
multiDexEnabled true
debuggable true
@ -33,6 +35,7 @@ android {
// but continue the build even when errors are found:
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
@ -54,7 +57,7 @@ dependencies {
exclude module: 'support-annotations'
})
implementation 'com.github.TeamNewPipe:NewPipeExtractor:91b1efc97e'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:79b0a19d1af'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.23.0'

View File

@ -1,13 +0,0 @@
package org.schabi.newpipe;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -119,7 +119,6 @@
<activity
android:name=".ReCaptchaActivity"
android:label="@string/reCaptchaActivity"/>
<activity android:name=".download.ExtSDDownloadFailedActivity" />
<provider
android:name="android.support.v4.content.FileProvider"
@ -210,6 +209,29 @@
<data android:pathPrefix="/user/"/>
</intent-filter>
<!-- Invidious filter -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:host="invidio.us"/>
<data android:host="www.invidio.us"/>
<!-- video prefix -->
<data android:pathPrefix="/embed/"/>
<data android:pathPrefix="/watch"/>
<!-- channel prefix -->
<data android:pathPrefix="/channel/"/>
<data android:pathPrefix="/user/"/>
<!-- playlist prefix -->
<data android:pathPrefix="/playlist"/>
</intent-filter>
<!-- Soundcloud filter -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>

View File

@ -1,5 +1,6 @@
package org.schabi.newpipe;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@ -65,6 +66,7 @@ import io.reactivex.plugins.RxJavaPlugins;
public class App extends Application {
protected static final String TAG = App.class.toString();
private RefWatcher refWatcher;
private static App app;
@SuppressWarnings("unchecked")
private static final Class<? extends ReportSenderFactory>[]
@ -88,6 +90,8 @@ public class App extends Application {
}
refWatcher = installLeakCanary();
app = this;
// Initialize settings first because others inits can use its values
SettingsActivity.initSettings(this);
@ -100,6 +104,9 @@ public class App extends Application {
ImageLoader.getInstance().init(getImageLoaderConfigurations(10, 50));
configureRxJavaErrorHandler();
// Check for new version
new CheckForNewAppVersionTask().execute();
}
protected Downloader getDownloader() {
@ -211,6 +218,31 @@ public class App extends Application {
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel);
setUpUpdateNotificationChannel(importance);
}
/**
* Set up notification channel for app update.
* @param importance
*/
@TargetApi(Build.VERSION_CODES.O)
private void setUpUpdateNotificationChannel(int importance) {
final String appUpdateId
= getString(R.string.app_update_notification_channel_id);
final CharSequence appUpdateName
= getString(R.string.app_update_notification_channel_name);
final String appUpdateDescription
= getString(R.string.app_update_notification_channel_description);
NotificationChannel appUpdateChannel
= new NotificationChannel(appUpdateId, appUpdateName, importance);
appUpdateChannel.setDescription(appUpdateDescription);
NotificationManager appUpdateNotificationManager
= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
appUpdateNotificationManager.createNotificationChannel(appUpdateChannel);
}
@Nullable
@ -226,4 +258,8 @@ public class App extends Application {
protected boolean isDisposedRxExceptionsReported() {
return false;
}
public static App getApp() {
return app;
}
}

View File

@ -0,0 +1,230 @@
package org.schabi.newpipe;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import org.json.JSONException;
import org.json.JSONObject;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* AsyncTask to check if there is a newer version of the NewPipe github apk available or not.
* If there is a newer version we show a notification, informing the user. On tapping
* the notification, the user will be directed to the download link.
*/
public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
private static final Application app = App.getApp();
private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
private static final String newPipeApiUrl = "https://newpipe.schabi.org/api/data.json";
private static final int timeoutPeriod = 30;
private SharedPreferences mPrefs;
private OkHttpClient client;
@Override
protected void onPreExecute() {
mPrefs = PreferenceManager.getDefaultSharedPreferences(app);
// Check if user has enabled/ disabled update checking
// and if the current apk is a github one or not.
if (!mPrefs.getBoolean(app.getString(R.string.update_app_key), true)
|| !isGithubApk()) {
this.cancel(true);
}
}
@Override
protected String doInBackground(Void... voids) {
// Make a network request to get latest NewPipe data.
if (client == null) {
client = new OkHttpClient
.Builder()
.readTimeout(timeoutPeriod, TimeUnit.SECONDS)
.build();
}
Request request = new Request.Builder()
.url(newPipeApiUrl)
.build();
try {
Response response = client.newCall(request).execute();
return response.body().string();
} catch (IOException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"app update API fail", R.string.app_ui_crash));
}
return null;
}
@Override
protected void onPostExecute(String response) {
// Parse the json from the response.
if (response != null) {
try {
JSONObject mainObject = new JSONObject(response);
JSONObject flavoursObject = mainObject.getJSONObject("flavors");
JSONObject githubObject = flavoursObject.getJSONObject("github");
JSONObject githubStableObject = githubObject.getJSONObject("stable");
String versionName = githubStableObject.getString("version");
String versionCode = githubStableObject.getString("version_code");
String apkLocationUrl = githubStableObject.getString("apk");
compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
} catch (JSONException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"could not parse app update JSON data", R.string.app_ui_crash));
}
}
}
/**
* Method to compare the current and latest available app version.
* If a newer version is available, we show the update notification.
* @param versionName
* @param apkLocationUrl
*/
private void compareAppVersionAndShowNotification(String versionName,
String apkLocationUrl,
String versionCode) {
int NOTIFICATION_ID = 2000;
if (BuildConfig.VERSION_CODE < Integer.valueOf(versionCode)) {
// A pending intent to open the apk location url in the browser.
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
PendingIntent pendingIntent
= PendingIntent.getActivity(app, 0, intent, 0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat
.Builder(app, app.getString(R.string.app_update_notification_channel_id))
.setSmallIcon(R.drawable.ic_newpipe_update)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setContentTitle(app.getString(R.string.app_update_notification_content_title))
.setContentText(app.getString(R.string.app_update_notification_content_text)
+ " " + versionName);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(app);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
}
/**
* Method to get the apk's SHA1 key.
* https://stackoverflow.com/questions/9293019/get-certificate-fingerprint-from-android-app#22506133
*/
private static String getCertificateSHA1Fingerprint() {
PackageManager pm = app.getPackageManager();
String packageName = app.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not find package info", R.string.app_ui_crash));
}
Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
InputStream input = new ByteArrayInputStream(cert);
CertificateFactory cf = null;
X509Certificate c = null;
try {
cf = CertificateFactory.getInstance("X509");
c = (X509Certificate) cf.generateCertificate(input);
} catch (CertificateException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Certificate error", R.string.app_ui_crash));
}
String hexString = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getEncoded());
hexString = byte2HexFormatted(publicKey);
} catch (NoSuchAlgorithmException ex1) {
ErrorActivity.reportError(app, ex1, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
} catch (CertificateEncodingException ex2) {
ErrorActivity.reportError(app, ex2, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
}
return hexString;
}
private static String byte2HexFormatted(byte[] arr) {
StringBuilder str = new StringBuilder(arr.length * 2);
for (int i = 0; i < arr.length; i++) {
String h = Integer.toHexString(arr[i]);
int l = h.length();
if (l == 1) h = "0" + h;
if (l > 2) h = h.substring(l - 2, l);
str.append(h.toUpperCase());
if (i < (arr.length - 1)) str.append(':');
}
return str.toString();
}
public static boolean isGithubApk() {
return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1);
}
}

View File

@ -457,7 +457,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
break;
case R.id.subtitle_button:
stream = subtitleStreamsAdapter.getItem(selectedSubtitleIndex);
location = NewPipeSettings.getVideoDownloadPath(context);// assume that subtitle & video go together
location = NewPipeSettings.getVideoDownloadPath(context);// assume that subtitle & video files go together
kind = 's';
break;
default:
@ -477,7 +477,6 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
final String finalFileName = fileName;
DownloadManagerService.checkForRunningMission(context, location, fileName, (listed, finished) -> {
// should be safe run the following code without "getActivity().runOnUiThread()"
if (listed) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.download_dialog_title)
@ -511,11 +510,11 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
if (secondaryStream != null) {
secondaryStreamUrl = secondaryStream.getStream().getUrl();
psName = selectedStream.getFormat() == MediaFormat.MPEG_4 ? Postprocessing.ALGORITHM_MP4_DASH_MUXER : Postprocessing.ALGORITHM_WEBM_MUXER;
psName = selectedStream.getFormat() == MediaFormat.MPEG_4 ? Postprocessing.ALGORITHM_MP4_MUXER : Postprocessing.ALGORITHM_WEBM_MUXER;
psArgs = null;
long videoSize = wrappedVideoStreams.getSizeInBytes((VideoStream) selectedStream);
// set nearLength, only, if both sizes are fetched or known. this probably does not work on weak internet connections
// set nearLength, only, if both sizes are fetched or known. this probably does not work on slow networks
if (secondaryStream.getSizeInBytes() > 0 && videoSize > 0) {
nearLength = secondaryStream.getSizeInBytes() + videoSize;
}

View File

@ -1,38 +0,0 @@
package org.schabi.newpipe.download;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
public class ExtSDDownloadFailedActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
}
@Override
protected void onStart() {
super.onStart();
new AlertDialog.Builder(this)
.setTitle(R.string.download_to_sdcard_error_title)
.setMessage(R.string.download_to_sdcard_error_message)
.setPositiveButton(R.string.yes, (DialogInterface dialogInterface, int i) -> {
NewPipeSettings.resetDownloadFolders(this);
finish();
})
.setNegativeButton(R.string.cancel, (DialogInterface dialogInterface, int i) -> {
dialogInterface.dismiss();
finish();
})
.create()
.show();
}
}

View File

@ -55,6 +55,7 @@ import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
@ -362,7 +363,8 @@ public class VideoDetailFragment
}
break;
case R.id.detail_controls_download:
if (PermissionHelper.checkStoragePermissions(activity, PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
if (PermissionHelper.checkStoragePermissions(activity,
PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
this.openDownloadDialog();
}
break;
@ -446,7 +448,6 @@ public class VideoDetailFragment
return;
}
//Log.d(TAG, "toggleExpandRelatedVideos() called with: info = [" + info + "], from = [" + INITIAL_RELATED_VIDEOS + "]");
for (int i = INITIAL_RELATED_VIDEOS; i < info.getRelatedStreams().size(); i++) {
InfoItem item = info.getRelatedStreams().get(i);
//Log.d(TAG, "i = " + i);
@ -519,7 +520,9 @@ public class VideoDetailFragment
infoItemBuilder.setOnStreamSelectedListener(new OnClickGesture<StreamInfoItem>() {
@Override
public void selected(StreamInfoItem selectedItem) {
selectAndLoadVideo(selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
selectAndLoadVideo(selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
}
@Override
@ -699,13 +702,13 @@ public class VideoDetailFragment
switch (id) {
case R.id.menu_item_share: {
if (currentInfo != null) {
shareUrl(currentInfo.getName(), currentInfo.getUrl());
shareUrl(currentInfo.getName(), currentInfo.getOriginalUrl());
}
return true;
}
case R.id.menu_item_openInBrowser: {
if (currentInfo != null) {
openUrlInBrowser(currentInfo.getUrl());
openUrlInBrowser(currentInfo.getOriginalUrl());
}
return true;
}
@ -742,10 +745,16 @@ public class VideoDetailFragment
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_external_video_player_key), false);
sortedVideoStreams = ListHelper.getSortedStreamVideosList(activity, info.getVideoStreams(), info.getVideoOnlyStreams(), false);
sortedVideoStreams = ListHelper.getSortedStreamVideosList(
activity,
info.getVideoStreams(),
info.getVideoOnlyStreams(),
false);
selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(activity, sortedVideoStreams);
final StreamItemAdapter<VideoStream, Stream> streamsAdapter = new StreamItemAdapter<>(activity, new StreamSizeWrapper<>(sortedVideoStreams, activity), isExternalPlayerEnabled);
final StreamItemAdapter<VideoStream, Stream> streamsAdapter =
new StreamItemAdapter<>(activity,
new StreamSizeWrapper<>(sortedVideoStreams, activity), isExternalPlayerEnabled);
spinnerToolbar.setAdapter(streamsAdapter);
spinnerToolbar.setSelection(selectedVideoStreamIndex);
spinnerToolbar.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@ -770,17 +779,17 @@ public class VideoDetailFragment
*/
protected final LinkedList<StackItem> stack = new LinkedList<>();
public void clearHistory() {
stack.clear();
}
public void pushToStack(int serviceId, String videoUrl, String name) {
if (DEBUG) {
Log.d(TAG, "pushToStack() called with: serviceId = [" + serviceId + "], videoUrl = [" + videoUrl + "], name = [" + name + "]");
Log.d(TAG, "pushToStack() called with: serviceId = ["
+ serviceId + "], videoUrl = [" + videoUrl + "], name = [" + name + "]");
}
if (stack.size() > 0 && stack.peek().getServiceId() == serviceId && stack.peek().getUrl().equals(videoUrl)) {
Log.d(TAG, "pushToStack() called with: serviceId == peek.serviceId = [" + serviceId + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
if (stack.size() > 0
&& stack.peek().getServiceId() == serviceId
&& stack.peek().getUrl().equals(videoUrl)) {
Log.d(TAG, "pushToStack() called with: serviceId == peek.serviceId = ["
+ serviceId + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
return;
} else {
Log.d(TAG, "pushToStack() wasn't equal");
@ -811,7 +820,11 @@ public class VideoDetailFragment
// Get stack item from the new top
StackItem peek = stack.peek();
selectAndLoadVideo(peek.getServiceId(), peek.getUrl(), !TextUtils.isEmpty(peek.getTitle()) ? peek.getTitle() : "");
selectAndLoadVideo(peek.getServiceId(),
peek.getUrl(),
!TextUtils.isEmpty(peek.getTitle())
? peek.getTitle()
: "");
return true;
}
@ -831,9 +844,10 @@ public class VideoDetailFragment
}
public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = ["
+ info + "], scrollToTop = [" + scrollToTop + "]");
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName());
setInitialData(info.getServiceId(), info.getUrl(), info.getName());
pushToStack(serviceId, url, name);
showLoading();
@ -1026,7 +1040,8 @@ public class VideoDetailFragment
private void showContentWithAnimation(long duration,
long delay,
@FloatRange(from = 0.0f, to = 1.0f) float translationPercent) {
@FloatRange(from = 0.0f, to = 1.0f)
float translationPercent) {
int translationY = (int) (getResources().getDisplayMetrics().heightPixels *
(translationPercent > 0.0f ? translationPercent : .06f));
@ -1134,7 +1149,7 @@ public class VideoDetailFragment
super.handleResult(info);
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName());
pushToStack(serviceId, url, name);
//pushToStack(serviceId, url, name);
animateView(thumbnailPlayButton, true, 200);
videoTitleTextView.setText(name);
@ -1185,11 +1200,13 @@ public class VideoDetailFragment
if (info.getDuration() > 0) {
detailDurationView.setText(Localization.getDurationString(info.getDuration()));
detailDurationView.setBackgroundColor(ContextCompat.getColor(activity, R.color.duration_background_color));
detailDurationView.setBackgroundColor(
ContextCompat.getColor(activity, R.color.duration_background_color));
animateView(detailDurationView, true, 100);
} else if (info.getStreamType() == StreamType.LIVE_STREAM) {
detailDurationView.setText(R.string.duration_live);
detailDurationView.setBackgroundColor(ContextCompat.getColor(activity, R.color.live_duration_background_color));
detailDurationView.setBackgroundColor(
ContextCompat.getColor(activity, R.color.live_duration_background_color));
animateView(detailDurationView, true, 100);
} else {
detailDurationView.setVisibility(View.GONE);
@ -1269,10 +1286,18 @@ public class VideoDetailFragment
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) {
Toast.makeText(activity,
R.string.could_not_setup_download_menu,
Toast.LENGTH_LONG).show();
e.printStackTrace();
ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
ServiceList.all()
.get(currentInfo
.getServiceId())
.getServiceInfo()
.getName(), "",
R.string.could_not_setup_download_menu);
ErrorActivity.reportError(getActivity(),
e,
getActivity().getClass(),
getActivity().findViewById(android.R.id.content), info);
}
}

View File

@ -233,10 +233,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
openRssFeed();
break;
case R.id.menu_item_openInBrowser:
openUrlInBrowser(url);
openUrlInBrowser(currentInfo.getOriginalUrl());
break;
case R.id.menu_item_share:
shareUrl(name, url);
shareUrl(name, currentInfo.getOriginalUrl());
break;
default:
return super.onOptionsItemSelected(item);

View File

@ -104,8 +104,13 @@ public class SearchFragment
// this three represet the current search query
@State
protected String searchString;
/**
* No content filter should add like contentfilter = all
* be aware of this when implementing an extractor.
*/
@State
protected String[] contentFilter;
protected String[] contentFilter = new String[0];
@State
protected String sortFilter;
@ -351,7 +356,7 @@ public class SearchFragment
|| (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchString)
? searchString
: searchEditText.getText().toString(), new String[0], "");
: searchEditText.getText().toString(), this.contentFilter, "");
} else {
if (searchEditText != null) {
searchEditText.setText("");
@ -752,6 +757,7 @@ public class SearchFragment
@Override
protected void loadMoreItems() {
if(nextPageUrl == null || nextPageUrl.isEmpty()) return;
isLoading.set(true);
showListFooter(true);
if (searchDisposable != null) searchDisposable.dispose();

View File

@ -1,7 +1,9 @@
package org.schabi.newpipe.player.playback;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import org.schabi.newpipe.player.BasePlayer;
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
@ -54,6 +56,12 @@ public class BasePlayerMediaSession implements MediaSessionCallback {
.setTitle(item.getTitle())
.setSubtitle(item.getUploader());
// set additional metadata for A2DP/AVRCP
Bundle additionalMetadata = new Bundle();
additionalMetadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, item.getUploader());
additionalMetadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, item.getDuration());
descriptionBuilder.setExtras(additionalMetadata);
final Uri thumbnailUri = Uri.parse(item.getThumbnailUrl());
if (thumbnailUri != null) descriptionBuilder.setIconUri(thumbnailUri);

View File

@ -93,15 +93,17 @@ public class VideoPlaybackResolver implements PlaybackResolver {
// Below are auxiliary media sources
// Create subtitle sources
for (final SubtitlesStream subtitle : info.getSubtitles()) {
final String mimeType = PlayerHelper.subtitleMimeTypesOf(subtitle.getFormat());
if (mimeType == null) continue;
if(info.getSubtitles() != null) {
for (final SubtitlesStream subtitle : info.getSubtitles()) {
final String mimeType = PlayerHelper.subtitleMimeTypesOf(subtitle.getFormat());
if (mimeType == null) continue;
final Format textFormat = Format.createTextSampleFormat(null, mimeType,
SELECTION_FLAG_AUTOSELECT, PlayerHelper.captionLanguageOf(context, subtitle));
final MediaSource textSource = dataSource.getSampleMediaSourceFactory()
.createMediaSource(Uri.parse(subtitle.getURL()), textFormat, TIME_UNSET);
mediaSources.add(textSource);
final Format textFormat = Format.createTextSampleFormat(null, mimeType,
SELECTION_FLAG_AUTOSELECT, PlayerHelper.captionLanguageOf(context, subtitle));
final MediaSource textSource = dataSource.getSampleMediaSourceFactory()
.createMediaSource(Uri.parse(subtitle.getURL()), textFormat, TIME_UNSET);
mediaSources.add(textSource);
}
}
if (mediaSources.size() == 1) {

View File

@ -4,6 +4,7 @@ import android.os.Bundle;
import android.support.v7.preference.Preference;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.CheckForNewAppVersionTask;
import org.schabi.newpipe.R;
public class MainSettingsFragment extends BasePreferenceFragment {
@ -13,6 +14,13 @@ public class MainSettingsFragment extends BasePreferenceFragment {
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.main_settings);
if (!CheckForNewAppVersionTask.isGithubApk()) {
final Preference update = findPreference(getString(R.string.update_pref_screen_key));
getPreferenceScreen().removePreference(update);
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
}
if (!DEBUG) {
final Preference debug = findPreference(getString(R.string.debug_pref_screen_key));
getPreferenceScreen().removePreference(debug);

View File

@ -0,0 +1,33 @@
package org.schabi.newpipe.settings;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.preference.Preference;
import org.schabi.newpipe.CheckForNewAppVersionTask;
import org.schabi.newpipe.R;
public class UpdateSettingsFragment extends BasePreferenceFragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String updateToggleKey = getString(R.string.update_app_key);
findPreference(updateToggleKey).setOnPreferenceChangeListener(updatePreferenceChange);
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.update_settings);
}
private Preference.OnPreferenceChangeListener updatePreferenceChange
= (preference, newValue) -> {
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key),
(boolean) newValue).apply();
return true;
};
}

View File

@ -31,6 +31,7 @@ import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.SuggestionExtractor;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
@ -46,6 +47,7 @@ import org.schabi.newpipe.report.UserAction;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Collections;
import java.util.List;
import io.reactivex.Maybe;
@ -95,10 +97,13 @@ public final class ExtractorHelper {
public static Single<List<String>> suggestionsFor(final int serviceId,
final String query) {
checkServiceId(serviceId);
return Single.fromCallable(() ->
NewPipe.getService(serviceId)
.getSuggestionExtractor()
.suggestionList(query));
return Single.fromCallable(() -> {
SuggestionExtractor extractor = NewPipe.getService(serviceId)
.getSuggestionExtractor();
return extractor != null
? extractor.suggestionList(query)
: Collections.emptyList();
});
}
public static Single<StreamInfo> getStreamInfo(final int serviceId,

View File

@ -31,6 +31,8 @@ public class KioskTranslator {
return c.getString(R.string.top_50);
case "New & hot":
return c.getString(R.string.new_and_hot);
case "conferences":
return c.getString(R.string.conferences);
default:
return kioskId;
}
@ -44,6 +46,8 @@ public class KioskTranslator {
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
case "New & hot":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
case "conferences":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
default:
return 0;
}

View File

@ -36,7 +36,6 @@ public class SecondaryStreamHelper<T extends Stream> {
* @return selected audio stream or null if a candidate was not found
*/
public static AudioStream getAudioStreamFor(@NonNull List<AudioStream> audioStreams, @NonNull VideoStream videoStream) {
// TODO: check if m4v and m4a selected streams are DASH compliant
switch (videoStream.getFormat()) {
case WEBM:
case MPEG_4:

View File

@ -24,9 +24,11 @@ public class ServiceHelper {
case 0:
return R.drawable.place_holder_youtube;
case 1:
return R.drawable.place_holder_circle;
return R.drawable.place_holder_cloud;
case 2:
return R.drawable.place_holder_gadse;
default:
return R.drawable.service;
return R.drawable.place_holder_circle;
}
}
@ -38,6 +40,8 @@ public class ServiceHelper {
case "playlists": return c.getString(R.string.playlists);
case "tracks": return c.getString(R.string.tracks);
case "users": return c.getString(R.string.users);
case "conferences" : return c.getString(R.string.conferences);
case "events" : return c.getString(R.string.events);
default: return filter;
}
}

View File

@ -28,7 +28,8 @@ import io.reactivex.schedulers.Schedulers;
import us.shandian.giga.util.Utility;
/**
* A list adapter for a list of {@link Stream streams}, currently supporting {@link VideoStream} and {@link AudioStream}.
* A list adapter for a list of {@link Stream streams},
* currently supporting {@link VideoStream}, {@link AudioStream} and {@link SubtitlesStream}
*/
public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseAdapter {
private final Context context;
@ -110,7 +111,10 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
}
}
} else if (stream instanceof AudioStream) {
qualityString = ((AudioStream) stream).getAverageBitrate() + "kbps";
AudioStream audioStream = ((AudioStream) stream);
qualityString = audioStream.getAverageBitrate() > 0
? audioStream.getAverageBitrate() + "kbps"
: audioStream.getFormat().getName();
} else if (stream instanceof SubtitlesStream) {
qualityString = ((SubtitlesStream) stream).getDisplayLanguageName();
if (((SubtitlesStream) stream).isAutoGenerated()) {
@ -154,8 +158,10 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
private final long[] streamSizes;
private final String unknownSize;
public StreamSizeWrapper(List<T> streamsList, Context context) {
this.streamsList = streamsList;
public StreamSizeWrapper(List<T> sL, Context context) {
this.streamsList = sL != null
? sL
: Collections.emptyList();
this.streamSizes = new long[streamsList.size()];
this.unknownSize = context == null ? "--.-" : context.getString(R.string.unknown_content);

View File

@ -137,7 +137,9 @@ public class ThemeHelper {
else if (selectedTheme.equals(darkTheme)) themeName = "DarkTheme";
themeName += "." + service.getServiceInfo().getName();
int resourceId = context.getResources().getIdentifier(themeName, "style", context.getPackageName());
int resourceId = context
.getResources()
.getIdentifier(themeName, "style", context.getPackageName());
if (resourceId > 0) {
return resourceId;

View File

@ -156,7 +156,6 @@ public class DownloadInitializer extends Thread {
if (retryCount++ > mMission.maxRetry) {
Log.e(TAG, "initializer failed", e);
mMission.running = false;
mMission.notifyError(e);
return;
}

View File

@ -39,7 +39,7 @@ public class DownloadMission extends Mission {
public static final int ERROR_SSL_EXCEPTION = 1004;
public static final int ERROR_UNKNOWN_HOST = 1005;
public static final int ERROR_CONNECT_HOST = 1006;
public static final int ERROR_POSTPROCESSING_FAILED = 1007;
public static final int ERROR_POSTPROCESSING = 1007;
public static final int ERROR_HTTP_NO_CONTENT = 204;
public static final int ERROR_HTTP_UNSUPPORTED_RANGE = 206;
@ -79,9 +79,12 @@ public class DownloadMission extends Mission {
public String postprocessingName;
/**
* Indicates if the post-processing algorithm is actually running, used to detect corrupt downloads
* Indicates if the post-processing state:
* 0: ready
* 1: running
* 2: completed
*/
public boolean postprocessingRunning;
public int postprocessingState;
/**
* Indicate if the post-processing algorithm works on the same file
@ -356,7 +359,7 @@ public class DownloadMission extends Mission {
finishCount++;
if (finishCount == currentThreadCount) {
if (errCode > ERROR_NOTHING) return;
if (errCode != ERROR_NOTHING) return;
if (DEBUG) {
Log.d(TAG, "onFinish" + (current + 1) + "/" + urls.length);
@ -382,19 +385,26 @@ public class DownloadMission extends Mission {
}
}
private void notifyPostProcessing(boolean processing) {
private void notifyPostProcessing(int state) {
if (DEBUG) {
Log.d(TAG, (processing ? "enter" : "exit") + " postprocessing on " + location + File.separator + name);
String action;
switch (state) {
case 1:
action = "Running";
break;
case 2:
action = "Completed";
break;
default:
action = "Failed";
}
Log.d(TAG, action + " postprocessing on " + location + File.separator + name);
}
synchronized (blockState) {
if (!processing) {
postprocessingName = null;
postprocessingArgs = null;
}
// don't return without fully write the current state
postprocessingRunning = processing;
postprocessingState = state;
Utility.writeToFile(metadata, DownloadMission.this);
}
}
@ -403,16 +413,30 @@ public class DownloadMission extends Mission {
* Start downloading with multiple threads.
*/
public void start() {
if (running || current >= urls.length) return;
if (running || isFinished()) return;
// ensure that the previous state is completely paused.
joinForThread(init);
for (Thread thread : threads) joinForThread(thread);
if (threads != null)
for (Thread thread : threads) joinForThread(thread);
enqueued = false;
running = true;
errCode = ERROR_NOTHING;
if (current >= urls.length && postprocessingName != null) {
runAsync(1, () -> {
if (doPostprocessing()) {
running = false;
deleteThisFromFile();
notify(DownloadManagerService.MESSAGE_FINISHED);
}
});
return;
}
if (blocks < 0) {
initializer();
return;
@ -420,7 +444,7 @@ public class DownloadMission extends Mission {
init = null;
if (threads.length < 1) {
if (threads == null || threads.length < 1) {
threads = new Thread[currentThreadCount];
}
@ -444,18 +468,18 @@ public class DownloadMission extends Mission {
public synchronized void pause() {
if (!running) return;
running = false;
recovered = true;
enqueued = false;
if (postprocessingRunning) {
if (isPsRunning()) {
if (DEBUG) {
Log.w(TAG, "pause during post-processing is not applicable.");
}
return;
}
if (init != null && init.isAlive()) {
running = false;
recovered = true;
enqueued = false;
if (init != null && Thread.currentThread() != init && init.isAlive()) {
init.interrupt();
synchronized (blockState) {
resetState();
@ -532,13 +556,36 @@ public class DownloadMission extends Mission {
mWritingToFile = false;
}
/**
* Indicates if the download if fully finished
*
* @return true, otherwise, false
*/
public boolean isFinished() {
return current >= urls.length && postprocessingName == null;
return current >= urls.length && (postprocessingName == null || postprocessingState == 2);
}
/**
* Indicates if the download file is corrupt due a failed post-processing
*
* @return {@code true} if this mission is unrecoverable
*/
public boolean isPsFailed() {
return postprocessingName != null && errCode == DownloadMission.ERROR_POSTPROCESSING && postprocessingThis;
}
/**
* Indicates if a post-processing algorithm is running
*
* @return true, otherwise, false
*/
public boolean isPsRunning() {
return postprocessingName != null && postprocessingState == 1;
}
public long getLength() {
long calculated;
if (postprocessingRunning) {
if (postprocessingState == 1) {
calculated = length;
} else {
calculated = offsets[current < offsets.length ? current : (offsets.length - 1)] + length;
@ -550,16 +597,19 @@ public class DownloadMission extends Mission {
}
private boolean doPostprocessing() {
if (postprocessingName == null) return true;
if (postprocessingName == null || postprocessingState == 2) return true;
notifyPostProcessing(1);
notifyProgress(0);
Thread.currentThread().setName("[" + TAG + "] post-processing = " + postprocessingName + " filename = " + name);
Exception exception = null;
try {
notifyPostProcessing(true);
notifyProgress(0);
Thread.currentThread().setName("[" + TAG + "] post-processing = " + postprocessingName + " filename = " + name);
Postprocessing algorithm = Postprocessing.getAlgorithm(postprocessingName, this);
algorithm.run();
Postprocessing
.getAlgorithm(postprocessingName, this)
.run();
} catch (Exception err) {
StringBuilder args = new StringBuilder(" ");
if (postprocessingArgs != null) {
@ -571,15 +621,21 @@ public class DownloadMission extends Mission {
}
Log.e(TAG, String.format("Post-processing failed. algorithm = %s args = [%s]", postprocessingName, args), err);
notifyError(ERROR_POSTPROCESSING_FAILED, err);
return false;
if (errCode == ERROR_NOTHING) errCode = ERROR_POSTPROCESSING;
exception = err;
} finally {
notifyPostProcessing(false);
notifyPostProcessing(errCode == ERROR_NOTHING ? 2 : 0);
}
if (errCode != ERROR_NOTHING) notify(DownloadManagerService.MESSAGE_ERROR);
if (errCode != ERROR_NOTHING) {
if (exception == null) exception = errObject;
notifyError(ERROR_POSTPROCESSING, exception);
return errCode == ERROR_NOTHING;
return false;
}
return true;
}
private boolean deleteThisFromFile() {

View File

@ -13,9 +13,7 @@ import us.shandian.giga.get.DownloadMission;
class Mp4DashMuxer extends Postprocessing {
Mp4DashMuxer(DownloadMission mission) {
super(mission);
recommendedReserve = 15360 * 1024;// 15 MiB
worksOnSameFile = true;
super(mission, 15360 * 1024/* 15 MiB */, true);
}
@Override

View File

@ -0,0 +1,136 @@
package us.shandian.giga.postprocessing;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaExtractor;
import android.media.MediaMuxer;
import android.media.MediaMuxer.OutputFormat;
import android.util.Log;
import static org.schabi.newpipe.BuildConfig.DEBUG;
import org.schabi.newpipe.streams.io.SharpStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import us.shandian.giga.get.DownloadMission;
class Mp4Muxer extends Postprocessing {
private static final String TAG = "Mp4Muxer";
private static final int NOTIFY_BYTES_INTERVAL = 128 * 1024;// 128 KiB
Mp4Muxer(DownloadMission mission) {
super(mission, 0, false);
}
@Override
int process(SharpStream out, SharpStream... sources) throws IOException {
File dlFile = mission.getDownloadedFile();
File tmpFile = new File(mission.location, mission.name.concat(".tmp"));
if (tmpFile.exists())
if (!tmpFile.delete()) return DownloadMission.ERROR_FILE_CREATION;
if (!tmpFile.createNewFile()) return DownloadMission.ERROR_FILE_CREATION;
FileInputStream source = null;
MediaMuxer muxer = null;
//noinspection TryFinallyCanBeTryWithResources
try {
source = new FileInputStream(dlFile);
MediaExtractor tracks[] = {
getMediaExtractor(source, mission.offsets[0], mission.offsets[1] - mission.offsets[0]),
getMediaExtractor(source, mission.offsets[1], mission.length - mission.offsets[1])
};
muxer = new MediaMuxer(tmpFile.getAbsolutePath(), OutputFormat.MUXER_OUTPUT_MPEG_4);
int tracksIndex[] = {
muxer.addTrack(tracks[0].getTrackFormat(0)),
muxer.addTrack(tracks[1].getTrackFormat(0))
};
ByteBuffer buffer = ByteBuffer.allocate(512 * 1024);// 512 KiB
BufferInfo info = new BufferInfo();
long written = 0;
long nextReport = NOTIFY_BYTES_INTERVAL;
muxer.start();
while (true) {
int done = 0;
for (int i = 0; i < tracks.length; i++) {
if (tracksIndex[i] < 0) continue;
info.set(0,
tracks[i].readSampleData(buffer, 0),
tracks[i].getSampleTime(),
tracks[i].getSampleFlags()
);
if (info.size >= 0) {
muxer.writeSampleData(tracksIndex[i], buffer, info);
written += info.size;
done++;
}
if (!tracks[i].advance()) {
// EOF reached
tracks[i].release();
tracksIndex[i] = -1;
}
if (written > nextReport) {
nextReport = written + NOTIFY_BYTES_INTERVAL;
super.progressReport(written);
}
}
if (done < 1) break;
}
// this part should not fail
if (!dlFile.delete()) return DownloadMission.ERROR_FILE_CREATION;
if (!tmpFile.renameTo(dlFile)) return DownloadMission.ERROR_FILE_CREATION;
return OK_RESULT;
} finally {
try {
if (muxer != null) {
muxer.stop();
muxer.release();
}
} catch (Exception err) {
if (DEBUG)
Log.e(TAG, "muxer stop/release failed", err);
}
if (source != null) {
try {
source.close();
} catch (IOException e) {
// nothing to do
}
}
// if the operation fails, delete the temporal file
if (tmpFile.exists()) {
//noinspection ResultOfMethodCallIgnored
tmpFile.delete();
}
}
}
private MediaExtractor getMediaExtractor(FileInputStream source, long offset, long length) throws IOException {
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(source.getFD(), offset, length);
extractor.selectTrack(0);
return extractor;
}
}

View File

@ -18,21 +18,21 @@ public abstract class Postprocessing {
public static final String ALGORITHM_TTML_CONVERTER = "ttml";
public static final String ALGORITHM_MP4_DASH_MUXER = "mp4D";
public static final String ALGORITHM_MP4_MUXER = "mp4";
public static final String ALGORITHM_WEBM_MUXER = "webm";
private static final String ALGORITHM_TEST_ALGO = "test";
public static Postprocessing getAlgorithm(String algorithmName, DownloadMission mission) {
if (null == algorithmName) {
throw new NullPointerException("algorithmName");
} else switch (algorithmName) {
case ALGORITHM_TTML_CONVERTER:
return new TttmlConverter(mission);
return new TtmlConverter(mission);
case ALGORITHM_MP4_DASH_MUXER:
return new Mp4DashMuxer(mission);
case ALGORITHM_MP4_MUXER:
return new Mp4Muxer(mission);
case ALGORITHM_WEBM_MUXER:
return new WebMMuxer(mission);
case ALGORITHM_TEST_ALGO:
return new TestAlgo(mission);
/*case "example-algorithm":
return new ExampleAlgorithm(mission);*/
default:
@ -52,71 +52,84 @@ public abstract class Postprocessing {
*/
public int recommendedReserve;
/**
* the download to post-process
*/
protected DownloadMission mission;
Postprocessing(DownloadMission mission) {
Postprocessing(DownloadMission mission, int recommendedReserve, boolean worksOnSameFile) {
this.mission = mission;
this.recommendedReserve = recommendedReserve;
this.worksOnSameFile = worksOnSameFile;
}
public void run() throws IOException {
File file = mission.getDownloadedFile();
CircularFile out = null;
ChunkFileInputStream[] sources = new ChunkFileInputStream[mission.urls.length];
int result;
long finalLength = -1;
try {
int i = 0;
for (; i < sources.length - 1; i++) {
sources[i] = new ChunkFileInputStream(file, mission.offsets[i], mission.offsets[i + 1], "rw");
}
sources[i] = new ChunkFileInputStream(file, mission.offsets[i], mission.getDownloadedFile().length(), "rw");
mission.done = 0;
mission.length = file.length();
int[] idx = {0};
CircularFile.OffsetChecker checker = () -> {
while (idx[0] < sources.length) {
/*
* WARNING: never use rewind() in any chunk after any writing (especially on first chunks)
* or the CircularFile can lead to unexpected results
*/
if (sources[idx[0]].isDisposed() || sources[idx[0]].available() < 1) {
idx[0]++;
continue;// the selected source is not used anymore
if (worksOnSameFile) {
ChunkFileInputStream[] sources = new ChunkFileInputStream[mission.urls.length];
try {
int i = 0;
for (; i < sources.length - 1; i++) {
sources[i] = new ChunkFileInputStream(file, mission.offsets[i], mission.offsets[i + 1], "rw");
}
sources[i] = new ChunkFileInputStream(file, mission.offsets[i], mission.getDownloadedFile().length(), "rw");
int[] idx = {0};
CircularFile.OffsetChecker checker = () -> {
while (idx[0] < sources.length) {
/*
* WARNING: never use rewind() in any chunk after any writing (especially on first chunks)
* or the CircularFile can lead to unexpected results
*/
if (sources[idx[0]].isDisposed() || sources[idx[0]].available() < 1) {
idx[0]++;
continue;// the selected source is not used anymore
}
return sources[idx[0]].getFilePointer() - 1;
}
return sources[idx[0]].getFilePointer() - 1;
return -1;
};
out = new CircularFile(file, 0, this::progressReport, checker);
result = process(out, sources);
if (result == OK_RESULT)
finalLength = out.finalizeFile();
} finally {
for (SharpStream source : sources) {
if (source != null && !source.isDisposed()) {
source.dispose();
}
}
return -1;
};
out = new CircularFile(file, 0, this::progressReport, checker);
mission.done = 0;
mission.length = file.length();
int result = process(out, sources);
if (result == OK_RESULT) {
long finalLength = out.finalizeFile();
mission.done = finalLength;
mission.length = finalLength;
} else {
mission.errCode = DownloadMission.ERROR_UNKNOWN_EXCEPTION;
mission.errObject = new RuntimeException("post-processing algorithm returned " + result);
}
if (result != OK_RESULT && worksOnSameFile) {
//noinspection ResultOfMethodCallIgnored
new File(mission.location, mission.name).delete();
}
} finally {
for (SharpStream source : sources) {
if (source != null && !source.isDisposed()) {
source.dispose();
if (out != null) {
out.dispose();
}
}
if (out != null) {
out.dispose();
}
} else {
result = process(null);
}
if (result == OK_RESULT) {
if (finalLength < 0) finalLength = file.length();
mission.done = finalLength;
mission.length = finalLength;
} else {
mission.errCode = DownloadMission.ERROR_UNKNOWN_EXCEPTION;
mission.errObject = new RuntimeException("post-processing algorithm returned " + result);
}
if (result != OK_RESULT && worksOnSameFile) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
}
@ -138,7 +151,7 @@ public abstract class Postprocessing {
return mission.postprocessingArgs[index];
}
private void progressReport(long done) {
void progressReport(long done) {
mission.done = done;
if (mission.length < mission.done) mission.length = mission.done;

View File

@ -1,54 +0,0 @@
package us.shandian.giga.postprocessing;
import android.util.Log;
import org.schabi.newpipe.streams.io.SharpStream;
import java.io.IOException;
import java.util.Random;
import us.shandian.giga.get.DownloadMission;
/**
* Algorithm for testing proposes
*/
class TestAlgo extends Postprocessing {
public TestAlgo(DownloadMission mission) {
super(mission);
worksOnSameFile = true;
recommendedReserve = 4096 * 1024;// 4 KiB
}
@Override
int process(SharpStream out, SharpStream... sources) throws IOException {
int written = 0;
int size = 5 * 1024 * 1024;// 5 MiB
byte[] buffer = new byte[8 * 1024];//8 KiB
mission.length = size;
Random rnd = new Random();
// only write random data
sources[0].dispose();
while (written < size) {
rnd.nextBytes(buffer);
int read = Math.min(buffer.length, size - written);
out.write(buffer, 0, read);
try {
Thread.sleep((int) (Math.random() * 10));
} catch (InterruptedException e) {
return -1;
}
written += read;
}
return Postprocessing.OK_RESULT;
}
}

View File

@ -18,13 +18,12 @@ import us.shandian.giga.postprocessing.io.SharpInputStream;
/**
* @author kapodamy
*/
class TttmlConverter extends Postprocessing {
private static final String TAG = "TttmlConverter";
class TtmlConverter extends Postprocessing {
private static final String TAG = "TtmlConverter";
TttmlConverter(DownloadMission mission) {
super(mission);
recommendedReserve = 0;// due how XmlPullParser works, the xml is fully loaded on the ram
worksOnSameFile = true;
TtmlConverter(DownloadMission mission) {
// due how XmlPullParser works, the xml is fully loaded on the ram
super(mission, 0, true);
}
@Override
@ -41,7 +40,7 @@ class TttmlConverter extends Postprocessing {
out,
getArgumentAt(1, "true").equals("true"),
getArgumentAt(2, "true").equals("true")
);
);
} catch (Exception err) {
Log.e(TAG, "subtitle parse failed", err);
@ -56,7 +55,7 @@ class TttmlConverter extends Postprocessing {
} else if (err instanceof XPathExpressionException) {
return 7;
}
return 8;
}

View File

@ -15,9 +15,7 @@ import us.shandian.giga.get.DownloadMission;
class WebMMuxer extends Postprocessing {
WebMMuxer(DownloadMission mission) {
super(mission);
recommendedReserve = 2048 * 1024;// 2 MiB
worksOnSameFile = true;
super(mission, 2048 * 1024/* 2 MiB */, true);
}
@Override

View File

@ -141,15 +141,18 @@ public class DownloadManager {
File dl = mis.getDownloadedFile();
boolean exists = dl.exists();
if (mis.postprocessingRunning && mis.postprocessingThis) {
// Incomplete post-processing results in a corrupted download file
// because the selected algorithm works on the same file to save space.
if (!dl.delete()) {
Log.w(TAG, "Unable to delete incomplete download file: " + sub.getPath());
if (mis.isPsRunning()) {
if (mis.postprocessingThis) {
// Incomplete post-processing results in a corrupted download file
// because the selected algorithm works on the same file to save space.
if (exists && dl.isFile() && !dl.delete())
Log.w(TAG, "Unable to delete incomplete download file: " + sub.getPath());
exists = true;
}
exists = true;
mis.postprocessingRunning = false;
mis.errCode = DownloadMission.ERROR_POSTPROCESSING_FAILED;
mis.postprocessingState = 0;
mis.errCode = DownloadMission.ERROR_POSTPROCESSING;
mis.errObject = new RuntimeException("stopped unexpectedly");
} else if (exists && !dl.isFile()) {
// probably a folder, this should never happens
@ -332,7 +335,7 @@ public class DownloadManager {
int count = 0;
synchronized (this) {
for (DownloadMission mission : mMissionsPending) {
if (mission.running && mission.errCode != DownloadMission.ERROR_POSTPROCESSING_FAILED && !mission.isFinished())
if (mission.running && !mission.isFinished() && !mission.isPsFailed())
count++;
}
}
@ -471,7 +474,7 @@ public class DownloadManager {
boolean flag = false;
synchronized (this) {
for (DownloadMission mission : mMissionsPending) {
if (mission.running && mission.isFinished() && !mission.postprocessingRunning) {
if (mission.running && !mission.isFinished() && !mission.isPsRunning()) {
flag = true;
mission.pause();
}
@ -528,6 +531,8 @@ public class DownloadManager {
ArrayList<Object> current;
ArrayList<Mission> hidden;
boolean hasFinished = false;
private MissionIterator() {
hidden = new ArrayList<>(2);
current = null;
@ -563,6 +568,7 @@ public class DownloadManager {
list.addAll(finished);
}
hasFinished = finished.size() > 0;
return list;
}
@ -637,6 +643,10 @@ public class DownloadManager {
hidden.remove(mission);
}
public boolean hasFinishedMissions() {
return hasFinished;
}
@Override
public int getOldListSize() {

View File

@ -50,6 +50,7 @@ import us.shandian.giga.ui.common.Deleter;
import us.shandian.giga.ui.common.ProgressDrawable;
import us.shandian.giga.util.Utility;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static us.shandian.giga.get.DownloadMission.ERROR_CONNECT_HOST;
@ -59,7 +60,7 @@ import static us.shandian.giga.get.DownloadMission.ERROR_HTTP_UNSUPPORTED_RANGE;
import static us.shandian.giga.get.DownloadMission.ERROR_NOTHING;
import static us.shandian.giga.get.DownloadMission.ERROR_PATH_CREATION;
import static us.shandian.giga.get.DownloadMission.ERROR_PERMISSION_DENIED;
import static us.shandian.giga.get.DownloadMission.ERROR_POSTPROCESSING_FAILED;
import static us.shandian.giga.get.DownloadMission.ERROR_POSTPROCESSING;
import static us.shandian.giga.get.DownloadMission.ERROR_SSL_EXCEPTION;
import static us.shandian.giga.get.DownloadMission.ERROR_UNKNOWN_EXCEPTION;
import static us.shandian.giga.get.DownloadMission.ERROR_UNKNOWN_HOST;
@ -67,7 +68,8 @@ import static us.shandian.giga.get.DownloadMission.ERROR_UNKNOWN_HOST;
public class MissionAdapter extends Adapter<ViewHolder> {
private static final SparseArray<String> ALGORITHMS = new SparseArray<>();
private static final String TAG = "MissionAdapter";
private static final String UNDEFINED_SPEED = "--.-%";
private static final String UNDEFINED_PROGRESS = "--.-%";
static {
ALGORITHMS.put(R.id.md5, "MD5");
@ -158,7 +160,7 @@ public class MissionAdapter extends Adapter<ViewHolder> {
str = R.string.missions_header_pending;
} else {
str = R.string.missions_header_finished;
setClearButtonVisibility(true);
if (mClear != null) mClear.setVisible(true);
}
((ViewHolderHeader) view).header.setText(str);
@ -178,7 +180,7 @@ public class MissionAdapter extends Adapter<ViewHolder> {
if (h.item.mission instanceof DownloadMission) {
DownloadMission mission = (DownloadMission) item.mission;
String length = Utility.formatBytes(mission.getLength());
if (mission.running && !mission.postprocessingRunning) length += " --.- kB/s";
if (mission.running && !mission.isPsRunning()) length += " --.- kB/s";
h.size.setText(length);
h.pause.setTitle(mission.unknownLength ? R.string.stop : R.string.pause);
@ -238,11 +240,10 @@ public class MissionAdapter extends Adapter<ViewHolder> {
}
if (hasError) {
if (Float.isNaN(progress) || Float.isInfinite(progress))
h.progress.setProgress(1f);
h.progress.setProgress(isNotFinite(progress) ? 1f : progress);
h.status.setText(R.string.msg_error);
} else if (Float.isNaN(progress) || Float.isInfinite(progress)) {
h.status.setText(UNDEFINED_SPEED);
} else if (isNotFinite(progress)) {
h.status.setText(UNDEFINED_PROGRESS);
} else {
h.status.setText(String.format("%.2f%%", progress * 100));
h.progress.setProgress(progress);
@ -251,11 +252,11 @@ public class MissionAdapter extends Adapter<ViewHolder> {
long length = mission.getLength();
int state;
if (mission.errCode == ERROR_POSTPROCESSING_FAILED) {
if (mission.isPsFailed()) {
state = 0;
} else if (!mission.running) {
state = mission.enqueued ? 1 : 2;
} else if (mission.postprocessingRunning) {
} else if (mission.isPsRunning()) {
state = 3;
} else {
state = 0;
@ -322,6 +323,9 @@ public class MissionAdapter extends Adapter<ViewHolder> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
}
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
}
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.v(TAG, "Starting intent: " + intent);
if (intent.resolveActivity(mContext.getPackageManager()) != null) {
@ -406,7 +410,7 @@ public class MissionAdapter extends Adapter<ViewHolder> {
case ERROR_CONNECT_HOST:
str.append(mContext.getString(R.string.error_connect_host));
break;
case ERROR_POSTPROCESSING_FAILED:
case ERROR_POSTPROCESSING:
str.append(mContext.getString(R.string.error_postprocessing_failed));
case ERROR_UNKNOWN_EXCEPTION:
break;
@ -437,7 +441,6 @@ public class MissionAdapter extends Adapter<ViewHolder> {
public void clearFinishedDownloads() {
mDownloadManager.forgetFinishedDownloads();
applyChanges();
setClearButtonVisibility(false);
}
private boolean handlePopupItem(@NonNull ViewHolderItem h, @NonNull MenuItem option) {
@ -447,6 +450,7 @@ public class MissionAdapter extends Adapter<ViewHolder> {
if (mission != null) {
switch (id) {
case R.id.start:
h.status.setText(UNDEFINED_PROGRESS);
h.state = -1;
h.size.setText(Utility.formatBytes(mission.getLength()));
mDownloadManager.resumeMission(mission);
@ -506,11 +510,7 @@ public class MissionAdapter extends Adapter<ViewHolder> {
mIterator.end();
checkEmptyMessageVisibility();
if (mIterator.getOldListSize() > 0) {
int lastItemType = mIterator.getSpecialAtItem(mIterator.getOldListSize() - 1);
setClearButtonVisibility(lastItemType == DownloadManager.SPECIAL_FINISHED);
}
if (mClear != null) mClear.setVisible(mIterator.hasFinishedMissions());
}
public void forceUpdate() {
@ -529,17 +529,10 @@ public class MissionAdapter extends Adapter<ViewHolder> {
}
public void setClearButton(MenuItem clearButton) {
if (mClear == null) {
int lastItemType = mIterator.getSpecialAtItem(mIterator.getOldListSize() - 1);
clearButton.setVisible(lastItemType == DownloadManager.SPECIAL_FINISHED);
}
if (mClear == null) clearButton.setVisible(mIterator.hasFinishedMissions());
mClear = clearButton;
}
private void setClearButtonVisibility(boolean flag) {
mClear.setVisible(flag);
}
private void checkEmptyMessageVisibility() {
int flag = mIterator.getOldListSize() > 0 ? View.GONE : View.VISIBLE;
if (mEmptyMessage.getVisibility() != flag) mEmptyMessage.setVisibility(flag);
@ -596,6 +589,10 @@ public class MissionAdapter extends Adapter<ViewHolder> {
}
}
private boolean isNotFinite(Float value) {
return Float.isNaN(value) || Float.isInfinite(value);
}
class ViewHolderItem extends RecyclerView.ViewHolder {
DownloadManager.MissionItem item;
@ -667,7 +664,7 @@ public class MissionAdapter extends Adapter<ViewHolder> {
DownloadMission mission = item.mission instanceof DownloadMission ? (DownloadMission) item.mission : null;
if (mission != null) {
if (!mission.postprocessingRunning) {
if (!mission.isPsRunning()) {
if (mission.running) {
pause.setVisible(true);
} else {
@ -678,8 +675,10 @@ public class MissionAdapter extends Adapter<ViewHolder> {
queue.setChecked(mission.enqueued);
delete.setVisible(true);
start.setVisible(mission.errCode != ERROR_POSTPROCESSING_FAILED);
queue.setVisible(mission.errCode != ERROR_POSTPROCESSING_FAILED);
boolean flag = !mission.isPsFailed();
start.setVisible(flag);
queue.setVisible(flag);
}
}
} else {

View File

@ -20,6 +20,7 @@ import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ThemeHelper;
import us.shandian.giga.service.DownloadManager;
import us.shandian.giga.service.DownloadManagerService;
@ -40,7 +41,7 @@ public class MissionsFragment extends Fragment {
private MissionAdapter mAdapter;
private GridLayoutManager mGridManager;
private LinearLayoutManager mLinearManager;
private Context mActivity;
private Context mContext;
private DMBinder mBinder;
private Bundle mBundle;
@ -53,7 +54,7 @@ public class MissionsFragment extends Fragment {
mBinder = (DownloadManagerService.DMBinder) binder;
mBinder.clearDownloadNotifications();
mAdapter = new MissionAdapter(mActivity, mBinder.getDownloadManager(), mClear, mEmpty);
mAdapter = new MissionAdapter(mContext, mBinder.getDownloadManager(), mClear, mEmpty);
mAdapter.deleterLoad(mBundle, getView());
mBundle = null;
@ -79,17 +80,17 @@ public class MissionsFragment extends Fragment {
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
mLinear = mPrefs.getBoolean("linear", false);
mActivity = getActivity();
//mContext = getActivity().getApplicationContext();
mBundle = savedInstanceState;
// Bind the service
mActivity.bindService(new Intent(mActivity, DownloadManagerService.class), mConnection, Context.BIND_AUTO_CREATE);
mContext.bindService(new Intent(mContext, DownloadManagerService.class), mConnection, Context.BIND_AUTO_CREATE);
// Views
mEmpty = v.findViewById(R.id.list_empty_view);
mList = v.findViewById(R.id.mission_recycler);
// Init
// Init layouts managers
mGridManager = new GridLayoutManager(getActivity(), SPAN_SIZE);
mGridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
@ -103,7 +104,6 @@ public class MissionsFragment extends Fragment {
}
}
});
mLinearManager = new LinearLayoutManager(getActivity());
setHasOptionsMenu(true);
@ -115,13 +115,13 @@ public class MissionsFragment extends Fragment {
* Added in API level 23.
*/
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
public void onAttach(Context context) {
super.onAttach(context);
// Bug: in api< 23 this is never called
// so mActivity=null
// so app crashes with nullpointer exception
mActivity = activity;
// so app crashes with null-pointer exception
mContext = context;
}
/**
@ -132,7 +132,7 @@ public class MissionsFragment extends Fragment {
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
mContext = activity.getApplicationContext();
}
@ -143,7 +143,7 @@ public class MissionsFragment extends Fragment {
mBinder.removeMissionEventListener(mAdapter.getMessenger());
mBinder.enableNotifications(true);
mActivity.unbindService(mConnection);
mContext.unbindService(mConnection);
mAdapter.deleterDispose(null);
mBinder = null;
@ -189,7 +189,15 @@ public class MissionsFragment extends Fragment {
mList.setAdapter(mAdapter);
if (mSwitch != null) {
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
boolean isLight = ThemeHelper.isLightThemeSelected(mContext);
int icon;
if (mLinear)
icon = isLight ? R.drawable.ic_list_black_24dp : R.drawable.ic_list_white_24dp;
else
icon = isLight ? R.drawable.ic_grid_black_24dp : R.drawable.ic_grid_white_24dp;
mSwitch.setIcon(icon);
mSwitch.setTitle(mLinear ? R.string.grid : R.string.list);
mPrefs.edit().putBoolean("linear", mLinear).apply();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/light_youtube_primary_color"/>
<item
android:width="80dp"
android:height="80dp"
android:gravity="center"
android:drawable="@drawable/splash_forground"/>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/dark_background_color"/>
</layer-list>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="m23.909,10.211v78.869c0,0 7.7,-4.556 12.4,-7.337V67.477,56.739 31.686c0,0 3.707,2.173 8.948,5.24 6.263,3.579 14.57,8.565 21.473,12.655 -9.358,5.483 -16.8,9.876 -22.496,13.234V77.053C57.974,68.927 75.176,58.762 90.762,49.581 75.551,40.634 57.144,29.768 43.467,21.715 31.963,14.94 23.909,10.211 23.909,10.211Z"
android:strokeWidth="1.2782383"
android:fillColor="#ffffff"/>
</vector>

View File

@ -239,7 +239,7 @@
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/list"
android:src="@drawable/ic_list_white_24dp"
android:background="?attr/selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>

View File

@ -237,7 +237,7 @@
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/list"
android:src="@drawable/ic_list_white_24dp"
android:background="?attr/selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>

View File

@ -4,8 +4,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
android:orientation="vertical"
android:layout_marginLeft="8dp"

View File

@ -3,17 +3,18 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/switch_mode"
android:icon="@drawable/list"
android:icon="?attr/ic_grid"
android:title="@string/grid"
app:showAsAction="ifRoom" />
<item android:id="@+id/action_settings"
app:showAsAction="never"
android:title="@string/settings"/>
<item android:id="@+id/clear_list"
android:visible="false"
android:icon="@drawable/ic_delete_sweep_white_24dp"
app:showAsAction="ifRoom"
android:title="@string/clear_finished_download"/>
android:icon="?attr/ic_delete"
android:title="@string/clear_finished_download"
app:showAsAction="ifRoom" />
<item android:id="@+id/action_settings"
android:title="@string/settings"
app:showAsAction="never" />
</menu>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="background_player_playing_toast">يتم التشغيل في الخلفية</string>
<string name="cancel">إلغاء</string>
@ -10,12 +10,12 @@
<string name="detail_likes_img_view_description">الإعجابات</string>
<string name="detail_thumbnail_view_description">صور معاينة الفيديو</string>
<string name="detail_uploader_thumbnail_view_description">الصورة المصغرة الشخصية</string>
<string name="did_you_mean">هل تقصد: %1$s ?</string>
<string name="did_you_mean">هل تقصد: %1$s\?</string>
<string name="download">تنزيل</string>
<string name="download_dialog_title">تنزيل</string>
<string name="download_path_audio_dialog_title">أدخل مسار تنزيل الملفات الصوتية</string>
<string name="download_path_audio_summary">مسار حفظ التنزيلات الصوتية</string>
<string name="download_path_audio_title">مسار ملفات الصوت المحفوظة</string>
<string name="download_path_audio_summary">يتم تخزين الصوت الذي تم تنزيله هنا</string>
<string name="download_path_audio_title">مسار مجلد الصوتيات المحفوظة</string>
<string name="download_path_dialog_title">أدخل مسار التنزيل لملفات الفيديو</string>
<string name="download_path_summary">مسار حفظ تنزيلات الفيديو في</string>
<string name="download_path_title">مسار ملفات الفيديو المحفوظة</string>
@ -26,7 +26,7 @@
<string name="light_theme_title">مضيء</string>
<string name="list_thumbnail_view_description">صور معاينة الفيديو</string>
<string name="network_error">خطأ في الشبكة</string>
<string name="next_video_title">الفيديو التالي</string>
<string name="next_video_title">التالى</string>
<string name="no_player_found">لا يوجد مشغل فيديو. هل تريد تثبيت VLC ؟</string>
<string name="open_in_browser">افتح في المتصفح</string>
<string name="play_audio">الصوت</string>
@ -38,22 +38,22 @@
<string name="settings">الإعدادات</string>
<string name="settings_category_appearance_title">المظهر</string>
<string name="settings_category_other_title">اخرى</string>
<string name="settings_category_video_audio_title">الفيديو والصوتيات</string>
<string name="settings_category_video_audio_title">الفيديو &amp; الصوتيات</string>
<string name="share">مشاركة</string>
<string name="share_dialog_title">مشاركة بواسطة</string>
<string name="show_next_and_similar_title">عرض الفديوهات \'التالية\'و\'المماثلة\'</string>
<string name="show_next_and_similar_title">عرض مقاطع الفيديو \"التالية\" و \"شبيه\"</string>
<string name="show_play_with_kodi_summary">عرض خيار لتشغيل الفيديو بواسطة مشغل كودي</string>
<string name="show_play_with_kodi_title">عرض خيار التشغيل بواسطة كودي</string>
<string name="theme_title">السمة</string>
<string name="upload_date_text">تم النشر يوم %1$s</string>
<string name="url_not_supported_toast">الرابط غير مدعوم</string>
<string name="url_not_supported_toast">رابط غير معتمد URL</string>
<string name="use_external_audio_player_title">استخدام مشغل صوت خارجي</string>
<string name="use_external_video_player_title">استخدام مشغل فيديو خارجي</string>
<string name="use_tor_summary">(إختبارية) إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ).</string>
<string name="use_tor_title">استخدام تور</string>
<string name="view_count_text">مشاهدات %1$s</string>
<string name="blocked_by_gema">تم حجبه بواسطة GEMA</string>
<string name="content_not_available">المحتوى غير متاح</string>
<string name="content_not_available">محتوى غير متوفر</string>
<string name="could_not_load_thumbnails">تعذرت عملية تحميل كافة صور المعاينة</string>
<string name="general_error">خطأ</string>
<string name="parsing_error">تعذرت عملية تحليل الموقع</string>
@ -70,7 +70,7 @@
<string name="autoplay_by_calling_app_title">تشغيل تلقائي</string>
<string name="black_theme_title">اسود</string>
<string name="enable_watch_history_title">التاريخ وذاكرة التخزين المؤقت</string>
<string name="settings_category_history_title">التاريخ و التخزين المؤقت</string>
<string name="settings_category_history_title">التاريخ &amp; ذاكرة التخزين المؤقت</string>
<string name="content">المحتوى</string>
<string name="downloads">التنزيلات</string>
<string name="downloads_title">التنزيلات</string>
@ -83,7 +83,7 @@
<string name="title_activity_history">التاريخ</string>
<string name="action_history">التاريخ</string>
<string name="open_in_popup_mode">افتح في وضع النافذة المنبثقة</string>
<string name="use_external_video_player_summary">"بعض الخيارات الدقة لن تحتوي على صوت عند تمكين هذا الخيار "</string>
<string name="use_external_video_player_summary">يزيل الصوت في بعض القرارات</string>
<string name="popup_mode_share_menu_title">وضع النوافذ المنبثقة NewPipe</string>
<string name="channel_unsubscribed">تم إلغاء الاشتراك في القناة</string>
<string name="subscription_change_failed">تعذر تغيير حالة الاشتراك</string>
@ -107,7 +107,7 @@
<string name="enable_watch_history_summary">تتبع مقاطع الفيديو التي تمت مشاهدتها</string>
<string name="resume_on_audio_focus_gain_title">استئناف عند اكتساب التركيز</string>
<string name="resume_on_audio_focus_gain_summary">متابعة التشغيل بعد المقاطعات (مثل المكالمات الهاتفية)</string>
<string name="show_hold_to_append_title">عرض تلميح \"اضغط للتجاهل\"</string>
<string name="show_hold_to_append_title">إظهار التلميحات \"اضغط للتجاهل\"</string>
<string name="show_hold_to_append_summary">عرض تلميح على صفحة التفاصيل عند استخدام وضع مشغل الخلفية أو النافذة المنبثقة</string>
<string name="settings_category_player_title">المشغل</string>
<string name="settings_category_player_behavior_title">السلوك</string>
@ -115,9 +115,9 @@
<string name="popup_playing_toast">يتم التشغيل في الوضع المنبثق</string>
<string name="background_player_append">تم وضعه على قائمة الانتظار في مشغل الخلفية</string>
<string name="popup_playing_append">تم وضعه على قائمة الانتظار في مشغل النافذة المنبثقة</string>
<string name="show_age_restricted_content_title">عرض المحتوى المقيّد بحسب العُمر</string>
<string name="video_is_age_restricted">فيديو مقيد بحسب العمر. السماح بهذا المحتوى ممكن عن طريق الإعدادات.</string>
<string name="duration_live">مباشر</string>
<string name="show_age_restricted_content_title">محتوى مقيد بحسب العمر</string>
<string name="video_is_age_restricted">"إظهار الفيديو المقيد بحسب العمر. يمكن السماح باستخدام هذه المواد من \"الإعدادات\"."</string>
<string name="duration_live">بث مباشر</string>
<string name="error_report_title">تقرير الخطأ</string>
<string name="playlist">قائمة التشغيل</string>
<string name="yes">نعم</string>
@ -132,13 +132,13 @@
<string name="play_all">تشغيل الكل</string>
<string name="notification_channel_name">تنبيهات NewPipe</string>
<string name="notification_channel_description">التنبيهات لمشغل NewPipe للخلفية والنوافذ المنبثقة</string>
<string name="notification_channel_description">تنبيهات مشغل NewPipe للخلفية والنوافذ المنبثقة</string>
<string name="unknown_content">[غير معروف]</string>
<string name="light_parsing_error">لا يمكن تحليل الموقع بشكل كلي</string>
<string name="could_not_setup_download_menu">تعذرت عملية إعداد قائمة التنزيل</string>
<string name="live_streams_not_supported">هذا بث مباشر، وهو غير معتمد الآن.</string>
<string name="live_streams_not_supported">لبث المباشر غير مدعوم حتى الآن</string>
<string name="could_not_get_stream">تعذر الحصول على أي بث</string>
<string name="could_not_load_image">تعذرت عملية تحميل الصورة</string>
<string name="app_ui_crash">تعطل التطبيق / واجهة المستخدم</string>
@ -160,7 +160,7 @@
<string name="report_error">الإبلاغ عن خطأ</string>
<string name="user_report">تقرير المستخدم</string>
<string name="search_no_results">لم يتم العثور على نتائج</string>
<string name="empty_subscription_feed_subtitle">لا شيء هنا غير صراصير الليل</string>
<string name="empty_subscription_feed_subtitle">لا شيء هنا غير الصراصير</string>
<string name="audio">الصوت</string>
<string name="retry">إعادة المحاولة</string>
@ -172,18 +172,18 @@
<string name="no_subscribers">ليس هناك مشترِكون</string>
<plurals name="subscribers">
<item quantity="zero">صفر مشترِك</item>
<item quantity="one">%s مشترِك</item>
<item quantity="two">مشترِكان</item>
<item quantity="few">%s مشتركين</item>
<item quantity="many">%s مشترك</item>
<item quantity="other">%s مشترك</item>
<item quantity="zero">%s لا يوجد مشترك</item>
<item quantity="one">%s مشترك</item>
<item quantity="two">%s اثنتين مشتركين</item>
<item quantity="few">%s اشتراكات</item>
<item quantity="many">%s مشتركين</item>
<item quantity="other">%s مشتركين</item>
</plurals>
<string name="no_views">دون مشاهدات</string>
<string name="no_videos">لاتوجد فيديوهات</string>
<string name="start">تشغيل</string>
<string name="pause">إيقاف</string>
<string name="start">ابدأ</string>
<string name="pause">إيقاف مؤقت</string>
<string name="view">شغل</string>
<string name="delete">حذف</string>
<string name="checksum">التوقيع</string>
@ -194,7 +194,7 @@
<string name="msg_name">اسم الملف</string>
<string name="msg_threads">التقسيم</string>
<string name="msg_error">الخطأ</string>
<string name="msg_server_unsupported">الخادم غير معتمد</string>
<string name="msg_server_unsupported">خادم غير مدعوم</string>
<string name="msg_exists">الملف موجود مسبقا</string>
<string name="msg_url_malform">العنوان غير صحيح أو الإنترنت غير متوفر</string>
<string name="msg_running">يقوم نيوبايب بالتنزيل</string>
@ -258,7 +258,7 @@
<string name="play_queue_audio_settings">الإعدادات الصوتية</string>
<string name="start_here_on_main">تشغيل هنا</string>
<string name="start_here_on_popup">تشغيل هنا في وضع النافذة المنبثقة</string>
<string name="reCaptcha_title">تحدي ريكابتشا</string>
<string name="reCaptcha_title">تحدي الكابتشا</string>
<string name="hold_to_append">اضغط للإدراج في قائمة الانتظار</string>
<plurals name="views">
<item quantity="zero">بدون مشاهدات</item>
@ -278,7 +278,7 @@
<item quantity="other">%s فيديوهات</item>
</plurals>
<string name="recaptcha_request_toast">إعادة طلب كلمة التحقق</string>
<string name="recaptcha_request_toast">طلب اختبار الكابتشا مطلوب</string>
<string name="copyright" formatted="true">© %1$sبواسطة%2$sتحت%3$s</string>
<string name="kiosk_page_summary">صفحة الكشك</string>
@ -301,10 +301,10 @@
<string name="just_once">مرة واحدة فقط</string>
<string name="invalid_url_toast">العنوان خاطئ</string>
<string name="no_player_found_toast">لم يتم العثور على مشغل الفديو (يمكنك تثبيت VLC لتشغيله)</string>
<string name="no_player_found_toast">لم يتم العثور على مشغل تدفق (يمكنك تثبيت VLC وتشغيله).</string>
<string name="import_data_title">استيراد قاعدة البيانات</string>
<string name="export_data_title">تصدير قاعدة البيانات</string>
<string name="import_data_summary">"سيقوم بالكتابة على سجل التاريخ والاشتراكات الحالية "</string>
<string name="import_data_summary">"الكتابة فوق سجل التاريخ والاشتراكات الحالية "</string>
<string name="export_data_summary">تصدير السجل، الإشتراكات وقوائم التشغيل</string>
<string name="show_info">عرض المعلومات</string>
@ -355,8 +355,8 @@
<string name="live_sync">مُزامَنة</string>
<string name="controls_download_desc">تنزيل ملف البث.</string>
<string name="tab_bookmarks">الإشارات مرجعية</string>
<string name="controls_download_desc">تنزيل ملف البث</string>
<string name="tab_bookmarks">الإشارات المرجعية</string>
<string name="use_inexact_seek_title">استعمال التقديم السريع الغير دقيق</string>
<string name="use_inexact_seek_summary">"التقديم الغير دقيق يسمح للمشغل بالإطلاع الى الأماكن بشكل اسرع مع دقة اقل "</string>
@ -364,8 +364,8 @@
<string name="thumbnail_cache_wipe_complete_notice">تم إفراغ مساحة ذاكرة التخزين المؤقتة الخاصة بالصور</string>
<string name="file">الملف</string>
<string name="invalid_directory">مجلد غير صالح</string>
<string name="file_name_empty_error">لا يمكن ترك إسم الملف فارغا</string>
<string name="invalid_directory">لا يوجد مثل هذا المجلد</string>
<string name="file_name_empty_error">لا يمكن أن يكون اسم الملف فارغا</string>
<string name="error_occurred_detail">طرأ هناك خطأ: %1$s</string>
<string name="no_valid_zip_file">ملف مضغوط ZIP غير صالح</string>
<string name="unbookmark_playlist">إزالة الفواصل المرجعية</string>
@ -385,12 +385,12 @@
<string name="import_soundcloud_instructions_hint">"معرفك , soundcloud.com/ الخاص بك "</string>
<string name="playback_default">إفتراضي</string>
<string name="download_thumbnail_summary">تعطيل إيقاف جميع الصور المصغرة من تحميل البيانات واستخدام الذاكرة وحفظها. سيؤدي التغيير هذا إلى محو-ذاكرة التخزين المؤقت في الذاكرة-وذاكرة على القرص</string>
<string name="download_thumbnail_summary">عند إيقاف تحميل أي صور مصغرة ، وتوفير البيانات واستخدام الذاكرة. تعمل التغييرات على محو ذاكرة التخزين المؤقت للصورة الموجودة على الذاكرة أو على القرص.</string>
<string name="metadata_cache_wipe_title">امسح البيانات الوصفية المخزنة مؤقتًا</string>
<string name="metadata_cache_wipe_summary">إزالة جميع بيانات صفحات الويب المخزنة مؤقتًا</string>
<string name="metadata_cache_wipe_complete_notice">تم محو ذاكرة التخزين المؤقت للبيانات الوصفية</string>
<string name="auto_queue_title">وضع البث القادم تلقائيا في قائمة الإنتظار</string>
<string name="auto_queue_summary">رفض البث المشابه في حال كون البث السابق يعمل في حالة عدم التكرار</string>
<string name="auto_queue_summary">رفض التدفق-التلقائي المشابه في حال كان التدفق السابق يعمل في-حالة عدم التكرار.</string>
<string name="set_as_playlist_thumbnail">إضافة صورة مصغرة إلى قائمة التشغيل</string>
<string name="bookmark_playlist">تفضيل قائمة التشغيل</string>
@ -408,15 +408,15 @@
<string name="clear_views_history_title">محو سجل المشاهدة</string>
<string name="clear_views_history_summary">احذف سِجل الفيديوهات التي تم تشغيلها</string>
<string name="delete_view_history_alert">حذف سجل المشاهدة بالكامل.</string>
<string name="delete_view_history_alert">حذف سجل المشاهدة بالكامل\?</string>
<string name="view_history_deleted">تم حذف سجل المشاهدة.</string>
<string name="clear_search_history_title">محو سجل البحث</string>
<string name="clear_search_history_summary">يحذف تاريخ البحث عن الكلمات الرئيسية</string>
<string name="delete_search_history_alert">حذف سِجل البحث بالكامل.</string>
<string name="delete_search_history_alert">حذف سِجل البحث بالكامل\?</string>
<string name="search_history_deleted">تم حذف سجل البحث.</string>
<string name="external_player_unsupported_link_type">المشغل الخارجي لا يدعم هذه الأنواع من الروابط</string>
<string name="invalid_source">ملف غير صالح / محتوى غير صالح</string>
<string name="invalid_file">الملف غير موجود أو ليس لديك الإذن الكافي للقراءة أو الكتابة فيه</string>
<string name="invalid_source">لا يوجد مثل هذا الملف/مصدر المحتوى</string>
<string name="invalid_file">الملف غير موجود أو الإذن بالقراءة أو الكتابة إليه غير موجود</string>
<string name="no_streams_available_download">لا يوجد بث متاح للتنزيل</string>
<string name="one_item_deleted">تم حذف عنصر واحد.</string>
@ -488,4 +488,4 @@
<string name="playlists">قوائم التشغيل</string>
<string name="tracks">المسارات</string>
<string name="users">المستخدمين</string>
</resources>
</resources>

View File

@ -1,16 +1,16 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="main_bg_subtitle">Націсніце пошук, каб пачаць</string>
<string name="view_count_text">%1$s праглядаў</string>
<string name="upload_date_text">Апублікавана %1$s</string>
<string name="no_player_found">Патокавы плэер не знойдзены. Усталяваць VLC?</string>
<string name="no_player_found_toast">Патокавы плэер не знойдзены (вы можаце ўсталяваць VLC)</string>
<string name="no_player_found_toast">Патокавы плэер не знойдзены (вы можаце ўсталяваць VLC).</string>
<string name="install">Усталяваць</string>
<string name="cancel">Скасаваць</string>
<string name="open_in_browser">Адкрыць у браўзэры</string>
<string name="open_in_browser">Адкрыць у браўзеры</string>
<string name="open_in_popup_mode">Адкрыць у асобным акне</string>
<string name="share">Падзяліцца</string>
<string name="download">Спампаваць</string>
<string name="controls_download_desc">Загрузка файла прамой трансляцыі.</string>
<string name="controls_download_desc">Загрузка файла прамой трансляцыі</string>
<string name="search">Пошук</string>
<string name="settings">Налады</string>
<string name="did_you_mean">Магчыма, вы мелі на ўвазе: %1$s?</string>
@ -18,7 +18,7 @@
<string name="choose_browser">Выбраць браўзэр</string>
<string name="screen_rotation">паварот</string>
<string name="use_external_video_player_title">Знешні відэаплэер</string>
<string name="use_external_video_player_summary">У некаторых разрозненнях НЕ будзе гуку, калі гэтая опцыя абраная</string>
<string name="use_external_video_player_summary">Адключае гук для НЕКАТОРЫХ разрозненняў</string>
<string name="use_external_audio_player_title">Знешні аўдыяплэер</string>
<string name="popup_mode_share_menu_title">NewPipe ў акне</string>
<string name="subscribe_button_title">Падпісацца</string>
@ -30,7 +30,7 @@
<string name="tab_main">Галоўная</string>
<string name="tab_subscriptions">Падпіскі</string>
<string name="tab_bookmarks">Закладкі</string>
<string name="tab_bookmarks">Адзначаныя плэйлісты</string>
<string name="fragment_whats_new">Што новага</string>
@ -42,8 +42,8 @@
<string name="download_path_summary">Папка для захоўвання загружаных відэа</string>
<string name="download_path_dialog_title">Увядзіце шлях да папкі для загрузкі відэа</string>
<string name="download_path_audio_title">Шлях загрузкі аўдыё</string>
<string name="download_path_audio_summary">Папка для захоўвання загружаных аўдыё</string>
<string name="download_path_audio_title">Тэчка загрузкі аўдыё</string>
<string name="download_path_audio_summary">"Сюды захоўваецца загружанае аўдыё "</string>
<string name="download_path_audio_dialog_title">Увядзіце шлях да папкі для загрузкі аўдыё</string>
<string name="autoplay_by_calling_app_title">Аўтапрайграванне</string>
@ -463,4 +463,4 @@
<string name="minimize_on_exit_background_description">Фонавы плэер</string>
<string name="minimize_on_exit_popup_description">Плэер у акне</string>
</resources>
</resources>

View File

@ -11,12 +11,12 @@
<string name="download">Изтегли</string>
<string name="search">Търси</string>
<string name="settings">Настройки</string>
<string name="did_you_mean">Може би имахте в предвид: %1$s ?</string>
<string name="did_you_mean">Може би имахте в предвид: %1$s\?</string>
<string name="share_dialog_title">Сподели с</string>
<string name="choose_browser">Избери браузър</string>
<string name="screen_rotation">ориентация</string>
<string name="use_external_video_player_title">Използвай външен видео плейър</string>
<string name="use_external_video_player_summary">Някои резолюции няма да имат вграден аудио поток, ако тази опция е избрана</string>
<string name="use_external_video_player_summary">Премахва аудио при НЯКОИ резолюции</string>
<string name="use_external_audio_player_title">Използвай външен аудио плейър</string>
<string name="popup_mode_share_menu_title">NewPipe в прозорец</string>
<string name="subscribe_button_title">Абониране</string>
@ -77,8 +77,8 @@
<string name="content_language_title">Език на съдържанието по подразбиране</string>
<string name="settings_category_player_title">Плейър</string>
<string name="settings_category_player_behavior_title">Поведение</string>
<string name="settings_category_video_audio_title">Видео &amp; Аудио</string>
<string name="settings_category_history_title">История и кеш-памет</string>
<string name="settings_category_video_audio_title">Видео &amp; аудио</string>
<string name="settings_category_history_title">История и кеш</string>
<string name="settings_category_popup_title">Нов прозорец</string>
<string name="settings_category_appearance_title">Външност</string>
<string name="settings_category_other_title">Други</string>
@ -88,9 +88,9 @@
<string name="popup_playing_append">Включен в опашката в нов прозорец</string>
<string name="play_btn_text">Възпроизвеждане</string>
<string name="content">Съдържание</string>
<string name="show_age_restricted_content_title">Покажи съдържание за възрастни</string>
<string name="video_is_age_restricted">Съдържание за възрастни. Разрешаването на такова съдържание става от Настройки.</string>
<string name="duration_live">на живо</string>
<string name="show_age_restricted_content_title">Съдържание за възрастни</string>
<string name="video_is_age_restricted">Покажи съдържание за възрастни. Разрешаването на такова съдържание става от \"Настройки\".</string>
<string name="duration_live">НА ЖИВО</string>
<string name="downloads">Изтегляния</string>
<string name="downloads_title">Изтегляния</string>
<string name="error_report_title">Съобщение за грешка</string>
@ -109,7 +109,7 @@
<string name="play_all">Възпроизведи всички</string>
<string name="notification_channel_name">Известия от NewPipe</string>
<string name="notification_channel_description">Известия за фоновия плейър и плейъра в отделен прозорец на NewPipe</string>
<string name="notification_channel_description">Известия за Фонов плейър и плейъра в Отделен прозорец на NewPipe</string>
<string name="unknown_content">[Неизвестен]</string>
@ -121,7 +121,7 @@
<string name="content_not_available">Съдържанието не е налично</string>
<string name="blocked_by_gema">Блокирано от „GEMA“</string>
<string name="could_not_setup_download_menu">Не мога да настроя меню за сваляне</string>
<string name="live_streams_not_supported">Това е предаване на живо, което все още не се поддържа.</string>
<string name="live_streams_not_supported">Предаването на живо все още не се поддържа</string>
<string name="could_not_get_stream">Не мога да достъпя нито един поток</string>
<string name="could_not_load_image">Не мога да заредя изображение</string>
<string name="app_ui_crash">Приложението или потребителският интерфейс се сринаха</string>
@ -209,11 +209,11 @@
<string name="tab_licenses">Лицензи</string>
<string name="app_description">Безплатно и леко поточно предаване за Android.</string>
<string name="view_on_github">Виж в GitHub</string>
<string name="controls_download_desc">Изтегли стрийм файл.</string>
<string name="controls_download_desc">Изтегли стрийм файл</string>
<string name="show_info">Покажи инфо</string>
<string name="tab_main">Главен</string>
<string name="tab_bookmarks">Отметки</string>
<string name="tab_bookmarks">Отметнати плейлисти</string>
<string name="controls_add_to_playlist_title">Добави към</string>
@ -225,7 +225,7 @@
<string name="metadata_cache_wipe_summary">Премахни всички метаданни за уебстраници от кеш-паметта</string>
<string name="metadata_cache_wipe_complete_notice">Кеш-паметта с метаданни бе изтрита</string>
<string name="auto_queue_title">Автоматично нареди на опашка следващия</string>
<string name="auto_queue_summary">"Автоматично прибавяне на сродно съдържание при неповтарящ се преглед "</string>
<string name="auto_queue_summary">Автоматично прибавяне на сродно съдържание при неповтарящ се преглед.</string>
<string name="default_content_country_title">Държава, за която да бъде показвано съдържание</string>
<string name="service_title">Услуга</string>
<string name="settings_category_debug_title">Отстраняване на грешки</string>
@ -240,14 +240,14 @@
<string name="import_data_title">Импортиране на база данни</string>
<string name="export_data_title">Експортиране на база данни</string>
<string name="import_data_summary">Ще замени текущите история и абонаменти</string>
<string name="import_data_summary">Замества текущите история и абонаменти</string>
<string name="export_data_summary">Експортиране на историята, абонаментите и плейлистите</string>
<string name="clear_views_history_title">Изтрий историята с изгледани</string>
<string name="delete_view_history_alert">Изтрий цялата история с изгледани.</string>
<string name="delete_view_history_alert">Изтрий цялата история с изгледани\?</string>
<string name="view_history_deleted">Историята с изгледани е изтрита.</string>
<string name="clear_search_history_title">Изтрий историята на търсенията</string>
<string name="clear_search_history_summary">Изтрива историята с въвежданите за търсене ключови думи</string>
<string name="delete_search_history_alert">Изтрий цялата история на търсенията.</string>
<string name="delete_search_history_alert">Изтрий цялата история на търсенията\?</string>
<string name="search_history_deleted">Историята на търсенията е изтрита.</string>
<string name="youtube_signature_decryption_error">URL подписът на клипа не можа да бъде дешифрован</string>
<string name="external_player_unsupported_link_type">Външните плейъри не поддържат този вид линкове</string>
@ -282,8 +282,8 @@
\nНашата политика за личните данни обяснява подробно какви данни изпращате и къде се съхраняват, когато изпращате съобщения за грешки.</string>
<string name="read_privacy_policy">Прочетете нашата политика за поверителност</string>
<string name="app_license_title">Лицензът на NewPipe</string>
<string name="no_player_found_toast">Липсва стрийм плейър (можете да изтеглите VLC, за да пуснете стрийма)</string>
<string name="download_thumbnail_summary">Изключете, за да спрете зареждането на всички миниатюри, спестявайки трафик и памет. При промяна на тази настройка, текущата кеш-памет на изображенията ще бъде изтрита</string>
<string name="no_player_found_toast">Липсва стрийм плейър (можете да изтеглите VLC, за да пуснете стрийма).</string>
<string name="download_thumbnail_summary">Изключете, за да спрете зареждането на всички миниатюри, спестявайки трафик и памет. При промяна на тази настройка, текущата кеш-памет на изображенията ще бъде изтрита.</string>
<string name="show_hold_to_append_summary">Показвай подсказка, когато е избран фонов режим или режим в прозорец на страницата с детайли на съответния клип</string>
<string name="clear_views_history_summary">Изтрива историята на възпроизвежданите стриймове</string>
<string name="video_streams_empty">Не са намерени видео стриймове</string>
@ -303,14 +303,14 @@
\nвъзпроизвеждане в отделен прозорец</string>
<string name="one_item_deleted">1 елемент е изтрит.</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptchaActivity">преКАПЧА</string>
<string name="reCaptcha_title">reCAPTCHA заявка</string>
<string name="recaptcha_request_toast">Изисква се въвеждане на reCAPTCHA</string>
<string name="settings_category_downloads_title">Изтегляне</string>
<string name="charset_most_special_characters">Повечето специални символи</string>
<string name="app_license">NewPipe е безплатен „copyleft“ софтуер: Можете да го използвате, изучавате, споделяте и подобрявате по ваше усмотрение. В частност, Вие можете да препубликувате и/или модифицирате приложението според правилата на Главния обществен лиценз на ГНУ, издаден от Фондацията за свободен софтуер версия 3 на лиценза или по-нова.</string>
<string name="app_license">NewPipe е безплатен „copyleft“ софтуер: Можете да го използвате, изучавате, споделяте и подобрявате по желание. В частност, Вие можете да препубликувате и/или модифицирате приложението според правилата на Главния обществен лиценз на ГНУ, издаден от Фондацията за свободен софтуер версия 3 на лиценза или по-нова.</string>
<string name="read_full_license">Прочетете лиценза</string>
@ -354,11 +354,11 @@
<string name="play_queue_stream_detail">Детайли</string>
<string name="play_queue_audio_settings">Аудио настройки</string>
<string name="hold_to_append">Задръжте, за да поставите на опашката</string>
<string name="enqueue_on_background">На опашката във „фонов режим“</string>
<string name="enqueue_on_popup">На опашката в „режим в прозорец“</string>
<string name="enqueue_on_background">На опашката при „фонов режим“</string>
<string name="enqueue_on_popup">На опашката при „режим в прозорец“</string>
<string name="start_here_on_main">Възпроизвеждане от тук</string>
<string name="start_here_on_background">Възпроизвеждане от тук във фонов режим</string>
<string name="start_here_on_popup">Възпроизвеждане от тук в прозорец</string>
<string name="start_here_on_background">Възпроизвеждане от тук при фонов режим</string>
<string name="start_here_on_popup">Възпроизвеждане от тук при прозорец</string>
<string name="drawer_open">Отвори навигационната лента</string>
<string name="drawer_close">Затвори навигационната лента</string>
@ -376,9 +376,9 @@
<string name="preferred_player_fetcher_notification_title">Получаване на инфо…</string>
<string name="preferred_player_fetcher_notification_message">Зареждане на заявеното съдържание</string>
<string name="create_playlist">Създай Нов Плейлист</string>
<string name="delete_playlist">Изтрий Плейлист</string>
<string name="rename_playlist">Преименувай Прелист</string>
<string name="create_playlist">Нов Плейлист</string>
<string name="delete_playlist">Изтрий</string>
<string name="rename_playlist">"Преименувай "</string>
<string name="playlist_name_input">Име</string>
<string name="append_playlist">Добави Към Плейлист</string>
<string name="set_as_playlist_thumbnail">Задай като миниатюра на плейлиста</string>
@ -389,8 +389,8 @@
<string name="delete_playlist_prompt">Искате ли да изтриете този плейлист?</string>
<string name="playlist_creation_success">Плейлистът е създаден</string>
<string name="playlist_add_stream_success">Добавено към плейлист</string>
<string name="playlist_thumbnail_change_success">Миниатюрата на плейлиста е сменена</string>
<string name="playlist_delete_failure">Плейлистът не можа да бъде изтрит</string>
<string name="playlist_thumbnail_change_success">Миниатюрата на плейлиста е сменена.</string>
<string name="playlist_delete_failure">Плейлистът не можа да бъде изтрит.</string>
<string name="caption_none">Без надписи</string>
@ -401,13 +401,13 @@
<string name="caption_auto_generated">Авто-генерирани</string>
<string name="caption_setting_title">Надписи</string>
<string name="caption_setting_description">Модифицирай мащаба на текста и фона на надписите. Изисква рестарт на приложението, за да се приложат промените</string>
<string name="caption_setting_description">Модифицирай мащаба на текста и фона на надписите. Изисква рестарт на приложението, за да се приложат промените.</string>
<string name="enable_leak_canary_title">Включи LeakCanary</string>
<string name="enable_leak_canary_summary">Следенето за пропускане на памет може да направи приложението нестабилно</string>
<string name="enable_disposed_exceptions_title">Докладвай за извънредни грешки</string>
<string name="import_export_title">Импортиране/експортиране</string>
<string name="import_export_title">Импортиране/Експортиране</string>
<string name="import_title">Импортирай</string>
<string name="import_from">Импортирай от</string>
<string name="export_to">Експортирай в</string>
@ -462,4 +462,7 @@
<string name="playback_reset">Възстанови</string>
<string name="playback_step">Стъпка</string>
<string name="enable_disposed_exceptions_summary">Насили докладването на неизпращаеми Rx изключения извън фрагмента или кръговрата на активност след приключване</string>
<string name="unhook_checkbox">Откачи (може да предизвика промени)</string>
</resources>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="install">Instal·la</string>
<string name="cancel">Cancel·la</string>
<string name="open_in_browser">Obre al navegador</string>
@ -12,12 +12,12 @@
<string name="show_info">Mostra la informació</string>
<string name="tab_subscriptions">Subscripcions</string>
<string name="tab_bookmarks">Adreces d\'interès</string>
<string name="tab_bookmarks">Llistes de reproducció desades</string>
<string name="fragment_whats_new">Novetats</string>
<string name="download_path_title">Ruta de baixada dels vídeos</string>
<string name="download_path_audio_title">Ruta de baixada dels fitxers d\'àudio</string>
<string name="download_path_audio_title">Carpeta de baixada dels fitxers d\'àudio</string>
<string name="autoplay_by_calling_app_title">Reproducció automàtica</string>
<string name="default_resolution_title">Resolució per defecte</string>
<string name="play_audio">Àudio</string>
@ -40,7 +40,7 @@
<string name="content">Contingut</string>
<string name="show_age_restricted_content_title">Desactiva les restriccions per edat</string>
<string name="video_is_age_restricted">Vídeo restringit per edat. Podeu permetre aquesta mena de continguts des de la configuració.</string>
<string name="duration_live">en directe</string>
<string name="duration_live">EN DIRECTE</string>
<string name="downloads">Baixades</string>
<string name="downloads_title">Baixades</string>
<string name="all">Tot</string>
@ -112,9 +112,9 @@
<string name="popup_player">Reproductor emergent</string>
<string name="always_ask_open_action">Demana-ho sempre</string>
<string name="create_playlist">Crea una nova llista de reproducció</string>
<string name="delete_playlist">Elimina la llista de reproducció</string>
<string name="rename_playlist">Canvia el nom de la llista de reproducció</string>
<string name="create_playlist">Crea una llista de reproducció</string>
<string name="delete_playlist">Elimina</string>
<string name="rename_playlist">Canvia el nom</string>
<string name="playlist_name_input">Nom</string>
<string name="append_playlist">Afegeix a una llista de reproducció</string>
<string name="import_title">Importa</string>
@ -129,9 +129,9 @@
<string name="view_count_text">%1$s reproduccions</string>
<string name="upload_date_text">Publicat el %1$s</string>
<string name="no_player_found">No s\'ha trobat un reproductor de fluxos. Voleu instal·lar el VLC?</string>
<string name="no_player_found_toast">No s\'ha trobat cap reproductor de fluxos (podeu instal·lar el VLC per reproduir-lo)</string>
<string name="no_player_found_toast">No s\'ha trobat cap reproductor de fluxos (podeu instal·lar el VLC per reproduir-lo).</string>
<string name="open_in_popup_mode">Obre en mode emergent</string>
<string name="controls_download_desc">Baixa el fitxer de vídeo.</string>
<string name="controls_download_desc">Baixa el fitxer de vídeo</string>
<string name="did_you_mean">Volíeu dir: %1$s?</string>
<string name="share_dialog_title">Comparteix-ho amb</string>
<string name="screen_rotation">rotació</string>
@ -148,7 +148,7 @@
<string name="download_path_summary">Ruta on es desaran els vídeos baixats</string>
<string name="download_path_dialog_title">Introduïu una ruta de baixada per als vídeos</string>
<string name="download_path_audio_summary">Ruta on es desaran els fitxers d\'àudio baixats</string>
<string name="download_path_audio_summary">Els fitxers d\'àudio baixats es desaran aquí</string>
<string name="download_path_audio_dialog_title">Introduïu una ruta de baixada per als fitxers d\'àudio</string>
<string name="autoplay_by_calling_app_summary">Reprodueix un vídeo quan el NewPipe s\'executa des d\'una altra aplicació</string>
@ -174,7 +174,7 @@
<string name="show_search_suggestions_summary">Mostra suggeriments durant la cerca</string>
<string name="enable_search_history_title">Historial de cerca</string>
<string name="resume_on_audio_focus_gain_summary">Reprèn la reproducció després d\'una interrupció (per exemple, una trucada)</string>
<string name="next_video_title">Vídeo següent</string>
<string name="next_video_title">Següent</string>
<string name="show_next_and_similar_title">Mostra el vídeo següent i similars</string>
<string name="default_content_country_title">País per defecte dels continguts</string>
<string name="content_language_title">Llengua per defecte dels continguts</string>
@ -194,7 +194,7 @@
<string name="content_not_available">Contingut no disponible</string>
<string name="blocked_by_gema">Blocat per la GEMA</string>
<string name="could_not_setup_download_menu">No s\'ha pogut configurar el menú de baixades</string>
<string name="live_streams_not_supported">Això és una emissió en directe, que encara no és compatible.</string>
<string name="live_streams_not_supported">Les emissions en directe encara no són compatibles</string>
<string name="could_not_get_stream">No s\'ha pogut obtenir cap vídeo</string>
<string name="could_not_load_image">No s\'ha pogut carregar la imatge</string>
<string name="app_ui_crash">Ha fallat l\'aplicació o la interfície d\'usuari</string>
@ -204,9 +204,9 @@
<string name="invalid_url_toast">L\'URL no és vàlid</string>
<string name="video_streams_empty">No s\'ha trobat cap flux de vídeo</string>
<string name="audio_streams_empty">No s\'ha trobat cap flux d\'àudio</string>
<string name="invalid_directory">Directori no vàlid</string>
<string name="invalid_source">Fitxer o font de contingut no vàlid</string>
<string name="invalid_file">El fitxer no existeix o no teniu permisos suficients per llegir-lo o escriure-hi</string>
<string name="invalid_directory">La carpeta no existeix</string>
<string name="invalid_source">El fitxer o la font de contingut no existeix</string>
<string name="invalid_file">El fitxer no existeix o no teniu permisos per llegir-lo o escriure-hi</string>
<string name="file_name_empty_error">El nom del fitxer no pot estar en blanc</string>
<string name="error_occurred_detail">S\'ha produït un error: %1$s</string>
<string name="error_report_button_text">Informa de l\'error per correu electrònic</string>
@ -299,16 +299,16 @@
<string name="preferred_player_fetcher_notification_message">S\'està carregant el contingut seleccionat</string>
<string name="delete_playlist_prompt">Voleu eliminar aquesta llista de reproducció?</string>
<string name="playlist_delete_failure">No s\'ha pogut eliminar la llista de reproducció</string>
<string name="playlist_delete_failure">No s\'ha pogut eliminar la llista de reproducció.</string>
<string name="import_export_title">Importació i exportació</string>
<string name="playback_speed_control">Controls de la velocitat de reproducció</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">To</string>
<string name="main_bg_subtitle">Toqueu el botó de cerca per començar</string>
<string name="use_external_video_player_summary">En algunes resolucions NO hi haurà àudio quan aquesta opció estigui activada</string>
<string name="use_external_video_player_summary">Elimina l\'àudio en algunes resolucions</string>
<string name="use_external_audio_player_title">Reproductor d\'àudio extern</string>
<string name="download_thumbnail_summary">Desactiveu-ho per evitar que es carreguin les miniatures i estalviar dades i memòria. Si canvieu aquesta opció, s\'esborrarà la memòria cau d\'imatges tant de la memòria com de l\'emmagatzematge</string>
<string name="download_thumbnail_summary">Desactiveu-ho per evitar que es carreguin les miniatures i estalviar dades i memòria. Si canvieu aquesta opció, s\'esborrarà la memòria cau d\'imatges tant de la memòria com de l\'emmagatzematge.</string>
<string name="enable_search_history_summary">Emmagatzema les cerques localment</string>
<string name="enable_watch_history_summary">Registra els vídeos visualitzats</string>
<string name="resume_on_audio_focus_gain_title">Reprèn automàticament</string>
@ -324,7 +324,7 @@
<string name="switch_to_popup">Canvia al mode emergent</string>
<string name="switch_to_main">Canvia al mode principal</string>
<string name="import_data_summary">Això sobreescriurà l\'historial i les subscripcions actuals</string>
<string name="import_data_summary">Sobreescriu l\'historial i les subscripcions actuals</string>
<string name="player_recoverable_failure">S\'està recuperant el reproductor després de l\'error</string>
<string name="sorry_string">Ho sentim, això no hauria d\'haver ocorregut.</string>
<string name="detail_drag_description">Arrossegueu per a reordenar la llista</string>
@ -356,8 +356,8 @@
<string name="no_channel_subscribed_yet">Encara no us heu subscrit a cap canal</string>
<string name="new_and_hot">Novetats</string>
<string name="hold_to_append">Manteniu premut per afegir a la cua</string>
<string name="start_here_on_background">Reprodueix en segon pla</string>
<string name="start_here_on_popup">Reprodueix en mode emergent</string>
<string name="start_here_on_background">Reprodueix aquí en segon pla</string>
<string name="start_here_on_popup">Reprodueix aquí en mode emergent</string>
<string name="set_as_playlist_thumbnail">Defineix com a miniatura de la llista de reproducció</string>
@ -365,8 +365,8 @@
<string name="unbookmark_playlist">Elimina l\'adreça d\'interès</string>
<string name="playlist_creation_success">S\'ha creat la llista de reproducció</string>
<string name="playlist_add_stream_success">Afegeix a la llista de reproducció</string>
<string name="playlist_thumbnail_change_success">S\'ha canviat la miniatura de la llista de reproducció</string>
<string name="playlist_add_stream_success">S\'ha afegit a la llista de reproducció</string>
<string name="playlist_thumbnail_change_success">S\'ha canviat la miniatura de la llista de reproducció.</string>
<string name="caption_none">Sense subtítols</string>
<string name="resize_fit">Ajusta</string>
@ -388,7 +388,7 @@
<string name="unhook_checkbox">Desvincula (pot provocar distorsió)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="metadata_cache_wipe_summary">Elimina totes les dades de llocs web de la memòria cau</string>
<string name="auto_queue_summary">Afegeix a la cua un vídeo relacionat quan es reprodueix l\'últim vídeo en una cua sense repetició</string>
<string name="auto_queue_summary">Afegeix a la cua un vídeo relacionat quan es reprodueix l\'últim vídeo en una cua sense repetició.</string>
<string name="show_hold_to_append_title">Mostra els missatges d\'ajuda</string>
<string name="show_hold_to_append_summary">Mostra un missatge d\'ajuda quan el botó de mode en segon pla o emergent estigui premut a la pàgina de detalls d\'un vídeo</string>
<string name="info_labels">Què ha passat:\\nPetició:\\nIdioma del contingut:\\nServei:\\nHora GMT:\\nPaquet:\\nVersió:\\nVersió del SO:</string>
@ -423,17 +423,17 @@
<string name="no_streams_available_download">No hi ha vídeos que es puguin baixar</string>
<string name="caption_setting_title">Subtítols</string>
<string name="caption_setting_description">Modifica la mida del text i el fons dels subtítols. Cal reiniciar l\'aplicació per aplicar els canvis</string>
<string name="caption_setting_description">Modifica la mida del text i el fons dels subtítols. Cal reiniciar l\'aplicació per aplicar els canvis.</string>
<string name="toast_no_player">No s\'ha trobat cap aplicació que pugui reproduir aquest fitxer</string>
<string name="clear_views_history_title">Esborra l\'historial de reproduccions</string>
<string name="clear_views_history_summary">Esborra l\'historial dels vídeos que s\'han reproduït</string>
<string name="delete_view_history_alert">Esborra tot l\'historial de reproduccions.</string>
<string name="delete_view_history_alert">Voleu esborrar tot l\'historial de reproduccions\?</string>
<string name="view_history_deleted">S\'ha esborrat l\'historial de reproduccions.</string>
<string name="clear_search_history_title">Esborra l\'historial de cerca</string>
<string name="clear_search_history_summary">Esborra l\'historial de paraules cercades</string>
<string name="delete_search_history_alert">Esborra tot l\'historial de cerca.</string>
<string name="delete_search_history_alert">Voleu esborrar tot l\'historial de cerca\?</string>
<string name="search_history_deleted">S\'ha esborrat l\'historial de cerca.</string>
<string name="one_item_deleted">S\'ha esborrat 1 element.</string>
@ -464,4 +464,4 @@
<string name="playlists">Llistes de reproducció</string>
<string name="tracks">Pistes</string>
<string name="users">Usuaris</string>
</resources>
</resources>

View File

@ -1,26 +1,26 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="main_bg_subtitle">点击搜索按钮即可开始使用</string>
<string name="view_count_text">%1$s 次观看</string>
<string name="upload_date_text">发布于 %1$s</string>
<string name="no_player_found">找不到流媒体播放器。您想安装 VLC 吗?</string>
<string name="no_player_found_toast">找不到媒体播放器(您可以安装 VLC 来播放)</string>
<string name="no_player_found">找不到媒体播放器。您要安装 VLC 吗?</string>
<string name="no_player_found_toast">找不到媒体播放器(您可以安装 VLC 来播放)</string>
<string name="install">安装</string>
<string name="cancel">取消</string>
<string name="open_in_browser">在浏览器中打开</string>
<string name="open_in_popup_mode">在悬浮窗模式下打开</string>
<string name="share">分享</string>
<string name="download">下载</string>
<string name="controls_download_desc">下载媒体文件</string>
<string name="controls_download_desc">下载媒体文件</string>
<string name="search">搜索</string>
<string name="settings">设置</string>
<string name="did_you_mean">您是不是要找: %1$s ?</string>
<string name="did_you_mean">您是不是要找%1$s</string>
<string name="share_dialog_title">分享至</string>
<string name="choose_browser">选择浏览器</string>
<string name="screen_rotation">旋转</string>
<string name="use_external_video_player_title">使用外部视频播放器</string>
<string name="use_external_video_player_summary">启用此选项时,某些分辨率将不会有音频</string>
<string name="use_external_video_player_summary">删除某些分辨率下的音频</string>
<string name="use_external_audio_player_title">使用外部音频播放器</string>
<string name="popup_mode_share_menu_title">NewPipe弹出模式</string>
<string name="popup_mode_share_menu_title">NewPipe 悬浮窗模式</string>
<string name="subscribe_button_title">订阅</string>
<string name="subscribed_button_title">订阅</string>
<string name="channel_unsubscribed">取消订阅成功</string>
@ -30,26 +30,26 @@
<string name="tab_main">主页</string>
<string name="tab_subscriptions">订阅</string>
<string name="tab_bookmarks">书签</string>
<string name="tab_bookmarks">已添加书签到播放列表</string>
<string name="fragment_whats_new">新功能</string>
<string name="controls_background_title">后台</string>
<string name="controls_popup_title">弹出窗口</string>
<string name="controls_background_title">转到后台</string>
<string name="controls_popup_title">悬浮窗</string>
<string name="controls_add_to_playlist_title">添加到</string>
<string name="download_path_title">视频下载路径</string>
<string name="download_path_summary">储存视频文件的路径</string>
<string name="download_path_dialog_title">输入视频的下载地址</string>
<string name="download_path_audio_title">音频下载路径</string>
<string name="download_path_audio_summary">储存音频的路径</string>
<string name="download_path_audio_title">音频下载路径</string>
<string name="download_path_audio_summary">下载音频的储存路径</string>
<string name="download_path_audio_dialog_title">输入音频的下载路径</string>
<string name="autoplay_by_calling_app_title">自动播放</string>
<string name="autoplay_by_calling_app_summary">NewPipes被其它程序调用时播放视频</string>
<string name="default_resolution_title">默认分辨率</string>
<string name="default_popup_resolution_title">默认弹出窗口分辨率</string>
<string name="default_popup_resolution_title">默认悬浮窗分辨率</string>
<string name="show_higher_resolutions_title">显示更高的分辨率</string>
<string name="show_higher_resolutions_summary">只有部分设备支持播放 2K/4K 视频</string>
<string name="play_with_kodi_title">用 Kodi 播放</string>
@ -63,10 +63,11 @@
<string name="light_theme_title">亮色</string>
<string name="dark_theme_title">酷黑</string>
<string name="black_theme_title">黑色</string>
<string name="popup_remember_size_pos_title">记住弹出窗口的尺寸与位置</string>
<string name="popup_remember_size_pos_summary">记住上一次弹出窗口的位置以及大小</string>
<string name="popup_remember_size_pos_title">记住悬浮窗的尺寸与位置</string>
<string name="m4a_description">M4A — 更好的音质</string>
<string name="popup_remember_size_pos_summary">记住上一次悬浮窗的位置以及大小</string>
<string name="thumbnail_cache_wipe_complete_notice">已清除图像缓存</string>
<string name="minimize_on_exit_popup_description">最小化弹出播放器</string>
<string name="minimize_on_exit_popup_description">最小化悬浮窗播放器</string>
<string name="clear_views_history_title">清除观看历史</string>
@ -87,7 +88,7 @@
<string name="msg_error">错误
\n</string>
<string name="msg_server_unsupported">服务器无法支持</string>
<string name="msg_server_unsupported">不支持的服务器</string>
<string name="msg_exists">文件已存在</string>
<string name="msg_running">NewPipe 下载中</string>
<string name="msg_wait">请稍等…</string>
@ -103,9 +104,9 @@
<string name="action_open_website">打开网页</string>
<string name="unbookmark_playlist">删除书签</string>
<string name="delete_playlist_prompt">请问你要删除这个列表吗?</string>
<string name="delete_playlist_prompt">确定要删除该播放列表吗?</string>
<string name="playlist_creation_success">已创建播放列表</string>
<string name="playlist_add_stream_success">已加入播放列表</string>
<string name="playlist_add_stream_success">播放列表</string>
<string name="playback_step">步骤</string>
<string name="playback_reset">重置</string>
@ -117,9 +118,9 @@
<string name="limit_data_usage_none_description">没有限制</string>
<string name="limit_mobile_data_usage_title">使用移动数据时的解析度限制</string>
<string name="minimize_on_exit_title">最小化应用程序切换</string>
<string name="minimize_on_exit_summary">从主视频播放器切换到其他应用程序时的操作 - %s</string>
<string name="minimize_on_exit_summary">从主视频播放器切换到其他应用时的操作 - %s</string>
<string name="minimize_on_exit_none_description">没有</string>
<string name="minimize_on_exit_background_description">最小化背景播放器</string>
<string name="minimize_on_exit_background_description">最小化后台播放</string>
<string name="webm_description">WebM — 自由视频格式</string>
<string name="use_inexact_seek_title">使用快速粗略定位</string>
<string name="use_inexact_seek_summary">粗略定位功能允许播放器以略低的精确度为代价换取更快的定位速度</string>
@ -127,4 +128,297 @@
<string name="metadata_cache_wipe_title">清除缓存的元数据</string>
<string name="metadata_cache_wipe_summary">移除所有缓存的网页数据</string>
<string name="metadata_cache_wipe_complete_notice">已清除缓存的元数据</string>
</resources>
<string name="download_thumbnail_summary">不加载缩略图时,可以节省数据和存储空间。更改后将清除存储空间和扩展空间的缓存。</string>
<string name="auto_queue_title">自动排列下一个媒体</string>
<string name="auto_queue_summary">在非重复排列中播放最后一个媒体时,自动推荐相关媒体。</string>
<string name="player_gesture_controls_title">玩家手势控制</string>
<string name="player_gesture_controls_summary">使用手势控制播放器的亮度和音量</string>
<string name="show_search_suggestions_title">搜索建议</string>
<string name="show_search_suggestions_summary">搜索时显示建议</string>
<string name="enable_search_history_title">搜索历史记录</string>
<string name="enable_search_history_summary">在本地存储搜索</string>
<string name="enable_watch_history_title">历史记录和缓存数据</string>
<string name="enable_watch_history_summary">记录观看过的视频</string>
<string name="resume_on_audio_focus_gain_title">取得视窗焦点时继续播放</string>
<string name="resume_on_audio_focus_gain_summary">在被打断后继续播放(例如有来电)</string>
<string name="download_dialog_title">下载</string>
<string name="next_video_title">下一部</string>
<string name="show_next_and_similar_title">显示「下一部」及「相关」的视频</string>
<string name="show_hold_to_append_title">显示「长按以新增」的提示</string>
<string name="show_hold_to_append_summary">在视频详细信息页按下后台播放或悬浮窗按钮时提示</string>
<string name="url_not_supported_toast">不支持该网址</string>
<string name="default_content_country_title">默认内容的国家</string>
<string name="service_title">服务</string>
<string name="search_language_title">默认内容的语言</string>
<string name="settings_category_player_title">播放器</string>
<string name="settings_category_player_behavior_title">行为</string>
<string name="settings_category_video_audio_title">视频与音频</string>
<string name="settings_category_history_title">历史记录和缓存数据</string>
<string name="settings_category_popup_title">悬浮窗</string>
<string name="settings_category_appearance_title">外观</string>
<string name="settings_category_other_title">其他</string>
<string name="settings_category_debug_title">调试</string>
<string name="background_player_playing_toast">在后台播放</string>
<string name="popup_playing_toast">在悬浮窗下播放</string>
<string name="background_player_append">已添加到后台播放队列</string>
<string name="popup_playing_append">已添加到悬浮窗播放队列</string>
<string name="play_btn_text">播放</string>
<string name="content">内容</string>
<string name="show_age_restricted_content_title">年龄限制内容</string>
<string name="video_is_age_restricted">显示有年龄限制的视频。可以从「设置」中允许该内容。</string>
<string name="duration_live">直播</string>
<string name="downloads">下载</string>
<string name="downloads_title">下载</string>
<string name="error_report_title">错误报告</string>
<string name="all">所有</string>
<string name="channel">频道</string>
<string name="channels">频道</string>
<string name="playlist">播放列表</string>
<string name="playlists">播放列表</string>
<string name="tracks">曲目</string>
<string name="users">用户</string>
<string name="yes">是的</string>
<string name="later">稍等</string>
<string name="disabled">不适用</string>
<string name="filter">过滤</string>
<string name="refresh">刷新</string>
<string name="clear">清除</string>
<string name="popup_resizing_indicator_title">调整</string>
<string name="best_resolution">最佳分辨率</string>
<string name="undo">复原</string>
<string name="play_all">全部播放</string>
<string name="always">总是</string>
<string name="just_once">仅一次</string>
<string name="file">文件</string>
<string name="notification_channel_name">NewPipe 通知</string>
<string name="notification_channel_description">NewPipe 后台播放和悬浮窗播放的通知</string>
<string name="unknown_content">[未知]</string>
<string name="toggle_orientation">切换方向</string>
<string name="switch_to_background">切换到后台</string>
<string name="switch_to_popup">切换到悬浮窗</string>
<string name="switch_to_main">切换到首页</string>
<string name="import_data_title">导入数据库</string>
<string name="export_data_title">导出数据库</string>
<string name="import_data_summary">覆盖您当前的历史记录和订阅</string>
<string name="export_data_summary">导出历史记录、订阅和播放列表</string>
<string name="clear_views_history_summary">删除播放过的媒体历史记录</string>
<string name="delete_view_history_alert">确定要清除所有观看历史记录吗?</string>
<string name="view_history_deleted">观看历史记录已清除。</string>
<string name="clear_search_history_title">清除搜索历史记录</string>
<string name="clear_search_history_summary">清除搜索关键词的历史记录</string>
<string name="delete_search_history_alert">确定要清除所有搜索历史记录吗?</string>
<string name="could_not_load_thumbnails">无法加载所有缩略图</string>
<string name="youtube_signature_decryption_error">无法解析视频网址签名</string>
<string name="parsing_error">无法解析网站</string>
<string name="light_parsing_error">无法完全解析网站</string>
<string name="content_not_available">内容不可用</string>
<string name="blocked_by_gema">内容被 GEMA 封锁</string>
<string name="could_not_setup_download_menu">无法设置下载菜单</string>
<string name="live_streams_not_supported">目前还不支持观看直播</string>
<string name="could_not_get_stream">无法获得任何媒体</string>
<string name="could_not_load_image">无法加载图片</string>
<string name="app_ui_crash">应用程序或界面出现崩溃了</string>
<string name="player_stream_failure">无法播放此媒体</string>
<string name="player_unrecoverable_failure">发生了无法恢复的播放器错误</string>
<string name="player_recoverable_failure">正在从播放器错误中恢复</string>
<string name="external_player_unsupported_link_type">外部播放器不支持此类型的链接</string>
<string name="invalid_url_toast">无效的网址</string>
<string name="video_streams_empty">找不到视频串流</string>
<string name="audio_streams_empty">找不到音频串流</string>
<string name="invalid_directory">无效的文件夹</string>
<string name="invalid_source">无效的文件/内容来源</string>
<string name="invalid_file">该文件不存在或缺少读写权限</string>
<string name="file_name_empty_error">文件名不能为空</string>
<string name="error_occurred_detail">发生错误:%1$s</string>
<string name="no_streams_available_download">没有可供下载的串流</string>
<string name="sorry_string">抱歉,这不应该发生的。</string>
<string name="error_report_button_text">通过电子邮件报告错误</string>
<string name="error_snackbar_message">抱歉,发生了一些错误。</string>
<string name="error_snackbar_action">报告</string>
<string name="what_device_headline">信息:</string>
<string name="what_happened_headline">发生了什么:</string>
<string name="info_labels">事件:\\n请求\\n内容语言\\n服务\\nGMT 时间:\\n组件\\n版本\\n系统版本</string>
<string name="your_comment">您的评论(请用英语):</string>
<string name="error_details_headline">详细:</string>
<string name="list_thumbnail_view_description">视频预览缩略图</string>
<string name="detail_thumbnail_view_description">视频预览缩略图</string>
<string name="detail_uploader_thumbnail_view_description">上传者的头像缩略图</string>
<string name="detail_likes_img_view_description">喜欢</string>
<string name="detail_dislikes_img_view_description">不喜欢</string>
<string name="use_tor_title">使用 Tor</string>
<string name="use_tor_summary">(实验性)通过 Tor 强制下载流量以增加隐私(暂不支持视频媒体)。</string>
<string name="user_report">用户报告</string>
<string name="empty_subscription_feed_subtitle">这里什么都没有</string>
<string name="detail_drag_description">拖动以重新排序</string>
<string name="err_dir_create">无法创建下载目录「%1$s」</string>
<string name="info_dir_created">已成功创建下载目录「%1$s」</string>
<string name="video">视频</string>
<string name="audio">音频</string>
<string name="retry">重试</string>
<string name="storage_permission_denied">手机存储访问权限被拒绝</string>
<string name="use_old_player_title">使用旧的播放器</string>
<string name="use_old_player_summary">旧的内置 Mediaframework 播放器</string>
<string name="short_thousand"></string>
<string name="short_million"></string>
<string name="short_billion">亿</string>
<string name="no_subscribers">没有订阅者</string>
<plurals name="subscribers">
<item quantity="one">%s 位订阅者</item>
<item quantity="other"></item>
</plurals>
<string name="no_views">无观看次数</string>
<plurals name="views">
<item quantity="one">%s 次观看</item>
<item quantity="other"></item>
</plurals>
<string name="no_videos">没有视频</string>
<plurals name="videos">
<item quantity="one">%s 部视频</item>
<item quantity="other"></item>
</plurals>
<string name="delete_one">删除</string>
<string name="checksum">校验</string>
<string name="dismiss">退出</string>
<string name="rename">重命名</string>
<string name="msg_name">文件名</string>
<string name="msg_threads">线程</string>
<string name="msg_url_malform">错误的网址或网络不可用</string>
<string name="msg_running_detail">点按以查看详细信息</string>
<string name="msg_copied">复制到剪贴板</string>
<string name="no_available_dir">请选择下载文件夹</string>
<string name="msg_popup_permission">在悬浮窗模式打开
\n需要此权限</string>
<string name="one_item_deleted">已删除一个项目。</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">reCAPTCHA 验证</string>
<string name="recaptcha_request_toast">需完成 reCAPTCHA 验证</string>
<string name="settings_category_downloads_title">下载</string>
<string name="settings_file_charset_title">文件名中允许的字符</string>
<string name="settings_file_replacement_character_summary">无效字符将替换为该值</string>
<string name="settings_file_replacement_character_title">替换字符</string>
<string name="copyright" formatted="true">© %1$s 由 %2$s 使用 %3$s 版权所有</string>
<string name="error_unable_to_load_license">无法加载许可证</string>
<string name="tab_about">关于</string>
<string name="tab_contributors">贡献者</string>
<string name="tab_licenses">许可证</string>
<string name="app_description">安卓上开源且轻便的媒体播放器。</string>
<string name="contribution_title">贡献</string>
<string name="contribution_encouragement">您是否有想法帮助我们:翻译、界面设计、代码优化以及真正繁重的功能扩展 - 我们随时欢迎您提供帮助。让 NewPipe 越变越好!</string>
<string name="view_on_github">在 GitHub 上查看</string>
<string name="donation_title">捐赠</string>
<string name="donation_encouragement">NewPipe 由社区人员维护和开发额,他们耗费时间务求为您带来最佳体验。现在是时候回过头来,让我们的开发人员能够在使 NewPipe 更加完美的同时,享受一杯咖啡。</string>
<string name="give_back">回馈</string>
<string name="website_title">网站</string>
<string name="website_encouragement">访问 NewPipe 网站了解更多信息和新闻。</string>
<string name="privacy_policy_title">NewPipe 的隐私政策</string>
<string name="privacy_policy_encouragement">NewPipe 项目是非常重视您的隐私。因此,未经您的同意,该应用程序不会收集任何数据。
\nNewPipe 的隐私政策详细说明了当您发送崩溃报告时,什么资料会被传送及储存。</string>
<string name="read_privacy_policy">阅读隐私政策</string>
<string name="app_license_title">NewPipe 的许可证</string>
<string name="app_license">NewPipe 是一个 Copyleft 的自由软件:您可以随意使用、研究、分享或改进它。在遵守由自由软件基金会所发布的 GNU 通用公共授权条款的状况下,您可以自由地再发布或修改它;授权条款预设使用第三版,但您也可以选择更新的版本。</string>
<string name="read_full_license">阅读许可证</string>
<string name="title_activity_history">历史记录</string>
<string name="title_history_search">搜索</string>
<string name="title_history_view">观看</string>
<string name="history_disabled">历史记录被关闭了</string>
<string name="action_history">历史记录</string>
<string name="history_empty">没有历史记录</string>
<string name="history_cleared">清除历史记录</string>
<string name="item_deleted">项目已删除</string>
<string name="delete_item_search_history">确定要从搜索历史记录中删除该项吗?</string>
<string name="delete_stream_history_prompt">确定要从观看历史记录中删除该项吗?</string>
<string name="delete_all_history_prompt">您确定要删除历史记录中的所有项吗?</string>
<string name="title_last_played">上一次播放</string>
<string name="title_most_played">最受欢迎</string>
<string name="main_page_content">首页内容</string>
<string name="blank_page_summary">空白页面</string>
<string name="kiosk_page_summary">互动页面</string>
<string name="subscription_page_summary">订阅页面</string>
<string name="feed_page_summary">Feed 页面</string>
<string name="channel_page_summary">频道页面</string>
<string name="select_a_channel">选择一个频道</string>
<string name="no_channel_subscribed_yet">尚未订阅任何频道</string>
<string name="select_a_kiosk">选择一个互动</string>
<string name="export_complete_toast">输出</string>
<string name="import_complete_toast">接入</string>
<string name="no_valid_zip_file">无效的压缩文件</string>
<string name="could_not_import_all_files">警告:无法导入所有文件。</string>
<string name="override_current_data">这将覆盖您当前的设定。</string>
<string name="import_settings">您是否要导入设定?</string>
<string name="kiosk">互动</string>
<string name="trending">趋势</string>
<string name="top_50">前 50</string>
<string name="new_and_hot">最新和热门</string>
<string name="title_activity_background_player">转到后台播放</string>
<string name="title_activity_popup_player">悬浮窗播放</string>
<string name="play_queue_remove">移除</string>
<string name="play_queue_stream_detail">详细</string>
<string name="play_queue_audio_settings">音频设置</string>
<string name="hold_to_append">长按以新增至队列</string>
<string name="enqueue_on_background">转到后台时排列</string>
<string name="enqueue_on_popup">排列新的悬浮窗</string>
<string name="start_here_on_main">从这里开始播放</string>
<string name="start_here_on_background">转到后台时从这里开始</string>
<string name="start_here_on_popup">从新的悬浮窗开始</string>
<string name="drawer_open">打开抽屉</string>
<string name="drawer_close">关闭抽屉</string>
<string name="drawer_header_action_paceholder_text">某些东西即将在此出现D</string>
<string name="preferred_open_action_settings_title">偏好的「开启」动作</string>
<string name="preferred_open_action_settings_summary">开启内容时的默认动作 - %s</string>
<string name="video_player">视频播放器</string>
<string name="background_player">转到后台播放</string>
<string name="popup_player">悬浮窗播放</string>
<string name="always_ask_open_action">总是询问</string>
<string name="preferred_player_fetcher_notification_title">正在获取信息…</string>
<string name="preferred_player_fetcher_notification_message">正在载入请求的内容</string>
<string name="create_playlist">新的播放列表</string>
<string name="delete_playlist">删除</string>
<string name="rename_playlist">重命名</string>
<string name="playlist_name_input">名称</string>
<string name="append_playlist">添加到播放列表</string>
<string name="set_as_playlist_thumbnail">设为播放列表缩略图</string>
<string name="bookmark_playlist">将播放列表加入书签</string>
<string name="playlist_thumbnail_change_success">播放列表缩略图已更改。</string>
<string name="playlist_delete_failure">无法删除播放列表。</string>
<string name="caption_none">没有字幕</string>
<string name="resize_fit">合适的</string>
<string name="resize_fill">填满</string>
<string name="resize_zoom">缩放</string>
<string name="caption_auto_generated">自动生成</string>
<string name="caption_setting_title">字幕</string>
<string name="caption_setting_description">修改播放器标题文本比例和背景样式。需要重启才能生效。</string>
<string name="enable_leak_canary_title">启用 LeakCanary</string>
<string name="enable_leak_canary_summary">内存泄漏监视可能导致应用程序在存储时无响应</string>
<string name="enable_disposed_exceptions_title">报告活动周期外错误</string>
<string name="enable_disposed_exceptions_summary">强制报告在处理完片段或活动周期外发生的无法传递的 Rx 异常</string>
<string name="import_export_title">导入/导出</string>
<string name="import_title">导入</string>
<string name="import_from">导入至</string>
<string name="export_to">导出到</string>
<string name="import_ongoing">正在导入…</string>
<string name="export_ongoing">正在导出…</string>
<string name="import_file_title">导入文件</string>
<string name="previous_export">之前的导出</string>
<string name="subscriptions_import_unsuccessful">无法导入订阅</string>
<string name="subscriptions_export_unsuccessful">无法导出订阅</string>
<string name="import_youtube_instructions">通过下载导出文件来导入 YouTube 订阅:
\n
\n1.移至该网址:%1$s
\n2.当被询问时登入帐号
\n3.应该开始下载(这是导出文件)</string>
<string name="import_soundcloud_instructions">通过输入 URL 或 ID 来导入 SoundCloud的配置文件
\n
\n1.在浏览器中启用「桌面模式」(该网站不适用于移动设备)
\n2.移至该网址:%1$s
\n3.当被询问时登入帐号
\n4.复制您重定向的配置文件到网址。</string>
<string name="import_soundcloud_instructions_hint">yourID, soundcloud.com/yourid</string>
<string name="import_network_expensive_warning">请记住,此操作可能造成昂贵的网络花费。
\n
\n您是否要继续</string>
<string name="playback_speed_control">播放速度控制</string>
<string name="playback_tempo">速度</string>
<string name="playback_pitch">音量</string>
<string name="unhook_checkbox">取消链接(可能会导致扭曲)</string>
<string name="skip_silence_checkbox">静音时快进</string>
</resources>

View File

@ -9,17 +9,17 @@
<string name="download">Stáhnout</string>
<string name="search">Vyhledat</string>
<string name="settings">Nastavení</string>
<string name="did_you_mean">Měli jste na mysli: %1$s?</string>
<string name="did_you_mean">Mysleli jste: %1$s\?</string>
<string name="share_dialog_title">Sdílet s</string>
<string name="choose_browser">Vybrat prohlížeč</string>
<string name="screen_rotation">otočení</string>
<string name="use_external_video_player_title">Použít externí video přehrávač</string>
<string name="use_external_audio_player_title">Použít externí audio přehrávač</string>
<string name="download_path_audio_summary">Cesta, kam se uloží stažené audio</string>
<string name="download_path_audio_summary">Stažené audio je uloženo zde</string>
<string name="download_path_audio_dialog_title">Zadejte umístění pro stažené audio soubory</string>
<string name="download_path_audio_title">Umístění pro stažené audio</string>
<string name="download_path_audio_title">Složka pro stažené audio</string>
<string name="default_resolution_title">Výchozí rozlišení</string>
<string name="play_with_kodi_title">Přehrát pomocí Kodi</string>
<string name="kore_not_found">Aplikace Kore nenalezena. Chcete ji nainstalovat?</string>
@ -67,9 +67,9 @@
<string name="autoplay_by_calling_app_title">Automaticky přehrávat</string>
<string name="autoplay_by_calling_app_summary">Přehrává video, když je NewPipe otevřen z jiné aplikace</string>
<string name="content">Obsah</string>
<string name="show_age_restricted_content_title">Zobrazovat věkově omezený obsah</string>
<string name="video_is_age_restricted">Toto video je věkově omezeno. Povolte věkově omezená videa v nastavení.</string>
<string name="duration_live">živě</string>
<string name="show_age_restricted_content_title">Věkově omezený obsah</string>
<string name="video_is_age_restricted">Zobrazit video s věkovým omezením. Povolit tento obsah lze v \"Nastavení\".</string>
<string name="duration_live">ŽIVĚ</string>
<string name="light_parsing_error">Nebylo možné kompletně analyzovat stránku</string>
<string name="main_bg_subtitle">Začni stiskem hledat</string>
@ -101,7 +101,7 @@
<string name="error_snackbar_message">Omlouváme se, nastaly určité chyby.</string>
<string name="could_not_load_image">Nepodařilo se nahrát obrázek</string>
<string name="app_ui_crash">Aplikace/UI spadlo</string>
<string name="live_streams_not_supported">Tento stream je vysílán živě, funkce ještě není podporována.</string>
<string name="live_streams_not_supported">Živé streamy zatím nejsou podporovány</string>
<string name="could_not_get_stream">Nepodařilo se dostat žádný stream</string>
<string name="could_not_setup_download_menu">Nepodařilo se nastavit menu stahování</string>
<string name="error_report_title">Nahlásit chybu</string>
@ -142,7 +142,7 @@
otevření ve vyskakovacím okně</string>
<string name="use_old_player_title">Použít starý přehrávač</string>
<string name="use_external_video_player_summary">Některé formáty rozlišení NEBUDOU obsahovat zvukovou stopu po zapnutí této funkce</string>
<string name="use_external_video_player_summary">Odstraňuje zvuk v některých rozlišeních</string>
<string name="show_higher_resolutions_title">Zobrazovat vyšší rozlišení</string>
<string name="show_higher_resolutions_summary">Pouze některá zařízení podporují přehrávání 2K/4K videí</string>
<string name="default_video_format_title">Výchozí formát videa</string>
@ -252,7 +252,7 @@ otevření ve vyskakovacím okně</string>
<string name="history_empty">Historie je prázdná</string>
<string name="history_cleared">Historie vymazána</string>
<string name="item_deleted">Položka byla odstraněna</string>
<string name="show_hold_to_append_title">Zobrazovat tip \"podržet pro přidání\"</string>
<string name="show_hold_to_append_title">Zobrazovat tip \"Podržet pro přidání\"</string>
<string name="show_hold_to_append_summary">Zobrazí se po stisku tlačítek přehrát na pozadí nebo přehrát v okně na stránce s videem</string>
<string name="background_player_append">Ve frontě přehrávače na pozadí</string>
<string name="popup_playing_append">Ve frontě přehrávače v okně</string>
@ -289,7 +289,7 @@ otevření ve vyskakovacím okně</string>
<string name="enqueue_on_background">Do fronty na pozadí</string>
<string name="enqueue_on_popup">Do fronty v okně</string>
<string name="start_here_on_main">Začne hrát zde</string>
<string name="start_here_on_background">Začne zde na pozadí</string>
<string name="start_here_on_background">Začne zde, když na pozadí</string>
<string name="start_here_on_popup">Začne zde v okně</string>
<string name="donation_title">Donate</string>
<string name="donation_encouragement">NewPipe je vyvíjen dobrovolníky, kteří tráví svůj čas, aby vaše zkušenost s aplikací byla co nejlepší. Vraťte vývojářům něco zpět, aby mohli NewPipe dále zlepšovat a zároveň si vychutnat šálek kávy.</string>
@ -305,7 +305,7 @@ otevření ve vyskakovacím okně</string>
<string name="drawer_open">Otevřít Drawer</string>
<string name="drawer_close">Zavřít Drawer</string>
<string name="no_player_found_toast">Nenalezen přehrávač streamu (pro přehrání můžete nainstalovat např. VLC)</string>
<string name="no_player_found_toast">Nenalezen přehrávač streamu (pro přehrání můžete nainstalovat např. VLC).</string>
<string name="always">Vždy</string>
<string name="just_once">Pouze jednou</string>
@ -318,8 +318,8 @@ otevření ve vyskakovacím okně</string>
<string name="video_streams_empty">Nenalezeny žádné video streamy</string>
<string name="audio_streams_empty">Nenalezeny žádné audio streamy</string>
<string name="export_complete_toast">Export dokončen</string>
<string name="import_complete_toast">Import dokončen</string>
<string name="export_complete_toast">Exportováno</string>
<string name="import_complete_toast">Importováno</string>
<string name="no_valid_zip_file">Žádný platný soubor ZIP</string>
<string name="could_not_import_all_files">Upozornění: Nelze importovat všechny soubory.</string>
<string name="override_current_data">Tímto se anuluje vaše aktuální nastavení.</string>
@ -331,10 +331,10 @@ otevření ve vyskakovacím okně</string>
<string name="preferred_player_fetcher_notification_title">Získávám informace…</string>
<string name="preferred_player_fetcher_notification_message">Načítání požadovaného obsahu</string>
<string name="controls_download_desc">Stáhnout soubor streamu.</string>
<string name="controls_download_desc">Stáhnout soubor streamu</string>
<string name="show_info">Ukázat informace</string>
<string name="tab_bookmarks">Záložky</string>
<string name="tab_bookmarks">Uložené playlisty</string>
<string name="controls_add_to_playlist_title">Přidat do</string>
@ -356,9 +356,9 @@ otevření ve vyskakovacím okně</string>
<string name="always_ask_open_action">Vždy se zeptat</string>
<string name="create_playlist">Vytvořit nový playlist</string>
<string name="delete_playlist">Vymazat playlist</string>
<string name="rename_playlist">Přejmenovat playlist</string>
<string name="create_playlist">Nový playlist</string>
<string name="delete_playlist">Vymazat</string>
<string name="rename_playlist">Přejmenovat</string>
<string name="playlist_name_input">Jméno</string>
<string name="append_playlist">Přidat do playlistu</string>
<string name="set_as_playlist_thumbnail">Nastavit jako náhled playlistu</string>
@ -366,11 +366,11 @@ otevření ve vyskakovacím okně</string>
<string name="bookmark_playlist">Založit playlist</string>
<string name="unbookmark_playlist">Smazat záložku</string>
<string name="delete_playlist_prompt">Přejete si smazat tento playlist?</string>
<string name="delete_playlist_prompt">Smazat tento playlist\?</string>
<string name="playlist_creation_success">Playlist vytvořen</string>
<string name="playlist_add_stream_success">Přidáno do playlistu</string>
<string name="playlist_thumbnail_change_success">Náhled playlistu změněn</string>
<string name="playlist_delete_failure">Playlist nelze smazat</string>
<string name="playlist_add_stream_success">V playlistu</string>
<string name="playlist_thumbnail_change_success">Náhled playlistu změněn.</string>
<string name="playlist_delete_failure">Playlist nelze smazat.</string>
<string name="caption_none">Žádné titulky</string>
@ -397,22 +397,22 @@ otevření ve vyskakovacím okně</string>
<string name="use_inexact_seek_title">Použít rychlé nepřesné hledání</string>
<string name="use_inexact_seek_summary">Nepřesné hledání umožní přehrávači posouvat se rychleji, ale se sníženou přesností</string>
<string name="download_thumbnail_title">Načítat náhledy</string>
<string name="download_thumbnail_summary">Zakažte, chcete-li zabránit načítání všech náhledů a tím šetřit data a využití paměti. Změnou tohoto nastavení dojde k vyčištění mezipaměti obrázků</string>
<string name="download_thumbnail_summary">Po vypnutí se nestahují náhledy a tím se šetří data a využití paměti. Změna tohoto nastavení vyčistí mezipamět obrázků.</string>
<string name="thumbnail_cache_wipe_complete_notice">Mezipaměť obrázků vymazána</string>
<string name="metadata_cache_wipe_title">Vymazat metadata v mezipaměti</string>
<string name="metadata_cache_wipe_summary">Odebrat všechna data uložená v mezipaměti</string>
<string name="metadata_cache_wipe_complete_notice">Mezipaměť metadat vymazána</string>
<string name="auto_queue_title">Automatická fronta dalšího streamu</string>
<string name="auto_queue_summary">Automaticky připojí související stream při přehrávání posledního streamu v neopakující se frontě</string>
<string name="auto_queue_summary">Automaticky připojí související stream při přehrávání posledního streamu v neopakující se frontě.</string>
<string name="file">Soubor</string>
<string name="invalid_directory">Neplatný adresář</string>
<string name="invalid_source">Neplatný zdroj souboru/obsahu</string>
<string name="invalid_file">Soubor neexistuje nebo máte nedostatečné oprávnění k jeho čtení či zápisu</string>
<string name="invalid_directory">Neexistující složka</string>
<string name="invalid_source">Neexistující zdroj souboru/obsahu</string>
<string name="invalid_file">Soubor neexistuje nebo chybí oprávnění k jeho čtení či zápisu</string>
<string name="file_name_empty_error">Název souboru nesmí být prázdný</string>
<string name="error_occurred_detail">Došlo k chybě: %1$s</string>
<string name="import_export_title">Import/Export
<string name="import_export_title">Import/export
\n</string>
<string name="import_title">Importovat
\n</string>
@ -458,17 +458,17 @@ otevření ve vyskakovacím okně</string>
<string name="preferred_open_action_settings_summary">Výchozí chování při otevírání obsahu — %s</string>
<string name="caption_setting_title">Titulky</string>
<string name="caption_setting_description">Upravuje velikost textu titulků a styly pozadí. Změny se projeví po restartu aplikace</string>
<string name="caption_setting_description">Upravuje velikost textu titulků a styly pozadí. Změny se projeví po restartu aplikace.</string>
<string name="toast_no_player">K přehrání tohoto souboru chybí vhodná aplikace</string>
<string name="clear_views_history_title">Vymazat historii sledování</string>
<string name="clear_views_history_summary">Vymaže historii přehrávaných streamů</string>
<string name="delete_view_history_alert">Vymazat celkovou historii sledování.</string>
<string name="delete_view_history_alert">Vymazat celkovou historii sledování\?</string>
<string name="view_history_deleted">Historie sledování smazána.</string>
<string name="clear_search_history_title">Vymazat historii vyhledávání</string>
<string name="clear_search_history_summary">Vymaže historii vyhledávaných klíčových slov</string>
<string name="delete_search_history_alert">Vymazat celkovou historii vyhledávání.</string>
<string name="delete_search_history_alert">Vymazat celkovou historii vyhledávání\?</string>
<string name="search_history_deleted">Historie vyhledávání smazána.</string>
<string name="one_item_deleted">Jedna položka smazána.</string>
@ -495,7 +495,7 @@ otevření ve vyskakovacím okně</string>
<string name="limit_data_usage_none_description">Bez omezení</string>
<string name="limit_mobile_data_usage_title">Omezit rozlišení při použití mobilních dat</string>
<string name="minimize_on_exit_title">Minimalizovat při přepínání aplikací</string>
<string name="minimize_on_exit_summary">Akce při přepínání aplikací z hlavního přehrávače videa - %s</string>
<string name="minimize_on_exit_summary">Akce při přepínání aplikací z hlavního přehrávače videa %s</string>
<string name="minimize_on_exit_none_description">Žádná</string>
<string name="minimize_on_exit_background_description">Minimalizovat přehrávač na pozadí</string>
<string name="minimize_on_exit_popup_description">Minimalizovat přehrávač do vyskakovacího okna</string>

Some files were not shown because too many files have changed in this diff Show More