Extract Uploader's Avatar on YouTube.

This commit is contained in:
FireMasterK 2021-09-02 00:10:00 +05:30
parent 68f1fa994a
commit b9fad4fcc8
No known key found for this signature in database
GPG Key ID: 49451E4482CC5BCD
18 changed files with 1382 additions and 16 deletions

View File

@ -79,6 +79,12 @@ public class BandcampRadioInfoItemExtractor implements StreamInfoItemExtractor {
return ""; return "";
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return false; return false;

View File

@ -4,6 +4,8 @@ import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper; import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
import javax.annotation.Nullable;
public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor { public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
private final JsonObject discograph; private final JsonObject discograph;
@ -18,6 +20,12 @@ public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInf
return discograph.getString("band_name"); return discograph.getString("band_name");
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public String getName() { public String getName() {
return discograph.getString("title"); return discograph.getString("title");

View File

@ -8,6 +8,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamExtractor;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
@ -53,6 +54,12 @@ public class BandcampPlaylistStreamInfoItemExtractor extends BandcampStreamInfoI
return ""; return "";
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
/** /**
* Each track can have its own cover art. Therefore, unless a substitute is provided, * Each track can have its own cover art. Therefore, unless a substitute is provided,
* the thumbnail is extracted using a stream extractor. * the thumbnail is extracted using a stream extractor.

View File

@ -3,6 +3,8 @@ package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import javax.annotation.Nullable;
public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor { public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
private final Element resultInfo, searchResult; private final Element resultInfo, searchResult;
@ -24,6 +26,12 @@ public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoIte
} }
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
return resultInfo.getElementsByClass("heading").text(); return resultInfo.getElementsByClass("heading").text();

View File

@ -73,6 +73,12 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
return "https://media.ccc.de/c/" + conferenceInfo.getString("slug"); return "https://media.ccc.de/c/" + conferenceInfo.getString("slug");
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return false; return false;

View File

@ -68,6 +68,12 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
.getUrl(); // web URL .getUrl(); // web URL
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return false; return false;

View File

@ -46,6 +46,12 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
return event.getString("conference_url"); return event.getString("conference_url");
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return false; return false;

View File

@ -9,6 +9,8 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import javax.annotation.Nullable;
public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor { public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
protected final JsonObject item; protected final JsonObject item;
@ -54,6 +56,12 @@ public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor
.fromId("accounts/" + name + "@" + host, baseUrl).getUrl(); .fromId("accounts/" + name + "@" + host, baseUrl).getUrl();
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return false; return false;

View File

@ -7,6 +7,8 @@ import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps; import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps;
@ -43,6 +45,12 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
return replaceHttpWithHttps(itemObject.getObject("user").getString("permalink_url")); return replaceHttpWithHttps(itemObject.getObject("user").getString("permalink_url"));
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return itemObject.getObject("user").getBoolean("verified"); return itemObject.getObject("user").getBoolean("verified");

View File

@ -50,6 +50,12 @@ public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
return entryElement.select("author > uri").first().text(); return entryElement.select("author > uri").first().text();
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return false; return false;

View File

@ -9,6 +9,7 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -40,7 +41,7 @@ import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
*/ */
public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor { public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
private JsonObject videoInfo; private final JsonObject videoInfo;
private final TimeAgoParser timeAgoParser; private final TimeAgoParser timeAgoParser;
private StreamType cachedStreamType; private StreamType cachedStreamType;
@ -162,6 +163,17 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
return url; return url;
} }
@Nullable
@Override
public String getUploaderAvatarUrl() throws ParsingException {
if(videoInfo.has("channelThumbnailSupportedRenderers")) {
return JsonUtils.getArray(videoInfo, "channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail.thumbnails").getObject(0).getString("url");
}
return null;
}
@Override @Override
public boolean isUploaderVerified() throws ParsingException { public boolean isUploaderVerified() throws ParsingException {
return YoutubeParsingHelper.isVerified(videoInfo.getArray("ownerBadges")); return YoutubeParsingHelper.isVerified(videoInfo.getArray("ownerBadges"));

View File

@ -39,6 +39,7 @@ public class StreamInfoItem extends InfoItem {
private long duration = -1; private long duration = -1;
private String uploaderUrl = null; private String uploaderUrl = null;
private String uploaderAvatarUrl = null;
private boolean uploaderVerified = false; private boolean uploaderVerified = false;
public StreamInfoItem(int serviceId, String url, String name, StreamType streamType) { public StreamInfoItem(int serviceId, String url, String name, StreamType streamType) {
@ -82,6 +83,15 @@ public class StreamInfoItem extends InfoItem {
this.uploaderUrl = uploaderUrl; this.uploaderUrl = uploaderUrl;
} }
@Nullable
public String getUploaderAvatarUrl() {
return uploaderAvatarUrl;
}
public void setUploaderAvatarUrl(String uploaderAvatarUrl) {
this.uploaderAvatarUrl = uploaderAvatarUrl;
}
@Nullable @Nullable
public String getTextualUploadDate() { public String getTextualUploadDate() {
return textualUploadDate; return textualUploadDate;

View File

@ -71,6 +71,15 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor {
String getUploaderUrl() throws ParsingException; String getUploaderUrl() throws ParsingException;
/**
* Get the uploader's avatar
*
* @return The uploader's avatar url or {@code null} if not provided by the service.
* @throws ParsingException if there is an error in the extraction
*/
@Nullable
String getUploaderAvatarUrl() throws ParsingException;
/** /**
* Whether the uploader has been verified by the service's provider. * Whether the uploader has been verified by the service's provider.
* If there is no verification implemented, return <code>false</code>. * If there is no verification implemented, return <code>false</code>.

View File

@ -91,6 +91,11 @@ public class StreamInfoItemsCollector extends InfoItemsCollector<StreamInfoItem,
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try {
resultItem.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl());
} catch (Exception e) {
addError(e);
}
try { try {
resultItem.setUploaderVerified(extractor.isUploaderVerified()); resultItem.setUploaderVerified(extractor.isUploaderVerified());
} catch (Exception e) { } catch (Exception e) {

View File

@ -1,24 +1,18 @@
package org.schabi.newpipe.extractor.services.youtube.search; package org.schabi.newpipe.extractor.services.youtube.search;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.downloader.DownloaderFactory; import org.schabi.newpipe.downloader.DownloaderFactory;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.*;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest; import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@ -29,14 +23,11 @@ import java.util.Random;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmptyErrors; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmptyErrors;
import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.extractor.ServiceList.YouTube;
import static org.schabi.newpipe.extractor.services.DefaultTests.assertNoDuplicatedItems; import static org.schabi.newpipe.extractor.services.DefaultTests.assertNoDuplicatedItems;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.CHANNELS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.*;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.PLAYLISTS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.VIDEOS;
public class YoutubeSearchExtractorTest { public class YoutubeSearchExtractorTest {
@ -320,4 +311,36 @@ public class YoutubeSearchExtractorTest {
assertTrue(verified); assertTrue(verified);
} }
} }
public static class VideoUploaderAvatar extends DefaultSearchExtractorTest {
private static SearchExtractor extractor;
private static final String QUERY = "sidemen";
@BeforeClass
public static void setUp() throws Exception {
YoutubeParsingHelper.resetClientVersionAndKey();
YoutubeParsingHelper.setNumberGenerator(new Random(1));
NewPipe.init(new DownloaderFactory().getDownloader(RESOURCE_PATH + "video_uploader_avatar"));
extractor = YouTube.getSearchExtractor(QUERY, singletonList(VIDEOS), "");
extractor.fetchPage();
}
@Override public SearchExtractor extractor() { return extractor; }
@Override public StreamingService expectedService() { return YouTube; }
@Override public String expectedName() { return QUERY; }
@Override public String expectedId() { return QUERY; }
@Override public String expectedUrlContains() { return "youtube.com/results?search_query=" + QUERY; }
@Override public String expectedOriginalUrlContains() { return "youtube.com/results?search_query=" + QUERY; }
@Override public String expectedSearchString() { return QUERY; }
@Nullable @Override public String expectedSearchSuggestion() { return null; }
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
@Test
public void testUploaderAvatar() throws IOException, ExtractionException {
final List<InfoItem> items = extractor.getInitialPage().getItems();
for (InfoItem item : items) {
assertNotNull(((StreamInfoItem) item).getUploaderAvatarUrl());
}
}
}
} }