[YouTube] Add support for extracting auto-translated captions

Closes TeamNewPipe/NewPipeExtractor#977
Based on and adresses TeamNewPipe/NewPipe#8023
This commit is contained in:
TobiGr 2022-12-05 18:03:34 +01:00
parent 41c8dce452
commit efce384d9b
3 changed files with 63 additions and 8 deletions

View File

@ -416,6 +416,7 @@ public class PeertubeStreamExtractor extends StreamExtractor {
.setMediaFormat(fmt) .setMediaFormat(fmt)
.setLanguageCode(languageCode) .setLanguageCode(languageCode)
.setAutoGenerated(false) .setAutoGenerated(false)
.setAutoTranslated(false)
.build()); .build());
} }
} }

View File

@ -688,7 +688,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Override @Override
@Nonnull @Nonnull
public List<SubtitlesStream> getSubtitles(final MediaFormat format) throws ParsingException { public List<SubtitlesStream> getSubtitles(@Nonnull final MediaFormat format) {
assertPageFetched(); assertPageFetched();
// We cannot store the subtitles list because the media format may change // We cannot store the subtitles list because the media format may change
@ -696,13 +696,12 @@ public class YoutubeStreamExtractor extends StreamExtractor {
final JsonObject renderer = playerResponse.getObject("captions") final JsonObject renderer = playerResponse.getObject("captions")
.getObject("playerCaptionsTracklistRenderer"); .getObject("playerCaptionsTracklistRenderer");
final JsonArray captionsArray = renderer.getArray("captionTracks"); 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++) { for (int i = 0; i < captionsArray.size(); i++) {
final String languageCode = captionsArray.getObject(i).getString("languageCode"); final JsonObject caption = captionsArray.getObject(i);
final String baseUrl = captionsArray.getObject(i).getString("baseUrl"); final String languageCode = caption.getString("languageCode");
final String vssId = captionsArray.getObject(i).getString("vssId"); final String baseUrl = caption.getString("baseUrl");
final String vssId = caption.getString("vssId");
if (languageCode != null && baseUrl != null && vssId != null) { if (languageCode != null && baseUrl != null && vssId != null) {
final boolean isAutoGenerated = vssId.startsWith("a."); final boolean isAutoGenerated = vssId.startsWith("a.");
@ -717,7 +716,24 @@ public class YoutubeStreamExtractor extends StreamExtractor {
.setMediaFormat(format) .setMediaFormat(format)
.setLanguageCode(languageCode) .setLanguageCode(languageCode)
.setAutoGenerated(isAutoGenerated) .setAutoGenerated(isAutoGenerated)
.setAutoTranslated(false)
.build()); .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());
}
}
} }
} }

View File

@ -12,6 +12,7 @@ public final class SubtitlesStream extends Stream {
private final MediaFormat format; private final MediaFormat format;
private final Locale locale; private final Locale locale;
private final boolean autoGenerated; private final boolean autoGenerated;
private final boolean autoTranslated;
private final String code; private final String code;
/** /**
@ -30,6 +31,7 @@ public final class SubtitlesStream extends Stream {
private String languageCode; private String languageCode;
// Use of the Boolean class instead of the primitive type needed for setter call check // Use of the Boolean class instead of the primitive type needed for setter call check
private Boolean autoGenerated; private Boolean autoGenerated;
private Boolean autoTranslated;
/** /**
* Create a new {@link Builder} instance with default values. * Create a new {@link Builder} instance with default values.
@ -150,6 +152,18 @@ public final class SubtitlesStream extends Stream {
return this; 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. * Build a {@link SubtitlesStream} using the builder's current values.
* *
@ -194,13 +208,19 @@ public final class SubtitlesStream extends Stream {
+ "with setIsAutoGenerated."); + "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) { if (id == null) {
id = languageCode + (mediaFormat != null ? "." + mediaFormat.suffix id = languageCode + (mediaFormat != null ? "." + mediaFormat.suffix
: ""); : "");
} }
return new SubtitlesStream(id, content, isUrl, mediaFormat, deliveryMethod, return new SubtitlesStream(id, content, isUrl, mediaFormat, deliveryMethod,
languageCode, autoGenerated, manifestUrl); languageCode, autoGenerated, autoTranslated, manifestUrl);
} }
} }
@ -217,6 +237,7 @@ public final class SubtitlesStream extends Stream {
* @param deliveryMethod the {@link DeliveryMethod} of the stream * @param deliveryMethod the {@link DeliveryMethod} of the stream
* @param languageCode the language code of the stream * @param languageCode the language code of the stream
* @param autoGenerated whether the subtitles are auto-generated by the streaming service * @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, * @param manifestUrl the URL of the manifest this stream comes from (if applicable,
* otherwise null) * otherwise null)
*/ */
@ -228,6 +249,7 @@ public final class SubtitlesStream extends Stream {
@Nonnull final DeliveryMethod deliveryMethod, @Nonnull final DeliveryMethod deliveryMethod,
@Nonnull final String languageCode, @Nonnull final String languageCode,
final boolean autoGenerated, final boolean autoGenerated,
final boolean autoTranslated,
@Nullable final String manifestUrl) { @Nullable final String manifestUrl) {
super(id, content, isUrl, mediaFormat, deliveryMethod, manifestUrl); super(id, content, isUrl, mediaFormat, deliveryMethod, manifestUrl);
@ -253,6 +275,7 @@ public final class SubtitlesStream extends Stream {
this.code = languageCode; this.code = languageCode;
this.format = mediaFormat; this.format = mediaFormat;
this.autoGenerated = autoGenerated; this.autoGenerated = autoGenerated;
this.autoTranslated = autoTranslated;
} }
/** /**
@ -265,7 +288,7 @@ public final class SubtitlesStream extends Stream {
} }
/** /**
* Return whether if the subtitles are auto-generated. * Return whether the subtitles are auto-generated.
* <p> * <p>
* Some streaming services can generate subtitles for their contents, like YouTube. * Some streaming services can generate subtitles for their contents, like YouTube.
* </p> * </p>
@ -276,6 +299,21 @@ public final class SubtitlesStream extends Stream {
return autoGenerated; return autoGenerated;
} }
/**
* Whether the subtitles are translated automatically by a machine.
*
* <p>
* 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.
* </p>
*
* @return {code true} if the subtitles are auto-translated, {@link false} otherwise
*/
public boolean isAutoTranslated() {
return autoTranslated;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */