import "pkg:/source/api/sdk.bs" function ItemGetPlaybackInfo(id as string, startTimeTicks = 0 as longinteger) params = { "UserId": m.global.session.user.id, "StartTimeTicks": startTimeTicks, "IsPlayback": true, "AutoOpenLiveStream": true, "MaxStreamingBitrate": "140000000" } resp = APIRequest(Substitute("Items/{0}/PlaybackInfo", id), params) return getJson(resp) end function function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioTrackIndex = -1 as integer, subtitleTrackIndex = -1 as integer, startTimeTicks = 0 as longinteger) body = { "DeviceProfile": getDeviceProfile() } params = { "UserId": m.global.session.user.id, "StartTimeTicks": startTimeTicks, "IsPlayback": true, "AutoOpenLiveStream": true, "MaxStreamingBitrate": "140000000", "MaxStaticBitrate": "140000000", "SubtitleStreamIndex": subtitleTrackIndex } if mediaSourceId <> "" then params.MediaSourceId = mediaSourceId if audioTrackIndex > -1 then params.AudioStreamIndex = audioTrackIndex req = APIRequest(Substitute("Items/{0}/PlaybackInfo", id), params) req.SetRequest("POST") return postJson(req, FormatJson(body)) end function ' Search across all libraries function searchMedia(query as string) if query <> "" data = api.users.GetItemsByQuery(m.global.session.user.id, { "searchTerm": query, "IncludePeople": true, "IncludeMedia": true, "IncludeShows": true, "IncludeGenres": true, "IncludeStudios": true, "IncludeArtists": true, "IncludeItemTypes": "LiveTvChannel,Movie,BoxSet,Series,Episode,Video,Person,Audio,MusicAlbum,MusicArtist,Playlist", "EnableTotalRecordCount": false, "ImageTypeLimit": 1, "Recursive": true, "limit": 100 }) results = [] for each item in data.Items tmp = CreateObject("roSGNode", "SearchData") tmp.image = PosterImage(item.id) tmp.json = item results.push(tmp) end for data.Items = results return data end if return [] end function ' MetaData about an item function ItemMetaData(id as string) url = Substitute("Users/{0}/Items/{1}", m.global.session.user.id, id) resp = APIRequest(url) data = getJson(resp) if data = invalid then return invalid imgParams = {} if data.type <> "Audio" if data.UserData <> invalid and data.UserData.PlayedPercentage <> invalid param = { "PercentPlayed": data.UserData.PlayedPercentage } imgParams.Append(param) end if end if if data.type = "Movie" or data.type = "MusicVideo" tmp = CreateObject("roSGNode", "MovieData") tmp.image = PosterImage(data.id, imgParams) tmp.json = data return tmp else if data.type = "Series" tmp = CreateObject("roSGNode", "SeriesData") tmp.image = PosterImage(data.id) tmp.json = data return tmp else if data.type = "Episode" ' param = { "AddPlayedIndicator": data.UserData.Played } ' imgParams.Append(param) tmp = CreateObject("roSGNode", "TVEpisodeData") tmp.image = PosterImage(data.id, imgParams) tmp.json = data return tmp else if data.type = "BoxSet" or data.type = "Playlist" tmp = CreateObject("roSGNode", "CollectionData") tmp.image = PosterImage(data.id, imgParams) tmp.json = data return tmp else if data.type = "Season" tmp = CreateObject("roSGNode", "TVSeasonData") tmp.image = PosterImage(data.id) tmp.json = data return tmp else if data.type = "Video" tmp = CreateObject("roSGNode", "VideoData") tmp.image = PosterImage(data.id) tmp.json = data return tmp else if data.type = "Trailer" tmp = CreateObject("roSGNode", "VideoData") tmp.json = data return tmp else if data.type = "TvChannel" or data.type = "Program" tmp = CreateObject("roSGNode", "ChannelData") tmp.image = PosterImage(data.id) tmp.isFavorite = data.UserData.isFavorite tmp.json = data return tmp else if data.type = "Person" tmp = CreateObject("roSGNode", "PersonData") tmp.image = PosterImage(data.id, { "MaxWidth": 300, "MaxHeight": 450 }) tmp.json = data return tmp else if data.type = "MusicArtist" ' User clicked on an artist and wants to see the list of their albums tmp = CreateObject("roSGNode", "MusicArtistData") tmp.image = PosterImage(data.id) tmp.json = data return tmp else if data.type = "MusicAlbum" ' User clicked on an album and wants to see the list of songs tmp = CreateObject("roSGNode", "MusicAlbumSongListData") tmp.image = PosterImage(data.id) tmp.json = data return tmp else if data.type = "Audio" ' User clicked on a song and wants it to play tmp = CreateObject("roSGNode", "MusicSongData") ' Try using song's parent for poster image tmp.image = PosterImage(data.ParentId, { "MaxWidth": 500, "MaxHeight": 500 }) ' Song's parent poster image is no good, try using the song's poster image if tmp.image = invalid tmp.image = PosterImage(data.id, { "MaxWidth": 500, "MaxHeight": 500 }) end if tmp.json = data return tmp else if data.type = "Recording" ' We know it's "Recording", but we don't do any special preprocessing ' for this data type at the moment, so just return the json. return data else print "Items.brs::ItemMetaData processed unhandled type: " data.type ' Return json if we don't know what it is return data end if end function ' Music Artist Data function ArtistOverview(name as string) req = createObject("roUrlTransfer") url = Substitute("Artists/{0}", req.escape(name)) resp = APIRequest(url) data = getJson(resp) if data = invalid then return invalid return data.overview end function ' Get list of albums belonging to an artist function MusicAlbumList(id as string) url = Substitute("Users/{0}/Items", m.global.session.user.id) resp = APIRequest(url, { "AlbumArtistIds": id, "includeitemtypes": "MusicAlbum", "sortBy": "SortName", "Recursive": true }) data = getJson(resp) results = [] for each item in data.Items tmp = CreateObject("roSGNode", "MusicAlbumData") tmp.image = PosterImage(item.id) tmp.json = item results.push(tmp) end for data.Items = results return data end function ' Get list of albums an artist appears on function AppearsOnList(id as string) url = Substitute("Users/{0}/Items", m.global.session.user.id) resp = APIRequest(url, { "ContributingArtistIds": id, "ExcludeItemIds": id, "includeitemtypes": "MusicAlbum", "sortBy": "PremiereDate,ProductionYear,SortName", "SortOrder": "Descending", "Recursive": true }) data = getJson(resp) results = [] for each item in data.Items tmp = CreateObject("roSGNode", "MusicAlbumData") tmp.image = PosterImage(item.id) tmp.json = item results.push(tmp) end for data.Items = results return data end function ' Get list of songs belonging to an artist function GetSongsByArtist(id as string, params = {} as object) url = Substitute("Users/{0}/Items", m.global.session.user.id) paramArray = { "AlbumArtistIds": id, "includeitemtypes": "Audio", "sortBy": "SortName", "Recursive": true } ' overwrite defaults with the params provided for each param in params paramArray.AddReplace(param, params[param]) end for resp = APIRequest(url, paramArray) data = getJson(resp) results = [] if data = invalid then return invalid if data.Items = invalid then return invalid if data.Items.Count() = 0 then return invalid for each item in data.Items tmp = CreateObject("roSGNode", "MusicAlbumData") tmp.image = PosterImage(item.id) tmp.json = item results.push(tmp) end for data.Items = results return data end function ' Get Items that are under the provided item function PlaylistItemList(id as string) url = Substitute("Playlists/{0}/Items", id) resp = APIRequest(url, { "UserId": m.global.session.user.id }) results = [] data = getJson(resp) if data = invalid then return invalid if data.Items = invalid then return invalid if data.Items.Count() = 0 then return invalid for each item in data.Items tmp = CreateObject("roSGNode", "PlaylistData") tmp.image = PosterImage(item.id) tmp.json = item results.push(tmp) end for data.Items = results return data end function ' Get Songs that are on an Album function MusicSongList(id as string) url = Substitute("Users/{0}/Items", m.global.session.user.id, id) resp = APIRequest(url, { "UserId": m.global.session.user.id, "parentId": id, "includeitemtypes": "Audio", "sortBy": "SortName" }) results = [] data = getJson(resp) if data = invalid then return invalid if data.Items = invalid then return invalid if data.Items.Count() = 0 then return invalid for each item in data.Items tmp = CreateObject("roSGNode", "MusicSongData") tmp.image = PosterImage(item.id) tmp.json = item results.push(tmp) end for data.Items = results return data end function ' Get Songs that are on an Album function AudioItem(id as string) url = Substitute("Users/{0}/Items/{1}", m.global.session.user.id, id) resp = APIRequest(url, { "UserId": m.global.session.user.id, "includeitemtypes": "Audio", "sortBy": "SortName" }) return getJson(resp) end function ' Get Instant Mix based on item function CreateInstantMix(id as string) url = Substitute("/Items/{0}/InstantMix", id) resp = APIRequest(url, { "UserId": m.global.session.user.id, "Limit": 201 }) return getJson(resp) end function ' Get Instant Mix based on item function CreateArtistMix(id as string) url = Substitute("Users/{0}/Items", m.global.session.user.id) resp = APIRequest(url, { "ArtistIds": id, "Recursive": "true", "MediaTypes": "Audio", "Filters": "IsNotFolder", "SortBy": "SortName", "Limit": 300, "Fields": "Chapters", "ExcludeLocationTypes": "Virtual", "EnableTotalRecordCount": false, "CollapseBoxSetItems": false }) return getJson(resp) end function ' Get Intro Videos for an item function GetIntroVideos(id as string) url = Substitute("Users/{0}/Items/{1}/Intros", m.global.session.user.id, id) resp = APIRequest(url, { "UserId": m.global.session.user.id }) return getJson(resp) end function function AudioStream(id as string) songData = AudioItem(id) if songData <> invalid content = createObject("RoSGNode", "ContentNode") if songData.title <> invalid content.title = songData.title end if playbackInfo = ItemPostPlaybackInfo(songData.id, songData.mediaSources[0].id) if playbackInfo <> invalid content.id = playbackInfo.PlaySessionId if useTranscodeAudioStream(playbackInfo) ' Transcode the audio content.url = buildURL(playbackInfo.mediaSources[0].TranscodingURL) else ' Direct Stream the audio params = { "Static": "true", "Container": songData.mediaSources[0].container, "MediaSourceId": songData.mediaSources[0].id } content.streamformat = songData.mediaSources[0].container content.url = buildURL(Substitute("Audio/{0}/stream", songData.id), params) end if else return invalid end if return content else return invalid end if end function function useTranscodeAudioStream(playbackInfo) return playbackInfo.mediaSources[0] <> invalid and playbackInfo.mediaSources[0].TranscodingURL <> invalid end function function BackdropImage(id as string) imgParams = { "maxHeight": "720", "maxWidth": "1280" } return ImageURL(id, "Backdrop", imgParams) end function ' Seasons for a TV Show function TVSeasons(id as string) as dynamic url = Substitute("Shows/{0}/Seasons", id) resp = APIRequest(url, { "UserId": m.global.session.user.id }) data = getJson(resp) ' validate data if data = invalid or data.Items = invalid then return invalid results = [] for each item in data.Items imgParams = { "AddPlayedIndicator": item.UserData.Played } tmp = CreateObject("roSGNode", "TVSeasonData") tmp.image = PosterImage(item.id, imgParams) tmp.json = item results.push(tmp) end for data.Items = results return data end function ' Returns a list of TV Shows for a given TV Show and season ' Accepts strings for the TV Show Id and the season Id function TVEpisodes(showId as string, seasonId as string) as dynamic ' Get and validate data data = api.shows.GetEpisodes(showId, { "seasonId": seasonId, "UserId": m.global.session.user.id, "fields": "MediaStreams,MediaSources" }) if data = invalid or data.Items = invalid then return invalid results = [] for each item in data.Items tmp = CreateObject("roSGNode", "TVEpisodeData") tmp.image = PosterImage(item.id, { "maxWidth": 400, "maxheight": 250 }) if isValid(tmp.image) tmp.image.posterDisplayMode = "scaleToZoom" end if tmp.json = item tmpMetaData = ItemMetaData(item.id) ' validate meta data if isValid(tmpMetaData) and isValid(tmpMetaData.overview) tmp.overview = tmpMetaData.overview end if results.push(tmp) end for data.Items = results return data end function ' Returns a list of extra features for a TV Show season ' Accepts a string that is a TV Show season id function TVSeasonExtras(seasonId as string) as dynamic ' Get and validate TV extra features data data = api.users.GetSpecialFeatures(m.global.session.user.id, seasonId) if not isValid(data) then return invalid results = [] for each item in data tmp = CreateObject("roSGNode", "TVEpisodeData") tmp.image = PosterImage(item.id, { "maxWidth": 400, "maxheight": 250 }) if isValid(tmp.image) tmp.image.posterDisplayMode = "scaleToZoom" end if tmp.json = item ' Force item type to Video so episode auto queue is not attempted tmp.type = "Video" tmpMetaData = ItemMetaData(item.id) ' Validate meta data if isValid(tmpMetaData) and isValid(tmpMetaData.overview) tmp.overview = tmpMetaData.overview end if results.push(tmp) end for ' Build that data format that the TVEpisodeRow expects return { Items: results } end function function TVEpisodeShuffleList(show_id as string) url = Substitute("Shows/{0}/Episodes", show_id) resp = APIRequest(url, { "UserId": m.global.session.user.id, "Limit": 200, "sortBy": "Random" }) data = getJson(resp) results = [] for each item in data.Items tmp = CreateObject("roSGNode", "TVEpisodeData") tmp.json = item results.push(tmp) end for data.Items = results return data end function