Skip to content

Commit

Permalink
Merge pull request #953 from Theta-Dev/attributed-text-desc
Browse files Browse the repository at this point in the history
[YouTube] Add support for attributed text description
  • Loading branch information
AudricV committed Nov 3, 2022
2 parents 31bf704 + 592e1d6 commit ffffb04
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,90 @@ public static String getTextFromObject(final JsonObject textObject, final boolea
return text;
}

/**
* Parse a video description in the new "attributed" format, which contains the entire visible
* plaintext ({@code content}) and an array of {@code commandRuns}.
*
* <p>
* The {@code commandRuns} include the links and their position in the text.
* </p>
*
* @param attributedDescription the JSON object of the attributed description
* @return the parsed description, in HTML format, as a string
*/
@Nullable
public static String getAttributedDescription(
@Nullable final JsonObject attributedDescription) {
if (isNullOrEmpty(attributedDescription)) {
return null;
}

final String content = attributedDescription.getString("content");
final JsonArray commandRuns = attributedDescription.getArray("commandRuns");
if (content == null) {
return null;
}

final StringBuilder textBuilder = new StringBuilder();
int textStart = 0;

for (final Object commandRun: commandRuns) {
if (!(commandRun instanceof JsonObject)) {
continue;
}

final JsonObject run = ((JsonObject) commandRun);
final int startIndex = run.getInt("startIndex", -1);
final int length = run.getInt("length");
final JsonObject navigationEndpoint = run.getObject("onTap")
.getObject("innertubeCommand");

if (startIndex < 0 || length < 1 || navigationEndpoint == null) {
continue;
}

final String url;
try {
url = getUrlFromNavigationEndpoint(navigationEndpoint);
} catch (final ParsingException e) {
continue;
}

if (url == null) {
continue;
}

// Append text before the link
if (startIndex > textStart) {
textBuilder.append(content, textStart, startIndex);
}

// Trim and append link text
// Channel/Video format: 3xu00a0, (/ •), u00a0, <Name>, 2xu00a0
final String linkText = content.substring(startIndex, startIndex + length)
.replace('\u00a0', ' ')
.trim()
.replaceFirst("^[/•] *", "");

textBuilder.append("<a href=\"")
.append(url)
.append("\">")
.append(linkText)
.append("</a>");

textStart = startIndex + length;
}

// Append the remaining text
if (textStart < content.length()) {
textBuilder.append(content.substring(textStart));
}

return textBuilder.toString()
.replaceAll("\\n", "<br>")
.replaceAll(" {2}", " &nbsp;");
}

@Nullable
public static String getTextFromObject(final JsonObject textObject) throws ParsingException {
return getTextFromObject(textObject, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonIosPostResponse;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getAttributedDescription;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareAndroidMobileJsonBuilder;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareIosMobileJsonBuilder;
Expand Down Expand Up @@ -288,6 +289,12 @@ public Description getDescription() throws ParsingException {
if (!isNullOrEmpty(description)) {
return new Description(description, Description.HTML);
}

final String attributedDescription = getAttributedDescription(
getVideoSecondaryInfoRenderer().getObject("attributedDescription"));
if (!isNullOrEmpty(attributedDescription)) {
return new Description(attributedDescription, Description.HTML);
}
} catch (final ParsingException ignored) {
// Age-restricted videos cause a ParsingException here
}
Expand Down

Large diffs are not rendered by default.

0 comments on commit ffffb04

Please sign in to comment.