Merge branch 'dev' into wifi-check-fix

This commit is contained in:
Mauricio Colli 2018-08-12 23:20:21 -03:00 committed by GitHub
commit 607dc436bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 3871 additions and 1427 deletions

View File

@ -1,2 +1,3 @@
- [ ] I carefully read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them.
- [ ] I checked if the issue/feature exists in the latest version.
- [ ] I did use the [incredible bugreport to markdown converter](https://teamnewpipe.github.io/CrashReportToMarkdown/) to paste bug reports.

View File

@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 27
versionCode 64
versionName "0.13.5"
versionCode 66
versionName "0.13.7"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -42,10 +42,10 @@ android {
ext {
supportLibVersion = '27.1.1'
exoPlayerLibVersion = '2.7.3'
roomDbLibVersion = '1.0.0'
exoPlayerLibVersion = '2.8.2'
roomDbLibVersion = '1.1.1'
leakCanaryLibVersion = '1.5.4'
okHttpLibVersion = '1.5.0'
okHttpLibVersion = '3.10.0'
icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0'
}
@ -54,10 +54,10 @@ dependencies {
exclude module: 'support-annotations'
}
implementation 'com.github.TeamNewPipe:NewPipeExtractor:bf1c771'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:1eff8c5708'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
testImplementation 'org.mockito:mockito-core:2.8.9'
implementation "com.android.support:appcompat-v7:$supportLibVersion"
implementation "com.android.support:support-v4:$supportLibVersion"
@ -79,7 +79,7 @@ dependencies {
debugImplementation "com.facebook.stetho:stetho-urlconnection:$stethoLibVersion"
debugImplementation 'com.android.support:multidex:1.0.3'
implementation 'io.reactivex.rxjava2:rxjava:2.1.10'
implementation 'io.reactivex.rxjava2:rxjava:2.1.14'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
@ -93,6 +93,6 @@ dependencies {
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryLibVersion"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryLibVersion"
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
debugImplementation "com.facebook.stetho:stetho-okhttp3:$okHttpLibVersion"
implementation "com.squareup.okhttp3:okhttp:$okHttpLibVersion"
debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoLibVersion"
}

View File

@ -61,6 +61,8 @@ import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper;
import static org.schabi.newpipe.extractor.InfoItem.InfoType.PLAYLIST;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@ -106,7 +108,19 @@ public class MainActivity extends AppCompatActivity {
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerClosed(View view) { super.onDrawerClosed(view); }
@Override
public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); }
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0);
}
};
toggle.syncState();
drawer.addDrawerListener(toggle);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@ -133,13 +147,11 @@ public class MainActivity extends AppCompatActivity {
private boolean changeService(MenuItem item) {
if (item.getGroupId() == R.id.menu_services_group) {
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
} else {
if (item.getGroupId() != R.id.menu_services_group)
return false;
}
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
drawer.closeDrawers();
return true;
}
@ -382,31 +394,45 @@ public class MainActivity extends AppCompatActivity {
}
private void handleIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
try {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
String url = intent.getStringExtra(Constants.KEY_URL);
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay);
break;
case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(), serviceId, url, title);
break;
case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), serviceId, url, title);
break;
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
String url = intent.getStringExtra(Constants.KEY_URL);
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay);
break;
case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(),
serviceId,
url,
title);
break;
case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(),
serviceId,
url,
title);
break;
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
String searchString = intent.getStringExtra(Constants.KEY_SEARCH_STRING);
if (searchString == null) searchString = "";
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(
getSupportFragmentManager(),
serviceId,
searchString);
} else {
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
String searchQuery = intent.getStringExtra(Constants.KEY_QUERY);
if (searchQuery == null) searchQuery = "";
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(getSupportFragmentManager(), serviceId, searchQuery);
} else {
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
}

View File

@ -1,14 +1,19 @@
package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.app.FragmentManager;
import android.app.IntentService;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
@ -23,6 +28,7 @@ import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
@ -31,6 +37,8 @@ import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
@ -38,16 +46,19 @@ import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Observer;
import icepick.Icepick;
import icepick.State;
@ -77,6 +88,8 @@ public class RouterActivity extends AppCompatActivity {
protected String currentUrl;
protected CompositeDisposable disposables = new CompositeDisposable();
private boolean selectionIsDownload = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -104,7 +117,7 @@ public class RouterActivity extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
handleUrl(currentUrl);
}
@ -165,6 +178,7 @@ public class RouterActivity extends AppCompatActivity {
final String videoPlayerKey = getString(R.string.video_player_key);
final String backgroundPlayerKey = getString(R.string.background_player_key);
final String popupPlayerKey = getString(R.string.popup_player_key);
final String downloadKey = getString(R.string.download_key);
final String alwaysAskKey = getString(R.string.always_ask_open_action_key);
if (selectedChoiceKey.equals(alwaysAskKey)) {
@ -179,6 +193,8 @@ public class RouterActivity extends AppCompatActivity {
}
} else if (selectedChoiceKey.equals(showInfoKey)) {
handleChoice(showInfoKey);
} else if (selectedChoiceKey.equals(downloadKey)) {
handleChoice(downloadKey);
} else {
final boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
final boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
@ -236,7 +252,9 @@ public class RouterActivity extends AppCompatActivity {
.setCancelable(true)
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
.setPositiveButton(R.string.always, dialogButtonsClickListener)
.setOnDismissListener((dialog) -> finish())
.setOnDismissListener((dialog) -> {
if(!selectionIsDownload) finish();
})
.create();
//noinspection CodeBlock2Expr
@ -316,6 +334,9 @@ public class RouterActivity extends AppCompatActivity {
resolveResourceIdFromAttr(context, R.attr.audio)));
}
returnList.add(new AdapterChoiceItem(getString(R.string.download_key), getString(R.string.download),
resolveResourceIdFromAttr(context, R.attr.download)));
return returnList;
}
@ -347,6 +368,14 @@ public class RouterActivity extends AppCompatActivity {
return;
}
if (selectedChoiceKey.equals(getString(R.string.download_key))) {
if (PermissionHelper.checkStoragePermissions(this, PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
selectionIsDownload = true;
openDownloadDialog();
}
return;
}
// stop and bypass FetcherService if InfoScreen was selected since
// StreamDetailFragment can fetch data itself
if (selectedChoiceKey.equals(getString(R.string.show_info_key))) {
@ -373,6 +402,47 @@ public class RouterActivity extends AppCompatActivity {
finish();
}
@SuppressLint("CheckResult")
private void openDownloadDialog() {
ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((@NonNull StreamInfo result) -> {
List<VideoStream> sortedVideoStreams = ListHelper.getSortedStreamVideosList(this,
result.getVideoStreams(),
result.getVideoOnlyStreams(),
false);
int selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(this,
sortedVideoStreams);
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
downloadDialog.setVideoStreams(sortedVideoStreams);
downloadDialog.setAudioStreams(result.getAudioStreams());
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.show(fm, "downloadDialog");
fm.executePendingTransactions();
downloadDialog.getDialog().setOnDismissListener(dialog -> {
finish();
});
}, (@NonNull Throwable throwable) -> {
onError();
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
for (int i: grantResults){
if (i == PackageManager.PERMISSION_DENIED){
finish();
return;
}
}
if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) {
openDownloadDialog();
}
}
private static class AdapterChoiceItem {
final String description, key;
@DrawableRes final int icon;

View File

@ -71,6 +71,14 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
info.getUploaderName(), info.getStreamCount());
}
@Ignore
public boolean isIdenticalTo(final PlaylistInfo info) {
return getServiceId() == info.getServiceId() && getName().equals(info.getName()) &&
getStreamCount() == info.getStreamCount() && getUrl().equals(info.getUrl()) &&
getThumbnailUrl().equals(info.getThumbnailUrl()) &&
getUploader().equals(info.getUploaderName());
}
public long getUid() {
return uid;
}

View File

@ -0,0 +1,158 @@
package org.schabi.newpipe.download;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BaseTransientBottomBar;
import android.support.design.widget.Snackbar;
import android.view.View;
import org.schabi.newpipe.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.get.DownloadMission;
public class DeleteDownloadManager {
private static final String KEY_STATE = "delete_manager_state";
private View mView;
private HashSet<String> mPendingMap;
private List<Disposable> mDisposableList;
private DownloadManager mDownloadManager;
private PublishSubject<DownloadMission> publishSubject = PublishSubject.create();
DeleteDownloadManager(Activity activity) {
mPendingMap = new HashSet<>();
mDisposableList = new ArrayList<>();
mView = activity.findViewById(android.R.id.content);
}
public Observable<DownloadMission> getUndoObservable() {
return publishSubject;
}
public boolean contains(@NonNull DownloadMission mission) {
return mPendingMap.contains(mission.url);
}
public void add(@NonNull DownloadMission mission) {
mPendingMap.add(mission.url);
if (mPendingMap.size() == 1) {
showUndoDeleteSnackbar(mission);
}
}
public void setDownloadManager(@NonNull DownloadManager downloadManager) {
mDownloadManager = downloadManager;
if (mPendingMap.size() < 1) return;
showUndoDeleteSnackbar();
}
public void restoreState(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) return;
List<String> list = savedInstanceState.getStringArrayList(KEY_STATE);
if (list != null) {
mPendingMap.addAll(list);
}
}
public void saveState(@Nullable Bundle outState) {
if (outState == null) return;
for (Disposable disposable : mDisposableList) {
disposable.dispose();
}
outState.putStringArrayList(KEY_STATE, new ArrayList<>(mPendingMap));
}
private void showUndoDeleteSnackbar() {
if (mPendingMap.size() < 1) return;
String url = mPendingMap.iterator().next();
for (int i = 0; i < mDownloadManager.getCount(); i++) {
DownloadMission mission = mDownloadManager.getMission(i);
if (url.equals(mission.url)) {
showUndoDeleteSnackbar(mission);
break;
}
}
}
private void showUndoDeleteSnackbar(@NonNull DownloadMission mission) {
final Snackbar snackbar = Snackbar.make(mView, mission.name, Snackbar.LENGTH_INDEFINITE);
final Disposable disposable = Observable.timer(3, TimeUnit.SECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(l -> snackbar.dismiss());
mDisposableList.add(disposable);
snackbar.setAction(R.string.undo, v -> {
mPendingMap.remove(mission.url);
publishSubject.onNext(mission);
disposable.dispose();
snackbar.dismiss();
});
snackbar.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
if (!disposable.isDisposed()) {
Completable.fromAction(() -> deletePending(mission))
.subscribeOn(Schedulers.io())
.subscribe();
}
mPendingMap.remove(mission.url);
snackbar.removeCallback(this);
mDisposableList.remove(disposable);
showUndoDeleteSnackbar();
}
});
snackbar.show();
}
public void deletePending() {
if (mPendingMap.size() < 1) return;
HashSet<Integer> idSet = new HashSet<>();
for (int i = 0; i < mDownloadManager.getCount(); i++) {
if (contains(mDownloadManager.getMission(i))) {
idSet.add(i);
}
}
for (Integer id : idSet) {
mDownloadManager.deleteMission(id);
}
mPendingMap.clear();
}
private void deletePending(@NonNull DownloadMission mission) {
for (int i = 0; i < mDownloadManager.getCount(); i++) {
if (mission.url.equals(mDownloadManager.getMission(i).url)) {
mDownloadManager.deleteMission(i);
break;
}
}
}
}

View File

@ -15,12 +15,17 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.settings.SettingsActivity;
import org.schabi.newpipe.util.ThemeHelper;
import io.reactivex.Completable;
import io.reactivex.schedulers.Schedulers;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.fragment.AllMissionsFragment;
import us.shandian.giga.ui.fragment.MissionsFragment;
public class DownloadActivity extends AppCompatActivity {
private static final String MISSIONS_FRAGMENT_TAG = "fragment_tag";
private DeleteDownloadManager mDeleteDownloadManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
// Service
@ -42,21 +47,35 @@ public class DownloadActivity extends AppCompatActivity {
actionBar.setDisplayShowTitleEnabled(true);
}
// Fragment
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
updateFragments();
getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
mDeleteDownloadManager = new DeleteDownloadManager(this);
mDeleteDownloadManager.restoreState(savedInstanceState);
MissionsFragment fragment = (MissionsFragment) getFragmentManager().findFragmentByTag(MISSIONS_FRAGMENT_TAG);
if (fragment != null) {
fragment.setDeleteManager(mDeleteDownloadManager);
} else {
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
updateFragments();
getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
mDeleteDownloadManager.saveState(outState);
super.onSaveInstanceState(outState);
}
private void updateFragments() {
MissionsFragment fragment = new AllMissionsFragment();
fragment.setDeleteManager(mDeleteDownloadManager);
getFragmentManager().beginTransaction()
.replace(R.id.frame, fragment)
.replace(R.id.frame, fragment, MISSIONS_FRAGMENT_TAG)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
@ -80,6 +99,7 @@ public class DownloadActivity extends AppCompatActivity {
case R.id.action_settings: {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
deletePending();
return true;
}
default:
@ -87,4 +107,15 @@ public class DownloadActivity extends AppCompatActivity {
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
deletePending();
}
private void deletePending() {
Completable.fromAction(mDeleteDownloadManager::deletePending)
.subscribeOn(Schedulers.io())
.subscribe();
}
}

View File

@ -10,6 +10,7 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
@ -127,7 +128,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
NavigationHelper.openSearchFragment(getFragmentManager(), ServiceHelper.getSelectedServiceId(activity), "");
try {
NavigationHelper.openSearchFragment(
getFragmentManager(),
ServiceHelper.getSelectedServiceId(activity),
"");
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
return true;
}
return super.onOptionsItemSelected(item);
@ -226,7 +234,9 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
FALLBACK_CHANNEL_URL);
String name = preferences.getString(getString(R.string.main_page_selected_channel_name),
FALLBACK_CHANNEL_NAME);
ChannelFragment fragment = ChannelFragment.getInstance(serviceId, url, name);
ChannelFragment fragment = ChannelFragment.getInstance(serviceId,
url,
name);
fragment.useAsFrontPage(true);
return fragment;
} else {
@ -255,20 +265,13 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
for (final String ks : kl.getAvailableKiosks()) {
menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE,
KioskTranslator.getTranslatedKioskName(ks, getContext()))
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
.setOnMenuItemClickListener(menuItem -> {
try {
NavigationHelper.openKioskFragment(getFragmentManager(), currentServiceId, ks);
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
return true;
}
});
i++;
}

View File

@ -17,6 +17,7 @@ import android.support.v4.content.ContextCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
@ -54,14 +55,17 @@ import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
@ -128,7 +132,7 @@ public class VideoDetailFragment
private StreamInfo currentInfo;
private Disposable currentWorker;
private CompositeDisposable disposables = new CompositeDisposable();
@NonNull private CompositeDisposable disposables = new CompositeDisposable();
private List<VideoStream> sortedVideoStreams;
private int selectedVideoStreamIndex = -1;
@ -363,11 +367,15 @@ public class VideoDetailFragment
if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) {
Log.w(TAG, "Can't open channel because we got no channel URL");
} else {
NavigationHelper.openChannelFragment(
getFragmentManager(),
currentInfo.getServiceId(),
currentInfo.getUploaderUrl(),
currentInfo.getUploaderName());
try {
NavigationHelper.openChannelFragment(
getFragmentManager(),
currentInfo.getServiceId(),
currentInfo.getUploaderUrl(),
currentInfo.getUploaderName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
break;
case R.id.detail_thumbnail_root_layout:
@ -540,7 +548,8 @@ public class VideoDetailFragment
final String[] commands = new String[]{
context.getResources().getString(R.string.enqueue_on_background),
context.getResources().getString(R.string.enqueue_on_popup),
context.getResources().getString(R.string.append_playlist)
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
@ -557,6 +566,9 @@ public class VideoDetailFragment
.show(getFragmentManager(), TAG);
}
break;
case 3:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@ -872,10 +884,7 @@ public class VideoDetailFragment
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
openNormalBackgroundPlayer(append);
} else {
NavigationHelper.playOnExternalPlayer(activity,
currentInfo.getName(),
currentInfo.getUploaderName(),
audioStream);
startOnExternalPlayer(activity, currentInfo, audioStream);
}
}
@ -902,10 +911,7 @@ public class VideoDetailFragment
if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
NavigationHelper.playOnExternalPlayer(activity,
currentInfo.getName(),
currentInfo.getUploaderName(),
selectedVideoStream);
startOnExternalPlayer(activity, currentInfo, selectedVideoStream);
} else {
openNormalPlayer(selectedVideoStream);
}
@ -949,6 +955,20 @@ public class VideoDetailFragment
this.autoPlayEnabled = autoplay;
}
private void startOnExternalPlayer(@NonNull final Context context,
@NonNull final StreamInfo info,
@NonNull final Stream selectedStream) {
NavigationHelper.playOnExternalPlayer(context, currentInfo.getName(),
currentInfo.getUploaderName(), selectedStream);
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
disposables.add(recordManager.onViewed(info).onErrorComplete()
.subscribe(
ignored -> {/* successful */},
error -> Log.e(TAG, "Register view failure: ", error)
));
}
@Nullable
private VideoStream getSelectedVideoStream() {
return sortedVideoStreams != null ? sortedVideoStreams.get(selectedVideoStreamIndex) : null;

View File

@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@ -24,6 +25,7 @@ import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.StateSaver;
@ -152,18 +154,35 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
try {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(useAsFrontPage ?
getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});
infoListAdapter.setOnPlaylistSelectedListener(new OnClickGesture<PlaylistInfoItem>() {
@Override
public void selected(PlaylistInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
try {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(
useAsFrontPage
? getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});
@ -178,7 +197,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
private void onStreamSelected(StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
NavigationHelper.openVideoDetailFragment(useAsFrontPage
? getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
@ -196,7 +217,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
final String[] commands = new String[]{
context.getResources().getString(R.string.enqueue_on_background),
context.getResources().getString(R.string.enqueue_on_popup),
context.getResources().getString(R.string.append_playlist)
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@ -213,6 +235,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
.show(getFragmentManager(), TAG);
}
break;
case 3:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}

View File

@ -8,6 +8,9 @@ import android.view.View;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.util.Constants;
import java.util.Queue;
@ -166,7 +169,6 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
public void handleResult(@NonNull I result) {
super.handleResult(result);
url = result.getUrl();
name = result.getName();
setTitle(name);

View File

@ -33,6 +33,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
@ -161,7 +162,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
context.getResources().getString(R.string.start_here_on_main),
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.append_playlist)
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
@ -190,6 +192,9 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
.show(getFragmentManager(), TAG);
}
break;
case 6:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@ -497,7 +502,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
if (super.onError(exception)) return true;
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId), url, errorId);
onUnrecoverableError(exception,
UserAction.REQUESTED_CHANNEL,
NewPipe.getNameOfService(serviceId),
url,
errorId);
return true;
}

View File

@ -11,22 +11,20 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.UrlIdHandler;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper;
import icepick.State;
import io.reactivex.Single;
@ -74,10 +72,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
throws ExtractionException {
KioskFragment instance = new KioskFragment();
StreamingService service = NewPipe.getService(serviceId);
UrlIdHandler kioskTypeUrlIdHandler = service.getKioskList()
.getUrlIdHandlerByType(kioskId);
ListLinkHandlerFactory kioskLinkHandlerFactory = service.getKioskList()
.getListLinkHandlerFactoryByType(kioskId);
instance.setInitialData(serviceId,
kioskTypeUrlIdHandler.getUrl(kioskId), kioskId);
kioskLinkHandlerFactory.fromId(kioskId).getUrl(), kioskId);
instance.kioskId = kioskId;
return instance;
}
@ -136,7 +134,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getKioskInfo(serviceId, url, contentCountry, forceReload);
return ExtractorHelper.getKioskInfo(serviceId,
url,
contentCountry,
forceReload);
}
@Override
@ -145,7 +146,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextPageUrl, contentCountry);
return ExtractorHelper.getMoreKioskItems(serviceId,
url,
currentNextPageUrl,
contentCountry);
}
/*//////////////////////////////////////////////////////////////////////////

View File

@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@ -19,6 +20,7 @@ import android.widget.TextView;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.App;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
@ -28,12 +30,14 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
@ -44,6 +48,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
@ -93,7 +98,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
super.onCreate(savedInstanceState);
disposables = new CompositeDisposable();
isBookmarkButtonReady = new AtomicBoolean(false);
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(getContext()));
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(
requireContext()));
}
@Override
@ -142,6 +148,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
context.getResources().getString(R.string.start_here_on_main),
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@ -162,6 +169,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@ -261,11 +271,16 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
if (!TextUtils.isEmpty(result.getUploaderName())) {
headerUploaderName.setText(result.getUploaderName());
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
headerUploaderLayout.setOnClickListener(v ->
headerUploaderLayout.setOnClickListener(v -> {
try {
NavigationHelper.openChannelFragment(getFragmentManager(),
result.getServiceId(), result.getUploaderUrl(),
result.getUploaderName())
);
result.getServiceId(),
result.getUploaderUrl(),
result.getUploaderName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
});
}
}
@ -281,14 +296,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
}
remotePlaylistManager.getPlaylist(result)
.flatMap(lists -> getUpdateProcessor(lists, result), (lists, id) -> lists)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistBookmarkSubscriber());
remotePlaylistManager.onUpdate(result)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(integer -> {/* Do nothing*/}, this::onError);
headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view ->
@ -336,7 +348,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
if (super.onError(exception)) return true;
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId), url, errorId);
onUnrecoverableError(exception,
UserAction.REQUESTED_PLAYLIST,
NewPipe.getNameOfService(serviceId),
url,
errorId);
return true;
}
@ -344,6 +360,17 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
// Utils
//////////////////////////////////////////////////////////////////////////*/
private Flowable<Integer> getUpdateProcessor(@NonNull List<PlaylistRemoteEntity> playlists,
@NonNull PlaylistInfo result) {
final Flowable<Integer> noItemToUpdate = Flowable.just(/*noItemToUpdate=*/-1);
if (playlists.isEmpty()) return noItemToUpdate;
final PlaylistRemoteEntity playlistEntity = playlists.get(0);
if (playlistEntity.isIdenticalTo(result)) return noItemToUpdate;
return remotePlaylistManager.onUpdate(playlists.get(0).getUid(), result).toFlowable();
}
private Subscriber<List<PlaylistRemoteEntity>> getPlaylistBookmarkSubscriber() {
return new Subscriber<List<PlaylistRemoteEntity>>() {
@Override
@ -416,4 +443,4 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
playlistBookmarkButton.setIcon(ThemeHelper.resolveResourceIdFromAttr(activity, iconAttr));
playlistBookmarkButton.setTitle(titleRes);
}
}
}

View File

@ -37,26 +37,30 @@ import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.LayoutManagerSmoothScroller;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import icepick.State;
@ -65,14 +69,15 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import static java.util.Arrays.asList;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class SearchFragment
extends BaseListFragment<SearchResult, ListExtractor.InfoItemsPage>
extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
implements BackPressable {
/*//////////////////////////////////////////////////////////////////////////
@ -92,19 +97,29 @@ public class SearchFragment
@State
protected int filterItemCheckedId = -1;
private SearchEngine.Filter filter = SearchEngine.Filter.ANY;
@State
protected int serviceId = Constants.NO_SERVICE_ID;
// this three represet the current search query
@State
protected String searchQuery;
protected String searchString;
@State
protected String lastSearchedQuery;
protected String[] contentFilter;
@State
protected String sortFilter;
// these represtent the last search
@State
protected String lastSearchedString;
@State
protected boolean wasSearchFocused = false;
private int currentPage = 0;
private int currentNextPage = 0;
private Map<Integer, String> menuItemToFilterName;
private StreamingService service;
private String currentPageUrl;
private String nextPageUrl;
private String contentCountry;
private boolean isSuggestionsEnabled = true;
private boolean isSearchHistoryEnabled = true;
@ -130,11 +145,11 @@ public class SearchFragment
/*////////////////////////////////////////////////////////////////////////*/
public static SearchFragment getInstance(int serviceId, String query) {
public static SearchFragment getInstance(int serviceId, String searchString) {
SearchFragment searchFragment = new SearchFragment();
searchFragment.setQuery(serviceId, query);
searchFragment.setQuery(serviceId, searchString, new String[0], "");
if (!TextUtils.isEmpty(query)) {
if (!TextUtils.isEmpty(searchString)) {
searchFragment.setSearchOnResume();
}
@ -202,13 +217,22 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "onResume() called");
super.onResume();
if (!TextUtils.isEmpty(searchQuery)) {
try {
service = NewPipe.getService(serviceId);
} catch (Exception e) {
ErrorActivity.reportError(getActivity(), e, getActivity().getClass(),
getActivity().findViewById(android.R.id.content),
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"",
"", R.string.general_error));
}
if (!TextUtils.isEmpty(searchString)) {
if (wasLoading.getAndSet(false)) {
if (currentNextPage > currentPage) loadMoreItems();
else search(searchQuery);
search(searchString, contentFilter, sortFilter);
} else if (infoListAdapter.getItemsList().size() == 0) {
if (savedState == null) {
search(searchQuery);
search(searchString, contentFilter, sortFilter);
} else if (!isLoading.get() && !wasSearchFocused) {
infoListAdapter.clearStreamItemList();
showEmptyState();
@ -218,7 +242,7 @@ public class SearchFragment
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
if (TextUtils.isEmpty(searchQuery) || wasSearchFocused) {
if (TextUtils.isEmpty(searchString) || wasSearchFocused) {
showKeyboardSearch();
showSuggestionsPanel();
} else {
@ -247,8 +271,9 @@ public class SearchFragment
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == Activity.RESULT_OK && !TextUtils.isEmpty(searchQuery)) {
search(searchQuery);
if (resultCode == Activity.RESULT_OK
&& !TextUtils.isEmpty(searchString)) {
search(searchString, contentFilter, sortFilter);
} else Log.e(TAG, "ReCaptcha failed");
break;
@ -282,20 +307,22 @@ public class SearchFragment
@Override
public void writeTo(Queue<Object> objectsToSave) {
super.writeTo(objectsToSave);
objectsToSave.add(currentPage);
objectsToSave.add(currentNextPage);
objectsToSave.add(currentPageUrl);
objectsToSave.add(nextPageUrl);
}
@Override
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
super.readFrom(savedObjects);
currentPage = (int) savedObjects.poll();
currentNextPage = (int) savedObjects.poll();
currentPageUrl = (String) savedObjects.poll();
nextPageUrl = (String) savedObjects.poll();
}
@Override
public void onSaveInstanceState(Bundle bundle) {
searchQuery = searchEditText != null ? searchEditText.getText().toString() : searchQuery;
searchString = searchEditText != null
? searchEditText.getText().toString()
: searchString;
super.onSaveInstanceState(bundle);
}
@ -305,8 +332,11 @@ public class SearchFragment
@Override
public void reloadContent() {
if (!TextUtils.isEmpty(searchQuery) || (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchQuery) ? searchQuery : searchEditText.getText().toString());
if (!TextUtils.isEmpty(searchString)
|| (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchString)
? searchString
: searchEditText.getText().toString(), new String[0], "");
} else {
if (searchEditText != null) {
searchEditText.setText("");
@ -330,22 +360,35 @@ public class SearchFragment
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
inflater.inflate(R.menu.menu_search, menu);
menuItemToFilterName = new HashMap<>();
int itemId = 0;
boolean isFirstItem = true;
final Context c = getContext();
for(String filter : service.getSearchQIHFactory().getAvailableContentFilter()) {
menuItemToFilterName.put(itemId, filter);
MenuItem item = menu.add(1,
itemId++,
0,
ServiceHelper.getTranslatedFilterString(filter, c));
if(isFirstItem) {
item.setChecked(true);
isFirstItem = false;
}
}
menu.setGroupCheckable(1, true, true);
restoreFilterChecked(menu, filterItemCheckedId);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_filter_all:
case R.id.menu_filter_video:
case R.id.menu_filter_channel:
case R.id.menu_filter_playlist:
changeFilter(item, getFilterFromMenuId(item.getItemId()));
return true;
default:
return super.onOptionsItemSelected(item);
}
List<String> contentFilter = new ArrayList<>(1);
contentFilter.add(menuItemToFilterName.get(item.getItemId()));
changeContentFilter(item, contentFilter);
return true;
}
private void restoreFilterChecked(Menu menu, int itemId) {
@ -354,21 +397,6 @@ public class SearchFragment
if (item == null) return;
item.setChecked(true);
filter = getFilterFromMenuId(itemId);
}
}
private SearchEngine.Filter getFilterFromMenuId(int itemId) {
switch (itemId) {
case R.id.menu_filter_video:
return SearchEngine.Filter.STREAM;
case R.id.menu_filter_channel:
return SearchEngine.Filter.CHANNEL;
case R.id.menu_filter_playlist:
return SearchEngine.Filter.PLAYLIST;
case R.id.menu_filter_all:
default:
return SearchEngine.Filter.ANY;
}
}
@ -379,14 +407,21 @@ public class SearchFragment
private TextWatcher textWatcher;
private void showSearchOnStart() {
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → " + searchQuery+", lastSearchedQuery → " + lastSearchedQuery);
searchEditText.setText(searchQuery);
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → "
+ searchString
+ ", lastSearchedQuery → "
+ lastSearchedString);
searchEditText.setText(searchString);
if (TextUtils.isEmpty(searchQuery) || TextUtils.isEmpty(searchEditText.getText())) {
if (TextUtils.isEmpty(searchString) || TextUtils.isEmpty(searchEditText.getText())) {
searchToolbarContainer.setTranslationX(100);
searchToolbarContainer.setAlpha(0f);
searchToolbarContainer.setVisibility(View.VISIBLE);
searchToolbarContainer.animate().translationX(0).alpha(1f).setDuration(200).setInterpolator(new DecelerateInterpolator()).start();
searchToolbarContainer.animate()
.translationX(0)
.alpha(1f)
.setDuration(200)
.setInterpolator(new DecelerateInterpolator()).start();
} else {
searchToolbarContainer.setTranslationX(0);
searchToolbarContainer.setAlpha(1f);
@ -396,47 +431,38 @@ public class SearchFragment
private void initSearchListeners() {
if (DEBUG) Log.d(TAG, "initSearchListeners() called");
searchClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (TextUtils.isEmpty(searchEditText.getText())) {
NavigationHelper.gotoMainFragment(getFragmentManager());
return;
}
searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<SuggestionItem>());
showKeyboardSearch();
searchClear.setOnClickListener(v -> {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (TextUtils.isEmpty(searchEditText.getText())) {
NavigationHelper.gotoMainFragment(getFragmentManager());
return;
}
searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<>());
showKeyboardSearch();
});
TooltipCompat.setTooltipText(searchClear, getString(R.string.clear));
searchEditText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
searchEditText.setOnClickListener(v -> {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
});
searchEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
searchEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
});
suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() {
@Override
public void onSuggestionItemSelected(SuggestionItem item) {
search(item.query);
search(item.query, new String[0], "");
searchEditText.setText(item.query);
}
@ -469,21 +495,22 @@ public class SearchFragment
}
};
searchEditText.addTextChangedListener(textWatcher);
searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (DEBUG) {
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
}
if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
search(searchEditText.getText().toString());
return true;
}
return false;
}
});
searchEditText.setOnEditorActionListener(
(TextView v, int actionId, KeyEvent event) -> {
if (DEBUG) {
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
}
if (event != null
&& (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
search(searchEditText.getText().toString(), new String[0], "");
return true;
}
return false;
});
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
if (suggestionDisposable == null || suggestionDisposable.isDisposed())
initSuggestionObserver();
}
private void unsetSearchListeners() {
@ -513,7 +540,8 @@ public class SearchFragment
if (searchEditText == null) return;
if (searchEditText.requestFocus()) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT);
}
}
@ -522,8 +550,10 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "hideKeyboardSearch() called");
if (searchEditText == null) return;
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
searchEditText.clearFocus();
}
@ -554,10 +584,12 @@ public class SearchFragment
@Override
public boolean onBackPressed() {
if (suggestionsPanel.getVisibility() == View.VISIBLE && infoListAdapter.getItemsList().size() > 0 && !isLoading.get()) {
if (suggestionsPanel.getVisibility() == View.VISIBLE
&& infoListAdapter.getItemsList().size() > 0
&& !isLoading.get()) {
hideSuggestionsPanel();
hideKeyboardSearch();
searchEditText.setText(lastSearchedQuery);
searchEditText.setText(lastSearchedString);
return true;
}
return false;
@ -573,8 +605,10 @@ public class SearchFragment
final Observable<String> observable = suggestionPublisher
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
.startWith(searchQuery != null ? searchQuery : "")
.filter(query -> isSuggestionsEnabled);
.startWith(searchString != null
? searchString
: "")
.filter(searchString -> isSuggestionsEnabled);
suggestionDisposable = observable
.switchMap(query -> {
@ -645,56 +679,44 @@ public class SearchFragment
// no-op
}
private void search(final String query) {
if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]");
if (query.isEmpty()) return;
private void search(final String searchString, String[] contentFilter, String sortFilter) {
if (DEBUG) Log.d(TAG, "search() called with: query = [" + searchString + "]");
if (searchString.isEmpty()) return;
try {
final StreamingService service = NewPipe.getServiceByUrl(query);
final StreamingService service = NewPipe.getServiceByUrl(searchString);
if (service != null) {
showLoading();
disposables.add(Observable
.fromCallable(new Callable<Intent>() {
@Override
public Intent call() throws Exception {
return NavigationHelper.getIntentByLink(activity, service, query);
}
})
.fromCallable(() ->
NavigationHelper.getIntentByLink(activity, service, searchString))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Intent>() {
@Override
public void accept(Intent intent) throws Exception {
getFragmentManager().popBackStackImmediate();
activity.startActivity(intent);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
showError(getString(R.string.url_not_supported_toast), false);
}
}));
.subscribe(intent -> {
getFragmentManager().popBackStackImmediate();
activity.startActivity(intent);
}, throwable ->
showError(getString(R.string.url_not_supported_toast), false)));
return;
}
} catch (Exception e) {
// Exception occurred, it's not a url
}
lastSearchedQuery = query;
searchQuery = query;
currentPage = 0;
lastSearchedString = this.searchString;
this.searchString = searchString;
infoListAdapter.clearStreamItemList();
hideSuggestionsPanel();
hideKeyboardSearch();
historyRecordManager.onSearched(serviceId, query)
historyRecordManager.onSearched(serviceId, searchString)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {},
error -> showSnackBarError(error, UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), query, 0)
NewPipe.getNameOfService(serviceId), searchString, 0)
);
suggestionPublisher.onNext(query);
suggestionPublisher.onNext(searchString);
startLoading(false);
}
@ -703,11 +725,16 @@ public class SearchFragment
super.startLoading(forceLoad);
if (disposables != null) disposables.clear();
if (searchDisposable != null) searchDisposable.dispose();
searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, contentCountry, filter)
searchDisposable = ExtractorHelper.searchFor(serviceId,
searchString,
Arrays.asList(contentFilter),
sortFilter,
contentCountry)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
.subscribe(this::handleResult, this::onError);
}
@Override
@ -715,8 +742,13 @@ public class SearchFragment
isLoading.set(true);
showListFooter(true);
if (searchDisposable != null) searchDisposable.dispose();
currentNextPage = currentPage + 1;
searchDisposable = ExtractorHelper.getMoreSearchItems(serviceId, searchQuery, currentNextPage, contentCountry, filter)
searchDisposable = ExtractorHelper.getMoreSearchItems(
serviceId,
searchString,
asList(contentFilter),
sortFilter,
nextPageUrl,
contentCountry)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
@ -739,19 +771,22 @@ public class SearchFragment
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void changeFilter(MenuItem item, SearchEngine.Filter filter) {
this.filter = filter;
private void changeContentFilter(MenuItem item, List<String> contentFilter) {
this.filterItemCheckedId = item.getItemId();
item.setChecked(true);
if (!TextUtils.isEmpty(searchQuery)) {
search(searchQuery);
this.contentFilter = new String[] {contentFilter.get(0)};
if (!TextUtils.isEmpty(searchString)) {
search(searchString, this.contentFilter, sortFilter);
}
}
private void setQuery(int serviceId, String searchQuery) {
private void setQuery(int serviceId, String searchString, String[] contentfilter, String sortFilter) {
this.serviceId = serviceId;
this.searchQuery = searchQuery;
this.searchString = searchString;
this.contentFilter = contentfilter;
this.sortFilter = sortFilter;
}
/*//////////////////////////////////////////////////////////////////////////
@ -772,8 +807,11 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]");
if (super.onError(exception)) return;
int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS, NewPipe.getNameOfService(serviceId), searchQuery, errorId);
int errorId = exception instanceof ParsingException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS,
NewPipe.getNameOfService(serviceId), searchString, errorId);
}
/*//////////////////////////////////////////////////////////////////////////
@ -798,16 +836,19 @@ public class SearchFragment
//////////////////////////////////////////////////////////////////////////*/
@Override
public void handleResult(@NonNull SearchResult result) {
if (!result.errors.isEmpty()) {
showSnackBarError(result.errors, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, 0);
public void handleResult(@NonNull SearchInfo result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, 0);
}
lastSearchedQuery = searchQuery;
lastSearchedString = searchString;
nextPageUrl = result.getNextPageUrl();
currentPageUrl = result.getUrl();
if (infoListAdapter.getItemsList().size() == 0) {
if (!result.getResults().isEmpty()) {
infoListAdapter.addInfoItemList(result.getResults());
if (!result.getRelatedItems().isEmpty()) {
infoListAdapter.addInfoItemList(result.getRelatedItems());
} else {
infoListAdapter.clearStreamItemList();
showEmptyState();
@ -821,12 +862,13 @@ public class SearchFragment
@Override
public void handleNextItems(ListExtractor.InfoItemsPage result) {
showListFooter(false);
currentPage = Integer.parseInt(result.getNextPageUrl());
currentPageUrl = result.getNextPageUrl();
infoListAdapter.addInfoItemList(result.getItems());
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED, NewPipe.getNameOfService(serviceId)
, "\"" + searchQuery + "\" → page " + currentPage, 0);
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId)
, "\"" + searchString + "\" → page: " + nextPageUrl, 0);
}
super.handleNextItems(result);
}
@ -835,12 +877,15 @@ public class SearchFragment
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
if (exception instanceof SearchEngine.NothingFoundException) {
if (exception instanceof SearchExtractor.NothingFoundException) {
infoListAdapter.clearStreamItemList();
showEmptyState();
} else {
int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, errorId);
int errorId = exception instanceof ParsingException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, errorId);
}
return true;

View File

@ -6,6 +6,7 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -19,9 +20,11 @@ import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
@ -99,19 +102,26 @@ public final class BookmarkFragment
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
// Requires the parent fragment to find holder for fragment replacement
if (getParentFragment() == null) return;
final FragmentManager fragmentManager = getParentFragment().getFragmentManager();
try {
// Requires the parent fragment to find holder for fragment replacement
if (getParentFragment() == null) return;
final FragmentManager fragmentManager = getParentFragment().getFragmentManager();
if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name);
if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name);
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment(fragmentManager, entry.getServiceId(),
entry.getUrl(), entry.getName());
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment(
fragmentManager,
entry.getServiceId(),
entry.getUrl(),
entry.getName());
}
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}

View File

@ -298,6 +298,7 @@ public class StatisticsPlaylistFragment
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.delete),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@ -321,6 +322,9 @@ public class StatisticsPlaylistFragment
case 5:
deleteEntry(index);
break;
case 6:
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
break;
default:
break;
}

View File

@ -520,7 +520,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.set_as_playlist_thumbnail),
context.getResources().getString(R.string.delete)
context.getResources().getString(R.string.delete),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@ -549,6 +550,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
case 6:
deleteItem(item);
break;
case 7:
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
break;
default:
break;
}

View File

@ -40,8 +40,11 @@ public class RemotePlaylistManager {
}).subscribeOn(Schedulers.io());
}
public Single<Integer> onUpdate(final PlaylistInfo playlistInfo) {
return Single.fromCallable(() -> playlistRemoteTable.update(new PlaylistRemoteEntity(playlistInfo)))
.subscribeOn(Schedulers.io());
public Single<Integer> onUpdate(final long playlistId, final PlaylistInfo playlistInfo) {
return Single.fromCallable(() -> {
PlaylistRemoteEntity playlist = new PlaylistRemoteEntity(playlistInfo);
playlist.setUid(playlistId);
return playlistRemoteTable.update(playlist);
}).subscribeOn(Schedulers.io());
}
}

View File

@ -15,6 +15,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@ -38,6 +39,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService;
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
@ -318,9 +320,15 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
// Requires the parent fragment to find holder for fragment replacement
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
try {
// Requires the parent fragment to find holder for fragment replacement
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});

View File

@ -26,9 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
@ -39,17 +37,16 @@ import android.widget.RemoteViews;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource;
import com.nostra13.universalimageloader.core.assist.FailReason;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
@ -94,7 +91,6 @@ public final class BackgroundPlayer extends Service {
private NotificationCompat.Builder notBuilder;
private RemoteViews notRemoteView;
private RemoteViews bigNotRemoteView;
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
private boolean shouldUpdateOnProgress;
@ -192,7 +188,9 @@ public final class BackgroundPlayer extends Service {
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCustomContentView(notRemoteView)
.setCustomBigContentView(bigNotRemoteView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) builder.setPriority(NotificationCompat.PRIORITY_MAX);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
builder.setPriority(NotificationCompat.PRIORITY_MAX);
}
return builder;
}
@ -249,15 +247,6 @@ public final class BackgroundPlayer extends Service {
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
}
private void setControlsOpacity(@IntRange(from = 0, to = 255) int opacity) {
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@ -279,8 +268,16 @@ public final class BackgroundPlayer extends Service {
protected class BasePlayerImpl extends BasePlayer {
@NonNull final private AudioPlaybackResolver resolver;
BasePlayerImpl(Context context) {
super(context);
this.resolver = new AudioPlaybackResolver(context, dataSource);
}
@Override
public void initPlayer(boolean playOnReady) {
super.initPlayer(playOnReady);
}
@Override
@ -293,30 +290,41 @@ public final class BackgroundPlayer extends Service {
startForeground(NOTIFICATION_ID, notBuilder.build());
}
@Override
public void initThumbnail(final String url) {
resetNotification();
if (notRemoteView != null) notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
updateNotification(-1);
super.initThumbnail(url);
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
private void updateNotificationThumbnail() {
if (basePlayerImpl == null) return;
if (notRemoteView != null) {
notRemoteView.setImageViewBitmap(R.id.notificationCover,
basePlayerImpl.getThumbnail());
}
if (bigNotRemoteView != null) {
bigNotRemoteView.setImageViewBitmap(R.id.notificationCover,
basePlayerImpl.getThumbnail());
}
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
if (loadedImage != null) {
// rebuild notification here since remote view does not release bitmaps, causing memory leaks
resetNotification();
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
updateNotification(-1);
}
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
super.onLoadingFailed(imageUri, view, failReason);
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
}
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onPrepared(boolean playWhenReady) {
super.onPrepared(playWhenReady);
@ -390,29 +398,18 @@ public final class BackgroundPlayer extends Service {
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
if (shouldUpdateOnProgress || hasPlayQueueItemChanged) {
resetNotification();
updateNotification(-1);
updateMetadata();
}
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
updateMetadata();
}
@Override
@Nullable
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final MediaSource liveSource = super.sourceOf(item, info);
if (liveSource != null) return liveSource;
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
if (index < 0 || index >= info.getAudioStreams().size()) return null;
final AudioStream audio = info.getAudioStreams().get(index);
return buildMediaSource(audio.getUrl(), PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()));
return resolver.resolve(info);
}
@Override
@ -439,8 +436,8 @@ public final class BackgroundPlayer extends Service {
}
private void updateMetadata() {
if (activityListener != null && currentInfo != null) {
activityListener.onMetadataUpdate(currentInfo);
if (activityListener != null && getCurrentMetadata() != null) {
activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
}
}
@ -531,44 +528,36 @@ public final class BackgroundPlayer extends Service {
updatePlayback();
}
@Override
public void onBlocked() {
super.onBlocked();
setControlsOpacity(77);
updateNotification(-1);
}
@Override
public void onPlaying() {
super.onPlaying();
setControlsOpacity(255);
resetNotification();
updateNotificationThumbnail();
updateNotification(R.drawable.ic_pause_white);
lockManager.acquireWifiAndCpu();
}
@Override
public void onPaused() {
super.onPaused();
resetNotification();
updateNotificationThumbnail();
updateNotification(R.drawable.ic_play_arrow_white);
lockManager.releaseWifiAndCpu();
}
@Override
public void onCompleted() {
super.onCompleted();
setControlsOpacity(255);
resetNotification();
if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
if (bigNotRemoteView != null) {
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
}
if (notRemoteView != null) {
notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
}
updateNotificationThumbnail();
updateNotification(R.drawable.ic_replay_white);
lockManager.releaseWifiAndCpu();
}
}

View File

@ -24,16 +24,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
@ -49,7 +47,6 @@ import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.Util;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
@ -57,7 +54,6 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.player.helper.AudioReactor;
import org.schabi.newpipe.player.helper.LoadController;
@ -72,6 +68,8 @@ import org.schabi.newpipe.player.playback.PlaybackListener;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.SerializedCache;
import java.io.IOException;
@ -82,12 +80,12 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.SerialDisposable;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
/**
* Base for the players, joining the common properties
@ -108,17 +106,26 @@ public abstract class BasePlayer implements
@NonNull final protected HistoryRecordManager recordManager;
@NonNull final protected CustomTrackSelector trackSelector;
@NonNull final protected PlayerDataSource dataSource;
@NonNull final private LoadControl loadControl;
@NonNull final private RenderersFactory renderFactory;
@NonNull final private SerialDisposable progressUpdateReactor;
@NonNull final private CompositeDisposable databaseUpdateReactor;
/*//////////////////////////////////////////////////////////////////////////
// Intent
//////////////////////////////////////////////////////////////////////////*/
public static final String REPEAT_MODE = "repeat_mode";
public static final String PLAYBACK_PITCH = "playback_pitch";
public static final String PLAYBACK_SPEED = "playback_speed";
public static final String PLAYBACK_QUALITY = "playback_quality";
public static final String PLAY_QUEUE_KEY = "play_queue_key";
public static final String APPEND_ONLY = "append_only";
public static final String SELECT_ON_APPEND = "select_on_append";
@NonNull public static final String REPEAT_MODE = "repeat_mode";
@NonNull public static final String PLAYBACK_PITCH = "playback_pitch";
@NonNull public static final String PLAYBACK_SPEED = "playback_speed";
@NonNull public static final String PLAYBACK_SKIP_SILENCE = "playback_skip_silence";
@NonNull public static final String PLAYBACK_QUALITY = "playback_quality";
@NonNull public static final String PLAY_QUEUE_KEY = "play_queue_key";
@NonNull public static final String APPEND_ONLY = "append_only";
@NonNull public static final String SELECT_ON_APPEND = "select_on_append";
/*//////////////////////////////////////////////////////////////////////////
// Playback
@ -129,12 +136,13 @@ public abstract class BasePlayer implements
protected PlayQueue playQueue;
protected PlayQueueAdapter playQueueAdapter;
protected MediaSourceManager playbackManager;
@Nullable protected MediaSourceManager playbackManager;
protected StreamInfo currentInfo;
protected PlayQueueItem currentItem;
@Nullable private PlayQueueItem currentItem;
@Nullable private MediaSourceTag currentMetadata;
@Nullable private Bitmap currentThumbnail;
protected Toast errorToast;
@Nullable protected Toast errorToast;
/*//////////////////////////////////////////////////////////////////////////
// Player
@ -145,18 +153,11 @@ public abstract class BasePlayer implements
protected final static int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
protected final static int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds
protected CustomTrackSelector trackSelector;
protected PlayerDataSource dataSource;
protected SimpleExoPlayer simpleExoPlayer;
protected AudioReactor audioReactor;
protected MediaSessionManager mediaSessionManager;
private boolean isPrepared = false;
private boolean isSynchronizing = false;
protected Disposable progressUpdateReactor;
protected CompositeDisposable databaseUpdateReactor;
//////////////////////////////////////////////////////////////////////////*/
@ -174,29 +175,32 @@ public abstract class BasePlayer implements
context.registerReceiver(broadcastReceiver, intentFilter);
this.recordManager = new HistoryRecordManager(context);
this.progressUpdateReactor = new SerialDisposable();
this.databaseUpdateReactor = new CompositeDisposable();
final String userAgent = Downloader.USER_AGENT;
final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter);
final TrackSelection.Factory trackSelectionFactory =
PlayerHelper.getQualitySelector(context, bandwidthMeter);
this.trackSelector = new CustomTrackSelector(trackSelectionFactory);
this.loadControl = new LoadController(context);
this.renderFactory = new DefaultRenderersFactory(context);
}
public void setup() {
if (simpleExoPlayer == null) initPlayer(/*playOnInit=*/true);
if (simpleExoPlayer == null) {
initPlayer(/*playOnInit=*/true);
}
initListeners();
}
public void initPlayer(final boolean playOnReady) {
if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]");
if (databaseUpdateReactor != null) databaseUpdateReactor.dispose();
databaseUpdateReactor = new CompositeDisposable();
final String userAgent = Downloader.USER_AGENT;
final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter);
final TrackSelection.Factory trackSelectionFactory =
PlayerHelper.getQualitySelector(context, bandwidthMeter);
trackSelector = new CustomTrackSelector(trackSelectionFactory);
final LoadControl loadControl = new LoadController(context);
final RenderersFactory renderFactory = new DefaultRenderersFactory(context);
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderFactory, trackSelector, loadControl);
simpleExoPlayer.addListener(this);
simpleExoPlayer.setPlayWhenReady(playOnReady);
@ -235,20 +239,24 @@ public abstract class BasePlayer implements
final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode());
final float playbackSpeed = intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed());
final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch());
final boolean playbackSkipSilence = intent.getBooleanExtra(PLAYBACK_SKIP_SILENCE,
getPlaybackSkipSilence());
// Good to go...
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, /*playOnInit=*/true);
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
/*playOnInit=*/true);
}
protected void initPlayback(@NonNull final PlayQueue queue,
@Player.RepeatMode final int repeatMode,
final float playbackSpeed,
final float playbackPitch,
final boolean playbackSkipSilence,
final boolean playOnReady) {
destroyPlayer();
initPlayer(playOnReady);
setRepeatMode(repeatMode);
setPlaybackParameters(playbackSpeed, playbackPitch);
setPlaybackParameters(playbackSpeed, playbackPitch, playbackSkipSilence);
playQueue = queue;
playQueue.init();
@ -270,7 +278,6 @@ public abstract class BasePlayer implements
if (playQueue != null) playQueue.dispose();
if (audioReactor != null) audioReactor.dispose();
if (playbackManager != null) playbackManager.dispose();
if (databaseUpdateReactor != null) databaseUpdateReactor.dispose();
if (mediaSessionManager != null) mediaSessionManager.dispose();
if (playQueueAdapter != null) {
@ -284,20 +291,22 @@ public abstract class BasePlayer implements
destroyPlayer();
unregisterBroadcastReceiver();
trackSelector = null;
databaseUpdateReactor.clear();
progressUpdateReactor.set(null);
simpleExoPlayer = null;
mediaSessionManager = null;
}
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
public void initThumbnail(final String url) {
private void initThumbnail(final String url) {
if (DEBUG) Log.d(TAG, "Thumbnail - initThumbnail() called");
if (url == null || url.isEmpty()) return;
ImageLoader.getInstance().resume();
ImageLoader.getInstance().loadImage(url, this);
ImageLoader.getInstance().loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS,
this);
}
@Override
@ -310,6 +319,7 @@ public abstract class BasePlayer implements
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
Log.e(TAG, "Thumbnail - onLoadingFailed() called on imageUri = [" + imageUri + "]",
failReason.getCause());
currentThumbnail = null;
}
@Override
@ -317,64 +327,14 @@ public abstract class BasePlayer implements
if (DEBUG) Log.d(TAG, "Thumbnail - onLoadingComplete() called with: " +
"imageUri = [" + imageUri + "], view = [" + view + "], " +
"loadedImage = [" + loadedImage + "]");
currentThumbnail = loadedImage;
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
if (DEBUG) Log.d(TAG, "Thumbnail - onLoadingCancelled() called with: " +
"imageUri = [" + imageUri + "], view = [" + view + "]");
}
/*//////////////////////////////////////////////////////////////////////////
// MediaSource Building
//////////////////////////////////////////////////////////////////////////*/
public MediaSource buildLiveMediaSource(@NonNull final String sourceUrl,
@C.ContentType final int type) {
if (DEBUG) {
Log.d(TAG, "buildLiveMediaSource() called with: url = [" + sourceUrl +
"], content type = [" + type + "]");
}
if (dataSource == null) return null;
final Uri uri = Uri.parse(sourceUrl);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getLiveDashMediaSourceFactory().createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getLiveHlsMediaSourceFactory().createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
public MediaSource buildMediaSource(@NonNull final String sourceUrl,
@NonNull final String cacheKey,
@NonNull final String overrideExtension) {
if (DEBUG) {
Log.d(TAG, "buildMediaSource() called with: url = [" + sourceUrl +
"], cacheKey = [" + cacheKey + "]" +
"], overrideExtension = [" + overrideExtension + "]");
}
if (dataSource == null) return null;
final Uri uri = Uri.parse(sourceUrl);
@C.ContentType final int type = TextUtils.isEmpty(overrideExtension) ?
Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getDashMediaSourceFactory().createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getHlsMediaSourceFactory().createMediaSource(uri);
case C.TYPE_OTHER:
return dataSource.getExtractorMediaSourceFactory(cacheKey).createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
currentThumbnail = null;
}
/*//////////////////////////////////////////////////////////////////////////
@ -510,13 +470,11 @@ public abstract class BasePlayer implements
public abstract void onUpdateProgress(int currentProgress, int duration, int bufferPercent);
protected void startProgressLoop() {
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
progressUpdateReactor = getProgressReactor();
progressUpdateReactor.set(getProgressReactor());
}
protected void stopProgressLoop() {
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
progressUpdateReactor = null;
progressUpdateReactor.set(null);
}
public void triggerProgressUpdate() {
@ -531,7 +489,8 @@ public abstract class BasePlayer implements
private Disposable getProgressReactor() {
return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> triggerProgressUpdate());
.subscribe(ignored -> triggerProgressUpdate(),
error -> Log.e(TAG, "Progress update failure: ", error));
}
/*//////////////////////////////////////////////////////////////////////////
@ -545,28 +504,16 @@ public abstract class BasePlayer implements
(manifest == null ? "no manifest" : "available manifest") + ", " +
"timeline size = [" + timeline.getWindowCount() + "], " +
"reason = [" + reason + "]");
if (playQueue == null) return;
switch (reason) {
case Player.TIMELINE_CHANGE_REASON_RESET: // called after #block
case Player.TIMELINE_CHANGE_REASON_PREPARED: // called after #unblock
case Player.TIMELINE_CHANGE_REASON_DYNAMIC: // called after playlist changes
// Ensures MediaSourceManager#update is complete
final boolean isPlaylistStable = timeline.getWindowCount() == playQueue.size();
// Ensure dynamic/livestream timeline changes does not cause negative position
if (isPlaylistStable && !isCurrentWindowValid() && !isSynchronizing) {
if (DEBUG) Log.d(TAG, "Playback - negative time position reached, " +
"clamping to default position.");
seekToDefault();
}
break;
}
maybeUpdateCurrentMetadata();
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
if (DEBUG) Log.d(TAG, "ExoPlayer - onTracksChanged(), " +
"track group size = " + trackGroups.length);
maybeUpdateCurrentMetadata();
}
@Override
@ -586,6 +533,8 @@ public abstract class BasePlayer implements
} else if (isLoading && !isProgressLoopRunning()) {
startProgressLoop();
}
maybeUpdateCurrentMetadata();
}
@Override
@ -609,6 +558,7 @@ public abstract class BasePlayer implements
}
break;
case Player.STATE_READY: //3
maybeUpdateCurrentMetadata();
maybeCorrectSeekPosition();
if (!isPrepared) {
isPrepared = true;
@ -625,38 +575,19 @@ public abstract class BasePlayer implements
}
private void maybeCorrectSeekPosition() {
if (playQueue == null || simpleExoPlayer == null || currentInfo == null) return;
if (playQueue == null || simpleExoPlayer == null || currentMetadata == null) return;
final int currentSourceIndex = playQueue.getIndex();
final PlayQueueItem currentSourceItem = playQueue.getItem();
if (currentSourceItem == null) return;
final long recoveryPositionMillis = currentSourceItem.getRecoveryPosition();
final boolean isCurrentWindowCorrect =
simpleExoPlayer.getCurrentPeriodIndex() == currentSourceIndex;
final StreamInfo currentInfo = currentMetadata.getMetadata();
final long presetStartPositionMillis = currentInfo.getStartPosition() * 1000;
if (recoveryPositionMillis != PlayQueueItem.RECOVERY_UNSET && isCurrentWindowCorrect) {
// Is recovering previous playback?
if (DEBUG) Log.d(TAG, "Playback - Rewinding to recovery time=" +
"[" + getTimeString((int)recoveryPositionMillis) + "]");
seekTo(recoveryPositionMillis);
playQueue.unsetRecovery(currentSourceIndex);
} else if (isSynchronizing && isLive()) {
if (DEBUG) Log.d(TAG, "Playback - Synchronizing livestream to default time");
// Is still synchronizing?
seekToDefault();
} else if (isSynchronizing && presetStartPositionMillis > 0L) {
if (presetStartPositionMillis > 0L) {
// Has another start position?
if (DEBUG) Log.d(TAG, "Playback - Seeking to preset start " +
"position=[" + presetStartPositionMillis + "]");
// Has another start position?
seekTo(presetStartPositionMillis);
currentInfo.setStartPosition(0);
}
isSynchronizing = false;
}
/**
@ -708,7 +639,7 @@ public abstract class BasePlayer implements
setRecovery();
final Throwable cause = error.getCause();
if (cause instanceof BehindLiveWindowException) {
if (error instanceof BehindLiveWindowException) {
reload();
} else if (cause instanceof UnknownHostException) {
playQueue.error(/*isNetworkProblem=*/true);
@ -727,22 +658,29 @@ public abstract class BasePlayer implements
public void onPositionDiscontinuity(@Player.DiscontinuityReason final int reason) {
if (DEBUG) Log.d(TAG, "ExoPlayer - onPositionDiscontinuity() called with " +
"reason = [" + reason + "]");
// Refresh the playback if there is a transition to the next video
final int newPeriodIndex = simpleExoPlayer.getCurrentPeriodIndex();
if (playQueue == null) return;
/* Discontinuity reasons!! Thank you ExoPlayer lords */
// Refresh the playback if there is a transition to the next video
final int newWindowIndex = simpleExoPlayer.getCurrentWindowIndex();
switch (reason) {
case DISCONTINUITY_REASON_PERIOD_TRANSITION:
if (newPeriodIndex == playQueue.getIndex()) {
// When player is in single repeat mode and a period transition occurs,
// we need to register a view count here since no metadata has changed
if (getRepeatMode() == Player.REPEAT_MODE_ONE &&
newWindowIndex == playQueue.getIndex()) {
registerView();
} else {
playQueue.offsetIndex(+1);
break;
}
case DISCONTINUITY_REASON_SEEK:
case DISCONTINUITY_REASON_SEEK_ADJUSTMENT:
case DISCONTINUITY_REASON_INTERNAL:
if (playQueue.getIndex() != newWindowIndex) {
playQueue.setIndex(newWindowIndex);
}
break;
}
maybeUpdateCurrentMetadata();
}
@Override
@ -788,7 +726,7 @@ public abstract class BasePlayer implements
if (DEBUG) Log.d(TAG, "Playback - onPlaybackBlock() called");
currentItem = null;
currentInfo = null;
currentMetadata = null;
simpleExoPlayer.stop();
isPrepared = false;
@ -805,42 +743,21 @@ public abstract class BasePlayer implements
simpleExoPlayer.prepare(mediaSource);
}
@Override
public void onPlaybackSynchronize(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info) {
public void onPlaybackSynchronize(@NonNull final PlayQueueItem item) {
if (DEBUG) Log.d(TAG, "Playback - onPlaybackSynchronize() called with " +
(info != null ? "available" : "null") + " info, " +
"item=[" + item.getTitle() + "], url=[" + item.getUrl() + "]");
if (simpleExoPlayer == null || playQueue == null) return;
final boolean onPlaybackInitial = currentItem == null;
final boolean hasPlayQueueItemChanged = currentItem != item;
final boolean hasStreamInfoChanged = currentInfo != info;
final int currentPlayQueueIndex = playQueue.indexOf(item);
final int currentPlaylistIndex = simpleExoPlayer.getCurrentWindowIndex();
final int currentPlaylistSize = simpleExoPlayer.getCurrentTimeline().getWindowCount();
// when starting playback on the last item when not repeating, maybe auto queue
if (info != null && currentPlayQueueIndex == playQueue.size() - 1 &&
getRepeatMode() == Player.REPEAT_MODE_OFF &&
PlayerHelper.isAutoQueueEnabled(context)) {
final PlayQueue autoQueue = PlayerHelper.autoQueueOf(info, playQueue.getStreams());
if (autoQueue != null) playQueue.append(autoQueue.getStreams());
}
// If nothing to synchronize
if (!hasPlayQueueItemChanged && !hasStreamInfoChanged) {
return;
}
if (!hasPlayQueueItemChanged) return;
currentItem = item;
currentInfo = info;
if (hasPlayQueueItemChanged) {
// updates only to the stream info should not trigger another view count
registerView();
initThumbnail(info == null ? item.getThumbnailUrl() : info.getThumbnailUrl());
}
onMetadataChanged(item, info, currentPlayQueueIndex, hasPlayQueueItemChanged);
// Check if on wrong window
if (currentPlayQueueIndex != playQueue.getIndex()) {
@ -855,39 +772,29 @@ public abstract class BasePlayer implements
"index=[" + currentPlayQueueIndex + "] with " +
"playlist length=[" + currentPlaylistSize + "]");
// If not playing correct stream, change window position and sets flag
// for synchronizing once window position is corrected
// @see maybeCorrectSeekPosition()
} else if (currentPlaylistIndex != currentPlayQueueIndex || onPlaybackInitial ||
!isPlaying()) {
if (DEBUG) Log.d(TAG, "Playback - Rewinding to correct" +
" index=[" + currentPlayQueueIndex + "]," +
" from=[" + currentPlaylistIndex + "], size=[" + currentPlaylistSize + "].");
isSynchronizing = true;
simpleExoPlayer.seekToDefaultPosition(currentPlayQueueIndex);
if (item.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
simpleExoPlayer.seekTo(currentPlayQueueIndex, item.getRecoveryPosition());
playQueue.unsetRecovery(currentPlayQueueIndex);
} else {
simpleExoPlayer.seekToDefaultPosition(currentPlayQueueIndex);
}
}
}
abstract protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged);
@Nullable
@Override
public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) {
final StreamType streamType = info.getStreamType();
if (!(streamType == StreamType.AUDIO_LIVE_STREAM || streamType == StreamType.LIVE_STREAM)) {
return null;
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
final StreamInfo info = tag.getMetadata();
if (DEBUG) {
Log.d(TAG, "Playback - onMetadataChanged() called, playing: " + info.getName());
}
if (!info.getHlsUrl().isEmpty()) {
return buildLiveMediaSource(info.getHlsUrl(), C.TYPE_HLS);
} else if (!info.getDashMpdUrl().isEmpty()) {
return buildLiveMediaSource(info.getDashMpdUrl(), C.TYPE_DASH);
}
return null;
initThumbnail(info.getThumbnailUrl());
registerView();
}
@Override
@ -1020,9 +927,7 @@ public abstract class BasePlayer implements
public void seekTo(long positionMillis) {
if (DEBUG) Log.d(TAG, "seekBy() called with: position = [" + positionMillis + "]");
if (simpleExoPlayer == null || positionMillis < 0 ||
positionMillis > simpleExoPlayer.getDuration()) return;
simpleExoPlayer.seekTo(positionMillis);
if (simpleExoPlayer != null) simpleExoPlayer.seekTo(positionMillis);
}
public void seekBy(long offsetMillis) {
@ -1046,12 +951,14 @@ public abstract class BasePlayer implements
//////////////////////////////////////////////////////////////////////////*/
private void registerView() {
if (databaseUpdateReactor == null || currentInfo == null) return;
databaseUpdateReactor.add(recordManager.onViewed(currentInfo).onErrorComplete()
if (currentMetadata == null) return;
final StreamInfo currentInfo = currentMetadata.getMetadata();
final Disposable viewRegister = recordManager.onViewed(currentInfo).onErrorComplete()
.subscribe(
ignored -> {/* successful */},
error -> Log.e(TAG, "Player onViewed() failure: ", error)
));
);
databaseUpdateReactor.add(viewRegister);
}
protected void reload() {
@ -1065,7 +972,7 @@ public abstract class BasePlayer implements
}
protected void savePlaybackState(final StreamInfo info, final long progress) {
if (info == null || databaseUpdateReactor == null) return;
if (info == null) return;
final Disposable stateSaver = recordManager.saveStreamState(info, progress)
.observeOn(AndroidSchedulers.mainThread())
.onErrorComplete()
@ -1077,7 +984,8 @@ public abstract class BasePlayer implements
}
private void savePlaybackState() {
if (simpleExoPlayer == null || currentInfo == null) return;
if (simpleExoPlayer == null || currentMetadata == null) return;
final StreamInfo currentInfo = currentMetadata.getMetadata();
if (simpleExoPlayer.getCurrentPosition() > RECOVERY_SKIP_THRESHOLD_MILLIS &&
simpleExoPlayer.getCurrentPosition() <
@ -1085,6 +993,34 @@ public abstract class BasePlayer implements
savePlaybackState(currentInfo, simpleExoPlayer.getCurrentPosition());
}
}
private void maybeUpdateCurrentMetadata() {
if (simpleExoPlayer == null) return;
final MediaSourceTag metadata;
try {
metadata = (MediaSourceTag) simpleExoPlayer.getCurrentTag();
} catch (IndexOutOfBoundsException | ClassCastException error) {
return;
}
if (metadata == null) return;
maybeAutoQueueNextStream(metadata);
if (currentMetadata == metadata) return;
currentMetadata = metadata;
onMetadataChanged(metadata);
}
private void maybeAutoQueueNextStream(@NonNull final MediaSourceTag currentMetadata) {
if (playQueue == null || playQueue.getIndex() != playQueue.size() - 1 ||
getRepeatMode() != Player.REPEAT_MODE_OFF ||
!PlayerHelper.isAutoQueueEnabled(context)) return;
// auto queue when starting playback on the last item when not repeating
final PlayQueue autoQueue = PlayerHelper.autoQueueOf(currentMetadata.getMetadata(),
playQueue.getStreams());
if (autoQueue != null) playQueue.append(autoQueue.getStreams());
}
/*//////////////////////////////////////////////////////////////////////////
// Getters and Setters
//////////////////////////////////////////////////////////////////////////*/
@ -1101,19 +1037,35 @@ public abstract class BasePlayer implements
return currentState;
}
@Nullable
public MediaSourceTag getCurrentMetadata() {
return currentMetadata;
}
@NonNull
public String getVideoUrl() {
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUrl();
return currentMetadata == null ? context.getString(R.string.unknown_content) : currentMetadata.getMetadata().getUrl();
}
@NonNull
public String getVideoTitle() {
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getTitle();
return currentMetadata == null ? context.getString(R.string.unknown_content) : currentMetadata.getMetadata().getName();
}
@NonNull
public String getUploaderName() {
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUploader();
return currentMetadata == null ? context.getString(R.string.unknown_content) : currentMetadata.getMetadata().getUploaderName();
}
@Nullable
public Bitmap getThumbnail() {
return currentThumbnail == null ?
BitmapFactory.decodeResource(context.getResources(), R.drawable.dummy_thumbnail) :
currentThumbnail;
}
/** Checks if the current playback is a livestream AND is playing at or beyond the live edge */
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean isLiveEdge() {
if (simpleExoPlayer == null || !isLive()) return false;
@ -1147,11 +1099,11 @@ public abstract class BasePlayer implements
@Player.RepeatMode
public int getRepeatMode() {
return simpleExoPlayer.getRepeatMode();
return simpleExoPlayer == null ? Player.REPEAT_MODE_OFF : simpleExoPlayer.getRepeatMode();
}
public void setRepeatMode(@Player.RepeatMode final int repeatMode) {
simpleExoPlayer.setRepeatMode(repeatMode);
if (simpleExoPlayer != null) simpleExoPlayer.setRepeatMode(repeatMode);
}
public float getPlaybackSpeed() {
@ -1162,19 +1114,22 @@ public abstract class BasePlayer implements
return getPlaybackParameters().pitch;
}
public boolean getPlaybackSkipSilence() {
return getPlaybackParameters().skipSilence;
}
public void setPlaybackSpeed(float speed) {
setPlaybackParameters(speed, getPlaybackPitch());
setPlaybackParameters(speed, getPlaybackPitch(), getPlaybackSkipSilence());
}
public PlaybackParameters getPlaybackParameters() {
final PlaybackParameters defaultParameters = new PlaybackParameters(1f, 1f);
if (simpleExoPlayer == null) return defaultParameters;
if (simpleExoPlayer == null) return PlaybackParameters.DEFAULT;
final PlaybackParameters parameters = simpleExoPlayer.getPlaybackParameters();
return parameters == null ? defaultParameters : parameters;
return parameters == null ? PlaybackParameters.DEFAULT : parameters;
}
public void setPlaybackParameters(float speed, float pitch) {
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch));
public void setPlaybackParameters(float speed, float pitch, boolean skipSilence) {
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch, skipSilence));
}
public PlayQueue getPlayQueue() {
@ -1190,7 +1145,7 @@ public abstract class BasePlayer implements
}
public boolean isProgressLoopRunning() {
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
return progressUpdateReactor.get() != null;
}
public void setRecovery() {

View File

@ -58,7 +58,6 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.SubtitleView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
@ -67,6 +66,8 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder;
import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder;
import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
@ -104,6 +105,7 @@ public final class MainVideoPlayer extends AppCompatActivity
@Nullable private PlayerState playerState;
private boolean isInMultiWindow;
private boolean isBackPressed;
/*//////////////////////////////////////////////////////////////////////////
// Activity LifeCycle
@ -125,7 +127,7 @@ public final class MainVideoPlayer extends AppCompatActivity
hideSystemUi();
setContentView(R.layout.activity_main_player);
playerImpl = new VideoPlayerImpl(this);
playerImpl = new VideoPlayerImpl(this);
playerImpl.setup(findViewById(android.R.id.content));
if (savedInstanceState != null && savedInstanceState.get(KEY_SAVED_STATE) != null) {
@ -152,7 +154,10 @@ public final class MainVideoPlayer extends AppCompatActivity
protected void onNewIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
super.onNewIntent(intent);
playerImpl.handleIntent(intent);
if (intent != null) {
playerState = null;
playerImpl.handleIntent(intent);
}
}
@Override
@ -177,7 +182,7 @@ public final class MainVideoPlayer extends AppCompatActivity
playerImpl.setPlaybackQuality(playerState.getPlaybackQuality());
playerImpl.initPlayback(playerState.getPlayQueue(), playerState.getRepeatMode(),
playerState.getPlaybackSpeed(), playerState.getPlaybackPitch(),
playerState.wasPlaying());
playerState.isPlaybackSkipSilence(), playerState.wasPlaying());
}
}
@ -191,6 +196,12 @@ public final class MainVideoPlayer extends AppCompatActivity
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
isBackPressed = true;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
if (DEBUG) Log.d(TAG, "onSaveInstanceState() called");
@ -200,7 +211,8 @@ public final class MainVideoPlayer extends AppCompatActivity
playerImpl.setRecovery();
playerState = new PlayerState(playerImpl.getPlayQueue(), playerImpl.getRepeatMode(),
playerImpl.getPlaybackSpeed(), playerImpl.getPlaybackPitch(),
playerImpl.getPlaybackQuality(), playerImpl.isPlaying());
playerImpl.getPlaybackQuality(), playerImpl.getPlaybackSkipSilence(),
playerImpl.isPlaying());
StateSaver.tryToSave(isChangingConfigurations(), null, outState, this);
}
@ -208,10 +220,17 @@ public final class MainVideoPlayer extends AppCompatActivity
protected void onStop() {
if (DEBUG) Log.d(TAG, "onStop() called");
super.onStop();
playerImpl.destroy();
PlayerHelper.setScreenBrightness(getApplicationContext(),
getWindow().getAttributes().screenBrightness);
if (playerImpl == null) return;
if (!isBackPressed) {
playerImpl.minimize();
}
playerImpl.destroy();
isInMultiWindow = false;
isBackPressed = false;
}
/*//////////////////////////////////////////////////////////////////////////
@ -335,8 +354,11 @@ public final class MainVideoPlayer extends AppCompatActivity
////////////////////////////////////////////////////////////////////////////
@Override
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch) {
if (playerImpl != null) playerImpl.setPlaybackParameters(playbackTempo, playbackPitch);
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
boolean playbackSkipSilence) {
if (playerImpl != null) {
playerImpl.setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence);
}
}
///////////////////////////////////////////////////////////////////////////
@ -441,6 +463,21 @@ public final class MainVideoPlayer extends AppCompatActivity
switchPopupButton.setOnClickListener(this);
}
public void minimize() {
switch (PlayerHelper.getMinimizeOnExitAction(context)) {
case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND:
onPlayBackgroundButtonClicked();
break;
case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP:
onFullScreenButtonClicked();
break;
case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE:
default:
// No action
break;
}
}
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@ -461,14 +498,11 @@ public final class MainVideoPlayer extends AppCompatActivity
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
super.onMetadataChanged(item, info, newPlayQueueIndex, false);
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
titleTextView.setText(getVideoTitle());
channelTextView.setText(getUploaderName());
titleTextView.setText(tag.getMetadata().getName());
channelTextView.setText(tag.getMetadata().getUploaderName());
}
@Override
@ -501,6 +535,7 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
);
context.startService(intent);
@ -522,6 +557,7 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
);
context.startService(intent);
@ -617,7 +653,8 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override
public void onPlaybackSpeedClicked() {
PlaybackParameterDialog.newInstance(getPlaybackSpeed(), getPlaybackPitch())
PlaybackParameterDialog
.newInstance(getPlaybackSpeed(), getPlaybackPitch(), getPlaybackSkipSilence())
.show(getSupportFragmentManager(), TAG);
}
@ -647,14 +684,19 @@ public final class MainVideoPlayer extends AppCompatActivity
}
@Override
protected int getDefaultResolutionIndex(final List<VideoStream> sortedVideos) {
return ListHelper.getDefaultResolutionIndex(context, sortedVideos);
}
protected VideoPlaybackResolver.QualityResolver getQualityResolver() {
return new VideoPlaybackResolver.QualityResolver() {
@Override
public int getDefaultResolutionIndex(List<VideoStream> sortedVideos) {
return ListHelper.getDefaultResolutionIndex(context, sortedVideos);
}
@Override
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality) {
return ListHelper.getResolutionIndex(context, sortedVideos, playbackQuality);
@Override
public int getOverrideResolutionIndex(List<VideoStream> sortedVideos,
String playbackQuality) {
return ListHelper.getResolutionIndex(context, sortedVideos, playbackQuality);
}
};
}
/*//////////////////////////////////////////////////////////////////////////
@ -678,7 +720,6 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override
public void onBuffering() {
super.onBuffering();
animatePlayButtons(false, 100);
getRootView().setKeepScreenOn(true);
}
@ -854,7 +895,6 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override
public boolean onDoubleTap(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
if (!playerImpl.isPlaying()) return false;
if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) {
playerImpl.onFastForward();

View File

@ -14,21 +14,26 @@ public class PlayerState implements Serializable {
private final float playbackSpeed;
private final float playbackPitch;
@Nullable private final String playbackQuality;
private final boolean playbackSkipSilence;
private final boolean wasPlaying;
PlayerState(@NonNull final PlayQueue playQueue, final int repeatMode,
final float playbackSpeed, final float playbackPitch, final boolean wasPlaying) {
this(playQueue, repeatMode, playbackSpeed, playbackPitch, null, wasPlaying);
final float playbackSpeed, final float playbackPitch,
final boolean playbackSkipSilence, final boolean wasPlaying) {
this(playQueue, repeatMode, playbackSpeed, playbackPitch, null,
playbackSkipSilence, wasPlaying);
}
PlayerState(@NonNull final PlayQueue playQueue, final int repeatMode,
final float playbackSpeed, final float playbackPitch,
@Nullable final String playbackQuality, final boolean wasPlaying) {
@Nullable final String playbackQuality, final boolean playbackSkipSilence,
final boolean wasPlaying) {
this.playQueue = playQueue;
this.repeatMode = repeatMode;
this.playbackSpeed = playbackSpeed;
this.playbackPitch = playbackPitch;
this.playbackQuality = playbackQuality;
this.playbackSkipSilence = playbackSkipSilence;
this.wasPlaying = wasPlaying;
}
@ -62,6 +67,10 @@ public class PlayerState implements Serializable {
return playbackQuality;
}
public boolean isPlaybackSkipSilence() {
return playbackSkipSilence;
}
public boolean wasPlaying() {
return wasPlaying;
}

View File

@ -34,7 +34,6 @@ import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics;
import android.util.Log;
@ -56,16 +55,17 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.SubtitleView;
import com.nostra13.universalimageloader.core.assist.FailReason;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
@ -98,6 +98,11 @@ public final class PopupVideoPlayer extends Service {
private static final int MINIMUM_SHOW_EXTRA_WIDTH_DP = 300;
private static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
private static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
private WindowManager windowManager;
private WindowManager.LayoutParams windowLayoutParams;
private GestureDetector gestureDetector;
@ -191,14 +196,17 @@ public final class PopupVideoPlayer extends Service {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_PHONE : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
windowLayoutParams = new WindowManager.LayoutParams(
(int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
layoutParamType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT);
windowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
@ -228,6 +236,7 @@ public final class PopupVideoPlayer extends Service {
notRemoteView.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle());
notRemoteView.setTextViewText(R.id.notificationArtist, playerImpl.getUploaderName());
notRemoteView.setImageViewBitmap(R.id.notificationCover, playerImpl.getThumbnail());
notRemoteView.setOnClickPendingIntent(R.id.notificationPlayPause,
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
@ -243,11 +252,15 @@ public final class PopupVideoPlayer extends Service {
setRepeatModeRemote(notRemoteView, playerImpl.getRepeatMode());
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
.setOngoing(true)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContent(notRemoteView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
builder.setPriority(NotificationCompat.PRIORITY_MAX);
}
return builder;
}
/**
@ -366,6 +379,12 @@ public final class PopupVideoPlayer extends Service {
}
}
private void updateWindowFlags(final int flags) {
if (windowLayoutParams == null || windowManager == null || playerImpl == null) return;
windowLayoutParams.flags = flags;
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
}
///////////////////////////////////////////////////////////////////////////
protected class VideoPlayerImpl extends VideoPlayer implements View.OnLayoutChangeListener {
@ -428,21 +447,6 @@ public final class PopupVideoPlayer extends Service {
super.destroy();
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
if (loadedImage != null) {
// rebuild notification here since remote view does not release bitmaps, causing memory leaks
notBuilder = createNotification();
if (notRemoteView != null) {
notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
}
updateNotification(-1);
}
}
@Override
public void onFullScreenButtonClicked() {
super.onFullScreenButtonClicked();
@ -459,6 +463,7 @@ public final class PopupVideoPlayer extends Service {
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -510,14 +515,47 @@ public final class PopupVideoPlayer extends Service {
}
@Override
protected int getDefaultResolutionIndex(final List<VideoStream> sortedVideos) {
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos);
protected VideoPlaybackResolver.QualityResolver getQualityResolver() {
return new VideoPlaybackResolver.QualityResolver() {
@Override
public int getDefaultResolutionIndex(List<VideoStream> sortedVideos) {
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos);
}
@Override
public int getOverrideResolutionIndex(List<VideoStream> sortedVideos,
String playbackQuality) {
return ListHelper.getPopupResolutionIndex(context, sortedVideos,
playbackQuality);
}
};
}
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
// rebuild notification here since remote view does not release bitmaps,
// causing memory leaks
resetNotification();
updateNotification(-1);
}
@Override
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality) {
return ListHelper.getPopupResolutionIndex(context, sortedVideos, playbackQuality);
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
super.onLoadingFailed(imageUri, view, failReason);
resetNotification();
updateNotification(-1);
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
super.onLoadingCancelled(imageUri, view);
resetNotification();
updateNotification(-1);
}
/*//////////////////////////////////////////////////////////////////////////
@ -538,8 +576,8 @@ public final class PopupVideoPlayer extends Service {
}
private void updateMetadata() {
if (activityListener != null && currentInfo != null) {
activityListener.onMetadataUpdate(currentInfo);
if (activityListener != null && getCurrentMetadata() != null) {
activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
}
}
@ -571,8 +609,9 @@ public final class PopupVideoPlayer extends Service {
public void onRepeatModeChanged(int i) {
super.onRepeatModeChanged(i);
setRepeatModeRemote(notRemoteView, i);
updateNotification(-1);
updatePlayback();
resetNotification();
updateNotification(-1);
}
@Override
@ -585,11 +624,10 @@ public final class PopupVideoPlayer extends Service {
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
super.onMetadataChanged(item, info, newPlayQueueIndex, false);
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
resetNotification();
updateNotification(-1);
updateMetadata();
}
@ -652,46 +690,70 @@ public final class PopupVideoPlayer extends Service {
@Override
public void onBlocked() {
super.onBlocked();
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
}
@Override
public void onPlaying() {
super.onPlaying();
updateNotification(R.drawable.ic_pause_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
lockManager.acquireWifiAndCpu();
updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS);
resetNotification();
updateNotification(R.drawable.ic_pause_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
startForeground(NOTIFICATION_ID, notBuilder.build());
lockManager.acquireWifiAndCpu();
}
@Override
public void onBuffering() {
super.onBuffering();
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
}
@Override
public void onPaused() {
super.onPaused();
updateWindowFlags(IDLE_WINDOW_FLAGS);
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_play_arrow_white);
lockManager.releaseWifiAndCpu();
stopForeground(false);
}
@Override
public void onPausedSeek() {
super.onPausedSeek();
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
resetNotification();
updateNotification(R.drawable.ic_play_arrow_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
}
@Override
public void onCompleted() {
super.onCompleted();
updateWindowFlags(IDLE_WINDOW_FLAGS);
resetNotification();
updateNotification(R.drawable.ic_replay_white);
videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white);
lockManager.releaseWifiAndCpu();
stopForeground(false);
}
@Override
@ -709,16 +771,15 @@ public final class PopupVideoPlayer extends Service {
super.hideControlsAndButton(duration, delay, videoPlayPause);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
/*package-private*/ void enableVideoRenderer(final boolean enable) {
final int videoRendererIndex = getRendererIndex(C.TRACK_TYPE_VIDEO);
if (trackSelector != null && videoRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setRendererDisabled(videoRendererIndex, !enable);
if (videoRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setParameters(trackSelector.buildUponParameters()
.setRendererDisabled(videoRendererIndex, !enable));
}
}

View File

@ -187,6 +187,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
this.player.getRepeatMode(),
this.player.getPlaybackSpeed(),
this.player.getPlaybackPitch(),
this.player.getPlaybackSkipSilence(),
null
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
@ -340,6 +341,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
return true;
});
final MenuItem share = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, /*pos=*/3,
Menu.NONE, R.string.share);
share.setOnMenuItemClickListener(menuItem -> {
shareUrl(item.getTitle(), item.getUrl());
return true;
});
menu.show();
}
@ -459,13 +467,16 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
private void openPlaybackParameterDialog() {
if (player == null) return;
PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(),
player.getPlaybackPitch()).show(getSupportFragmentManager(), getTag());
PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(), player.getPlaybackPitch(),
player.getPlaybackSkipSilence()).show(getSupportFragmentManager(), getTag());
}
@Override
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch) {
if (player != null) player.setPlaybackParameters(playbackTempo, playbackPitch);
public void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
boolean playbackSkipSilence) {
if (player != null) {
player.setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence);
}
}
////////////////////////////////////////////////////////////////////////////
@ -509,6 +520,18 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
.show(getSupportFragmentManager(), getTag());
}
////////////////////////////////////////////////////////////////////////////
// Share
////////////////////////////////////////////////////////////////////////////
private void shareUrl(String subject, String url) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, url);
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
}
////////////////////////////////////////////////////////////////////////////
// Binding Service Listener
////////////////////////////////////////////////////////////////////////////

View File

@ -29,7 +29,6 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
@ -47,11 +46,9 @@ import android.widget.SeekBar;
import android.widget.TextView;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.CaptionStyleCompat;
@ -62,21 +59,17 @@ import com.google.android.exoplayer2.video.VideoListener;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.Subtitles;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ListHelper;
import java.util.ArrayList;
import java.util.List;
import static com.google.android.exoplayer2.C.SELECTION_FLAG_AUTOSELECT;
import static com.google.android.exoplayer2.C.TIME_UNSET;
import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed;
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -105,13 +98,12 @@ public abstract class VideoPlayer extends BasePlayer
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
private ArrayList<VideoStream> availableStreams;
private List<VideoStream> availableStreams;
private int selectedStreamIndex;
protected String playbackQuality;
protected boolean wasPlaying = false;
@NonNull final private VideoPlaybackResolver resolver;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
@ -162,6 +154,7 @@ public abstract class VideoPlayer extends BasePlayer
public VideoPlayer(String debugTag, Context context) {
super(context);
this.TAG = debugTag;
this.resolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
}
public void setup(View rootView) {
@ -241,7 +234,8 @@ public abstract class VideoPlayer extends BasePlayer
// Setup audio session with onboard equalizer
if (Build.VERSION.SDK_INT >= 21) {
trackSelector.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(context));
trackSelector.setParameters(trackSelector.buildUponParameters()
.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(context)));
}
}
@ -297,8 +291,9 @@ public abstract class VideoPlayer extends BasePlayer
0, Menu.NONE, R.string.caption_none);
captionOffItem.setOnMenuItemClickListener(menuItem -> {
final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT);
if (trackSelector != null && textRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setRendererDisabled(textRendererIndex, true);
if (textRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setParameters(trackSelector.buildUponParameters()
.setRendererDisabled(textRendererIndex, true));
}
return true;
});
@ -310,68 +305,61 @@ public abstract class VideoPlayer extends BasePlayer
i + 1, Menu.NONE, captionLanguage);
captionItem.setOnMenuItemClickListener(menuItem -> {
final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT);
if (trackSelector != null && textRendererIndex != RENDERER_UNAVAILABLE) {
if (textRendererIndex != RENDERER_UNAVAILABLE) {
trackSelector.setPreferredTextLanguage(captionLanguage);
trackSelector.setRendererDisabled(textRendererIndex, false);
trackSelector.setParameters(trackSelector.buildUponParameters()
.setRendererDisabled(textRendererIndex, false));
}
return true;
});
}
captionPopupMenu.setOnDismissListener(this);
}
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected abstract int getDefaultResolutionIndex(final List<VideoStream> sortedVideos);
protected abstract int getOverrideResolutionIndex(final List<VideoStream> sortedVideos, final String playbackQuality);
private void updateStreamRelatedViews() {
if (getCurrentMetadata() == null) return;
final MediaSourceTag tag = getCurrentMetadata();
final StreamInfo metadata = tag.getMetadata();
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
qualityTextView.setVisibility(View.GONE);
playbackSpeedTextView.setVisibility(View.GONE);
playbackEndTime.setVisibility(View.GONE);
playbackLiveSync.setVisibility(View.GONE);
final StreamType streamType = info == null ? StreamType.NONE : info.getStreamType();
switch (streamType) {
switch (metadata.getStreamType()) {
case AUDIO_STREAM:
surfaceView.setVisibility(View.GONE);
endScreen.setVisibility(View.VISIBLE);
playbackEndTime.setVisibility(View.VISIBLE);
break;
case AUDIO_LIVE_STREAM:
surfaceView.setVisibility(View.GONE);
endScreen.setVisibility(View.VISIBLE);
playbackLiveSync.setVisibility(View.VISIBLE);
break;
case LIVE_STREAM:
surfaceView.setVisibility(View.VISIBLE);
endScreen.setVisibility(View.GONE);
playbackLiveSync.setVisibility(View.VISIBLE);
break;
case VIDEO_STREAM:
if (info.getVideoStreams().size() + info.getVideoOnlyStreams().size() == 0) break;
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false);
availableStreams = new ArrayList<>(videos);
if (playbackQuality == null) {
selectedStreamIndex = getDefaultResolutionIndex(videos);
} else {
selectedStreamIndex = getOverrideResolutionIndex(videos, getPlaybackQuality());
}
if (metadata.getVideoStreams().size() + metadata.getVideoOnlyStreams().size() == 0)
break;
availableStreams = tag.getSortedAvailableVideoStreams();
selectedStreamIndex = tag.getSelectedVideoStreamIndex();
buildQualityMenu();
qualityTextView.setVisibility(View.VISIBLE);
qualityTextView.setVisibility(View.VISIBLE);
surfaceView.setVisibility(View.VISIBLE);
default:
endScreen.setVisibility(View.GONE);
playbackEndTime.setVisibility(View.VISIBLE);
break;
}
@ -379,69 +367,21 @@ public abstract class VideoPlayer extends BasePlayer
buildPlaybackSpeedMenu();
playbackSpeedTextView.setVisibility(View.VISIBLE);
}
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected abstract VideoPlaybackResolver.QualityResolver getQualityResolver();
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
updateStreamRelatedViews();
}
@Override
@Nullable
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final MediaSource liveSource = super.sourceOf(item, info);
if (liveSource != null) return liveSource;
List<MediaSource> mediaSources = new ArrayList<>();
// Create video stream source
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false);
final int index;
if (videos.isEmpty()) {
index = -1;
} else if (playbackQuality == null) {
index = getDefaultResolutionIndex(videos);
} else {
index = getOverrideResolutionIndex(videos, getPlaybackQuality());
}
final VideoStream video = index >= 0 && index < videos.size() ? videos.get(index) : null;
if (video != null) {
final MediaSource streamSource = buildMediaSource(video.getUrl(),
PlayerHelper.cacheKeyOf(info, video),
MediaFormat.getSuffixById(video.getFormatId()));
mediaSources.add(streamSource);
}
// Create optional audio stream source
final List<AudioStream> audioStreams = info.getAudioStreams();
final AudioStream audio = audioStreams.isEmpty() ? null : audioStreams.get(
ListHelper.getDefaultAudioFormat(context, audioStreams));
// Use the audio stream if there is no video stream, or
// Merge with audio stream in case if video does not contain audio
if (audio != null && ((video != null && video.isVideoOnly) || video == null)) {
final MediaSource audioSource = buildMediaSource(audio.getUrl(),
PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()));
mediaSources.add(audioSource);
}
// If there is no audio or video sources, then this media source cannot be played back
if (mediaSources.isEmpty()) return null;
// Below are auxiliary media sources
// Create subtitle sources
for (final Subtitles subtitle : info.getSubtitles()) {
final String mimeType = PlayerHelper.mimeTypesOf(subtitle.getFileType());
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);
}
if (mediaSources.size() == 1) {
return mediaSources.get(0);
} else {
return new MergingMediaSource(mediaSources.toArray(
new MediaSource[mediaSources.size()]));
}
return resolver.resolve(info);
}
/*//////////////////////////////////////////////////////////////////////////
@ -460,7 +400,6 @@ public abstract class VideoPlayer extends BasePlayer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
animateView(endScreen, false, 0);
loadingPanel.setBackgroundColor(Color.BLACK);
animateView(loadingPanel, true, 0);
animateView(surfaceForeground, true, 100);
@ -470,6 +409,8 @@ public abstract class VideoPlayer extends BasePlayer
public void onPlaying() {
super.onPlaying();
updateStreamRelatedViews();
showAndAnimateControl(-1, true);
playbackSeekBar.setEnabled(true);
@ -480,14 +421,12 @@ public abstract class VideoPlayer extends BasePlayer
loadingPanel.setVisibility(View.GONE);
animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200);
animateView(endScreen, false, 0);
}
@Override
public void onBuffering() {
if (DEBUG) Log.d(TAG, "onBuffering() called");
loadingPanel.setBackgroundColor(Color.TRANSPARENT);
animateView(loadingPanel, true, 500);
}
@Override
@ -552,8 +491,7 @@ public abstract class VideoPlayer extends BasePlayer
final int textRenderer = getRendererIndex(C.TRACK_TYPE_TEXT);
if (captionTextView == null) return;
if (trackSelector == null || trackSelector.getCurrentMappedTrackInfo() == null ||
textRenderer == RENDERER_UNAVAILABLE) {
if (trackSelector.getCurrentMappedTrackInfo() == null || textRenderer == RENDERER_UNAVAILABLE) {
captionTextView.setVisibility(View.GONE);
return;
}
@ -575,8 +513,8 @@ public abstract class VideoPlayer extends BasePlayer
// Build UI
buildCaptionMenu(availableLanguages);
if (trackSelector.getRendererDisabled(textRenderer) || preferredLanguage == null ||
!availableLanguages.contains(preferredLanguage)) {
if (trackSelector.getParameters().getRendererDisabled(textRenderer) ||
preferredLanguage == null || !availableLanguages.contains(preferredLanguage)) {
captionTextView.setText(R.string.caption_none);
} else {
captionTextView.setText(preferredLanguage);
@ -905,11 +843,12 @@ public abstract class VideoPlayer extends BasePlayer
//////////////////////////////////////////////////////////////////////////*/
public void setPlaybackQuality(final String quality) {
this.playbackQuality = quality;
this.resolver.setPlaybackQuality(quality);
}
@Nullable
public String getPlaybackQuality() {
return playbackQuality;
return resolver.getPlaybackQuality();
}
public AspectRatioFrameLayout getAspectRatioFrameLayout() {

View File

@ -39,10 +39,13 @@ public class MediaSessionManager {
return MediaButtonReceiver.handleIntent(mediaSession, intent);
}
/**
* Should be called on player destruction to prevent leakage.
* */
public void dispose() {
this.sessionConnector.setPlayer(null, null);
this.sessionConnector.setQueueNavigator(null);
this.mediaSession.setActive(false);
this.mediaSession.release();
}
}
}

View File

@ -21,25 +21,34 @@ import static org.schabi.newpipe.player.BasePlayer.DEBUG;
public class PlaybackParameterDialog extends DialogFragment {
@NonNull private static final String TAG = "PlaybackParameterDialog";
public static final double MINIMUM_PLAYBACK_VALUE = 0.25f;
// Minimum allowable range in ExoPlayer
public static final double MINIMUM_PLAYBACK_VALUE = 0.10f;
public static final double MAXIMUM_PLAYBACK_VALUE = 3.00f;
public static final char STEP_UP_SIGN = '+';
public static final char STEP_DOWN_SIGN = '-';
public static final double PLAYBACK_STEP_VALUE = 0.05f;
public static final double NIGHTCORE_TEMPO = 1.20f;
public static final double NIGHTCORE_PITCH_LOWER = 1.15f;
public static final double NIGHTCORE_PITCH_UPPER = 1.25f;
public static final double STEP_ONE_PERCENT_VALUE = 0.01f;
public static final double STEP_FIVE_PERCENT_VALUE = 0.05f;
public static final double STEP_TEN_PERCENT_VALUE = 0.10f;
public static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f;
public static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 1.00f;
public static final double DEFAULT_TEMPO = 1.00f;
public static final double DEFAULT_PITCH = 1.00f;
public static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE;
public static final boolean DEFAULT_SKIP_SILENCE = false;
@NonNull private static final String INITIAL_TEMPO_KEY = "initial_tempo_key";
@NonNull private static final String INITIAL_PITCH_KEY = "initial_pitch_key";
@NonNull private static final String TEMPO_KEY = "tempo_key";
@NonNull private static final String PITCH_KEY = "pitch_key";
@NonNull private static final String STEP_SIZE_KEY = "step_size_key";
public interface Callback {
void onPlaybackParameterChanged(final float playbackTempo, final float playbackPitch);
void onPlaybackParameterChanged(final float playbackTempo, final float playbackPitch,
final boolean playbackSkipSilence);
}
@Nullable private Callback callback;
@ -50,6 +59,11 @@ public class PlaybackParameterDialog extends DialogFragment {
private double initialTempo = DEFAULT_TEMPO;
private double initialPitch = DEFAULT_PITCH;
private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE;
private double tempo = DEFAULT_TEMPO;
private double pitch = DEFAULT_PITCH;
private double stepSize = DEFAULT_STEP;
@Nullable private SeekBar tempoSlider;
@Nullable private TextView tempoMinimumText;
@ -65,16 +79,26 @@ public class PlaybackParameterDialog extends DialogFragment {
@Nullable private TextView pitchStepDownText;
@Nullable private TextView pitchStepUpText;
@Nullable private CheckBox unhookingCheckbox;
@Nullable private TextView stepSizeOnePercentText;
@Nullable private TextView stepSizeFivePercentText;
@Nullable private TextView stepSizeTenPercentText;
@Nullable private TextView stepSizeTwentyFivePercentText;
@Nullable private TextView stepSizeOneHundredPercentText;
@Nullable private TextView nightCorePresetText;
@Nullable private TextView resetPresetText;
@Nullable private CheckBox unhookingCheckbox;
@Nullable private CheckBox skipSilenceCheckbox;
public static PlaybackParameterDialog newInstance(final double playbackTempo,
final double playbackPitch) {
final double playbackPitch,
final boolean playbackSkipSilence) {
PlaybackParameterDialog dialog = new PlaybackParameterDialog();
dialog.initialTempo = playbackTempo;
dialog.initialPitch = playbackPitch;
dialog.tempo = playbackTempo;
dialog.pitch = playbackPitch;
dialog.initialSkipSilence = playbackSkipSilence;
return dialog;
}
@ -98,6 +122,10 @@ public class PlaybackParameterDialog extends DialogFragment {
if (savedInstanceState != null) {
initialTempo = savedInstanceState.getDouble(INITIAL_TEMPO_KEY, DEFAULT_TEMPO);
initialPitch = savedInstanceState.getDouble(INITIAL_PITCH_KEY, DEFAULT_PITCH);
tempo = savedInstanceState.getDouble(TEMPO_KEY, DEFAULT_TEMPO);
pitch = savedInstanceState.getDouble(PITCH_KEY, DEFAULT_PITCH);
stepSize = savedInstanceState.getDouble(STEP_SIZE_KEY, DEFAULT_STEP);
}
}
@ -106,6 +134,10 @@ public class PlaybackParameterDialog extends DialogFragment {
super.onSaveInstanceState(outState);
outState.putDouble(INITIAL_TEMPO_KEY, initialTempo);
outState.putDouble(INITIAL_PITCH_KEY, initialPitch);
outState.putDouble(TEMPO_KEY, getCurrentTempo());
outState.putDouble(PITCH_KEY, getCurrentPitch());
outState.putDouble(STEP_SIZE_KEY, getCurrentStepSize());
}
/*//////////////////////////////////////////////////////////////////////////
@ -123,7 +155,9 @@ public class PlaybackParameterDialog extends DialogFragment {
.setView(view)
.setCancelable(true)
.setNegativeButton(R.string.cancel, (dialogInterface, i) ->
setPlaybackParameters(initialTempo, initialPitch))
setPlaybackParameters(initialTempo, initialPitch, initialSkipSilence))
.setNeutralButton(R.string.playback_reset, (dialogInterface, i) ->
setPlaybackParameters(DEFAULT_TEMPO, DEFAULT_PITCH, DEFAULT_SKIP_SILENCE))
.setPositiveButton(R.string.finish, (dialogInterface, i) ->
setCurrentPlaybackParameters());
@ -136,9 +170,13 @@ public class PlaybackParameterDialog extends DialogFragment {
private void setupControlViews(@NonNull View rootView) {
setupHookingControl(rootView);
setupSkipSilenceControl(rootView);
setupTempoControl(rootView);
setupPitchControl(rootView);
setupPresetControl(rootView);
changeStepSize(stepSize);
setupStepSizeSelector(rootView);
}
private void setupTempoControl(@NonNull View rootView) {
@ -150,31 +188,15 @@ public class PlaybackParameterDialog extends DialogFragment {
tempoStepDownText = rootView.findViewById(R.id.tempoStepDown);
if (tempoCurrentText != null)
tempoCurrentText.setText(PlayerHelper.formatSpeed(initialTempo));
tempoCurrentText.setText(PlayerHelper.formatSpeed(tempo));
if (tempoMaximumText != null)
tempoMaximumText.setText(PlayerHelper.formatSpeed(MAXIMUM_PLAYBACK_VALUE));
if (tempoMinimumText != null)
tempoMinimumText.setText(PlayerHelper.formatSpeed(MINIMUM_PLAYBACK_VALUE));
if (tempoStepUpText != null) {
tempoStepUpText.setText(getStepUpPercentString(PLAYBACK_STEP_VALUE));
tempoStepUpText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() + PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (tempoStepDownText != null) {
tempoStepDownText.setText(getStepDownPercentString(PLAYBACK_STEP_VALUE));
tempoStepDownText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() - PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (tempoSlider != null) {
tempoSlider.setMax(strategy.progressOf(MAXIMUM_PLAYBACK_VALUE));
tempoSlider.setProgress(strategy.progressOf(initialTempo));
tempoSlider.setProgress(strategy.progressOf(tempo));
tempoSlider.setOnSeekBarChangeListener(getOnTempoChangedListener());
}
}
@ -188,31 +210,15 @@ public class PlaybackParameterDialog extends DialogFragment {
pitchStepUpText = rootView.findViewById(R.id.pitchStepUp);
if (pitchCurrentText != null)
pitchCurrentText.setText(PlayerHelper.formatPitch(initialPitch));
pitchCurrentText.setText(PlayerHelper.formatPitch(pitch));
if (pitchMaximumText != null)
pitchMaximumText.setText(PlayerHelper.formatPitch(MAXIMUM_PLAYBACK_VALUE));
if (pitchMinimumText != null)
pitchMinimumText.setText(PlayerHelper.formatPitch(MINIMUM_PLAYBACK_VALUE));
if (pitchStepUpText != null) {
pitchStepUpText.setText(getStepUpPercentString(PLAYBACK_STEP_VALUE));
pitchStepUpText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() + PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (pitchStepDownText != null) {
pitchStepDownText.setText(getStepDownPercentString(PLAYBACK_STEP_VALUE));
pitchStepDownText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() - PLAYBACK_STEP_VALUE);
setCurrentPlaybackParameters();
});
}
if (pitchSlider != null) {
pitchSlider.setMax(strategy.progressOf(MAXIMUM_PLAYBACK_VALUE));
pitchSlider.setProgress(strategy.progressOf(initialPitch));
pitchSlider.setProgress(strategy.progressOf(pitch));
pitchSlider.setOnSeekBarChangeListener(getOnPitchChangedListener());
}
}
@ -220,7 +226,7 @@ public class PlaybackParameterDialog extends DialogFragment {
private void setupHookingControl(@NonNull View rootView) {
unhookingCheckbox = rootView.findViewById(R.id.unhookCheckbox);
if (unhookingCheckbox != null) {
unhookingCheckbox.setChecked(initialPitch != initialTempo);
unhookingCheckbox.setChecked(pitch != tempo);
unhookingCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> {
if (isChecked) return;
// When unchecked, slide back to the minimum of current tempo or pitch
@ -231,24 +237,84 @@ public class PlaybackParameterDialog extends DialogFragment {
}
}
private void setupPresetControl(@NonNull View rootView) {
nightCorePresetText = rootView.findViewById(R.id.presetNightcore);
if (nightCorePresetText != null) {
nightCorePresetText.setOnClickListener(view -> {
final double randomPitch = NIGHTCORE_PITCH_LOWER +
Math.random() * (NIGHTCORE_PITCH_UPPER - NIGHTCORE_PITCH_LOWER);
private void setupSkipSilenceControl(@NonNull View rootView) {
skipSilenceCheckbox = rootView.findViewById(R.id.skipSilenceCheckbox);
if (skipSilenceCheckbox != null) {
skipSilenceCheckbox.setChecked(initialSkipSilence);
skipSilenceCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) ->
setCurrentPlaybackParameters());
}
}
setTempoSlider(NIGHTCORE_TEMPO);
setPitchSlider(randomPitch);
private void setupStepSizeSelector(@NonNull final View rootView) {
stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent);
stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent);
stepSizeTenPercentText = rootView.findViewById(R.id.stepSizeTenPercent);
stepSizeTwentyFivePercentText = rootView.findViewById(R.id.stepSizeTwentyFivePercent);
stepSizeOneHundredPercentText = rootView.findViewById(R.id.stepSizeOneHundredPercent);
if (stepSizeOnePercentText != null) {
stepSizeOnePercentText.setText(getPercentString(STEP_ONE_PERCENT_VALUE));
stepSizeOnePercentText.setOnClickListener(view ->
changeStepSize(STEP_ONE_PERCENT_VALUE));
}
if (stepSizeFivePercentText != null) {
stepSizeFivePercentText.setText(getPercentString(STEP_FIVE_PERCENT_VALUE));
stepSizeFivePercentText.setOnClickListener(view ->
changeStepSize(STEP_FIVE_PERCENT_VALUE));
}
if (stepSizeTenPercentText != null) {
stepSizeTenPercentText.setText(getPercentString(STEP_TEN_PERCENT_VALUE));
stepSizeTenPercentText.setOnClickListener(view ->
changeStepSize(STEP_TEN_PERCENT_VALUE));
}
if (stepSizeTwentyFivePercentText != null) {
stepSizeTwentyFivePercentText.setText(getPercentString(STEP_TWENTY_FIVE_PERCENT_VALUE));
stepSizeTwentyFivePercentText.setOnClickListener(view ->
changeStepSize(STEP_TWENTY_FIVE_PERCENT_VALUE));
}
if (stepSizeOneHundredPercentText != null) {
stepSizeOneHundredPercentText.setText(getPercentString(STEP_ONE_HUNDRED_PERCENT_VALUE));
stepSizeOneHundredPercentText.setOnClickListener(view ->
changeStepSize(STEP_ONE_HUNDRED_PERCENT_VALUE));
}
}
private void changeStepSize(final double stepSize) {
this.stepSize = stepSize;
if (tempoStepUpText != null) {
tempoStepUpText.setText(getStepUpPercentString(stepSize));
tempoStepUpText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() + stepSize);
setCurrentPlaybackParameters();
});
}
resetPresetText = rootView.findViewById(R.id.presetReset);
if (resetPresetText != null) {
resetPresetText.setOnClickListener(view -> {
setTempoSlider(DEFAULT_TEMPO);
setPitchSlider(DEFAULT_PITCH);
if (tempoStepDownText != null) {
tempoStepDownText.setText(getStepDownPercentString(stepSize));
tempoStepDownText.setOnClickListener(view -> {
onTempoSliderUpdated(getCurrentTempo() - stepSize);
setCurrentPlaybackParameters();
});
}
if (pitchStepUpText != null) {
pitchStepUpText.setText(getStepUpPercentString(stepSize));
pitchStepUpText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() + stepSize);
setCurrentPlaybackParameters();
});
}
if (pitchStepDownText != null) {
pitchStepDownText.setText(getStepDownPercentString(stepSize));
pitchStepDownText.setOnClickListener(view -> {
onPitchSliderUpdated(getCurrentPitch() - stepSize);
setCurrentPlaybackParameters();
});
}
@ -342,10 +408,11 @@ public class PlaybackParameterDialog extends DialogFragment {
//////////////////////////////////////////////////////////////////////////*/
private void setCurrentPlaybackParameters() {
setPlaybackParameters(getCurrentTempo(), getCurrentPitch());
setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence());
}
private void setPlaybackParameters(final double tempo, final double pitch) {
private void setPlaybackParameters(final double tempo, final double pitch,
final boolean skipSilence) {
if (callback != null && tempoCurrentText != null && pitchCurrentText != null) {
if (DEBUG) Log.d(TAG, "Setting playback parameters to " +
"tempo=[" + tempo + "], " +
@ -353,27 +420,40 @@ public class PlaybackParameterDialog extends DialogFragment {
tempoCurrentText.setText(PlayerHelper.formatSpeed(tempo));
pitchCurrentText.setText(PlayerHelper.formatPitch(pitch));
callback.onPlaybackParameterChanged((float) tempo, (float) pitch);
callback.onPlaybackParameterChanged((float) tempo, (float) pitch, skipSilence);
}
}
private double getCurrentTempo() {
return tempoSlider == null ? initialTempo : strategy.valueOf(
return tempoSlider == null ? tempo : strategy.valueOf(
tempoSlider.getProgress());
}
private double getCurrentPitch() {
return pitchSlider == null ? initialPitch : strategy.valueOf(
return pitchSlider == null ? pitch : strategy.valueOf(
pitchSlider.getProgress());
}
private double getCurrentStepSize() {
return stepSize;
}
private boolean getCurrentSkipSilence() {
return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked();
}
@NonNull
private static String getStepUpPercentString(final double percent) {
return STEP_UP_SIGN + PlayerHelper.formatPitch(percent);
return STEP_UP_SIGN + getPercentString(percent);
}
@NonNull
private static String getStepDownPercentString(final double percent) {
return STEP_DOWN_SIGN + PlayerHelper.formatPitch(percent);
return STEP_DOWN_SIGN + getPercentString(percent);
}
@NonNull
private static String getPercentString(final double percent) {
return PlayerHelper.formatPitch(percent);
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.accessibility.CaptioningManager;
@ -28,6 +29,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import java.lang.annotation.Retention;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
@ -42,6 +44,8 @@ import java.util.concurrent.TimeUnit;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FILL;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.*;
public class PlayerHelper {
private PlayerHelper() {}
@ -51,6 +55,14 @@ public class PlayerHelper {
private static final NumberFormat speedFormatter = new DecimalFormat("0.##x");
private static final NumberFormat pitchFormatter = new DecimalFormat("##%");
@Retention(SOURCE)
@IntDef({MINIMIZE_ON_EXIT_MODE_NONE, MINIMIZE_ON_EXIT_MODE_BACKGROUND,
MINIMIZE_ON_EXIT_MODE_POPUP})
public @interface MinimizeMode {
int MINIMIZE_ON_EXIT_MODE_NONE = 0;
int MINIMIZE_ON_EXIT_MODE_BACKGROUND = 1;
int MINIMIZE_ON_EXIT_MODE_POPUP = 2;
}
////////////////////////////////////////////////////////////////////////////
// Exposed helpers
////////////////////////////////////////////////////////////////////////////
@ -173,6 +185,22 @@ public class PlayerHelper {
return isAutoQueueEnabled(context, false);
}
@MinimizeMode
public static int getMinimizeOnExitAction(@NonNull final Context context) {
final String defaultAction = context.getString(R.string.minimize_on_exit_none_key);
final String popupAction = context.getString(R.string.minimize_on_exit_popup_key);
final String backgroundAction = context.getString(R.string.minimize_on_exit_background_key);
final String action = getMinimizeOnExitAction(context, defaultAction);
if (action.equals(popupAction)) {
return MINIMIZE_ON_EXIT_MODE_POPUP;
} else if (action.equals(backgroundAction)) {
return MINIMIZE_ON_EXIT_MODE_BACKGROUND;
} else {
return MINIMIZE_ON_EXIT_MODE_NONE;
}
}
@NonNull
public static SeekParameters getSeekParameters(@NonNull final Context context) {
return isUsingInexactSeek(context, false) ?
@ -213,7 +241,6 @@ public class PlayerHelper {
public static TrackSelection.Factory getQualitySelector(@NonNull final Context context,
@NonNull final BandwidthMeter meter) {
return new AdaptiveTrackSelection.Factory(meter,
AdaptiveTrackSelection.DEFAULT_MAX_INITIAL_BITRATE,
/*bufferDurationRequiredForQualityIncrease=*/1000,
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
@ -225,7 +252,7 @@ public class PlayerHelper {
}
public static int getShutdownFlingVelocity(@NonNull final Context context) {
return 10000;
return 6000;
}
public static int getTossFlingVelocity(@NonNull final Context context) {
@ -249,7 +276,6 @@ public class PlayerHelper {
* System font scaling:
* Very small - 0.25f, Small - 0.5f, Normal - 1.0f, Large - 1.5f, Very Large - 2.0f
* */
@NonNull
public static float getCaptionScale(@NonNull final Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return 1f;
@ -322,4 +348,10 @@ public class PlayerHelper {
return sp.getFloat(context.getString(R.string.screen_brightness_key), screenBrightness);
}
}
private static String getMinimizeOnExitAction(@NonNull final Context context,
final String key) {
return getPreferences(context).getString(context.getString(R.string.minimize_on_exit_key),
key);
}
}

View File

@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
import android.util.Log;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.upstream.Allocator;
@ -11,7 +12,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import java.io.IOException;
public class FailedMediaSource implements ManagedMediaSource {
public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSource {
private final String TAG = "FailedMediaSource@" + Integer.toHexString(hashCode());
public static class FailedMediaSourceException extends Exception {
@ -72,11 +73,6 @@ public class FailedMediaSource implements ManagedMediaSource {
return System.currentTimeMillis() >= retryTimestamp;
}
@Override
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
Log.e(TAG, "Loading failed source: ", error);
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
throw new IOException(error);
@ -90,8 +86,14 @@ public class FailedMediaSource implements ManagedMediaSource {
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {}
@Override
public void releaseSource() {}
protected void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
Log.e(TAG, "Loading failed source: ", error);
}
@Override
protected void releaseSourceInternal() {}
@Override
public boolean shouldBeReplacedWith(@NonNull final PlayQueueItem newIdentity,

View File

@ -1,10 +1,12 @@
package org.schabi.newpipe.player.mediasource;
import android.os.Handler;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.upstream.Allocator;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
@ -34,7 +36,8 @@ public class LoadedMediaSource implements ManagedMediaSource {
}
@Override
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
public void prepareSource(ExoPlayer player, boolean isTopLevelSource,
SourceInfoRefreshListener listener) {
source.prepareSource(player, isTopLevelSource, listener);
}
@ -54,8 +57,18 @@ public class LoadedMediaSource implements ManagedMediaSource {
}
@Override
public void releaseSource() {
source.releaseSource();
public void releaseSource(SourceInfoRefreshListener listener) {
source.releaseSource(listener);
}
@Override
public void addEventListener(Handler handler, MediaSourceEventListener eventListener) {
source.addEventListener(handler, eventListener);
}
@Override
public void removeEventListener(MediaSourceEventListener eventListener) {
source.removeEventListener(eventListener);
}
@Override

View File

@ -3,14 +3,14 @@ package org.schabi.newpipe.player.mediasource;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ShuffleOrder;
public class ManagedMediaSourcePlaylist {
@NonNull private final DynamicConcatenatingMediaSource internalSource;
@NonNull private final ConcatenatingMediaSource internalSource;
public ManagedMediaSourcePlaylist() {
internalSource = new DynamicConcatenatingMediaSource(/*isPlaylistAtomic=*/false,
internalSource = new ConcatenatingMediaSource(/*isPlaylistAtomic=*/false,
new ShuffleOrder.UnshuffledShuffleOrder(0));
}
@ -32,12 +32,8 @@ public class ManagedMediaSourcePlaylist {
null : (ManagedMediaSource) internalSource.getMediaSource(index);
}
public void dispose() {
internalSource.releaseSource();
}
@NonNull
public DynamicConcatenatingMediaSource getParentMediaSource() {
public ConcatenatingMediaSource getParentMediaSource() {
return internalSource;
}
@ -46,7 +42,7 @@ public class ManagedMediaSourcePlaylist {
//////////////////////////////////////////////////////////////////////////*/
/**
* Expands the {@link DynamicConcatenatingMediaSource} by appending it with a
* Expands the {@link ConcatenatingMediaSource} by appending it with a
* {@link PlaceholderMediaSource}.
*
* @see #append(ManagedMediaSource)
@ -56,17 +52,17 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Appends a {@link ManagedMediaSource} to the end of {@link DynamicConcatenatingMediaSource}.
* @see DynamicConcatenatingMediaSource#addMediaSource
* Appends a {@link ManagedMediaSource} to the end of {@link ConcatenatingMediaSource}.
* @see ConcatenatingMediaSource#addMediaSource
* */
public synchronized void append(@NonNull final ManagedMediaSource source) {
internalSource.addMediaSource(source);
}
/**
* Removes a {@link ManagedMediaSource} from {@link DynamicConcatenatingMediaSource}
* Removes a {@link ManagedMediaSource} from {@link ConcatenatingMediaSource}
* at the given index. If this index is out of bound, then the removal is ignored.
* @see DynamicConcatenatingMediaSource#removeMediaSource(int)
* @see ConcatenatingMediaSource#removeMediaSource(int)
* */
public synchronized void remove(final int index) {
if (index < 0 || index > internalSource.getSize()) return;
@ -75,10 +71,10 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Moves a {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
* Moves a {@link ManagedMediaSource} in {@link ConcatenatingMediaSource}
* from the given source index to the target index. If either index is out of bound,
* then the call is ignored.
* @see DynamicConcatenatingMediaSource#moveMediaSource(int, int)
* @see ConcatenatingMediaSource#moveMediaSource(int, int)
* */
public synchronized void move(final int source, final int target) {
if (source < 0 || target < 0) return;
@ -99,7 +95,7 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Updates the {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
* Updates the {@link ManagedMediaSource} in {@link ConcatenatingMediaSource}
* at the given index with a given {@link ManagedMediaSource}.
* @see #update(int, ManagedMediaSource, Runnable)
* */
@ -108,11 +104,11 @@ public class ManagedMediaSourcePlaylist {
}
/**
* Updates the {@link ManagedMediaSource} in {@link DynamicConcatenatingMediaSource}
* Updates the {@link ManagedMediaSource} in {@link ConcatenatingMediaSource}
* at the given index with a given {@link ManagedMediaSource}. If the index is out of bound,
* then the replacement is ignored.
* @see DynamicConcatenatingMediaSource#addMediaSource
* @see DynamicConcatenatingMediaSource#removeMediaSource(int, Runnable)
* @see ConcatenatingMediaSource#addMediaSource
* @see ConcatenatingMediaSource#removeMediaSource(int, Runnable)
* */
public synchronized void update(final int index, @NonNull final ManagedMediaSource source,
@Nullable final Runnable finalizingAction) {

View File

@ -3,20 +3,19 @@ package org.schabi.newpipe.player.mediasource;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.upstream.Allocator;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import java.io.IOException;
public class PlaceholderMediaSource implements ManagedMediaSource {
public class PlaceholderMediaSource extends BaseMediaSource implements ManagedMediaSource {
// Do nothing, so this will stall the playback
@Override public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {}
@Override public void maybeThrowSourceInfoRefreshError() throws IOException {}
@Override public void maybeThrowSourceInfoRefreshError() {}
@Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { return null; }
@Override public void releasePeriod(MediaPeriod mediaPeriod) {}
@Override public void releaseSource() {}
@Override protected void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {}
@Override protected void releaseSourceInternal() {}
@Override
public boolean shouldBeReplacedWith(@NonNull PlayQueueItem newIdentity,

View File

@ -5,12 +5,10 @@ import android.support.annotation.Nullable;
import android.support.v4.util.ArraySet;
import android.util.Log;
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.mediasource.FailedMediaSource;
import org.schabi.newpipe.player.mediasource.LoadedMediaSource;
import org.schabi.newpipe.player.mediasource.ManagedMediaSource;
@ -24,10 +22,8 @@ import org.schabi.newpipe.player.playqueue.events.RemoveEvent;
import org.schabi.newpipe.player.playqueue.events.ReorderEvent;
import org.schabi.newpipe.util.ServiceHelper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@ -37,8 +33,6 @@ import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.SerialDisposable;
import io.reactivex.functions.Consumer;
import io.reactivex.internal.subscriptions.EmptySubscription;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
@ -104,7 +98,6 @@ public class MediaSourceManager {
private final static int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1;
@NonNull private final CompositeDisposable loaderReactor;
@NonNull private final Set<PlayQueueItem> loadingItems;
@NonNull private final SerialDisposable syncReactor;
@NonNull private final AtomicBoolean isBlocked;
@ -144,7 +137,6 @@ public class MediaSourceManager {
this.playQueueReactor = EmptySubscription.INSTANCE;
this.loaderReactor = new CompositeDisposable();
this.syncReactor = new SerialDisposable();
this.isBlocked = new AtomicBoolean(false);
@ -171,8 +163,6 @@ public class MediaSourceManager {
playQueueReactor.cancel();
loaderReactor.dispose();
syncReactor.dispose();
playlist.dispose();
}
/*//////////////////////////////////////////////////////////////////////////
@ -311,21 +301,7 @@ public class MediaSourceManager {
final PlayQueueItem currentItem = playQueue.getItem();
if (isBlocked.get() || currentItem == null) return;
final Consumer<StreamInfo> onSuccess = info -> syncInternal(currentItem, info);
final Consumer<Throwable> onError = throwable -> syncInternal(currentItem, null);
final Disposable sync = currentItem.getStream()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onSuccess, onError);
syncReactor.set(sync);
}
private void syncInternal(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info) {
// Ensure the current item is up to date with the play queue
if (playQueue.getItem() == item) {
playbackListener.onPlaybackSynchronize(item, info);
}
playbackListener.onPlaybackSynchronize(currentItem);
}
private synchronized void maybeSynchronizePlayer() {
@ -424,7 +400,8 @@ public class MediaSourceManager {
}
/**
* Checks if the corresponding MediaSource in {@link DynamicConcatenatingMediaSource}
* Checks if the corresponding MediaSource in
* {@link com.google.android.exoplayer2.source.ConcatenatingMediaSource}
* for a given {@link PlayQueueItem} needs replacement, either due to gapless playback
* readiness or playlist desynchronization.
* <br><br>
@ -481,8 +458,6 @@ public class MediaSourceManager {
private void resetSources() {
if (DEBUG) Log.d(TAG, "resetSources() called.");
playlist.dispose();
playlist = new ManagedMediaSourcePlaylist();
}

View File

@ -45,7 +45,7 @@ public interface PlaybackListener {
*
* May be called anytime at any amount once unblock is called.
* */
void onPlaybackSynchronize(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info);
void onPlaybackSynchronize(@NonNull final PlayQueueItem item);
/**
* Requests the listener to resolve a stream info into a media source

View File

@ -0,0 +1,41 @@
package org.schabi.newpipe.player.resolver;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.MediaSource;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.util.ListHelper;
public class AudioPlaybackResolver implements PlaybackResolver {
@NonNull private final Context context;
@NonNull private final PlayerDataSource dataSource;
public AudioPlaybackResolver(@NonNull final Context context,
@NonNull final PlayerDataSource dataSource) {
this.context = context;
this.dataSource = dataSource;
}
@Override
@Nullable
public MediaSource resolve(@NonNull StreamInfo info) {
final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info);
if (liveSource != null) return liveSource;
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
if (index < 0 || index >= info.getAudioStreams().size()) return null;
final AudioStream audio = info.getAudioStreams().get(index);
final MediaSourceTag tag = new MediaSourceTag(info);
return buildMediaSource(dataSource, audio.getUrl(), PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()), tag);
}
}

View File

@ -0,0 +1,51 @@
package org.schabi.newpipe.player.resolver;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
public class MediaSourceTag implements Serializable {
@NonNull private final StreamInfo metadata;
@NonNull private final List<VideoStream> sortedAvailableVideoStreams;
private final int selectedVideoStreamIndex;
public MediaSourceTag(@NonNull final StreamInfo metadata,
@NonNull final List<VideoStream> sortedAvailableVideoStreams,
final int selectedVideoStreamIndex) {
this.metadata = metadata;
this.sortedAvailableVideoStreams = sortedAvailableVideoStreams;
this.selectedVideoStreamIndex = selectedVideoStreamIndex;
}
public MediaSourceTag(@NonNull final StreamInfo metadata) {
this(metadata, Collections.emptyList(), /*indexNotAvailable=*/-1);
}
@NonNull
public StreamInfo getMetadata() {
return metadata;
}
@NonNull
public List<VideoStream> getSortedAvailableVideoStreams() {
return sortedAvailableVideoStreams;
}
public int getSelectedVideoStreamIndex() {
return selectedVideoStreamIndex;
}
@Nullable
public VideoStream getSelectedVideoStream() {
return selectedVideoStreamIndex < 0 ||
selectedVideoStreamIndex >= sortedAvailableVideoStreams.size() ? null :
sortedAvailableVideoStreams.get(selectedVideoStreamIndex);
}
}

View File

@ -0,0 +1,84 @@
package org.schabi.newpipe.player.resolver;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.util.Util;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.player.helper.PlayerDataSource;
public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
@Nullable
default MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final StreamInfo info) {
final StreamType streamType = info.getStreamType();
if (!(streamType == StreamType.AUDIO_LIVE_STREAM || streamType == StreamType.LIVE_STREAM)) {
return null;
}
final MediaSourceTag tag = new MediaSourceTag(info);
if (!info.getHlsUrl().isEmpty()) {
return buildLiveMediaSource(dataSource, info.getHlsUrl(), C.TYPE_HLS, tag);
} else if (!info.getDashMpdUrl().isEmpty()) {
return buildLiveMediaSource(dataSource, info.getDashMpdUrl(), C.TYPE_DASH, tag);
}
return null;
}
@NonNull
default MediaSource buildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final String sourceUrl,
@C.ContentType final int type,
@NonNull final MediaSourceTag metadata) {
final Uri uri = Uri.parse(sourceUrl);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getLiveDashMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getLiveHlsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
@NonNull
default MediaSource buildMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final String sourceUrl,
@NonNull final String cacheKey,
@NonNull final String overrideExtension,
@NonNull final MediaSourceTag metadata) {
final Uri uri = Uri.parse(sourceUrl);
@C.ContentType final int type = TextUtils.isEmpty(overrideExtension) ?
Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_DASH:
return dataSource.getDashMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_HLS:
return dataSource.getHlsMediaSourceFactory().setTag(metadata)
.createMediaSource(uri);
case C.TYPE_OTHER:
return dataSource.getExtractorMediaSourceFactory(cacheKey).setTag(metadata)
.createMediaSource(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
}

View File

@ -0,0 +1,8 @@
package org.schabi.newpipe.player.resolver;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public interface Resolver<Source, Product> {
@Nullable Product resolve(@NonNull Source source);
}

View File

@ -0,0 +1,123 @@
package org.schabi.newpipe.player.resolver;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.Subtitles;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.util.ListHelper;
import java.util.ArrayList;
import java.util.List;
import static com.google.android.exoplayer2.C.SELECTION_FLAG_AUTOSELECT;
import static com.google.android.exoplayer2.C.TIME_UNSET;
public class VideoPlaybackResolver implements PlaybackResolver {
public interface QualityResolver {
int getDefaultResolutionIndex(final List<VideoStream> sortedVideos);
int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality);
}
@NonNull private final Context context;
@NonNull private final PlayerDataSource dataSource;
@NonNull private final QualityResolver qualityResolver;
@Nullable private String playbackQuality;
public VideoPlaybackResolver(@NonNull final Context context,
@NonNull final PlayerDataSource dataSource,
@NonNull final QualityResolver qualityResolver) {
this.context = context;
this.dataSource = dataSource;
this.qualityResolver = qualityResolver;
}
@Override
@Nullable
public MediaSource resolve(@NonNull StreamInfo info) {
final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info);
if (liveSource != null) return liveSource;
List<MediaSource> mediaSources = new ArrayList<>();
// Create video stream source
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false);
final int index;
if (videos.isEmpty()) {
index = -1;
} else if (playbackQuality == null) {
index = qualityResolver.getDefaultResolutionIndex(videos);
} else {
index = qualityResolver.getOverrideResolutionIndex(videos, getPlaybackQuality());
}
final MediaSourceTag tag = new MediaSourceTag(info, videos, index);
@Nullable final VideoStream video = tag.getSelectedVideoStream();
if (video != null) {
final MediaSource streamSource = buildMediaSource(dataSource, video.getUrl(),
PlayerHelper.cacheKeyOf(info, video),
MediaFormat.getSuffixById(video.getFormatId()), tag);
mediaSources.add(streamSource);
}
// Create optional audio stream source
final List<AudioStream> audioStreams = info.getAudioStreams();
final AudioStream audio = audioStreams.isEmpty() ? null : audioStreams.get(
ListHelper.getDefaultAudioFormat(context, audioStreams));
// Use the audio stream if there is no video stream, or
// Merge with audio stream in case if video does not contain audio
if (audio != null && ((video != null && video.isVideoOnly) || video == null)) {
final MediaSource audioSource = buildMediaSource(dataSource, audio.getUrl(),
PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()), tag);
mediaSources.add(audioSource);
}
// If there is no audio or video sources, then this media source cannot be played back
if (mediaSources.isEmpty()) return null;
// Below are auxiliary media sources
// Create subtitle sources
for (final Subtitles subtitle : info.getSubtitles()) {
final String mimeType = PlayerHelper.mimeTypesOf(subtitle.getFileType());
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);
}
if (mediaSources.size() == 1) {
return mediaSources.get(0);
} else {
return new MergingMediaSource(mediaSources.toArray(
new MediaSource[mediaSources.size()]));
}
}
@Nullable
public String getPlaybackQuality() {
return playbackQuality;
}
public void setPlaybackQuality(@Nullable String playbackQuality) {
this.playbackQuality = playbackQuality;
}
}

View File

@ -56,6 +56,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
private File databasesDir;
private File newpipe_db;
private File newpipe_db_journal;
private File newpipe_db_shm;
private File newpipe_db_wal;
private File newpipe_settings;
private String thumbnailLoadToggleKey;
@ -88,6 +90,9 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
databasesDir = new File(homeDir + "/databases");
newpipe_db = new File(homeDir + "/databases/newpipe.db");
newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal");
newpipe_db_shm = new File(homeDir + "/databases/newpipe.db-shm");
newpipe_db_wal = new File(homeDir + "/databases/newpipe.db-wal");
newpipe_settings = new File(homeDir + "/databases/newpipe.settings");
newpipe_settings.delete();
@ -207,7 +212,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
new BufferedOutputStream(
new FileOutputStream(path)));
ZipHelper.addFileToZip(outZip, newpipe_db.getPath(), "newpipe.db");
ZipHelper.addFileToZip(outZip, newpipe_db_journal.getPath(), "newpipe.db-journal");
saveSharedPreferencesToFile(newpipe_settings);
ZipHelper.addFileToZip(outZip, newpipe_settings.getPath(), "newpipe.settings");
@ -263,8 +268,16 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
throw new Exception("Could not create databases dir");
}
if(!(ZipHelper.extractFileFromZip(filePath, newpipe_db.getPath(), "newpipe.db")
&& ZipHelper.extractFileFromZip(filePath, newpipe_db_journal.getPath(), "newpipe.db-journal"))) {
final boolean isDbFileExtracted = ZipHelper.extractFileFromZip(filePath,
newpipe_db.getPath(), "newpipe.db");
if (isDbFileExtracted) {
newpipe_db_journal.delete();
newpipe_db_wal.delete();
newpipe_db_shm.delete();
} else {
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
.show();
}

View File

@ -6,7 +6,7 @@ public class Constants {
public static final String KEY_TITLE = "key_title";
public static final String KEY_LINK_TYPE = "key_link_type";
public static final String KEY_OPEN_SEARCH = "key_open_search";
public static final String KEY_QUERY = "key_query";
public static final String KEY_SEARCH_STRING = "key_search_string";
public static final String KEY_THEME_CHANGE = "key_theme_change";
public static final String KEY_MAIN_PAGE_CHANGE = "key_main_page_change";

View File

@ -37,9 +37,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
@ -50,7 +49,6 @@ import java.util.List;
import io.reactivex.Maybe;
import io.reactivex.Single;
import io.reactivex.annotations.NonNull;
public final class ExtractorHelper {
private static final String TAG = ExtractorHelper.class.getSimpleName();
@ -66,29 +64,35 @@ public final class ExtractorHelper {
}
}
public static Single<SearchResult> searchFor(final int serviceId,
final String query,
final int pageNumber,
final String contentCountry,
final SearchEngine.Filter filter) {
public static Single<SearchInfo> searchFor(final int serviceId,
final String searchString,
final List<String> contentFilter,
final String sortFilter,
final String contentCountry) {
checkServiceId(serviceId);
return Single.fromCallable(() ->
SearchResult.getSearchResult(NewPipe.getService(serviceId).getSearchEngine(),
query, pageNumber, contentCountry, filter)
);
SearchInfo.getInfo(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry));
}
public static Single<InfoItemsPage> getMoreSearchItems(final int serviceId,
final String query,
final int nextPageNumber,
final String searchLanguage,
final SearchEngine.Filter filter) {
final String searchString,
final List<String> contentFilter,
final String sortFilter,
final String pageUrl,
final String contentCountry) {
checkServiceId(serviceId);
return searchFor(serviceId, query, nextPageNumber, searchLanguage, filter)
.map((@NonNull SearchResult searchResult) ->
new InfoItemsPage(searchResult.resultList,
nextPageNumber + "",
searchResult.errors));
return Single.fromCallable(() ->
SearchInfo.getMoreItems(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry,
pageUrl));
}
public static Single<List<String>> suggestionsFor(final int serviceId,
@ -233,7 +237,6 @@ public final class ExtractorHelper {
serviceId == -1 ? "none" : NewPipe.getNameOfService(serviceId), url + (optionalErrorMessage == null ? "" : optionalErrorMessage), errorId));
}
});
}
/**

View File

@ -26,10 +26,13 @@ import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
@ -100,11 +103,13 @@ public class NavigationHelper {
final int repeatMode,
final float playbackSpeed,
final float playbackPitch,
final boolean playbackSkipSilence,
@Nullable final String playbackQuality) {
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality)
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch)
.putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence);
}
public static void playOnMainPlayer(final Context context, final PlayQueue queue) {
@ -281,9 +286,11 @@ public class NavigationHelper {
return fragmentManager.popBackStackImmediate(SEARCH_FRAGMENT_TAG, 0);
}
public static void openSearchFragment(FragmentManager fragmentManager, int serviceId, String query) {
public static void openSearchFragment(FragmentManager fragmentManager,
int serviceId,
String searchString) {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, query))
.replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, searchString))
.addToBackStack(SEARCH_FRAGMENT_TAG)
.commit();
}
@ -312,7 +319,11 @@ public class NavigationHelper {
.commit();
}
public static void openChannelFragment(FragmentManager fragmentManager, int serviceId, String url, String name) {
public static void openChannelFragment(
FragmentManager fragmentManager,
int serviceId,
String url,
String name) {
if (name == null) name = "";
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name))
@ -320,7 +331,10 @@ public class NavigationHelper {
.commit();
}
public static void openPlaylistFragment(FragmentManager fragmentManager, int serviceId, String url, String name) {
public static void openPlaylistFragment(FragmentManager fragmentManager,
int serviceId,
String url,
String name) {
if (name == null) name = "";
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, name))
@ -368,10 +382,10 @@ public class NavigationHelper {
// Through Intents
//////////////////////////////////////////////////////////////////////////*/
public static void openSearch(Context context, int serviceId, String query) {
public static void openSearch(Context context, int serviceId, String searchString) {
Intent mIntent = new Intent(context, MainActivity.class);
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
mIntent.putExtra(Constants.KEY_QUERY, query);
mIntent.putExtra(Constants.KEY_SEARCH_STRING, searchString);
mIntent.putExtra(Constants.KEY_OPEN_SEARCH, true);
context.startActivity(mIntent);
}
@ -465,7 +479,8 @@ public class NavigationHelper {
switch (linkType) {
case STREAM:
rIntent.putExtra(VideoDetailFragment.AUTO_PLAY, PreferenceManager.getDefaultSharedPreferences(context)
rIntent.putExtra(VideoDetailFragment.AUTO_PLAY,
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.autoplay_through_intent_key), false));
break;
}

View File

@ -5,7 +5,6 @@ import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
@ -31,6 +30,18 @@ public class ServiceHelper {
}
}
public static String getTranslatedFilterString(String filter, Context c) {
switch(filter) {
case "all": return c.getString(R.string.all);
case "videos": return c.getString(R.string.videos);
case "channels": return c.getString(R.string.channels);
case "playlists": return c.getString(R.string.playlists);
case "tracks": return c.getString(R.string.tracks);
case "users": return c.getString(R.string.users);
default: return filter;
}
}
/**
* Get a resource string with instructions for importing subscriptions for each service.
*

View File

@ -25,10 +25,13 @@ import android.widget.TextView;
import android.widget.Toast;
import org.schabi.newpipe.R;
import org.schabi.newpipe.download.DeleteDownloadManager;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -52,18 +55,34 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
private Activity mContext;
private LayoutInflater mInflater;
private DownloadManager mManager;
private DownloadManager mDownloadManager;
private DeleteDownloadManager mDeleteDownloadManager;
private List<DownloadMission> mItemList;
private DownloadManagerService.DMBinder mBinder;
private int mLayout;
public MissionAdapter(Activity context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
public MissionAdapter(Activity context, DownloadManagerService.DMBinder binder, DownloadManager downloadManager, DeleteDownloadManager deleteDownloadManager, boolean isLinear) {
mContext = context;
mManager = manager;
mDownloadManager = downloadManager;
mDeleteDownloadManager = deleteDownloadManager;
mBinder = binder;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item;
mItemList = new ArrayList<>();
updateItemList();
}
public void updateItemList() {
mItemList.clear();
for (int i = 0; i < mDownloadManager.getCount(); i++) {
DownloadMission mission = mDownloadManager.getMission(i);
if (!mDeleteDownloadManager.contains(mission)) {
mItemList.add(mDownloadManager.getMission(i));
}
}
}
@Override
@ -102,7 +121,7 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
@Override
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
DownloadMission ms = mManager.getMission(pos);
DownloadMission ms = mItemList.get(pos);
h.mission = ms;
h.position = pos;
@ -123,7 +142,7 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
@Override
public int getItemCount() {
return mManager.getCount();
return mItemList.size();
}
@Override
@ -214,12 +233,12 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
int id = item.getItemId();
switch (id) {
case R.id.start:
mManager.resumeMission(h.position);
mBinder.onMissionAdded(mManager.getMission(h.position));
mDownloadManager.resumeMission(h.position);
mBinder.onMissionAdded(mItemList.get(h.position));
return true;
case R.id.pause:
mManager.pauseMission(h.position);
mBinder.onMissionRemoved(mManager.getMission(h.position));
mDownloadManager.pauseMission(h.position);
mBinder.onMissionRemoved(mItemList.get(h.position));
h.lastTimeStamp = -1;
h.lastDone = -1;
return true;
@ -245,12 +264,13 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
return true;
case R.id.delete:
mManager.deleteMission(h.position);
mDeleteDownloadManager.add(h.mission);
updateItemList();
notifyDataSetChanged();
return true;
case R.id.md5:
case R.id.sha1:
DownloadMission mission = mManager.getMission(h.position);
DownloadMission mission = mItemList.get(h.position);
new ChecksumTask(mContext).execute(mission.location + "/" + mission.name, ALGORITHMS.get(id));
return true;
default:
@ -262,19 +282,6 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
popup.show();
}
private void viewFile(File file, String mimetype) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), mimetype);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
}
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.v(TAG, "Starting intent: " + intent);
mContext.startActivity(intent);
}
private void viewFileWithFileProvider(File file, String mimetype) {
String ourPackage = mContext.getApplicationContext().getPackageName();
Uri uri = FileProvider.getUriForFile(mContext, ourPackage + ".provider", file);

View File

@ -10,6 +10,8 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@ -19,13 +21,15 @@ import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.download.DeleteDownloadManager;
import io.reactivex.disposables.Disposable;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.adapter.MissionAdapter;
public abstract class MissionsFragment extends Fragment {
private DownloadManager mManager;
private DownloadManager mDownloadManager;
private DownloadManagerService.DMBinder mBinder;
private SharedPreferences mPrefs;
@ -37,14 +41,19 @@ public abstract class MissionsFragment extends Fragment {
private GridLayoutManager mGridManager;
private LinearLayoutManager mLinearManager;
private Context mActivity;
private DeleteDownloadManager mDeleteDownloadManager;
private Disposable mDeleteDisposable;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mBinder = (DownloadManagerService.DMBinder) binder;
mManager = setupDownloadManager(mBinder);
updateList();
mDownloadManager = setupDownloadManager(mBinder);
if (mDeleteDownloadManager != null) {
mDeleteDownloadManager.setDownloadManager(mDownloadManager);
updateList();
}
}
@Override
@ -55,6 +64,14 @@ public abstract class MissionsFragment extends Fragment {
};
public void setDeleteManager(@NonNull DeleteDownloadManager deleteDownloadManager) {
mDeleteDownloadManager = deleteDownloadManager;
if (mDownloadManager != null) {
mDeleteDownloadManager.setDownloadManager(mDownloadManager);
updateList();
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.missions, container, false);
@ -104,10 +121,26 @@ public abstract class MissionsFragment extends Fragment {
mActivity = activity;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mDeleteDownloadManager != null) {
mDeleteDisposable = mDeleteDownloadManager.getUndoObservable().subscribe(mission -> {
if (mAdapter != null) {
mAdapter.updateItemList();
mAdapter.notifyDataSetChanged();
}
});
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
getActivity().unbindService(mConnection);
if (mDeleteDisposable != null) {
mDeleteDisposable.dispose();
}
}
@Override
@ -129,7 +162,7 @@ public abstract class MissionsFragment extends Fragment {
}
private void updateList() {
mAdapter = new MissionAdapter((Activity) mActivity, mBinder, mManager, mLinear);
mAdapter = new MissionAdapter((Activity) mActivity, mBinder, mDownloadManager, mDeleteDownloadManager, mLinear);
if (mLinear) {
mList.setLayoutManager(mLinearManager);
@ -143,7 +176,7 @@ public abstract class MissionsFragment extends Fragment {
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
}
mPrefs.edit().putBoolean("linear", mLinear).commit();
mPrefs.edit().putBoolean("linear", mLinear).apply();
}
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);

View File

@ -41,7 +41,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerInside"
android:visibility="gone"
tools:background="@android:color/white"
tools:ignore="ContentDescription"

View File

@ -260,13 +260,95 @@
</RelativeLayout>
<View
android:id="@+id/separatorCheckbox"
android:id="@+id/separatorStepSizeSelector"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/pitchControl"
android:layout_margin="@dimen/video_item_search_padding"
android:background="?attr/separator_color"/>
<LinearLayout
android:id="@+id/stepSizeSelector"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:layout_below="@id/separatorStepSizeSelector">
<TextView
android:id="@+id/stepSizeText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/playback_step"
android:textStyle="bold"
android:clickable="false"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeOnePercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeFivePercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeTenPercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeTwentyFivePercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/stepSizeOneHundredPercent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
</LinearLayout>
<View
android:id="@+id/separatorCheckbox"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/stepSizeSelector"
android:layout_margin="@dimen/video_item_search_padding"
android:background="?attr/separator_color"/>
<CheckBox
android:id="@+id/unhookCheckbox"
android:layout_width="match_parent"
@ -279,33 +361,17 @@
android:layout_centerHorizontal="true"
android:layout_below="@id/separatorCheckbox"/>
<LinearLayout
android:id="@+id/presetSelector"
<CheckBox
android:id="@+id/skipSilenceCheckbox"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:layout_below="@id/unhookCheckbox">
<TextView
android:id="@+id/presetNightcore"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/playback_nightcore"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
<TextView
android:id="@+id/presetReset"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/playback_default"
android:background="?attr/selectableItemBackground"
android:textColor="?attr/colorAccent"/>
</LinearLayout>
android:layout_height="wrap_content"
android:checked="false"
android:clickable="true"
android:focusable="true"
android:text="@string/skip_silence_checkbox"
android:maxLines="1"
android:layout_centerHorizontal="true"
android:layout_below="@id/unhookCheckbox"/>
<!-- END HERE -->

View File

@ -101,7 +101,8 @@
<RelativeLayout
android:id="@+id/detail_content_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="?android:windowBackground">
<!-- TITLE -->
<FrameLayout

View File

@ -111,7 +111,7 @@
<TextView
android:id="@+id/resizeTextView"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="?attr/selectableItemBackground"

View File

@ -4,7 +4,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical">
<RelativeLayout
@ -12,7 +11,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true">
android:focusable="true"
android:background="?attr/selectableItemBackground">
<ImageView
android:id="@+id/sortButtonIcon"
android:layout_width="48dp"

View File

@ -1,8 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="background_player_playing_toast">جاري التشغيل في الخلفية</string>
<string name="background_player_playing_toast">يتم التشغيل في الخلفية</string>
<string name="cancel">إلغاء</string>
<string name="choose_browser">إختر متصفح</string>
<string name="choose_browser">اختر المتصفح</string>
<string name="dark_theme_title">مظلم</string>
<string name="default_audio_format_title">صيغة الصوت الإفتراضية</string>
<string name="default_resolution_title">الدقة الإفتراضية</string>
@ -25,7 +25,7 @@
<string name="kore_not_found">تطبيق Kore غير موجود. هل تريد تثبيته ؟</string>
<string name="light_theme_title">مضيء</string>
<string name="list_thumbnail_view_description">صور معاينة الفيديو</string>
<string name="m4a_description">M4A — جودة أفضل</string>
<string name="m4a_description">جودة أفضل — M4A</string>
<string name="network_error">خطأ في الشبكة</string>
<string name="next_video_title">الفيديو التالي</string>
<string name="no_player_found">لا يوجد مشغل فيديو. هل تريد تثبيت VLC ؟</string>
@ -42,7 +42,7 @@
<string name="settings_category_video_audio_title">الفيديو والصوتيات</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">عرض خيار لتشغيل الفيديو بواسطة Kodi Media Center</string>
<string name="show_play_with_kodi_title">عرض خيار التشغيل بواسطة Kodi</string>
<string name="theme_title">السمة</string>
@ -61,10 +61,10 @@
<string name="parsing_error">تعذرت عملية تحليل الموقع</string>
<string name="youtube_signature_decryption_error">تعذر فك تشفير توقيع رابط الفيديو</string>
<string name="main_bg_subtitle">اضغط بحث للبدء</string>
<string name="subscribe_button_title">إشتراك</string>
<string name="subscribe_button_title">اشتراك</string>
<string name="subscribed_button_title">مشترك</string>
<string name="tab_main">الرئيسية</string>
<string name="tab_subscriptions">الإشتراكات</string>
<string name="tab_subscriptions">الاشتراكات</string>
<string name="fragment_whats_new">ما الجديد</string>
@ -72,7 +72,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">التاريخ و ذاكرة التخزين المؤقتة</string>
<string name="content">المحتوى</string>
<string name="downloads">التنزيلات</string>
<string name="downloads_title">التنزيلات</string>
@ -84,24 +84,24 @@
<string name="tab_about">عن التطبيق</string>
<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="open_in_popup_mode">فتح في وضع النافذة المنبثقة</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>
<string name="channel_unsubscribed">تم إلغاء الاشتراك في القناة</string>
<string name="subscription_change_failed">تعذر تغيير حالة الاشتراك</string>
<string name="subscription_update_failed">تعذر تحديث الاشتراك</string>
<string name="controls_popup_title">نافذة</string>
<string name="autoplay_by_calling_app_summary">تشغيل مقطع الفيديو عند إستدعاء NewPipe من تطبيق آخر</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="default_video_format_title">تنسيق الفيديو الافتراضي</string>
<string name="popup_remember_size_pos_title">تذكر حجم النافذة و وضعها</string>
<string name="popup_remember_size_pos_summary">تذكر آخر حجم ومكان للنافذة</string>
<string name="popup_remember_size_pos_summary">تذكر آخر مكان و حجم للنافذة المنبثقة</string>
<string name="player_gesture_controls_title">اعدادات إيماءة المشغل</string>
<string name="player_gesture_controls_summary">استخدم إيماءات للتحكم في سطوع وحجم المشغل</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>
@ -114,7 +114,7 @@
<string name="settings_category_player_title">المشغل</string>
<string name="settings_category_player_behavior_title">السلوك</string>
<string name="settings_category_popup_title">المنبثق</string>
<string name="popup_playing_toast">التشغيل في الوضع المنبثق</string>
<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>
@ -170,9 +170,9 @@
<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="short_thousand">الف</string>
<string name="short_million">مليون</string>
<string name="short_billion">بليون</string>
<string name="no_subscribers">صفر لا تقم با الإختيار (في بعض اللغات) لأنها ليست \"حالة خاصة\" للأندرويد</string>
<plurals name="subscribers">
@ -186,14 +186,14 @@
<string name="no_views">لاتوجد مشاهدات</string>
<string name="no_videos">لاتوجد فديوهات</string>
<string name="start">بداية</string>
<string name="start">تشغيل</string>
<string name="pause">إيقاف</string>
<string name="view">شغل</string>
<string name="delete">حذف</string>
<string name="checksum">التوقيع</string>
<string name="add">مهمة جديدة</string>
<string name="finish">حسنا</string>
<string name="finish">حسناً</string>
<string name="msg_name">اسم الملف</string>
<string name="msg_threads">العمليات</string>
@ -238,7 +238,7 @@
<string name="title_history_search">البحث</string>
<string name="title_history_view">شاهد</string>
<string name="title_history_view">تمت مشاهدته</string>
<string name="history_disabled">تم تعطيل السجل</string>
<string name="history_empty">التاريخ فارغ</string>
<string name="history_cleared">تم مسح التاريخ</string>
@ -251,7 +251,7 @@
<string name="feed_page_summary">صفحة الخلاصة</string>
<string name="channel_page_summary">صفحة القناة</string>
<string name="select_a_channel">حدد قناة</string>
<string name="no_channel_subscribed_yet">لم يتم الاشتراك في القناة بعد</string>
<string name="no_channel_subscribed_yet">لم يتم الاشتراك في اي قناة بعد</string>
<string name="trending">الترند</string>
<string name="top_50">أفضل 50</string>
<string name="new_and_hot">جديد &amp; وساخن</string>
@ -265,9 +265,9 @@
<string name="reCaptcha_title">تحدي ريكابتشا</string>
<string name="hold_to_append">اضغط للإدراج بقائمة الانتظار</string>
<plurals name="views">
<item quantity="zero">لم تتم المشاهدة</item>
<item quantity="zero">لاتوجد مشاهدة</item>
<item quantity="one">%s مشاهدة</item>
<item quantity="two">"اثنان مشاهدات "</item>
<item quantity="two">مشاهدتين</item>
<item quantity="few">%s مشاهدات كثيرة</item>
<item quantity="many">%s عدد المشاهدات</item>
<item quantity="other">%s أقصى مشاهدات</item>
@ -276,10 +276,10 @@
<plurals name="videos">
<item quantity="zero">صفر</item>
<item quantity="one">واحد</item>
<item quantity="two">اثنان</item>
<item quantity="few">قليل</item>
<item quantity="many">عدد كثير</item>
<item quantity="other">"أخرى "</item>
<item quantity="two">%s اثنان</item>
<item quantity="few">%s قليل</item>
<item quantity="many">%s عدد كثير</item>
<item quantity="other">"%s أخرى "</item>
</plurals>
<string name="recaptcha_request_toast">إعادة طلب كلمة التحقق</string>
@ -289,27 +289,27 @@
<string name="select_a_kiosk">حدد كشك</string>
<string name="kiosk">كشك</string>
<string name="enqueue_on_background">إدراج بقائمة الانتظار على خلفية</string>
<string name="enqueue_on_background">إدراج بقائمة الانتظار في مشغل الخلفية</string>
<string name="enqueue_on_popup">إدراج بقائمة الانتظار على المنبثقة</string>
<string name="start_here_on_background">ابدأ هنا على خلفية المصدر</string>
<string name="default_content_country_title">المحتوى الإفتراضي حسب البلد</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="switch_to_background">الإنتقال إلى التشغيل في الخلفية</string>
<string name="switch_to_popup">الإنتقال إلى التشغيل في النافذة المنبثقة</string>
<string name="switch_to_main">الإنتقال إلى الرئيسية</string>
<string name="service_title">الخدمة</string>
<string name="drawer_open">فتح الدرج</string>
<string name="drawer_close">إغلاق الدرج</string>
<string name="always">دائمًا</string>
<string name="always">دائماً</string>
<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="export_data_summary">تصدير سجل, الاشتراكات وقوائم التشغيل.</string>
<string name="export_data_summary">تصدير سجل, الاشتراكات وقوائم التشغيل</string>
<string name="show_info">عرض المعلومات</string>
<string name="controls_add_to_playlist_title">إضافة إلى</string>
@ -334,7 +334,7 @@
<string name="video_player">مشغل الفديو</string>
<string name="always_ask_open_action">السؤال دائمًا</string>
<string name="always_ask_open_action">السؤال دائماً</string>
<string name="preferred_player_fetcher_notification_title">الحصول على المعلومات …</string>
<string name="preferred_player_fetcher_notification_message">تحميل المحتوى المطلوب</string>
@ -342,14 +342,14 @@
<string name="create_playlist">إنشاء قائمة تشغيل جديدة</string>
<string name="delete_playlist">حذف قائمة التشغيل</string>
<string name="rename_playlist">"إعادة تسمية قائمة التشغيل "</string>
<string name="playlist_name_input">التسمية</string>
<string name="playlist_name_input">الأسم</string>
<string name="append_playlist">إضافة إلى قائمة تشغيل</string>
<string name="delete_playlist_prompt">هل تريد حذف قائمة التشغيل هذه ؟</string>
<string name="playlist_creation_success">قائمة التشغيل التي تم إنشاؤها</string>
<string name="playlist_add_stream_success">تمت إضافتها إلى قائمة التشغيل</string>
<string name="playlist_delete_failure">لا يمكن حذف قائمة التشغيل</string>
<string name="resize_fill">ملئ</string>
<string name="resize_fill">ملئ الشاشة</string>
<string name="resize_zoom">تكبير</string>
<string name="caption_font_size_settings_title">حجم خط التسمية</string>
@ -359,7 +359,7 @@
<string name="live_sync">مُزامَنة</string>
<string name="controls_download_desc">تنزيل الملف البث.</string>
<string name="controls_download_desc">تنزيل ملف البث.</string>
<string name="tab_bookmarks">الإشارات مرجعية</string>
<string name="use_inexact_seek_title">استعمال التقديم السريع الغير دقيق</string>
@ -389,38 +389,38 @@
<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="set_as_playlist_thumbnail">إضافة إلى قائمة التشغيل كصورة مصغرة</string>
<string name="auto_queue_summary">رفض البث المشابه في حال كون البث السابق يعمل في حالة عدم التكرار</string>
<string name="set_as_playlist_thumbnail">إضافة صورة مصغرة إلى قائمة التشغيل</string>
<string name="bookmark_playlist">قائمة التشغيل المخزنة</string>
<string name="playlist_thumbnail_change_success">تم تغيير الصورة المصغرة لقائمة التشغيل</string>
<string name="caption_none">بدون تسميات توضيحية</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_leak_canary_summary">قد تتسبب مراقبة تسرب الذاكرة في عدم استجابة التطبيق عند تفريغ السجلات</string>
<string name="enable_disposed_exceptions_title">تقرير الأخطاء خارج دورة الحياة</string>
<string name="enable_disposed_exceptions_summary">فرض الإبلاغ عن استثناءات Rx غير القابلة للتسليم خارج دورة حياة الجزء أو النشاط بعد التخلص منها</string>
<string name="clear_views_history_title">محو سجل المشاهدة</string>
<string name="clear_views_history_summary">احذف محفوظات التدفقات التي تم تشغيلها.</string>
<string name="delete_view_history_alert">حذف سجل المشاهدة بالكامل.</string>
<string name="view_history_deleted">سجل المشاهدة المحذوف.</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="clear_search_history_summary">يحذف تاريخ البحث عن الكلمات الرئيسية</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_file">الملف غير موجود أو ليس لديك الإذن الكافي للقراءة أو الكتابة إليه</string>
<string name="no_streams_available_download">لا توجد تدفقات متاحة للتنزيل</string>
<string name="one_item_deleted">تم حذف عنصر واحد.</string>
@ -433,7 +433,7 @@
<string name="title_last_played">آخر ما تم تشغيله</string>
<string name="title_most_played">الأكثر تشغيلا</string>
<string name="override_current_data">هذا سوف يتجاوز الإعداد الحالي الخاص بك.</string>
<string name="override_current_data">هذا سوف يتجاوز الإعدادت الحالية الخاصة بك.</string>
<string name="preferred_open_action_settings_title">طريقة \'التشغيل\' المفضلة</string>
<string name="preferred_open_action_settings_summary">"الإجراء الافتراضي عند فتح المحتوى — %s"</string>
@ -445,25 +445,25 @@
<string name="subscriptions_import_unsuccessful">تعذر استيراد الاشتراكات</string>
<string name="subscriptions_export_unsuccessful">لا يمكن تصدير الاشتراكات</string>
<string name="import_youtube_instructions">استيراد اشتراكات YouTube عن طريق تنزيل ملف التصدير:
\n
\n1. انتقل إلى عنوان URL هذا: %1$s
\n2. تسجيل الدخول عندما يتطلب
\n3. يجب أن يبدأ تنزيل (وهذا ملف التصدير)</string>
<string name="import_soundcloud_instructions">قم باستيراد ملف تعريف SoundCloud عن طريق كتابة عنوان URL أو معرفك:
\n
\n1. تمكين \"وضع سطح المكتب\" في متصفح الويب (الموقع غير متاح للأجهزة المحمولة)
\n2. انتقل إلى عنوان URL هذا: %1$s
\n3. تسجيل الدخول عندما يتطلب
<string name="import_youtube_instructions">استيراد اشتراكات YouTube عن طريق تنزيل ملف التصدير:
\n
\n1. انتقل إلى عنوان URL هذا: %1$s
\n2. تسجيل الدخول عندما يطلب منك
\n3. يجب أن يبدأ التنزيل (وهذا ملف التصدير)</string>
<string name="import_soundcloud_instructions">قم باستيراد ملف تعريف SoundCloud عن طريق كتابة عنوان URL أو معرفك:
\n
\n1. تمكين \"وضع سطح المكتب\" في متصفح الويب (الموقع غير متاح للأجهزة المحمولة)
\n2. انتقل إلى عنوان URL هذا: %1$s
\n3. تسجيل الدخول عندما يطلب منك
\n4. انسخ عنوان URL للملف الشخصي الذي تمت إعادة توجيهك إليه.</string>
<string name="import_network_expensive_warning">ضع في اعتبارك أن هذه العملية يمكن أن تكون باهظة الثمن.
\n
<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="unhook_checkbox">نزع الإرتباط (قد يسبب تشويه)</string>
<string name="playback_nightcore">تعديل الايقاع Nightcore</string>
<string name="import_settings">هل تريد أيضا استيراد الإعدادات؟</string>
@ -478,4 +478,18 @@
<string name="limit_data_usage_none_description">لا حدود</string>
<string name="limit_mobile_data_usage_title">الحد من جودة الفيديو عند استخدام بيانات الهاتف المحمول</string>
<string name="skip_silence_checkbox">تسريع إلى الأمام أثناء الصمت</string>
<string name="playback_step">خطوة</string>
<string name="playback_reset">إعادة تعيين</string>
<string name="minimize_on_exit_title">تصغير عند تبديل التطبيق</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_popup_description">تصغير إلى مشغل منبثق</string>
<string name="channels">القنوات</string>
<string name="playlists">قوائم التشغيل</string>
<string name="tracks">المسارات</string>
<string name="users">المستخدمين</string>
</resources>

View File

@ -1,14 +1,14 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">Натисни на търсене за да започнеш</string>
<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="install">Инсталирай</string>
<string name="cancel">Затвори</string>
<string name="open_in_browser">Отвори в браузъра</string>
<string name="open_in_popup_mode">Отвори в подпрозорец</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="download">Изтегли</string>
<string name="search">Търси</string>
<string name="settings">Настройки</string>
<string name="did_you_mean">Може би имахте в предвид: %1$s ?</string>
@ -16,81 +16,81 @@
<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>
<string name="subscription_change_failed">Не мога да променя абонаментите</string>
<string name="subscription_update_failed">Не мога да обновя абонаментите</string>
<string name="subscription_change_failed">Неуспешна промяна на абонамента</string>
<string name="subscription_update_failed">Неуспешно обновление на абонамента</string>
<string name="tab_subscriptions">Абонаменти</string>
<string name="fragment_whats_new">Новости</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="download_path_title">Локация за запис на клиповете</string>
<string name="download_path_summary">Път за съхранение на свалени клипове</string>
<string name="download_path_dialog_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_dialog_title">Въведи път за съхранение на свалено аудио</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">Автоматично въпроизвеждане на видео, когато NewPipe е повикан от друго приложение</string>
<string name="autoplay_by_calling_app_summary">Въпроизвежда видео, когато NewPipe е повикан от друго приложение</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>
<string name="kore_not_found">Приложението Kore не е намерено. Желаете ли да го инсталирате?</string>
<string name="show_play_with_kodi_title">Покажи опция \"Въпроизвеждане с Kodi\"</string>
<string name="show_play_with_kodi_summary">Показване на опция за въпроизвеждане на видео чрез Kodi media center</string>
<string name="kore_not_found">Приложението Kore не е намерено. Желаете ли да го инсталирате?</string>
<string name="show_play_with_kodi_title">Покажи „Възпроизвеждане с Kodi“</string>
<string name="show_play_with_kodi_summary">Показване на опция за възпроизвеждане на видео чрез Kodi media center</string>
<string name="play_audio">Аудио</string>
<string name="default_audio_format_title">Аудио формат по подразбиране</string>
<string name="default_video_format_title">Видео формат по подразбиране</string>
<string name="webm_description">WebM — свободен формат</string>
<string name="m4a_description">M4A — по-добро качество</string>
<string name="theme_title">Тема</string>
<string name="theme_title">Тема на външния вид</string>
<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="popup_remember_size_pos_summary">Използвай размера и позицията на прозореца от предишния път</string>
<string name="player_gesture_controls_title">Контролиране на плейъра чрез жестове</string>
<string name="player_gesture_controls_summary">Използвай жестове за да контролираш яркостта и силата на звука на плейъра</string>
<string name="player_gesture_controls_summary">Позволи използване на жестове за контрол на яркостта и силата на звука на плейъра</string>
<string name="show_search_suggestions_title">Предложения за търсене</string>
<string name="show_search_suggestions_summary">Покажи предположения по време на търсене</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="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="url_not_supported_toast">Неподдържан URL</string>
<string name="show_next_and_similar_title">Показвай „следващ“ и „подобни“</string>
<string name="show_hold_to_append_title">Показвай съвет „задръж за добавяне“</string>
<string name="url_not_supported_toast">Непознат URL</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">Видео &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_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="background_player_playing_toast">Въпроизвеждане във фонов режим</string>
<string name="popup_playing_toast">Въпроизвеждане в подпрозорец</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="popup_playing_append">Включен в опашката в нов прозорец</string>
<string name="play_btn_text">Възпроизвеждане</string>
<string name="content">Съдържание</string>
<string name="show_age_restricted_content_title">Покажи съдържание с ограничение във възрастта</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>
@ -111,17 +111,17 @@
<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="general_error">Грешка</string>
<string name="network_error">Проблем с мрежата</string>
<string name="could_not_load_thumbnails">Не мога да заредя всички миниатюри</string>
<string name="parsing_error">Не мога да интерпретирам правилно уебсайта</string>
<string name="light_parsing_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="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>
@ -131,7 +131,7 @@
<string name="player_unrecoverable_failure">Критичен проблем с плейъра</string>
<string name="player_recoverable_failure">Опит за възстановяване от възникналия проблем с плейъра</string>
<string name="sorry_string">Съжаляваме, но това не би трябвало да се случва.</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>
@ -141,23 +141,23 @@
<string name="error_details_headline">Подробности:</string>
<string name="detail_thumbnail_view_description">Миниатюра на клипа</string>
<string name="detail_uploader_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="use_tor_title">Използвай Tor</string>
<string name="use_tor_summary">(Експериментално) Пренасочвай трафика при сваляне към мрежата на Tor за по-високо ниво на поверителност (стриймването на видео все още не се поддържа).</string>
<string name="report_error">Докладвай за грешка</string>
<string name="search_no_results">Няма резултати</string>
<string name="empty_subscription_feed_subtitle">Тук все още няма нищо</string>
<string name="empty_subscription_feed_subtitle">Тук няма нищо</string>
<string name="err_dir_create">Не мога да създам директория за свалянията в \'%1$s\'</string>
<string name="info_dir_created">Създадох директория за свалянията в \'%1$s\'</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="storage_permission_denied">Достъпа до хранилището е отказан</string>
<string name="use_old_player_title">Използвай стария плейър</string>
<string name="use_old_player_summary">Използваю стария вграден Mediaframewoek плейър</string>
@ -181,34 +181,286 @@
<string name="pause">Пауза</string>
<string name="view">Начало</string>
<string name="delete">Изтрий</string>
<string name="checksum">Чексума</string>
<string name="checksum">Контролна сума</string>
<string name="finish">Готово</string>
<string name="finish">ОК</string>
<string name="msg_name">Име на файла</string>
<string name="msg_threads">Нишки</string>
<string name="msg_error">Грешка</string>
<string name="msg_server_unsupported">Сървърът не се поддържа</string>
<string name="msg_exists">Файлът вече съществува</string>
<string name="msg_url_malform">Неправилен формат на URL-а или няма връзка</string>
<string name="msg_running">NewPipe Сваляне</string>
<string name="msg_running_detail">Натисни за подробности</string>
<string name="msg_url_malform">Неправилен хиперлинк или няма връзка с Интернет</string>
<string name="msg_running">NewPipe Изтегляне</string>
<string name="msg_running_detail">Докосни за подробности</string>
<string name="msg_wait">Моля, изчакайте…</string>
<string name="msg_copied">Копирано в клипборда</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="settings_file_replacement_character_title">Символ за замяна</string>
<string name="charset_letters_and_digits">Букви и цифри</string>
<string name="title_activity_about">За NewPipe</string>
<string name="action_settings">Настройки</string>
<string name="action_about">Относно приложението</string>
<string name="title_licenses">Лицензи от трети страни</string>
<string name="error_unable_to_load_license">Не мога да заредя лицензите</string>
<string name="error_unable_to_load_license">Неуспешно зареждане на лиценза</string>
<string name="action_open_website">Отвори уебсайта</string>
<string name="tab_about">Относно приложението</string>
<string name="tab_contributors">Допринесли</string>
<string name="tab_licenses">Лицензии</string>
<string name="app_description">Безплатен и лек YouTube фронтенд за Android.</string>
<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="show_info">Покажи инфо</string>
<string name="tab_main">Главен</string>
<string name="tab_bookmarks">Отметки</string>
<string name="controls_add_to_playlist_title">Добави към</string>
<string name="use_inexact_seek_title">Използвай бързо, но неточно превъртане</string>
<string name="use_inexact_seek_summary">Неточното превъртане позволява на плейъра да превърта кадри по-бързо, с намалена прецизност</string>
<string name="download_thumbnail_title">Зареждай миниатюри</string>
<string name="thumbnail_cache_wipe_complete_notice">Кеш-паметта с изображения е изтрита</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="default_content_country_title">Държава, за която да бъде показвано съдържание</string>
<string name="service_title">Услуга</string>
<string name="settings_category_debug_title">Отстраняване на грешки</string>
<string name="always">Винаги</string>
<string name="just_once">Само веднъж</string>
<string name="file">Файл</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_title">Изтрий историята с изгледани</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="search_history_deleted">Историята на търсенията е изтрита.</string>
<string name="youtube_signature_decryption_error">URL подписът на клипа не можа да бъде дешифрован</string>
<string name="external_player_unsupported_link_type">Външните плейъри не поддържат този вид линкове</string>
<string name="invalid_url_toast">Невалиден URL</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="short_thousand">хил.</string>
<string name="short_million">млн.</string>
<string name="short_billion">млрд.</string>
<string name="no_subscribers">Няма абонати</string>
<string name="create">Създай</string>
<string name="delete_all">Изтрий всички</string>
<string name="dismiss">Откажи</string>
<string name="toast_no_player">Няма инсталирано приложение, което да изпълни този файл</string>
<string name="copyright" formatted="true">© %1$s от %2$s под лиценза %3$s</string>
<string name="contribution_title">Съдействайте</string>
<string name="contribution_encouragement">За всичко, което се сетите: превод, промени по дизайна, изчистване на кода или много сериозни промени по кода помощта е винаги добре дошла. Колкото повече развитие, толкова по-добре!</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 се отнася много сериозно към вашата поверителност. За това, приложението не събира никакви данни без вашето съгласие.
\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="show_hold_to_append_summary">Показвай подсказка, когато е избран фонов режим или режим в прозорец на страницата с детайли на съответния клип</string>
<string name="clear_views_history_summary">Изтрива историята на възпроизвежданите стриймове.</string>
<string name="video_streams_empty">Не са намерени видео стриймове</string>
<string name="audio_streams_empty">Не са намерени аудио стриймове</string>
<string name="info_labels">"Какво:\\nЗаявка:\\nЕзик на съдържанието:\\nУслуга:\\nВреме по GMT:\\nПакет:\\nВерсия:\\nОС версия: "</string>
<string name="list_thumbnail_view_description">Миниатюра на видео</string>
<string name="user_report">Потребителски доклад</string>
<string name="detail_drag_description">Пренареди чрез плъзгане</string>
<string name="start">Начало</string>
<string name="delete_one">Изтрий един</string>
<string name="rename">Преименувай</string>
<string name="add">Нова цел</string>
<string name="no_available_dir">Моля, изберете достъпна папка за изтегляния</string>
<string name="msg_popup_permission">Това разрешение се изисква за
\nвъзпроизвеждане в отделен прозорец</string>
<string name="one_item_deleted">1 елемент е изтрит.</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="charset_most_special_characters">Повечето специални символи</string>
<string name="app_license">NewPipe е безплатен „copyleft“ софтуер: Можете да го използвате, изучавате, споделяте и подобрявате по ваше усмотрение. В частност, Вие можете да препубликувате и/или модифицирате приложението според правилата на Главния обществен лиценз на ГНУ, издаден от Фондацията за свободен софтуер версия 3 на лиценза или по-нова.</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">Страница с акценти от вашите абонаменти</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">Невалиден ZIP файл</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="unbookmark_playlist">Премахни отметката</string>
<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="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="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">Импортирайте SoundCloud профил чрез въвеждане на хипервръзката към него или чрез вашия ID:
\n
\n1. Включете „десктоп режим“ в браузър (сайтът е недостъпен за мобилни устройства)
\n2. Посетете връзката: %1$s
\n3. Влезте в профила си, ако се изисква
\n4. Копирайте хипервръзката на профилната страница, към която сте насочени.</string>
<string name="import_soundcloud_instructions_hint">вашиятID, soundcloud.com/вашиятID</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="skip_silence_checkbox">Бързо превъртане при тишина</string>
<string name="start_accept_privacy_policy">От съображения към Общия европейски регламент относно защитата на данните, Ви привличаме вниманието към политиката за поверителност на NewPipe. Моля, прочетете я внимателно.
\nТрябва да сте съгласни с условията, за да ни изпратите доклада за грешката.</string>
<string name="accept">Приеми</string>
<string name="decline">Откажи</string>
<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_none_description">Без минимизиране</string>
<string name="minimize_on_exit_background_description">Минимизирай във фонов режим</string>
<string name="minimize_on_exit_popup_description">Минимизирай в прозорец</string>
<string name="channels">Канали</string>
<string name="playlists">Плейлисти</string>
<string name="tracks">Песни</string>
<string name="users">Потребители</string>
<string name="playback_reset">Възстанови</string>
</resources>

View File

@ -61,7 +61,7 @@
<string name="import_data_title">Importa una base de dades</string>
<string name="export_data_title">Exporta una base de dades</string>
<string name="export_data_summary">Exporta l\'historial, les subscripcions i les llistes de reproducció.</string>
<string name="export_data_summary">Exporta l\'historial, les subscripcions i les llistes de reproducció</string>
<string name="general_error">Error</string>
<string name="network_error">Error de xarxa</string>
<string name="video">Vídeo</string>
@ -226,7 +226,7 @@
<string name="detail_likes_img_view_description">M\'agrada</string>
<string name="detail_dislikes_img_view_description">No m\'agrada</string>
<string name="use_tor_title">Fes servir el Tor</string>
<string name="use_tor_summary">(En proves) Força el trànsit de baixada a través del Tot per a més privadesa (no compatible encara amb les emissions de vídeo).</string>
<string name="use_tor_summary">(En proves) Força el trànsit de baixada a través del Tor per a més privadesa (no compatible encara amb les emissions de vídeo).</string>
<string name="report_error">Informa sobre un error</string>
<string name="user_report">Informe de l\'usuari</string>
<string name="search_no_results">Cap resultat</string>
@ -310,7 +310,7 @@
<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_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>
@ -390,7 +390,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>
@ -425,17 +425,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="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="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="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="search_history_deleted">S\'ha esborrat l\'historial de cerca.</string>
<string name="one_item_deleted">S\'ha esborrat 1 element.</string>
@ -452,4 +452,18 @@
<string name="decline">Rebutja</string>
<string name="limit_data_usage_none_description">Sense restriccions</string>
<string name="limit_mobile_data_usage_title">Restringeix la resolució quan es facin servir dades mòbils</string>
<string name="minimize_on_exit_title">Minimitza en canviar d\'aplicació</string>
<string name="minimize_on_exit_summary">Acció en canviar a una altra aplicació des del reproductor de vídeo principal — %s</string>
<string name="minimize_on_exit_none_description">Cap</string>
<string name="minimize_on_exit_background_description">Minimitza al reproductor en segon pla</string>
<string name="minimize_on_exit_popup_description">Minimitza al reproductor emergent</string>
<string name="skip_silence_checkbox">Avança ràpid durant el silenci</string>
<string name="playback_step">Pas</string>
<string name="playback_reset">Reinicialitza</string>
<string name="channels">Canals</string>
<string name="playlists">Llistes de reproducció</string>
<string name="tracks">Pistes</string>
<string name="users">Usuaris</string>
</resources>

View File

@ -1,2 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
<?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="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="search">搜索</string>
<string name="settings">设置</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_audio_player_title">使用外部音频播放器</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>
<string name="subscription_change_failed">无法更改订阅</string>
<string name="subscription_update_failed">无法更新订阅</string>
<string name="show_info">显示详情</string>
<string name="tab_main">主页</string>
<string name="tab_subscriptions">订阅</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_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_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="show_higher_resolutions_title">显示更高的分辨率</string>
<string name="show_higher_resolutions_summary">只有部分设备支持播放 2K/4K 视频</string>
<string name="play_with_kodi_title">用 Kodi 播放</string>
<string name="kore_not_found">没找到 Kore 应用,需要安装它吗?</string>
<string name="show_play_with_kodi_title">显示“用 Kodi 播放”选项</string>
<string name="show_play_with_kodi_summary">显示用 Kodi媒体中心 播放视频的选项</string>
<string name="play_audio">音频</string>
<string name="default_audio_format_title">默认音频格式</string>
<string name="default_video_format_title">默认视频格式</string>
<string name="theme_title">主题</string>
<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="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="clear_views_history_title">清楚观看历史</string>
<string name="search_history_deleted">已删除搜索历史</string>
<string name="general_error">错误</string>
<string name="network_error">网络错误</string>
<string name="report_error">举报错误</string>
<string name="search_no_results">没有结果</string>
<string name="start">开始</string>
<string name="pause">暂停</string>
<string name="view">播放</string>
<string name="create">创建</string>
<string name="delete">删除</string>
<string name="delete_all">删除所有</string>
<string name="add">新任务</string>
<string name="finish">
\n</string>
<string name="msg_error">错误
\n</string>
<string name="msg_server_unsupported">服务器无法支持</string>
<string name="msg_exists">文件已存在</string>
<string name="msg_running">NewPipe 下载中</string>
<string name="msg_wait">请稍等…</string>
<string name="charset_letters_and_digits">字母与数字</string>
<string name="charset_most_special_characters">最特别的字符</string>
<string name="toast_no_player">这个文件里没有已下载应用程式</string>
<string name="title_activity_about">关于NewPipe</string>
<string name="action_settings">设置</string>
<string name="action_about">关于</string>
<string name="title_licenses">第三方执照</string>
<string name="action_open_website">打开网页</string>
<string name="unbookmark_playlist">删除书签</string>
<string name="delete_playlist_prompt">请问你要删除这个列表吗?</string>
<string name="playlist_creation_success">已创建播放列表</string>
<string name="playlist_add_stream_success">已加入播放列表</string>
<string name="playback_step">步骤</string>
<string name="playback_reset">重置</string>
<string name="start_accept_privacy_policy">为了遵守欧洲通用数据保护法规GDPR,我们请你注意NewPipe的隐私政策.请仔细阅读.
\n你必须接受它才能将错误报告发送给我们.</string>
<string name="accept">接受</string>
<string name="decline">拒绝</string>
<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_none_description">没有</string>
<string name="minimize_on_exit_background_description">最小化背景播放器</string>
</resources>

View File

@ -96,7 +96,7 @@
<string name="duration_live">Live</string>
<string name="main_bg_subtitle">„Suchen“ antippen um zu beginnen</string>
<string name="main_bg_subtitle">„Suchen“ antippen, um zu beginnen</string>
<string name="downloads">Downloads</string>
<string name="downloads_title">Downloads</string>
<string name="error_report_title">Fehlerbericht</string>
@ -286,7 +286,7 @@
<string name="show_hold_to_append_summary">Tipp anzeigen, wenn der Hintergrundwiedergabe- oder Pop-up-Button auf der Videodetailseite gedrückt gehalten wird</string>
<string name="background_player_append">In der Warteschlange der Hintergrundwiedergabe</string>
<string name="new_and_hot">Neu &amp; Heiß</string>
<string name="hold_to_append">Zum Anfügen gedrückt halten</string>
<string name="hold_to_append">Halten, um zur Wiedergabeliste hinzuzufügen</string>
<string name="show_hold_to_append_title">\"Gedrückt halten, um Tipp hinzuzufügen\" anzeigen</string>
<string name="unknown_content">[Unbekannt]</string>
@ -298,8 +298,8 @@
<string name="donation_title">Spenden</string>
<string name="give_back">Zurückgeben</string>
<string name="website_title">Website</string>
<string name="website_encouragement">Besuche die NewPipe Website für weitere Informationen und Neuigkeiten.</string>
<string name="donation_encouragement">NewPipe wird von Freiwilligen entwickelt, die ihre Freizeit dafür aufbringen, die beste Nutzererfahrung zu bieten. Gib etwas zurück, um Entwicklern zu helfen, NewPipe bei einer Tasse Kaffee noch besser zu machen.</string>
<string name="website_encouragement">Besuche die NewPipe-Website für weitere Informationen und Neuigkeiten.</string>
<string name="donation_encouragement">NewPipe wird von Freiwilligen entwickelt, die ihre Freizeit dafür verwenden, dir die beste Nutzererfahrung zu bieten. Gib etwas zurück, um den Entwicklern zu helfen, NewPipe noch besser machen zu können, während sie sich an einer Tasse Kaffee erfreuen.</string>
<string name="service_title">Service</string>
<string name="no_player_found_toast">Keinen Streamplayer gefunden (du kannst VLC installieren, um den Stream abzuspielen)</string>
<string name="default_content_country_title">Standard-Land des Inhalts</string>
@ -325,11 +325,11 @@
<string name="always_ask_player">Immer fragen</string>
<string name="preferred_player_fetcher_notification_title">Informationen werden abgerufen…</string>
<string name="preferred_player_fetcher_notification_message">Die angeforderten Inhalte werden geladen</string>
<string name="preferred_player_fetcher_notification_message">Gewünschten Inhalt laden</string>
<string name="import_data_title">Datenbank importieren</string>
<string name="export_data_title">Datenbank exportieren</string>
<string name="import_data_summary">Wird deinen Verlauf und deine Abos überschreiben</string>
<string name="export_data_summary">Verlauf, Abos und Wiedergabelisten exportieren.</string>
<string name="export_data_summary">Verlauf, Abos und Wiedergabelisten exportieren</string>
<string name="no_valid_zip_file">Keine gültige ZIP-Datei</string>
<string name="could_not_import_all_files">Warnung: Nicht alle Dateien konnten importiert werden.</string>
<string name="override_current_data">Dies wird deine aktuellen Einstellungen überschreiben.</string>
@ -358,7 +358,7 @@
<string name="delete_playlist">Wiedergabeliste löschen</string>
<string name="rename_playlist">Wiedergabeliste umbenennen</string>
<string name="append_playlist">Zur Wiedergabeliste hinzufügen</string>
<string name="set_as_playlist_thumbnail">Als Vorschaubild der Wiedergabeliste festlegen</string>
<string name="set_as_playlist_thumbnail">Als Symbolbild der Wiedergabeliste festlegen</string>
<string name="unbookmark_playlist">Lesezeichen entfernen</string>
@ -407,8 +407,8 @@
<string name="metadata_cache_wipe_complete_notice">Metadatencache gelöscht</string>
<string name="settings_category_debug_title">Debug</string>
<string name="invalid_source">Ungültige Datei-/Inhaltsquelle</string>
<string name="export_complete_toast">Export fertiggestellt</string>
<string name="import_complete_toast">Import fertiggestellt</string>
<string name="export_complete_toast">Export abgeschlossen</string>
<string name="import_complete_toast">Import abgeschlossen</string>
<string name="playlist_name_input">Name</string>
<string name="import_export_title">Import/Export</string>
<string name="import_title">Import</string>
@ -418,12 +418,12 @@
<string name="playback_speed_control">Wiedergabegeschwindigkeitsregler</string>
<string name="playback_tempo">Geschwindigkeit</string>
<string name="playback_pitch">Tonhöhe</string>
<string name="unhook_checkbox">Aushaken (kann zu Verzerrungen führen)</string>
<string name="unhook_checkbox">Verknüpfung aufheben (kann zu Verzerrungen führen)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Standard</string>
<string name="download_thumbnail_summary">Deaktiviere diese Option, um das Laden aller Miniaturansichten zu stoppen und Daten und Speicherverbrauch zu sparen. Wenn du dies änderst, wird sowohl der In-Memory- als auch der On-Disk-Image-Cache gelöscht.</string>
<string name="download_thumbnail_summary">Deaktiviere diese Option, um das Laden aller Miniaturansichten zu stoppen und Daten und Speicherverbrauch zu sparen. Wenn du dies änderst, wird sowohl der In-Memory- als auch der On-Disk-Image-Cache gelöscht</string>
<string name="auto_queue_title">Nächsten Stream automatisch einreihen</string>
<string name="auto_queue_summary">Automatisches Anhängen eines verwandten Streams beim Abspielen des letzten Streams in einer nicht wiederholten Warteschlange.</string>
<string name="auto_queue_summary">Automatisches Anhängen eines verwandten Streams beim Abspielen des letzten Streams in einer nicht wiederholten Warteschlange</string>
<string name="drawer_header_action_paceholder_text">Hier wird bald etwas stehen ;D</string>
@ -456,17 +456,17 @@
<string name="preferred_open_action_settings_summary">Standardaktion beim Öffnen von Inhalten - %s</string>
<string name="caption_setting_title">Untertitel</string>
<string name="caption_setting_description">Ändere Textgröße und Hintergrundstil des Untertitels im Player. Wird erst nach Neustart des App wirksam.</string>
<string name="caption_setting_description">Ändere Textgröße und Hintergrundstil des Untertitels im Player. Wird erst nach Neustart des App wirksam</string>
<string name="toast_no_player">Keine App zum Abspielen dieser Datei installiert</string>
<string name="clear_views_history_title">Verlauf leeren</string>
<string name="clear_views_history_summary">Löscht den Verlauf der abgespielten Streams.</string>
<string name="delete_view_history_alert">Löscht den ganzen Verlauf.</string>
<string name="clear_views_history_summary">Löscht den Verlauf der abgespielten Streams</string>
<string name="delete_view_history_alert">Löscht den ganzen Verlauf</string>
<string name="view_history_deleted">Verlauf gelöscht.</string>
<string name="clear_search_history_title">Suchverlauf löschen</string>
<string name="clear_search_history_summary">Lösche Verlauf der Suchschlüsselwörter.</string>
<string name="delete_search_history_alert">Lösche gesamten Suchverlauf.</string>
<string name="clear_search_history_summary">Lösche Verlauf der Suchschlüsselwörter</string>
<string name="delete_search_history_alert">Lösche gesamten Suchverlauf</string>
<string name="search_history_deleted">Suchverlauf gelöscht.</string>
<string name="one_item_deleted">1 Element gelöscht.</string>
@ -479,8 +479,22 @@
<string name="read_privacy_policy">Lies die Datenschutzbestimmungen</string>
<string name="accept">Akzeptieren</string>
<string name="decline">Ablehnen</string>
<string name="start_accept_privacy_policy">Um den Anforderungen der Datenschutz-Grundverordnung (DSGVO) genüge zu tun, möchten wir dich hiermit auf die Datenschutzbedingungen von NewPipe aufmerksam machen. Bitte lies sie sorgfältig durch.
\nDu musst ihr zustimmen, um einen Bugreport an uns zu senden.</string>
<string name="start_accept_privacy_policy">Um der europäischen Datenschutz-Grundverordnung (DSGVO) gerecht zu werden, weisen wir hiermit auf NewPipe\'s Datenschutzerklärung hin. Bitte lies sie sorgfältig durch.
\nDu musst den Datenschutzrichtlinien zustimmen, um den Fehlerbericht an uns zu senden.</string>
<string name="limit_data_usage_none_description">Unbegrenzt</string>
<string name="limit_mobile_data_usage_title">Auflösung bei Verwendung mobiler Daten begrenzen</string>
<string name="minimize_on_exit_title">Minimieren beim Anwendungswechsel</string>
<string name="minimize_on_exit_summary">Aktion beim Umschalten auf eine andere Anwendung vom Haupt-Videoplayer - %s</string>
<string name="minimize_on_exit_none_description">Keine</string>
<string name="minimize_on_exit_background_description">Zum Hintergrund-Player minimieren</string>
<string name="minimize_on_exit_popup_description">Zum Popup-Player minimieren</string>
<string name="skip_silence_checkbox">Vorspulen während der Stille</string>
<string name="playback_step">Schritt</string>
<string name="playback_reset">Zurücksetzen</string>
<string name="channels">Kanäle</string>
<string name="playlists">Wiedergabelisten</string>
<string name="tracks">Titel</string>
<string name="users">Nutzer</string>
</resources>

View File

@ -62,4 +62,43 @@
<string name="info_dir_created">Δημιουργήθηκε ο φάκελος \'%1$s\'</string>
<string name="short_billion">Δ</string>
<string name="open_in_popup_mode">Άνοιγμα σε αναδυόμενο παράθυρο</string>
<string name="subscribe_button_title">Εγγραφή</string>
<string name="subscribed_button_title">Εγγεγραμμένος</string>
<string name="show_higher_resolutions_summary">Μόνο μερικές συσκευές υποστηρίζουν την αναπαραγωγή 2K/4K βίντεο</string>
<string name="black_theme_title">Μαύρο</string>
<string name="show_search_suggestions_title">Προτάσεις αναζήτησης</string>
<string name="enable_search_history_title">Ιστορικό αναζήτησης</string>
<string name="downloads">Λήψεις</string>
<string name="downloads_title">Λήψεις</string>
<string name="all">Όλα</string>
<string name="channel">Κανάλι</string>
<string name="yes">Ναι</string>
<string name="later">Αργότερα</string>
<string name="filter">Φίλτρο</string>
<string name="general_error">Σφάλμα</string>
<string name="error_snackbar_action">ΑΝΑΦΟΡΑ</string>
<string name="what_device_headline">Πληροφορίες:</string>
<string name="what_happened_headline">Τι συνέβη:</string>
<string name="your_comment">Το σχόλιό σας (στα Αγγλικά):</string>
<string name="error_details_headline">Λεπτομέρειες:</string>
<string name="report_error">Αναφορά Σφάλματος</string>
<string name="video">Βίντεο</string>
<string name="audio">Ήχος</string>
<string name="pause">Παύση</string>
<string name="delete">Διαγραφή</string>
<string name="msg_error">Σφάλμα</string>
<string name="msg_exists">Το αρχείο υπάρχει ήδη</string>
<string name="msg_running_detail">Πατήστε για λεπτομέρειες</string>
<string name="settings_category_downloads_title">Λήψη</string>
<string name="charset_letters_and_digits">Γράμματα και ψηφία</string>
<string name="action_settings">Ρυθμίσεις</string>
<string name="action_open_website">Άνοιγμα ιστοσελίδας</string>
<string name="tab_licenses">Άδειες</string>
<string name="title_activity_history">Ιστορικό</string>
<string name="action_history">Ιστορικό</string>
<string name="show_info">Εμφάνιση πληροφοριών</string>
</resources>

View File

@ -330,7 +330,7 @@ abrir en modo popup</string>
<string name="import_data_title">Importar base de datos</string>
<string name="export_data_title">Exportar base de datos</string>
<string name="import_data_summary">Reemplazará su historial actual y sus suscripciones</string>
<string name="export_data_summary">Exportar historial, suscripciones y listas de reproducción.</string>
<string name="export_data_summary">Exportar historial, suscripciones y listas de reproducción</string>
<string name="export_complete_toast">Exportación completa</string>
<string name="import_complete_toast">Importación completa</string>
<string name="no_valid_zip_file">Archivo ZIP no válido</string>
@ -403,7 +403,7 @@ abrir en modo popup</string>
<string name="use_inexact_seek_title">Usar búsqueda rápida inexacta</string>
<string name="use_inexact_seek_summary">La búsqueda inexacta permite al reproductor buscar posiciones más rápido con menor precisión</string>
<string name="auto_queue_title">Auto-encolar la siguiente transmisión</string>
<string name="auto_queue_summary">Auto-añadir un vídeo relacionado al reproducir el último vídeo en una cola no repetitiva.</string>
<string name="auto_queue_summary">Auto-añadir un vídeo relacionado al reproducir el último vídeo en una cola no repetitiva</string>
<string name="live">DIRECTO</string>
<string name="live_sync">SINCRONIZAR</string>
<string name="file">Archivo</string>
@ -445,7 +445,7 @@ abrir en modo popup</string>
\n
\n¿Desea continuar?</string>
<string name="download_thumbnail_title">Cargar Miniaturas</string>
<string name="download_thumbnail_summary">Descativar todas las miniaturas para evitar que se carguen, guarden datos y usen memoria. Al cambiar esto se borrarán tanto la caché de imágenes en la memoria como en el disco.</string>
<string name="download_thumbnail_summary">Descativar todas las miniaturas para evitar que se carguen, guarden datos y usen memoria. Al cambiar esto se borrarán tanto la caché de imágenes en la memoria como en el disco</string>
<string name="thumbnail_cache_wipe_complete_notice">Caché de imagen limpiado</string>
<string name="metadata_cache_wipe_title">Metadatos eliminados del caché</string>
<string name="metadata_cache_wipe_summary">Eliminar todos los datos de la página web en caché</string>
@ -453,7 +453,7 @@ abrir en modo popup</string>
<string name="playback_speed_control">Controles de velocidad de reproducción</string>
<string name="playback_tempo">Tiempo</string>
<string name="playback_pitch">Tono</string>
<string name="unhook_checkbox">Desenganchar (puede casusar distorsión)</string>
<string name="unhook_checkbox">Desenganchar (puede causar distorsión)</string>
<string name="playback_nightcore">Nightcore (tipo de música)</string>
<string name="playback_default">Reproducción por defecto</string>
<string name="no_streams_available_download">No hay streams disponibles para descargar</string>
@ -464,15 +464,15 @@ abrir en modo popup</string>
<string name="toast_no_player">No hay ninguna app instalada para reproducir este archivo</string>
<string name="caption_setting_title">Subtítulos</string>
<string name="caption_setting_description">Modificar la escala de texto de los subtítulos y los estilos de fondo del reproductor. Requiere el reinicio de la app para que surta efecto.</string>
<string name="caption_setting_description">Modificar la escala de texto de los subtítulos y los estilos de fondo del reproductor. Requiere el reinicio de la app para que surta efecto</string>
<string name="clear_views_history_title">Borrar historial de reproducciones</string>
<string name="clear_views_history_summary">Elimina el historial de las transmisiones reproducidas.</string>
<string name="delete_view_history_alert">Elimina todo el historial de reproducciones.</string>
<string name="clear_views_history_summary">Elimina el historial de las transmisiones reproducidas</string>
<string name="delete_view_history_alert">Elimina todo el historial de reproducciones</string>
<string name="view_history_deleted">Historial de reproducciones eliminado.</string>
<string name="clear_search_history_title">Borrar historial de búsqueda</string>
<string name="clear_search_history_summary">Elimina el historial de palabras clave de búsqueda.</string>
<string name="delete_search_history_alert">Elimina todo el historial de búsqueda.</string>
<string name="clear_search_history_summary">Elimina el historial de palabras clave de búsqueda</string>
<string name="delete_search_history_alert">Elimina todo el historial de búsqueda</string>
<string name="search_history_deleted">Historial de búsquedas eliminado.</string>
<string name="one_item_deleted">1 elemento eliminado.</string>
@ -488,4 +488,18 @@ abrir en modo popup</string>
<string name="limit_data_usage_none_description">Sin límite</string>
<string name="limit_mobile_data_usage_title">Limitar resolución cuando se use Datos Móviles</string>
<string name="minimize_on_exit_title">Mimimizar al cambiar de aplicación</string>
<string name="minimize_on_exit_summary">Acción de cambiar a otra aplicación desde el reproductor principal</string>
<string name="minimize_on_exit_none_description">Ninguna</string>
<string name="minimize_on_exit_background_description">Minimizar al reproductor de fondo</string>
<string name="minimize_on_exit_popup_description">Minimizar el reproductor emergente</string>
<string name="skip_silence_checkbox">Avance rápido durante el silencio</string>
<string name="playback_step">Paso</string>
<string name="playback_reset">Reiniciar</string>
<string name="channels">Canales</string>
<string name="users">Usuarios</string>
<string name="playlists">Listas de reproducción</string>
<string name="tracks">Pistas</string>
</resources>

View File

@ -0,0 +1,421 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">Alustuseks puuduta otsingut</string>
<string name="view_count_text">%1$s vaatamist</string>
<string name="upload_date_text">Avaldatud %1$s</string>
<string name="no_player_found">Voogesituseks puudub pleier. Kas paigaldada VLC?</string>
<string name="no_player_found_toast">Voogesituseks puudub pleier. (Selleks võib paigaldada VLC)</string>
<string name="install">Paigalda</string>
<string name="cancel">Tühista</string>
<string name="open_in_browser">Ava veebilehitsejas</string>
<string name="open_in_popup_mode">Ava hüpikaknas</string>
<string name="share">Jaga</string>
<string name="download">Laadi alla</string>
<string name="controls_download_desc">Laadi voog alla.</string>
<string name="search">Otsi</string>
<string name="settings">Seaded</string>
<string name="did_you_mean">Kas mõtlesid: %1$s ?</string>
<string name="share_dialog_title">Jaga</string>
<string name="choose_browser">Vali veebilehitseja</string>
<string name="screen_rotation">pööramine</string>
<string name="use_external_video_player_title">Kasuta välist videopleierit</string>
<string name="use_external_video_player_summary">Kui see valik lubada, puudub osadel lahutustel heli</string>
<string name="use_external_audio_player_title">Kasuta välist audiopleierit</string>
<string name="popup_mode_share_menu_title">NewPipe hüpikaknarežiim</string>
<string name="subscribe_button_title">Telli</string>
<string name="subscribed_button_title">Tellitud</string>
<string name="channel_unsubscribed">Kanali tellimus tühistatud</string>
<string name="subscription_change_failed">Tellimuse muutmine nurjus</string>
<string name="subscription_update_failed">Tellimuse uuendamine nurjus</string>
<string name="show_info">Kuva info</string>
<string name="tab_subscriptions">Tellimused</string>
<string name="tab_bookmarks">Järjehoidjad</string>
<string name="fragment_whats_new">Mis on uut</string>
<string name="controls_background_title">Taust</string>
<string name="controls_popup_title">Hüpikaken</string>
<string name="controls_add_to_playlist_title">"Lisa "</string>
<string name="download_path_title">Video allalaadimise kaust</string>
<string name="download_path_summary">Kaust allalaetud videote hoiustamiseks</string>
<string name="download_path_dialog_title">Sisesta videote allalaadimise rada</string>
<string name="download_path_audio_title">Audio allalaadimise kaust</string>
<string name="download_path_audio_summary">Kaust allalaetud audio hoiustamiseks</string>
<string name="download_path_audio_dialog_title">Sisesta audio allalaadimise rada</string>
<string name="autoplay_by_calling_app_title">Automaatesitus</string>
<string name="autoplay_by_calling_app_summary">Esita video, kui NewPipe käivitub teise rakenduse kaudu</string>
<string name="default_resolution_title">Vaikelahutus</string>
<string name="default_popup_resolution_title">Hüpikakna vaikelahutus</string>
<string name="show_higher_resolutions_title">Kuva kõrgemaid lahutusi</string>
<string name="show_higher_resolutions_summary">Kõik seadmed ei toeta 2K/4K videoid</string>
<string name="play_with_kodi_title">Esita Kodi abil</string>
<string name="kore_not_found">Kore rakendust ei leitud. Kas paigaldada see?</string>
<string name="show_play_with_kodi_title">Kuva valik \"Esita Kodi abil\"</string>
<string name="show_play_with_kodi_summary">Kuva valik video esitamiseks Kodi meediakeskuse kaudu</string>
<string name="play_audio">Heli</string>
<string name="default_audio_format_title">Heli vaikevorming</string>
<string name="default_video_format_title">Video vaikevorming</string>
<string name="webm_description">WebM — libre vorming</string>
<string name="m4a_description">M4A — parem kvaliteet</string>
<string name="theme_title">Teema</string>
<string name="light_theme_title">Hele</string>
<string name="dark_theme_title">Tume</string>
<string name="black_theme_title">Must</string>
<string name="popup_remember_size_pos_title">Pea hüpikakna suurus ja asukoht meeles</string>
<string name="popup_remember_size_pos_summary">Pea hüpikakna viimane suurus ja asukoht meeles</string>
<string name="use_inexact_seek_title">Kasuta ebatäpset kerimist</string>
<string name="use_inexact_seek_summary">Ebatäpne kerimine lubab pleieril otsida asukohta kiiremini täpsuse arvel</string>
<string name="download_thumbnail_title">Laadi pisipildid</string>
<string name="download_thumbnail_summary">Keela, peatamaks pisipiltide laadimine ja vähenda andme- ja mälukasutust. Selle muutmine puhastab nii sisemälu kui piltide vahemälu andmekandjal</string>
<string name="thumbnail_cache_wipe_complete_notice">Pildid kustutati vahemälust</string>
<string name="metadata_cache_wipe_title">Kustuta metaandmed vahemälust</string>
<string name="metadata_cache_wipe_summary">Kustuta veebilehtede andmed vahemälust</string>
<string name="metadata_cache_wipe_complete_notice">Metaandmed kustutati vahemälust</string>
<string name="auto_queue_title">Järgmine voog automaatselt järjekorda</string>
<string name="auto_queue_summary">Lisa seotud voog automaatselt, kui esitusel on viimane voog mittekorduvast järjekorrast</string>
<string name="player_gesture_controls_title">Pleieri juhtimise viiped</string>
<string name="player_gesture_controls_summary">Luba viiped helitugevuse ja ereduse juhtimiseks</string>
<string name="show_search_suggestions_title">Kuva soovitused</string>
<string name="show_search_suggestions_summary">Kuva otsingu ajal soovitusi</string>
<string name="enable_search_history_title">Otsinguajalugu</string>
<string name="enable_search_history_summary">Salvesta otsinguajalugu kohalikult</string>
<string name="enable_watch_history_title">Ajalugu ja vahemälu</string>
<string name="enable_watch_history_summary">Jälgi videote vaatamist</string>
<string name="resume_on_audio_focus_gain_title">Jätka taasesitust fookuse saamisel</string>
<string name="resume_on_audio_focus_gain_summary">Jätka taasesitust pärast katkestamist (nt. telefonikõne)</string>
<string name="download_dialog_title">Laadi alla</string>
<string name="next_video_title">Järgmine video</string>
<string name="show_next_and_similar_title">Kuva \'järgmine\' ja \'sarnased\' videod</string>
<string name="show_hold_to_append_title">Kuva vihjet \"lisamiseks hoia\"</string>
<string name="show_hold_to_append_summary">Kuva vihje, kui videoandmete lehel vajutatakse tausta või hüpikakna nupule</string>
<string name="url_not_supported_toast">URL pole toetatud</string>
<string name="default_content_country_title">Sisu vaikimisi riik</string>
<string name="service_title">Teenus</string>
<string name="search_language_title">Sisu vaikimisi keel</string>
<string name="settings_category_player_title">Mängija</string>
<string name="settings_category_player_behavior_title">Käitumine</string>
<string name="settings_category_video_audio_title">Heli ja pilt</string>
<string name="settings_category_history_title">Ajalugu ja vahemälu</string>
<string name="settings_category_popup_title">Hüpikaken</string>
<string name="settings_category_appearance_title">Välimus</string>
<string name="settings_category_other_title">Muu</string>
<string name="settings_category_debug_title">Silumine</string>
<string name="background_player_playing_toast">Taasesitus taustal</string>
<string name="popup_playing_toast">Taasesitus hüpikaknas</string>
<string name="background_player_append">Lisati taustapleieri järjekorda</string>
<string name="popup_playing_append">Lisati hüpikpleieri järjekorda</string>
<string name="play_btn_text">Esita</string>
<string name="content">Sisu</string>
<string name="show_age_restricted_content_title">Kuva vanusepiiranguga sisu</string>
<string name="video_is_age_restricted">Vanusepiiranguga sisu. Selle saab lubada seadetes.</string>
<string name="duration_live">Otse</string>
<string name="downloads">Allalaadimised</string>
<string name="downloads_title">Allalaadimised</string>
<string name="error_report_title">Vea teatamine</string>
<string name="all">Kõik</string>
<string name="channel">Kanal</string>
<string name="playlist">Pleilist</string>
<string name="yes">Jah</string>
<string name="later">Hiljem</string>
<string name="disabled">Keelatud</string>
<string name="filter">Filter</string>
<string name="refresh">Värskenda</string>
<string name="clear">Kustuta</string>
<string name="popup_resizing_indicator_title">Suuruse muutmine</string>
<string name="best_resolution">Parim lahutus</string>
<string name="undo">Võta tagasi</string>
<string name="play_all">Esita kõik</string>
<string name="always">Alati</string>
<string name="just_once">Üks kord</string>
<string name="file">Fail</string>
<string name="notification_channel_name">NewPipe teavitus</string>
<string name="notification_channel_description">Teavitused NewPipe tausta- ja hüpikpleierile</string>
<string name="unknown_content">[Tundmatu]</string>
<string name="toggle_orientation">Vaheta suunda</string>
<string name="switch_to_background">Lülita taustale</string>
<string name="switch_to_popup">Lülita hüpikpleierile</string>
<string name="import_data_title">Impordi andmebaas</string>
<string name="export_data_title">Ekspordi andmebaas</string>
<string name="import_data_summary">Alistab praeguse ajaloo ja tellimused</string>
<string name="export_data_summary">Ekspordi ajalugu, tellimused ja pleilistid</string>
<string name="clear_views_history_title">Puhasta vaatamiste ajalugu</string>
<string name="clear_views_history_summary">Kustutab vaadatud voogude ajaloo</string>
<string name="delete_view_history_alert">Kustuta kogu vaatamiste ajalugu</string>
<string name="view_history_deleted">Vaatamiste ajalugu kustutati.</string>
<string name="clear_search_history_title">Kustuta otsinguajalugu</string>
<string name="clear_search_history_summary">Kustutab otsisõnade ajaloo</string>
<string name="delete_search_history_alert">Kustuta kogu otsinguajalugu</string>
<string name="search_history_deleted">Otsinguajalugu kustutati.</string>
<string name="general_error">Viga</string>
<string name="network_error">Võrgu viga</string>
<string name="could_not_load_thumbnails">Kõiki pisipilte ei õnnestunud laadida</string>
<string name="youtube_signature_decryption_error">Video URLi dekrüptimine nurjus</string>
<string name="parsing_error">Veebilehe töötlemine nurjus</string>
<string name="light_parsing_error">Veebilehe täielik töötlemine nurjus</string>
<string name="content_not_available">Sisu pole saadaval</string>
<string name="blocked_by_gema">GEMA poolt blokeeritud</string>
<string name="could_not_setup_download_menu">Allalaadimismenüü seadistamine nurjus</string>
<string name="live_streams_not_supported">See on OTSEVOOG, mis pole veel toetatud.</string>
<string name="could_not_get_stream">Ühtegi voogu ei leitud</string>
<string name="could_not_load_image">Pildi laadimine nurjus</string>
<string name="app_ui_crash">Rakendus jooksis kokku</string>
<string name="player_stream_failure">Selle voo esitus nurjus</string>
<string name="player_unrecoverable_failure">Ilmnes taastamatu pleieri viga</string>
<string name="player_recoverable_failure">Pleieri veast taastumine</string>
<string name="external_player_unsupported_link_type">Välised pleierid ei toeta seda tüüpi linke</string>
<string name="invalid_url_toast">Kehtetu URL</string>
<string name="video_streams_empty">Videovooge ei leitud</string>
<string name="audio_streams_empty">Helivooge ei leitud</string>
<string name="invalid_directory">Kehtetu kataloog</string>
<string name="invalid_source">Kehtetu faili/sisu allikas</string>
<string name="invalid_file">Fail puudub või puuduvad õigused seda lugeda või kirjutada</string>
<string name="file_name_empty_error">Tühi failinimi pole lubatud</string>
<string name="error_occurred_detail">Ilmnes viga: %1$s</string>
<string name="no_streams_available_download">Allalaaditavaid videovooge pole</string>
<string name="sorry_string">Vabandust, seda poleks pidanud juhtuma.</string>
<string name="error_report_button_text">Teata veast e-posti kaudu</string>
<string name="error_snackbar_message">Vabandust, ilmnesid mõned vead.</string>
<string name="error_snackbar_action">TEATA</string>
<string name="what_device_headline">Info:</string>
<string name="what_happened_headline">Mis juhtus:</string>
<string name="info_labels">Mis:\\nPäring:\\nSisu Keel:\\nTeenus:\\nGMT aeg:\\nPakett:\\nVersioon:\\nOS versioon:</string>
<string name="your_comment">Oma kommentaar (inglise keeles):</string>
<string name="error_details_headline">Üksikasjad:</string>
<string name="list_thumbnail_view_description">Video eelvaate pisipilt</string>
<string name="detail_thumbnail_view_description">Video eelvaate pisipilt</string>
<string name="detail_uploader_thumbnail_view_description">Üleslaadiaja avatari pisipilt</string>
<string name="detail_likes_img_view_description">Meeldib</string>
<string name="detail_dislikes_img_view_description">Ei meeldi</string>
<string name="use_tor_title">Kasuta Tor võrku</string>
<string name="use_tor_summary">(Eksperimentaalne) Kasuta allalaadimiseks Tor võrku, et suurendada privaatsust (voogesitus ei ole veel toetatud).</string>
<string name="report_error">Teata veast</string>
<string name="user_report">Kasutaja raport</string>
<string name="search_no_results">Tulemusi pole</string>
<string name="empty_subscription_feed_subtitle">Siin pole veel midagi</string>
<string name="detail_drag_description">Lohista järjestuse muutmiseks</string>
<string name="err_dir_create">Allalaadimiskataloogi \'%1$s\' loomine nurjus</string>
<string name="info_dir_created">Loodi allalaadimiskataloog \'%1$s\'</string>
<string name="video">Video</string>
<string name="audio">Audio</string>
<string name="retry">Proovi uuesti</string>
<string name="storage_permission_denied">Pääsuõigused salvestile puuduvad</string>
<string name="use_old_player_title">Kasuta vana mängijat</string>
<string name="use_old_player_summary">Vana sisseehitatud mediaframework pleier</string>
<string name="short_thousand">K</string>
<string name="short_million">M</string>
<string name="short_billion">B</string>
<string name="no_subscribers">Tellijaid pole</string>
<plurals name="subscribers">
<item quantity="one">%s tellija</item>
<item quantity="other">%s tellijat</item>
</plurals>
<string name="no_views">Pole vaadatud</string>
<plurals name="views">
<item quantity="one">%s vaatamine</item>
<item quantity="other">%s vaatamist</item>
</plurals>
<string name="no_videos">Videoid pole</string>
<plurals name="videos">
<item quantity="one">%s video</item>
<item quantity="other">%s videot</item>
</plurals>
<string name="start">"Start "</string>
<string name="pause">Paus</string>
<string name="view">Esita</string>
<string name="create">Loo</string>
<string name="delete">Kustuta</string>
<string name="delete_one">Kustuta üks</string>
<string name="delete_all">Kustuta kõik</string>
<string name="checksum">Kontrollsumma</string>
<string name="dismiss">Loobu</string>
<string name="rename">Nimeta ümber</string>
<string name="add">Uus ülesanne</string>
<string name="finish">OK</string>
<string name="msg_name">Faili nimi</string>
<string name="msg_threads">Lõimed</string>
<string name="msg_error">Viga</string>
<string name="msg_server_unsupported">Server pole toetatud</string>
<string name="msg_exists">Fail on juba olemas</string>
<string name="msg_url_malform">Vigane URL või internet pole saadaval</string>
<string name="msg_running">NewPipe allalaadimine</string>
<string name="msg_running_detail">Puuduta üksikasjade nägemiseks</string>
<string name="msg_wait">Palun oota…</string>
<string name="msg_copied">Kopeeriti lõikepuhvrisse</string>
<string name="no_available_dir">Vali saadaolev allalaadimiste kataloog</string>
<string name="msg_popup_permission">Need õigused on vajalikud
\nhüpikakna avamiseks</string>
<string name="one_item_deleted">Kustutati 1 element.</string>
<string name="reCaptchaActivity">"reCAPTCHA "</string>
<string name="settings_category_downloads_title">Laadi alla</string>
<string name="settings_file_charset_title">Lubatud tähemärgid failinimedes</string>
<string name="settings_file_replacement_character_summary">Vigased tähemärgid asendatakse selle väärtusega</string>
<string name="settings_file_replacement_character_title">Asendustähemärk</string>
<string name="charset_letters_and_digits">Tähed ja numbrid</string>
<string name="charset_most_special_characters">Erimärgid</string>
<string name="toast_no_player">Selle faili esitamiseks puudub rakendus</string>
<string name="title_activity_about">NewPipe rakendusest</string>
<string name="action_settings">Seaded</string>
<string name="action_about">Programmist</string>
<string name="title_licenses">Kolmanda osapoole litsentsid</string>
<string name="error_unable_to_load_license">Litsentsi laadimine nurjus</string>
<string name="action_open_website">Ava veebileht</string>
<string name="tab_about">Programmist</string>
<string name="tab_contributors">Kaasautorid</string>
<string name="tab_licenses">Litsentsid</string>
<string name="contribution_title">Panusta</string>
<string name="view_on_github">Vaata GitHubis</string>
<string name="donation_title">Anneta</string>
<string name="website_title">Veebileht</string>
<string name="website_encouragement">Enama info saamiseks külasta NewPipe veebilehte.</string>
<string name="privacy_policy_title">NewPipe privaatsuspoliitika</string>
<string name="read_privacy_policy">Loe privaatsuspoliitikat</string>
<string name="app_license_title">NewPipe litsents</string>
<string name="read_full_license">Loe litsentsi</string>
<string name="title_activity_history">Ajalugu</string>
<string name="title_history_search">Otsitud</string>
<string name="title_history_view">Vaadatud</string>
<string name="history_disabled">Ajalugu on keelatud</string>
<string name="action_history">Ajalugu</string>
<string name="history_empty">Ajalugu on tühi</string>
<string name="history_cleared">Ajalugu kustutati</string>
<string name="item_deleted">Element kustutati</string>
<string name="delete_item_search_history">Kas kustutada see kirje otsinguajaloost?</string>
<string name="delete_stream_history_prompt">Kas kustutada see kirje vaatamiste ajaloost?</string>
<string name="delete_all_history_prompt">Kas kustutada kõik kirjed ajaloost?</string>
<string name="title_last_played">Viimati esitatud</string>
<string name="title_most_played">Enim esitatud</string>
<string name="main_page_content">Avalehe sisu</string>
<string name="blank_page_summary">Tühi leht</string>
<string name="kiosk_page_summary">Kioski leht</string>
<string name="subscription_page_summary">Tellimuse leht</string>
<string name="feed_page_summary">Voo leht</string>
<string name="channel_page_summary">Kanali leht</string>
<string name="select_a_channel">Vali kanal</string>
<string name="no_channel_subscribed_yet">Ühtki kanalit pole veel tellitud</string>
<string name="select_a_kiosk">Vali kiosk</string>
<string name="export_complete_toast">Eksport tehtud</string>
<string name="import_complete_toast">Import tehtud</string>
<string name="no_valid_zip_file">Korralikku ZIP faili pole</string>
<string name="could_not_import_all_files">Hoiatus: Kõiki faile ei õnnestunud importida.</string>
<string name="override_current_data">See alistab praeguse seadistuse.</string>
<string name="import_settings">Kas importida ka seadistused?</string>
<string name="kiosk">"Kiosk "</string>
<string name="trending">Trendid</string>
<string name="top_50">"Top 50 "</string>
<string name="new_and_hot">Uus ja kuum</string>
<string name="title_activity_background_player">Taustapleier</string>
<string name="title_activity_popup_player">Hüpikpleier</string>
<string name="play_queue_remove">Eemalda</string>
<string name="play_queue_stream_detail">Üksikasjad</string>
<string name="play_queue_audio_settings">Heli seaded</string>
<string name="hold_to_append">Hoia järjekorda lisamiseks</string>
<string name="enqueue_on_background">Lisa järjekorda taustal</string>
<string name="enqueue_on_popup">Lisa järjekorda hüpikaknaga</string>
<string name="start_here_on_main">Alusta taasesitust siit</string>
<string name="start_here_on_background">Alusta siit taustal</string>
<string name="start_here_on_popup">Alusta siit hüpikaknaga</string>
<string name="drawer_open">Ava sahtel</string>
<string name="drawer_close">Sulge sahtel</string>
<string name="drawer_header_action_paceholder_text">Siia ilmub varsti midagi ;D</string>
<string name="preferred_open_action_settings_title">Eelistatud \'ava\' toiming</string>
<string name="preferred_open_action_settings_summary">Vaikimisi tegevus sisu avamisel — %s</string>
<string name="video_player">Videopleier</string>
<string name="background_player">Taustapleier</string>
<string name="popup_player">Hüpikpleier</string>
<string name="always_ask_open_action">Küsi alati</string>
<string name="preferred_player_fetcher_notification_title">Info hankimine…</string>
<string name="preferred_player_fetcher_notification_message">Soovitud sisu laadimine</string>
<string name="create_playlist">Loo uus pleilist</string>
<string name="delete_playlist">Kustuta pleilist</string>
<string name="rename_playlist">Nimeta pleilist ümber</string>
<string name="playlist_name_input">Nimi</string>
<string name="append_playlist">Lisa pleilisti</string>
<string name="set_as_playlist_thumbnail">Määra pleilisti pisipildiks</string>
<string name="bookmark_playlist">Lisa pleilist järjehoidjaks</string>
<string name="unbookmark_playlist">Eemalda järjehoidja</string>
<string name="delete_playlist_prompt">Kas kustutada see pleilist?</string>
<string name="playlist_creation_success">Pleilist loodud</string>
<string name="playlist_add_stream_success">Lisati pleilisti</string>
<string name="playlist_thumbnail_change_success">Pleilisti pisipilt muudetud</string>
<string name="playlist_delete_failure">Pleilisti kustutamine nurjus</string>
<string name="caption_none">Subtiitriteta</string>
<string name="resize_fit">Mahuta</string>
<string name="resize_fill">Täida</string>
<string name="resize_zoom">Suumi</string>
<string name="caption_auto_generated">Automaatselt loodud</string>
<string name="caption_setting_title">Subtiitrid</string>
<string name="caption_setting_description">Kohanda pleieri subtiitrite teksti suurust ja tausta. Jõustamiseks tuleb rakendus taaskäivitada</string>
<string name="enable_leak_canary_summary">Mälulekke seire võib põhjustada rakenduse hangumise</string>
<string name="import_export_title">Import/eksport</string>
<string name="import_title">Import</string>
<string name="import_from">Impordi asukohast</string>
<string name="export_to">Ekspordi asukohta</string>
<string name="import_ongoing">Import…</string>
<string name="export_ongoing">Eksport…</string>
<string name="import_file_title">Impordi fail</string>
<string name="previous_export">Eelmine eksport</string>
<string name="subscriptions_import_unsuccessful">Tellimuste import nurjus</string>
<string name="subscriptions_export_unsuccessful">Tellimuste eksport nurjus</string>
<string name="import_youtube_instructions">Impordi YouTube tellimused eksportfaili abil:
\n
\n1. Ava URL: %1$s
\n2. Logi sisse
\n3. Allalaadimine peaks algama (see on eksportfail)</string>
<string name="import_network_expensive_warning">See toiming võib põhjustada suurt võrguliiklust.
\n
\nKas jätkata?</string>
<string name="playback_speed_control">Taasesituse kiiruse juhtimine</string>
<string name="playback_tempo">"Tempo "</string>
<string name="playback_default">Vaikimisi</string>
<string name="accept">Nõustu</string>
<string name="decline">Keeldu</string>
<string name="limit_data_usage_none_description">Piiranguta</string>
<string name="limit_mobile_data_usage_title">Piira lahutust mobiilse andmeside kasutamisel</string>
</resources>

View File

@ -328,21 +328,21 @@
<string name="metadata_cache_wipe_summary">Kendu cache-ko wegbuneen datu guztiak</string>
<string name="metadata_cache_wipe_complete_notice">Metadatuen cache-a ezabatuta</string>
<string name="auto_queue_title">Gehitu ilarara hurrengo jarioa</string>
<string name="auto_queue_summary">Gehitu erlazionatutako jario bat azken jarioa jo bitartean errepikapenik gabeko ilara batean.</string>
<string name="auto_queue_summary">Gehitu erlazionatutako jario bat azken jarioa jo bitartean errepikapenik gabeko ilara batean</string>
<string name="settings_category_debug_title">Arazketa</string>
<string name="file">Fitxategia</string>
<string name="import_data_title">Inportatu datu-basea</string>
<string name="export_data_title">Esportatu datu-basea</string>
<string name="import_data_summary">Zure uneko historiala eta harpidetzak gainidatziko ditu</string>
<string name="export_data_summary">Esportatu historiala, harpidetzak eta erreprodukzio-zerrendak.</string>
<string name="export_data_summary">Esportatu historiala, harpidetzak eta erreprodukzio-zerrendak</string>
<string name="clear_views_history_title">Garbitu ikusitakoaren historiala</string>
<string name="clear_views_history_summary">Jotako jarioen historiala ezabatzen du.</string>
<string name="delete_view_history_alert">Ezabatu ikusitakoaren historial osoa.</string>
<string name="clear_views_history_summary">Jotako jarioen historiala ezabatzen du</string>
<string name="delete_view_history_alert">Ezabatu ikusitakoaren historial osoa</string>
<string name="view_history_deleted">Ikusitakoaren historiala ezabatuta.</string>
<string name="clear_search_history_title">Garbitu bilaketa historiala</string>
<string name="clear_search_history_summary">Ezabatu bilaketa gakoen historiala.</string>
<string name="delete_search_history_alert">Ezabatu bilaketen historial osoa.</string>
<string name="clear_search_history_summary">Ezabatu bilaketa gakoen historiala</string>
<string name="delete_search_history_alert">Ezabatu bilaketen historial osoa</string>
<string name="search_history_deleted">Bilaketen historiala ezabatuta.</string>
<string name="invalid_directory">Direktorio baliogabea</string>
<string name="invalid_source">Fitxategi edo edukiaren iturri baliogabea</string>
@ -407,7 +407,7 @@
<string name="caption_auto_generated">Automatikoki sortuak</string>
<string name="caption_setting_title">Azpitituluak</string>
<string name="caption_setting_description">Aldatu azpitituluen testuaren eskala eta atzealdeko estiloa. Aplikazioa berrabiarazi behar da aldaketak aplikatzeko.</string>
<string name="caption_setting_description">Aldatu azpitituluen testuaren eskala eta atzealdeko estiloa. Aplikazioa berrabiarazi behar da aldaketak aplikatzeko</string>
<string name="enable_leak_canary_title">Gaitu LeakCanary</string>
<string name="enable_leak_canary_summary">Memoria galeren monitorizazioa. Aplikazioak agian ez du erantzungo memoriaren aitortza egin bitartean</string>
@ -453,7 +453,7 @@
<string name="import_settings">Ezarpenak ere inportatu nahi dituzu?</string>
<string name="use_inexact_seek_summary">Bilaketa ez zehatzak posizioak azkarrago baina prezisio gutxiagoz bilatzea ahalbidetzen du</string>
<string name="download_thumbnail_summary">"Desgaitu iruditxoak kargatzea gelditzeko eta datuak eta memoria aurrezteko. Hau aldatzean memoria eta diskoko irudien cache-ak garbituko dira."</string>
<string name="download_thumbnail_summary">"Desgaitu iruditxoak kargatzea gelditzeko eta datuak eta memoria aurrezteko. Hau aldatzean memoria eta diskoko irudien cache-ak garbituko dira"</string>
<string name="app_license">NewPipe Software Librea eta Copyleft da: Nahi eran erabili, ikertu, partekatu eta hobetu dezakezu. Zehazki, elkarbanatzea eta aldatzea Free Software Foundation-ek argitaratutako GNU General Public License-ren 3. bertsioa edo berriagoren baten terminoen arabera egiteko baimena duzu.</string>
<string name="enable_disposed_exceptions_summary">Behartu aktibitatearen bizitza ziklotik kanpo baztertu eta gero entregatu ezin diren Rx salbuespenen inguruko txostena</string>
@ -468,4 +468,18 @@
<string name="limit_data_usage_none_description">Mugagabea</string>
<string name="limit_mobile_data_usage_title">Mugatu bereizmena datu mugikorrak erabiltzean</string>
<string name="skip_silence_checkbox">Aurreratu azkar isilunea dagoenean</string>
<string name="playback_step">Urratsa</string>
<string name="playback_reset">Leheneratu</string>
<string name="minimize_on_exit_title">Minimizatu aplikazioa aldatzean</string>
<string name="minimize_on_exit_summary">Ekintza bideo erreproduzigailu nagusitik beste aplikazio batera aldatzean — %s</string>
<string name="minimize_on_exit_none_description">Bat ere ez</string>
<string name="minimize_on_exit_background_description">Minimizatu bigarren planoko erreproduzigailura</string>
<string name="minimize_on_exit_popup_description">Minimizatu laster-liho erreproduzigailura</string>
<string name="channels">Kanalak</string>
<string name="playlists">Erreprodukzio-zerrendak</string>
<string name="tracks">Pistak</string>
<string name="users">Erabiltzaileak</string>
</resources>

View File

@ -384,7 +384,7 @@
<string name="use_inexact_seek_title">Recherche rapide approximative</string>
<string name="use_inexact_seek_summary">Permettre au lecteur d\'accéder plus rapidement à une position au détriment de la précision</string>
<string name="download_thumbnail_title">Charger miniatures</string>
<string name="download_thumbnail_summary">Désactiver pour arrêter le chargement de toutes les miniatures et sauvegarder les données et l\'utilisation de la mémoire. Modifier cela effacera à la fois le cache d\'image en mémoire et sur disque.</string>
<string name="download_thumbnail_summary">Désactiver pour arrêter le chargement de toutes les miniatures et sauvegarder les données et l\'utilisation de la mémoire. Modifier cela effacera à la fois le cache d\'image en mémoire et sur disque</string>
<string name="thumbnail_cache_wipe_complete_notice">Images en cache effacées</string>
<string name="metadata_cache_wipe_title">Effacer les données en cache</string>
<string name="metadata_cache_wipe_summary">Effacer toutes les pages web mises en cache</string>
@ -403,7 +403,7 @@
<string name="controls_download_desc">Télécharger le fichier de flux</string>
<string name="auto_queue_title">Vidéo suivante en file d\'attente</string>
<string name="auto_queue_summary">Ajout automatique d\'un flux connexe lors de la lecture du dernier flux dans une file d\'attente non répétitive.</string>
<string name="auto_queue_summary">Ajout automatique d\'un morceau suggéré lors de la lecture du dernier morceau dans une file d\'attente non bouclée.</string>
<string name="settings_category_debug_title">Débogage</string>
<string name="resize_fill">Remplir</string>
<string name="caption_auto_generated">Générés automatiquement</string>
@ -434,11 +434,11 @@
\n3. Un téléchargement va démarrer (ce sera celui du fichier nécessaire à l\'importation des abonnements)"</string>
<string name="import_soundcloud_instructions">Pour importer vos abonnements SoundCloud, vous devez connaitre l\'URL de votre profil ou votre identifiant (id). Si vous le savez, tapez-le ci-dessous.
\n
\nSi vous ne le connaissez pas, veuillez suivre les étapes suivantes :
\nSi vous ne le connaissez pas, veuillez suivre les étapes suivantes :
\n
\n1. Activer le \"mode bureau\" dans votre navigateur (le site n\'est pas accesible en mode mobile)
\n2. Suivez ce : %1$s
\n3. Connectez-vous à votre compte
\n1. Activer le \"mode bureau\" dans votre navigateur (le site n\'est pas accesible en mode mobile)
\n2. Suivez cette URL : %1$s
\n3. Connectez-vous à votre compte
\n4. Copier l\'URL vers lequel vous venez d\'être redirigé (qui est l\'URL de votre profil)</string>
<string name="import_soundcloud_instructions_hint">votreID, soundcloud.com/votreid</string>
@ -448,7 +448,7 @@
<string name="playback_speed_control">Vitesse de lecture</string>
<string name="playback_tempo">Cadence</string>
<string name="unhook_checkbox">Unhook (déformations possibles)</string>
<string name="unhook_checkbox">Détacher (déformations possibles)</string>
<string name="playback_default">Défaut</string>
<string name="preferred_open_action_settings_title">Ouvrir de préférence avec</string>
<string name="preferred_open_action_settings_summary">Action par défaut lors de l\'ouverture de contenu - %s</string>
@ -485,4 +485,18 @@
\nVous devez l\'accepter pour nous envoyer le rapport de bug.</string>
<string name="limit_data_usage_none_description">Pas de limite</string>
<string name="limit_mobile_data_usage_title">Limiter la résolution en données mobile</string>
</resources>
<string name="channels">Chaînes</string>
<string name="playlists">Listes de lecture</string>
<string name="tracks">Pistes</string>
<string name="users">Utilisateurs</string>
<string name="skip_silence_checkbox">Accélérer pendant les silences</string>
<string name="playback_step">Étape</string>
<string name="playback_reset">Réinitialiser</string>
<string name="minimize_on_exit_title">Minimiser lors du changement d\'application</string>
<string name="minimize_on_exit_summary">Action lors du changement d\'applications depuis le lecteur vidéo —%s</string>
<string name="minimize_on_exit_none_description">Aucune</string>
<string name="minimize_on_exit_background_description">Lecture en arrière plan</string>
<string name="minimize_on_exit_popup_description">Mettre en lecteur popup</string>
</resources>

View File

@ -32,7 +32,7 @@
<string name="download_path_audio_dialog_title">הכנס נתיב לשמירת קבצי שמע</string>
<string name="autoplay_by_calling_app_title">נגן אוטומטית</string>
<string name="autoplay_by_calling_app_summary">הפעל סרטון אוטומטית כאשר NewPipe נפתח דרך אפליקציה אחרת</string>
<string name="autoplay_by_calling_app_summary">מנגן סרטון כאשר NewPipe נפתח דרך אפליקציה אחרת</string>
<string name="default_resolution_title">רזולוציית ברירת המחדל</string>
<string name="default_popup_resolution_title">רזולוציית ברירת המחדל לחלון צץ</string>
<string name="show_higher_resolutions_title">הצג רזולוציות גבוהות יותר</string>
@ -119,7 +119,7 @@
<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_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>
@ -386,4 +386,16 @@
\n3. ההורדה אמורה להתחיל (זהו קובץ היצוא)</string>
<string name="playback_tempo">קצב</string>
<string name="playback_default">ברירת מחדל</string>
</resources>
<string name="use_inexact_seek_title">השתמש בחיפוש מהיר שאינו מדויק</string>
<string name="use_inexact_seek_summary">חיפוש לא מדויק מאפשר לנגן לחפש נקודת זמן מהר יותר, עם דיוק מופחת</string>
<string name="download_thumbnail_title">טען תמונות ממוזערות</string>
<string name="download_thumbnail_summary">"השבת כדי לעצור את טעינת כל התמונות הממוזערות וחסוך בשימוש בנתונים ובזכרון. השינוי ימחק את המטמון בזכרון ובדיסק. "</string>
<string name="metadata_cache_wipe_summary">הסר את כל נתוני העמודים במטמון</string>
<string name="auto_queue_title">אוטומטית הכנס לתור את ההזרמה הבאה</string>
<string name="auto_queue_summary">"אוטומטית הוסף הזרמה קשורה כאשר ההזרמה האחרונה לא נמצאת במצב הזרמה חוזרת של התור. "</string>
<string name="toggle_orientation">כפתור כיוון</string>
<string name="switch_to_main">החלף לראשי</string>
<string name="import_data_summary">פעולה זו תדרוס את ההיסטוריה ורשימת המנויים הקיימת.</string>
<string name="clear_views_history_summary">"מחק הסטוריה של ניגונים קודמים. "</string>
</resources>

View File

@ -186,7 +186,7 @@
<string name="action_about">Informazioni</string>
<string name="title_licenses">Licenze di terze parti</string>
<string name="copyright" formatted="true">© %1$s di %2$s protetto da licenza %3$s</string>
<string name="error_unable_to_load_license">Impossible caricare il contratto di licenza</string>
<string name="error_unable_to_load_license">Impossible caricare la licenza</string>
<string name="action_open_website">Visita il sito</string>
<string name="tab_about">Informazioni</string>
<string name="tab_contributors">Contributori</string>
@ -218,7 +218,7 @@
<string name="settings_category_downloads_title">Scarica</string>
<string name="settings_file_charset_title">Caratteri ammessi nei nomi dei file</string>
<string name="settings_file_replacement_character_summary">I caratteri non validi vengono sostituiti con questo</string>
<string name="settings_file_replacement_character_summary">I caratteri non validi vengono sostituiti con</string>
<string name="settings_file_replacement_character_title">Carattere sostitutivo</string>
<string name="charset_letters_and_digits">Lettere e cifre</string>
@ -325,9 +325,9 @@
<string name="video_streams_empty">Nessun flusso video trovato</string>
<string name="audio_streams_empty">Nessun flusso audio trovato</string>
<string name="video_player">Riproduttore video</string>
<string name="background_player">Riproduttore di fondo</string>
<string name="popup_player">Riproduttore a comparsa</string>
<string name="video_player">Lettore video</string>
<string name="background_player">Riproduzione in sottofondo</string>
<string name="popup_player">Riproduzione in modalità popup</string>
<string name="always_ask_player">Chiedi sempre</string>
<string name="preferred_player_fetcher_notification_title">Raccogliendo informazioni…</string>
@ -418,7 +418,7 @@
<string name="import_export_title">Importa/Esporta</string>
<string name="import_title">Importa</string>
<string name="import_from">Importa da</string>
<string name="export_to">Esporta a</string>
<string name="export_to">Esporta in</string>
<string name="import_ongoing">Importando…</string>
<string name="export_ongoing">Esportando…</string>
@ -454,7 +454,7 @@
<string name="playback_speed_control">Controlli della velocità di riproduzione</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">Tono</string>
<string name="unhook_checkbox">Slega (può causare distorsione)</string>
<string name="unhook_checkbox">Scollega (può causare distorsione)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Valore predefinito</string>
<string name="no_streams_available_download">Nessun flusso disponibile per il download</string>
@ -472,7 +472,7 @@
<string name="delete_view_history_alert">Elimina l\'intera cronologia visualizzazioni.</string>
<string name="view_history_deleted">Cronologia visualizzazioni eliminata.</string>
<string name="clear_search_history_title">Pulisci cronologia delle ricerche</string>
<string name="clear_search_history_summary">Elimina la cronologia delle ricerche effettuate.</string>
<string name="clear_search_history_summary">Cancella la cronologia dei termini di ricerca.</string>
<string name="delete_search_history_alert">Elimina l\'intera cronologia delle ricerche.</string>
<string name="search_history_deleted">Cronologia delle ricerche eliminata.</string>
<string name="one_item_deleted">1 elemento eliminato.</string>
@ -491,4 +491,14 @@
<string name="limit_data_usage_none_description">Senza limiti</string>
<string name="limit_mobile_data_usage_title">Limita la risoluzione quando si utilizzano dati mobili</string>
</resources>
<string name="skip_silence_checkbox">Avanzamento veloce durante il silenzio</string>
<string name="playback_step">Step</string>
<string name="playback_reset">Reset</string>
<string name="minimize_on_exit_title">Minimizza al cambio applicazione</string>
<string name="minimize_on_exit_summary">Azione quando si passa ad un\'altra applicazione dal lettore video principale — %s</string>
<string name="minimize_on_exit_none_description">Nessuna</string>
<string name="minimize_on_exit_background_description">Minimizza al lettore in sottofondo</string>
<string name="minimize_on_exit_popup_description">Minimizza al lettore popup</string>
</resources>

View File

@ -444,4 +444,15 @@
<string name="caption_setting_title">Преводи</string>
<string name="caption_setting_description">Смени ја големината и стилот на преводот. Потребен е рестарт за промена.</string>
<string name="privacy_policy_title">NewPipe - политика за приватност</string>
<string name="privacy_policy_encouragement">Проектот NewPipe сериозно ја сфаќа вашата приватност. Затоа апликацијата не собира ваши податоци без ваша дозвола.
\nПолитиката за приватност на NewPipe детално објаснува кои податоци се зачувани и пратени кога праќате извештај за грешка во апликацијата.</string>
<string name="read_privacy_policy">Прочитај ја политиката за приватност</string>
<string name="start_accept_privacy_policy">За да постапуваме соодветно со регулацијата за заштита на податоци (GDPR) на ЕУ, вараме да обрнете внимание на политиката за приватност на NewPipe.
\nВе молиме прочитајте ја внимателно. Мора да ја прифатите за да ни го испратите извештајот за грешка во апликацијата.</string>
<string name="accept">Прифати</string>
<string name="decline">Отфрли</string>
<string name="limit_data_usage_none_description">Неограничено</string>
<string name="limit_mobile_data_usage_title">Ограничи резолуција при користење мобилен интернет</string>
</resources>

View File

@ -477,4 +477,18 @@
<string name="decline">Avslå</string>
<string name="limit_data_usage_none_description">Ubegrenset</string>
<string name="limit_mobile_data_usage_title">Begrens oppløsning når mobildata brukes</string>
<string name="minimize_on_exit_title">Minimer ved programbytte</string>
<string name="minimize_on_exit_summary">Handling ved bytting til annet program fra hovedspiller — %s</string>
<string name="minimize_on_exit_none_description">Ingen</string>
<string name="minimize_on_exit_background_description">Minimer til bakgrunnsspiller</string>
<string name="minimize_on_exit_popup_description">Minimer til oppsprettsspiller</string>
<string name="channels">Kanaler</string>
<string name="playlists">Spillelister</string>
<string name="tracks">Spor</string>
<string name="users">Brukere</string>
<string name="skip_silence_checkbox">Hurtig foroverspoling ved stillhet</string>
<string name="playback_step">Steg</string>
<string name="playback_reset">Tilbakestill</string>
</resources>

View File

@ -448,4 +448,14 @@
<string name="decline">Weigeren</string>
<string name="limit_data_usage_none_description">Onbeperkt</string>
<string name="limit_mobile_data_usage_title">Resolutie beperken bij gebruik van mobiele gegevens</string>
<string name="minimize_on_exit_title">Minimaliseren bij overschakelen naar anderen app</string>
<string name="minimize_on_exit_summary">Actie bij overschakelen van videospeler naar anderen app — %s</string>
<string name="minimize_on_exit_none_description">Geen</string>
<string name="minimize_on_exit_background_description">Afspelen in achtergrond</string>
<string name="minimize_on_exit_popup_description">Afspelen in pop-up</string>
<string name="skip_silence_checkbox">Doorspoelen tijdens stilte</string>
<string name="playback_step">Stap</string>
<string name="playback_reset">Standaardwaarden herstellen</string>
</resources>

View File

@ -331,7 +331,7 @@ te openen in pop-upmodus</string>
<string name="import_data_title">Database importeren</string>
<string name="export_data_title">Database exporteren</string>
<string name="import_data_summary">Dit zal je huidige geschiedenis en abonnementen overschrijven</string>
<string name="export_data_summary">Exporteer geschiedenis, abonnementen en speellijsten.</string>
<string name="export_data_summary">Exporteer geschiedenis, abonnementen en speellijsten</string>
<string name="export_complete_toast">Export voltooid</string>
<string name="import_complete_toast">Import voltooid</string>
<string name="no_valid_zip_file">Geen geldig ZIP-bestand</string>
@ -399,7 +399,7 @@ te openen in pop-upmodus</string>
<string name="use_inexact_seek_title">Snelle, minder exact spoelen gebruiken</string>
<string name="use_inexact_seek_summary">Minder exact spoelen laat de speler sneller posities zoeken met verminderde precisie</string>
<string name="auto_queue_title">Volgende stream automatisch in wachtrij plaatsen</string>
<string name="auto_queue_summary">Automatisch een gerelateerde stream toekennen bij het afspelen van de laatste stream in een niet-herhalende afspeelwachtlijst.</string>
<string name="auto_queue_summary">Automatisch een gerelateerde stream toekennen bij het afspelen van de laatste stream in een niet-herhalende afspeelwachtlijst</string>
<string name="live_sync">SYNCHRONISEREN</string>
<string name="file">Bestand</string>
@ -441,7 +441,7 @@ te openen in pop-upmodus</string>
\n
\nWil je doorgaan?</string>
<string name="download_thumbnail_title">Miniatuurvoorbeelden laden</string>
<string name="download_thumbnail_summary">Schakel dit uit om alle miniatuurvoorbeelden niet meer te laden; dit bespaart gegevens en geheugen. Het wijzigen van deze instelling wist het geheugen en de afbeeldingscache.</string>
<string name="download_thumbnail_summary">Schakel dit uit om alle miniatuurvoorbeelden niet meer te laden; dit bespaart gegevens en geheugen. Het wijzigen van deze instelling wist het geheugen en de afbeeldingscache</string>
<string name="thumbnail_cache_wipe_complete_notice">Afbeeldingscache gewist</string>
<string name="metadata_cache_wipe_title">Gecachete metagegevens wissen</string>
<string name="metadata_cache_wipe_summary">Alle gecachete webpagina-gegevens wissen</string>
@ -449,7 +449,7 @@ te openen in pop-upmodus</string>
<string name="playback_speed_control">Afspeelsnelheidbesturing</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">Toon</string>
<string name="unhook_checkbox">Ontkoppelen (kan ruis veroorzaken)</string>
<string name="unhook_checkbox">Ontlinken (kan ruis veroorzaken)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Standaard</string>
<string name="preferred_open_action_settings_title">Voorkeursactie voor openen</string>
@ -458,17 +458,17 @@ te openen in pop-upmodus</string>
<string name="no_streams_available_download">Geen streams beschikbaar voor downloaden</string>
<string name="caption_setting_title">Bijschriften</string>
<string name="caption_setting_description">Bijschriftgrootte en achtergrondstijlen wijzigen. Vereist een herstart van de app.</string>
<string name="caption_setting_description">Bijschriftgrootte en achtergrondstijlen wijzigen. Vereist een herstart van de app</string>
<string name="toast_no_player">Er is geen app geïnstalleerd die dit bestand kan afspelen</string>
<string name="clear_views_history_title">Kijkgeschiedenis wissen</string>
<string name="clear_views_history_summary">Verwijdert de geschiedenis van afgespeelde streams.</string>
<string name="delete_view_history_alert">Verwijdert de gehele kijkgeschiedenis.</string>
<string name="clear_views_history_summary">Verwijdert de geschiedenis van afgespeelde streams</string>
<string name="delete_view_history_alert">Verwijdert de gehele kijkgeschiedenis</string>
<string name="view_history_deleted">Kijkgeschiedenis verwijderd.</string>
<string name="clear_search_history_title">Zoekgeschiedenis wissen</string>
<string name="clear_search_history_summary">Verwijdert de gebruikte zoektermen.</string>
<string name="delete_search_history_alert">Verwijdert de gehele geschiedenis.</string>
<string name="clear_search_history_summary">Verwijdert de gebruikte zoektermen</string>
<string name="delete_search_history_alert">Verwijdert de gehele geschiedenis</string>
<string name="search_history_deleted">Zoekgeschiedenis verwijderd.</string>
<string name="one_item_deleted">1 item verwijderd.</string>
@ -485,4 +485,18 @@ te openen in pop-upmodus</string>
<string name="decline">Weigeren</string>
<string name="limit_data_usage_none_description">Ongelimiteerd</string>
<string name="limit_mobile_data_usage_title">Resolutie beperken bij gebruik van mobiele gegevens</string>
<string name="minimize_on_exit_title">Minimaliseren bij overschakelen naar andere app</string>
<string name="minimize_on_exit_summary">Actie bij overschakelen van videospeler naar andere app — %s</string>
<string name="minimize_on_exit_none_description">Geen</string>
<string name="minimize_on_exit_background_description">Afspelen op achtergrond</string>
<string name="minimize_on_exit_popup_description">Afspelen in pop-upvenster</string>
<string name="skip_silence_checkbox">Vooruitspoelen tijdens stilte</string>
<string name="playback_step">Stap</string>
<string name="playback_reset">Standaardwaarden</string>
<string name="channels">Kanalen</string>
<string name="playlists">Afspeellijsten</string>
<string name="tracks">Nummers</string>
<string name="users">Gebruikers</string>
</resources>

View File

@ -266,7 +266,7 @@
<string name="charset_most_special_characters">Większość znaków specjalnych</string>
<string name="donation_title">Dotacja</string>
<string name="donation_encouragement">NewPipe rozwijane jest przez wolontariuszy, którzy poświęcają swój wolny czas, by zapewnić ci jak najlepsze wrażenia podczas korzystania z aplikacji. To dobry moment, aby wesprzeć programistów i sprawić, by program był jeszcze lepszy, nie przerywając przy tym popijania kawy!</string>
<string name="donation_encouragement">NewPipe rozwijane jest przez wolontariuszy, którzy poświęcają swój wolny czas, by zapewnić ci jak najlepsze wrażenia podczas korzystania z aplikacji. To dobry moment, aby wesprzeć programistów i sprawić, by program był jeszcze lepszy, nie przerywając przy tym popijania kawy.</string>
<string name="give_back">Daj od siebie</string>
<string name="website_title">Witryna</string>
<string name="website_encouragement">By otrzymać więcej informacji oraz najnowsze wiadomości o NewPipe, odwiedź naszą stronę.</string>
@ -473,4 +473,14 @@
<string name="app_license">NewPipe jest wolnym i bezpłatnym oprogramowaniem: Możesz używać, brać udział oraz udoskonalać je według własnego uznania. W szczególności możesz redystrybuować lub/i modyfikować pod zasadami GNU General Public License opublikowanej przez Free Software Fundation w wersji 3 lub (w zależności od ciebie) dowolnej późniejszej wersji.</string>
<string name="import_settings">Czy chcesz zaimportować również ustawienia?</string>
<string name="privacy_policy_title">Polityka prywatności programu NewPipe</string>
<string name="privacy_policy_encouragement">Projekt NewPipe bardzo poważnie traktuję politykę prywatności. Aplikacja nie zbiera żadnych danych bez Twojej zgody. Polityka prywatności programu NewPipe szczegółowo wyjaśnia, jakie dane są zbierane i przesyłane, kiedy wysyłasz raport o błędach aplikacji.</string>
<string name="read_privacy_policy">Przeczytaj politykę prywatności</string>
<string name="start_accept_privacy_policy">Ze względu na wprowadzenie Ogólnego Rozporządzenia o ochronie danych (RODO), chcielibyśmy zwrócić Twoją uwagę na politykę prywatności NewPipe. Proszę przeczytać ją uważnie.
\nMusisz ją zaakceptować, aby przesyłać raporty o błędach.</string>
<string name="accept">Akceptuj</string>
<string name="decline">Odrzuć</string>
<string name="limit_data_usage_none_description">Brak limitu</string>
<string name="limit_mobile_data_usage_title">Limit przy użyciu danych mobilnych</string>
</resources>

View File

@ -100,7 +100,7 @@
<string name="msg_threads">Threads</string>
<string name="msg_url_malform">URL inválida ou internet indisponível</string>
<string name="no_available_dir">Selecione uma pasta para download</string>
<string name="no_player_found">Nenhum player de stream encontrado. Deseja Instalar o VLC?</string>
<string name="no_player_found">Nenhum player de stream encontrado. Deseja instalar o VLC?</string>
<string name="parsing_error">Não foi possível interpretar o site</string>
<string name="play_audio">Áudio</string>
<string name="play_btn_text">Reproduzir</string>
@ -310,7 +310,7 @@ abrir em modo popup</string>
<string name="import_data_title">Importar base de dados</string>
<string name="export_data_title">Exportar base de dados</string>
<string name="import_data_summary">Isso irá sobrescrever seu histórico e inscrições</string>
<string name="export_data_summary">Exportar histórico, inscrições e listas de reprodução.</string>
<string name="export_data_summary">Exportar histórico, inscrições e listas de reprodução</string>
<string name="export_complete_toast">Exportação completa</string>
<string name="import_complete_toast">Importação completa</string>
<string name="no_valid_zip_file">Não há nenhum arquivo ZIP válido</string>
@ -379,7 +379,7 @@ abrir em modo popup</string>
<string name="use_inexact_seek_title">Usar índice de indexação rápido porém não preciso</string>
<string name="use_inexact_seek_summary">Usar índice de indexação inexato</string>
<string name="auto_queue_title">Adicionar a próxima stream à fila automaticamente</string>
<string name="auto_queue_summary">Auto anexar uma stream relacionada quando a reprodução iniciar na última stream em uma fila não repetitiva.</string>
<string name="auto_queue_summary">Auto anexar uma stream relacionada quando a reprodução iniciar na última stream em uma fila não repetitiva</string>
<string name="live_sync">Sincronizar</string>
<string name="file">Arquivo</string>
@ -427,9 +427,9 @@ abrir em modo popup</string>
<string name="metadata_cache_wipe_complete_notice">Cache de metadados foi limpo</string>
<string name="playback_speed_control">Controles de Velocidade de Reprodução</string>
<string name="playback_tempo">"Tempo "</string>
<string name="download_thumbnail_summary">Desabilite para para de carregar todas as miniaturas e economizar dados e uso de memória. Alterar esta configuração irá limpar o cache de imagens na memória e em disco.</string>
<string name="download_thumbnail_summary">Desabilite para para de carregar todas as miniaturas e economizar dados e uso de memória. Alterar esta configuração irá limpar o cache de imagens na memória e em disco</string>
<string name="playback_pitch">Passo</string>
<string name="unhook_checkbox">Desenganchar</string>
<string name="unhook_checkbox">Desvincular (pode causar distorção)</string>
<string name="playback_default">Padrão</string>
<string name="preferred_open_action_settings_title">Ação de \'abrir\' preferida</string>
<string name="preferred_open_action_settings_summary">Ação padrão quando abrir conteúdo — %s</string>
@ -439,18 +439,18 @@ abrir em modo popup</string>
<string name="drawer_open">Abrir gaveta</string>
<string name="drawer_close">Fechar gaveta</string>
<string name="caption_setting_title">Legendas</string>
<string name="caption_setting_description">Modifique o tamanho da legenda e o estilo da tela de fundo. Necessário reiniciar o aplicativo para ter efeito.</string>
<string name="caption_setting_description">Modifique o tamanho da legenda e o estilo da tela de fundo. Necessário reiniciar o aplicativo para ter efeito</string>
<string name="playback_nightcore">Night-core</string>
<string name="toast_no_player">Nenhum player instalado para reproduzir este arquivo</string>
<string name="clear_views_history_title">"Limpar histórico de já assistidos "</string>
<string name="clear_views_history_summary">Deleta o histórico de videos já reproduzidos.</string>
<string name="delete_view_history_alert">Deleta todo o histórico de já reproduzidos.</string>
<string name="clear_views_history_summary">Deleta o histórico de videos já reproduzidos</string>
<string name="delete_view_history_alert">Deleta todo o histórico de já reproduzidos</string>
<string name="view_history_deleted">Histórico de já assistidos deletado.</string>
<string name="clear_search_history_title">Limpar histórico de pesquisa</string>
<string name="clear_search_history_summary">Deleta histórico de palavras chave pesquisadas.</string>
<string name="delete_search_history_alert">Deletar todo o histórico de pesquisa.</string>
<string name="clear_search_history_summary">Deleta histórico de palavras chave pesquisadas</string>
<string name="delete_search_history_alert">Deletar todo o histórico de pesquisa</string>
<string name="search_history_deleted">Histórico de pesquisa deletado.</string>
<string name="one_item_deleted">1 item deletado.</string>
@ -467,4 +467,19 @@ abrir em modo popup</string>
<string name="decline">Recusar</string>
<string name="limit_data_usage_none_description">Ilimitado</string>
<string name="limit_mobile_data_usage_title">Limitar resolução quando dados móveis estiverem em uso</string>
<string name="minimize_on_exit_title">Minimizar ao trocar de aplicativo</string>
<string name="minimize_on_exit_summary">Ação ao trocar de aplicativo quando estiver no reprodutor de vídeo principal — %s</string>
<string name="minimize_on_exit_none_description">"Nenhuma "</string>
<string name="minimize_on_exit_background_description">Minimizar para reprodutor em plano de fundo</string>
<string name="minimize_on_exit_popup_description">Minimizar para reprodutor popup</string>
<string name="skip_silence_checkbox">Avançar rapidamente durante silêncio</string>
<string name="playback_step">Parar
\n</string>
<string name="playback_reset">Reiniciar</string>
<string name="channels">Canais</string>
<string name="playlists">Listas de reprodução</string>
<string name="tracks">Faixas</string>
<string name="users">Usuários</string>
</resources>

View File

@ -2,36 +2,36 @@
<resources>
<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">Потоковый плеер не найден. Установить VLC?</string>
<string name="install">Установить</string>
<string name="cancel">Отмена</string>
<string name="open_in_browser">Открыть в браузере</string>
<string name="share">Поделиться</string>
<string name="download">Скачать</string>
<string name="search">Найти</string>
<string name="search">Поиск</string>
<string name="settings">Настройки</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="download_path_title">Папка для загрузки видео</string>
<string name="download_path_title">Путь загрузки видео</string>
<string name="download_path_summary">Папка для хранения загруженных видео</string>
<string name="download_path_dialog_title">Введите путь к папке для загрузки видео</string>
<string name="default_resolution_title">Разрешение по умолчанию</string>
<string name="play_with_kodi_title">Воспроизвести с помощью Kodi</string>
<string name="play_with_kodi_title">Воспроизвести в Kodi</string>
<string name="kore_not_found">Приложение Kore не найдено. Установить его?</string>
<string name="show_play_with_kodi_title">Показывать опцию «Воспроизвести с помощью Kodi»</string>
<string name="show_play_with_kodi_summary">Показать опцию воспроизведения видео через Kodi media center</string>
<string name="show_play_with_kodi_title">\"Воспроизвести в Kodi\"</string>
<string name="show_play_with_kodi_summary">Показать опцию воспроизведения видео через медиацентр Kodi</string>
<string name="play_audio">Аудио</string>
<string name="default_audio_format_title">Формат аудио по умолчанию</string>
<string name="webm_description">WebM — свободный формат</string>
<string name="m4a_description">M4A — лучше качество</string>
<string name="webm_description">WebM — свободный</string>
<string name="m4a_description">M4A — выше качество</string>
<string name="download_dialog_title">Скачать</string>
<string name="next_video_title">Следующее видео</string>
<string name="url_not_supported_toast">URL не поддерживается</string>
<string name="show_next_and_similar_title">Показывать \'следующее\' и \'предложенные\' видео</string>
<string name="show_next_and_similar_title">\"Следующее\" и \"Предложенные\"</string>
<string name="search_language_title">Язык контента по умолчанию</string>
<string name="settings_category_video_audio_title">Видео и Аудио</string>
<string name="settings_category_video_audio_title">Видео и аудио</string>
<string name="settings_category_appearance_title">Внешний вид</string>
<string name="settings_category_other_title">Другое</string>
@ -40,8 +40,8 @@
<string name="detail_uploader_thumbnail_view_description">Миниатюра аватара пользователя</string>
<string name="detail_dislikes_img_view_description">Не понравилось</string>
<string name="detail_likes_img_view_description">Понравилось</string>
<string name="use_external_video_player_title">Использовать внешний видеоплеер</string>
<string name="use_external_audio_player_title">Использовать внешний аудиоплеер</string>
<string name="use_external_video_player_title">Внешний видеоплеер</string>
<string name="use_external_audio_player_title">Внешний аудиоплеер</string>
<string name="background_player_playing_toast">Воспроизведение в фоновом режиме</string>
<string name="theme_title">Тема</string>
<string name="dark_theme_title">Тёмная</string>
@ -51,7 +51,7 @@
<string name="network_error">Ошибка сети</string>
<string name="use_tor_title">Использовать Tor</string>
<string name="download_path_audio_title">Папка для загрузки аудио</string>
<string name="download_path_audio_title">Путь загрузки аудио</string>
<string name="download_path_audio_summary">Папка для хранения загруженных аудио</string>
<string name="download_path_audio_dialog_title">Введите путь к папке для загрузки аудио</string>
@ -59,7 +59,7 @@
<string name="msg_wait">Подождите…</string>
<string name="msg_exists">Файл уже существует</string>
<string name="msg_threads">Потоки</string>
<string name="finish">OK</string>
<string name="finish">ОК</string>
<string name="start">Начать</string>
<string name="pause">Пауза</string>
<string name="delete">Удалить</string>
@ -69,17 +69,17 @@
<string name="msg_error">Ошибка</string>
<string name="msg_server_unsupported">Сервер не поддерживается</string>
<string name="msg_running">NewPipe скачивает</string>
<string name="msg_url_malform">Неправильный URL или нет доступа к интернету</string>
<string name="msg_running_detail">Нажмите для деталей</string>
<string name="msg_url_malform">Неверный URL или нет доступа к интернету</string>
<string name="msg_running_detail">Подробности</string>
<string name="msg_copied">Скопировано в буфер обмена</string>
<string name="no_available_dir">Выберите доступную папку для загрузки</string>
<string name="show_age_restricted_content_title">Показывать контент с ограничением по возрасту</string>
<string name="show_age_restricted_content_title">Контент 18+</string>
<string name="general_error">Ошибка</string>
<string name="your_comment">Ваш комментарий (на английском):</string>
<string name="err_dir_create">Невозможно создать папку для загрузки \'%1$s\'</string>
<string name="autoplay_by_calling_app_title">Воспроизводить автоматически</string>
<string name="your_comment">Ваш комментарий (English):</string>
<string name="err_dir_create">Не удалось создать папку для загрузки \"%1$s\"</string>
<string name="autoplay_by_calling_app_title">Автовоспроизведение</string>
<string name="autoplay_by_calling_app_summary">Воспроизводить видео при вызове NewPipe из другого приложения</string>
<string name="content">Контент</string>
<string name="video_is_age_restricted">Видео с возрастными ограничениями. Разрешить подобный контент можно в настройках.</string>
@ -95,29 +95,29 @@
<string name="could_not_setup_download_menu">Не удалось создать меню загрузки</string>
<string name="live_streams_not_supported">Это прямая трансляция, они пока не поддерживаются.</string>
<string name="could_not_load_image">Не удалось загрузить изображение</string>
<string name="app_ui_crash">"Падение приложения/пользовательского интерфейса "</string>
<string name="app_ui_crash">Падение приложения/UI</string>
<string name="sorry_string">Простите, это не должно было произойти.</string>
<string name="error_report_button_text">Отправить отчёт об ошибке по электронной почте</string>
<string name="error_report_button_text">Отправить отчёт по e-mail</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="error_details_headline">Детали:</string>
<string name="error_details_headline">Подробности:</string>
<string name="use_tor_summary">(Экспериментально) Загружать через Tor для повышения конфиденциальности (прямые трансляции пока не поддерживаются).</string>
<string name="report_error">Сообщить об ошибке</string>
<string name="user_report">Сообщить о нарушении</string>
<string name="info_dir_created">Создана папка для загрузок \'%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="storage_permission_denied">Нет доступа к накопителю</string>
<string name="could_not_load_thumbnails">Не удалось загрузить все миниатюры</string>
<string name="youtube_signature_decryption_error">Не удалось расшифровать подпись URL у видео</string>
<string name="could_not_get_stream">Не удалось найти ни один поток</string>
<string name="could_not_get_stream">Не удалось найти ни одного потока</string>
<string name="view">Воспроизвести</string>
@ -125,19 +125,19 @@
<string name="controls_background_title">В фоне</string>
<string name="controls_popup_title">В окне</string>
<string name="show_higher_resolutions_summary">Только некоторые устройства могут воспроизводить 2K/4K видео</string>
<string name="show_higher_resolutions_summary">Только некоторые устройства могут воспроизводить видео в 2K/4K</string>
<string name="default_video_format_title">Формат видео по умолчанию</string>
<string name="black_theme_title">Чёрная</string>
<string name="popup_remember_size_pos_title">Запоминать размер и положение всплывающего окна</string>
<string name="player_gesture_controls_summary">Использовать жесты для изменения яркости и громкости</string>
<string name="popup_remember_size_pos_title">Восстановить окно</string>
<string name="player_gesture_controls_summary">Изменять яркость и громкость жестами</string>
<string name="settings_category_popup_title">Всплывающее окно</string>
<string name="popup_playing_toast">Воспроизведение во всплывающем окне</string>
<string name="channel">Канал</string>
<string name="yes">Да</string>
<string name="refresh">Обновить</string>
<string name="clear">Очистить</string>
<string name="use_old_player_title">Использовать старый плеер</string>
<string name="player_gesture_controls_title">Контроль жестов</string>
<string name="use_old_player_title">Старый плеер</string>
<string name="player_gesture_controls_title">Управление жестами</string>
<string name="all">Всё</string>
<string name="filter">Фильтр</string>
@ -149,27 +149,27 @@
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="open_in_popup_mode">Открыть в отдельном окне</string>
<string name="show_search_suggestions_summary">Показывать подсказки в поиске</string>
<string name="show_search_suggestions_summary">Отображать подсказки при поиске</string>
<string name="later">Позже</string>
<string name="disabled">Отключено</string>
<string name="popup_resizing_indicator_title">Изменение размера</string>
<string name="use_external_video_player_summary">Для некоторых видео может отсутствовать звук, если включена эта опция</string>
<string name="use_external_video_player_summary">В некоторых разрешениях НЕ будет звука, если эта опция выбрана</string>
<string name="short_million"> млн.</string>
<string name="short_billion"> млрд.</string>
<string name="short_thousand"> тыс.</string>
<string name="default_popup_resolution_title">Разрешение в режиме всплывающего окна</string>
<string name="popup_remember_size_pos_summary">Запоминать последний размер и положение всплывающего окна</string>
<string name="show_search_suggestions_title">Поисковые подсказки</string>
<string name="default_popup_resolution_title">Разрешение всплывающего окна</string>
<string name="popup_remember_size_pos_summary">Запоминать размер и положение всплывающего окна</string>
<string name="show_search_suggestions_title">Варианты поиска</string>
<string name="best_resolution">Лучшее разрешение</string>
<string name="use_old_player_summary">Старый встроенный плеер на Mediaframework</string>
<string name="reCaptcha_title">Запрос reCAPTCHA</string>
<string name="recaptcha_request_toast">Запрошен ввод reCAPTCHA</string>
<string name="show_higher_resolutions_title">Показывать более высокое разрешение</string>
<string name="show_higher_resolutions_title">Высокие разрешения</string>
<string name="popup_mode_share_menu_title">NewPipe в окне</string>
<string name="title_activity_about">О NewPipe</string>
<string name="action_settings">Настройки</string>
@ -182,7 +182,7 @@
<string name="tab_about">О приложении</string>
<string name="tab_contributors">Участники</string>
<string name="read_full_license">Прочитать лицензию</string>
<string name="app_description">Библиотека лёгкого потокового воспроизведения на Android.</string>
<string name="app_description">Свободное легковесное потоковое воспроизведение на Android.</string>
<string name="view_on_github">Открыть на GitHub</string>
<string name="contribution_encouragement">Приветствуется всё — идеи, перевод, изменения дизайна, чистка кода или огромные изменения в коде. Чем больше сделано, тем лучше!</string>
<string name="copyright" formatted="true">© %1$s %2$s под лицензией %3$s</string>
@ -198,9 +198,9 @@
<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_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_title">Возобновить при фокусе</string>
<string name="resume_on_audio_focus_gain_summary">Возобновлять воспроизведение после перерывов (например, телефонных звонков)</string>
@ -208,12 +208,12 @@
<string name="notification_channel_description">Уведомления для NewPipe в фоне и во всплывающем окне</string>
<string name="settings_category_downloads_title">Загрузки</string>
<string name="settings_file_charset_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="charset_letters_and_digits">Буквы и цифры</string>
<string name="charset_most_special_characters">Большинство специальных символов</string>
<string name="charset_most_special_characters">Большинство спецсимволов</string>
<string name="title_activity_history">История</string>
<string name="title_history_search">История поиска</string>
@ -225,12 +225,12 @@
<string name="settings_category_player_title">Плеер</string>
<string name="settings_category_player_behavior_title">Поведение</string>
<string name="settings_category_history_title">История и кеш</string>
<string name="settings_category_history_title">История и кэш</string>
<string name="playlist">Плейлист</string>
<string name="undo">Отменить</string>
<string name="search_no_results">Нет результатов</string>
<string name="empty_subscription_feed_subtitle">Тут только сверчки</string>
<string name="empty_subscription_feed_subtitle">Ничего нет</string>
<string name="no_subscribers">Нет подписчиков</string>
<plurals name="subscribers">
@ -277,74 +277,74 @@
<string name="play_queue_audio_settings">Настройки аудио</string>
<string name="no_channel_subscribed_yet">Пока нет подписок на каналы</string>
<string name="play_queue_remove">Удалить</string>
<string name="subscribed_button_title">Отписаться</string>
<string name="subscribed_button_title">Вы подписаны</string>
<string name="channel_unsubscribed">Подписка отменена</string>
<string name="show_hold_to_append_title">Показать \"держать, чтобы добавить\" подсказку</string>
<string name="show_hold_to_append_summary">Показывать подсказку при нажатии на иконку «В окне» или «В фоне» на странице сведений о видео</string>
<string name="show_hold_to_append_title">\"Зажмите, чтобы добавить\"</string>
<string name="show_hold_to_append_summary">Показать подсказку при нажатии \"В окне\" или \"В фоне\" на странице сведений о видео</string>
<string name="unknown_content">[Неизвестно]</string>
<string name="player_recoverable_failure">Восстановление после ошибки проигрывателя</string>
<string name="player_recoverable_failure">Восстановление после ошибки плеера</string>
<string name="title_activity_background_player">В фоне</string>
<string name="title_activity_popup_player">В окне</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="no_player_found_toast">Ни одного потокового проигрывателя не было найдено (вы можете установить VLC)</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="no_player_found_toast">Потоковый плеер не найден (вы можете установить VLC)</string>
<string name="default_content_country_title">Страна контента по умолчанию</string>
<string name="service_title">Сервис</string>
<string name="always">Всегда</string>
<string name="just_once">Только один раз</string>
<string name="just_once">Только сейчас</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="player_unrecoverable_failure">Ошибка проигрывателя без возможности восстановления</string>
<string name="external_player_unsupported_link_type">Внешние проигрыватели не поддерживают ссылки этих типов</string>
<string name="player_unrecoverable_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="donation_title">Пожертвовать</string>
<string name="donation_encouragement">NewPipe разрабатывается волонтерами, которые проводят время, принося вам лучший опыт. Вернитесь, чтобы помочь разработчикам сделать NewPipe еще лучше, наслаждаясь чашечкой кофе.</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="drawer_open">Открыть лоток</string>
<string name="drawer_close">Закрыть лоток</string>
<string name="website_encouragement">Для получения более подробной информации и последних новостей о NewPipe посетите наш веб-сайт.</string>
<string name="drawer_open">Открыть боковую панель</string>
<string name="drawer_close">Закрыть боковую панель</string>
<string name="video_player">Проигрыватель видео</string>
<string name="background_player">Фоновый проигрыватель</string>
<string name="popup_player">Проигрыватель в окне</string>
<string name="video_player">Видеоплеер</string>
<string name="background_player">Фоновый плеер</string>
<string name="popup_player">Плеер в окне</string>
<string name="always_ask_player">Всегда спрашивать</string>
<string name="preferred_player_fetcher_notification_title">Получение информации</string>
<string name="preferred_player_fetcher_notification_title">Получение сведений</string>
<string name="preferred_player_fetcher_notification_message">Загрузка запрошенного контента</string>
<string name="controls_download_desc">Загрузка файла прямой трансляции.</string>
<string name="show_info">Показать информацию</string>
<string name="show_info">Показать сведения</string>
<string name="tab_bookmarks">Закладки</string>
<string name="controls_add_to_playlist_title">Добавить к</string>
<string name="use_inexact_seek_title">Использовать быстрый, но неточный поиск</string>
<string name="use_inexact_seek_summary">Неточный поиск позволяет плееру искать позицию быстрее, но с пониженной точностью</string>
<string name="auto_queue_title">Автоматическая очередь следующего стрима</string>
<string name="auto_queue_summary">Автоматически добавлять связанные видео, при воспроизведении с последнего видео в неповторяющейся очереди.</string>
<string name="use_inexact_seek_title">Быстрый поиск позиции</string>
<string name="use_inexact_seek_summary">Неточный поиск позволяет плееру искать позицию быстрее, но менее точно</string>
<string name="auto_queue_title">Автодополнение очереди</string>
<string name="auto_queue_summary">Добавлять похожие потоки в очередь при воспроизведении последнего, если не включён повтор</string>
<string name="settings_category_debug_title">Отладка</string>
<string name="file">Файл</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="invalid_directory">Неправильная директория</string>
<string name="invalid_source">Неправильный файл/контент источника</string>
<string name="export_data_summary">Экспорт истории, подписок и плейлистов</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>
@ -357,41 +357,41 @@
<string name="dismiss">Отклонить</string>
<string name="rename">Переименовать</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="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="export_complete_toast">Экспорт завершён</string>
<string name="import_complete_toast">Импорт завершён</string>
<string name="no_valid_zip_file">Нет верного Zip файла</string>
<string name="could_not_import_all_files">Предупреждение: нет возможности импорта всех файлов.</string>
<string name="override_current_data">Это перезапишет вашу текущую установку.</string>
<string name="no_valid_zip_file">Нет верного Zip-файла</string>
<string name="could_not_import_all_files">Внимание: не все файлы были импортированы.</string>
<string name="override_current_data">Текущие данные будут заменены.</string>
<string name="drawer_header_action_paceholder_text">Скоро что-то тут появится ;D</string>
<string name="drawer_header_action_paceholder_text">Скоро здесь кое-что появится ;D</string>
<string name="always_ask_open_action">Всегда спрашивать</string>
<string name="create_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>
<string name="set_as_playlist_thumbnail">На миниатюру плейлиста</string>
<string name="bookmark_playlist">Добавить плейлист в закладки</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_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>
<string name="resize_fit">Пригнать</string>
<string name="resize_fit">Подогнать</string>
<string name="resize_fill">Заполнить</string>
<string name="resize_zoom">Приблизить</string>
@ -406,8 +406,8 @@
<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="enable_disposed_exceptions_title">Сообщать об ошибках жизненного цикла</string>
<string name="enable_disposed_exceptions_summary">Принудительно сообщать о недоставляемых Rx-исключениях вне фрагмента или жизненного цикла после удаления</string>
<string name="import_export_title">Импорт/Экспорт</string>
<string name="import_title">Импорт</string>
@ -420,58 +420,83 @@
<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="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">Импорт подписок из SoundCloud набрав либо URL или ваш ID:
\n
<string name="import_soundcloud_instructions">Импорт подписок из SoundCloud набрав либо URL, либо ваш ID:
\n
\n1. Включите \"режим рабочего стола\" в браузере (сайт недоступен на телефоне)
\n2. Пройдите на: %1$s
\n3. Войдите, если надо
\n4. Скопируйте адрес из адресной строки.</string>
<string name="import_soundcloud_instructions_hint">вашID, soundcloud.com/вашID</string>
<string name="import_network_expensive_warning">Эта операция может использовать большие объемы сетевого трафика.
<string name="import_network_expensive_warning">Это действие может вызвать большой расход трафика.
\n
\nПродолжить?</string>
<string name="download_thumbnail_title">Загружать миниатюры</string>
<string name="download_thumbnail_summary">Отключите, чтобы перестать загружать миниатюры и начать экономить трафик и память. Изменение настройки очистит кеш изображений в памяти и на диске.</string>
<string name="thumbnail_cache_wipe_complete_notice">Кеш изображений очищен</string>
<string name="metadata_cache_wipe_title">Очистить кеш метаданных</string>
<string name="metadata_cache_wipe_complete_notice">Кеш метаданных очищен</string>
<string name="download_thumbnail_summary">Отключите, чтобы не загружать миниатюры и сэкономить трафик и память. Изменение настройки очистит кэш изображений в памяти и накопителе</string>
<string name="thumbnail_cache_wipe_complete_notice">Кэш изображений очищен</string>
<string name="metadata_cache_wipe_title">Очистить кэш метаданных</string>
<string name="metadata_cache_wipe_complete_notice">Кэш метаданных очищен</string>
<string name="playback_speed_control">Управление скоростью воспроизведения</string>
<string name="playback_tempo">Темп</string>
<string name="playback_pitch">Тон</string>
<string name="unhook_checkbox">Независимо (может вызвать искажение)</string>
<string name="unhook_checkbox">Независимо (искажения)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">По умолчанию</string>
<string name="metadata_cache_wipe_summary">Удалить все загруженные данные веб-страниц</string>
<string name="preferred_open_action_settings_summary">Действие по умолчанию при открытии контента — %s</string>
<string name="preferred_open_action_settings_summary">При открытии ссылки на контент — %s</string>
<string name="no_streams_available_download">Нет потоков, доступных для загрузки</string>
<string name="caption_setting_title">Титры</string>
<string name="toast_no_player">Нет приложения, установленного для воспроизведения этого файла</string>
<string name="toast_no_player">Приложение для воспроизведения этого файла не установлено</string>
<string name="caption_setting_description">Изменить размер заголовка и фоновые стили. Требуется перезапуск приложения для изменений.</string>
<string name="caption_setting_description">Изменить размер и фон титров. Изменения вступят в силу после перезапуска</string>
<string name="clear_views_history_title">Очистить историю просмотров</string>
<string name="delete_view_history_alert">Удалить всю историю просмотров.</string>
<string name="view_history_deleted">История просмотров была удалена.</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="clear_views_history_summary">Удаляет историю воспроизведенных потоков.</string>
<string name="delete_search_history_alert">Удалить всю историю поиска.</string>
<string name="search_history_deleted">Удалить историю поиска.</string>
<string name="clear_search_history_summary">Удалить историю поисковых запросов</string>
<string name="clear_views_history_summary">Удалить историю воспроизведённых потоков</string>
<string name="delete_search_history_alert">Вся история поиска будет удалена</string>
<string name="search_history_deleted">История поиска удалена.</string>
<string name="one_item_deleted">1 элемент удалён.</string>
<string name="app_license">NewPipe - программное обеспечение copyleft libre: вы можете использовать, изучать и улучшать его по своему усмотрению. В частности, вы можете распространять и/или изменять его в соответствии с GNU General Public License, опубликованным Free Software Foundation, либо версией 3 License, либо (по вашему выбору) любой более поздней версии.</string>
<string name="preferred_open_action_settings_title">Предпочтительное действие \'открытие\'</string>
<string name="import_settings">Желаете ли вы также импортировать настройки?</string>
<string name="app_license">NewPipe — свободное программное обеспечение: вы можете использовать, изучать и улучшать его по своему усмотрению. В частности, вы можете распространять и/или изменять его в соответствии с условиями GNU General Public License, опубликованной Free Software Foundation, либо версии 3, либо (по вашему выбору) любой более поздней версии.</string>
<string name="preferred_open_action_settings_title">При открытии контента</string>
<string name="import_settings">Хотите импортировать настройки?</string>
</resources>
<string name="privacy_policy_title">Политика конфиденциальности NewPipe</string>
<string name="privacy_policy_encouragement">Проект NewPipe очень серьёзно относится к вашей конфиденциальности. Поэтому приложение не собирает никаких данных без вашего согласия.
\nПолитика конфиденциальности NewPipe подробно объясняет, какие данные отправляются и хранятся при отправке отчёта о сбоях.</string>
<string name="read_privacy_policy">Прочитать политику</string>
<string name="start_accept_privacy_policy">В соответствии с Общим регламентом по защите данных ЕС (GDPR), обращаем ваше внимание на политику конфиденциальности NewPipe. Пожалуйста, внимательно ознакомьтесь с ней.
\nВам необходимо принять её условия, чтобы отправить нам отчёт об ошибке.</string>
<string name="accept">Принять</string>
<string name="decline">Отклонить</string>
<string name="limit_data_usage_none_description">Без ограничений</string>
<string name="limit_mobile_data_usage_title">Предел разрешения в мобильной сети</string>
<string name="channels">Каналы</string>
<string name="playlists">Плейлисты</string>
<string name="tracks">Дорожки</string>
<string name="users">Пользователи</string>
<string name="skip_silence_checkbox">Пропускать тишину</string>
<string name="playback_step">Шаг</string>
<string name="playback_reset">Сброс</string>
<string name="minimize_on_exit_title">При сворачивании плеера</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_popup_description">Плеер в окне</string>
</resources>

View File

@ -361,7 +361,7 @@
<string name="always_ask_open_action">Vždy sa opýtať</string>
<string name="preferred_player_fetcher_notification_title">Získavajú sa informácie…</string>
<string name="preferred_player_fetcher_notification_message">Požadovaný obsah sa načítava</string>
<string name="preferred_player_fetcher_notification_message">Načítanie požadované obsahu</string>
<string name="create_playlist">Vytvoriť nový zoznam skladieb</string>
<string name="delete_playlist">Vymazať zoznam skladieb</string>
@ -374,7 +374,7 @@
<string name="unbookmark_playlist">Odstrániť Záložku</string>
<string name="delete_playlist_prompt">Chcete odstrániť tento zoznam skladieb?</string>
<string name="playlist_creation_success">Zoznam skladieb bol vytvorený</string>
<string name="playlist_creation_success">Zoznam skladieb vytvorený</string>
<string name="playlist_add_stream_success">Pridané do zoznamu skladieb</string>
<string name="playlist_thumbnail_change_success">Miniatúra zoznamu skladieb bola zmenená</string>
<string name="playlist_delete_failure">Nemožno odstrániť zoznam skladieb</string>
@ -414,18 +414,18 @@
<string name="drawer_header_action_paceholder_text">Čoskoro sa tu niečo objaví ;D</string>
<string name="preferred_open_action_settings_title">Preferovaná akcia pri otvorení</string>
<string name="preferred_open_action_settings_title">Preferovaná akcia \'otvoriť\'</string>
<string name="preferred_open_action_settings_summary">Predvolená akcia pri otváraní obsahu — %s</string>
<string name="caption_auto_generated">Automaticky vygenerované</string>
<string name="caption_setting_title">Nastavenie titulkov</string>
<string name="caption_setting_description">Upravte mierku textu titulkov a štýly pozadia. Vyžaduje reštartovanie prehrávača.</string>
<string name="caption_setting_description">Upravte mierku textu titulkov a štýly pozadia. Vyžaduje reštart prehrávača.</string>
<string name="enable_leak_canary_title">Povoliť službu LeakCanary</string>
<string name="enable_leak_canary_summary">Monitorovanie pretečenia pamäte môže spôsobiť, že aplikácia nebude reagovať</string>
<string name="enable_disposed_exceptions_title">Nahlásiť mimocyklické chyby</string>
<string name="enable_disposed_exceptions_title">Nahlásiť mimo-cyklické chyby</string>
<string name="enable_disposed_exceptions_summary">Vynútenie hlásenia nedodržateľných výnimiek Rx, ktoré sa vyskytnú mimo časového cyklu fragmentu alebo aktivity po zlikvidovaní</string>
<string name="import_export_title">Import/Export
@ -451,14 +451,14 @@
\n2. Po výzve sa prihláste do svojho účtu
\n3. Sťahovanie by malo začať (to je exportovaný zoznam)
\n</string>
<string name="import_soundcloud_instructions">"Pre importovanie vašich SoundCloud dát, musíte poznať adresu URL alebo ID vašeho profilu. Ak poznáte jedno alebo druhé, zadajte ho do nižšie uvedeného vstupu a ste pripravení ísť.
\n
\nAk nepoznáte ani URL ani ID vašeho profilu, môžete postupovať nasledovne:
\n
\n1. V niektorom prehliadači povoľte režim \"desktop\" (web nie je dostupný pre mobilné zariadenia)
\n2. Prejdite na túto adresu URL: %1$s
\n3. Po výzve sa prihláste do svojho účtu
\n4. Skopírujte adresu URL, na ktorú ste boli presmerovaní (to je adresa vášho profilu)
<string name="import_soundcloud_instructions">"Importovať SoundCloud profil zadaním URL adresy alebo vášho ID:
\n
\nAk nepoznáte ani URL ani ID vašeho profilu, môžete postupovať nasledovne:
\n
\n1. V niektorom prehliadači povoľte režim \"desktop\" (web nie je dostupný pre mobilné zariadenia)
\n2. Prejdite na túto adresu URL: %1$s
\n3. Po výzve sa prihláste do svojho účtu
\n4. Skopírujte adresu URL, na ktorú ste boli presmerovaní (to je adresa vášho profilu).
\n"</string>
<string name="import_soundcloud_instructions_hint">ID,soundcloud.com/ID</string>
@ -483,4 +483,16 @@
<string name="one_item_deleted">1 položka bola vymazaná.</string>
<string name="app_license">"NewPipe je slobodný softvér pod licenciou copyleft. Môžete ho používať, študovať a vylepšovať ako len chcete. Konkrétne ho môžete šíriť a/alebo upravovať pod podmienkami Všeobecnej verejnej licencie GNU, ako ju publikuje Free Software Foundation, buď verzia 3 licencie, alebo (podľa vašej voľby) ktorákoľvek neskoršia verzia."</string>
<string name="privacy_policy_title">Ochrana osobných údajov v NewPipe</string>
<string name="privacy_policy_encouragement">NewPipe projekt berie vaše súkromie vážne. Preto aplikácia nezhromažďuje žiadne údaje bez vášho súhlasu.
\nNewPipe v ochrane súkromia podrobne vysvetľuje, aké údaje budú odoslané a uložené pri hlásení o páde.</string>
<string name="read_privacy_policy">Prečítajte si pravidlá ochrany osobných údajov</string>
<string name="import_settings">Chcete zároveň importovať aj nastavenia?</string>
<string name="start_accept_privacy_policy">V súlade s Európskym Všeobecným Nariadením o Ochrane Údajov (GDPR), chceme upriamiť vašu pozornosť na ochranu osobných údajov v NewPipe. Starostlivo si ich prečítajte. Musíte ich prijať pred nahlásením chyby.</string>
<string name="accept">Prijať</string>
<string name="decline">Odmietnuť</string>
<string name="limit_data_usage_none_description">Bez limitu</string>
<string name="limit_mobile_data_usage_title">Limitovať rozlíšenie pri použití mobilných dát</string>
</resources>

View File

@ -4,7 +4,7 @@
<string name="no_player_found">Ingen strömspelare hittades. Vill du installera VLC?</string>
<string name="install">Installera</string>
<string name="cancel">Avbryt</string>
<string name="open_in_browser">Öppna i bläddrare</string>
<string name="open_in_browser">Öppna i webbläsare</string>
<string name="open_in_popup_mode">Öppna i popup-läge</string>
<string name="share">Dela</string>
<string name="download">Ladda ner</string>
@ -12,7 +12,7 @@
<string name="settings">Inställningar</string>
<string name="did_you_mean">Menade du: %1$s ?</string>
<string name="share_dialog_title">Dela med</string>
<string name="choose_browser">Välj bläddrare</string>
<string name="choose_browser">Välj webbläsare</string>
<string name="screen_rotation">rotering</string>
<string name="use_external_video_player_title">Använd extern videospelare</string>
<string name="use_external_video_player_summary">Några upplösningar kommer INTE ha ljud när det här alternativet är aktiverat</string>
@ -25,11 +25,11 @@
<string name="download_path_summary">Genväg för att lagra nerladdade videor i</string>
<string name="download_path_dialog_title">Fyll i genvägen som videor ska laddas ner till</string>
<string name="download_path_audio_title">Plats för att ladda ner ljud till</string>
<string name="download_path_audio_summary">Plats för att lagra nerladdat ljud i</string>
<string name="download_path_audio_dialog_title">Ange nerladdningsplats för ljudfiler</string>
<string name="download_path_audio_title">Genväg för nerladdning av ljud</string>
<string name="download_path_audio_summary">Genväg för att lagra nerladdade ljudfiler i</string>
<string name="download_path_audio_dialog_title">Fyll i genvägen som ljudfiler ska laddas ner till</string>
<string name="autoplay_by_calling_app_summary">Spelar automatiskt upp en video när NewPipe öppnas av en annan app</string>
<string name="autoplay_by_calling_app_summary">Automatiskt spelar en video upp när NewPipe öppnas av en annan app</string>
<string name="default_resolution_title">Standardupplösning</string>
<string name="default_popup_resolution_title">Standardupplösning för popup</string>
<string name="show_higher_resolutions_title">Visa högre upplösningar</string>
@ -41,7 +41,7 @@
<string name="play_audio">Ljud</string>
<string name="default_audio_format_title">Standardformat för ljud</string>
<string name="default_video_format_title">Videoformat som föredras</string>
<string name="webm_description">WebM — fritt format</string>
<string name="webm_description">WebM — öppet format</string>
<string name="m4a_description">M4A — bättre kvalité</string>
<string name="theme_title">Tema</string>
<string name="light_theme_title">Ljus</string>
@ -57,7 +57,7 @@
<string name="download_dialog_title">Ladda ner</string>
<string name="next_video_title">Nästa video</string>
<string name="show_next_and_similar_title">Visa nästkommande och liknande videor</string>
<string name="show_next_and_similar_title">Visa \'nästkommande\' och \'liknande\' videor</string>
<string name="url_not_supported_toast">Webbadressen stöds inte</string>
<string name="search_language_title">Standard innehållsspråk</string>
<string name="settings_category_video_audio_title">Video &amp; Ljud</string>
@ -120,18 +120,18 @@
<string name="fragment_whats_new">Vad är nytt</string>
<string name="autoplay_by_calling_app_title">Autospela</string>
<string name="autoplay_by_calling_app_title">Spela upp automatiskt</string>
<string name="enable_search_history_title">Sök historik</string>
<string name="enable_search_history_summary">Spara sökfrågor lokalt</string>
<string name="enable_watch_history_title">Historik</string>
<string name="enable_watch_history_title">Historik &amp; Cacheminne</string>
<string name="enable_watch_history_summary">Håll koll på videor som du tittat på</string>
<string name="resume_on_audio_focus_gain_title">Fortsätt då fokus fås</string>
<string name="resume_on_audio_focus_gain_summary">Fortsätta spela efter avbrott (t.ex. telefonsamtal)</string>
<string name="show_hold_to_append_title">Visa \"Håll för att lägga till\" tips</string>
<string name="show_hold_to_append_title">Visa \"håll för att lägga till\" tips</string>
<string name="show_hold_to_append_summary">Visa tips när bakgrunds- eller popup-knappen trycks på sidan för videodetaljer</string>
<string name="settings_category_player_title">Spelare</string>
<string name="settings_category_player_behavior_title">Beteende</string>
<string name="settings_category_history_title">Historik</string>
<string name="settings_category_history_title">Historik &amp; Cacheminne</string>
<string name="background_player_append">Tillagd till bakgrunds-spelar kön</string>
<string name="popup_playing_append">Tillagd till popup-spelar kön</string>
<string name="playlist">Spellista</string>
@ -189,7 +189,7 @@
<string name="no_videos">Inga videor</string>
<plurals name="videos">
<item quantity="one">%s video</item>
<item quantity="other">%s videon</item>
<item quantity="other">%s videor</item>
</plurals>
<string name="start">Start</string>
@ -237,7 +237,7 @@
<string name="tab_about">Om</string>
<string name="tab_contributors">Medverkande</string>
<string name="tab_licenses">Licenser</string>
<string name="app_description">Gratis och enkel YouTube-app för Android.</string>
<string name="app_description">Öppet och enkel Android app för mediastreaming.</string>
<string name="view_on_github">Visa på GitHub</string>
<string name="app_license_title">NewPipes licens</string>
<string name="contribution_encouragement">Vad du än har för idéer gällande översättning, designändringar, kod rengöring eller riktigt tunga så är hjälp alltid välkommet. Ju mer som görs desto bättre blir det!</string>
@ -280,8 +280,193 @@
<string name="start_here_on_background">Börja här i bakgrunden</string>
<string name="start_here_on_popup">Börja här i popup</string>
<string name="donation_title">Donera</string>
<string name="donation_encouragement">NewPipe utvecklas av frivilliga som spenderar sin fritid på att ge dig den bästa användarupplevelsen. Nu är det tid att ge tillbaka för att säkerställa att utvecklarna kan göra NewPipe ännu bättre medan de njuter av en kopp kaffe!</string>
<string name="donation_encouragement">NewPipe utvecklas av frivilliga som spenderar sin fritid på att ge dig den bästa användarupplevelsen. Nu är det tid att ge tillbaka för att säkerställa att utvecklarna kan göra NewPipe ännu bättre medan de njuter av en kopp kaffe.</string>
<string name="give_back">Ge tillbaka</string>
<string name="website_title">Webbplats</string>
<string name="website_encouragement">För att få mer information och de senaste nyheterna om NewPipe, besök vår webbplats.</string>
</resources>
<string name="show_info">Visa info</string>
<string name="tab_bookmarks">Bokmärken</string>
<string name="controls_add_to_playlist_title">Lägga till</string>
<string name="use_inexact_seek_title">Använda snabb inexact sökning</string>
<string name="download_thumbnail_title">Ladda miniatyrer</string>
<string name="download_thumbnail_summary">Inaktivera för att stoppa alla miniatyrbilder från att ladda och spara på data och minnesanvändning. Ändring av detta kommer att rensa cache-minnet</string>
<string name="thumbnail_cache_wipe_complete_notice">Bild cacheminnet var rensad</string>
<string name="service_title">Tjänst</string>
<string name="settings_category_debug_title">Debug</string>
<string name="always">Alltid</string>
<string name="just_once">Bara en gång</string>
<string name="file">Fil</string>
<string name="toggle_orientation">Byta orientationen</string>
<string name="metadata_cache_wipe_title">Rensa metadatan i cacheminnet</string>
<string name="no_player_found_toast">Ingen strömspelare hittades (du kan installera VLC för att spela upp)</string>
<string name="controls_download_desc">Ladda ned sändning.</string>
<string name="use_inexact_seek_summary">"Ej exakt sökning tillåter spelaren att söka positioner snabbare med minskad precision "</string>
<string name="metadata_cache_wipe_summary">Ta bort alla cachade webbsidor</string>
<string name="metadata_cache_wipe_complete_notice">Metadata cache rensad</string>
<string name="auto_queue_title">"Köa nästa ström automatiskt "</string>
<string name="auto_queue_summary">Lägg automatiskt till en relaterad ström när du spelar den sista strömmen i en ej upprepad kö</string>
<string name="default_content_country_title">Standard innehåll land</string>
<string name="channels">Kanaler</string>
<string name="playlists">Spellistor</string>
<string name="tracks">Spår</string>
<string name="users">Användare</string>
<string name="switch_to_background">Växla till Bakgrunden</string>
<string name="switch_to_popup">Växla till popup</string>
<string name="switch_to_main">Växla till main</string>
<string name="import_data_title">Importera databas</string>
<string name="export_data_title">Exportera databas</string>
<string name="import_data_summary">Kommer att skriva över din nuvarande historik och prenumerationer</string>
<string name="export_data_summary">Exportera historik, prenumerationer och spellistor</string>
<string name="clear_views_history_title">Rensa visningshistorik</string>
<string name="clear_views_history_summary">Tar bort historiken för spelade videoklipp</string>
<string name="delete_view_history_alert">Ta bort hela visningshistoriken</string>
<string name="view_history_deleted">Visningshistorik borttagen.</string>
<string name="clear_search_history_title">Rensa sökhistorik</string>
<string name="clear_search_history_summary">Tar bort historiken för sökta nyckelord</string>
<string name="delete_search_history_alert">Ta bort hela sökhistoriken</string>
<string name="search_history_deleted">Sökhistorik borttagen.</string>
<string name="external_player_unsupported_link_type">Externa spelare stöder inte dessa typer av länkar</string>
<string name="invalid_url_toast">Ogiltig URL</string>
<string name="video_streams_empty">Inga videoströmmar hittades</string>
<string name="audio_streams_empty">Inga ljudspår hittades</string>
<string name="invalid_directory">Ogiltig katalog</string>
<string name="invalid_source">Ogiltig fil/innehålls källa</string>
<string name="invalid_file">Filen finns inte eller otillräcklig behörighet att läsa eller skriva till den</string>
<string name="file_name_empty_error">Filnamnet får inte vara tomt</string>
<string name="error_occurred_detail">Ett fel uppstod: %1$s</string>
<string name="no_streams_available_download">Inga strömmar är tillgängliga för nedladdning</string>
<string name="detail_drag_description">Dra för att ändra ordning</string>
<string name="create">Skapa</string>
<string name="delete_one">Ta bort en</string>
<string name="delete_all">Ta bort alla</string>
<string name="dismiss">Avfärda</string>
<string name="rename">Döp om</string>
<string name="one_item_deleted">1 objekt borttaget.</string>
<string name="toast_no_player">Ingen app installerad för att spela upp filen</string>
<string name="privacy_policy_title">NewPipes Sekretesspolicy</string>
<string name="privacy_policy_encouragement">"NewPipe projektet tar din integritet på största allvar. Appen samlar därför inte in några uppgifter utan ditt medgivande. NewPipes Sekretesspolicy förklarar i detalj vad för data som skickas och lagras när du skickar en kraschrapport."</string>
<string name="read_privacy_policy">Läs sekretesspolicy</string>
<string name="app_license">NewPipe är copyleft fri programvara: Du kan använda, studera, dela och förbättra den som du vill. Specifikt kan du distribuera och/eller modifiera det under villkoren för GNU General Public License som publicerats av Free Software Foundation, antingen version 3 av licensen, eller (om du så önskar) en senare version.</string>
<string name="delete_stream_history_prompt">Vill du ta bort det här föremålet från visningshistoriken?</string>
<string name="delete_all_history_prompt">Är du säker du vill ta bort alla föremål från historiken?</string>
<string name="title_last_played">Senast spelade</string>
<string name="title_most_played">Mest spelade</string>
<string name="export_complete_toast">Exporteringen har slutförts</string>
<string name="import_complete_toast">Importeringen har slutförts</string>
<string name="no_valid_zip_file">Ingen giltig ZIP-fil</string>
<string name="could_not_import_all_files">Varning: det gick inte att importera alla filer.</string>
<string name="override_current_data">Det här kommer skriva över dina nuvarande inställningar.</string>
<string name="import_settings">Vill du också importera inställningar?</string>
<string name="drawer_open">Öppna navigationspanelen</string>
<string name="drawer_close">Stäng navigationspanelen</string>
<string name="drawer_header_action_paceholder_text">Något kommer att visas här snart ;D</string>
<string name="preferred_open_action_settings_title">Föredra \'öppna\' åtgärden</string>
<string name="preferred_open_action_settings_summary">Standardåtgärden när du öppnar innehåll — %s</string>
<string name="video_player">Videospelare</string>
<string name="background_player">Bakgrundsspelare</string>
<string name="popup_player">Popup-spelare</string>
<string name="always_ask_open_action">Fråga alltid</string>
<string name="preferred_player_fetcher_notification_title">Hämtar information…</string>
<string name="preferred_player_fetcher_notification_message">Laddar begärt innehåll</string>
<string name="create_playlist">Skapa ny spellista</string>
<string name="delete_playlist">Radera spellista</string>
<string name="rename_playlist">Byt namn på spellista</string>
<string name="playlist_name_input">Namn</string>
<string name="append_playlist">Lägg till i spellistan</string>
<string name="set_as_playlist_thumbnail">Använd som spellistans miniatyrbild</string>
<string name="bookmark_playlist">Bokmärk spellistan</string>
<string name="unbookmark_playlist">Ta bort bokmärke</string>
<string name="delete_playlist_prompt">Vill du ta bort den här spellistan?</string>
<string name="playlist_creation_success">Spellistan skapades</string>
<string name="playlist_add_stream_success">Tillagad i spellistan</string>
<string name="playlist_thumbnail_change_success">"Spellistans miniatyrbild förändrades "</string>
<string name="playlist_delete_failure">Kunde inte ta bort spellistan</string>
<string name="caption_none">Ingen textning</string>
<string name="resize_fit">Passa</string>
<string name="resize_fill">Fyll</string>
<string name="resize_zoom">Zoom</string>
<string name="caption_auto_generated">Auto-genererade</string>
<string name="caption_setting_title">Textning</string>
<string name="caption_setting_description">Ändra spelaren textskala och bakgrundsstil. Kräver att appen startar om för att träder i kraft</string>
<string name="enable_leak_canary_title">Aktivera LeakCanary</string>
<string name="enable_leak_canary_summary">Minnesläcka övervakning kan orsaka att appen inte svarar under heap dumpning</string>
<string name="enable_disposed_exceptions_title">Rapportera Out-of-lifecycle fel</string>
<string name="enable_disposed_exceptions_summary">Tvinga rapportering av otillåtna Rx-undantag utanför fragment eller aktivitetslivscykel efter uppstädning</string>
<string name="import_export_title">Importera/Exportera</string>
<string name="import_title">Importera</string>
<string name="import_from">Importera från</string>
<string name="export_to">Exportera till</string>
<string name="import_ongoing">Importera…</string>
<string name="export_ongoing">Exporterar…</string>
<string name="import_file_title">Importera fil</string>
<string name="previous_export">Föregående export</string>
<string name="subscriptions_import_unsuccessful">Kunde inte importera prenumerationer</string>
<string name="subscriptions_export_unsuccessful">kunde inte exportera prenumerationer</string>
<string name="import_youtube_instructions">Importera YouTube-prenumerationer genom att hämta export-filen:
\n
\n1. Gå till följande webbadress: %1$s
\n2. Logga in när du blir tillfrågad
\n3. En nedladdning ska starta (det är export-filen)</string>
<string name="import_soundcloud_instructions">Importera en SoundCloud profil genom att skriva in webbadressen eller ditt ID:
\n
\n1. Aktivera \"skrivbordsläge\" i en webbläsare (sidan är inte tillgänglig för mobila enheter)
\n2. Gå till följande webbadress: %1$s
\n3. Logga in när du blir tillfrågad
\n4. Kopiera profilens webbadress som du blev omdirigerad till.</string>
<string name="import_soundcloud_instructions_hint">dittID, soundcloud.com/dittid</string>
<string name="import_network_expensive_warning">Tänk på att operationer kan vara nätverks intensiv.
\n
\nVill du fortsätta?</string>
<string name="playback_speed_control">Uppspelningshastighet Kontroller</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">Pitch</string>
<string name="unhook_checkbox">Avlänka (kan orsaka förvrängning)</string>
<string name="skip_silence_checkbox">Snabbspola vid frånvaro av ljud</string>
<string name="playback_step">Steg</string>
<string name="playback_reset">Återställ</string>
<string name="start_accept_privacy_policy">För att uppfylla den Europeiska dataskyddsförordningen (GDPR), uppmärksammar vi NewPipes sekretesspolicy. Läs den noggrant. Du måste acceptera det om du vill skicka felrapporten.</string>
<string name="accept">Acceptera</string>
<string name="decline">Avböj</string>
<string name="limit_data_usage_none_description">Ingen gräns</string>
<string name="limit_mobile_data_usage_title">Begränsa upplösningen när du använder mobil data</string>
<string name="minimize_on_exit_title">"Minimera vid växling av applikationen "</string>
<string name="minimize_on_exit_summary">Åtgärd när du växlar till andra applikationer från huvudvideospelare — %s</string>
<string name="minimize_on_exit_none_description">Ingen</string>
<string name="minimize_on_exit_background_description">Minimera till Bakgrunds-spelare</string>
<string name="minimize_on_exit_popup_description">Minimera till popup-spelare</string>
</resources>

View File

@ -119,16 +119,16 @@
<string name="could_not_setup_download_menu">İndirme menüsü ayarlanamadı</string>
<string name="open_in_popup_mode">ılır pencere kipinde aç</string>
<string name="popup_mode_share_menu_title">NewPipe açılır pencere kipi</string>
<string name="open_in_popup_mode">ılır oynatıcı kipinde aç</string>
<string name="popup_mode_share_menu_title">NewPipe açılır oynatıcı kipi</string>
<string name="default_popup_resolution_title">Öntanımlıılır pencere çözünürlüğü</string>
<string name="default_popup_resolution_title">Öntanımlıılır oynatıcı çözünürlüğü</string>
<string name="show_higher_resolutions_title">Daha yüksek çözünürlükleri göster</string>
<string name="show_higher_resolutions_summary">Yalnızca bazı aygıtlar 2K/4K video oynatmayı destekler</string>
<string name="default_video_format_title">Öntanımlı video biçimi</string>
<string name="black_theme_title">Siyah</string>
<string name="popup_playing_toast">ılır pencere kipinde oynatılıyor</string>
<string name="popup_playing_toast">ılır oynatıcı kipinde oynatılıyor</string>
<string name="all">Tümü</string>
<string name="channel">Kanal</string>
<string name="yes">Evet</string>
@ -148,7 +148,7 @@
<string name="short_million">M</string>
<string name="short_billion">B</string>
<string name="msg_popup_permission">Bu izin açılır pencere kipinde
<string name="msg_popup_permission">Bu izin açılır oynatıcı kipinde
\naçmak için gerekli</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
@ -156,16 +156,16 @@
<string name="recaptcha_request_toast">reCAPTCHA Formu istendi</string>
<string name="controls_background_title">Arka Plan</string>
<string name="controls_popup_title">ılır Pencere</string>
<string name="controls_popup_title">ılır Oynatıcı</string>
<string name="filter">Süz</string>
<string name="refresh">Yenile</string>
<string name="clear">Temizle</string>
<string name="popup_remember_size_pos_title">ılır pencere boyutunu ve konumunu hatırla</string>
<string name="popup_remember_size_pos_summary">ılır pencerenin ayarlandığı son boyutu ve konumu hatırla</string>
<string name="popup_remember_size_pos_title">ılır oynatıcı boyutunu ve konumunu hatırla</string>
<string name="popup_remember_size_pos_summary">ılır oynatıcının ayarlandığı son boyutu ve konumu hatırla</string>
<string name="settings_category_popup_title">ılır Pencere</string>
<string name="settings_category_popup_title">ılır Oynatıcı</string>
<string name="popup_resizing_indicator_title">Boyutlandırılıyor</string>
<string name="use_external_video_player_summary">Bu seçenek etkinken bazı çözünürlüklerin sesi olmayacaktır</string>
@ -224,7 +224,7 @@
<string name="undo">Geri Al</string>
<string name="notification_channel_name">NewPipe Bildirimi</string>
<string name="notification_channel_description">NewPipe Arka Plan ve Açılır Pencere Oynatıcıları için Bildirimler</string>
<string name="notification_channel_description">NewPipe Arka Plan ve Açılır Oynatıcılar için Bildirimler</string>
<string name="search_no_results">Sonuç yok</string>
<string name="empty_subscription_feed_subtitle">Burada Cırcır Böceklerinden Başka Şey Yok</string>
@ -257,9 +257,9 @@
<string name="item_deleted">Öge silindi</string>
<string name="delete_item_search_history">Bu içeriği arama geçmişinden silmek istiyor musunuz?</string>
<string name="show_hold_to_append_title">\"Kuyruğa almak İçin bas\" ipucunu göster</string>
<string name="show_hold_to_append_summary">Video ayrıntıları sayfasında arka plan veya açılır pencere düğmesine basıldığında ipucu göster</string>
<string name="show_hold_to_append_summary">Video ayrıntıları sayfasında arka plan veya açılır oynatıcı düğmesine basıldığında ipucu göster</string>
<string name="background_player_append">Arka plan oynatıcıda kuyruğa eklendi</string>
<string name="popup_playing_append">ılır pencere oynatıcıda kuyruğa eklendi</string>
<string name="popup_playing_append">ılır oynatıcıda kuyruğa eklendi</string>
<string name="play_all">Tümünü Oynat</string>
<string name="unknown_content">[Bilinmeyen]</string>
@ -283,16 +283,16 @@
<string name="top_50">En İyi 50</string>
<string name="new_and_hot">Yeni ve sıcak</string>
<string name="title_activity_background_player">Arka Plan Oynatıcı</string>
<string name="title_activity_popup_player">ılır Pencere Oynatıcı</string>
<string name="title_activity_popup_player">ılır Oynatıcı</string>
<string name="play_queue_remove">Kaldır</string>
<string name="play_queue_stream_detail">Ayrıntılar</string>
<string name="play_queue_audio_settings">Ses Ayarları</string>
<string name="hold_to_append">Kuyruğa Almak İçin Bas</string>
<string name="enqueue_on_background">Arka Planda Kuyruğa Al</string>
<string name="enqueue_on_popup">ılır Pencerede Kuyruğa Al</string>
<string name="enqueue_on_popup">ılır Oynatıcıda Kuyruğa Al</string>
<string name="start_here_on_main">Burada Oynatmaya Başla</string>
<string name="start_here_on_background">Burada Arka Planda Başlat</string>
<string name="start_here_on_popup">Burada Açılır Pencerede Başlat</string>
<string name="start_here_on_popup">Burada Açılır Oynatıcıda Başlat</string>
<string name="donation_title">Bağış yapın</string>
<string name="donation_encouragement">NewPipe, size en iyi deneyimi sunmak için zamanını harcayan gönüllüler tarafından geliştirilmiştir. Bir fincan kahveyi yudumlarken NewPipe\'ı daha da iyi yapmaları için geliştiricilere yardımcı olun.</string>
<string name="give_back">Geri ver</string>
@ -302,7 +302,7 @@
<string name="service_title">Hizmet</string>
<string name="toggle_orientation">Yönelimi Değiştir</string>
<string name="switch_to_background">Arka Plana Geç</string>
<string name="switch_to_popup">ılır Pencereye Geç</string>
<string name="switch_to_popup">ılır Oynatıcıya Geç</string>
<string name="switch_to_main">Ana Görünüme Geç</string>
<string name="drawer_open">Çekmeceyi Aç</string>
@ -318,7 +318,7 @@
<string name="video_player">Video oynatıcı</string>
<string name="background_player">Arka plan oynatıcı</string>
<string name="popup_player">ılır pencere oynatıcı</string>
<string name="popup_player">ılır oynatıcı</string>
<string name="always_ask_player">Her zaman sor</string>
<string name="preferred_player_fetcher_notification_title">Bilgi alınıyor…</string>
@ -326,7 +326,7 @@
<string name="import_data_title">Veri tabanını içe aktar</string>
<string name="export_data_title">Veri tabanını dışa aktar</string>
<string name="import_data_summary">Şu anki geçmişinizi ve aboneliklerinizi geçersiz kılar</string>
<string name="export_data_summary">Geçmişi, abonelikleri ve oynatma listelerini dışa aktar.</string>
<string name="export_data_summary">Geçmişi, abonelikleri ve oynatma listelerini dışa aktar</string>
<string name="export_complete_toast">Dışa aktarım bitti</string>
<string name="import_complete_toast">İçe aktarım bitti</string>
<string name="no_valid_zip_file">Geçerli ZIP dosyası yok</string>
@ -395,7 +395,7 @@
<string name="use_inexact_seek_title">Hızlı isabetsiz konumlama kullan</string>
<string name="use_inexact_seek_summary">İsabetsiz konumlama, oynatıcının azaltılmış kesinlikle daha hızlı konumlama yapmasını sağlar</string>
<string name="auto_queue_title">Sonraki akışı kendiliğinden kuyruğa ekle</string>
<string name="auto_queue_summary">Yinelemeyen bir kuyruktaki son akış oynatılırken ilgili bir akışı kendiliğinden sonuna ekle.</string>
<string name="auto_queue_summary">Yinelemeyen bir kuyruktaki son akış oynatılırken ilgili bir akışı kendiliğinden sonuna ekle</string>
<string name="live_sync">EŞZAMANLA</string>
<string name="file">Dosya</string>
@ -437,7 +437,7 @@
\n
\nDevam etmek istiyor musunuz?</string>
<string name="download_thumbnail_title">Küçük resimleri yükle</string>
<string name="download_thumbnail_summary">Küçük resimlerin hepsinin yüklenmesini engellemek ve bellek ve veri kullanımını azaltmak için devre dışı bırakın. Bunu değiştirmek, hem bellekteki hem de diskteki resim önbelleğini temizler.</string>
<string name="download_thumbnail_summary">Küçük resimlerin hepsinin yüklenmesini engellemek ve bellek ve veri kullanımını azaltmak için devre dışı bırakın. Bunu değiştirmek, hem bellekteki hem de diskteki resim önbelleğini temizler</string>
<string name="thumbnail_cache_wipe_complete_notice">Resim önbelleği temizlendi</string>
<string name="metadata_cache_wipe_title">Önbelleklenmiş üst veriyi temizle</string>
<string name="metadata_cache_wipe_summary">Önbelleklenmiş tüm web sayfası verisini kaldır</string>
@ -446,7 +446,7 @@
<string name="playback_tempo">Hız</string>
<string name="playback_default">Öntanımlı</string>
<string name="playback_pitch">Ses titreşimi</string>
<string name="unhook_checkbox">Çengeli Çıkar (bozukluğa neden olabilir)</string>
<string name="unhook_checkbox">Bağlantıyı kes (bozulmalara neden olabilir)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="no_streams_available_download">İndirmeye uygun akış yok</string>
@ -454,17 +454,17 @@
<string name="preferred_open_action_settings_summary">İçerik açılırken öntanımlı eylem — %s</string>
<string name="caption_setting_title">Ek Açıklamalar</string>
<string name="caption_setting_description">Oynatıcı ek açıklamalar metin boyutunu ve arka plan biçimlerini düzenle. Etki için uygulamayı yeniden başlatmak gerekir.</string>
<string name="caption_setting_description">Oynatıcı ek açıklamalar metin boyutunu ve arka plan biçimlerini düzenle. Etki için uygulamayı yeniden başlatmak gerekir</string>
<string name="toast_no_player">Bu dosyayı oynatmak için kurulan uygulama yok</string>
<string name="clear_views_history_title">İzleme geçmişini temizle</string>
<string name="clear_views_history_summary">Oynatılan akışların geçmişini siler.</string>
<string name="delete_view_history_alert">Tüm izleme geçmişini sil.</string>
<string name="clear_views_history_summary">Oynatılan akışların geçmişini siler</string>
<string name="delete_view_history_alert">Tüm izleme geçmişini sil</string>
<string name="view_history_deleted">İzleme geçmişi silindi.</string>
<string name="clear_search_history_title">Arama geçmişini temizle</string>
<string name="clear_search_history_summary">Aranan anahtar sözcüklerin geçmişini siler.</string>
<string name="delete_search_history_alert">Tüm arama geçmişini sil.</string>
<string name="clear_search_history_summary">Aranan anahtar sözcüklerin geçmişini siler</string>
<string name="delete_search_history_alert">Tüm arama geçmişini sil</string>
<string name="search_history_deleted">Arama geçmişi silindi.</string>
<string name="one_item_deleted">1 öge silindi.</string>
@ -481,4 +481,18 @@
<string name="decline">Kabul etme</string>
<string name="limit_data_usage_none_description">Sınırsız</string>
<string name="limit_mobile_data_usage_title">Mobil veri kullanırken çözünürlüğü sınırla</string>
<string name="minimize_on_exit_title">Uygulama geçişinde küçült</string>
<string name="minimize_on_exit_summary">Ana video oynatıcıdan başka bir uygulamaya geçişteki eylem — %s</string>
<string name="minimize_on_exit_none_description">Yok</string>
<string name="minimize_on_exit_background_description">Arka plan oynatıcıya küçült</string>
<string name="minimize_on_exit_popup_description">ılır oynatıcıya küçült</string>
<string name="skip_silence_checkbox">Sessizlik sırasında hızlı ileri</string>
<string name="playback_step">Adım</string>
<string name="playback_reset">Sıfırla</string>
<string name="channels">Kanallar</string>
<string name="playlists">Oynatma Listeleri</string>
<string name="tracks">Parçalar</string>
<string name="users">Kullanıcılar</string>
</resources>

View File

@ -192,7 +192,7 @@
<string name="import_data_title">Імпортувати базу</string>
<string name="export_data_title">Експортувати базу</string>
<string name="import_data_summary">Це перепише чинну історію та підписання</string>
<string name="export_data_summary">Експортувати історію, підписання та плейлисти.</string>
<string name="export_data_summary">Експортувати історію, підписання та плейлисти</string>
<string name="player_stream_failure">Неможливо відтворити цей стрим</string>
<string name="player_recoverable_failure">Відновлююсь після помилки програвача</string>
<string name="invalid_url_toast">Помилкова URL</string>
@ -387,7 +387,7 @@
<string name="use_inexact_seek_title">Використовувати неточне шукання</string>
<string name="use_inexact_seek_summary">Неточне шукання дозволяє програвачеві рухатися позиціями швидше, проте з меншою точністю</string>
<string name="auto_queue_title">Автоматично додати до черги наступний стрим</string>
<string name="auto_queue_summary">Автоматично додавати пов\'язаний стрим під час відтворення останнього у неповторювальній черзі.</string>
<string name="auto_queue_summary">Автоматично додавати пов\'язаний стрим під час відтворення останнього у неповторювальній черзі</string>
<string name="live_sync">СИНХРОНІЗАЦІЯ</string>
<string name="file">Теки</string>
@ -429,7 +429,7 @@
\n
\nПродовжуватимете?</string>
<string name="download_thumbnail_title">Завантажити ескізи</string>
<string name="download_thumbnail_summary">Відключити аби зупинити завантаження ескізів та заощадити використання ресурсів та пам\'яті. Увімкнення функції призведе до повного вичищення кешу зображень.</string>
<string name="download_thumbnail_summary">Відключити аби зупинити завантаження ескізів та заощадити використання ресурсів та пам\'яті. Увімкнення функції призведе до повного вичищення кешу зображень</string>
<string name="thumbnail_cache_wipe_complete_notice">Кеш зображень стерто</string>
<string name="metadata_cache_wipe_title">Стерти кеш метаданих</string>
<string name="metadata_cache_wipe_summary">"Усунути всі кешовані дані веб-сторінки "</string>
@ -446,17 +446,17 @@
<string name="preferred_open_action_settings_summary">Типова дія під час відкриття вмісту — %s</string>
<string name="caption_setting_title">Субтитри</string>
<string name="caption_setting_description">Змінення маштабу тексту субтитрів та фонових стилів. Увімкнення функції потребує перезавантаження застосунку.</string>
<string name="caption_setting_description">Змінення маштабу тексту субтитрів та фонових стилів. Увімкнення функції потребує перезавантаження застосунку</string>
<string name="toast_no_player">Не знайдено відповідного застосунку для відтворення цього файла</string>
<string name="clear_views_history_title">Очистити історію переглядів</string>
<string name="clear_views_history_summary">Видаляє історію відтворень.</string>
<string name="delete_view_history_alert">Видалити всю історію переглядів.</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="clear_search_history_summary">Видаляє історію шуканих ключових слів</string>
<string name="delete_search_history_alert">Видалити всю пошукову історію</string>
<string name="search_history_deleted">Пошукову історію було видалено.</string>
<string name="one_item_deleted">Видалено один фраґмент.</string>
@ -473,4 +473,18 @@
<string name="decline">Відхилити</string>
<string name="limit_data_usage_none_description">Безмежно</string>
<string name="limit_mobile_data_usage_title">Обмежити роздільну здатність під час користування мобільним інтернетом</string>
<string name="skip_silence_checkbox">Перемотувати підчас тиші</string>
<string name="playback_step">Крок</string>
<string name="playback_reset">Скинути</string>
<string name="minimize_on_exit_title">Зменшити при перемкненні застосунку</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_popup_description">Зменшити до віконного програвачу</string>
<string name="channels">Канали</string>
<string name="playlists">Плейлисти</string>
<string name="tracks">Стежки</string>
<string name="users">Користувачі</string>
</resources>

View File

@ -4,10 +4,19 @@
<string name="upload_date_text">کوشائع ہوا</string>
<string name="install">انسٹال</string>
<string name="cancel">منسوخ کریں</string>
<string name="share">اشتراک کریں</string>
<string name="share">بانٹیں</string>
<string name="download">ڈاؤن لوڈکریں</string>
<string name="search">تلاش کریں</string>
<string name="did_you_mean">نتائج دکھارہاہےبرائے</string>
<string name="did_you_mean">کیا آپ کا مطلب تھا</string>
<string name="open_in_browser">انٹرنیٹ میں کھولیں</string>
<string name="settings">ترتیبات</string>
<string name="no_player_found">کوئیstream پلیئر نہیں ملا.کیا آپ VLC انسٹال کرنا چاہتے ہیں؟</string>
<string name="no_player_found_toast">کوئیstream پلیئر نہیں ملا.(آپ VLC انسٹال کر سکتے ہیے)</string>
<string name="open_in_popup_mode">PopUp موڈ میں کھولیں</string>
<string name="controls_download_desc">سٹریم فائل ڈاؤن لوڈ کریں.</string>
<string name="share_dialog_title">کے ساتھ بانٹیں</string>
<string name="choose_browser">Internet Browser منتخب کریں
\n</string>
<string name="screen_rotation">rotation کرو</string>
<string name="use_external_video_player_title">external video player استعمال کریں</string>
</resources>

View File

@ -34,7 +34,7 @@
<string name="download_dialog_title">下载</string>
<string name="next_video_title">即将播放</string>
<string name="show_next_and_similar_title">显示下一部和相的视频</string>
<string name="show_next_and_similar_title">显示下一部和相的视频</string>
<string name="url_not_supported_toast">不支援此网址</string>
<string name="search_language_title">默认内容语言</string>
<string name="settings_category_video_audio_title">视频和音频</string>
@ -88,7 +88,7 @@
<string name="retry">重试</string>
<string name="storage_permission_denied">无权访问存储空间</string>
<string name="autoplay_by_calling_app_title">自动播放</string>
<string name="autoplay_by_calling_app_summary">NewPipe被其他应用调用时自动播放视频</string>
<string name="autoplay_by_calling_app_summary">NewPipe被其他应用调用时自动播放视频</string>
<string name="duration_live">直播</string>
<string name="main_bg_subtitle">点击搜索开始NewPipe</string>
@ -155,7 +155,7 @@
<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_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>
@ -204,7 +204,7 @@
<string name="tab_about">关于</string>
<string name="tab_contributors">贡献者</string>
<string name="tab_licenses">许可</string>
<string name="app_description">一款开源、轻量级的安卓 YouTube 客户端。</string>
<string name="app_description">开源的轻量级流媒体Android客户端。</string>
<string name="view_on_github">在 GitHub 上查看</string>
<string name="app_license_title">NewPipe 许可</string>
<string name="contribution_encouragement">无论你有什么想法:翻译程序,改进设计,优化代码或是想做出大量修改——我们都随时欢迎。做得越多它将变得越好!</string>
@ -230,7 +230,7 @@
<string name="unknown_content">[未知]</string>
<string name="player_stream_failure">播放此流媒体失败</string>
<string name="player_stream_failure">无法播放此流媒体</string>
<string name="player_unrecoverable_failure">发生无法解决的播放器错误</string>
<string name="search_no_results">没有结果</string>
<string name="empty_subscription_feed_subtitle">空空如也</string>
@ -268,8 +268,8 @@
<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="show_hold_to_append_title">显示长按以扩展的提示</string>
<string name="auto_queue_summary">当播放完非循环列表中的最后一个视频时,自动加入一个相关视频到播放列表</string>
<string name="show_hold_to_append_title">显示\"长按添加\"提示</string>
<string name="show_hold_to_append_summary">当视频详情页中的背景或悬浮按钮被按下的时候显示提示</string>
<string name="default_content_country_title">默认内容国家</string>
<string name="service_title">服务</string>
@ -308,10 +308,10 @@
<string name="rename">重命名</string>
<string name="donation_title">捐赠</string>
<string name="donation_encouragement">NewPipe 是一群志愿者花费业余时间开发的,目的是给你们带来最佳体验。你的一点心意,可以让开发者们可以享受一杯 Java 的咖啡的同时让 NewPipe 变得更好用!</string>
<string name="donation_encouragement">NewPipe 是一群志愿者花费业余时间开发的,目的是为您带来最佳体验。你的一点心意,可以让开发者们在享受一杯 Java 的咖啡的同时让 NewPipe 变得更好用。</string>
<string name="give_back">捐赠</string>
<string name="website_title">网页</string>
<string name="website_encouragement">为了获得 NewPipe 的更多信息和最新消息请访问我们的网站</string>
<string name="website_encouragement">访问 NewPipe 网站以获取更多的信息和新闻</string>
<string name="delete_item_search_history">你想从搜索历史中删除此项吗?</string>
<string name="delete_stream_history_prompt">你想从观看历史中删除此项吗?</string>
<string name="delete_all_history_prompt">确实要删除历史记录的所有项目吗?</string>
@ -335,7 +335,7 @@
<string name="kiosk">Kiosk</string>
<string name="trending">流行的</string>
<string name="top_50">Top 50</string>
<string name="top_50">50</string>
<string name="new_and_hot">最新 &amp; 最热</string>
<string name="title_activity_background_player">后台播放器</string>
<string name="title_activity_popup_player">悬浮窗播放器</string>
@ -351,10 +351,10 @@
<string name="drawer_open">打开侧栏菜单</string>
<string name="drawer_close">关闭侧栏菜单</string>
<string name="drawer_header_action_paceholder_text">马上就好 ;D</string>
<string name="drawer_header_action_paceholder_text">精彩内容即将呈现 ;D</string>
<string name="preferred_open_action_settings_title">偏好的打开动作</string>
<string name="preferred_open_action_settings_title">偏好\"打开\"动作</string>
<string name="preferred_open_action_settings_summary">打开内容的默认动作 — %s</string>
<string name="video_player">视频播放器</string>
@ -363,7 +363,7 @@
<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="preferred_player_fetcher_notification_message">正在加载请求内容</string>
<string name="create_playlist">创建新播放列表</string>
<string name="delete_playlist">删除播放列表</string>
@ -379,7 +379,7 @@
<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_delete_failure">无法删除播放列表</string>
<string name="caption_none">无字幕</string>
@ -394,10 +394,10 @@
<string name="larger_caption_font_size">大字体</string>
<string name="enable_leak_canary_title">启用 LeakCanary</string>
<string name="enable_leak_canary_summary">heap dumping 的时候,内存泄露监测可能会导致应用未响应</string>
<string name="enable_leak_canary_summary">内存泄露监测可能会在heap dumping时导致应用失去响应</string>
<string name="enable_disposed_exceptions_title">报告生命周期外的错误</string>
<string name="enable_disposed_exceptions_summary">处理之后,强制报告无法送达的、发生在Fragment或activity生命周期之外的Rx异常</string>
<string name="enable_disposed_exceptions_summary">处理无法送达的、发生在Fragment或activity生命周期之外的Rx异常后强制报告</string>
<string name="import_export_title">导入/导出</string>
<string name="import_title">导入</string>
@ -410,22 +410,22 @@
<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="subscriptions_import_unsuccessful">无法导入订阅</string>
<string name="subscriptions_export_unsuccessful">无法导出订阅</string>
<string name="import_youtube_instructions">为了导入你的 YouTube 订阅,你需要一个导出文件,按照下面的步骤来下载此文件:
\n1. 浏览器打开链接:%1$s
\n2. 登录你的账户
\n3. 下载应该马上开始(这个就是你的导出文件)</string>
<string name="import_soundcloud_instructions">为了导入你的 SoundCloud你需要知道你的个人页链接或者id如果你知道只要在下面的输入框中输入任意一种就OK了。
\n如果你不知道,你需要这么做:
\n1. 浏览器打开链接:%1$s
\n2. 在某些手机浏览器中你得开启桌面模式desktop mode
\n3. 复制你跳转到的链接(这个就是你的个人页链接)</string>
<string name="import_youtube_instructions">通过下载导出文件导入 YouTube 订阅:
\n1. 在浏览器打开URL%1$s
\n2. 登录账户
\n3. 下载应该马上开始(这个就是导出文件)</string>
<string name="import_soundcloud_instructions">通过输入URL或您的ID导入SoundCloud配置
\n1. 在浏览器中开启\"桌面模式\"(该网站不适用于移动设备)
\n2. 打开URL%1$s
\n3. 登录账号
\n3. 复制重定向后的URL。</string>
<string name="import_soundcloud_instructions_hint">你的ID 或 soundcloud.com/你的ID</string>
<string name="import_network_expensive_warning">请注意这个操作可能耗费大量流量。
\n你想继续吗?</string>
<string name="import_network_expensive_warning">请注意该操作可能消耗大量网络流量。
\n您希望继续吗?</string>
<string name="playback_speed_control">播放速度控制</string>
<string name="playback_tempo">速度</string>
@ -433,9 +433,35 @@
<string name="unhook_checkbox">Unhook可能导致失真</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">默认</string>
<string name="toast_no_player">找不到能播放此文件的播放器</string>
<string name="toast_no_player">未安装能播放此文件的应用</string>
<string name="caption_setting_title">字幕</string>
<string name="caption_setting_description">修改播放器的字幕文本大小和背景样式。需要重启播放器以生效。</string>
<string name="caption_setting_description">修改播放器的字幕文本大小和背景样式。需要重启应用程序以生效。</string>
<string name="clear_views_history_title">清除观看记录</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="search_history_deleted">搜索记录已删除。</string>
<string name="one_item_deleted">已删除1项。</string>
<string name="privacy_policy_title">NewPipe的隐私策略</string>
<string name="privacy_policy_encouragement">NewPipe 项目非常重视您的隐私。因此, 未经您的同意,应用程序不会收集任何您的数据。 NewPipe 的隐私策略详细解释了您在发送崩溃报告时会发送和存储的哪些数据。</string>
<string name="read_privacy_policy">阅读隐私策略</string>
<string name="app_license">NewPipe 是 copyleft 的自由软件: 你可以按照自己的意愿使用、学习、分享和改进它。具体地说, 您可以根据自由软件基金会发布的 GNU 通用公共许可证的条款第3版或者任何更高版本, 重新发布和/或修改本软件。</string>
<string name="import_settings">您是否希望同时导入设置?</string>
<string name="start_accept_privacy_policy">为了遵守欧洲通用数据保护条例GDPR我们提醒您注意NewPipe的隐私政策。 请仔细阅读。
\n您必须接受它才能向我们发送错误报告。</string>
<string name="accept">接受</string>
<string name="decline">拒绝</string>
<string name="limit_data_usage_none_description">不限制</string>
<string name="limit_mobile_data_usage_title">使用移动数据时限制分辨率</string>
<string name="channels">更多频道</string>
<string name="playlists">更多频道</string>
<string name="users">用户们</string>
</resources>

View File

@ -271,7 +271,7 @@
<string name="import_data_title">匯入資料庫</string>
<string name="export_data_title">匯出資料庫</string>
<string name="import_data_summary">將覆蓋您目前的歷史記錄和訂閱</string>
<string name="export_data_summary">匯出歷史記錄、訂閱和播放清單</string>
<string name="export_data_summary">匯出歷史記錄、訂閱和播放清單</string>
<string name="give_back">返回</string>
<string name="website_encouragement">欲了解更多關於 NewPipe 的資訊和新聞,請造訪我們的網站。</string>
<string name="main_page_content">首頁內容</string>
@ -382,7 +382,7 @@
<string name="use_inexact_seek_title">使用粗略但快速的尋找</string>
<string name="use_inexact_seek_summary">粗略尋找讓播放器更快找到影片的進度位置</string>
<string name="auto_queue_title">自動播放隊列中下一部影片</string>
<string name="auto_queue_summary">在非重複播放佇列中的最後一個串流上開始播放時,自動附上相關串流</string>
<string name="auto_queue_summary">在非重複播放佇列中的最後一個串流上開始播放時,自動附上相關串流</string>
<string name="live_sync">同步</string>
<string name="file">檔案</string>
@ -424,7 +424,7 @@
\n3. 詢問時登入到您的帳號
\n4. 複製您被重新導向到的網址。</string>
<string name="download_thumbnail_title">載入縮圖</string>
<string name="download_thumbnail_summary">停用後NewPipe將不再載入縮圖減少數據使用與騰空儲存空間亦會清除記憶體和磁碟上的縮圖快取</string>
<string name="download_thumbnail_summary">停用後NewPipe將不再載入縮圖減少數據使用與騰空儲存空間亦會清除記憶體和磁碟上的縮圖快取</string>
<string name="thumbnail_cache_wipe_complete_notice">已清除圖片快取</string>
<string name="metadata_cache_wipe_title">抹除快取中介資料</string>
<string name="metadata_cache_wipe_summary">移除所有網頁的快取資料</string>
@ -432,7 +432,7 @@
<string name="playback_speed_control">播放速度控制</string>
<string name="playback_tempo">節拍</string>
<string name="playback_pitch">間距</string>
<string name="unhook_checkbox">解除 (可能導致失真)</string>
<string name="unhook_checkbox">解除連結(可能導致失真)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">預設</string>
<string name="preferred_open_action_settings_title">偏好的「開啟」動作</string>
@ -441,17 +441,17 @@
<string name="no_streams_available_download">沒有可供下載的串流</string>
<string name="caption_setting_title">字幕</string>
<string name="caption_setting_description">調整播放器字幕大小與背景樣式。必須重新啟動應用程式才會生效</string>
<string name="caption_setting_description">調整播放器字幕大小與背景樣式。必須重新啟動應用程式才會生效</string>
<string name="toast_no_player">未安裝可播放此檔案的應用程式</string>
<string name="clear_views_history_title">清除觀看歷史</string>
<string name="clear_views_history_summary">刪除播放過的串流歷史</string>
<string name="delete_view_history_alert">刪除全部的觀看歷史</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="clear_search_history_summary">刪除搜尋關鍵字的歷史</string>
<string name="delete_search_history_alert">刪除全部的搜尋歷史</string>
<string name="search_history_deleted">搜尋歷史已刪除。</string>
<string name="one_item_deleted">已刪除 1 個項目。</string>
@ -468,4 +468,18 @@
<string name="decline">下降</string>
<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_none_description"></string>
<string name="minimize_on_exit_background_description">最小化為背景播放器</string>
<string name="minimize_on_exit_popup_description">最小化為彈出式播放器</string>
<string name="skip_silence_checkbox">在靜音時快轉</string>
<string name="playback_step">步進</string>
<string name="playback_reset">重設</string>
<string name="channels">頻道</string>
<string name="playlists">播放清單</string>
<string name="tracks"></string>
<string name="users">使用者</string>
</resources>

View File

@ -25,6 +25,22 @@
<string name="screen_brightness_key" translatable="false">screen_brightness_key</string>
<string name="screen_brightness_timestamp_key" translatable="false">screen_brightness_timestamp_key</string>
<string name="minimize_on_exit_key" translatable="false">minimize_on_exit_key</string>
<string name="minimize_on_exit_value" translatable="false">@string/minimize_on_exit_none_key</string>
<string name="minimize_on_exit_none_key" translatable="false">minimize_on_exit_none_key</string>
<string name="minimize_on_exit_background_key" translatable="false">minimize_on_exit_background_key</string>
<string name="minimize_on_exit_popup_key" translatable="false">minimize_on_exit_popup_key</string>
<string-array name="minimize_on_exit_action_key" translatable="false">
<item>@string/minimize_on_exit_none_key</item>
<item>@string/minimize_on_exit_background_key</item>
<item>@string/minimize_on_exit_popup_key</item>
</string-array>
<string-array name="minimize_on_exit_action_description" translatable="false">
<item>@string/minimize_on_exit_none_description</item>
<item>@string/minimize_on_exit_background_description</item>
<item>@string/minimize_on_exit_popup_description</item>
</string-array>
<string name="default_resolution_key" translatable="false">default_resolution</string>
<string name="default_resolution_value" translatable="false">360p</string>
<string name="show_higher_resolutions_key" translatable="false">show_higher_resolutions</string>
@ -178,6 +194,7 @@
<string name="video_player_key" translatable="false">video_player</string>
<string name="background_player_key" translatable="false">background_player</string>
<string name="popup_player_key" translatable="false">popup_player</string>
<string name="download_key" translatable="false">download</string>
<string name="always_ask_open_action_key" translatable="false">always_ask_player</string>
<string-array name="preferred_open_action_description_list" translatable="false">
@ -185,6 +202,7 @@
<item>@string/video_player</item>
<item>@string/background_player</item>
<item>@string/popup_player</item>
<item>@string/download</item>
<item>@string/always_ask_open_action</item>
</string-array>
<string-array name="preferred_open_action_values_list" translatable="false">
@ -192,6 +210,7 @@
<item>@string/video_player_key</item>
<item>@string/background_player_key</item>
<item>@string/popup_player_key</item>
<item>@string/download_key</item>
<item>@string/always_ask_open_action_key</item>
</string-array>

View File

@ -125,7 +125,12 @@
<string name="error_report_title">Error report</string>
<string name="all">All</string>
<string name="channel">Channel</string>
<string name="channels">Channels</string>
<string name="playlist">Playlist</string>
<string name="playlists">Playlists</string>
<string name="videos">Videos</string>
<string name="tracks">Tracks</string>
<string name="users">Users</string>
<string name="yes">Yes</string>
<string name="later">Later</string>
<string name="disabled">Disabled</string>
@ -479,9 +484,10 @@
<string name="playback_speed_control">Playback Speed Controls</string>
<string name="playback_tempo">Tempo</string>
<string name="playback_pitch">Pitch</string>
<string name="unhook_checkbox">Unhook (may cause distortion)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Default</string>
<string name="unhook_checkbox">Unlink (may cause distortion)</string>
<string name="skip_silence_checkbox">Fast-forward during silence</string>
<string name="playback_step">Step</string>
<string name="playback_reset">Reset</string>
<!-- GDPR dialog -->
<string name="start_accept_privacy_policy">In order to comply with the European General Data Protection Regulation (GDPR), we herby draw your attention to NewPipe\'s privacy policy. Please read it carefully.\nYou must accept it to send us the bug report.</string>
@ -504,4 +510,11 @@
<item>144p</item>
</string-array>
<!-- Minimize to exit action -->
<string name="minimize_on_exit_title">Minimize on application switch</string>
<string name="minimize_on_exit_summary">Action when switching to other application from main video player — %s</string>
<string name="minimize_on_exit_none_description">None</string>
<string name="minimize_on_exit_background_description">Minimize to background player</string>
<string name="minimize_on_exit_popup_description">Minimize to popup player</string>
</resources>

View File

@ -90,6 +90,14 @@
android:summary="@string/preferred_open_action_settings_summary"
android:title="@string/preferred_open_action_settings_title"/>
<ListPreference
android:defaultValue="@string/minimize_on_exit_value"
android:entries="@array/minimize_on_exit_action_description"
android:entryValues="@array/minimize_on_exit_action_key"
android:key="@string/minimize_on_exit_key"
android:summary="@string/minimize_on_exit_summary"
android:title="@string/minimize_on_exit_title"/>
<SwitchPreference
android:defaultValue="false"
android:key="@string/resume_on_audio_focus_gain_key"

View File

@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.android.tools.build:gradle:3.1.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -0,0 +1,28 @@
### Improvements
- Disable burgermenu icon animation #1486
- undo delete of downloads #1472
- Download option in share menu #1498
- Added share option to long tap menu #1454
- Minimize main player on exit #1354
- Library version update and database backup fix #1510
- ExoPlayer 2.8.2 Update #1392
- Reworked the playback speed control dialog to support different step sizes for faster speed change.
- Added a toggle to fast-forward during silences in playback speed control. This should be helpful for audiobooks and certain music genres, and can bring a true seamless experience (and can break a song with lots of silences =\\).
- Refactored media source resolution to allow passing metadata alongside media internally in the player, rather than doing so manually. Now we have a single source of metadata and is directly available when playback starts.
- Fixed remote playlist metadata not updating when new metadata is available when playlist fragment is opened.
- Various UI fixes: #1383, background player notification controls now always white, easier to shutdown popup player through flinging
- Use new extractor with refactored architecture for multiservice
### Fixes
- Fix #1440 Broken Video Info Layout #1491
- View history fix #1497
- #1495, by updating the metadata (thumbnail, title and video count) as soon as the user access the playlist.
- #1475, by registering a view in the database when the user starts a video on external player on detail fragment.
- Fix creen timeout in case of popup mode. #1463 (Fixed #640)
- Main video player fix #1509
- [#1412] Fixed repeat mode causing player NPE when new intent is received while player activity is in background.
- Fixed minimizing player to popup does not destroy player when popup permission is not granted.

View File

@ -0,0 +1,34 @@
# Changelog of v0.13.7
### Fixed
- Fix sort filter issues of v0.13.6
# Changelog of v0.13.6
### Improvements
- Disable burgermenu icon animation #1486
- undo delete of downloads #1472
- Download option in share menu #1498
- Added share option to long tap menu #1454
- Minimize main player on exit #1354
- Library version update and database backup fix #1510
- ExoPlayer 2.8.2 Update #1392
- Reworked the playback speed control dialog to support different step sizes for faster speed change.
- Added a toggle to fast-forward during silences in playback speed control. This should be helpful for audiobooks and certain music genres, and can bring a true seamless experience (and can break a song with lots of silences =\\).
- Refactored media source resolution to allow passing metadata alongside media internally in the player, rather than doing so manually. Now we have a single source of metadata and is directly available when playback starts.
- Fixed remote playlist metadata not updating when new metadata is available when playlist fragment is opened.
- Various UI fixes: #1383, background player notification controls now always white, easier to shutdown popup player through flinging
- Use new extractor with refactored architecture for multiservice
### Fixes
- Fix #1440 Broken Video Info Layout #1491
- View history fix #1497
- #1495, by updating the metadata (thumbnail, title and video count) as soon as the user access the playlist.
- #1475, by registering a view in the database when the user starts a video on external player on detail fragment.
- Fix creen timeout in case of popup mode. #1463 (Fixed #640)
- Main video player fix #1509
- [#1412] Fixed repeat mode causing player NPE when new intent is received while player activity is in background.
- Fixed minimizing player to popup does not destroy player when popup permission is not granted.