diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index ecaf94d5d9a..f6c3ae65ae8 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -41,6 +41,6 @@ You'll see exactly what is sent, be able to add your comments, and then send it.
## Communication
-* The [#newpipe](irc:irc.freenode.net/newpipe) channel on freenode has the core team and other developers in it. [Click here for webchat](https://webchat.freenode.net/?channels=newpipe)!
-* You can also use a Matrix account to join the Newpipe channel at [#freenode_#newpipe:matrix.org](https://matrix.to/#/#freenode_#newpipe:matrix.org).
+* The #newpipe channel on Libera Chat (`ircs://irc.libera.chat:6697/newpipe`) has the core team and other developers in it. [Click here for webchat](https://web.libera.chat/#newpipe)!
+* You can also use a Matrix account to join the NewPipe channel at [#newpipe:libera.chat](https://matrix.to/#/#newpipe:libera.chat).
* Post suggestions, changes, ideas etc. on GitHub or IRC.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 5a97b366252..b0fdb56db70 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 💬 IRC
- url: https://webchat.freenode.net/#newpipe
+ url: https://web.libera.chat/#newpipe
about: Chat with us via IRC for quick Q/A
- name: 💬 Matrix
- url: https://matrix.to/#/#freenode_#newpipe:matrix.org
+ url: https://matrix.to/#/#newpipe:libera.chat
about: Chat with us via Matrix for quick Q/A
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 427da73915d..676e46333b7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,6 +4,7 @@ on:
pull_request:
branches:
- dev
+ - master
push:
branches:
- dev
@@ -25,7 +26,7 @@ jobs:
uses: actions/setup-java@v2
with:
java-version: 8
- distribution: "zulu"
+ distribution: "adopt"
- name: Cache Gradle dependencies
uses: actions/cache@v2
@@ -54,7 +55,7 @@ jobs:
uses: actions/setup-java@v2
with:
java-version: 8
- distribution: "zulu"
+ distribution: "adopt"
- name: Cache Gradle dependencies
uses: actions/cache@v2
@@ -79,7 +80,7 @@ jobs:
# uses: actions/setup-java@v2
# with:
# java-version: 11 # Sonar requires JDK 11
-# distribution: "zulu"
+# distribution: "adopt"
# - name: Cache SonarCloud packages
# uses: actions/cache@v2
diff --git a/README.es.md b/README.es.md
index 7eae15c08da..3920545d595 100644
--- a/README.es.md
+++ b/README.es.md
@@ -9,7 +9,7 @@
-
+
diff --git a/README.ja.md b/README.ja.md
index a961005a713..980dc914a8c 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -9,7 +9,7 @@
-
+
diff --git a/README.ko.md b/README.ko.md
index a677aaca42f..269cfda497c 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -9,7 +9,7 @@
-
+
diff --git a/README.md b/README.md
index 8ccaad64b38..93c70a532f3 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-
+
diff --git a/README.pt_BR.md b/README.pt_BR.md
index e32da820669..5cdd8ae138b 100644
--- a/README.pt_BR.md
+++ b/README.pt_BR.md
@@ -10,7 +10,7 @@
-
+
diff --git a/README.ro.md b/README.ro.md
index fb5624630fe..a53019c9cce 100644
--- a/README.ro.md
+++ b/README.ro.md
@@ -9,7 +9,7 @@
-
+
diff --git a/README.so.md b/README.so.md
index 31afbe47515..ed42fde0d09 100644
--- a/README.so.md
+++ b/README.so.md
@@ -9,7 +9,7 @@
-
+
diff --git a/README.tr.md b/README.tr.md
index bb9148af82b..0bd644934af 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -9,7 +9,7 @@
-
+
diff --git a/app/build.gradle b/app/build.gradle
index 1b77756e186..ed9fad79785 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,8 +17,8 @@ android {
resValue "string", "app_name", "NewPipe"
minSdkVersion 19
targetSdkVersion 29
- versionCode 969
- versionName "0.21.3"
+ versionCode 970
+ versionName "0.21.4"
multiDexEnabled true
@@ -178,12 +178,12 @@ sonarqube {
dependencies {
/** Desugaring **/
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
/** NewPipe libraries **/
// You can use a local version by uncommenting a few lines in settings.gradle
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.3'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.4'
/** Checkstyle **/
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
@@ -258,7 +258,7 @@ dependencies {
implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0"
// Date and time formatting
- implementation "org.ocpsoft.prettytime:prettytime:5.0.0.Final"
+ implementation "org.ocpsoft.prettytime:prettytime:5.0.1.Final"
/** Debugging **/
// Memory leak detection
@@ -270,7 +270,7 @@ dependencies {
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
/** Testing **/
- testImplementation 'junit:junit:4.13.1'
+ testImplementation 'junit:junit:4.13.2'
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
testImplementation "org.mockito:mockito-inline:${mockitoVersion}"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e247e57b89d..eb15cddcea0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -319,7 +319,7 @@
-
+
@@ -330,10 +330,23 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getChoicesForService(final StreamingService serv
if (capabilities.contains(AUDIO)) {
returnList.add(backgroundPlayer);
}
+ // download is redundant for linkType CHANNEL AND PLAYLIST (till playlist downloading is
+ // not supported )
+ returnList.add(new AdapterChoiceItem(getString(R.string.download_key),
+ getString(R.string.download),
+ R.drawable.ic_file_download));
} else {
returnList.add(showInfo);
@@ -478,10 +483,6 @@ private List getChoicesForService(final StreamingService serv
}
}
- returnList.add(new AdapterChoiceItem(getString(R.string.download_key),
- getString(R.string.download),
- R.drawable.ic_file_download));
-
return returnList;
}
diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
index db2b3b7e2ad..e7ae8d8790b 100644
--- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
@@ -312,25 +312,36 @@ public void onStopTrackingTouch(final SeekBar p1) { }
private void fetchStreamsSize() {
disposables.clear();
-
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams)
.subscribe(result -> {
- if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.video_button) {
- setupVideoSpinner();
- }
- }));
+ if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
+ == R.id.video_button) {
+ setupVideoSpinner();
+ }
+ }, throwable -> ErrorActivity.reportErrorInSnackbar(context,
+ new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
+ "Downloading video stream size",
+ currentInfo.getServiceId()))));
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedAudioStreams)
.subscribe(result -> {
- if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) {
- setupAudioSpinner();
- }
- }));
+ if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
+ == R.id.audio_button) {
+ setupAudioSpinner();
+ }
+ }, throwable -> ErrorActivity.reportErrorInSnackbar(context,
+ new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
+ "Downloading audio stream size",
+ currentInfo.getServiceId()))));
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedSubtitleStreams)
.subscribe(result -> {
- if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.subtitle_button) {
- setupSubtitleSpinner();
- }
- }));
+ if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
+ == R.id.subtitle_button) {
+ setupSubtitleSpinner();
+ }
+ }, throwable -> ErrorActivity.reportErrorInSnackbar(context,
+ new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
+ "Downloading subtitle stream size",
+ currentInfo.getServiceId()))));
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java
index fbf2711bcc4..d4e73bcac78 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java
@@ -11,15 +11,20 @@
import org.schabi.newpipe.R;
public class EmptyFragment extends BaseFragment {
- final boolean showMessage;
+ private static final String SHOW_MESSAGE = "SHOW_MESSAGE";
- public EmptyFragment(final boolean showMessage) {
- this.showMessage = showMessage;
+ public static final EmptyFragment newInstance(final boolean showMessage) {
+ final EmptyFragment emptyFragment = new EmptyFragment();
+ final Bundle bundle = new Bundle(1);
+ bundle.putBoolean(SHOW_MESSAGE, showMessage);
+ emptyFragment.setArguments(bundle);
+ return emptyFragment;
}
@Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
final Bundle savedInstanceState) {
+ final boolean showMessage = getArguments().getBoolean(SHOW_MESSAGE);
final View view = inflater.inflate(R.layout.fragment_empty, container, false);
view.findViewById(R.id.empty_state_view).setVisibility(
showMessage ? View.VISIBLE : View.GONE);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java
index b4424928fa3..5f1cbc36583 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java
@@ -4,23 +4,38 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.TextView;
+import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.appcompat.widget.TooltipCompat;
import androidx.core.text.HtmlCompat;
+import com.google.android.material.chip.Chip;
+
import org.schabi.newpipe.BaseFragment;
+import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentDescriptionBinding;
+import org.schabi.newpipe.databinding.ItemMetadataBinding;
+import org.schabi.newpipe.databinding.ItemMetadataTagsBinding;
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.util.Localization;
+import org.schabi.newpipe.util.NavigationHelper;
+import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.TextLinkifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import icepick.State;
import io.reactivex.rxjava3.disposables.Disposable;
import static android.text.TextUtils.isEmpty;
+import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
+import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
public class DescriptionFragment extends BaseFragment {
@@ -28,6 +43,7 @@ public class DescriptionFragment extends BaseFragment {
StreamInfo streamInfo = null;
@Nullable
Disposable descriptionDisposable = null;
+ FragmentDescriptionBinding binding;
public DescriptionFragment() {
}
@@ -40,11 +56,11 @@ public DescriptionFragment(final StreamInfo streamInfo) {
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
- final FragmentDescriptionBinding binding =
- FragmentDescriptionBinding.inflate(inflater, container, false);
+ binding = FragmentDescriptionBinding.inflate(inflater, container, false);
if (streamInfo != null) {
- setupUploadDate(binding.detailUploadDateView);
- setupDescription(binding.detailDescriptionView);
+ setupUploadDate();
+ setupDescription();
+ setupMetadata(inflater, binding.detailMetadataLayout);
}
return binding.getRoot();
}
@@ -57,37 +73,197 @@ public void onDestroy() {
}
}
- private void setupUploadDate(final TextView uploadDateTextView) {
+
+ private void setupUploadDate() {
if (streamInfo.getUploadDate() != null) {
- uploadDateTextView.setText(Localization
+ binding.detailUploadDateView.setText(Localization
.localizeUploadDate(activity, streamInfo.getUploadDate().offsetDateTime()));
} else {
- uploadDateTextView.setVisibility(View.GONE);
+ binding.detailUploadDateView.setVisibility(View.GONE);
}
}
- private void setupDescription(final TextView descriptionTextView) {
+
+ private void setupDescription() {
final Description description = streamInfo.getDescription();
if (description == null || isEmpty(description.getContent())
|| description == Description.emptyDescription) {
- descriptionTextView.setText("");
+ binding.detailDescriptionView.setVisibility(View.GONE);
+ binding.detailSelectDescriptionButton.setVisibility(View.GONE);
return;
}
+ // start with disabled state. This also loads description content (!)
+ disableDescriptionSelection();
+
+ binding.detailSelectDescriptionButton.setOnClickListener(v -> {
+ if (binding.detailDescriptionNoteView.getVisibility() == View.VISIBLE) {
+ disableDescriptionSelection();
+ } else {
+ // enable selection only when button is clicked to prevent flickering
+ enableDescriptionSelection();
+ }
+ });
+ }
+
+ private void enableDescriptionSelection() {
+ binding.detailDescriptionNoteView.setVisibility(View.VISIBLE);
+ binding.detailDescriptionView.setTextIsSelectable(true);
+
+ final String buttonLabel = getString(R.string.description_select_disable);
+ binding.detailSelectDescriptionButton.setContentDescription(buttonLabel);
+ TooltipCompat.setTooltipText(binding.detailSelectDescriptionButton, buttonLabel);
+ binding.detailSelectDescriptionButton.setImageResource(R.drawable.ic_close);
+ }
+
+ private void disableDescriptionSelection() {
+ // show description content again, otherwise some links are not clickable
+ loadDescriptionContent();
+
+ binding.detailDescriptionNoteView.setVisibility(View.GONE);
+ binding.detailDescriptionView.setTextIsSelectable(false);
+
+ final String buttonLabel = getString(R.string.description_select_enable);
+ binding.detailSelectDescriptionButton.setContentDescription(buttonLabel);
+ TooltipCompat.setTooltipText(binding.detailSelectDescriptionButton, buttonLabel);
+ binding.detailSelectDescriptionButton.setImageResource(R.drawable.ic_select_all);
+ }
+
+ private void loadDescriptionContent() {
+ final Description description = streamInfo.getDescription();
switch (description.getType()) {
case Description.HTML:
descriptionDisposable = TextLinkifier.createLinksFromHtmlBlock(requireContext(),
- description.getContent(), descriptionTextView,
+ description.getContent(), binding.detailDescriptionView,
HtmlCompat.FROM_HTML_MODE_LEGACY);
break;
case Description.MARKDOWN:
descriptionDisposable = TextLinkifier.createLinksFromMarkdownText(requireContext(),
- description.getContent(), descriptionTextView);
+ description.getContent(), binding.detailDescriptionView);
break;
case Description.PLAIN_TEXT: default:
descriptionDisposable = TextLinkifier.createLinksFromPlainText(requireContext(),
- description.getContent(), descriptionTextView);
+ description.getContent(), binding.detailDescriptionView);
break;
}
}
+
+
+ private void setupMetadata(final LayoutInflater inflater,
+ final LinearLayout layout) {
+ addMetadataItem(inflater, layout, false,
+ R.string.metadata_category, streamInfo.getCategory());
+
+ addTagsMetadataItem(inflater, layout);
+
+ addMetadataItem(inflater, layout, false,
+ R.string.metadata_licence, streamInfo.getLicence());
+
+ addPrivacyMetadataItem(inflater, layout);
+
+ if (streamInfo.getAgeLimit() != NO_AGE_LIMIT) {
+ addMetadataItem(inflater, layout, false,
+ R.string.metadata_age_limit, String.valueOf(streamInfo.getAgeLimit()));
+ }
+
+ if (streamInfo.getLanguageInfo() != null) {
+ addMetadataItem(inflater, layout, false,
+ R.string.metadata_language, streamInfo.getLanguageInfo().getDisplayLanguage());
+ }
+
+ addMetadataItem(inflater, layout, true,
+ R.string.metadata_support, streamInfo.getSupportInfo());
+ addMetadataItem(inflater, layout, true,
+ R.string.metadata_host, streamInfo.getHost());
+ addMetadataItem(inflater, layout, true,
+ R.string.metadata_thumbnail_url, streamInfo.getThumbnailUrl());
+ }
+
+ private void addMetadataItem(final LayoutInflater inflater,
+ final LinearLayout layout,
+ final boolean linkifyContent,
+ @StringRes final int type,
+ @Nullable final String content) {
+ if (isBlank(content)) {
+ return;
+ }
+
+ final ItemMetadataBinding itemBinding
+ = ItemMetadataBinding.inflate(inflater, layout, false);
+
+ itemBinding.metadataTypeView.setText(type);
+ itemBinding.metadataTypeView.setOnLongClickListener(v -> {
+ ShareUtils.copyToClipboard(requireContext(), content);
+ return true;
+ });
+
+ if (linkifyContent) {
+ TextLinkifier.createLinksFromPlainText(requireContext(),
+ content, itemBinding.metadataContentView);
+ } else {
+ itemBinding.metadataContentView.setText(content);
+ }
+
+ layout.addView(itemBinding.getRoot());
+ }
+
+ private void addTagsMetadataItem(final LayoutInflater inflater, final LinearLayout layout) {
+ if (streamInfo.getTags() != null && !streamInfo.getTags().isEmpty()) {
+ final ItemMetadataTagsBinding itemBinding
+ = ItemMetadataTagsBinding.inflate(inflater, layout, false);
+
+ final List tags = new ArrayList<>(streamInfo.getTags());
+ Collections.sort(tags);
+ for (final String tag : tags) {
+ final Chip chip = (Chip) inflater.inflate(R.layout.chip,
+ itemBinding.metadataTagsChips, false);
+ chip.setText(tag);
+ chip.setOnClickListener(this::onTagClick);
+ chip.setOnLongClickListener(this::onTagLongClick);
+ itemBinding.metadataTagsChips.addView(chip);
+ }
+
+ layout.addView(itemBinding.getRoot());
+ }
+ }
+
+ private void onTagClick(final View chip) {
+ if (getParentFragment() != null) {
+ NavigationHelper.openSearchFragment(getParentFragment().getParentFragmentManager(),
+ streamInfo.getServiceId(), ((Chip) chip).getText().toString());
+ }
+ }
+
+ private boolean onTagLongClick(final View chip) {
+ ShareUtils.copyToClipboard(requireContext(), ((Chip) chip).getText().toString());
+ return true;
+ }
+
+ private void addPrivacyMetadataItem(final LayoutInflater inflater, final LinearLayout layout) {
+ if (streamInfo.getPrivacy() != null) {
+ @StringRes final int contentRes;
+ switch (streamInfo.getPrivacy()) {
+ case PUBLIC:
+ contentRes = R.string.metadata_privacy_public;
+ break;
+ case UNLISTED:
+ contentRes = R.string.metadata_privacy_unlisted;
+ break;
+ case PRIVATE:
+ contentRes = R.string.metadata_privacy_private;
+ break;
+ case INTERNAL:
+ contentRes = R.string.metadata_privacy_internal;
+ break;
+ case OTHER: default:
+ contentRes = 0;
+ break;
+ }
+
+ if (contentRes != 0) {
+ addMetadataItem(inflater, layout, false,
+ R.string.metadata_privacy, getString(contentRes));
+ }
+ }
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 71739ba3d45..784a1c3be7d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -929,20 +929,20 @@ private void initTabs() {
if (showRelatedItems && binding.relatedItemsLayout == null) {
// temp empty fragment. will be updated in handleResult
- pageAdapter.addFragment(new EmptyFragment(false), RELATED_TAB_TAG);
+ pageAdapter.addFragment(EmptyFragment.newInstance(false), RELATED_TAB_TAG);
tabIcons.add(R.drawable.ic_art_track);
tabContentDescriptions.add(R.string.related_items_tab_description);
}
if (showDescription) {
// temp empty fragment. will be updated in handleResult
- pageAdapter.addFragment(new EmptyFragment(false), DESCRIPTION_TAB_TAG);
+ pageAdapter.addFragment(EmptyFragment.newInstance(false), DESCRIPTION_TAB_TAG);
tabIcons.add(R.drawable.ic_description);
tabContentDescriptions.add(R.string.description_tab_description);
}
if (pageAdapter.getCount() == 0) {
- pageAdapter.addFragment(new EmptyFragment(true), EMPTY_TAB_TAG);
+ pageAdapter.addFragment(EmptyFragment.newInstance(true), EMPTY_TAB_TAG);
}
pageAdapter.notifyDataSetUpdate();
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java
index 38fdbccab38..72855868559 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java
@@ -370,10 +370,10 @@ protected void showStreamDialog(final StreamInfoItem item) {
StreamDialogEntry.share
));
}
+ entries.add(StreamDialogEntry.open_in_browser);
if (KoreUtil.shouldShowPlayWithKodi(context, item.getServiceId())) {
entries.add(StreamDialogEntry.play_with_kodi);
}
-
if (!isNullOrEmpty(item.getUploaderUrl())) {
entries.add(StreamDialogEntry.show_channel_details);
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
index a729b7cc9bf..0e36d18c702 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
@@ -59,6 +59,7 @@
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
+import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
@@ -160,9 +161,15 @@ protected void showStreamDialog(final StreamInfoItem item) {
StreamDialogEntry.share
));
}
+ entries.add(StreamDialogEntry.open_in_browser);
if (KoreUtil.shouldShowPlayWithKodi(context, item.getServiceId())) {
entries.add(StreamDialogEntry.play_with_kodi);
}
+
+ if (!isNullOrEmpty(item.getUploaderUrl())) {
+ entries.add(StreamDialogEntry.show_channel_details);
+ }
+
StreamDialogEntry.setEnabledEntries(entries);
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItem) ->
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
index b52aaf2f878..70fce1cb75b 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
@@ -9,6 +9,7 @@
import android.text.Html;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.text.style.CharacterStyle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -588,6 +589,11 @@ public void onTextChanged(final CharSequence s, final int start,
@Override
public void afterTextChanged(final Editable s) {
+ // Remove rich text formatting
+ for (final CharacterStyle span : s.getSpans(0, s.length(), CharacterStyle.class)) {
+ s.removeSpan(span);
+ }
+
final String newText = searchEditText.getText().toString();
suggestionPublisher.onNext(newText);
}
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java
index b106e029dbd..ae7ddfd6350 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java
@@ -137,7 +137,10 @@ public void updateFromItem(final InfoItem infoItem,
}
if (item.getLikeCount() >= 0) {
- itemLikesCountView.setText(String.valueOf(item.getLikeCount()));
+ itemLikesCountView.setText(
+ Localization.shortCount(
+ itemBuilder.getContext(),
+ item.getLikeCount()));
} else {
itemLikesCountView.setText("-");
}
diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
index a2107d5e56d..10aa8aa68df 100644
--- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
@@ -53,6 +53,8 @@
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
+import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
+
public class StatisticsPlaylistFragment
extends BaseLocalListFragment, Void> {
private final CompositeDisposable disposables = new CompositeDisposable();
@@ -356,9 +358,15 @@ private void showStreamDialog(final StreamStatisticsEntry item) {
StreamDialogEntry.share
));
}
+ entries.add(StreamDialogEntry.open_in_browser);
if (KoreUtil.shouldShowPlayWithKodi(context, infoItem.getServiceId())) {
entries.add(StreamDialogEntry.play_with_kodi);
}
+
+ if (!isNullOrEmpty(infoItem.getUploaderUrl())) {
+ entries.add(StreamDialogEntry.show_channel_details);
+ }
+
StreamDialogEntry.setEnabledEntries(entries);
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
index 66bd341429f..f79282641cb 100644
--- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
@@ -66,6 +66,7 @@
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;
+import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
public class LocalPlaylistFragment extends BaseLocalListFragment, Void> {
@@ -768,9 +769,15 @@ protected void showStreamItemDialog(final PlaylistStreamEntry item) {
StreamDialogEntry.share
));
}
+ entries.add(StreamDialogEntry.open_in_browser);
if (KoreUtil.shouldShowPlayWithKodi(context, infoItem.getServiceId())) {
entries.add(StreamDialogEntry.play_with_kodi);
}
+
+ if (!isNullOrEmpty(infoItem.getUploaderUrl())) {
+ entries.add(StreamDialogEntry.show_channel_details);
+ }
+
StreamDialogEntry.setEnabledEntries(entries);
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java b/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java
index 30142157ecc..602e418a085 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java
@@ -40,7 +40,7 @@ public void setResultServiceIntent(final Intent resultServiceIntent) {
@Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
assureCorrectAppLanguage(getContext());
- return new AlertDialog.Builder(getContext())
+ return new AlertDialog.Builder(requireContext())
.setMessage(R.string.import_network_expensive_warning)
.setCancelable(true)
.setNegativeButton(R.string.cancel, null)
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt
index d3f80e214e6..8a235fa8abc 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt
@@ -294,12 +294,16 @@ class SubscriptionFragment : BaseStateFragment() {
}
private fun showLongTapDialog(selectedItem: ChannelInfoItem) {
- val commands = arrayOf(getString(R.string.share), getString(R.string.unsubscribe))
+ val commands = arrayOf(
+ getString(R.string.share), getString(R.string.open_in_browser),
+ getString(R.string.unsubscribe)
+ )
val actions = DialogInterface.OnClickListener { _, i ->
when (i) {
0 -> ShareUtils.shareText(requireContext(), selectedItem.name, selectedItem.url)
- 1 -> deleteChannel(selectedItem)
+ 1 -> ShareUtils.openUrlInBrowser(requireContext(), selectedItem.url)
+ 2 -> deleteChannel(selectedItem)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java
index 08a2762a979..29c9ac77b60 100644
--- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java
@@ -15,6 +15,7 @@
import android.widget.PopupMenu;
import android.widget.SeekBar;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -456,6 +457,7 @@ public void onPlaybackParameterChanged(final float playbackTempo, final float pl
final boolean playbackSkipSilence) {
if (player != null) {
player.setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence);
+ onPlaybackParameterChanged(player.getPlaybackParameters());
}
}
@@ -639,7 +641,7 @@ private void onPlayModeChanged(final int repeatMode, final boolean shuffled) {
queueControlBinding.controlShuffle.setImageAlpha(shuffleAlpha);
}
- private void onPlaybackParameterChanged(final PlaybackParameters parameters) {
+ private void onPlaybackParameterChanged(@Nullable final PlaybackParameters parameters) {
if (parameters != null) {
if (menu != null && player != null) {
final MenuItem item = menu.findItem(R.id.action_playback_speed);
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index 44d10df37cb..d51a257897e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -3029,6 +3029,7 @@ private void onSegmentsClicked() {
buildSegments();
binding.itemsListHeaderTitle.setVisibility(View.VISIBLE);
+ binding.itemsListHeaderDuration.setVisibility(View.GONE);
binding.shuffleButton.setVisibility(View.GONE);
binding.repeatButton.setVisibility(View.GONE);
diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
index 6131d856545..014c1333901 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
@@ -40,29 +40,25 @@
*/
public abstract class PlayQueue implements Serializable {
public static final boolean DEBUG = MainActivity.DEBUG;
-
- private ArrayList backup;
- private ArrayList streams;
-
@NonNull
private final AtomicInteger queueIndex;
- private final ArrayList history;
+ private final List history = new ArrayList<>();
+
+ private List backup;
+ private List streams;
private transient BehaviorSubject eventBroadcast;
private transient Flowable broadcastReceiver;
-
- private transient boolean disposed;
+ private transient boolean disposed = false;
PlayQueue(final int index, final List startWith) {
- streams = new ArrayList<>();
- streams.addAll(startWith);
- history = new ArrayList<>();
+ streams = new ArrayList<>(startWith);
+
if (streams.size() > index) {
history.add(streams.get(index));
}
queueIndex = new AtomicInteger(index);
- disposed = false;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -137,18 +133,36 @@ public int getIndex() {
public synchronized void setIndex(final int index) {
final int oldIndex = getIndex();
- int newIndex = index;
+ final int newIndex;
+
if (index < 0) {
newIndex = 0;
+ } else if (index < streams.size()) {
+ // Regular assignment for index in bounds
+ newIndex = index;
+ } else if (streams.isEmpty()) {
+ // Out of bounds from here on
+ // Need to check if stream is empty to prevent arithmetic error and negative index
+ newIndex = 0;
+ } else if (isComplete()) {
+ // Circular indexing
+ newIndex = index % streams.size();
+ } else {
+ // Index of last element
+ newIndex = streams.size() - 1;
}
- if (index >= streams.size()) {
- newIndex = isComplete() ? index % streams.size() : streams.size() - 1;
- }
+
+ queueIndex.set(newIndex);
+
if (oldIndex != newIndex) {
history.add(streams.get(newIndex));
}
- queueIndex.set(newIndex);
+ /*
+ TODO: Documentation states that a SelectEvent will only be emitted if the new index is...
+ different from the old one but this is emitted regardless? Not sure what this what it does
+ exactly so I won't touch it
+ */
broadcast(new SelectEvent(oldIndex, newIndex));
}
@@ -180,8 +194,6 @@ public PlayQueueItem getItem(final int index) {
* @return the index of the given item
*/
public int indexOf(@NonNull final PlayQueueItem item) {
- // referential equality, can't think of a better way to do this
- // todo: better than this
return streams.indexOf(item);
}
@@ -410,34 +422,42 @@ public synchronized void unsetRecovery(final int index) {
}
/**
- * Shuffles the current play queue.
+ * Shuffles the current play queue
*
- * This method first backs up the existing play queue and item being played.
- * Then a newly shuffled play queue will be generated along with currently
- * playing item placed at the beginning of the queue.
+ * This method first backs up the existing play queue and item being played. Then a newly
+ * shuffled play queue will be generated along with currently playing item placed at the
+ * beginning of the queue. This item will also be added to the history.
*
*
- * Will emit a {@link ReorderEvent} in any context.
+ * Will emit a {@link ReorderEvent} if shuffled.
*
+ *
+ * @implNote Does nothing if the queue has a size <= 2 (the currently playing video must stay on
+ * top, so shuffling a size-2 list does nothing)
*/
public synchronized void shuffle() {
+ // Can't shuffle an list that's empty or only has one element
+ if (size() <= 2) {
+ return;
+ }
+ // Create a backup if it doesn't already exist
if (backup == null) {
backup = new ArrayList<>(streams);
}
- final int originIndex = getIndex();
- final PlayQueueItem current = getItem();
+
+ final int originalIndex = getIndex();
+ final PlayQueueItem currentItem = getItem();
+
Collections.shuffle(streams);
- final int newIndex = streams.indexOf(current);
- if (newIndex != -1) {
- streams.add(0, streams.remove(newIndex));
- }
+ // Move currentItem to the head of the queue
+ streams.remove(currentItem);
+ streams.add(0, currentItem);
queueIndex.set(0);
- if (streams.size() > 0) {
- history.add(streams.get(0));
- }
- broadcast(new ReorderEvent(originIndex, queueIndex.get()));
+ history.add(currentItem);
+
+ broadcast(new ReorderEvent(originalIndex, 0));
}
/**
@@ -457,7 +477,6 @@ public synchronized void unshuffle() {
final int originIndex = getIndex();
final PlayQueueItem current = getItem();
- streams.clear();
streams = backup;
backup = null;
@@ -500,22 +519,19 @@ public synchronized boolean previous() {
* we don't have to do anything with new queue.
* This method also gives a chance to track history of items in a queue in
* VideoDetailFragment without duplicating items from two identical queues
- * */
+ */
@Override
public boolean equals(@Nullable final Object obj) {
- if (!(obj instanceof PlayQueue)
- || getStreams().size() != ((PlayQueue) obj).getStreams().size()) {
+ if (!(obj instanceof PlayQueue)) {
return false;
}
-
final PlayQueue other = (PlayQueue) obj;
- for (int i = 0; i < getStreams().size(); i++) {
- if (!getItem(i).getUrl().equals(other.getItem(i).getUrl())) {
- return false;
- }
- }
+ return streams.equals(other.streams);
+ }
- return true;
+ @Override
+ public int hashCode() {
+ return streams.hashCode();
}
public boolean isDisposed() {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
index ab6ff7414bb..9af3666a6e5 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
@@ -1,5 +1,6 @@
package org.schabi.newpipe.settings;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -25,8 +26,8 @@
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
-import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.FilePathUtils;
+import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.ZipHelper;
import java.io.File;
@@ -177,17 +178,14 @@ public void onActivityResult(final int requestCode, final int resultCode,
if ((requestCode == REQUEST_IMPORT_PATH || requestCode == REQUEST_EXPORT_PATH)
&& resultCode == Activity.RESULT_OK && data.getData() != null) {
final File file = Utils.getFileForUri(data.getData());
- final String path = file.getAbsolutePath();
- setImportExportDataPath(file);
if (requestCode == REQUEST_EXPORT_PATH) {
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
- exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
+ exportDatabase(file);
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setMessage(R.string.override_current_data)
.setPositiveButton(getString(R.string.finish),
- (d, id) -> importDatabase(path))
+ (d, id) -> importDatabase(file))
.setNegativeButton(android.R.string.cancel,
(d, id) -> d.cancel());
builder.create().show();
@@ -195,26 +193,34 @@ public void onActivityResult(final int requestCode, final int resultCode,
}
}
- private void exportDatabase(final String path) {
+ private void exportDatabase(@NonNull final File folder) {
try {
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
+ final String path = folder.getAbsolutePath() + "/NewPipeData-"
+ + sdf.format(new Date()) + ".zip";
+
//checkpoint before export
NewPipeDatabase.checkpoint();
final SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(requireContext());
+ .getDefaultSharedPreferences(requireContext());
manager.exportDatabase(preferences, path);
+ setImportExportDataPath(folder, false);
+
Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
} catch (final Exception e) {
ErrorActivity.reportUiErrorInSnackbar(this, "Exporting database", e);
}
}
- private void importDatabase(final String filePath) {
+ private void importDatabase(@NonNull final File file) {
+ final String filePath = file.getAbsolutePath();
+
// check if file is supported
if (!ZipHelper.isValidZipFile(filePath)) {
Toast.makeText(getContext(), R.string.no_valid_zip_file, Toast.LENGTH_SHORT)
- .show();
+ .show();
return;
}
@@ -225,7 +231,7 @@ private void importDatabase(final String filePath) {
if (!manager.extractDb(filePath)) {
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
- .show();
+ .show();
}
//If settings file exist, ask if it should be imported.
@@ -235,27 +241,40 @@ private void importDatabase(final String filePath) {
alert.setNegativeButton(android.R.string.no, (dialog, which) -> {
dialog.dismiss();
- // restart app to properly load db
- System.exit(0);
+ finishImport(file);
});
alert.setPositiveButton(getString(R.string.finish), (dialog, which) -> {
dialog.dismiss();
manager.loadSharedPreferences(PreferenceManager
- .getDefaultSharedPreferences(requireContext()));
- // restart app to properly load db
- System.exit(0);
+ .getDefaultSharedPreferences(requireContext()));
+ finishImport(file);
});
alert.show();
} else {
- // restart app to properly load db
- System.exit(0);
+ finishImport(file);
}
} catch (final Exception e) {
ErrorActivity.reportUiErrorInSnackbar(this, "Importing database", e);
}
}
- private void setImportExportDataPath(final File file) {
+ /**
+ * Save import path and restart system.
+ *
+ * @param file The file of the created backup
+ */
+ private void finishImport(@NonNull final File file) {
+ if (file.getParentFile() != null) {
+ //immediately because app is about to exit
+ setImportExportDataPath(file.getParentFile(), true);
+ }
+
+ // restart app to properly load db
+ System.exit(0);
+ }
+
+ @SuppressLint("ApplySharedPref")
+ private void setImportExportDataPath(@NonNull final File file, final boolean immediately) {
final String directoryPath;
if (file.isDirectory()) {
directoryPath = file.getAbsolutePath();
@@ -267,6 +286,13 @@ private void setImportExportDataPath(final File file) {
directoryPath = "";
}
}
- defaultPreferences.edit().putString(importExportDataPathKey, directoryPath).apply();
+ final SharedPreferences.Editor editor = defaultPreferences
+ .edit()
+ .putString(importExportDataPathKey, directoryPath);
+ if (immediately) {
+ editor.commit();
+ } else {
+ editor.apply();
+ }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index df804136a20..106399735ab 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -596,6 +596,7 @@ public static void playWithKore(final Context context, final Uri videoURL) {
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setPackage(context.getString(R.string.kore_package));
intent.setData(videoURL);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java
index 2d59febc22e..610f9f85237 100644
--- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java
+++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java
@@ -26,7 +26,8 @@ public enum StreamDialogEntry {
show_channel_details(R.string.show_channel_details, (fragment, item) ->
// For some reason `getParentFragmentManager()` doesn't work, but this does.
- NavigationHelper.openChannelFragment(fragment.getActivity().getSupportFragmentManager(),
+ NavigationHelper.openChannelFragment(
+ fragment.requireActivity().getSupportFragmentManager(),
item.getServiceId(), item.getUploaderUrl(), item.getUploaderName())
),
@@ -80,14 +81,17 @@ public enum StreamDialogEntry {
play_with_kodi(R.string.play_with_kodi_title, (fragment, item) -> {
final Uri videoUrl = Uri.parse(item.getUrl());
try {
- NavigationHelper.playWithKore(fragment.getContext(), videoUrl);
+ NavigationHelper.playWithKore(fragment.requireContext(), videoUrl);
} catch (final Exception e) {
KoreUtil.showInstallKoreDialog(fragment.getActivity());
}
}),
share(R.string.share, (fragment, item) ->
- ShareUtils.shareText(fragment.getContext(), item.getName(), item.getUrl()));
+ ShareUtils.shareText(fragment.getContext(), item.getName(), item.getUrl())),
+
+ open_in_browser(R.string.open_in_browser, (fragment, item) ->
+ ShareUtils.openUrlInBrowser(fragment.getContext(), item.getUrl()));
///////////////
diff --git a/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java b/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java
index eba9437e1e5..ad64042f179 100644
--- a/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java
+++ b/app/src/main/java/us/shandian/giga/io/StoredFileHelper.java
@@ -275,7 +275,7 @@ public boolean create() {
if (!docTree.canRead() || !docTree.canWrite()) return false;
try {
docFile = createSAF(context, srcType, srcName);
- if (docFile == null || docFile.getName() == null) return false;
+ if (docFile.getName() == null) return false;
result = true;
} catch (IOException e) {
return false;
@@ -354,7 +354,9 @@ private void takePermissionSAF() throws IOException {
}
}
- private DocumentFile createSAF(@Nullable Context context, String mime, String filename) throws IOException {
+ @NonNull
+ private DocumentFile createSAF(@Nullable Context context, String mime, String filename)
+ throws IOException {
DocumentFile res = StoredDirectoryHelper.findFileSAFHelper(context, docTree, filename);
if (res != null && res.exists() && res.isDirectory()) {
diff --git a/app/src/main/res/drawable-night/ic_select_all.xml b/app/src/main/res/drawable-night/ic_select_all.xml
new file mode 100644
index 00000000000..15773491175
--- /dev/null
+++ b/app/src/main/res/drawable-night/ic_select_all.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_select_all.xml b/app/src/main/res/drawable/ic_select_all.xml
new file mode 100644
index 00000000000..e8693d51bc4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_select_all.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/chip.xml b/app/src/main/res/layout/chip.xml
new file mode 100644
index 00000000000..f7a55fdf34c
--- /dev/null
+++ b/app/src/main/res/layout/chip.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_description.xml b/app/src/main/res/layout/fragment_description.xml
index e3845e89230..c44b88cb6bb 100644
--- a/app/src/main/res/layout/fragment_description.xml
+++ b/app/src/main/res/layout/fragment_description.xml
@@ -2,14 +2,15 @@
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:scrollbars="vertical">
+ android:layout_height="wrap_content"
+ android:animateLayoutChanges="true">
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_metadata.xml b/app/src/main/res/layout/item_metadata.xml
new file mode 100644
index 00000000000..43dce5cfb51
--- /dev/null
+++ b/app/src/main/res/layout/item_metadata.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_metadata_tags.xml b/app/src/main/res/layout/item_metadata_tags.xml
new file mode 100644
index 00000000000..69225b35381
--- /dev/null
+++ b/app/src/main/res/layout/item_metadata_tags.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_channel_item.xml b/app/src/main/res/layout/list_channel_item.xml
index baaddd814d2..6af6add2b82 100644
--- a/app/src/main/res/layout/list_channel_item.xml
+++ b/app/src/main/res/layout/list_channel_item.xml
@@ -34,11 +34,12 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/channel_item_description_to_details_margin"
android:ellipsize="end"
- android:lines="2"
+ android:maxLines="2"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_uploader_text_size"
app:layout_constraintBottom_toTopOf="@+id/itemAdditionalDetails"
- app:layout_constraintLeft_toLeftOf="@+id/itemTitleView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/itemTitleView"
app:layout_constraintTop_toBottomOf="@+id/itemTitleView"
tools:text="Channel description, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit" />
diff --git a/app/src/main/res/layout/main_bg.xml b/app/src/main/res/layout/main_bg.xml
index 996ecdde485..659784e457a 100644
--- a/app/src/main/res/layout/main_bg.xml
+++ b/app/src/main/res/layout/main_bg.xml
@@ -5,6 +5,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
+ android:paddingStart="@dimen/activity_horizontal_margin"
+ android:paddingEnd="@dimen/activity_horizontal_margin"
tools:context=".fragments.detail.VideoDetailFragment">
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 82ee9efae34..21138e156f7 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -720,4 +720,20 @@
إظهار تفاصيل القناة
تعطيل نفق الوسائط إذا واجهت شاشة سوداء أو التقطيع في تشغيل الفيديو
تعطيل نفق الوسائط
+ داخلي
+ خاص
+ غير مدرج
+ عامة
+ عنوان URL للصورة المصغرة
+ المضيف
+ الدعم
+ اللغة
+ الحد العمري
+ الخصوصيّة
+ الرخصة
+ الفئة
+ الصنف
+ تعطيل تحديد النص في الوصف
+ تمكين تحديد نص في الوصف
+ يمكنك الآن تحديد نص داخل الوصف. لاحظ أن الصفحة قد تومض وقد لا تكون الروابط قابلة للنقر أثناء وضع التحديد.
\ No newline at end of file
diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml
index 447de56768e..287de7dba38 100644
--- a/app/src/main/res/values-b+ast/strings.xml
+++ b/app/src/main/res/values-b+ast/strings.xml
@@ -30,7 +30,7 @@
Nun pudo analizase\'l sitiu web
Los fluxos en direuto entá nun se sofiten
Nun pudo consiguise nengún fluxu
- Buff... Esto nun debió asoceder.
+ Buff… Esto nun debió asoceder.
Perdona mas asocedió daqué malo.
Información:
Detalles:
@@ -107,7 +107,7 @@
Llicencies
Ver en GitHub
Llicencia de NewPipe
- Si tienes idees, quies traducir, facer dalgún cambéu nel diseñu, acuriosar poco o muncho\'l códigu... Agradecemos l\'ayuda. ¡Cuanto más se faiga, meyor!
+ Si tienes idees, quies traducir, facer dalgún cambéu nel diseñu, acuriosar poco o muncho\'l códigu… Agradecemos l\'ayuda. ¡Cuanto más se faiga, meyor!
Lleer
Collaboración
Historial
diff --git a/app/src/main/res/values-b+zh+HANS+CN/strings.xml b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
index 2fe2643b6c1..52805a43c08 100644
--- a/app/src/main/res/values-b+zh+HANS+CN/strings.xml
+++ b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
@@ -387,7 +387,7 @@
音调
解除音视挂钩(可能导致失真)
首选“打开”操作
- 打开内容时的默认操作 - %s
+ 打开内容时的默认操作 — %s
没有可下载的串流
字幕
修改播放器字幕比例和背景样式。重启应用生效。
@@ -678,4 +678,20 @@
显示频道详情
如果遇到黑屏或视频播放卡顿的情况,请禁用媒体隧道
禁用媒体隧道
+ 关闭简介中的文本选择功能
+ 内部
+ 私享
+ 未分类
+ 公开
+ 缩略图 URL
+ 所在服务器
+ 支持
+ 语言
+ 年龄限制
+ 私有性
+ 发行许可
+ 标签
+ 类别
+ 开启简介中的文本选择功能
+ 你现在可以选择简介中的文本,注意,在选择模式下,页面可能会闪烁,链接可能无法点击。
\ No newline at end of file
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index 72c113deb56..6dad087b043 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -346,7 +346,7 @@
শেয়ার
পপ-আপ মোডে ওপেন করো
ব্রাউজারে ওপেন করো
- বাদ দিন
+ বাতিল
ইনস্টল
কোন স্ট্রিম প্লেয়ার পাওয়া যায়নি (প্লে করতে VLC ইন্সটল করতে পারেন).
কোন স্ট্রিম প্লেয়ার পাওয়া যায়নি। VLC ইনস্টল করতে চাও\?
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index d84c1421507..a35cf88473f 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -553,7 +553,7 @@
\nEn definitiva, l\'elecció depèn de si preferiu rapidesa a assegurar-vos que l\'informació és precisa.
Desactiva el mode ràpid
Activa el mode ràpid
- Disponible en alguns serveis, normalment és més ràpid, però podria només incloure un nombre limitat de contingut i sovint informació incomplerta (per exemple, sense durada, tipus...).
+ Disponible en alguns serveis, normalment és més ràpid, però podria només incloure un nombre limitat de contingut i sovint informació incomplerta (per exemple, sense durada, tipus…).
Recupera des d\'un feed dedicat si és possible
Actualitza sempre
Temps que ha de passar perquè una subscripció es consideri obsoleta — %s
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 1e3c8fc8af2..66f163baeb1 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -251,7 +251,7 @@
In den Hintergrund wechseln
Zum Pop-up wechseln
Zur normalen Wiedergabe wechseln
- Externe Abspielprogramme unterstützen diese Art von Links nicht
+ Externe Player unterstützen diese Art von Links nicht
Ungültige URL
Keine Video-Streams gefunden
Keine Audio-Streams gefunden
@@ -632,7 +632,7 @@
Nichts
Warteschlange abspielen
Automatische Warteschlange
- Der Wechsel von einem Abspielprogramm zu einem anderen kann Ihre Warteschlange überschreiben
+ Der Wechsel von einem Player zu einem anderen kann deine Warteschlange überschreiben
Überschreiben der Warteschlange bestätigen
Die aktive Player-Warteschlange wird ersetzt
Eingereiht
@@ -680,4 +680,20 @@
Kanal-Details anzeigen
Deaktiviere das Media-Tunneling, wenn bei der Videowiedergabe ein schwarzer Bildschirm oder Stottern auftritt
Media-Tunneling deaktivieren
+ Intern
+ Privat
+ Öffentlich
+ Sprache
+ Altersbeschränkung
+ Sichtbarkeit
+ Lizenz
+ Tags
+ Kategorie
+ Nicht gelistet
+ Vorschaubild-URL
+ Server
+ Unterstützung
+ Auswählen von Text in der Beschreibung deaktivieren
+ Auswählen von Text in der Beschreibung aktivieren
+ Du kannst nun Text innerhalb der Beschreibung auswählen. Beachte, dass die Seite flackern kann und Links im Auswahlmodus möglicherweise nicht anklickbar sind.
\ No newline at end of file
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 07fa633c798..d735a149378 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -645,7 +645,7 @@
Στιγμιότυπα PeerTube
Χρωματισμός ειδοποιήσεων
Επιτρέπει στο Android να τροποποιήσει το χρώμα της ειδοποίησης, σύμφωνα με το κύριο χρώμα του εικονιδίου (δεν διατίθεται σε όλες τις συσκευές)
- Χρήση των εικονιδίων στην οθόνη κλειδώματοςως φόντο και στις ειδοποιήσεις
+ Χρήση των εικονιδίων ως φόντο στην οθόνη κλειδώματος και στις ειδοποιήσεις
Εμφάνιση
Υπολογισμός hash
Ειδοποιήσεις για πρόοδο βίντεο hashing
@@ -680,4 +680,19 @@
Εμφάνιση λεπτομερειών καναλιού
Απενεργοποιήστε το media tunneling, αν εμφανίζεται μαύρη οθόνη ή διακοπτόμενος ήχος κατά την αναπαραγωγή βίντεο
Απενεργοποίηση media tunneling
+ Εσωτερικό
+ Ιδιωτικό
+ Εκτός λίστας
+ Δημόσιο
+ URL εικονιδίου
+ Υποστήριξη
+ Γλώσσα
+ Όριο ηλικίας
+ Ιδιωτικότητα
+ Άδεια
+ Ετικέτες
+ Κατηγορία
+ Απενεργοποίηση επιλογής κειμένου στην περιγραφή
+ Ενεργοποίηση επιλογής κειμένου στην περιγραφή
+ Τώρα μπορείτε να επιλέξετε κείμενο εντός της περιγραφής. Σημειώστε ότι, η σελίδα μπορεί να παρουσιάζει αστάθεια κατά τη διάρκεια της κατάστασης επιλογής κειμένου.
\ No newline at end of file
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index c84a3bf1b78..cb9e0636702 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -660,7 +660,7 @@
Iruzkinak
Desaktibatu bideoaren deskribapena eta informazio gehigarria ezkutatzeko
Erakutsi deskribapena
- Ireki honekin...
+ Ireki honekin…
Irratia
Nabarmenduak
Eduki hau ordaindu duten erabiltzaileentzat soilik dago eskuragarri, eta NewPipe-k ezin du tramsmititu edo deskargatu.
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index dd8c07fd3f1..3476b8cfb7a 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -97,7 +97,7 @@
Serveur non pris en charge
Fichier déjà existant
Lien malformé ou accès à Internet indisponible
- NewPipe télécharge...
+ NewPipe télécharge…
Appuyer pour plus de détails
Veuillez patienter…
Copié dans le presse-papiers
@@ -682,4 +682,20 @@
Afficher les détails de la chaîne
Désactivez le tunnelage multimédia si vous constatez un écran noir ou un bégaiement lors de la lecture d\'une vidéo
Désactiver le tunnelage média
+ Désactiver la sélection de texte dans la description
+ Permettre la sélection de texte dans la description
+ Interne
+ Privé
+ Non répertorié
+ Public
+ URL de la miniature
+ Hôte
+ Support
+ Langue
+ Limite d\'âge
+ Confidentialité
+ Licence
+ Étiquettes
+ Catégorie
+ Vous pouvez maintenant sélectionner du texte à l\'intérieur de la description. Notez que la page peut scintiller et que les liens peuvent ne pas être cliquables en mode sélection.
\ No newline at end of file
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 276f868c2fb..d9b074f422d 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -700,4 +700,20 @@
להציג את פרטי הערוץ
כדאי להשבית תיעול מדיה אם הופיעה תופעה של מסך שחור או גמגום בנגינת וידאו
השבתת תיעול מדיה
+ פנימי
+ פרטי
+ לא מופיע ברשימות
+ ציבורי
+ כתובת תמונה ממוזערת
+ אירוח
+ תמיכה
+ שפה
+ הגבלת גיל
+ פרטיות
+ רישיון
+ תגיות
+ קטגוריה
+ השבתת בחירת טקסט בתיאור
+ הפעלת בחירת טקסט בתיאור
+ מעתה ניתן לבחור טקסט בתוך התיאור. נא לשים לב שהעמוד עשוי להבהב והקישורים לא יהיו לחיצים בזמן מצב הבחירה.
\ No newline at end of file
diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml
index 01372470d97..95703334bbc 100644
--- a/app/src/main/res/values-ia/strings.xml
+++ b/app/src/main/res/values-ia/strings.xml
@@ -54,7 +54,7 @@
Chronologia de cerca
Immagazinar le cercas localmente
Chronologia de reproductiones
- We
+ Resumer le reproduction
Restaurar le ultime position del reproduction
Positiones in le listas
Monstrar le indicatores de position in listas
@@ -151,7 +151,7 @@
Configurationes
A proposito de
© %1$s per %2$s sub %3$s
- Aperir sito web
+ Aperir le sito web
A proposito de
Licentias
Contribuer
@@ -207,4 +207,54 @@
Nemo is observante
Chronologia de reproductiones vacuate.
Deler omne chronologia de reproductiones\?
+ Pardono, qualcosa vadeva incorrecte.
+ Pardono, illo non deberea haber ocurrite.
+ Deler le positiones de reproduction
+ Cantos
+ Listas de reproduction
+ Lista de reproduction
+ Reporto de error
+ Activar el \"Modo restricte\" de YouTube
+ Plus de 100 videos
+ Nemo es ascoltante
+
+ - %s reproduction
+ - %s reproductiones
+
+ Le numero de subscriptores non es disponibile
+
+ - %s subscriptor
+ - %s subscriptores
+
+ Nulle subscriptores
+ Audio
+ Video
+ Nihil contentos ci
+ Nulle resultatos
+ Reporto de usator
+ Reportar un error
+ Description
+ Fluxos associate
+ Non me place
+ Commentos
+ Me place
+ Reportar
+ Reportar in GitHub
+ Permitter monstrar supra altere applicationes
+ Esque tu vole reinitialisar le valores predefinite\?
+ Reinitialisar le valores predefinite
+ Nulle fluxos disponibile pro discargar
+ Ocurreva un error: %1$s
+ File displaciate o delite
+ Falleva le interfacie del application
+ Le discarga al carta SD non es possibile. Reinitialisar le location del dossier de discargas\?
+ Immagazinage externe non disponibile
+ Error
+ Adjuta
+ Esque tu vole deler tote le positiones de reproduction\?
+ Dele tote le positiones de reproduction
+ Usar le recerca rapide e inexacte
+ Thema nocturne
+ Nihil
+ Monstrar le description
\ No newline at end of file
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 98ea1a5c92d..cee727ebc9e 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -656,7 +656,7 @@
Konten ini bersifat pribadi, jadi tidak dapat di stream ataupun diunduh oleh NewPipe.
Ini adalah sebuah trek SoundCloud Go +, setidaknya di negara Anda, sehingga tidak dapat di stream atau diunduh oleh Newpipe.
Konten ini tidak tersedia di negara Anda.
- Aplikasi crash
+ Hentikan aplikasi
Video ini dibatasi usia.
\nKarena kebijakan YouTube yang baru dengan video yang dibatasi usia, Newpipe tidak dapat mengakses aliran video dan karenanya tidak dapat memainkannya.
Kamu bisa pilih tema malam favoritmu dibawah
@@ -670,4 +670,20 @@
Tampilkan rincian channel
Nonaktifkan terowongan media (tunnel) jiaka anda mengalami sebuah layar hitam atau kerusakan dalam memutar video
Nonaktifkan terowongan media (tunnel)
+ Internal
+ Privasi
+ Tidak didaftar
+ Publik
+ Alamat URL gambar mini/thumbnail
+ Host
+ Dukungan
+ Bahasa
+ Batas umur
+ Privasi
+ Lisensi
+ Tag
+ Kategori
+ Nonaktifkan dapat memilih teks pada deskripsi
+ Aktifkan dapat memilih teks pada deskripsi
+ Anda sekarang dapat memilih teks di dalam deskripsi. Perhatikan bahwa halaman mungkin berkedip dan tautan tidak dapat diklik saat dalam mode pemilihan.
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 42cff5ed1b2..1efc03209b1 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -680,4 +680,20 @@
Mostra dettagli canale
Disattiva il tunneling multimediale se durante la riproduzione dei video si verificano schermate nere o irregolarità nell\'audio
Disattiva tunneling multimediale
+ Interno
+ Privato
+ Non elencato
+ Pubblico
+ URL miniatura
+ Host
+ Supporto
+ Lingua
+ Limite di età
+ Privacy
+ Licenza
+ Etichette
+ Categoria
+ Disattiva la selezione del testo nella descrizione
+ Attiva la selezione del testo nella descrizione
+ Ora puoi selezionare il testo all\'interno della descrizione. Nota che la pagina potrebbe sfarfallare e i collegamenti potrebbero non essere cliccabili mentre sei in modalità di selezione.
\ No newline at end of file
diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml
index a0f111be0e9..4c54d5edef0 100644
--- a/app/src/main/res/values-nl-rBE/strings.xml
+++ b/app/src/main/res/values-nl-rBE/strings.xml
@@ -665,4 +665,19 @@
Toon beschrijving
Nacht Thema
Open met
+ Featured
+ Deze inhoud is privé, waardoor het niet kan worden gestreamd of gedownload door NewPipe.
+ Laat de app crashen
+ Schakel media tunneling uit indien u een zwart scherm of haperingen vaststelt bij het afspelen van video\'s
+ De download is begonnen
+ U kunt uw favoriete nachtthema hieronder kiezen
+ Kies uw favoriete nachtthema - %s
+ Automatisch (thema van de telefoon)
+ Radio
+ Deze inhoud is enkel toegankelijk voor betalende gebruikers, waardoor het niet kan worden gestreamd of gedownload door NewPipe.
+ Deze video is enkel toegankelijk voor YouTube Music Premium leden, waardoor het niet kan worden gestreamd of gedownload door NewPipe.
+ Dit is (tenminste in uw land) een SoundCloud Go+ titel, waardoor het niet kan worden gestreamd of gedownload door NewPipe.
+ Deze inhoud is niet toegankelijk in uw land.
+ Geen app beschikbaar om dit te openen
+ Hoofdstukken
\ No newline at end of file
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 8bf1b8796ec..adc400ea255 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -72,7 +72,7 @@
Video
Geluid
Opnieuw proberen
- Druk op \"zoeken\" om te beginnen.
+ Druk op \"Zoeken\" om te beginnen.
Automatisch afspelen
Speelt video’s af als NewPipe vanuit een andere app wordt geopend
Live
@@ -262,8 +262,8 @@
Bezig met laden van gevraagde inhoud
Databank importeren
Databank exporteren
- Dit overschrijft je huidige geschiedenis, abonnementen, playlists en instellingen
- Exporteer geschiedenis, abonnementen, playlists en instellingen
+ Dit overschrijft je huidige geschiedenis, abonnementen, afspeellijsten en instellingen
+ Exporteer geschiedenis, abonnementen, afspeellijsten en instellingen
Geëxporteerd
Geïmporteerd
Geen geldig ZIP-bestand
@@ -599,7 +599,7 @@
\nSchakel \"%1$s\" in bij de instellingen als u die wilt zien.
Verwijder bekeken
Originele teksten van services zijn zichtbaar in stream-items
- YouTube \"beperkte modus\" aanzetten
+ YouTube \"Beperkte modus\" aanzetten
Laat originele tijd geleden zien
Kanaal avatar afbeelding
Door %s
@@ -641,7 +641,7 @@
Verwijder cookies die NewPipe opslaat wanneer u een reCAPTCHA oplost
reCAPTCHA cookies zijn verwijderd
Verwijder reCAPTCHA cookies
- YouTube biedt een \"beperkte modus\" aan; dit verbergt mogelijk materiaal voor volwassenen
+ YouTube biedt een \"Beperkte modus\" aan, dit verbergt mogelijk materiaal voor volwassenen
Toon inhoud die mogelijk niet geschikt is voor kinderen omwille van een leeftijdslimiet (zoals 18+)
Laat Android de kleur van de notificatie aanpassen, op basis van de meest voorkomende kleur in de thumbnail (let op: niet beschikbaar op elk apparaat)
Notificatie kleur aanpassen
@@ -675,7 +675,25 @@
Downloaden is gestart
Je kan je favoriete nacht thema hier beneden selecteren
Selecteer uw favoriete nacht thema — %s
- Automatisch (apparaat thema)
+ Automatisch (toestel thema)
Toon details van kanaal
Nachtmodus
+ Intern
+ Privé
+ Niet vermeld
+ Openbaar
+ Miniatuur-URL
+ Host
+ Ondersteuning
+ Taal
+ Leeftijdslimiet
+ Privacy
+ Licentie
+ Tags
+ Categorie
+ Tekst selecteren in de beschrijving uitschakelen
+ Tekst selecteren in de beschrijving inschakelen
+ U kunt nu tekst in de beschrijving selecteren. Houd er rekening mee dat de pagina kan flikkeren en dat links mogelijk niet klikbaar zijn in de selectiemodus.
+ Media tunneling uitschakelen als u een zwart scherm ervaart of video hapert bij het afspelen
+ Media tunneling uitschakelen
\ No newline at end of file
diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml
index a6b3daec935..df3cc588c29 100644
--- a/app/src/main/res/values-or/strings.xml
+++ b/app/src/main/res/values-or/strings.xml
@@ -1,2 +1,5 @@
-
\ No newline at end of file
+
+ %1$s ଭିଉଜ、
+ ଆରମ୍ଭ କରିବା ପାଇଁ \"ସର୍ଚ୍ଚ\" ବଟନ କୁ ଦବାନ୍ତୁ
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 716a96f85ad..8fb599aa249 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -690,4 +690,18 @@
Pokaż szczegóły kanału
Wyłącz tunelowanie multimediów jeśli zaobserwowałeś czarny ekran bądź brak płynności odtwarzania wideo
Wyłącz tunelowanie multimediów
+ Ograniczenie wieku
+ Wyłącz zaznaczanie tekstu w opisie
+ Włącz zaznaczanie tekstu w opisie
+ Wewnętrzny
+ Prywatny
+ Publiczny
+ Adres URL miniatury
+ Host
+ Wsparcie
+ Język
+ Prywatność
+ Licencja
+ Tagi
+ Kategoria
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 218bfd84419..650694d5bd8 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -680,4 +680,20 @@
Mostrar detalhes do canal
Desative o túnel de mídia se você tiver uma tela preta ou a reprodução do vídeo estiver engasgando
Desativar túnel de mídia
+ Interno
+ Privado
+ Não Listado
+ Público
+ Limite de Idade
+ URL da Capa
+ Hospedado em
+ Suporte
+ Idioma
+ Privacidade
+ Licença
+ Tags
+ Categoria
+ Desativar seleção de texto na descrição
+ Ativar seleção de texto na descrição
+ Agora você pode selecionar o texto dentro da descrição. Observe que a página pode piscar e os links podem não ser clicáveis no modo de seleção.
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index cda5c24f2ea..3f05b39094a 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -623,7 +623,7 @@
Pedir confirmação antes de limpar uma fila
Lembrar do último tamanho e posição do popup
Nada
- Processamento...
+ Processamento…
Baralhar
Repetir
Pode selecionar, no máximo, três ações para mostrar na notificação compacta!
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 124d7bc9275..7e91b18459d 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -621,7 +621,7 @@
Iniciar reprodução automaticamente — %s
Reproduzir fila
Nunca
- Processamento...
+ Processamento…
A fila do reprodutor ativo será substituída
URL não reconhecido. Abrir com outra aplicação\?
Colocar na fila automaticamente
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 888fec2dcb3..6fcee1f2305 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -691,4 +691,20 @@
Подробно о канале
Отключите туннелирование медиа, если вы видите черный экран или подёргивание при воспроизведении видео
Отключить туннелирование медиа
+ Теперь вы можете выделить текст внутри описания. Заметьте, что страница может мигать и ссылки могут быть некликабельны в режиме выделения.
+ Поддержка
+ Язык
+ Возрастное ограничение
+ Лицензия
+ Тэги
+ Категория
+ Отключить выделение текста в описании
+ Включить выделение текста в описании
+ Внутренняя
+ Приватная
+ Не в списке
+ Публичная
+ URL миниатюры
+ Сервер
+ Доступность
\ No newline at end of file
diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml
index 2d5597d90d3..955e4d5e028 100644
--- a/app/src/main/res/values-sc/strings.xml
+++ b/app/src/main/res/values-sc/strings.xml
@@ -680,4 +680,20 @@
Ammustra sos detàllios de su canale
Disabìlita sa tunnellizatzione de sos elementos multimediales si durante sa riprodutzione bi sunt ischermadas nieddas o su flussu de su vìdeu no est regulare
Disabìlita sa tunnellizatzione de sos mèdios
+ Internu
+ No elencadu
+ Privadu
+ Pùblicu
+ URL de sa miniadura
+ Istrangiadore
+ Suportu
+ Limba
+ Lìmite de edade
+ Riservadesa
+ Litzèntzia
+ Etichetas
+ Categoria
+ Disabìlita s\'ischerta de su testu in sa descritzione
+ Abìlita s\'ischerta de su testu in sa descritzione
+ Como podes ischertare su testu in intro de sa descritzione. Ammenta·ti chi sa pàgina diat pòdere trèmere e sos ligàmenes si diant pòdere no abèrrere cando ses in modalidade de ischerta.
\ No newline at end of file
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index d7d1399bf6e..e31aa4f10b4 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -26,7 +26,7 @@
Prehrať cez Kodi
Nainštalovať chýbajúcu aplikáciu Kore\?
Zobraziť možnosť \"Prehrať cez Kodi\"
- Zobraziť možnosť \"Prehraj cez Kodi\"
+ Zobraziť možnosť \"Prehrať cez Kodi\"
Zvuk
Predvolený zvukový formát
Téma
@@ -128,7 +128,7 @@
Filter
Obnoviť
Vyčistiť
- Neprehrá audio pri niektorých rozlíšeniach
+ Odstráni audio pri niektorých rozlíšeniach
Zapamätať si parametre mini okna
Zapamätať si posledné nastavenie veľkosti a pozície mini okna
Ovládanie prehrávača gestami
@@ -313,7 +313,7 @@
Odstrániť všetky údaje webových stránok vo vyrovnávacej pamäti
Vyrovnávacia pamäť metadát bola vymazaná
Automaticky zaradiť daľší stream
- Končiaci neopakujúci sa zoznam prehrávania bude pokračovať súvisiacim streamom
+ Končiaci (neopakujúci sa) zoznam prehrávania bude pokračovať súvisiacim streamom
Ladenie
Súbor
Neplatný adresár
@@ -690,4 +690,20 @@
Nočná Téma
Ak vám video pri prehrávaní seká alebo sa zobrazuje čierna obrazovka zakážte tunelovanie médií
Zakázať tunelovanie médií
+ Interné
+ Súkromné
+ Nezaradené
+ URL miniatúry
+ Verejné
+ Hostiteľ
+ Podpora
+ Jazyk
+ Vekový limit
+ Ochrana osobných údajov
+ Licencia
+ Tagy
+ Kategória
+ Zakázať výber textu v popise
+ Povolenie výberu textu v popise
+ Teraz môžete vybrať text vo vnútri popisu. Upozorňujeme, že stránka môže blikať a odkazy nemusia byť klikateľné, keď je v režime výberu.
\ No newline at end of file
diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml
index 93d14b571e4..ab7038b3445 100644
--- a/app/src/main/res/values-so/strings.xml
+++ b/app/src/main/res/values-so/strings.xml
@@ -319,7 +319,7 @@
U ogolow app-ka inuu dul fuulo applicationada kale
Ma rabtaa inaad sidii hore kuceliso\?
Dib ugu celi sidii hore
- Lama akhrin karo daaqadihii la kaydiyay, ...isticmaalaya kuwii app-ku kusoo baxay
+ Lama akhrin karo daaqadihii la kaydiyay, …isticmaalaya kuwii app-ku kusoo baxay
Wax la dajiyo lama heli karo
Khalad ayaa ka dhacay: %1$s
Magaca shayga ma madhnaan karo
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index bd9293f897a..d607a3347e5 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -76,7 +76,7 @@
Пријавите грешку
Извештај корисника
Уживо
- тапните на лупу да започнете
+ Тапните на лупу да започнете.
Аутопуштање
Пушта видео по позиву из друге апликације
Почни
@@ -139,10 +139,10 @@
Прати се
Прати
Главно
- Претплате
+ Праћења
Шта је ново
Историјат претраге
- Уписуј појмове претраге локално
+ Појмове претраге складишти локално
Историја гледања
Чувај историјат гледања
Настави са репродукцијом
@@ -166,9 +166,9 @@
Историјат
Историјат је празан
Историјат очишћен
- Настави репродукцију након прекида (нпр. позива)
+ Настави пуштање након прекида (нпр. позива)
Дозвољени знакови у називима
- Слова и бројке
+ слова и бројке
© %1$s од %2$s под %3$s
Не могу да учитам лиценцу
Одјављен са канала
@@ -183,11 +183,11 @@
Обавештења за Њупајп плејере у позадини и искачућем прозору
Нема резултата
Нема ничега овде осим цврчака
- Нема претплатника
+ Нема пратилаца
- - %s претплатник
- - %s претплатника
- - %s претплатника
+ - %s пратилац
+ - %s пратилаца
+ - %s пратилаца
Нема прегледа
@@ -203,7 +203,7 @@
Неисправни знакови биће замењени овим
Знак за замену
- Посебни знакови
+ и посебни знакови
Лиценце треће стране
Лиценца Њупајпа
Помоћ је увек добро дошла, било да имате идеју за превод, дизајн, козметичке или озбиљне измене кôда. Што се више уради, боље је!
@@ -331,23 +331,23 @@
Тапните за преузимање
Доступно је ажурирање за ЊуПајп!
Промени приказ
- Аутоматски
- Мрежа
- Листа
+ аутоматски
+ мрежа
+ листа
Режим приказа листе
- Никад
- Само на бежичној
- Покрени пуштање аутоматски — %s
- Умањи на искачући плејер
- Умањи на позадински плејер
+ никад
+ само на бежичној
+ Покрени аутоматски — %s
+ умањи на искачући плејер
+ умањи на позадински плејер
ништа
Радња при преласку на другу апликацију са главног видео плејера — %s
Умањи при мењању апликације
Прикажи обавештење када постоји нова верзија апликације
Ажурирања
- Ограничи резолуцију када користим мобилне податке
+ Ограничити резолуцију на мобилној мрежи
Одбиј
- Без органичења
+ без ограничења
Прихвати
Да бисмо били у складу са Европском општом уредбом о заштити података („GDPR“), скрећемо вам пажњу на политику приватности апликације ЊуПајпа. Пажљиво је прочитајте.
\nМорате је прихватити да бисте нам послали извештај о грешци.
@@ -484,7 +484,7 @@
- %s гледалаца
Нико не гледа
- Број претплатника није доступан
+ Број пратилаца није доступан
Мењај услугу, тренутно изабрана:
Превуците за преуређивање
Опис
@@ -554,7 +554,7 @@
\n
\nУкључите „%1$s“ у поставкама ако желите да га видите.
Јутјуб омогућава „Ограничени режим“ који скрива потенцијални садржај за одрасле
- Укључите Јутјубов „Ограничени режим“
+ Укључити Јутјубов „Ограничени режим“
Приказ садржаја који можда није прикладан за децу јер има старосну границу (попут 18+)
Обавештење
Ажурирања
@@ -611,7 +611,7 @@
Преузимање је започело
Испод можете одабрати своју омиљену ноћну тему
Изаберите своју омиљену ноћну тему — %s
- Аутоматски (тема уређаја)
+ аутоматски (тема уређаја)
Радио
Истакнуто
Овај садржај је доступан само корисницима који су платили, тако да га ЊуПајп не може стримовати или преузимати.
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 9f9a81990d9..3b06823f5e5 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -1,6 +1,6 @@
- Tryck på \"Sök\" för att komma igång
+ Tryck på förstoringsglaset för att komma igång
Publicerad den %1$s
Ingen strömspelare hittades. Vill du installera VLC?
Installera
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 0c34781f86c..ccc9f4b3465 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -670,4 +670,20 @@
顯示頻道詳細資訊
如果您遇到黑畫面或影片播放停頓的現象,請停用媒體隧道
停用媒體隧道
+ 內部
+ 私人
+ 未列出
+ 公開
+ 縮圖 URL
+ 主機
+ 支援
+ 語言
+ 年齡限制
+ 隱私
+ 授權條款
+ 標籤
+ 分類
+ 停用選取描述中的文字
+ 啟用選取描述中的文字
+ 您現在可以選取描述中的文字了。請注意,在選取模式下,頁面可能會閃爍,連結也可能無法點擊。
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b8ca33c8783..4fb4019bd63 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -716,4 +716,20 @@
Select your favorite night theme — %s
You can select your favorite night theme below
Download has started
+ You can now select text inside the description. Note that the page may flicker and links may not be clickable while in selection mode.
+ Enable selecting text in the description
+ Disable selecting text in the description
+ Category
+ Tags
+ Licence
+ Privacy
+ Age limit
+ Language
+ Support
+ Host
+ Thumbnail URL
+ Public
+ Unlisted
+ Private
+ Internal
\ No newline at end of file
diff --git a/app/src/test/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelperTest.java b/app/src/test/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelperTest.java
index d3d1fa4190a..679c9da1d81 100644
--- a/app/src/test/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelperTest.java
+++ b/app/src/test/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelperTest.java
@@ -91,7 +91,7 @@ private List readFromFile() throws Exception {
final List itemsFromFile = ImportExportJsonHelper.readFrom(
inputStream, null);
- if (itemsFromFile == null || itemsFromFile.isEmpty()) {
+ if (itemsFromFile.isEmpty()) {
fail("ImportExportJsonHelper.readFrom(input) returned a null or empty list");
}
@@ -116,7 +116,7 @@ private List readFromWriteTo(final String jsonOut) throws Exce
final List secondReadItems = ImportExportJsonHelper.readFrom(
inputStream, null);
- if (secondReadItems == null || secondReadItems.isEmpty()) {
+ if (secondReadItems.isEmpty()) {
fail("second call to readFrom returned an empty list");
}
diff --git a/app/src/test/java/org/schabi/newpipe/player/playqueue/PlayQueueTest.java b/app/src/test/java/org/schabi/newpipe/player/playqueue/PlayQueueTest.java
new file mode 100644
index 00000000000..43e0909006a
--- /dev/null
+++ b/app/src/test/java/org/schabi/newpipe/player/playqueue/PlayQueueTest.java
@@ -0,0 +1,183 @@
+package org.schabi.newpipe.player.playqueue;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.schabi.newpipe.extractor.stream.StreamInfoItem;
+import org.schabi.newpipe.extractor.stream.StreamType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+@SuppressWarnings("checkstyle:HideUtilityClassConstructor")
+public class PlayQueueTest {
+ static PlayQueue makePlayQueue(final int index, final List streams) {
+ // I tried using Mockito, but it didn't work for some reason
+ return new PlayQueue(index, streams) {
+ @Override
+ public boolean isComplete() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void fetch() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ static PlayQueueItem makeItemWithUrl(final String url) {
+ final StreamInfoItem infoItem = new StreamInfoItem(
+ 0, url, "", StreamType.VIDEO_STREAM
+ );
+ return new PlayQueueItem(infoItem);
+ }
+
+ public static class SetIndexTests {
+ private static final int SIZE = 5;
+ private PlayQueue nonEmptyQueue;
+ private PlayQueue emptyQueue;
+
+ @Before
+ public void setup() {
+ final List streams = new ArrayList<>(5);
+ for (int i = 0; i < 5; ++i) {
+ streams.add(makeItemWithUrl("URL_" + i));
+ }
+ nonEmptyQueue = spy(makePlayQueue(0, streams));
+ emptyQueue = spy(makePlayQueue(0, new ArrayList<>()));
+ }
+
+ @Test
+ public void negative() {
+ nonEmptyQueue.setIndex(-5);
+ assertEquals(0, nonEmptyQueue.getIndex());
+
+ emptyQueue.setIndex(-5);
+ assertEquals(0, nonEmptyQueue.getIndex());
+ }
+
+ @Test
+ public void inBounds() {
+ nonEmptyQueue.setIndex(2);
+ assertEquals(2, nonEmptyQueue.getIndex());
+
+ // emptyQueue not tested because 0 isn't technically inBounds
+ }
+
+ @Test
+ public void outOfBoundIsComplete() {
+ doReturn(true).when(nonEmptyQueue).isComplete();
+ nonEmptyQueue.setIndex(7);
+ assertEquals(2, nonEmptyQueue.getIndex());
+
+ doReturn(true).when(emptyQueue).isComplete();
+ emptyQueue.setIndex(2);
+ assertEquals(0, emptyQueue.getIndex());
+ }
+
+ @Test
+ public void outOfBoundsNotComplete() {
+ doReturn(false).when(nonEmptyQueue).isComplete();
+ nonEmptyQueue.setIndex(7);
+ assertEquals(SIZE - 1, nonEmptyQueue.getIndex());
+
+ doReturn(false).when(emptyQueue).isComplete();
+ emptyQueue.setIndex(2);
+ assertEquals(0, emptyQueue.getIndex());
+ }
+
+ @Test
+ public void indexZero() {
+ nonEmptyQueue.setIndex(0);
+ assertEquals(0, nonEmptyQueue.getIndex());
+
+ doReturn(true).when(emptyQueue).isComplete();
+ emptyQueue.setIndex(0);
+ assertEquals(0, emptyQueue.getIndex());
+
+ doReturn(false).when(emptyQueue).isComplete();
+ emptyQueue.setIndex(0);
+ assertEquals(0, emptyQueue.getIndex());
+ }
+
+ @Test
+ public void addToHistory() {
+ nonEmptyQueue.setIndex(0);
+ assertFalse(nonEmptyQueue.previous());
+
+ nonEmptyQueue.setIndex(3);
+ assertTrue(nonEmptyQueue.previous());
+ assertEquals("URL_0", Objects.requireNonNull(nonEmptyQueue.getItem()).getUrl());
+ }
+ }
+
+ public static class GetItemTests {
+ private static List streams;
+ private PlayQueue queue;
+
+ @BeforeClass
+ public static void init() {
+ streams = new ArrayList<>(Collections.nCopies(5, makeItemWithUrl("OTHER_URL")));
+ streams.set(3, makeItemWithUrl("TARGET_URL"));
+ }
+
+ @Before
+ public void setup() {
+ queue = makePlayQueue(0, streams);
+ }
+
+ @Test
+ public void inBounds() {
+ assertEquals("TARGET_URL", Objects.requireNonNull(queue.getItem(3)).getUrl());
+ assertEquals("OTHER_URL", Objects.requireNonNull(queue.getItem(1)).getUrl());
+ }
+
+ @Test
+ public void outOfBounds() {
+ assertNull(queue.getItem(-1));
+ assertNull(queue.getItem(5));
+ }
+ }
+
+ public static class EqualsTests {
+ private final PlayQueueItem item1 = makeItemWithUrl("URL_1");
+ private final PlayQueueItem item2 = makeItemWithUrl("URL_2");
+
+ @Test
+ public void sameStreams() {
+ final List streams = Collections.nCopies(5, item1);
+ final PlayQueue queue1 = makePlayQueue(0, streams);
+ final PlayQueue queue2 = makePlayQueue(0, streams);
+ assertEquals(queue1, queue2);
+ }
+
+ @Test
+ public void sameSizeDifferentItems() {
+ final List streams1 = Collections.nCopies(5, item1);
+ final List streams2 = Collections.nCopies(5, item2);
+ final PlayQueue queue1 = makePlayQueue(0, streams1);
+ final PlayQueue queue2 = makePlayQueue(0, streams2);
+ assertNotEquals(queue1, queue2);
+ }
+
+ @Test
+ public void differentSizeStreams() {
+ final List streams1 = Collections.nCopies(5, item1);
+ final List streams2 = Collections.nCopies(6, item2);
+ final PlayQueue queue1 = makePlayQueue(0, streams1);
+ final PlayQueue queue2 = makePlayQueue(0, streams2);
+ assertNotEquals(queue1, queue2);
+ }
+ }
+}
diff --git a/fastlane/metadata/android/de/changelogs/968.txt b/fastlane/metadata/android/de/changelogs/968.txt
new file mode 100644
index 00000000000..a619dc5c2f6
--- /dev/null
+++ b/fastlane/metadata/android/de/changelogs/968.txt
@@ -0,0 +1,7 @@
+Option "Kanaldetails" wurde zum Langdruckmenü hinzugefügt.
+Funktion zum Umbenennen des Playlist-Namens von der Playlist-Oberfläche zugefügt.
+Benutzer kann nun pausieren, während ein Video gepuffert wird.
+Weiße Thema wurde aufpoliert.
+Überlappende Schriftarten bei Verwendung einer größeren Schriftgröße behoben.
+Kein Video auf Formuler- und Zephier-Geräten behoben.
+Verschiedene Abstürze behoben.
diff --git a/fastlane/metadata/android/de/changelogs/969.txt b/fastlane/metadata/android/de/changelogs/969.txt
new file mode 100644
index 00000000000..e6cfbdd84b8
--- /dev/null
+++ b/fastlane/metadata/android/de/changelogs/969.txt
@@ -0,0 +1,8 @@
+• Installation auf externen Speicher möglich
+• [Bandcamp] Unterstützung für die Anzeige der ersten drei Kommentare in einem Stream
+• Nur Hinweisanzeige "Download gestartet", wenn der Download gestartet wird
+• Setzt kein reCaptcha-Cookie, wenn kein Cookie gespeichert wird
+• Cache-Leistung verbessert
+• Problem behoben, dass die Wiedergabe nicht automatisch abspielt wird
+• Verwerfen früherer Snackbars beim Löschen von Downloads
+• Fehler beim Löschen des Objekts, das nicht in der Liste enthalten ist
diff --git a/fastlane/metadata/android/de/changelogs/970.txt b/fastlane/metadata/android/de/changelogs/970.txt
new file mode 100644
index 00000000000..301ea1e3f0e
--- /dev/null
+++ b/fastlane/metadata/android/de/changelogs/970.txt
@@ -0,0 +1,11 @@
+Neu
+• Inhaltsmetadaten (Tags, Kategorien, Lizenz, ...) unter der Beschreibung anzeigen
+• Option "Kanaldetails anzeigen" in remote (nicht lokalen) Wiedergabelisten hinzugefügt
+• Option "Im Browser öffnen" zum Langdruck-Menü hinzugefügt
+
+Behoben
+• Rotationsabsturz auf der Videodetailseite
+• "Mit Kodi spielen"-Button im Player fordert immer auf, Kore zu installieren
+• Setzen von Import- und Exportpfaden wurde behoben und verbessert
+• [YouTube] Anzahl Kommentar-Likes korrigiert
+Und vieles mehr
diff --git a/fastlane/metadata/android/en-US/changelogs/970.txt b/fastlane/metadata/android/en-US/changelogs/970.txt
new file mode 100644
index 00000000000..f4ff5fe3483
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/970.txt
@@ -0,0 +1,11 @@
+New
+• Show content metadata (tags, categories, license, ...) below the description
+• Added "Show channel details" option in remote (non-local) playlists
+• Added "Open in browser" option to long-press menu
+
+Fixed
+• Fixed rotation crash on video detail page
+• Fixed "Play with Kodi" button in player always prompts to install Kore
+• Fixed and improved setting import and export paths
+• [YouTube] Fixed comment like count
+And much more
diff --git a/fastlane/metadata/android/fr/changelogs/963.txt b/fastlane/metadata/android/fr/changelogs/963.txt
new file mode 100644
index 00000000000..0ea6c42741c
--- /dev/null
+++ b/fastlane/metadata/android/fr/changelogs/963.txt
@@ -0,0 +1 @@
+• [YouTube] rétablissement des flux
diff --git a/fastlane/metadata/android/fr/changelogs/967.txt b/fastlane/metadata/android/fr/changelogs/967.txt
new file mode 100644
index 00000000000..29bdde9f111
--- /dev/null
+++ b/fastlane/metadata/android/fr/changelogs/967.txt
@@ -0,0 +1 @@
+La réparation du flux YouTube ne fonctionnait pas correctement dans l'UE. Cela était dû à un nouveau système de cookies et de consentement à la confidentialité qui exige que NewPipe définisse un cookie CONSENT.
diff --git a/fastlane/metadata/android/fr/changelogs/968.txt b/fastlane/metadata/android/fr/changelogs/968.txt
new file mode 100644
index 00000000000..06b84c7fda1
--- /dev/null
+++ b/fastlane/metadata/android/fr/changelogs/968.txt
@@ -0,0 +1,6 @@
+Ajout d'une option de détails sur les chaînes dans le menu de pression longue.
+Ajout d'une fonctionnalité permettant de renommer le nom de la liste de lecture à partir de l'interface de la liste de lecture.
+Permet à l'utilisateur de faire une pause pendant la mise en mémoire tampon d'une vidéo.
+Le thème blanc a été amélioré.
+Correction du chevauchement des polices lors de l'utilisation d'une taille de police plus grande.
+Correction de l'absence de vidéo sur les appareils Formuler et Zephier.
diff --git a/fastlane/metadata/android/id/changelogs/969.txt b/fastlane/metadata/android/id/changelogs/969.txt
new file mode 100644
index 00000000000..7f081aca2e7
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/969.txt
@@ -0,0 +1,8 @@
+• Izinkan instalasi pada penyimpanan eksternal
+• [Bandcamp] Penambahan dukungan untuk menampilkan tiga komentar pertama pada stream
+• Hanya Tampilkan toast 'Unduh telah dimulai' saat mengunduh dimulai
+• Jangan mengatur cookie reCaptcha ketika tidak ada cookie yang disimpan
+• [Pemutar] Peningkatan kinerja cache
+• [Pemutar] Tidak memutar secara otomatis
+• Abaikan Snackbars sebelumnya saat menghapus unduhan
+• Perbaikan mencoba menghapus objek tidak dalam daftar
diff --git a/fastlane/metadata/android/lt/full_description.txt b/fastlane/metadata/android/lt/full_description.txt
index e729e722144..07f08197564 100644
--- a/fastlane/metadata/android/lt/full_description.txt
+++ b/fastlane/metadata/android/lt/full_description.txt
@@ -1 +1 @@
-NewPipe nenaudoja jokių Google paslaugų ar Youtube API. Jisai tiktai nuskanuoja svetaine dėl reikemos informacijos. Dėlto NewPipe nereikia įdiegtų Google paslaugų. Ir jums nereikia Youtube paskyros norint naudoti NewPipe taip pat yra FLOSS.
+NewPipe nenaudoja jokių Google paslaugų ar Youtube API. Programa tik paima informaciją kurios jai reikia. Dėlto NewPipe nereikia įdiegtų Google paslaugų. Taipogi jums nereikia Youtube paskyros norint naudoti NewPipe, tai yra FLOSS.
diff --git a/fastlane/metadata/android/lt/short_description.txt b/fastlane/metadata/android/lt/short_description.txt
new file mode 100644
index 00000000000..c5fd36f0afd
--- /dev/null
+++ b/fastlane/metadata/android/lt/short_description.txt
@@ -0,0 +1 @@
+Nemokama ir lengva "YouTube" sąsaja skirta "Android".
diff --git a/fastlane/metadata/android/or/short_description.txt b/fastlane/metadata/android/or/short_description.txt
index e1b83993b82..83e4166bda1 100644
--- a/fastlane/metadata/android/or/short_description.txt
+++ b/fastlane/metadata/android/or/short_description.txt
@@ -1 +1 @@
-ଏକ ମାଗଣା ହାଲୁକା Youtube ସେବା Android ପାଇଁ |
+ଏକ ମାଗଣା ହାଲୁକା Youtube ସେବା Android ପାଇଁ
diff --git a/fastlane/metadata/android/sk/changelogs/969.txt b/fastlane/metadata/android/sk/changelogs/969.txt
new file mode 100644
index 00000000000..b8039948263
--- /dev/null
+++ b/fastlane/metadata/android/sk/changelogs/969.txt
@@ -0,0 +1,8 @@
+• Povoliť inštaláciu na externom úložisku
+ • [Bandcamp] Pridaná podpora pre zobrazovanie prvých troch komentárov v streame
+ • Zobraziť „sťahovanie začalo“ sa zobrazí až pri začatí sťahovania
+ • Nenastavovať súbor cookie reCaptcha, ak nie je uložený žiadny súbor cookie
+ • [Player] Zlepšenie výkonu medzipamäte
+ • [Prehrávač] Opravené automatické prehrávanie
+ • Zavrieť predchádzajúce Snackbary pri odstraňovaní sťahovaní
+ • Opravený pokus o odstránenie objektu, ktorý nie je na zozname
diff --git a/fastlane/metadata/android/sr/full_description.txt b/fastlane/metadata/android/sr/full_description.txt
index dd7b821a3ef..df0c3c3f95c 100644
--- a/fastlane/metadata/android/sr/full_description.txt
+++ b/fastlane/metadata/android/sr/full_description.txt
@@ -1 +1 @@
-ЊуПајп не користи ниједну библиотеку Гугл оквира нити Јутјуб АПИ. Само скида са Јутјуба оно што му је потребно. Стога се ова апликација може користити на уређајима без инсталираних Гугл услуга. Такође, није вам потребан Јутјуб налог да бисте користили ЊуПајп, и још је ФЛОСС.
+ЊуПајп не користи ниједну библиотеку Гугл оквира нити Јутјуб АПИ. Само скида са Јутјуба оно што му је потребно. Стога се ова апликација може користити на уређајима без инсталираних Гугл услуга. Такође, није вам потребан Јутјуб налог да бисте користили ЊуПајп, и још је слободан, бесплатан и отвореног кода.
diff --git a/fastlane/metadata/android/uk/short_description.txt b/fastlane/metadata/android/uk/short_description.txt
index f35a32646e6..f73a0228837 100644
--- a/fastlane/metadata/android/uk/short_description.txt
+++ b/fastlane/metadata/android/uk/short_description.txt
@@ -1 +1 @@
-Безкоштовний та легкий YouTube інткрфейс для Android
+Безкоштовний та легкий YouTube-інтерфейс для Android.
diff --git a/fastlane/metadata/android/zh-Hans/changelogs/66.txt b/fastlane/metadata/android/zh-Hans/changelogs/66.txt
index 86adf2d1193..d555a70cab8 100644
--- a/fastlane/metadata/android/zh-Hans/changelogs/66.txt
+++ b/fastlane/metadata/android/zh-Hans/changelogs/66.txt
@@ -1,2 +1,22 @@
v0.13.7
修复排序异常
+v0.13.6
+##改进
+禁用汉堡菜单图标动画
+刚删除的下载文件可恢复
+添加分享菜单中的下载选项
+添加分享选项至长按菜单
+退出时最小化主播放器
+库版本更新和数据库备份修复
+更新 ExoPlayer 至 2.8.2
+重新设计播放速度控制对话框,可支持不同的步长加快速度
+播放速度控制中增加沉默期间快进开关,给某些有声读物和音乐类型带来真正的无缝体验
+重构媒体源解析,允许在播放器内部将元数据与媒体一起传递,而非手动进行,因此成为单一元数据来源,在开始播放时直接可用
+修正在打开播放列表碎片且新的元数据可用时远程播放列表元数据未更新
+各种UI修复:后台播放器通知控件现在为白色,更容易通过滑动关闭悬浮窗播放器
+使用新的提取器,重构了多服务架构
+##修复
+修复异常视频信息页布局
+修复历史记录视图异常(更新元数据(缩略图,标题和数量)时恰访问播放列表;当从详情页从外部播放器上播放视频时,在数据库中注册一个视图)
+修复悬浮窗下视频超时
+修复主视频播放器(修正当播放器活动处于后台时收到新意图后重复模式导致NPE;修正当未获得悬浮窗权限,切换悬浮窗时播放器异常)
diff --git a/fastlane/metadata/android/zh-Hans/changelogs/970.txt b/fastlane/metadata/android/zh-Hans/changelogs/970.txt
new file mode 100644
index 00000000000..a29e44b31ee
--- /dev/null
+++ b/fastlane/metadata/android/zh-Hans/changelogs/970.txt
@@ -0,0 +1,10 @@
+新增
+• 在音视频简介下方显示元数据 (标签、类别、发行许可证...)
+• 在远程(非本地)播放列表中添加"显示频道详情"选项
+• 在长按菜单中添加"在浏览器中打开"选项
+修复
+• 修复 视频详情页面旋转屏幕时崩溃
+• 修复 在播放器内"使用Kodi播放"按钮总是提示安装Kore
+• 修复和改进了设置导出导入路径
+• [YouTube]修复 评论点赞计数
+• 以及修复其他细小问题