mirror of
https://github.com/TeamNewPipe/NewPipeExtractor
synced 2024-12-13 04:19:37 +01:00
Merge pull request #723 from FireMasterK/uploader-avatar
Extract Uploader's Avatar on YouTube and PeerTube
This commit is contained in:
commit
c8037f5e00
@ -79,6 +79,12 @@ public class BandcampRadioInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
|
@ -4,6 +4,8 @@ import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
|
||||
|
||||
private final JsonObject discograph;
|
||||
@ -18,6 +20,12 @@ public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInf
|
||||
return discograph.getString("band_name");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return discograph.getString("title");
|
||||
|
@ -8,6 +8,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@ -53,6 +54,12 @@ public class BandcampPlaylistStreamInfoItemExtractor extends BandcampStreamInfoI
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each track can have its own cover art. Therefore, unless a substitute is provided,
|
||||
* the thumbnail is extracted using a stream extractor.
|
||||
|
@ -3,6 +3,8 @@ package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
|
||||
|
||||
private final Element resultInfo, searchResult;
|
||||
@ -24,6 +26,12 @@ public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoIte
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return resultInfo.getElementsByClass("heading").text();
|
||||
|
@ -73,6 +73,12 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
|
||||
return "https://media.ccc.de/c/" + conferenceInfo.getString("slug");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
|
@ -68,6 +68,12 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
|
||||
.getUrl(); // web URL
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
|
@ -46,6 +46,12 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
|
||||
return event.getString("conference_url");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
|
@ -9,6 +9,8 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.extractor.utils.JsonUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
|
||||
protected final JsonObject item;
|
||||
@ -54,6 +56,16 @@ public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor
|
||||
.fromId("accounts/" + name + "@" + host, baseUrl).getUrl();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
final JsonObject account = item.getObject("account");
|
||||
if (account.has("avatar") && !account.isNull("avatar")) {
|
||||
return baseUrl + account.getObject("avatar").getString("path");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
|
@ -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.StreamType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
|
||||
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"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return itemObject.getObject("user").getBoolean("verified");
|
||||
|
@ -50,6 +50,12 @@ public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
return entryElement.select("author > uri").first().text();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() throws ParsingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
|
@ -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.stream.StreamInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.extractor.utils.JsonUtils;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -40,7 +41,7 @@ import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
*/
|
||||
|
||||
public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
private JsonObject videoInfo;
|
||||
private final JsonObject videoInfo;
|
||||
private final TimeAgoParser timeAgoParser;
|
||||
private StreamType cachedStreamType;
|
||||
|
||||
@ -162,6 +163,18 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
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
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return YoutubeParsingHelper.isVerified(videoInfo.getArray("ownerBadges"));
|
||||
|
@ -39,6 +39,7 @@ public class StreamInfoItem extends InfoItem {
|
||||
private long duration = -1;
|
||||
|
||||
private String uploaderUrl = null;
|
||||
private String uploaderAvatarUrl = null;
|
||||
private boolean uploaderVerified = false;
|
||||
|
||||
public StreamInfoItem(int serviceId, String url, String name, StreamType streamType) {
|
||||
@ -82,6 +83,15 @@ public class StreamInfoItem extends InfoItem {
|
||||
this.uploaderUrl = uploaderUrl;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getUploaderAvatarUrl() {
|
||||
return uploaderAvatarUrl;
|
||||
}
|
||||
|
||||
public void setUploaderAvatarUrl(final String uploaderAvatarUrl) {
|
||||
this.uploaderAvatarUrl = uploaderAvatarUrl;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getTextualUploadDate() {
|
||||
return textualUploadDate;
|
||||
|
@ -71,6 +71,15 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor {
|
||||
|
||||
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.
|
||||
* If there is no verification implemented, return <code>false</code>.
|
||||
|
@ -91,6 +91,11 @@ public class StreamInfoItemsCollector extends InfoItemsCollector<StreamInfoItem,
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl());
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.setUploaderVerified(extractor.isUploaderVerified());
|
||||
} catch (Exception e) {
|
||||
|
@ -48,6 +48,11 @@ public final class DefaultTests {
|
||||
assertExpectedLinkType(expectedService, uploaderUrl, LinkType.CHANNEL);
|
||||
}
|
||||
|
||||
final String uploaderAvatarUrl = streamInfoItem.getUploaderAvatarUrl();
|
||||
if (!isNullOrEmpty(uploaderAvatarUrl)) {
|
||||
assertIsSecureUrl(uploaderAvatarUrl);
|
||||
}
|
||||
|
||||
assertExpectedLinkType(expectedService, streamInfoItem.getUrl(), LinkType.STREAM);
|
||||
|
||||
if (!isNullOrEmpty(streamInfoItem.getTextualUploadDate())) {
|
||||
|
@ -1,24 +1,18 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube.search;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.*;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
|
||||
import org.schabi.newpipe.extractor.stream.Description;
|
||||
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@ -29,14 +23,11 @@ import java.util.Random;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmptyErrors;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
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.PLAYLISTS;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.VIDEOS;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.*;
|
||||
|
||||
public class YoutubeSearchExtractorTest {
|
||||
|
||||
@ -320,4 +311,36 @@ public class YoutubeSearchExtractorTest {
|
||||
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 (final InfoItem item : items) {
|
||||
assertNotNull(((StreamInfoItem) item).getUploaderAvatarUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user