Remove fetching of the DASH manifest extracted when getting information of a content with StreamInfo

DashMpdParser is only working with YouTube streams, as it uses the ItagItem class.

Also improve code and comments of StreamInfo (especially final use where possible).
This commit is contained in:
TiA4f8R 2022-03-03 09:56:03 +01:00
parent 2f061b8dbd
commit ad993b920f
No known key found for this signature in database
GPG Key ID: E6D3E7F5949450DD
1 changed files with 43 additions and 96 deletions

View File

@ -9,7 +9,6 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.utils.DashMpdParser;
import org.schabi.newpipe.extractor.utils.ExtractorHelper; import org.schabi.newpipe.extractor.utils.ExtractorHelper;
import java.io.IOException; import java.io.IOException;
@ -26,24 +25,24 @@ import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
* Created by Christian Schabesberger on 26.08.15. * Created by Christian Schabesberger on 26.08.15.
* *
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* StreamInfo.java is part of NewPipe. * StreamInfo.java is part of NewPipe Extractor.
* *
* NewPipe is free software: you can redistribute it and/or modify * NewPipe Extractor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* NewPipe is distributed in the hope that it will be useful, * NewPipe Extractor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe Extractor. If not, see <https://www.gnu.org/licenses/>.
*/ */
/** /**
* Info object for opened videos, ie the video ready to play. * Info object for opened contents, i.e. the content ready to play.
*/ */
public class StreamInfo extends Info { public class StreamInfo extends Info {
@ -69,27 +68,26 @@ public class StreamInfo extends Info {
return getInfo(NewPipe.getServiceByUrl(url), url); return getInfo(NewPipe.getServiceByUrl(url), url);
} }
public static StreamInfo getInfo(final StreamingService service, public static StreamInfo getInfo(@Nonnull final StreamingService service,
final String url) throws IOException, ExtractionException { final String url) throws IOException, ExtractionException {
return getInfo(service.getStreamExtractor(url)); return getInfo(service.getStreamExtractor(url));
} }
public static StreamInfo getInfo(final StreamExtractor extractor) public static StreamInfo getInfo(@Nonnull final StreamExtractor extractor)
throws ExtractionException, IOException { throws ExtractionException, IOException {
extractor.fetchPage(); extractor.fetchPage();
final StreamInfo streamInfo;
try { try {
final StreamInfo streamInfo = extractImportantData(extractor); streamInfo = extractImportantData(extractor);
extractStreams(streamInfo, extractor); extractStreams(streamInfo, extractor);
extractOptionalData(streamInfo, extractor); extractOptionalData(streamInfo, extractor);
return streamInfo; return streamInfo;
} catch (final ExtractionException e) { } catch (final ExtractionException e) {
// Currently YouTube does not distinguish between age restricted videos and // Currently, YouTube does not distinguish between age restricted videos and videos
// videos blocked // blocked by country. This means that during the initialisation of the extractor, the
// by country. This means that during the initialisation of the extractor, the // extractor will assume that a video is age restricted while in reality it is blocked
// extractor // by country.
// will assume that a video is age restricted while in reality it it blocked by
// country.
// //
// We will now detect whether the video is blocked by country or not. // We will now detect whether the video is blocked by country or not.
@ -102,22 +100,27 @@ public class StreamInfo extends Info {
} }
} }
private static StreamInfo extractImportantData(final StreamExtractor extractor) @Nonnull
private static StreamInfo extractImportantData(@Nonnull final StreamExtractor extractor)
throws ExtractionException { throws ExtractionException {
/* ---- important data, without the video can't be displayed goes here: ---- */ // Important data, without it the content can't be displayed.
// if one of these is not available an exception is meant to be thrown directly // If one of these is not available, the frontend will receive an exception directly.
// into the frontend.
final int serviceId = extractor.getServiceId();
final String url = extractor.getUrl(); final String url = extractor.getUrl();
final String originalUrl = extractor.getOriginalUrl();
final StreamType streamType = extractor.getStreamType(); final StreamType streamType = extractor.getStreamType();
final String id = extractor.getId(); final String id = extractor.getId();
final String name = extractor.getName(); final String name = extractor.getName();
final int ageLimit = extractor.getAgeLimit(); final int ageLimit = extractor.getAgeLimit();
// suppress always-non-null warning as here we double-check it really is not null // Suppress always-non-null warning as here we double-check it is really not null
//noinspection ConstantConditions //noinspection ConstantConditions
if (streamType == StreamType.NONE || isNullOrEmpty(url) || isNullOrEmpty(id) if (streamType == StreamType.NONE
|| name == null /* but it can be empty of course */ || ageLimit == -1) { || isNullOrEmpty(url)
|| isNullOrEmpty(id)
|| name == null /* but it can be empty of course */
|| ageLimit == -1) {
throw new ExtractionException("Some important stream information was not given."); throw new ExtractionException("Some important stream information was not given.");
} }
@ -125,16 +128,18 @@ public class StreamInfo extends Info {
streamType, id, name, ageLimit); streamType, id, name, ageLimit);
} }
private static void extractStreams(final StreamInfo streamInfo, final StreamExtractor extractor)
private static void extractStreams(final StreamInfo streamInfo,
final StreamExtractor extractor)
throws ExtractionException { throws ExtractionException {
/* ---- stream extraction goes here ---- */ /* ---- Stream extraction goes here ---- */
// At least one type of stream has to be available, // At least one type of stream has to be available, otherwise an exception will be thrown
// otherwise an exception will be thrown directly into the frontend. // directly into the frontend.
try { try {
streamInfo.setDashMpdUrl(extractor.getDashMpdUrl()); streamInfo.setDashMpdUrl(extractor.getDashMpdUrl());
} catch (final Exception e) { } catch (final Exception e) {
streamInfo.addError(new ExtractionException("Couldn't get Dash manifest", e)); streamInfo.addError(new ExtractionException("Couldn't get DASH manifest", e));
} }
try { try {
@ -151,12 +156,14 @@ public class StreamInfo extends Info {
} catch (final Exception e) { } catch (final Exception e) {
streamInfo.addError(new ExtractionException("Couldn't get audio streams", e)); streamInfo.addError(new ExtractionException("Couldn't get audio streams", e));
} }
/* Extract video stream url */ /* Extract video stream url */
try { try {
streamInfo.setVideoStreams(extractor.getVideoStreams()); streamInfo.setVideoStreams(extractor.getVideoStreams());
} catch (final Exception e) { } catch (final Exception e) {
streamInfo.addError(new ExtractionException("Couldn't get video streams", e)); streamInfo.addError(new ExtractionException("Couldn't get video streams", e));
} }
/* Extract video only stream url */ /* Extract video only stream url */
try { try {
streamInfo.setVideoOnlyStreams(extractor.getVideoOnlyStreams()); streamInfo.setVideoOnlyStreams(extractor.getVideoOnlyStreams());
@ -164,7 +171,7 @@ public class StreamInfo extends Info {
streamInfo.addError(new ExtractionException("Couldn't get video only streams", e)); streamInfo.addError(new ExtractionException("Couldn't get video only streams", e));
} }
// Lists can be null if a exception was thrown during extraction // Lists can be null if an exception was thrown during extraction
if (streamInfo.getVideoStreams() == null) { if (streamInfo.getVideoStreams() == null) {
streamInfo.setVideoStreams(Collections.emptyList()); streamInfo.setVideoStreams(Collections.emptyList());
} }
@ -175,37 +182,9 @@ public class StreamInfo extends Info {
streamInfo.setAudioStreams(Collections.emptyList()); streamInfo.setAudioStreams(Collections.emptyList());
} }
Exception dashMpdError = null; // Either audio or video has to be available, otherwise we didn't get a stream (since
if (!isNullOrEmpty(streamInfo.getDashMpdUrl())) { // videoOnly are optional, they don't count).
try {
final DashMpdParser.ParserResult result = DashMpdParser.getStreams(streamInfo);
streamInfo.getVideoOnlyStreams().addAll(result.getVideoOnlyStreams());
streamInfo.getAudioStreams().addAll(result.getAudioStreams());
streamInfo.getVideoStreams().addAll(result.getVideoStreams());
streamInfo.segmentedVideoOnlyStreams = result.getSegmentedVideoOnlyStreams();
streamInfo.segmentedAudioStreams = result.getSegmentedAudioStreams();
streamInfo.segmentedVideoStreams = result.getSegmentedVideoStreams();
} catch (final Exception e) {
// Sometimes we receive 403 (forbidden) error when trying to download the
// manifest (similar to what happens with youtube-dl),
// just skip the exception (but store it somewhere), as we later check if we
// have streams anyway.
dashMpdError = e;
}
}
// Either audio or video has to be available, otherwise we didn't get a stream
// (since videoOnly are optional, they don't count).
if ((streamInfo.videoStreams.isEmpty()) && (streamInfo.audioStreams.isEmpty())) { if ((streamInfo.videoStreams.isEmpty()) && (streamInfo.audioStreams.isEmpty())) {
if (dashMpdError != null) {
// If we don't have any video or audio and the dashMpd 'errored', add it to the
// error list
// (it's optional and it don't get added automatically, but it's good to have
// some additional error context)
streamInfo.addError(dashMpdError);
}
throw new StreamExtractException( throw new StreamExtractException(
"Could not get any stream. See error variable to get further details."); "Could not get any stream. See error variable to get further details.");
} }
@ -214,11 +193,9 @@ public class StreamInfo extends Info {
@SuppressWarnings("MethodLength") @SuppressWarnings("MethodLength")
private static void extractOptionalData(final StreamInfo streamInfo, private static void extractOptionalData(final StreamInfo streamInfo,
final StreamExtractor extractor) { final StreamExtractor extractor) {
/* ---- optional data goes here: ---- */ /* ---- Optional data goes here: ---- */
// If one of these fails, the frontend needs to handle that they are not // If one of these fails, the frontend needs to handle that they are not available.
// available. // Exceptions are therefore not thrown into the frontend, but stored into the error list,
// Exceptions are therefore not thrown into the frontend, but stored into the
// error List,
// so the frontend can afterwards check where errors happened. // so the frontend can afterwards check where errors happened.
try { try {
@ -314,7 +291,7 @@ public class StreamInfo extends Info {
streamInfo.addError(e); streamInfo.addError(e);
} }
//additional info // Additional info
try { try {
streamInfo.setHost(extractor.getHost()); streamInfo.setHost(extractor.getHost());
} catch (final Exception e) { } catch (final Exception e) {
@ -360,15 +337,14 @@ public class StreamInfo extends Info {
} catch (final Exception e) { } catch (final Exception e) {
streamInfo.addError(e); streamInfo.addError(e);
} }
try { try {
streamInfo.setPreviewFrames(extractor.getFrames()); streamInfo.setPreviewFrames(extractor.getFrames());
} catch (final Exception e) { } catch (final Exception e) {
streamInfo.addError(e); streamInfo.addError(e);
} }
streamInfo streamInfo.setRelatedItems(ExtractorHelper.getRelatedItemsOrLogError(streamInfo,
.setRelatedItems(ExtractorHelper.getRelatedItemsOrLogError(streamInfo, extractor)); extractor));
} }
private StreamType streamType; private StreamType streamType;
@ -398,11 +374,6 @@ public class StreamInfo extends Info {
private List<VideoStream> videoOnlyStreams = new ArrayList<>(); private List<VideoStream> videoOnlyStreams = new ArrayList<>();
private String dashMpdUrl = ""; private String dashMpdUrl = "";
private List<VideoStream> segmentedVideoStreams = new ArrayList<>();
private List<AudioStream> segmentedAudioStreams = new ArrayList<>();
private List<VideoStream> segmentedVideoOnlyStreams = new ArrayList<>();
private String hlsUrl = ""; private String hlsUrl = "";
private List<InfoItem> relatedItems = new ArrayList<>(); private List<InfoItem> relatedItems = new ArrayList<>();
@ -625,30 +596,6 @@ public class StreamInfo extends Info {
this.dashMpdUrl = dashMpdUrl; this.dashMpdUrl = dashMpdUrl;
} }
public List<VideoStream> getSegmentedVideoStreams() {
return segmentedVideoStreams;
}
public void setSegmentedVideoStreams(final List<VideoStream> segmentedVideoStreams) {
this.segmentedVideoStreams = segmentedVideoStreams;
}
public List<AudioStream> getSegmentedAudioStreams() {
return segmentedAudioStreams;
}
public void setSegmentedAudioStreams(final List<AudioStream> segmentedAudioStreams) {
this.segmentedAudioStreams = segmentedAudioStreams;
}
public List<VideoStream> getSegmentedVideoOnlyStreams() {
return segmentedVideoOnlyStreams;
}
public void setSegmentedVideoOnlyStreams(final List<VideoStream> segmentedVideoOnlyStreams) {
this.segmentedVideoOnlyStreams = segmentedVideoOnlyStreams;
}
public String getHlsUrl() { public String getHlsUrl() {
return hlsUrl; return hlsUrl;
} }