make Subtitle object Stream compliant

* merge SubtitlesFormat into MediaFormat
* implement Stream interface on Subtitle class
* misc fixes: dont show a snackbar error on age-restricted videos, lint fix.
This commit is contained in:
kapodamy 2018-09-24 16:04:22 -03:00
parent 7129d6db55
commit e4afb21862
6 changed files with 140 additions and 20 deletions

View File

@ -36,7 +36,14 @@ public enum MediaFormat {
M4A (0x3, "m4a", "m4a", "audio/mp4"), M4A (0x3, "m4a", "m4a", "audio/mp4"),
WEBMA (0x4, "WebM", "webm", "audio/webm"), WEBMA (0x4, "WebM", "webm", "audio/webm"),
MP3 (0x5, "MP3", "mp3", "audio/mpeg"), MP3 (0x5, "MP3", "mp3", "audio/mpeg"),
OPUS (0x6, "opus", "opus", "audio/opus"); OPUS (0x6, "opus", "opus", "audio/opus"),
// subtitles formats
VTT (0x7, "WebVTT", "vtt", "text/vtt"),
TTML (0x8, "Timed Text Markup Language", "ttml", "application/ttml+xml"),
TRANSCRIPT1 (0x9, "TranScript v1", "srv1", "text/xml"),
TRANSCRIPT2 (0xA, "TranScript v2", "srv2", "text/xml"),
TRANSCRIPT3 (0xB, "TranScript v3", "srv3", "text/xml"),
SRT (0xC, "SubRip file format", "srt", "text/srt");
public final int id; public final int id;
public final String name; public final String name;

View File

@ -172,13 +172,13 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
@Override @Override
@Nonnull @Nonnull
public List<Subtitles> getSubtitlesDefault() throws IOException, ExtractionException { public List<SubtitlesStream> getSubtitlesDefault() throws IOException, ExtractionException {
return Collections.emptyList(); return Collections.emptyList();
} }
@Override @Override
@Nonnull @Nonnull
public List<Subtitles> getSubtitles(SubtitlesFormat format) throws IOException, ExtractionException { public List<SubtitlesStream> getSubtitles(MediaFormat format) throws IOException, ExtractionException {
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@ -7,6 +7,7 @@ import com.grack.nanojson.JsonParserException;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function; import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.ScriptableObject;
@ -460,15 +461,15 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Override @Override
@Nonnull @Nonnull
public List<Subtitles> getSubtitlesDefault() throws IOException, ExtractionException { public List<SubtitlesStream> getSubtitlesDefault() throws IOException, ExtractionException {
return getSubtitles(SubtitlesFormat.TTML); return getSubtitles(MediaFormat.TTML);
} }
@Override @Override
@Nonnull @Nonnull
public List<Subtitles> getSubtitles(final SubtitlesFormat format) throws IOException, ExtractionException { public List<SubtitlesStream> getSubtitles(final MediaFormat format) throws IOException, ExtractionException {
assertPageFetched(); assertPageFetched();
List<Subtitles> subtitles = new ArrayList<>(); List<SubtitlesStream> subtitles = new ArrayList<>();
for (final SubtitlesInfo subtitlesInfo : subtitlesInfos) { for (final SubtitlesInfo subtitlesInfo : subtitlesInfos) {
subtitles.add(subtitlesInfo.getSubtitle(format)); subtitles.add(subtitlesInfo.getSubtitle(format));
} }
@ -494,9 +495,13 @@ public class YoutubeStreamExtractor extends StreamExtractor {
assertPageFetched(); assertPageFetched();
try { try {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
collector.commit(extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]")
.first().select("li").first()));
Elements watch = doc.select("div[class=\"watch-sidebar-section\"]");
if (watch.size() < 1) {
return null;// prevent the snackbar notification "report error" on age-restricted videos
}
collector.commit(extractVideoPreviewInfo(watch.first().select("li").first()));
return collector.getItems().get(0); return collector.getItems().get(0);
} catch (Exception e) { } catch (Exception e) {
throw new ParsingException("Could not get next video", e); throw new ParsingException("Could not get next video", e);
@ -815,21 +820,16 @@ public class YoutubeStreamExtractor extends StreamExtractor {
final String languageCode; final String languageCode;
final boolean isGenerated; final boolean isGenerated;
final Locale locale;
public SubtitlesInfo(final String baseUrl, final String languageCode, final boolean isGenerated) { public SubtitlesInfo(final String baseUrl, final String languageCode, final boolean isGenerated) {
this.cleanUrl = baseUrl this.cleanUrl = baseUrl
.replaceAll("&fmt=[^&]*", "") // Remove preexisting format if exists .replaceAll("&fmt=[^&]*", "") // Remove preexisting format if exists
.replaceAll("&tlang=[^&]*", ""); // Remove translation language .replaceAll("&tlang=[^&]*", ""); // Remove translation language
this.languageCode = languageCode; this.languageCode = languageCode;
this.isGenerated = isGenerated; this.isGenerated = isGenerated;
final String[] splits = languageCode.split("-");
this.locale = splits.length == 2 ? new Locale(splits[0], splits[1]) : new Locale(languageCode);
} }
public Subtitles getSubtitle(final SubtitlesFormat format) { public SubtitlesStream getSubtitle(final MediaFormat format) {
return new Subtitles(format, locale, cleanUrl + "&fmt=" + format.getExtension(), isGenerated); return new SubtitlesStream(format, languageCode, cleanUrl + "&fmt=" + format.getSuffix(), isGenerated);
} }
} }

View File

@ -21,6 +21,7 @@ package org.schabi.newpipe.extractor.stream;
*/ */
import org.schabi.newpipe.extractor.Extractor; import org.schabi.newpipe.extractor.Extractor;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
@ -314,5 +315,44 @@ public abstract class StreamExtractor extends Extractor {
} }
} else { } else {
return 0; return 0;
}}; }
}
public abstract long getViewCount() throws ParsingException;
public abstract long getLikeCount() throws ParsingException;
public abstract long getDislikeCount() throws ParsingException;
@Nonnull
public abstract String getUploaderUrl() throws ParsingException;
@Nonnull
public abstract String getUploaderName() throws ParsingException;
@Nonnull
public abstract String getUploaderAvatarUrl() throws ParsingException;
/**
* Get the dash mpd url
* @return the url as a string or an empty string
* @throws ParsingException if an error occurs while reading
*/
@Nonnull public abstract String getDashMpdUrl() throws ParsingException;
@Nonnull public abstract String getHlsUrl() throws ParsingException;
public abstract List<AudioStream> getAudioStreams() throws IOException, ExtractionException;
public abstract List<VideoStream> getVideoStreams() throws IOException, ExtractionException;
public abstract List<VideoStream> getVideoOnlyStreams() throws IOException, ExtractionException;
@Nonnull
public abstract List<SubtitlesStream> getSubtitlesDefault() throws IOException, ExtractionException;
@Nonnull
public abstract List<SubtitlesStream> getSubtitles(MediaFormat format) throws IOException, ExtractionException;
public abstract StreamType getStreamType() throws ParsingException;
public abstract StreamInfoItem getNextVideo() throws IOException, ExtractionException;
public abstract StreamInfoItemsCollector getRelatedVideos() throws IOException, ExtractionException;
/**
* Analyses the webpage's document and extracts any error message there might be.
*
* @return Error message; null if there is no error message.
*/
public abstract String getErrorMessage();
} }

View File

@ -283,7 +283,7 @@ public class StreamInfo extends Info {
private List<InfoItem> relatedStreams; private List<InfoItem> relatedStreams;
private long startPosition = 0; private long startPosition = 0;
private List<Subtitles> subtitles; private List<SubtitlesStream> subtitles;
/** /**
* Get the stream type * Get the stream type
@ -494,11 +494,11 @@ public class StreamInfo extends Info {
this.startPosition = startPosition; this.startPosition = startPosition;
} }
public List<Subtitles> getSubtitles() { public List<SubtitlesStream> getSubtitles() {
return subtitles; return subtitles;
} }
public void setSubtitles(List<Subtitles> subtitles) { public void setSubtitles(List<SubtitlesStream> subtitles) {
this.subtitles = subtitles; this.subtitles = subtitles;
} }

View File

@ -0,0 +1,73 @@
package org.schabi.newpipe.extractor.stream;
import org.schabi.newpipe.extractor.MediaFormat;
import java.io.Serializable;
import java.util.Locale;
public class SubtitlesStream extends Stream implements Serializable {
private final MediaFormat format;
private final Locale locale;
private final String url;
private final boolean autoGenerated;
private final String code;
public SubtitlesStream(MediaFormat format, String languageCode, String url, boolean autoGenerated) {
super(url, format);
/*
* Locale.forLanguageTag only for API >= 21
* Locale.Builder only for API >= 21
* Country codes doesn't work well without
*/
final String[] splits = languageCode.split("-");
switch (splits.length) {
default:
this.locale = new Locale(splits[0]);
break;
case 3:
this.locale = new Locale(splits[0], splits[1], splits[2]);// complex variants doesn't work!
break;
case 2:
this.locale = new Locale(splits[0], splits[1]);
break;
}
this.code = languageCode;
this.format = format;
this.url = url;
this.autoGenerated = autoGenerated;
}
public String getExtension() {
return format.suffix;
}
public String getURL() {
return url;
}
public boolean isAutoGenerated() {
return autoGenerated;
}
@Override
public boolean equalStats(Stream cmp) {
return super.equalStats(cmp)&&
cmp instanceof SubtitlesStream &&
code.equals(((SubtitlesStream) cmp).code) &&
autoGenerated == ((SubtitlesStream) cmp).autoGenerated;
}
public String getDisplayLanguageName() {
return locale.getDisplayName(locale);
}
public String getLanguageTag() {
return code;
}
public Locale getLocale() {
return locale;
}
}