Skip to content

Commit a00dc9c

Browse files
fix: videos in search results have wrong uploader names under certain conditions (YouTube)
1 parent d75b7ad commit a00dc9c

2 files changed

Lines changed: 216 additions & 36 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeLockupStreamInfoItemExtractor.java

Lines changed: 135 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -234,19 +234,19 @@ private String getDurationTextFromOverlay(@Nonnull final JsonObject overlay) {
234234

235235
@Override
236236
public String getUploaderName() throws ParsingException {
237-
final JsonArray metadataRows = lockupMetadataViewModel.getObject("metadata")
238-
.getObject("contentMetadataViewModel")
239-
.getArray("metadataRows");
237+
final JsonObject navigationEndpoint = getUploaderNavigationEndpoint();
238+
if (navigationEndpoint != null && navigationEndpoint.has("showDialogCommand")) {
239+
final String uploaderName = getFirstContributorName(navigationEndpoint);
240+
if (!isNullOrEmpty(uploaderName)) {
241+
return uploaderName;
242+
}
243+
}
240244

241-
if (metadataRows.size() > 0) {
242-
final JsonArray metadataParts = metadataRows.getObject(0).getArray("metadataParts");
243-
if (metadataParts.size() > 0) {
244-
final String uploaderName = metadataParts.getObject(0)
245-
.getObject("text")
246-
.getString("content");
247-
if (!isNullOrEmpty(uploaderName)) {
248-
return uploaderName;
249-
}
245+
final JsonObject uploaderText = getUploaderText();
246+
if (uploaderText != null) {
247+
final String uploaderName = uploaderText.getString("content");
248+
if (!isNullOrEmpty(uploaderName)) {
249+
return uploaderName;
250250
}
251251
}
252252

@@ -255,13 +255,85 @@ public String getUploaderName() throws ParsingException {
255255

256256
@Override
257257
public String getUploaderUrl() throws ParsingException {
258-
return getUrlFromNavigationEndpoint(
259-
lockupMetadataViewModel.getObject("image")
260-
.getObject("decoratedAvatarViewModel")
261-
.getObject("rendererContext")
262-
.getObject("commandContext")
263-
.getObject("onTap")
264-
.getObject("innertubeCommand"));
258+
final JsonObject navigationEndpoint = getUploaderNavigationEndpoint();
259+
if (navigationEndpoint != null) {
260+
final String uploaderUrl = getUrlFromNavigationEndpoint(navigationEndpoint);
261+
if (!isNullOrEmpty(uploaderUrl)) {
262+
return uploaderUrl;
263+
}
264+
}
265+
266+
throw new ParsingException("Could not get uploader url");
267+
}
268+
269+
@Nullable
270+
private JsonObject getUploaderText() {
271+
final JsonArray metadataRows = lockupMetadataViewModel.getObject("metadata")
272+
.getObject("contentMetadataViewModel")
273+
.getArray("metadataRows");
274+
275+
if (metadataRows.isEmpty()) {
276+
return null;
277+
}
278+
279+
final JsonArray metadataParts = metadataRows.getObject(0).getArray("metadataParts");
280+
if (metadataParts.isEmpty()) {
281+
return null;
282+
}
283+
284+
return metadataParts.getObject(0).getObject("text");
285+
}
286+
287+
@Nullable
288+
private JsonObject getUploaderNavigationEndpoint() {
289+
final JsonObject image = lockupMetadataViewModel.getObject("image");
290+
if (image == null || image.isEmpty()) {
291+
return null;
292+
}
293+
294+
if (image.has("decoratedAvatarViewModel")) {
295+
return image.getObject("decoratedAvatarViewModel")
296+
.getObject("rendererContext")
297+
.getObject("commandContext")
298+
.getObject("onTap")
299+
.getObject("innertubeCommand");
300+
}
301+
302+
if (image.has("avatarStackViewModel")) {
303+
return image.getObject("avatarStackViewModel")
304+
.getObject("rendererContext")
305+
.getObject("commandContext")
306+
.getObject("onTap")
307+
.getObject("innertubeCommand");
308+
}
309+
310+
return null;
311+
}
312+
313+
@Nullable
314+
private String getFirstContributorName(final JsonObject navigationEndpoint) {
315+
try {
316+
final JsonArray listItems = navigationEndpoint
317+
.getObject("showDialogCommand")
318+
.getObject("panelLoadingStrategy")
319+
.getObject("inlineContent")
320+
.getObject("dialogViewModel")
321+
.getObject("customContent")
322+
.getObject("listViewModel")
323+
.getArray("listItems");
324+
325+
if (listItems == null || listItems.isEmpty()) {
326+
return null;
327+
}
328+
329+
final String uploaderName = listItems.getObject(0)
330+
.getObject("listItemViewModel")
331+
.getObject("title")
332+
.getString("content", "");
333+
return isNullOrEmpty(uploaderName) ? null : uploaderName;
334+
} catch (final Exception ignored) {
335+
return null;
336+
}
265337
}
266338

267339
@Override
@@ -408,14 +480,50 @@ public boolean requiresMembership() throws ParsingException {
408480
@Override
409481
public String getUploaderAvatarUrl() throws ParsingException {
410482
try {
411-
return lockupMetadataViewModel.getObject("image")
412-
.getObject("decoratedAvatarViewModel")
413-
.getObject("avatar")
414-
.getObject("avatarViewModel")
415-
.getObject("image")
416-
.getArray("sources")
417-
.getObject(0)
418-
.getString("url");
483+
final JsonObject navigationEndpoint = getUploaderNavigationEndpoint();
484+
if (navigationEndpoint != null && navigationEndpoint.has("showDialogCommand")) {
485+
final JsonArray sources = navigationEndpoint
486+
.getObject("showDialogCommand")
487+
.getObject("panelLoadingStrategy")
488+
.getObject("inlineContent")
489+
.getObject("dialogViewModel")
490+
.getObject("customContent")
491+
.getObject("listViewModel")
492+
.getArray("listItems")
493+
.getObject(0)
494+
.getObject("listItemViewModel")
495+
.getObject("leadingAccessory")
496+
.getObject("avatarViewModel")
497+
.getObject("image")
498+
.getArray("sources");
499+
if (!sources.isEmpty()) {
500+
return sources.getObject(0).getString("url");
501+
}
502+
}
503+
504+
final JsonObject image = lockupMetadataViewModel.getObject("image");
505+
if (image.has("decoratedAvatarViewModel")) {
506+
return image.getObject("decoratedAvatarViewModel")
507+
.getObject("avatar")
508+
.getObject("avatarViewModel")
509+
.getObject("image")
510+
.getArray("sources")
511+
.getObject(0)
512+
.getString("url");
513+
}
514+
515+
if (image.has("avatarStackViewModel")) {
516+
return image.getObject("avatarStackViewModel")
517+
.getArray("avatars")
518+
.getObject(0)
519+
.getObject("avatarViewModel")
520+
.getObject("image")
521+
.getArray("sources")
522+
.getObject(0)
523+
.getString("url");
524+
}
525+
526+
return null;
419527
} catch (final Exception e) {
420528
return null;
421529
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -284,13 +284,13 @@ public long getDuration() throws ParsingException {
284284

285285
@Override
286286
public String getUploaderName() throws ParsingException {
287-
String name = getTextFromObject(videoInfo.getObject("longBylineText"));
287+
String name = getUploaderNameFromTextObject(videoInfo.getObject("longBylineText"));
288288

289289
if (isNullOrEmpty(name)) {
290-
name = getTextFromObject(videoInfo.getObject("ownerText"));
290+
name = getUploaderNameFromTextObject(videoInfo.getObject("ownerText"));
291291

292292
if (isNullOrEmpty(name)) {
293-
name = getTextFromObject(videoInfo.getObject("shortBylineText"));
293+
name = getUploaderNameFromTextObject(videoInfo.getObject("shortBylineText"));
294294

295295
if (isNullOrEmpty(name)) {
296296
if (!isNullOrEmpty(fallbackUploaderName)) {
@@ -306,16 +306,13 @@ public String getUploaderName() throws ParsingException {
306306

307307
@Override
308308
public String getUploaderUrl() throws ParsingException {
309-
String url = getUrlFromNavigationEndpoint(videoInfo.getObject("longBylineText")
310-
.getArray("runs").getObject(0).getObject("navigationEndpoint"));
309+
String url = getUploaderUrlFromTextObject(videoInfo.getObject("longBylineText"));
311310

312311
if (isNullOrEmpty(url)) {
313-
url = getUrlFromNavigationEndpoint(videoInfo.getObject("ownerText")
314-
.getArray("runs").getObject(0).getObject("navigationEndpoint"));
312+
url = getUploaderUrlFromTextObject(videoInfo.getObject("ownerText"));
315313

316314
if (isNullOrEmpty(url)) {
317-
url = getUrlFromNavigationEndpoint(videoInfo.getObject("shortBylineText")
318-
.getArray("runs").getObject(0).getObject("navigationEndpoint"));
315+
url = getUploaderUrlFromTextObject(videoInfo.getObject("shortBylineText"));
319316

320317
if (isNullOrEmpty(url)) {
321318
if (!isNullOrEmpty(fallbackUploaderUrl)) {
@@ -329,6 +326,81 @@ public String getUploaderUrl() throws ParsingException {
329326
return url;
330327
}
331328

329+
@Nullable
330+
private String getUploaderNameFromTextObject(@Nullable final JsonObject textObject)
331+
throws ParsingException {
332+
if (textObject == null || textObject.isEmpty()) {
333+
return null;
334+
}
335+
336+
final JsonObject navigationEndpoint = getUploaderNavigationEndpoint(textObject);
337+
if (navigationEndpoint != null && navigationEndpoint.has("showDialogCommand")) {
338+
final String name = getFirstContributorName(navigationEndpoint);
339+
if (!isNullOrEmpty(name)) {
340+
return name;
341+
}
342+
}
343+
344+
return getTextFromObject(textObject);
345+
}
346+
347+
@Nullable
348+
private String getUploaderUrlFromTextObject(@Nullable final JsonObject textObject)
349+
throws ParsingException {
350+
final JsonObject navigationEndpoint = getUploaderNavigationEndpoint(textObject);
351+
if (navigationEndpoint == null) {
352+
return null;
353+
}
354+
355+
return getUrlFromNavigationEndpoint(navigationEndpoint);
356+
}
357+
358+
@Nullable
359+
private JsonObject getUploaderNavigationEndpoint(@Nullable final JsonObject textObject) {
360+
if (textObject == null || textObject.isEmpty() || !textObject.has("runs")) {
361+
return null;
362+
}
363+
364+
final JsonArray runs = textObject.getArray("runs");
365+
if (runs == null || runs.isEmpty()) {
366+
return null;
367+
}
368+
369+
final JsonObject firstRun = runs.getObject(0);
370+
if (firstRun == null || !firstRun.has("navigationEndpoint")) {
371+
return null;
372+
}
373+
374+
return firstRun.getObject("navigationEndpoint");
375+
}
376+
377+
@Nullable
378+
private String getFirstContributorName(final JsonObject navigationEndpoint) {
379+
try {
380+
final JsonArray listItems = navigationEndpoint
381+
.getObject("showDialogCommand")
382+
.getObject("panelLoadingStrategy")
383+
.getObject("inlineContent")
384+
.getObject("dialogViewModel")
385+
.getObject("customContent")
386+
.getObject("listViewModel")
387+
.getArray("listItems");
388+
389+
if (listItems == null || listItems.isEmpty()) {
390+
return null;
391+
}
392+
393+
final String name = listItems.getObject(0)
394+
.getObject("listItemViewModel")
395+
.getObject("title")
396+
.getString("content", "");
397+
398+
return isNullOrEmpty(name) ? null : name;
399+
} catch (final Exception ignored) {
400+
return null;
401+
}
402+
}
403+
332404
@Nullable
333405
@Override
334406
public String getUploaderAvatarUrl() throws ParsingException {

0 commit comments

Comments
 (0)