2
0
mirror of https://github.com/TeamNewPipe/NewPipeExtractor synced 2025-08-29 13:27:38 +00:00

Handle situations where there are multiple uploaders

This commit is contained in:
litetex 2025-07-12 12:45:17 +02:00
parent ef6a53ebb3
commit 182df6eebe
No known key found for this signature in database
GPG Key ID: 525B43E6039B3689

View File

@ -44,6 +44,7 @@ public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtra
private String cachedName; private String cachedName;
private Optional<String> cachedTextualUploadDate; private Optional<String> cachedTextualUploadDate;
private ChannelImageViewModel cachedChannelImageViewModel;
private JsonArray cachedMetadataRows; private JsonArray cachedMetadataRows;
/** /**
@ -165,10 +166,15 @@ public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtra
@Override @Override
public String getUploaderUrl() throws ParsingException { public String getUploaderUrl() throws ParsingException {
final String channelId = JsonUtils.getString(lockupViewModel, final String channelId = channelImageViewModel()
"metadata.lockupMetadataViewModel.image.decoratedAvatarViewModel" .forUploaderUrlExtraction()
+ ".rendererContext.commandContext.onTap" .getObject("rendererContext")
+ ".innertubeCommand.browseEndpoint.browseId"); .getObject("commandContext")
.getObject("onTap")
.getObject("innertubeCommand")
.getObject("browseEndpoint")
.getString("browseId");
if (isNullOrEmpty(channelId)) { if (isNullOrEmpty(channelId)) {
throw new ParsingException("Could not get uploader url"); throw new ParsingException("Could not get uploader url");
} }
@ -179,9 +185,9 @@ public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtra
@Override @Override
public List<Image> getUploaderAvatars() throws ParsingException { public List<Image> getUploaderAvatars() throws ParsingException {
return YoutubeParsingHelper.getImagesFromThumbnailsArray( return YoutubeParsingHelper.getImagesFromThumbnailsArray(
JsonUtils.getArray(lockupViewModel, JsonUtils.getArray(
"metadata.lockupMetadataViewModel.image.decoratedAvatarViewModel" channelImageViewModel().forAvatarExtraction(),
+ ".avatar.avatarViewModel.image.sources")); "avatarViewModel.image.sources"));
} }
@Override @Override
@ -253,6 +259,34 @@ public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtra
"contentImage.thumbnailViewModel.image.sources")); "contentImage.thumbnailViewModel.image.sources"));
} }
private ChannelImageViewModel channelImageViewModel() throws ParsingException {
if (cachedChannelImageViewModel == null) {
cachedChannelImageViewModel = determineChannelImageViewModel();
}
return cachedChannelImageViewModel;
}
private ChannelImageViewModel determineChannelImageViewModel() throws ParsingException {
final JsonObject image = lockupViewModel
.getObject("metadata")
.getObject("lockupMetadataViewModel")
.getObject("image");
final JsonObject single = image
.getObject("decoratedAvatarViewModel", null);
if (single != null) {
return new SingleChannelImageViewModel(single);
}
final JsonObject multi = image.getObject("avatarStackViewModel", null);
if (multi != null) {
return new MultiChannelImageViewModel(multi);
}
throw new ParsingException("Failed to determine channel image view model");
}
private Optional<JsonObject> metadataPart(final int rowIndex, final int partIndex) private Optional<JsonObject> metadataPart(final int rowIndex, final int partIndex)
throws ParsingException { throws ParsingException {
if (cachedMetadataRows == null) { if (cachedMetadataRows == null) {
@ -274,4 +308,64 @@ public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtra
private String getTextContentFromMetadataPart(final JsonObject metadataPart) { private String getTextContentFromMetadataPart(final JsonObject metadataPart) {
return metadataPart.getObject("text").getString("content"); return metadataPart.getObject("text").getString("content");
} }
abstract static class ChannelImageViewModel {
protected JsonObject viewModel;
protected ChannelImageViewModel(final JsonObject viewModel) {
this.viewModel = viewModel;
}
public abstract JsonObject forUploaderUrlExtraction();
public abstract JsonObject forAvatarExtraction();
}
static class SingleChannelImageViewModel extends ChannelImageViewModel {
SingleChannelImageViewModel(final JsonObject viewModel) {
super(viewModel);
}
@Override
public JsonObject forUploaderUrlExtraction() {
return viewModel;
}
@Override
public JsonObject forAvatarExtraction() {
return viewModel.getObject("avatar");
}
}
static class MultiChannelImageViewModel extends ChannelImageViewModel {
MultiChannelImageViewModel(final JsonObject viewModel) {
super(viewModel);
}
@Override
public JsonObject forUploaderUrlExtraction() {
return viewModel
.getObject("rendererContext")
.getObject("commandContext")
.getObject("onTap")
.getObject("innertubeCommand")
.getObject("showDialogCommand")
.getObject("panelLoadingStrategy")
.getObject("inlineContent")
.getObject("dialogViewModel")
.getObject("customContent")
.getObject("listViewModel")
.getArray("listItems")
.streamAsJsonObjects()
.map(item -> item.getObject("listItemViewModel"))
.findFirst()
.orElse(null);
}
@Override
public JsonObject forAvatarExtraction() {
return viewModel.getArray("avatars")
.getObject(0);
}
}
} }