2023-10-17 01:33:06 +00:00
import "pkg:/source/api/sdk.bs"
2021-06-26 10:04:32 +00:00
function ItemGetPlaybackInfo(id as string, startTimeTicks = 0 as longinteger)
2021-07-09 20:08:32 +00:00
params = {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id,
2021-07-09 20:08:32 +00:00
"StartTimeTicks": startTimeTicks,
"IsPlayback": true,
"AutoOpenLiveStream": true,
"MaxStreamingBitrate": "140000000"
}
resp = APIRequest(Substitute("Items/{0}/PlaybackInfo", id), params)
return getJson(resp)
2020-05-31 13:46:33 +00:00
end function
2021-07-09 20:08:32 +00:00
function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioTrackIndex = -1 as integer, subtitleTrackIndex = -1 as integer, startTimeTicks = 0 as longinteger)
body = {
"DeviceProfile": getDeviceProfile()
}
params = {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id,
2021-07-09 20:08:32 +00:00
"StartTimeTicks": startTimeTicks,
"IsPlayback": true,
"AutoOpenLiveStream": true,
"MaxStreamingBitrate": "140000000",
"MaxStaticBitrate": "140000000",
"SubtitleStreamIndex": subtitleTrackIndex
}
2021-06-12 15:03:47 +00:00
2021-07-09 20:08:32 +00:00
if mediaSourceId <> "" then params.MediaSourceId = mediaSourceId
2021-06-12 15:03:47 +00:00
2021-07-09 20:08:32 +00:00
if audioTrackIndex > -1 then params.AudioStreamIndex = audioTrackIndex
2021-06-12 15:03:47 +00:00
2021-07-09 20:08:32 +00:00
req = APIRequest(Substitute("Items/{0}/PlaybackInfo", id), params)
req.SetRequest("POST")
return postJson(req, FormatJson(body))
2020-02-15 01:47:06 +00:00
end function
2019-04-20 22:28:28 +00:00
2019-03-19 02:59:23 +00:00
' Search across all libraries
2022-09-05 06:50:13 +00:00
function searchMedia(query as string)
if query <> ""
2023-10-28 22:59:28 +00:00
data = api.users.GetItemsByQuery(m.global.session.user.id, {
2022-09-05 06:50:13 +00:00
"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 = []
2023-10-27 03:38:14 +00:00
for each item in data.Items
2022-09-05 06:50:13 +00:00
tmp = CreateObject("roSGNode", "SearchData")
tmp.image = PosterImage(item.id)
tmp.json = item
results.push(tmp)
end for
2023-10-27 03:38:14 +00:00
data.Items = results
2022-09-05 06:50:13 +00:00
return data
end if
return []
2019-03-19 02:59:23 +00:00
end function
' MetaData about an item
2019-10-11 19:01:46 +00:00
function ItemMetaData(id as string)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items/{1}", m.global.session.user.id, id)
2023-10-30 01:21:57 +00:00
resp = APIRequest(url, { "fields": "Chapters" })
2021-07-09 20:08:32 +00:00
data = getJson(resp)
if data = invalid then return invalid
2023-02-25 16:43:36 +00:00
2021-07-09 20:08:32 +00:00
imgParams = {}
2022-06-04 01:30:20 +00:00
if data.type <> "Audio"
2023-03-09 19:35:29 +00:00
if data.UserData <> invalid and data.UserData.PlayedPercentage <> invalid
2022-06-04 01:30:20 +00:00
param = { "PercentPlayed": data.UserData.PlayedPercentage }
imgParams.Append(param)
end if
2021-07-09 20:08:32 +00:00
end if
2022-09-21 19:36:38 +00:00
if data.type = "Movie" or data.type = "MusicVideo"
2021-07-09 20:08:32 +00:00
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"
tmp = CreateObject("roSGNode", "TVEpisodeData")
tmp.image = PosterImage(data.id, imgParams)
tmp.json = data
return tmp
2022-09-05 06:50:13 +00:00
else if data.type = "BoxSet" or data.type = "Playlist"
2021-07-09 20:08:32 +00:00
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
2022-09-03 07:31:15 +00:00
else if data.type = "Trailer"
tmp = CreateObject("roSGNode", "VideoData")
tmp.json = data
return tmp
2021-12-18 06:03:33 +00:00
else if data.type = "TvChannel" or data.type = "Program"
2021-07-09 20:08:32 +00:00
tmp = CreateObject("roSGNode", "ChannelData")
tmp.image = PosterImage(data.id)
2022-05-14 10:13:28 +00:00
tmp.isFavorite = data.UserData.isFavorite
2021-07-09 20:08:32 +00:00
tmp.json = data
return tmp
2022-03-13 08:46:03 +00:00
else if data.type = "Person"
tmp = CreateObject("roSGNode", "PersonData")
tmp.image = PosterImage(data.id, { "MaxWidth": 300, "MaxHeight": 450 })
tmp.json = data
return tmp
2022-05-14 02:35:50 +00:00
else if data.type = "MusicArtist"
2022-05-14 21:14:15 +00:00
' User clicked on an artist and wants to see the list of their albums
2022-05-14 02:35:50 +00:00
tmp = CreateObject("roSGNode", "MusicArtistData")
tmp.image = PosterImage(data.id)
tmp.json = data
return tmp
2022-05-14 15:47:44 +00:00
else if data.type = "MusicAlbum"
2022-05-14 21:14:15 +00:00
' User clicked on an album and wants to see the list of songs
tmp = CreateObject("roSGNode", "MusicAlbumSongListData")
2022-05-14 15:47:44 +00:00
tmp.image = PosterImage(data.id)
tmp.json = data
return tmp
else if data.type = "Audio"
2022-05-15 02:30:29 +00:00
' User clicked on a song and wants it to play
tmp = CreateObject("roSGNode", "MusicSongData")
2022-05-27 02:04:59 +00:00
2022-05-27 01:23:59 +00:00
' Try using song's parent for poster image
2022-12-11 20:25:42 +00:00
tmp.image = PosterImage(data.ParentId, { "MaxWidth": 500, "MaxHeight": 500 })
2022-05-27 01:23:59 +00:00
' Song's parent poster image is no good, try using the song's poster image
if tmp.image = invalid
2022-12-11 20:25:42 +00:00
tmp.image = PosterImage(data.id, { "MaxWidth": 500, "MaxHeight": 500 })
2022-05-27 01:23:59 +00:00
end if
2022-05-15 02:30:29 +00:00
tmp.json = data
return tmp
2023-01-05 13:31:48 +00:00
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
2021-07-09 20:08:32 +00:00
else
print "Items.brs::ItemMetaData processed unhandled type: " data.type
' Return json if we don't know what it is
return data
end if
2019-03-19 02:59:23 +00:00
end function
2022-09-27 01:26:17 +00:00
' 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
2022-05-14 21:14:15 +00:00
' Get list of albums belonging to an artist
function MusicAlbumList(id as string)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items", m.global.session.user.id)
2022-05-14 23:25:17 +00:00
resp = APIRequest(url, {
2022-09-27 01:26:17 +00:00
"AlbumArtistIds": id,
2022-05-14 03:46:05 +00:00
"includeitemtypes": "MusicAlbum",
2022-09-27 01:26:17 +00:00
"sortBy": "SortName",
"Recursive": true
2022-05-14 03:46:05 +00:00
})
data = getJson(resp)
results = []
for each item in data.Items
2022-05-14 21:14:15 +00:00
tmp = CreateObject("roSGNode", "MusicAlbumData")
2022-09-27 01:26:17 +00:00
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)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items", m.global.session.user.id)
2022-09-27 01:26:17 +00:00
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)
2022-05-14 03:46:05 +00:00
tmp.json = item
results.push(tmp)
end for
data.Items = results
return data
end function
2022-10-02 18:23:42 +00:00
' Get list of songs belonging to an artist
2023-09-16 21:18:03 +00:00
function GetSongsByArtist(id as string, params = {} as object)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items", m.global.session.user.id)
2023-09-16 21:18:03 +00:00
paramArray = {
2022-10-02 18:23:42 +00:00
"AlbumArtistIds": id,
"includeitemtypes": "Audio",
"sortBy": "SortName",
"Recursive": true
2023-09-16 21:18:03 +00:00
}
' overwrite defaults with the params provided
for each param in params
paramArray.AddReplace(param, params[param])
end for
2022-10-02 18:23:42 +00:00
2023-09-16 21:18:03 +00:00
resp = APIRequest(url, paramArray)
2022-10-02 18:23:42 +00:00
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
2023-02-25 16:43:36 +00:00
' Get Items that are under the provided item
function PlaylistItemList(id as string)
url = Substitute("Playlists/{0}/Items", id)
resp = APIRequest(url, {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id
2023-02-25 16:43:36 +00:00
})
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
2022-05-14 21:14:15 +00:00
' Get Songs that are on an Album
function MusicSongList(id as string)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items", m.global.session.user.id, id)
2022-05-14 23:25:17 +00:00
resp = APIRequest(url, {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id,
2022-05-14 03:46:05 +00:00
"parentId": id,
2022-05-31 20:27:26 +00:00
"includeitemtypes": "Audio",
2022-05-14 03:46:05 +00:00
"sortBy": "SortName"
2022-05-14 02:35:50 +00:00
})
results = []
2022-10-02 18:23:42 +00:00
data = getJson(resp)
if data = invalid then return invalid
if data.Items = invalid then return invalid
if data.Items.Count() = 0 then return invalid
2022-05-14 02:35:50 +00:00
for each item in data.Items
2022-05-14 21:14:15 +00:00
tmp = CreateObject("roSGNode", "MusicSongData")
2022-05-14 02:35:50 +00:00
tmp.image = PosterImage(item.id)
tmp.json = item
results.push(tmp)
end for
data.Items = results
return data
end function
2022-05-15 02:30:29 +00:00
' Get Songs that are on an Album
function AudioItem(id as string)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items/{1}", m.global.session.user.id, id)
2022-05-15 02:30:29 +00:00
resp = APIRequest(url, {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id,
2022-05-31 20:27:26 +00:00
"includeitemtypes": "Audio",
2022-05-15 02:30:29 +00:00
"sortBy": "SortName"
})
2022-05-29 18:41:06 +00:00
return getJson(resp)
end function
2022-05-21 15:45:38 +00:00
2022-06-08 13:08:05 +00:00
' Get Instant Mix based on item
function CreateInstantMix(id as string)
url = Substitute("/Items/{0}/InstantMix", id)
resp = APIRequest(url, {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id,
2022-06-08 13:08:05 +00:00
"Limit": 201
})
return getJson(resp)
end function
2022-07-19 02:28:06 +00:00
' Get Instant Mix based on item
function CreateArtistMix(id as string)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items", m.global.session.user.id)
2022-07-19 02:28:06 +00:00
resp = APIRequest(url, {
2022-09-27 01:26:17 +00:00
"ArtistIds": id,
"Recursive": "true",
"MediaTypes": "Audio",
2022-07-19 02:28:06 +00:00
"Filters": "IsNotFolder",
"SortBy": "SortName",
2022-09-27 01:26:17 +00:00
"Limit": 300,
"Fields": "Chapters",
"ExcludeLocationTypes": "Virtual",
"EnableTotalRecordCount": false,
"CollapseBoxSetItems": false
2022-07-19 02:28:06 +00:00
})
return getJson(resp)
end function
2022-07-09 08:28:15 +00:00
' Get Intro Videos for an item
function GetIntroVideos(id as string)
2023-06-01 12:43:27 +00:00
url = Substitute("Users/{0}/Items/{1}/Intros", m.global.session.user.id, id)
2022-07-09 08:28:15 +00:00
resp = APIRequest(url, {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id
2022-07-09 08:28:15 +00:00
})
return getJson(resp)
end function
2022-05-29 18:41:06 +00:00
function AudioStream(id as string)
songData = AudioItem(id)
2023-03-04 18:46:36 +00:00
if songData <> invalid
content = createObject("RoSGNode", "ContentNode")
if songData.title <> invalid
content.title = songData.title
end if
2022-05-29 18:41:06 +00:00
2023-03-04 18:46:36 +00:00
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
2022-11-10 01:56:12 +00:00
2023-03-04 18:46:36 +00:00
return content
2022-11-10 14:42:29 +00:00
else
2023-03-04 18:46:36 +00:00
return invalid
2022-11-10 01:56:12 +00:00
end if
2022-05-29 18:41:06 +00:00
end function
2022-11-10 14:42:29 +00:00
function useTranscodeAudioStream(playbackInfo)
return playbackInfo.mediaSources[0] <> invalid and playbackInfo.mediaSources[0].TranscodingURL <> invalid
end function
2022-05-29 18:41:06 +00:00
function BackdropImage(id as string)
imgParams = { "maxHeight": "720", "maxWidth": "1280" }
return ImageURL(id, "Backdrop", imgParams)
2022-05-15 02:30:29 +00:00
end function
2019-03-19 02:59:23 +00:00
' Seasons for a TV Show
2023-04-19 19:19:45 +00:00
function TVSeasons(id as string) as dynamic
2021-07-09 20:08:32 +00:00
url = Substitute("Shows/{0}/Seasons", id)
2023-06-01 12:43:27 +00:00
resp = APIRequest(url, { "UserId": m.global.session.user.id })
2021-07-09 20:08:32 +00:00
data = getJson(resp)
2023-04-19 19:19:45 +00:00
' validate data
2023-04-23 17:38:30 +00:00
if data = invalid or data.Items = invalid then return invalid
2023-04-19 19:19:45 +00:00
2021-07-09 20:08:32 +00:00
results = []
for each item in data.Items
2023-09-16 21:18:03 +00:00
tmp = CreateObject("roSGNode", "TVSeasonData")
2023-12-03 01:04:14 +00:00
tmp.image = PosterImage(item.id)
2021-07-09 20:08:32 +00:00
tmp.json = item
results.push(tmp)
end for
data.Items = results
return data
2019-03-19 02:59:23 +00:00
end function
2023-10-17 01:33:06 +00:00
' 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" })
2023-04-23 17:38:30 +00:00
if data = invalid or data.Items = invalid then return invalid
2023-04-19 23:19:37 +00:00
2021-07-09 20:08:32 +00:00
results = []
for each item in data.Items
tmp = CreateObject("roSGNode", "TVEpisodeData")
2023-10-17 01:33:06 +00:00
tmp.image = PosterImage(item.id, { "maxWidth": 400, "maxheight": 250 })
if isValid(tmp.image)
2022-06-09 20:51:04 +00:00
tmp.image.posterDisplayMode = "scaleToZoom"
2021-07-09 20:08:32 +00:00
end if
tmp.json = item
2023-04-19 23:19:37 +00:00
tmpMetaData = ItemMetaData(item.id)
2023-10-17 01:33:06 +00:00
2023-04-19 23:19:37 +00:00
' validate meta data
2023-10-17 01:33:06 +00:00
if isValid(tmpMetaData) and isValid(tmpMetaData.overview)
2023-04-19 23:19:37 +00:00
tmp.overview = tmpMetaData.overview
end if
2021-07-09 20:08:32 +00:00
results.push(tmp)
end for
data.Items = results
return data
2019-04-14 04:47:27 +00:00
end function
2023-02-25 16:43:36 +00:00
2023-10-17 01:33:06 +00:00
' 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
2023-10-15 02:21:08 +00:00
results = []
for each item in data
tmp = CreateObject("roSGNode", "TVEpisodeData")
2023-10-17 01:33:06 +00:00
tmp.image = PosterImage(item.id, { "maxWidth": 400, "maxheight": 250 })
if isValid(tmp.image)
2023-10-15 02:21:08 +00:00
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
2023-10-17 01:33:06 +00:00
if isValid(tmpMetaData) and isValid(tmpMetaData.overview)
2023-10-15 02:21:08 +00:00
tmp.overview = tmpMetaData.overview
end if
results.push(tmp)
end for
' Build that data format that the TVEpisodeRow expects
2023-10-17 01:35:10 +00:00
return { Items: results }
2023-10-15 02:21:08 +00:00
end function
2023-02-25 16:43:36 +00:00
function TVEpisodeShuffleList(show_id as string)
url = Substitute("Shows/{0}/Episodes", show_id)
resp = APIRequest(url, {
2023-06-01 12:43:27 +00:00
"UserId": m.global.session.user.id,
2023-02-25 16:43:36 +00:00
"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