more fixes
* use bold style in status (mission_item_linear.xml) * fix download attemps not begin updated * dont stop the queue if a download fails * implement partial wake-lock & wifi-lock * show notifications for failed downloads * ¿proper bitmap dispose? (DownloadManagerService.java) * improve buffer filling (CircularFile.java) * [Mp4Dash] increment reserved space from 2MiB to 15MiB. This is expensive but useful for devices with low ram * [WebM] use 2MiB of reserved space * fix debug warning if one thread is used * fix wrong download speed when the activity is suspended * Fix "Queue" menu item that appears in post-processing errors * fix mission length dont being updated (missing commit)
This commit is contained in:
parent
fef9d541ed
commit
d647555e3a
19 changed files with 400 additions and 150 deletions
|
|
@ -11,6 +11,7 @@ import android.support.v4.app.DialogFragment;
|
|||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -37,6 +38,7 @@ import org.schabi.newpipe.settings.NewPipeSettings;
|
|||
import org.schabi.newpipe.util.FilenameUtils;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
import org.schabi.newpipe.util.SecondaryStreamHelper;
|
||||
import org.schabi.newpipe.util.StreamItemAdapter;
|
||||
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
|
@ -55,17 +57,24 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
private static final String TAG = "DialogFragment";
|
||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
|
||||
@State protected StreamInfo currentInfo;
|
||||
@State protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty();
|
||||
@State protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty();
|
||||
@State protected StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty();
|
||||
@State protected int selectedVideoIndex = 0;
|
||||
@State protected int selectedAudioIndex = 0;
|
||||
@State protected int selectedSubtitleIndex = 0;
|
||||
@State
|
||||
protected StreamInfo currentInfo;
|
||||
@State
|
||||
protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty();
|
||||
@State
|
||||
protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty();
|
||||
@State
|
||||
protected StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty();
|
||||
@State
|
||||
protected int selectedVideoIndex = 0;
|
||||
@State
|
||||
protected int selectedAudioIndex = 0;
|
||||
@State
|
||||
protected int selectedSubtitleIndex = 0;
|
||||
|
||||
private StreamItemAdapter<AudioStream> audioStreamsAdapter;
|
||||
private StreamItemAdapter<VideoStream> videoStreamsAdapter;
|
||||
private StreamItemAdapter<SubtitlesStream> subtitleStreamsAdapter;
|
||||
private StreamItemAdapter<AudioStream, Stream> audioStreamsAdapter;
|
||||
private StreamItemAdapter<VideoStream, AudioStream> videoStreamsAdapter;
|
||||
private StreamItemAdapter<SubtitlesStream, Stream> subtitleStreamsAdapter;
|
||||
|
||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
|
|
@ -144,7 +153,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
|
||||
getDialog().dismiss();
|
||||
return;
|
||||
|
|
@ -153,14 +163,29 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(getContext()));
|
||||
Icepick.restoreInstanceState(this, savedInstanceState);
|
||||
|
||||
this.videoStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedVideoStreams, true);
|
||||
SparseArray<SecondaryStreamHelper<AudioStream>> secondaryStreams = new SparseArray<>(4);
|
||||
List<VideoStream> videoStreams = wrappedVideoStreams.getStreamsList();
|
||||
|
||||
for (int i = 0; i < videoStreams.size(); i++) {
|
||||
if (!videoStreams.get(i).isVideoOnly()) continue;
|
||||
AudioStream audioStream = SecondaryStreamHelper.getAudioStreamFor(wrappedAudioStreams.getStreamsList(), videoStreams.get(i));
|
||||
|
||||
if (audioStream != null) {
|
||||
secondaryStreams.append(i, new SecondaryStreamHelper<>(wrappedAudioStreams, audioStream));
|
||||
} else if (DEBUG) {
|
||||
Log.w(TAG, "No audio stream candidates for video format " + videoStreams.get(i).getFormat().name());
|
||||
}
|
||||
}
|
||||
|
||||
this.videoStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedVideoStreams, secondaryStreams);
|
||||
this.audioStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedAudioStreams);
|
||||
this.subtitleStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedSubtitleStreams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||
return inflater.inflate(R.layout.download_dialog, container);
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +318,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
|
||||
@Override
|
||||
public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
|
||||
if (DEBUG) Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]");
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]");
|
||||
boolean flag = true;
|
||||
|
||||
switch (checkedId) {
|
||||
|
|
@ -318,7 +344,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (DEBUG) Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
|
||||
switch (radioVideoAudioGroup.getCheckedRadioButtonId()) {
|
||||
case R.id.audio_button:
|
||||
selectedAudioIndex = position;
|
||||
|
|
@ -458,57 +485,41 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
String[] urls;
|
||||
String psName = null;
|
||||
String[] psArgs = null;
|
||||
String secondaryStream = null;
|
||||
String secondaryStreamUrl = null;
|
||||
long nearLength = 0;
|
||||
|
||||
if (selectedStream instanceof VideoStream) {
|
||||
VideoStream videoStream = (VideoStream) selectedStream;
|
||||
if (videoStream.isVideoOnly() && videoStream.getFormat() != MediaFormat.v3GPP) {
|
||||
boolean m4v = videoStream.getFormat() == MediaFormat.MPEG_4;
|
||||
SecondaryStreamHelper<AudioStream> secondaryStream = videoStreamsAdapter
|
||||
.getAllSecondary()
|
||||
.get(wrappedVideoStreams.getStreamsList().indexOf(selectedStream));
|
||||
|
||||
for (AudioStream audio : audioStreamsAdapter.getAll()) {
|
||||
if (audio.getFormat() == (m4v ? MediaFormat.M4A : MediaFormat.WEBMA)) {
|
||||
secondaryStream = audio.getUrl();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (secondaryStream != null) {
|
||||
secondaryStreamUrl = secondaryStream.getStream().getUrl();
|
||||
psName = selectedStream.getFormat() == MediaFormat.MPEG_4 ? Postprocessing.ALGORITHM_MP4_DASH_MUXER : Postprocessing.ALGORITHM_WEBM_MUXER;
|
||||
psArgs = null;
|
||||
long videoSize = wrappedVideoStreams.getSizeInBytes((VideoStream) selectedStream);
|
||||
|
||||
if (secondaryStream == null) {
|
||||
// retry, but this time in reverse order
|
||||
List<AudioStream> audioStreams = audioStreamsAdapter.getAll();
|
||||
for (int i = audioStreams.size() - 1; i >= 0; i--) {
|
||||
AudioStream audio = audioStreams.get(i);
|
||||
if (audio.getFormat() == (m4v ? MediaFormat.MP3 : MediaFormat.OPUS)) {
|
||||
secondaryStream = audio.getUrl();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (secondaryStream == null) {
|
||||
Log.w(TAG, "No audio stream candidates for video format " + videoStream.getFormat().name());
|
||||
psName = null;
|
||||
psArgs = null;
|
||||
} else {
|
||||
psName = m4v ? Postprocessing.ALGORITHM_MP4_DASH_MUXER : Postprocessing.ALGORITHM_WEBM_MUXER;
|
||||
psArgs = null;
|
||||
// set nearLength, only, if both sizes are fetched or known. this probably does not work on weak internet connections
|
||||
if (secondaryStream.getSizeInBytes() > 0 && videoSize > 0) {
|
||||
nearLength = secondaryStream.getSizeInBytes() + videoSize;
|
||||
}
|
||||
}
|
||||
} else if ((selectedStream instanceof SubtitlesStream) && selectedStream.getFormat() == MediaFormat.TTML) {
|
||||
psName = Postprocessing.ALGORITHM_TTML_CONVERTER;
|
||||
psArgs = new String[]{
|
||||
selectedStream.getFormat().getSuffix(),
|
||||
"false",//ignore empty frames
|
||||
"false",// detect youtube duplicateLines
|
||||
"false",// ignore empty frames
|
||||
"false",// detect youtube duplicate lines
|
||||
};
|
||||
}
|
||||
|
||||
if (secondaryStream == null) {
|
||||
if (secondaryStreamUrl == null) {
|
||||
urls = new String[]{selectedStream.getUrl()};
|
||||
} else {
|
||||
urls = new String[]{selectedStream.getUrl(), secondaryStream};
|
||||
urls = new String[]{selectedStream.getUrl(), secondaryStreamUrl};
|
||||
}
|
||||
|
||||
DownloadManagerService.startMission(context, urls, location, fileName, kind, threads, currentInfo.getUrl(), psName, psArgs);
|
||||
DownloadManagerService.startMission(context, urls, location, fileName, kind, threads, currentInfo.getUrl(), psName, psArgs, nearLength);
|
||||
|
||||
getDialog().dismiss();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -746,7 +746,7 @@ public class VideoDetailFragment
|
|||
sortedVideoStreams = ListHelper.getSortedStreamVideosList(activity, info.getVideoStreams(), info.getVideoOnlyStreams(), false);
|
||||
selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(activity, sortedVideoStreams);
|
||||
|
||||
final StreamItemAdapter<VideoStream> streamsAdapter = new StreamItemAdapter<>(activity, new StreamSizeWrapper<>(sortedVideoStreams, activity), isExternalPlayerEnabled);
|
||||
final StreamItemAdapter<VideoStream, Stream> streamsAdapter = new StreamItemAdapter<>(activity, new StreamSizeWrapper<>(sortedVideoStreams, activity), isExternalPlayerEnabled);
|
||||
spinnerToolbar.setAdapter(streamsAdapter);
|
||||
spinnerToolbar.setSelection(selectedVideoStreamIndex);
|
||||
spinnerToolbar.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
|
|
@ -1335,4 +1335,4 @@ public class VideoDetailFragment
|
|||
relatedStreamRootLayout.setVisibility(visibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ public class Mp4DashReader {
|
|||
|
||||
private String boxName(int type) {
|
||||
try {
|
||||
return new String(ByteBuffer.allocate(4).putInt(type).array(), "US-ASCII");
|
||||
return new String(ByteBuffer.allocate(4).putInt(type).array(), "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return "0x" + Integer.toHexString(type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.Stream;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SecondaryStreamHelper<T extends Stream> {
|
||||
private final int position;
|
||||
private final StreamSizeWrapper<T> streams;
|
||||
|
||||
public SecondaryStreamHelper(StreamSizeWrapper<T> streams, T selectedStream) {
|
||||
this.streams = streams;
|
||||
this.position = streams.getStreamsList().indexOf(selectedStream);
|
||||
if (this.position < 0) throw new RuntimeException("selected stream not found");
|
||||
}
|
||||
|
||||
public T getStream() {
|
||||
return streams.getStreamsList().get(position);
|
||||
}
|
||||
|
||||
public long getSizeInBytes() {
|
||||
return streams.getSizeInBytes(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* find the correct audio stream for the desired video stream
|
||||
*
|
||||
* @param audioStreams list of audio streams
|
||||
* @param videoStream desired video ONLY stream
|
||||
* @return selected audio stream or null if a candidate was not found
|
||||
*/
|
||||
public static AudioStream getAudioStreamFor(@NonNull List<AudioStream> audioStreams, @NonNull VideoStream videoStream) {
|
||||
// TODO: check if m4v and m4a selected streams are DASH compliant
|
||||
switch (videoStream.getFormat()) {
|
||||
case WEBM:
|
||||
case MPEG_4:
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean m4v = videoStream.getFormat() == MediaFormat.MPEG_4;
|
||||
|
||||
for (AudioStream audio : audioStreams) {
|
||||
if (audio.getFormat() == (m4v ? MediaFormat.M4A : MediaFormat.WEBMA)) {
|
||||
return audio;
|
||||
}
|
||||
}
|
||||
|
||||
// retry, but this time in reverse order
|
||||
for (int i = audioStreams.size() - 1; i >= 0; i--) {
|
||||
AudioStream audio = audioStreams.get(i);
|
||||
if (audio.getFormat() == (m4v ? MediaFormat.MP3 : MediaFormat.OPUS)) {
|
||||
return audio;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -29,26 +30,34 @@ import us.shandian.giga.util.Utility;
|
|||
/**
|
||||
* A list adapter for a list of {@link Stream streams}, currently supporting {@link VideoStream} and {@link AudioStream}.
|
||||
*/
|
||||
public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
|
||||
public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseAdapter {
|
||||
private final Context context;
|
||||
|
||||
private final StreamSizeWrapper<T> streamsWrapper;
|
||||
private final boolean showIconNoAudio;
|
||||
private final SparseArray<SecondaryStreamHelper<U>> secondaryStreams;
|
||||
|
||||
public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper, boolean showIconNoAudio) {
|
||||
public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper, SparseArray<SecondaryStreamHelper<U>> secondaryStreams) {
|
||||
this.context = context;
|
||||
this.streamsWrapper = streamsWrapper;
|
||||
this.showIconNoAudio = showIconNoAudio;
|
||||
this.secondaryStreams = secondaryStreams;
|
||||
}
|
||||
|
||||
public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper, boolean showIconNoAudio) {
|
||||
this(context, streamsWrapper, showIconNoAudio ? new SparseArray<>() : null);
|
||||
}
|
||||
|
||||
public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper) {
|
||||
this(context, streamsWrapper, false);
|
||||
this(context, streamsWrapper, null);
|
||||
}
|
||||
|
||||
public List<T> getAll() {
|
||||
return streamsWrapper.getStreamsList();
|
||||
}
|
||||
|
||||
public SparseArray<SecondaryStreamHelper<U>> getAllSecondary() {
|
||||
return secondaryStreams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return streamsWrapper.getStreamsList().size();
|
||||
|
|
@ -90,22 +99,15 @@ public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
|
|||
String qualityString;
|
||||
|
||||
if (stream instanceof VideoStream) {
|
||||
qualityString = ((VideoStream) stream).getResolution();
|
||||
VideoStream videoStream = ((VideoStream) stream);
|
||||
qualityString = videoStream.getResolution();
|
||||
|
||||
if (!showIconNoAudio) {
|
||||
woSoundIconVisibility = View.GONE;
|
||||
} else if (((VideoStream) stream).isVideoOnly()) {
|
||||
switch (stream.getFormat()) {
|
||||
case WEBM:// fully supported
|
||||
case MPEG_4:// ¿is DASH MPEG-4 format?
|
||||
woSoundIconVisibility = View.INVISIBLE;
|
||||
break;
|
||||
default:
|
||||
woSoundIconVisibility = View.VISIBLE;
|
||||
break;
|
||||
if (secondaryStreams != null) {
|
||||
if (videoStream.isVideoOnly()) {
|
||||
woSoundIconVisibility = secondaryStreams.get(position) == null ? View.VISIBLE : View.INVISIBLE;
|
||||
} else if (isDropdownItem) {
|
||||
woSoundIconVisibility = View.INVISIBLE;
|
||||
}
|
||||
} else if (isDropdownItem) {
|
||||
woSoundIconVisibility = View.INVISIBLE;
|
||||
}
|
||||
} else if (stream instanceof AudioStream) {
|
||||
qualityString = ((AudioStream) stream).getAverageBitrate() + "kbps";
|
||||
|
|
@ -119,7 +121,13 @@ public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
|
|||
}
|
||||
|
||||
if (streamsWrapper.getSizeInBytes(position) > 0) {
|
||||
sizeView.setText(streamsWrapper.getFormattedSize(position));
|
||||
SecondaryStreamHelper secondary = secondaryStreams == null ? null : secondaryStreams.get(position);
|
||||
if (secondary != null) {
|
||||
long size = secondary.getSizeInBytes() + streamsWrapper.getSizeInBytes(position);
|
||||
sizeView.setText(Utility.formatBytes(size));
|
||||
} else {
|
||||
sizeView.setText(streamsWrapper.getFormattedSize(position));
|
||||
}
|
||||
sizeView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
sizeView.setVisibility(View.GONE);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue