diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java index 64fec0b00..e2507ff88 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java @@ -476,6 +476,7 @@ public class PeertubeStreamExtractor extends StreamExtractor { .setMediaFormat(fmt) .setLanguageCode(languageCode) .setAutoGenerated(false) + .setAutoTranslated(false) .build()); } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java index 0cf2e138e..923b30157 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java @@ -665,7 +665,8 @@ public class YoutubeStreamExtractor extends StreamExtractor { @Override @Nonnull - public List getSubtitles(final MediaFormat format) throws ParsingException { + public List getSubtitles(@Nonnull final MediaFormat format) + throws ParsingException { assertPageFetched(); // We cannot store the subtitles list because the media format may change @@ -673,13 +674,12 @@ public class YoutubeStreamExtractor extends StreamExtractor { final JsonObject renderer = playerResponse.getObject("captions") .getObject("playerCaptionsTracklistRenderer"); final JsonArray captionsArray = renderer.getArray("captionTracks"); - // TODO: use this to apply auto translation to different language from a source language - // final JsonArray autoCaptionsArray = renderer.getArray("translationLanguages"); for (int i = 0; i < captionsArray.size(); i++) { - final String languageCode = captionsArray.getObject(i).getString("languageCode"); - final String baseUrl = captionsArray.getObject(i).getString("baseUrl"); - final String vssId = captionsArray.getObject(i).getString("vssId"); + final JsonObject caption = captionsArray.getObject(i); + final String languageCode = caption.getString("languageCode"); + final String baseUrl = caption.getString("baseUrl"); + final String vssId = caption.getString("vssId"); if (languageCode != null && baseUrl != null && vssId != null) { final boolean isAutoGenerated = vssId.startsWith("a."); @@ -694,7 +694,24 @@ public class YoutubeStreamExtractor extends StreamExtractor { .setMediaFormat(format) .setLanguageCode(languageCode) .setAutoGenerated(isAutoGenerated) + .setAutoTranslated(false) .build()); + if (i == 0 && caption.getBoolean("isTranslatable") + && renderer.has("translationLanguages")) { + final JsonArray languages = renderer.getArray("translationLanguages"); + for (int j = 0; j < languages.size(); j++) { + final JsonObject lang = languages.getObject(j); + final String tLanguageCode = lang.getString("languageCode"); + subtitlesToReturn.add(new SubtitlesStream.Builder() + .setContent(cleanUrl + "&fmt=" + format.getSuffix() + + "&tlang=" + tLanguageCode, true) + .setMediaFormat(format) + .setLanguageCode(tLanguageCode) + .setAutoGenerated(isAutoGenerated) + .setAutoTranslated(true) + .build()); + } + } } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/SubtitlesStream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/SubtitlesStream.java index 08886dcac..22a4379be 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/SubtitlesStream.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/SubtitlesStream.java @@ -14,6 +14,7 @@ public final class SubtitlesStream extends Stream { private final MediaFormat format; private final Locale locale; private final boolean autoGenerated; + private final boolean autoTranslated; private final String code; /** @@ -32,6 +33,7 @@ public final class SubtitlesStream extends Stream { private String languageCode; // Use of the Boolean class instead of the primitive type needed for setter call check private Boolean autoGenerated; + private Boolean autoTranslated; /** * Create a new {@link Builder} instance with default values. @@ -152,6 +154,18 @@ public final class SubtitlesStream extends Stream { return this; } + /** + * Set whether the subtitles have been automatically translated + * (i.e. by a machine like Google Translator) by the streaming service. + * @param autoTranslated whether the subtitles have been automatically translated by the + * streaming service + * @return this {@link Builder} instance + */ + public Builder setAutoTranslated(final boolean autoTranslated) { + this.autoTranslated = autoTranslated; + return this; + } + /** * Build a {@link SubtitlesStream} using the builder's current values. * @@ -196,13 +210,19 @@ public final class SubtitlesStream extends Stream { + "with setIsAutoGenerated."); } + if (autoTranslated == null) { + throw new IllegalStateException("The subtitles stream has been not set as an " + + "automatically translated subtitles stream or not. " + + "Please specify this information with setIsAutoTranslated."); + } + if (id == null) { id = languageCode + (mediaFormat != null ? "." + mediaFormat.suffix : ""); } return new SubtitlesStream(id, content, isUrl, mediaFormat, deliveryMethod, - languageCode, autoGenerated, manifestUrl); + languageCode, autoGenerated, autoTranslated, manifestUrl); } } @@ -219,6 +239,7 @@ public final class SubtitlesStream extends Stream { * @param deliveryMethod the {@link DeliveryMethod} of the stream * @param languageCode the language code of the stream * @param autoGenerated whether the subtitles are auto-generated by the streaming service + * @param autoTranslated whether the subtitles are auto-translated by the streaming service * @param manifestUrl the URL of the manifest this stream comes from (if applicable, * otherwise null) */ @@ -230,6 +251,7 @@ public final class SubtitlesStream extends Stream { @Nonnull final DeliveryMethod deliveryMethod, @Nonnull final String languageCode, final boolean autoGenerated, + final boolean autoTranslated, @Nullable final String manifestUrl) throws ParsingException { super(id, content, isUrl, mediaFormat, deliveryMethod, manifestUrl); this.locale = LocaleCompat.forLanguageTag(languageCode).orElseThrow( @@ -238,6 +260,7 @@ public final class SubtitlesStream extends Stream { this.code = languageCode; this.format = mediaFormat; this.autoGenerated = autoGenerated; + this.autoTranslated = autoTranslated; } /** @@ -250,7 +273,7 @@ public final class SubtitlesStream extends Stream { } /** - * Return whether if the subtitles are auto-generated. + * Return whether the subtitles are auto-generated. *

* Some streaming services can generate subtitles for their contents, like YouTube. *

@@ -261,6 +284,21 @@ public final class SubtitlesStream extends Stream { return autoGenerated; } + /** + * Whether the subtitles are translated automatically by a machine. + * + *

+ * Some streaming services provide automatically translated subtitles. + * YouTube, for example, uses Google translator to generate translated subtitles. + * Automatically translated subtitles might not coincide completely with the original text. + *

+ * + * @return {code true} if the subtitles are auto-translated, {@link false} otherwise + */ + public boolean isAutoTranslated() { + return autoTranslated; + } + /** * {@inheritDoc} */