Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.liskovsoft.mediaserviceinterfaces.data;

public interface EndScreenItem {
int TYPE_VIDEO = 0;
int TYPE_CHANNEL = 1;
int TYPE_PLAYLIST = 2;
int TYPE_UNKNOWN = 3;

int getType();
String getTitle();
String getMetadata();
String getImageUrl();
String getId(); // videoId, channelId, or playlistId
long getStartTimeMs();
long getEndTimeMs();
float getLeft();
float getTop();
float getWidth();
float getAspectRatio();
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ public interface MediaItemMetadata {
List<NotificationState> getNotificationStates();
long getDurationMs();
String getBadgeText();
List<EndScreenItem> getEndScreenItems();
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.liskovsoft.youtubeapi.common.models.gen

import com.liskovsoft.googlecommon.common.helpers.YouTubeHelper
import com.liskovsoft.mediaserviceinterfaces.data.MediaItem
import com.liskovsoft.sharedutils.helpers.DateHelper
import com.liskovsoft.youtubeapi.browse.v2.gen.getContinuationToken
import com.liskovsoft.youtubeapi.browse.v2.gen.getThumbnails
import com.liskovsoft.youtubeapi.browse.v2.gen.getVideoId
import com.liskovsoft.googlecommon.common.helpers.ServiceHelper
import com.liskovsoft.youtubeapi.next.v2.gen.getContinuationToken
import com.liskovsoft.youtubeapi.next.v1.models.EndScreenElement
import com.liskovsoft.mediaserviceinterfaces.data.MediaItem
import com.liskovsoft.sharedutils.helpers.DateHelper

// A badge before the image
private const val BADGE_STYLE_LIVE = "LIVE"
Expand Down Expand Up @@ -402,4 +403,9 @@ internal fun ResponseContext.getSuggestToken(): String? = serviceTrackingParams?
if (it?.service == SERVICE_SUGGEST) {
it.params?.firstOrNull { it?.key == KEY_SUGGEST_TOKEN }?.value
} else null
}
}

////////////
// End Screen Support

internal fun EndScreenElement.getThumbnails() = image?.thumbnails
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.liskovsoft.youtubeapi.next.v1.models;

import com.google.gson.annotations.SerializedName;

public class EndScreen {
@SerializedName("endscreenRenderer")
private EndScreenRenderer mRenderer;

public EndScreenRenderer getRenderer() {
return mRenderer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.liskovsoft.youtubeapi.next.v1.models;

import com.google.gson.annotations.SerializedName;
import com.liskovsoft.googlecommon.common.models.V2.TextItem;
import com.liskovsoft.youtubeapi.common.models.gen.ThumbnailItem;

public class EndScreenElement {
@SerializedName("style")
private String mStyle; // "CHANNEL", "VIDEO", "PLAYLIST"

@SerializedName("image")
private ThumbnailItem mImage;

@SerializedName("title")
private TextItem mTitle;

@SerializedName("metadata")
private TextItem mMetadata;

@SerializedName("left")
private float mLeft;

@SerializedName("top")
private float mTop;

@SerializedName("width")
private float mWidth;

@SerializedName("aspectRatio")
private float mAspectRatio;

@SerializedName("startMs")
private String mStartMs;

@SerializedName("endMs")
private String mEndMs;

@SerializedName("endpoint")
private Endpoint mEndpoint;

public String getStyle() {
return mStyle;
}

public ThumbnailItem getImage() {
return mImage;
}

public TextItem getTitle() {
return mTitle;
}

public TextItem getMetadata() {
return mMetadata;
}

public float getLeft() {
return mLeft;
}

public float getTop() {
return mTop;
}

public float getWidth() {
return mWidth;
}

public float getAspectRatio() {
return mAspectRatio;
}

public String getStartMs() {
return mStartMs;
}

public String getEndMs() {
return mEndMs;
}

public Endpoint getEndpoint() {
return mEndpoint;
}

public static class Endpoint {
@SerializedName("browseEndpoint")
private BrowseEndpoint mBrowseEndpoint;

@SerializedName("watchEndpoint")
private WatchEndpoint mWatchEndpoint;

public BrowseEndpoint getBrowseEndpoint() {
return mBrowseEndpoint;
}

public WatchEndpoint getWatchEndpoint() {
return mWatchEndpoint;
}

public static class BrowseEndpoint {
@SerializedName("browseId")
private String mBrowseId; // channelId or playlistId

public String getBrowseId() {
return mBrowseId;
}
}

public static class WatchEndpoint {
@SerializedName("videoId")
private String mVideoId;

public String getVideoId() {
return mVideoId;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.liskovsoft.youtubeapi.next.v1.models;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class EndScreenRenderer {
@SerializedName("elements")
private List<ElementWrapper> mElements;

public List<ElementWrapper> getElements() {
return mElements;
}

public static class ElementWrapper {
@SerializedName("endscreenElementRenderer")
private EndScreenElement mElement;

public EndScreenElement getElement() {
return mElement;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.liskovsoft.youtubeapi.next.v1.models.Playlist;
import com.liskovsoft.youtubeapi.next.v1.models.SuggestedSection;
import com.liskovsoft.youtubeapi.next.v1.models.VideoOwner;
import com.liskovsoft.youtubeapi.next.v1.models.EndScreen;

import java.util.List;

Expand All @@ -35,6 +36,8 @@ public class WatchNextResult {
private ItemWrapper mReplayItem;
@JsonPath("$.transportControls.transportControlsRenderer")
private ButtonStates mButtonStates;
@JsonPath("$.endscreen")
private EndScreen mEndScreen;

public List<SuggestedSection> getSuggestedSections() {
return mSuggestedSections;
Expand Down Expand Up @@ -71,4 +74,8 @@ public ButtonStates getButtonStates() {
public VideoItem getVideoDetails() {
return mVideoDetails;
}

public EndScreen getEndScreen() {
return mEndScreen;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,8 @@ internal data class MediaItemMetadataImpl(private val watchNextResult: WatchNext
override fun getBadgeText(): String? {
return badgeTextItem
}
}

override fun getEndScreenItems(): MutableList<EndScreenItem>? {
return null // V2 API doesn't support end screens yet
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.liskovsoft.youtubeapi.service.data;

import com.liskovsoft.mediaserviceinterfaces.data.EndScreenItem;
import com.liskovsoft.sharedutils.helpers.Helpers;
import com.liskovsoft.youtubeapi.next.v1.models.EndScreenElement;
import com.liskovsoft.googlecommon.common.helpers.YouTubeHelper;

import static com.liskovsoft.youtubeapi.common.models.gen.CommonHelperKt.getThumbnails;

public class YouTubeEndScreenItem implements EndScreenItem {
private int mType;
private String mTitle;
private String mMetadata;
private String mImageUrl;
private String mId;
private long mStartTimeMs;
private long mEndTimeMs;
private float mLeft;
private float mTop;
private float mWidth;
private float mAspectRatio;

public static YouTubeEndScreenItem from(EndScreenElement element) {
if (element == null) {
return null;
}

YouTubeEndScreenItem item = new YouTubeEndScreenItem();

// Determine type
String style = element.getStyle();
if ("VIDEO".equals(style)) {
item.mType = TYPE_VIDEO;
} else if ("CHANNEL".equals(style)) {
item.mType = TYPE_CHANNEL;
} else if ("PLAYLIST".equals(style)) {
item.mType = TYPE_PLAYLIST;
} else {
item.mType = TYPE_UNKNOWN;
}

// Extract text content
item.mTitle = element.getTitle() != null ? element.getTitle().toString() : null;
item.mMetadata = element.getMetadata() != null ? element.getMetadata().toString() : null;

// Extract image URL using Kotlin extension
if (element.getImage() != null) {
item.mImageUrl = YouTubeHelper.findOptimalResThumbnailUrl(getThumbnails(element));
}

// Extract timing
item.mStartTimeMs = Helpers.parseLong(element.getStartMs());
item.mEndTimeMs = Helpers.parseLong(element.getEndMs());

// Extract positioning
item.mLeft = element.getLeft();
item.mTop = element.getTop();
item.mWidth = element.getWidth();
item.mAspectRatio = element.getAspectRatio();

// Extract ID based on endpoint type
if (element.getEndpoint() != null) {
EndScreenElement.Endpoint endpoint = element.getEndpoint();

if (endpoint.getWatchEndpoint() != null) {
item.mId = endpoint.getWatchEndpoint().getVideoId();
} else if (endpoint.getBrowseEndpoint() != null) {
item.mId = endpoint.getBrowseEndpoint().getBrowseId();
}
}

return item;
}

@Override
public int getType() {
return mType;
}

@Override
public String getTitle() {
return mTitle;
}

@Override
public String getMetadata() {
return mMetadata;
}

@Override
public String getImageUrl() {
return mImageUrl;
}

@Override
public String getId() {
return mId;
}

@Override
public long getStartTimeMs() {
return mStartTimeMs;
}

@Override
public long getEndTimeMs() {
return mEndTimeMs;
}

@Override
public float getLeft() {
return mLeft;
}

@Override
public float getTop() {
return mTop;
}

@Override
public float getWidth() {
return mWidth;
}

@Override
public float getAspectRatio() {
return mAspectRatio;
}
}
Loading