Skip to content

Commit bd6659c

Browse files
committed
Handle situations where there are multiple uploaders
1 parent 94b76ed commit bd6659c

1 file changed

Lines changed: 101 additions & 7 deletions

File tree

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

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtra
4444
private String cachedName;
4545
private Optional<String> cachedTextualUploadDate;
4646

47+
private ChannelImageViewModel cachedChannelImageViewModel;
4748
private JsonArray cachedMetadataRows;
4849

4950
/**
@@ -165,10 +166,15 @@ public String getUploaderName() throws ParsingException {
165166

166167
@Override
167168
public String getUploaderUrl() throws ParsingException {
168-
final String channelId = JsonUtils.getString(lockupViewModel,
169-
"metadata.lockupMetadataViewModel.image.decoratedAvatarViewModel"
170-
+ ".rendererContext.commandContext.onTap"
171-
+ ".innertubeCommand.browseEndpoint.browseId");
169+
final String channelId = channelImageViewModel()
170+
.forUploaderUrlExtraction()
171+
.getObject("rendererContext")
172+
.getObject("commandContext")
173+
.getObject("onTap")
174+
.getObject("innertubeCommand")
175+
.getObject("browseEndpoint")
176+
.getString("browseId");
177+
172178
if (isNullOrEmpty(channelId)) {
173179
throw new ParsingException("Could not get uploader url");
174180
}
@@ -179,9 +185,9 @@ public String getUploaderUrl() throws ParsingException {
179185
@Override
180186
public List<Image> getUploaderAvatars() throws ParsingException {
181187
return YoutubeParsingHelper.getImagesFromThumbnailsArray(
182-
JsonUtils.getArray(lockupViewModel,
183-
"metadata.lockupMetadataViewModel.image.decoratedAvatarViewModel"
184-
+ ".avatar.avatarViewModel.image.sources"));
188+
JsonUtils.getArray(
189+
channelImageViewModel().forAvatarExtraction(),
190+
"avatarViewModel.image.sources"));
185191
}
186192

187193
@Override
@@ -253,6 +259,34 @@ public List<Image> getThumbnails() throws ParsingException {
253259
"contentImage.thumbnailViewModel.image.sources"));
254260
}
255261

262+
private ChannelImageViewModel channelImageViewModel() throws ParsingException {
263+
if (cachedChannelImageViewModel == null) {
264+
cachedChannelImageViewModel = determineChannelImageViewModel();
265+
}
266+
267+
return cachedChannelImageViewModel;
268+
}
269+
270+
private ChannelImageViewModel determineChannelImageViewModel() throws ParsingException {
271+
final JsonObject image = lockupViewModel
272+
.getObject("metadata")
273+
.getObject("lockupMetadataViewModel")
274+
.getObject("image");
275+
276+
final JsonObject single = image
277+
.getObject("decoratedAvatarViewModel", null);
278+
if (single != null) {
279+
return new SingleChannelImageViewModel(single);
280+
}
281+
282+
final JsonObject multi = image.getObject("avatarStackViewModel", null);
283+
if (multi != null) {
284+
return new MultiChannelImageViewModel(multi);
285+
}
286+
287+
throw new ParsingException("Failed to determine channel image view model");
288+
}
289+
256290
private Optional<JsonObject> metadataPart(final int rowIndex, final int partIndex)
257291
throws ParsingException {
258292
if (cachedMetadataRows == null) {
@@ -274,4 +308,64 @@ private Optional<JsonObject> metadataPart(final int rowIndex, final int partInde
274308
private String getTextContentFromMetadataPart(final JsonObject metadataPart) {
275309
return metadataPart.getObject("text").getString("content");
276310
}
311+
312+
abstract static class ChannelImageViewModel {
313+
protected JsonObject viewModel;
314+
315+
protected ChannelImageViewModel(final JsonObject viewModel) {
316+
this.viewModel = viewModel;
317+
}
318+
319+
public abstract JsonObject forUploaderUrlExtraction();
320+
321+
public abstract JsonObject forAvatarExtraction();
322+
}
323+
324+
static class SingleChannelImageViewModel extends ChannelImageViewModel {
325+
SingleChannelImageViewModel(final JsonObject viewModel) {
326+
super(viewModel);
327+
}
328+
329+
@Override
330+
public JsonObject forUploaderUrlExtraction() {
331+
return viewModel;
332+
}
333+
334+
@Override
335+
public JsonObject forAvatarExtraction() {
336+
return viewModel.getObject("avatar");
337+
}
338+
}
339+
340+
static class MultiChannelImageViewModel extends ChannelImageViewModel {
341+
MultiChannelImageViewModel(final JsonObject viewModel) {
342+
super(viewModel);
343+
}
344+
345+
@Override
346+
public JsonObject forUploaderUrlExtraction() {
347+
return viewModel
348+
.getObject("rendererContext")
349+
.getObject("commandContext")
350+
.getObject("onTap")
351+
.getObject("innertubeCommand")
352+
.getObject("showDialogCommand")
353+
.getObject("panelLoadingStrategy")
354+
.getObject("inlineContent")
355+
.getObject("dialogViewModel")
356+
.getObject("customContent")
357+
.getObject("listViewModel")
358+
.getArray("listItems")
359+
.streamAsJsonObjects()
360+
.map(item -> item.getObject("listItemViewModel"))
361+
.findFirst()
362+
.orElse(null);
363+
}
364+
365+
@Override
366+
public JsonObject forAvatarExtraction() {
367+
return viewModel.getArray("avatars")
368+
.getObject(0);
369+
}
370+
}
277371
}

0 commit comments

Comments
 (0)