1117 lines
43 KiB
HTML
1117 lines
43 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width">
|
|
<title>jellyfin-roku api docs Source: components/ItemGrid/LoadVideoContentTask.bs</title>
|
|
|
|
<!--[if lt IE 9]>
|
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
<![endif]-->
|
|
<link type="text/css" rel="stylesheet" href="styles/sunlight.dark.css">
|
|
|
|
<link type="text/css" rel="stylesheet" href="styles/site.darkly.css">
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div class="navbar navbar-default navbar-fixed-top ">
|
|
<div class="container">
|
|
<div class="navbar-header">
|
|
<a class="navbar-brand" href="index.html">jellyfin-roku api docs</a>
|
|
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#topNavigation">
|
|
<span class="icon-bar"></span>
|
|
<span class="icon-bar"></span>
|
|
<span class="icon-bar"></span>
|
|
</button>
|
|
</div>
|
|
<div class="navbar-collapse collapse" id="topNavigation">
|
|
<ul class="nav navbar-nav">
|
|
|
|
<li class="dropdown">
|
|
<a href="modules.list.html" class="dropdown-toggle" data-toggle="dropdown">Modules<b class="caret"></b></a>
|
|
<ul class="dropdown-menu ">
|
|
<li><a href="module-AlbumData.html">AlbumData</a></li><li><a href="module-AlbumGrid.html">AlbumGrid</a></li><li><a href="module-AlbumTrackList.html">AlbumTrackList</a></li><li><a href="module-AlbumView.html">AlbumView</a></li><li><a href="module-Alpha.html">Alpha</a></li><li><a href="module-ArtistView.html">ArtistView</a></li><li><a href="module-AudioPlayer.html">AudioPlayer</a></li><li><a href="module-AudioPlayerView.html">AudioPlayerView</a></li><li><a href="module-AudioTrackListItem.html">AudioTrackListItem</a></li><li><a href="module-ButtonGroupHoriz.html">ButtonGroupHoriz</a></li><li><a href="module-ButtonGroupVert.html">ButtonGroupVert</a></li><li><a href="module-ChannelData.html">ChannelData</a></li><li><a href="module-Clock.html">Clock</a></li><li><a href="module-CollectionData.html">CollectionData</a></li><li><a href="module-ConfigData.html">ConfigData</a></li><li><a href="module-ConfigItem.html">ConfigItem</a></li><li><a href="module-ConfigList.html">ConfigList</a></li><li><a href="module-ExtrasItem.html">ExtrasItem</a></li><li><a href="module-ExtrasRowList.html">ExtrasRowList</a></li><li><a href="module-FavoriteItemsTask.html">FavoriteItemsTask</a></li><li><a href="module-FolderData.html">FolderData</a></li><li><a href="module-GetFiltersTask.html">GetFiltersTask</a></li><li><a href="module-GetNextEpisodeTask.html">GetNextEpisodeTask</a></li><li><a href="module-GetPlaybackInfoTask.html">GetPlaybackInfoTask</a></li><li><a href="module-GetShuffleEpisodesTask.html">GetShuffleEpisodesTask</a></li><li><a href="module-GridItem.html">GridItem</a></li><li><a href="module-GridItemSmall.html">GridItemSmall</a></li><li><a href="module-Home.html">Home</a></li><li><a href="module-HomeData.html">HomeData</a></li><li><a href="module-HomeItem.html">HomeItem</a></li><li><a href="module-HomeRows.html">HomeRows</a></li><li><a href="module-IconButton.html">IconButton</a></li><li><a href="module-Image.html">Image</a></li><li><a href="module-ImageData.html">ImageData</a></li><li><a href="module-IntegerKeyboard.html">IntegerKeyboard</a></li><li><a href="module-ItemGrid.html">ItemGrid</a></li><li><a href="module-ItemGridOptions.html">ItemGridOptions</a></li><li><a href="module-Items.html">Items</a></li><li><a href="module-JFButton.html">JFButton</a></li><li><a href="module-JFButtons.html">JFButtons</a></li><li><a href="module-JFGroup.html">JFGroup</a></li><li><a href="module-JFMessageDialog.html">JFMessageDialog</a></li><li><a href="module-JFOverhang.html">JFOverhang</a></li><li><a href="module-JFScene.html">JFScene</a></li><li><a href="module-JFScreen.html">JFScreen</a></li><li><a href="module-JFServer.html">JFServer</a></li><li><a href="module-JFVideo.html">JFVideo</a></li><li><a href="module-ListPoster.html">ListPoster</a></li><li><a href="module-LoadChannelsTask.html">LoadChannelsTask</a></li><li><a href="module-LoadItemsTask.html">LoadItemsTask</a></li><li><a href="module-LoadItemsTask2.html">LoadItemsTask2</a></li><li><a href="module-LoadPhotoTask.html">LoadPhotoTask</a></li><li><a href="module-LoadProgramDetailsTask.html">LoadProgramDetailsTask</a></li><li><a href="module-LoadScreenSaverTimeoutTask.html">LoadScreenSaverTimeoutTask</a></li><li><a href="module-LoadSheduleTask.html">LoadSheduleTask</a></li><li><a href="module-LoadVideoContentTask.html">LoadVideoContentTask</a></li><li><a href="module-LoginScene.html">LoginScene</a></li><li><a href="module-Main.html">Main</a></li><li><a href="module-MovieData.html">MovieData</a></li><li><a href="module-MovieDetails.html">MovieDetails</a></li><li><a href="module-MovieLibraryView.html">MovieLibraryView</a></li><li><a href="module-MovieOptions.html">MovieOptions</a></li><li><a href="module-MusicAlbumData.html">MusicAlbumData</a></li><li><a href="module-MusicAlbumSongListData.html">MusicAlbumSongListData</a></li><li><a href="module-MusicArtistData.html">MusicArtistData</a></li><li><a href="module-MusicArtistGridItem.html">MusicArtistGridItem</a></li><li><a href="module-MusicLibraryView.html">MusicLibraryView</a></li><li><a href="module-MusicSongData.html">MusicSongData</a></li><li><a href="module-OptionNode.html">OptionNode</a></li><li><a href="module-OptionsButton.html">OptionsButton</a></li><li><a href="module-OptionsData.html">OptionsData</a></li><li><a href="module-OptionsSlider.html">OptionsSlider</a></li><li><a href="module-OverviewDialog.html">OverviewDialog</a></li><li><a href="module-PauseMenu.html">PauseMenu</a></li><li><a href="module-PersonData.html">PersonData</a></li><li><a href="module-PersonDetails.html">PersonDetails</a></li><li><a href="module-PhotoData.html">PhotoData</a></li><li><a href="module-PhotoDetails.html">PhotoDetails</a></li><li><a href="module-PlaybackDialog.html">PlaybackDialog</a></li><li><a href="module-PlayedCheckmark.html">PlayedCheckmark</a></li><li><a href="module-PlaylistData.html">PlaylistData</a></li><li><a href="module-PlaylistView.html">PlaylistView</a></li><li><a href="module-PlaystateTask.html">PlaystateTask</a></li><li><a href="module-PostTask.html">PostTask</a></li><li><a href="module-ProgramDetails.html">ProgramDetails</a></li><li><a href="module-PublicUserData.html">PublicUserData</a></li><li><a href="module-QueueManager.html">QueueManager</a></li><li><a href="module-QuickConnect.html">QuickConnect</a></li><li><a href="module-QuickConnectDialog.html">QuickConnectDialog</a></li><li><a href="module-RadioDialog.html">RadioDialog</a></li><li><a href="module-RecordProgramTask.html">RecordProgramTask</a></li><li><a href="module-SceneManager.html">SceneManager</a></li><li><a href="module-ScheduleProgramData.html">ScheduleProgramData</a></li><li><a href="module-SearchBox.html">SearchBox</a></li><li><a href="module-SearchData.html">SearchData</a></li><li><a href="module-SearchResults.html">SearchResults</a></li><li><a href="module-SearchRow.html">SearchRow</a></li><li><a href="module-SearchTask.html">SearchTask</a></li><li><a href="module-SeriesData.html">SeriesData</a></li><li><a href="module-ServerDiscoveryTask.html">ServerDiscoveryTask</a></li><li><a href="module-SetServerScreen.html">SetServerScreen</a></li><li><a href="module-ShowScenes.html">ShowScenes</a></li><li><a href="module-SlideOutButton.html">SlideOutButton</a></li><li><a href="module-SongItem.html">SongItem</a></li><li><a href="module-Spinner.html">Spinner</a></li><li><a href="module-StandardDialog.html">StandardDialog</a></li><li><a href="module-Subtitles.html">Subtitles</a></li><li><a href="module-TVEpisode.html">TVEpisode</a></li><li><a href="module-TVEpisodeData.html">TVEpisodeData</a></li><li><a href="module-TVEpisodeRow.html">TVEpisodeRow</a></li><li><a href="module-TVEpisodeRowWithOptions.html">TVEpisodeRowWithOptions</a></li><li><a href="module-TVEpisodes.html">TVEpisodes</a></li><li><a href="module-TVListDetails.html">TVListDetails</a></li><li><a href="module-TVListOptions.html">TVListOptions</a></li><li><a href="module-TVSeasonData.html">TVSeasonData</a></li><li><a href="module-TVSeasonRow.html">TVSeasonRow</a></li><li><a href="module-TVShowDescription.html">TVShowDescription</a></li><li><a href="module-TVShowDetails.html">TVShowDetails</a></li><li><a href="module-TextSizeTask.html">TextSizeTask</a></li><li><a href="module-UserData.html">UserData</a></li><li><a href="module-UserItem.html">UserItem</a></li><li><a href="module-UserLibrary.html">UserLibrary</a></li><li><a href="module-UserRow.html">UserRow</a></li><li><a href="module-UserSelect.html">UserSelect</a></li><li><a href="module-VideoData.html">VideoData</a></li><li><a href="module-VideoPlayer.html">VideoPlayer</a></li><li><a href="module-VideoPlayerView.html">VideoPlayerView</a></li><li><a href="module-VideoTrackListItem.html">VideoTrackListItem</a></li><li><a href="module-ViewCreator.html">ViewCreator</a></li><li><a href="module-WhatsNewDialog.html">WhatsNewDialog</a></li><li><a href="module-baserequest.html">baserequest</a></li><li><a href="module-captionTask.html">captionTask</a></li><li><a href="module-config.html">config</a></li><li><a href="module-deviceCapabilities.html">deviceCapabilities</a></li><li><a href="module-globals.html">globals</a></li><li><a href="module-migrations.html">migrations</a></li><li><a href="module-misc.html">misc</a></li><li><a href="module-quickplay.html">quickplay</a></li><li><a href="module-schedule.html">schedule</a></li><li><a href="module-section.html">section</a></li><li><a href="module-sectionScroller.html">sectionScroller</a></li><li><a href="module-settings.html">settings</a></li><li><a href="module-userauth.html">userauth</a></li>
|
|
</ul>
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
<div class="col-sm-3 col-md-3">
|
|
<form class="navbar-form" role="search">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" placeholder="Search" name="q" id="search-input">
|
|
<div class="input-group-btn">
|
|
<button class="btn btn-default" id="search-submit"><i class="glyphicon glyphicon-search"></i></button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="container" id="toc-content">
|
|
<div class="row">
|
|
|
|
|
|
<div class="col-md-12">
|
|
|
|
<div id="main">
|
|
|
|
|
|
<h1 class="page-title">Source: components/ItemGrid/LoadVideoContentTask.bs</h1>
|
|
|
|
<section>
|
|
<article>
|
|
<pre
|
|
class="sunlight-highlight-javascript linenums">import "pkg:/source/utils/misc.bs"
|
|
import "pkg:/source/api/Items.bs"
|
|
import "pkg:/source/api/UserLibrary.bs"
|
|
import "pkg:/source/api/baserequest.bs"
|
|
import "pkg:/source/utils/config.bs"
|
|
import "pkg:/source/api/Image.bs"
|
|
import "pkg:/source/api/userauth.bs"
|
|
import "pkg:/source/utils/deviceCapabilities.bs"
|
|
|
|
sub init()
|
|
m.user = AboutMe()
|
|
m.top.functionName = "loadItems"
|
|
end sub
|
|
|
|
sub loadItems()
|
|
' Reset intro tracker in case task gets reused
|
|
m.top.isIntro = false
|
|
|
|
' Only show preroll once per queue
|
|
if m.global.queueManager.callFunc("isPrerollActive")
|
|
' Prerolls not allowed if we're resuming video
|
|
if m.global.queueManager.callFunc("getCurrentItem").startingPoint = 0
|
|
preRoll = GetIntroVideos(m.top.itemId)
|
|
if isValid(preRoll) and preRoll.TotalRecordCount > 0 and isValid(preRoll.items[0])
|
|
' If an error is thrown in the Intros plugin, instead of passing the error they pass the entire rick roll music video.
|
|
' Bypass the music video and treat it as an error message
|
|
if lcase(preRoll.items[0].name) <> "rick roll'd"
|
|
m.global.queueManager.callFunc("push", m.global.queueManager.callFunc("getCurrentItem"))
|
|
m.top.itemId = preRoll.items[0].id
|
|
m.global.queueManager.callFunc("setPrerollStatus", false)
|
|
m.top.isIntro = true
|
|
end if
|
|
end if
|
|
end if
|
|
end if
|
|
|
|
if m.top.selectedAudioStreamIndex = 0
|
|
currentItem = m.global.queueManager.callFunc("getCurrentItem")
|
|
if isValid(currentItem) and isValid(currentItem.json)
|
|
m.top.selectedAudioStreamIndex = FindPreferredAudioStream(currentItem.json.MediaStreams)
|
|
end if
|
|
end if
|
|
|
|
id = m.top.itemId
|
|
mediaSourceId = invalid
|
|
audio_stream_idx = m.top.selectedAudioStreamIndex
|
|
subtitle_idx = m.top.selectedSubtitleIndex
|
|
forceTranscoding = false
|
|
|
|
m.top.content = [LoadItems_VideoPlayer(id, mediaSourceId, audio_stream_idx, subtitle_idx, forceTranscoding)]
|
|
end sub
|
|
|
|
function LoadItems_VideoPlayer(id as string, mediaSourceId = invalid as dynamic, audio_stream_idx = 1 as integer, subtitle_idx = -1 as integer, forceTranscoding = false as boolean) as dynamic
|
|
|
|
video = {}
|
|
video.id = id
|
|
video.content = createObject("RoSGNode", "ContentNode")
|
|
|
|
LoadItems_AddVideoContent(video, mediaSourceId, audio_stream_idx, subtitle_idx, forceTranscoding)
|
|
|
|
if video.content = invalid
|
|
return invalid
|
|
end if
|
|
|
|
return video
|
|
end function
|
|
|
|
sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, subtitle_idx = -1 as integer, forceTranscoding = false as boolean)
|
|
|
|
meta = ItemMetaData(video.id)
|
|
|
|
if not isValid(meta)
|
|
video.errorMsg = "Error loading metadata"
|
|
video.content = invalid
|
|
return
|
|
end if
|
|
|
|
videotype = LCase(meta.type)
|
|
|
|
if videotype = "episode" or videotype = "series"
|
|
video.content.contenttype = "episode"
|
|
end if
|
|
|
|
video.chapters = meta.json.Chapters
|
|
video.content.title = meta.title
|
|
video.showID = meta.showID
|
|
|
|
user = AboutMe()
|
|
if user.Configuration.EnableNextEpisodeAutoPlay
|
|
if LCase(m.top.itemType) = "episode"
|
|
addNextEpisodesToQueue(video.showID)
|
|
end if
|
|
end if
|
|
|
|
playbackPosition = 0!
|
|
|
|
currentItem = m.global.queueManager.callFunc("getCurrentItem")
|
|
|
|
if isValid(currentItem) and isValid(currentItem.startingPoint)
|
|
playbackPosition = currentItem.startingPoint
|
|
end if
|
|
|
|
' PlayStart requires the time to be in seconds
|
|
video.content.PlayStart = int(playbackPosition / 10000000)
|
|
|
|
if not isValid(mediaSourceId) then mediaSourceId = video.id
|
|
if meta.live then mediaSourceId = ""
|
|
|
|
m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
|
|
video.videoId = video.id
|
|
video.mediaSourceId = mediaSourceId
|
|
video.audioIndex = audio_stream_idx
|
|
|
|
if not isValid(m.playbackInfo)
|
|
video.errorMsg = "Error loading playback info"
|
|
video.content = invalid
|
|
return
|
|
end if
|
|
|
|
video.PlaySessionId = m.playbackInfo.PlaySessionId
|
|
|
|
if meta.live
|
|
video.content.live = true
|
|
video.content.StreamFormat = "hls"
|
|
end if
|
|
|
|
video.container = getContainerType(meta)
|
|
|
|
if not isValid(m.playbackInfo.MediaSources[0])
|
|
m.playbackInfo = meta.json
|
|
end if
|
|
|
|
addSubtitlesToVideo(video, meta)
|
|
|
|
if meta.live
|
|
video.transcodeParams = {
|
|
"MediaSourceId": m.playbackInfo.MediaSources[0].Id,
|
|
"LiveStreamId": m.playbackInfo.MediaSources[0].LiveStreamId,
|
|
"PlaySessionId": video.PlaySessionId
|
|
}
|
|
end if
|
|
|
|
|
|
' 'TODO: allow user selection of subtitle track before playback initiated, for now set to no subtitles
|
|
video.directPlaySupported = m.playbackInfo.MediaSources[0].SupportsDirectPlay
|
|
fully_external = false
|
|
|
|
|
|
' For h264/hevc video, Roku spec states that it supports specfic encoding levels
|
|
' The device can decode content with a Higher Encoding level but may play it back with certain
|
|
' artifacts. If the user preference is set, and the only reason the server says we need to
|
|
' transcode is that the Encoding Level is not supported, then try to direct play but silently
|
|
' fall back to the transcode if that fails.
|
|
if m.playbackInfo.MediaSources[0].MediaStreams.Count() > 0 and meta.live = false
|
|
tryDirectPlay = m.global.session.user.settings["playback.tryDirect.h264ProfileLevel"] and m.playbackInfo.MediaSources[0].MediaStreams[0].codec = "h264"
|
|
tryDirectPlay = tryDirectPlay or (m.global.session.user.settings["playback.tryDirect.hevcProfileLevel"] and m.playbackInfo.MediaSources[0].MediaStreams[0].codec = "hevc")
|
|
if tryDirectPlay and isValid(m.playbackInfo.MediaSources[0].TranscodingUrl) and forceTranscoding = false
|
|
transcodingReasons = getTranscodeReasons(m.playbackInfo.MediaSources[0].TranscodingUrl)
|
|
if transcodingReasons.Count() = 1 and transcodingReasons[0] = "VideoLevelNotSupported"
|
|
video.directPlaySupported = true
|
|
video.transcodeAvailable = true
|
|
end if
|
|
end if
|
|
end if
|
|
|
|
if video.directPlaySupported
|
|
video.isTranscoded = false
|
|
addVideoContentURL(video, mediaSourceId, audio_stream_idx, fully_external)
|
|
else
|
|
if m.playbackInfo.MediaSources[0].TranscodingUrl = invalid
|
|
' If server does not provide a transcode URL, display a message to the user
|
|
m.global.sceneManager.callFunc("userMessage", tr("Error Getting Playback Information"), tr("An error was encountered while playing this item. Server did not provide required transcoding data."))
|
|
video.errorMsg = "Error getting playback information"
|
|
video.content = invalid
|
|
return
|
|
end if
|
|
' Get transcoding reason
|
|
video.transcodeReasons = getTranscodeReasons(m.playbackInfo.MediaSources[0].TranscodingUrl)
|
|
video.content.url = buildURL(m.playbackInfo.MediaSources[0].TranscodingUrl)
|
|
video.isTranscoded = true
|
|
end if
|
|
|
|
setCertificateAuthority(video.content)
|
|
video.audioTrack = (audio_stream_idx + 1).ToStr() ' Roku's track indexes count from 1. Our index is zero based
|
|
|
|
video.SelectedSubtitle = subtitle_idx
|
|
|
|
if not fully_external
|
|
video.content = authRequest(video.content)
|
|
end if
|
|
end sub
|
|
|
|
sub addVideoContentURL(video, mediaSourceId, audio_stream_idx, fully_external)
|
|
protocol = LCase(m.playbackInfo.MediaSources[0].Protocol)
|
|
if protocol <> "file"
|
|
uri = parseUrl(m.playbackInfo.MediaSources[0].Path)
|
|
if isLocalhost(uri[2])
|
|
' if the domain of the URI is local to the server,
|
|
' create a new URI by appending the received path to the server URL
|
|
' later we will substitute the users provided URL for this case
|
|
video.content.url = buildURL(uri[4])
|
|
else
|
|
fully_external = true
|
|
video.content.url = m.playbackInfo.MediaSources[0].Path
|
|
end if
|
|
else
|
|
params = {
|
|
"Static": "true",
|
|
"Container": video.container,
|
|
"PlaySessionId": video.PlaySessionId,
|
|
"AudioStreamIndex": audio_stream_idx
|
|
}
|
|
|
|
if mediaSourceId <> ""
|
|
params.MediaSourceId = mediaSourceId
|
|
end if
|
|
|
|
video.content.url = buildURL(Substitute("Videos/{0}/stream", video.id), params)
|
|
end if
|
|
end sub
|
|
|
|
sub addSubtitlesToVideo(video, meta)
|
|
subtitles = sortSubtitles(meta.id, m.playbackInfo.MediaSources[0].MediaStreams)
|
|
safesubs = subtitles["all"]
|
|
subtitleTracks = []
|
|
|
|
if m.global.session.user.settings["playback.subs.onlytext"] = true
|
|
safesubs = subtitles["text"]
|
|
end if
|
|
|
|
for each subtitle in safesubs
|
|
subtitleTracks.push(subtitle.track)
|
|
end for
|
|
|
|
video.content.SubtitleTracks = subtitleTracks
|
|
video.fullSubtitleData = safesubs
|
|
end sub
|
|
|
|
|
|
'
|
|
' Extract array of Transcode Reasons from the content URL
|
|
' @returns Array of Strings
|
|
function getTranscodeReasons(url as string) as object
|
|
|
|
regex = CreateObject("roRegex", "&TranscodeReasons=([^&]*)", "")
|
|
match = regex.Match(url)
|
|
|
|
if match.count() > 1
|
|
return match[1].Split(",")
|
|
end if
|
|
|
|
return []
|
|
end function
|
|
|
|
function directPlaySupported(meta as object) as boolean
|
|
devinfo = CreateObject("roDeviceInfo")
|
|
if isValid(meta.json.MediaSources[0]) and meta.json.MediaSources[0].SupportsDirectPlay = false
|
|
return false
|
|
end if
|
|
|
|
if meta.json.MediaStreams[0] = invalid
|
|
return false
|
|
end if
|
|
|
|
streamInfo = { Codec: meta.json.MediaStreams[0].codec }
|
|
if isValid(meta.json.MediaStreams[0].Profile) and meta.json.MediaStreams[0].Profile.len() > 0
|
|
streamInfo.Profile = LCase(meta.json.MediaStreams[0].Profile)
|
|
end if
|
|
if isValid(meta.json.MediaSources[0].container) and meta.json.MediaSources[0].container.len() > 0
|
|
'CanDecodeVideo() requires the .container to be format: “mp4”, “hls”, “mkv”, “ism”, “dash”, “ts” if its to direct stream
|
|
if meta.json.MediaSources[0].container = "mov"
|
|
streamInfo.Container = "mp4"
|
|
else
|
|
streamInfo.Container = meta.json.MediaSources[0].container
|
|
end if
|
|
end if
|
|
|
|
decodeResult = devinfo.CanDecodeVideo(streamInfo)
|
|
return decodeResult <> invalid and decodeResult.result
|
|
|
|
end function
|
|
|
|
function getContainerType(meta as object) as string
|
|
' Determine the file type of the video file source
|
|
if meta.json.mediaSources = invalid then return ""
|
|
|
|
container = meta.json.mediaSources[0].container
|
|
if container = invalid
|
|
container = ""
|
|
else if container = "m4v" or container = "mov"
|
|
container = "mp4"
|
|
end if
|
|
|
|
return container
|
|
end function
|
|
|
|
' Add next episodes to the playback queue
|
|
sub addNextEpisodesToQueue(showID)
|
|
' Don't queue next episodes if we already have a playback queue
|
|
maxQueueCount = 1
|
|
|
|
if m.top.isIntro
|
|
maxQueueCount = 2
|
|
end if
|
|
|
|
if m.global.queueManager.callFunc("getCount") > maxQueueCount then return
|
|
|
|
videoID = m.top.itemId
|
|
|
|
' If first item is an intro video, use the next item in the queue
|
|
if m.top.isIntro
|
|
currentVideo = m.global.queueManager.callFunc("getItemByIndex", 1)
|
|
|
|
if isValid(currentVideo) and isValid(currentVideo.id)
|
|
videoID = currentVideo.id
|
|
|
|
' Override showID value since it's for the intro video
|
|
meta = ItemMetaData(videoID)
|
|
if isValid(meta)
|
|
showID = meta.showID
|
|
end if
|
|
end if
|
|
end if
|
|
|
|
url = Substitute("Shows/{0}/Episodes", showID)
|
|
urlParams = { "UserId": m.global.session.user.id }
|
|
urlParams.Append({ "StartItemId": videoID })
|
|
urlParams.Append({ "Limit": 50 })
|
|
resp = APIRequest(url, urlParams)
|
|
data = getJson(resp)
|
|
|
|
if isValid(data) and data.Items.Count() > 1
|
|
for i = 1 to data.Items.Count() - 1
|
|
m.global.queueManager.callFunc("push", data.Items[i])
|
|
end for
|
|
end if
|
|
end sub
|
|
|
|
'Checks available subtitle tracks and puts subtitles in forced, default, and non-default/forced but preferred language at the top
|
|
function sortSubtitles(id as string, MediaStreams)
|
|
tracks = { "forced": [], "default": [], "normal": [], "text": [] }
|
|
'Too many args for using substitute
|
|
prefered_lang = m.global.session.user.configuration.SubtitleLanguagePreference
|
|
for each stream in MediaStreams
|
|
if stream.type = "Subtitle"
|
|
|
|
url = ""
|
|
if isValid(stream.DeliveryUrl)
|
|
url = buildURL(stream.DeliveryUrl)
|
|
end if
|
|
|
|
stream = {
|
|
"Track": { "Language": stream.language, "Description": stream.displaytitle, "TrackName": url },
|
|
"IsTextSubtitleStream": stream.IsTextSubtitleStream,
|
|
"Index": stream.index,
|
|
"IsDefault": stream.IsDefault,
|
|
"IsForced": stream.IsForced,
|
|
"IsExternal": stream.IsExternal,
|
|
"IsEncoded": stream.DeliveryMethod = "Encode"
|
|
}
|
|
if stream.isForced
|
|
trackType = "forced"
|
|
else if stream.IsDefault
|
|
trackType = "default"
|
|
else if stream.IsTextSubtitleStream
|
|
trackType = "text"
|
|
else
|
|
trackType = "normal"
|
|
end if
|
|
if prefered_lang <> "" and prefered_lang = stream.Track.Language
|
|
tracks[trackType].unshift(stream)
|
|
else
|
|
tracks[trackType].push(stream)
|
|
end if
|
|
end if
|
|
end for
|
|
|
|
tracks["default"].append(tracks["normal"])
|
|
tracks["forced"].append(tracks["default"])
|
|
tracks["forced"].append(tracks["text"])
|
|
|
|
return { "all": tracks["forced"], "text": tracks["text"] }
|
|
end function
|
|
|
|
function FindPreferredAudioStream(streams as dynamic) as integer
|
|
preferredLanguage = m.user.Configuration.AudioLanguagePreference
|
|
playDefault = m.user.Configuration.PlayDefaultAudioTrack
|
|
|
|
if playDefault <> invalid and playDefault = true
|
|
return 1
|
|
end if
|
|
|
|
' Do we already have the MediaStreams or not?
|
|
if streams = invalid
|
|
url = Substitute("Users/{0}/Items/{1}", m.user.id, m.top.itemId)
|
|
resp = APIRequest(url)
|
|
jsonResponse = getJson(resp)
|
|
|
|
if jsonResponse = invalid or jsonResponse.MediaStreams = invalid then return 1
|
|
|
|
streams = jsonResponse.MediaStreams
|
|
end if
|
|
|
|
if preferredLanguage <> invalid
|
|
for i = 0 to streams.Count() - 1
|
|
if LCase(streams[i].Type) = "audio"
|
|
if streams[i].Language <> invalid and LCase(streams[i].Language) = LCase(preferredLanguage)
|
|
return i
|
|
end if
|
|
end if
|
|
end for
|
|
end if
|
|
|
|
return 1
|
|
end function
|
|
|
|
function getSubtitleLanguages()
|
|
return {
|
|
"aar": "Afar",
|
|
"abk": "Abkhazian",
|
|
"ace": "Achinese",
|
|
"ach": "Acoli",
|
|
"ada": "Adangme",
|
|
"ady": "Adyghe; Adygei",
|
|
"afa": "Afro-Asiatic languages",
|
|
"afh": "Afrihili",
|
|
"afr": "Afrikaans",
|
|
"ain": "Ainu",
|
|
"aka": "Akan",
|
|
"akk": "Akkadian",
|
|
"alb": "Albanian",
|
|
"ale": "Aleut",
|
|
"alg": "Algonquian languages",
|
|
"alt": "Southern Altai",
|
|
"amh": "Amharic",
|
|
"ang": "English, Old (ca.450-1100)",
|
|
"anp": "Angika",
|
|
"apa": "Apache languages",
|
|
"ara": "Arabic",
|
|
"arc": "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)",
|
|
"arg": "Aragonese",
|
|
"arm": "Armenian",
|
|
"arn": "Mapudungun; Mapuche",
|
|
"arp": "Arapaho",
|
|
"art": "Artificial languages",
|
|
"arw": "Arawak",
|
|
"asm": "Assamese",
|
|
"ast": "Asturian; Bable; Leonese; Asturleonese",
|
|
"ath": "Athapascan languages",
|
|
"aus": "Australian languages",
|
|
"ava": "Avaric",
|
|
"ave": "Avestan",
|
|
"awa": "Awadhi",
|
|
"aym": "Aymara",
|
|
"aze": "Azerbaijani",
|
|
"bad": "Banda languages",
|
|
"bai": "Bamileke languages",
|
|
"bak": "Bashkir",
|
|
"bal": "Baluchi",
|
|
"bam": "Bambara",
|
|
"ban": "Balinese",
|
|
"baq": "Basque",
|
|
"bas": "Basa",
|
|
"bat": "Baltic languages",
|
|
"bej": "Beja; Bedawiyet",
|
|
"bel": "Belarusian",
|
|
"bem": "Bemba",
|
|
"ben": "Bengali",
|
|
"ber": "Berber languages",
|
|
"bho": "Bhojpuri",
|
|
"bih": "Bihari languages",
|
|
"bik": "Bikol",
|
|
"bin": "Bini; Edo",
|
|
"bis": "Bislama",
|
|
"bla": "Siksika",
|
|
"bnt": "Bantu (Other)",
|
|
"bos": "Bosnian",
|
|
"bra": "Braj",
|
|
"bre": "Breton",
|
|
"btk": "Batak languages",
|
|
"bua": "Buriat",
|
|
"bug": "Buginese",
|
|
"bul": "Bulgarian",
|
|
"bur": "Burmese",
|
|
"byn": "Blin; Bilin",
|
|
"cad": "Caddo",
|
|
"cai": "Central American Indian languages",
|
|
"car": "Galibi Carib",
|
|
"cat": "Catalan; Valencian",
|
|
"cau": "Caucasian languages",
|
|
"ceb": "Cebuano",
|
|
"cel": "Celtic languages",
|
|
"cha": "Chamorro",
|
|
"chb": "Chibcha",
|
|
"che": "Chechen",
|
|
"chg": "Chagatai",
|
|
"chi": "Chinese",
|
|
"chk": "Chuukese",
|
|
"chm": "Mari",
|
|
"chn": "Chinook jargon",
|
|
"cho": "Choctaw",
|
|
"chp": "Chipewyan; Dene Suline",
|
|
"chr": "Cherokee",
|
|
"chu": "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic",
|
|
"chv": "Chuvash",
|
|
"chy": "Cheyenne",
|
|
"cmc": "Chamic languages",
|
|
"cop": "Coptic",
|
|
"cor": "Cornish",
|
|
"cos": "Corsican",
|
|
"cpe": "Creoles and pidgins, English based",
|
|
"cpf": "Creoles and pidgins, French-based ",
|
|
"cpp": "Creoles and pidgins, Portuguese-based ",
|
|
"cre": "Cree",
|
|
"crh": "Crimean Tatar; Crimean Turkish",
|
|
"crp": "Creoles and pidgins ",
|
|
"csb": "Kashubian",
|
|
"cus": "Cushitic languages",
|
|
"cze": "Czech",
|
|
"dak": "Dakota",
|
|
"dan": "Danish",
|
|
"dar": "Dargwa",
|
|
"day": "Land Dayak languages",
|
|
"del": "Delaware",
|
|
"den": "Slave (Athapascan)",
|
|
"dgr": "Dogrib",
|
|
"din": "Dinka",
|
|
"div": "Divehi; Dhivehi; Maldivian",
|
|
"doi": "Dogri",
|
|
"dra": "Dravidian languages",
|
|
"dsb": "Lower Sorbian",
|
|
"dua": "Duala",
|
|
"dum": "Dutch, Middle (ca.1050-1350)",
|
|
"dut": "Dutch; Flemish",
|
|
"dyu": "Dyula",
|
|
"dzo": "Dzongkha",
|
|
"efi": "Efik",
|
|
"egy": "Egyptian (Ancient)",
|
|
"eka": "Ekajuk",
|
|
"elx": "Elamite",
|
|
"eng": "English",
|
|
"enm": "English, Middle (1100-1500)",
|
|
"epo": "Esperanto",
|
|
"est": "Estonian",
|
|
"ewe": "Ewe",
|
|
"ewo": "Ewondo",
|
|
"fan": "Fang",
|
|
"fao": "Faroese",
|
|
"fat": "Fanti",
|
|
"fij": "Fijian",
|
|
"fil": "Filipino; Pilipino",
|
|
"fin": "Finnish",
|
|
"fiu": "Finno-Ugrian languages",
|
|
"fon": "Fon",
|
|
"fre": "French",
|
|
"frm": "French, Middle (ca.1400-1600)",
|
|
"fro": "French, Old (842-ca.1400)",
|
|
"frc": "French (Canada)",
|
|
"frr": "Northern Frisian",
|
|
"frs": "Eastern Frisian",
|
|
"fry": "Western Frisian",
|
|
"ful": "Fulah",
|
|
"fur": "Friulian",
|
|
"gaa": "Ga",
|
|
"gay": "Gayo",
|
|
"gba": "Gbaya",
|
|
"gem": "Germanic languages",
|
|
"geo": "Georgian",
|
|
"ger": "German",
|
|
"gez": "Geez",
|
|
"gil": "Gilbertese",
|
|
"gla": "Gaelic; Scottish Gaelic",
|
|
"gle": "Irish",
|
|
"glg": "Galician",
|
|
"glv": "Manx",
|
|
"gmh": "German, Middle High (ca.1050-1500)",
|
|
"goh": "German, Old High (ca.750-1050)",
|
|
"gon": "Gondi",
|
|
"gor": "Gorontalo",
|
|
"got": "Gothic",
|
|
"grb": "Grebo",
|
|
"grc": "Greek, Ancient (to 1453)",
|
|
"gre": "Greek, Modern (1453-)",
|
|
"grn": "Guarani",
|
|
"gsw": "Swiss German; Alemannic; Alsatian",
|
|
"guj": "Gujarati",
|
|
"gwi": "Gwich'in",
|
|
"hai": "Haida",
|
|
"hat": "Haitian; Haitian Creole",
|
|
"hau": "Hausa",
|
|
"haw": "Hawaiian",
|
|
"heb": "Hebrew",
|
|
"her": "Herero",
|
|
"hil": "Hiligaynon",
|
|
"him": "Himachali languages; Western Pahari languages",
|
|
"hin": "Hindi",
|
|
"hit": "Hittite",
|
|
"hmn": "Hmong; Mong",
|
|
"hmo": "Hiri Motu",
|
|
"hrv": "Croatian",
|
|
"hsb": "Upper Sorbian",
|
|
"hun": "Hungarian",
|
|
"hup": "Hupa",
|
|
"iba": "Iban",
|
|
"ibo": "Igbo",
|
|
"ice": "Icelandic",
|
|
"ido": "Ido",
|
|
"iii": "Sichuan Yi; Nuosu",
|
|
"ijo": "Ijo languages",
|
|
"iku": "Inuktitut",
|
|
"ile": "Interlingue; Occidental",
|
|
"ilo": "Iloko",
|
|
"ina": "Interlingua (International Auxiliary Language Association)",
|
|
"inc": "Indic languages",
|
|
"ind": "Indonesian",
|
|
"ine": "Indo-European languages",
|
|
"inh": "Ingush",
|
|
"ipk": "Inupiaq",
|
|
"ira": "Iranian languages",
|
|
"iro": "Iroquoian languages",
|
|
"ita": "Italian",
|
|
"jav": "Javanese",
|
|
"jbo": "Lojban",
|
|
"jpn": "Japanese",
|
|
"jpr": "Judeo-Persian",
|
|
"jrb": "Judeo-Arabic",
|
|
"kaa": "Kara-Kalpak",
|
|
"kab": "Kabyle",
|
|
"kac": "Kachin; Jingpho",
|
|
"kal": "Kalaallisut; Greenlandic",
|
|
"kam": "Kamba",
|
|
"kan": "Kannada",
|
|
"kar": "Karen languages",
|
|
"kas": "Kashmiri",
|
|
"kau": "Kanuri",
|
|
"kaw": "Kawi",
|
|
"kaz": "Kazakh",
|
|
"kbd": "Kabardian",
|
|
"kha": "Khasi",
|
|
"khi": "Khoisan languages",
|
|
"khm": "Central Khmer",
|
|
"kho": "Khotanese; Sakan",
|
|
"kik": "Kikuyu; Gikuyu",
|
|
"kin": "Kinyarwanda",
|
|
"kir": "Kirghiz; Kyrgyz",
|
|
"kmb": "Kimbundu",
|
|
"kok": "Konkani",
|
|
"kom": "Komi",
|
|
"kon": "Kongo",
|
|
"kor": "Korean",
|
|
"kos": "Kosraean",
|
|
"kpe": "Kpelle",
|
|
"krc": "Karachay-Balkar",
|
|
"krl": "Karelian",
|
|
"kro": "Kru languages",
|
|
"kru": "Kurukh",
|
|
"kua": "Kuanyama; Kwanyama",
|
|
"kum": "Kumyk",
|
|
"kur": "Kurdish",
|
|
"kut": "Kutenai",
|
|
"lad": "Ladino",
|
|
"lah": "Lahnda",
|
|
"lam": "Lamba",
|
|
"lao": "Lao",
|
|
"lat": "Latin",
|
|
"lav": "Latvian",
|
|
"lez": "Lezghian",
|
|
"lim": "Limburgan; Limburger; Limburgish",
|
|
"lin": "Lingala",
|
|
"lit": "Lithuanian",
|
|
"lol": "Mongo",
|
|
"loz": "Lozi",
|
|
"ltz": "Luxembourgish; Letzeburgesch",
|
|
"lua": "Luba-Lulua",
|
|
"lub": "Luba-Katanga",
|
|
"lug": "Ganda",
|
|
"lui": "Luiseno",
|
|
"lun": "Lunda",
|
|
"luo": "Luo (Kenya and Tanzania)",
|
|
"lus": "Lushai",
|
|
"mac": "Macedonian",
|
|
"mad": "Madurese",
|
|
"mag": "Magahi",
|
|
"mah": "Marshallese",
|
|
"mai": "Maithili",
|
|
"mak": "Makasar",
|
|
"mal": "Malayalam",
|
|
"man": "Mandingo",
|
|
"mao": "Maori",
|
|
"map": "Austronesian languages",
|
|
"mar": "Marathi",
|
|
"mas": "Masai",
|
|
"may": "Malay",
|
|
"mdf": "Moksha",
|
|
"mdr": "Mandar",
|
|
"men": "Mende",
|
|
"mga": "Irish, Middle (900-1200)",
|
|
"mic": "Mi'kmaq; Micmac",
|
|
"min": "Minangkabau",
|
|
"mis": "Uncoded languages",
|
|
"mkh": "Mon-Khmer languages",
|
|
"mlg": "Malagasy",
|
|
"mlt": "Maltese",
|
|
"mnc": "Manchu",
|
|
"mni": "Manipuri",
|
|
"mno": "Manobo languages",
|
|
"moh": "Mohawk",
|
|
"mon": "Mongolian",
|
|
"mos": "Mossi",
|
|
"mul": "Multiple languages",
|
|
"mun": "Munda languages",
|
|
"mus": "Creek",
|
|
"mwl": "Mirandese",
|
|
"mwr": "Marwari",
|
|
"myn": "Mayan languages",
|
|
"myv": "Erzya",
|
|
"nah": "Nahuatl languages",
|
|
"nai": "North American Indian languages",
|
|
"nap": "Neapolitan",
|
|
"nau": "Nauru",
|
|
"nav": "Navajo; Navaho",
|
|
"nbl": "Ndebele, South; South Ndebele",
|
|
"nde": "Ndebele, North; North Ndebele",
|
|
"ndo": "Ndonga",
|
|
"nds": "Low German; Low Saxon; German, Low; Saxon, Low",
|
|
"nep": "Nepali",
|
|
"new": "Nepal Bhasa; Newari",
|
|
"nia": "Nias",
|
|
"nic": "Niger-Kordofanian languages",
|
|
"niu": "Niuean",
|
|
"nno": "Norwegian Nynorsk; Nynorsk, Norwegian",
|
|
"nob": "Bokmål, Norwegian; Norwegian Bokmål",
|
|
"nog": "Nogai",
|
|
"non": "Norse, Old",
|
|
"nor": "Norwegian",
|
|
"nqo": "N'Ko",
|
|
"nso": "Pedi; Sepedi; Northern Sotho",
|
|
"nub": "Nubian languages",
|
|
"nwc": "Classical Newari; Old Newari; Classical Nepal Bhasa",
|
|
"nya": "Chichewa; Chewa; Nyanja",
|
|
"nym": "Nyamwezi",
|
|
"nyn": "Nyankole",
|
|
"nyo": "Nyoro",
|
|
"nzi": "Nzima",
|
|
"oci": "Occitan (post 1500); Provençal",
|
|
"oji": "Ojibwa",
|
|
"ori": "Oriya",
|
|
"orm": "Oromo",
|
|
"osa": "Osage",
|
|
"oss": "Ossetian; Ossetic",
|
|
"ota": "Turkish, Ottoman (1500-1928)",
|
|
"oto": "Otomian languages",
|
|
"paa": "Papuan languages",
|
|
"pag": "Pangasinan",
|
|
"pal": "Pahlavi",
|
|
"pam": "Pampanga; Kapampangan",
|
|
"pan": "Panjabi; Punjabi",
|
|
"pap": "Papiamento",
|
|
"pau": "Palauan",
|
|
"peo": "Persian, Old (ca.600-400 B.C.)",
|
|
"per": "Persian",
|
|
"phi": "Philippine languages",
|
|
"phn": "Phoenician",
|
|
"pli": "Pali",
|
|
"pol": "Polish",
|
|
"pon": "Pohnpeian",
|
|
"por": "Portuguese",
|
|
"pob": "Portuguese (Brazil)",
|
|
"pra": "Prakrit languages",
|
|
"pro": "Provençal, Old (to 1500)",
|
|
"pus": "Pushto; Pashto",
|
|
"qaa-qtz": "Reserved for local use",
|
|
"que": "Quechua",
|
|
"raj": "Rajasthani",
|
|
"rap": "Rapanui",
|
|
"rar": "Rarotongan; Cook Islands Maori",
|
|
"roa": "Romance languages",
|
|
"roh": "Romansh",
|
|
"rom": "Romany",
|
|
"rum": "Romanian; Moldavian; Moldovan",
|
|
"run": "Rundi",
|
|
"rup": "Aromanian; Arumanian; Macedo-Romanian",
|
|
"rus": "Russian",
|
|
"sad": "Sandawe",
|
|
"sag": "Sango",
|
|
"sah": "Yakut",
|
|
"sai": "South American Indian (Other)",
|
|
"sal": "Salishan languages",
|
|
"sam": "Samaritan Aramaic",
|
|
"san": "Sanskrit",
|
|
"sas": "Sasak",
|
|
"sat": "Santali",
|
|
"scn": "Sicilian",
|
|
"sco": "Scots",
|
|
"sel": "Selkup",
|
|
"sem": "Semitic languages",
|
|
"sga": "Irish, Old (to 900)",
|
|
"sgn": "Sign Languages",
|
|
"shn": "Shan",
|
|
"sid": "Sidamo",
|
|
"sin": "Sinhala; Sinhalese",
|
|
"sio": "Siouan languages",
|
|
"sit": "Sino-Tibetan languages",
|
|
"sla": "Slavic languages",
|
|
"slo": "Slovak",
|
|
"slv": "Slovenian",
|
|
"sma": "Southern Sami",
|
|
"sme": "Northern Sami",
|
|
"smi": "Sami languages",
|
|
"smj": "Lule Sami",
|
|
"smn": "Inari Sami",
|
|
"smo": "Samoan",
|
|
"sms": "Skolt Sami",
|
|
"sna": "Shona",
|
|
"snd": "Sindhi",
|
|
"snk": "Soninke",
|
|
"sog": "Sogdian",
|
|
"som": "Somali",
|
|
"son": "Songhai languages",
|
|
"sot": "Sotho, Southern",
|
|
"spa": "Spanish; Latin",
|
|
"spa": "Spanish; Castilian",
|
|
"srd": "Sardinian",
|
|
"srn": "Sranan Tongo",
|
|
"srp": "Serbian",
|
|
"srr": "Serer",
|
|
"ssa": "Nilo-Saharan languages",
|
|
"ssw": "Swati",
|
|
"suk": "Sukuma",
|
|
"sun": "Sundanese",
|
|
"sus": "Susu",
|
|
"sux": "Sumerian",
|
|
"swa": "Swahili",
|
|
"swe": "Swedish",
|
|
"syc": "Classical Syriac",
|
|
"syr": "Syriac",
|
|
"tah": "Tahitian",
|
|
"tai": "Tai languages",
|
|
"tam": "Tamil",
|
|
"tat": "Tatar",
|
|
"tel": "Telugu",
|
|
"tem": "Timne",
|
|
"ter": "Tereno",
|
|
"tet": "Tetum",
|
|
"tgk": "Tajik",
|
|
"tgl": "Tagalog",
|
|
"tha": "Thai",
|
|
"tib": "Tibetan",
|
|
"tig": "Tigre",
|
|
"tir": "Tigrinya",
|
|
"tiv": "Tiv",
|
|
"tkl": "Tokelau",
|
|
"tlh": "Klingon; tlhIngan-Hol",
|
|
"tli": "Tlingit",
|
|
"tmh": "Tamashek",
|
|
"tog": "Tonga (Nyasa)",
|
|
"ton": "Tonga (Tonga Islands)",
|
|
"tpi": "Tok Pisin",
|
|
"tsi": "Tsimshian",
|
|
"tsn": "Tswana",
|
|
"tso": "Tsonga",
|
|
"tuk": "Turkmen",
|
|
"tum": "Tumbuka",
|
|
"tup": "Tupi languages",
|
|
"tur": "Turkish",
|
|
"tut": "Altaic languages",
|
|
"tvl": "Tuvalu",
|
|
"twi": "Twi",
|
|
"tyv": "Tuvinian",
|
|
"udm": "Udmurt",
|
|
"uga": "Ugaritic",
|
|
"uig": "Uighur; Uyghur",
|
|
"ukr": "Ukrainian",
|
|
"umb": "Umbundu",
|
|
"und": "Undetermined",
|
|
"urd": "Urdu",
|
|
"uzb": "Uzbek",
|
|
"vai": "Vai",
|
|
"ven": "Venda",
|
|
"vie": "Vietnamese",
|
|
"vol": "Volapük",
|
|
"vot": "Votic",
|
|
"wak": "Wakashan languages",
|
|
"wal": "Walamo",
|
|
"war": "Waray",
|
|
"was": "Washo",
|
|
"wel": "Welsh",
|
|
"wen": "Sorbian languages",
|
|
"wln": "Walloon",
|
|
"wol": "Wolof",
|
|
"xal": "Kalmyk; Oirat",
|
|
"xho": "Xhosa",
|
|
"yao": "Yao",
|
|
"yap": "Yapese",
|
|
"yid": "Yiddish",
|
|
"yor": "Yoruba",
|
|
"ypk": "Yupik languages",
|
|
"zap": "Zapotec",
|
|
"zbl": "Blissymbols; Blissymbolics; Bliss",
|
|
"zen": "Zenaga",
|
|
"zgh": "Standard Moroccan Tamazight",
|
|
"zha": "Zhuang; Chuang",
|
|
"znd": "Zande languages",
|
|
"zul": "Zulu",
|
|
"zun": "Zuni",
|
|
"zxx": "No linguistic content; Not applicable",
|
|
"zza": "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"
|
|
}
|
|
end function
|
|
</pre>
|
|
</article>
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="clearfix"></div>
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="modal fade" id="searchResults">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
<h4 class="modal-title">Search results</h4>
|
|
</div>
|
|
<div class="modal-body"></div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
</div>
|
|
</div><!-- /.modal-content -->
|
|
</div><!-- /.modal-dialog -->
|
|
</div>
|
|
|
|
|
|
<footer>
|
|
|
|
<span class="jsdoc-message">Source code: <a href="https://github.com/jellyfin/jellyfin-roku">https://github.com/jellyfin/jellyfin-roku</a></span><span class="jsdoc-message">Jellyfin Roku Development Forum: <a href="https://forum.jellyfin.org/f-roku-development">https://forum.jellyfin.org/f-roku-development</a></span>
|
|
|
|
|
|
<span class="jsdoc-message">
|
|
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a>
|
|
|
|
on Nov 11th 2023
|
|
|
|
using the <a href="https://github.com/docstrap/docstrap">DocStrap template</a>.
|
|
</span>
|
|
</footer>
|
|
|
|
<script src="scripts/docstrap.lib.js"></script>
|
|
<script src="scripts/toc.js"></script>
|
|
|
|
<script type="text/javascript" src="scripts/fulltext-search-ui.js"></script>
|
|
|
|
|
|
<script>
|
|
$( function () {
|
|
$( "[id*='$']" ).each( function () {
|
|
var $this = $( this );
|
|
|
|
$this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) );
|
|
} );
|
|
|
|
$( ".tutorial-section pre, .readme-section pre, pre.prettyprint.source" ).each( function () {
|
|
var $this = $( this );
|
|
|
|
var example = $this.find( "code" );
|
|
exampleText = example.html();
|
|
var lang = /{@lang (.*?)}/.exec( exampleText );
|
|
if ( lang && lang[1] ) {
|
|
exampleText = exampleText.replace( lang[0], "" );
|
|
example.html( exampleText );
|
|
lang = lang[1];
|
|
} else {
|
|
var langClassMatch = example.parent()[0].className.match(/lang\-(\S+)/);
|
|
lang = langClassMatch ? langClassMatch[1] : "javascript";
|
|
}
|
|
|
|
if ( lang ) {
|
|
|
|
$this
|
|
.addClass( "sunlight-highlight-" + lang )
|
|
.addClass( "linenums" )
|
|
.html( example.html() );
|
|
|
|
}
|
|
} );
|
|
|
|
Sunlight.highlightAll( {
|
|
lineNumbers : true,
|
|
showMenu : true,
|
|
enableDoclinks : true
|
|
} );
|
|
|
|
$.catchAnchorLinks( {
|
|
navbarOffset: 10
|
|
} );
|
|
$( "#toc" ).toc( {
|
|
anchorName : function ( i, heading, prefix ) {
|
|
return $( heading ).attr( "id" ) || ( prefix + i );
|
|
},
|
|
selectors : "#toc-content h1,#toc-content h2,#toc-content h3,#toc-content h4",
|
|
showAndHide : false,
|
|
smoothScrolling: true
|
|
} );
|
|
|
|
$( "#main span[id^='toc']" ).addClass( "toc-shim" );
|
|
$( '.dropdown-toggle' ).dropdown();
|
|
|
|
$( "table" ).each( function () {
|
|
var $this = $( this );
|
|
$this.addClass('table');
|
|
} );
|
|
|
|
} );
|
|
</script>
|
|
|
|
|
|
|
|
<!--Navigation and Symbol Display-->
|
|
|
|
|
|
<!--Google Analytics-->
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
$(document).ready(function() {
|
|
SearcherDisplay.init();
|
|
});
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html>
|