1026 lines
169 KiB
HTML
1026 lines
169 KiB
HTML
|
|
||
|
|
||
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
|
||
|
<meta charset="utf-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
||
|
<title>
|
||
|
components/ItemGrid/LoadVideoContentTask.brs - Documentation
|
||
|
</title>
|
||
|
|
||
|
<link href="https://www.braintreepayments.com/images/favicon-ccda0b14.png" rel="icon" type="image/png">
|
||
|
|
||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js"></script>
|
||
|
<script>hljs.initHighlightingOnLoad();</script>
|
||
|
|
||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
|
||
|
|
||
|
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||
|
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
<!-- start Mixpanel -->
|
||
|
<script type="text/javascript">(function(e,a){if(!a.__SV){var b=window;try{var c,l,i,j=b.location,g=j.hash;c=function(a,b){return(l=a.match(RegExp(b+"=([^&]*)")))?l[1]:null};g&&c(g,"state")&&(i=JSON.parse(decodeURIComponent(c(g,"state"))),"mpeditor"===i.action&&(b.sessionStorage.setItem("_mpcehash",g),history.replaceState(i.desiredHash||"",e.title,j.pathname+j.search)))}catch(m){}var k,h;window.mixpanel=a;a._i=[];a.init=function(b,c,f){function e(b,a){var c=a.split(".");2==c.length&&(b=b[c[0]],a=c[1]);b[a]=function(){b.push([a].concat(Array.prototype.slice.call(arguments,
|
||
|
0)))}}var d=a;"undefined"!==typeof f?d=a[f]=[]:f="mixpanel";d.people=d.people||[];d.toString=function(b){var a="mixpanel";"mixpanel"!==f&&(a+="."+f);b||(a+=" (stub)");return a};d.people.toString=function(){return d.toString(1)+".people (stub)"};k="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config reset people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||
|
for(h=0;h<k.length;h++)e(d,k[h]);a._i.push([b,c,f])};a.__SV=1.2;b=e.createElement("script");b.type="text/javascript";b.async=!0;b.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";c=e.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c)}})(document,window.mixpanel||[]);
|
||
|
mixpanel.init("1919205b2da72e4da3b9b6639b444d59");</script>
|
||
|
<!-- end Mixpanel -->
|
||
|
</head>
|
||
|
|
||
|
<body>
|
||
|
<svg style="display: none;">
|
||
|
<defs>
|
||
|
<symbol id="linkIcon" fill="#706d77" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||
|
<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>
|
||
|
</symbol>
|
||
|
</defs>
|
||
|
</svg>
|
||
|
|
||
|
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
||
|
<label for="nav-trigger" class="navicon-button x">
|
||
|
<div class="navicon"></div>
|
||
|
</label>
|
||
|
|
||
|
<label for="nav-trigger" class="overlay"></label>
|
||
|
|
||
|
<div class="top-nav-wrapper">
|
||
|
<ul>
|
||
|
<li >
|
||
|
<a href="index.html">
|
||
|
|
||
|
<svg fill="#6D6D6D" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||
|
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
|
||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||
|
</svg>
|
||
|
|
||
|
|
||
|
</a>
|
||
|
</li>
|
||
|
|
||
|
|
||
|
|
||
|
</ul>
|
||
|
</div>
|
||
|
|
||
|
<nav>
|
||
|
<h3 class="reference-title">
|
||
|
Braintree SDK Client Reference
|
||
|
</h3>
|
||
|
|
||
|
|
||
|
|
||
|
<h3>Classes</h3><ul><li id="Logger-nav"><a href="module-log.Logger.html">Logger</a><ul class='methods'><li data-type="method" id="Logger-debug-nav"><a href="module-log.Logger.html#debug">debug</a></li><li data-type="method" id="Logger-decreaseIndent-nav"><a href="module-log.Logger.html#decreaseIndent">decreaseIndent</a></li><li data-type="method" id="Logger-error-nav"><a href="module-log.Logger.html#error">error</a></li><li data-type="method" id="Logger-increaseIndent-nav"><a href="module-log.Logger.html#increaseIndent">increaseIndent</a></li><li data-type="method" id="Logger-info-nav"><a href="module-log.Logger.html#info">info</a></li><li data-type="method" id="Logger-log-nav"><a href="module-log.Logger.html#log">log</a></li><li data-type="method" id="Logger-method-nav"><a href="module-log.Logger.html#method">method</a></li><li data-type="method" id="Logger-resetIndent-nav"><a href="module-log.Logger.html#resetIndent">resetIndent</a></li><li data-type="method" id="Logger-toString-nav"><a href="module-log.Logger.html#toString">toString</a></li><li data-type="method" id="Logger-verbose-nav"><a href="module-log.Logger.html#verbose">verbose</a></li><li data-type="method" id="Logger-warn-nav"><a href="module-log.Logger.html#warn">warn</a></li></ul></li></ul><h3>Modules</h3><ul><li id="AlbumData-nav"><a href="module-AlbumData.html">AlbumData</a><ul class='methods'><li data-type="method" id="AlbumData-setFields-nav"><a href="module-AlbumData.html#.setFields">setFields</a></li></ul></li><li id="AlbumGrid-nav"><a href="module-AlbumGrid.html">AlbumGrid</a><ul class='methods'><li data-type="method" id="AlbumGrid-getData-nav"><a href="module-AlbumGrid.html#.getData">getData</a></li><li data-type="method" id="AlbumGrid-init-nav"><a href="module-AlbumGrid.html#.init">init</a></li><li data-type="method" id="AlbumGrid-onKeyEvent-nav"><a href="module-AlbumGrid.html#.onKeyEvent">onKeyEvent</a></li></ul></li><li id="AlbumTrackList-nav"><a href="module-AlbumTrackList.html">AlbumTrackList</a><ul class='methods'><li data-type="method" id="AlbumTrackList-getData-nav"><a href="module-AlbumTrackList.html#.getData">getData</a></li><li data-type="method" id="AlbumTrackList-init-nav"><a href="module-AlbumTrackList.html#.init">init</a></li></ul></li><li id="AlbumView-nav"><a href="module-AlbumView.html">AlbumView</a><ul class='methods'><li data-type="method" id="AlbumView-OnScreenHidden-nav"><a href="module-AlbumView.html#.OnScreenHidden">OnScreenHidden</a></li><li data-type="method" id="AlbumView-adjustScreenForNoOverview-nav"><a href="module-AlbumView.html#.adjustScreenForNoOverview">adjustScreenForNoOverview</a></li><li data-type="method" id="AlbumView-createDialogPallete-nav"><a href="module-AlbumView.html#.createDialogPallete">createDialogPallete</a></li><li data-type="method" id="AlbumView-createFullDscrDlg-nav"><a href="module-AlbumView.html#.createFullDscrDlg">createFullDscrDlg</a></li><li data-type="method" id="AlbumView-init-nav"><a href="module-AlbumView.html#.init">init</a></li><li data-type="method" id="AlbumView-onDoneLoading-nav"><a href="module-AlbumView.html#.onDoneLoading">onDoneLoading</a></li><li data-type="method" id="AlbumView-onKeyEvent-nav"><a href="module-AlbumView.html#.onKeyEvent">onKeyEvent</a></li><li data-type="method" id="AlbumView-pageContentChanged-nav"><a href="module-AlbumView.html#.pageContentChanged">pageContentChanged</a></li><li data-type="method" id="AlbumView-setOnScreenTextValues-nav"><a href="module-AlbumView.html#.setOnScreenTextValues">setOnScreenTextValues</a></li><li data-type="method" id="AlbumView-setPosterImage-nav"><a href="module-AlbumView.html#.setPosterImage">setPosterImage</a></li><li data-type="method" id="AlbumView-setScreenTitle-nav"><a href="module-AlbumView.html#.setScreenTitle">setScreenTitle</a></li><li data-type="method" id="AlbumView-setupMainNode-nav"><a href="module-AlbumView.html#.setupMainNode">setupMainNode</a></li></ul></li><li id="Alpha-nav"><a href="module-Alpha.html">Alpha</a><ul class='methods'><li data-type="method" id="Alpha-init-nav"><a href="module-Alpha.html#.init">
|
||
|
</nav>
|
||
|
|
||
|
<div id="main">
|
||
|
|
||
|
<h1 class="page-title">
|
||
|
components/ItemGrid/LoadVideoContentTask.brs
|
||
|
</h1>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
<section>
|
||
|
<article>
|
||
|
<pre class="prettyprint source linenums"><code>import "pkg:/source/utils/misc.brs"
|
||
|
import "pkg:/source/api/Items.brs"
|
||
|
import "pkg:/source/api/UserLibrary.brs"
|
||
|
import "pkg:/source/api/baserequest.brs"
|
||
|
import "pkg:/source/utils/config.brs"
|
||
|
import "pkg:/source/api/Image.brs"
|
||
|
import "pkg:/source/api/userauth.brs"
|
||
|
import "pkg:/source/utils/deviceCapabilities.brs"
|
||
|
|
||
|
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.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
|
||
|
addVideoContentURL(video, mediaSourceId, audio_stream_idx, fully_external)
|
||
|
video.isTranscoded = false
|
||
|
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 = {}
|
||
|
|
||
|
params.append({
|
||
|
"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
|
||
|
</code></pre>
|
||
|
</article>
|
||
|
</section>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<br class="clear">
|
||
|
|
||
|
<footer>
|
||
|
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a>
|
||
|
</footer>
|
||
|
|
||
|
<script src="scripts/linenumber.js"></script>
|
||
|
<script src="scripts/pagelocation.js"></script>
|
||
|
|
||
|
|
||
|
|
||
|
</body>
|
||
|
</html>
|