Revert "Update Home Screen to Respect User Preferences (#553)"

This reverts commit 26d9360fd3.
This commit is contained in:
Charles Ewert 2022-07-12 19:15:59 -04:00
parent 3a27212c46
commit 13b660c7c2
14 changed files with 297 additions and 954 deletions

View File

@ -3,13 +3,13 @@
<children> <children>
<poster id="backdrop" <poster id="backdrop"
loadDisplayMode="scaleToFit" loadDisplayMode="scaleToFill"
width="1920" width="1920"
height="1080" height="1080"
opacity="0.25" opacity="0.25"
/> />
<poster id="backdropTransition" <poster id="backdropTransition"
loadDisplayMode="scaleToFit" loadDisplayMode="scaleToFill"
width="1920" width="1920"
height="1080" height="1080"
opacity="0.25" opacity="0.25"

View File

@ -37,7 +37,7 @@ sub setData()
imgParams.Append({ "maxHeight": 261 }) imgParams.Append({ "maxHeight": 261 })
imgParams.Append({ "maxWidth": 464 }) imgParams.Append({ "maxWidth": 464 })
m.top.thumbnailURL = ImageURL(datum.Id, "Primary", imgParams) m.top.thumbnailURL = ImageURL(datum.id, "Primary", imgParams)
' Add Wide Poster (Series Backdrop) ' Add Wide Poster (Series Backdrop)
if datum.ParentThumbImageTag <> invalid if datum.ParentThumbImageTag <> invalid
@ -45,7 +45,7 @@ sub setData()
else if datum.ParentBackdropImageTags <> invalid else if datum.ParentBackdropImageTags <> invalid
m.top.widePosterUrl = ImageURL(datum.ParentBackdropItemId, "Backdrop", imgParams) m.top.widePosterUrl = ImageURL(datum.ParentBackdropItemId, "Backdrop", imgParams)
else if datum.ImageTags.Primary <> invalid else if datum.ImageTags.Primary <> invalid
m.top.posterUrl = ImageURL(datum.SeriesId, "Primary", imgParams) m.top.widePosterUrl = ImageURL(datum.id, "Primary", imgParams)
end if end if
else if datum.type = "Series" else if datum.type = "Series"
@ -82,7 +82,7 @@ sub setData()
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid
m.top.thumbnailUrl = ImageURL(datum.Id, "Thumb", imgParams) m.top.thumbnailUrl = ImageURL(datum.Id, "Thumb", imgParams)
else if datum.BackdropImageTags <> invalid and datum.BackdropImageTags[0] <> invalid else if datum.BackdropImageTags[0] <> invalid
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams) m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
end if end if
@ -106,7 +106,7 @@ sub setData()
else if datum.BackdropImageTags[0] <> invalid else if datum.BackdropImageTags[0] <> invalid
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams) m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
end if end if
else if datum.type = "MusicAlbum" or datum.type = "Audio" or datum.type = "Book" else if datum.type = "MusicAlbum"
params = { "maxHeight": 261, "maxWidth": 261 } params = { "maxHeight": 261, "maxWidth": 261 }
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params) m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)
m.top.widePosterUrl = m.top.thumbnailURL m.top.widePosterUrl = m.top.thumbnailURL

View File

@ -12,8 +12,6 @@
<field id="collectionType" type="string" /> <field id="collectionType" type="string" />
<field id="imageWidth" type="integer" value="464" /> <field id="imageWidth" type="integer" value="464" />
<field id="usePoster" type="bool" value="false" /> <field id="usePoster" type="bool" value="false" />
<field id="stretch" type="bool" value="false" />
<field id="isSmall" type="bool" value="false" />
</interface> </interface>
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" /> <script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" /> <script type="text/brightscript" uri="pkg:/source/utils/config.brs" />

View File

@ -9,7 +9,7 @@ sub init()
end sub end sub
sub refresh() sub refresh()
m.top.findNode("homeRows").callFunc("loadLibraries") m.top.findNode("homeRows").callFunc("updateHomeRows")
end sub end sub
sub loadLibraries() sub loadLibraries()

View File

@ -18,6 +18,8 @@ sub itemContentChanged()
itemData = m.top.itemContent itemData = m.top.itemContent
if itemData = invalid then return if itemData = invalid then return
itemData.Title = itemData.name ' Temporarily required while we move from "HomeItem" to "JFContentItem" itemData.Title = itemData.name ' Temporarily required while we move from "HomeItem" to "JFContentItem"
m.itemPoster.width = itemData.imageWidth m.itemPoster.width = itemData.imageWidth
m.itemText.maxWidth = itemData.imageWidth m.itemText.maxWidth = itemData.imageWidth
m.itemTextExtra.width = itemData.imageWidth m.itemTextExtra.width = itemData.imageWidth
@ -30,32 +32,18 @@ sub itemContentChanged()
m.itemIcon.uri = itemData.iconUrl m.itemIcon.uri = itemData.iconUrl
end if end if
if itemData.stretch = true
m.itemPoster.loadDisplayMode = "scaleToZoom"
end if
' Format the Data based on the type of Home Data ' Format the Data based on the type of Home Data
if itemData.type = "CollectionFolder" or itemData.type = "UserView" or itemData.type = "Channel" if itemData.type = "CollectionFolder" or itemData.type = "UserView" or itemData.type = "Channel"
m.itemText.text = itemData.name m.itemText.text = itemData.name
if itemData.isSmall = true m.itemPoster.uri = itemData.widePosterURL
m.backdrop.height = "100"
m.itemText.translation = [0, 20]
itemData.usePoster = false
end if
if itemData.usePoster = true
m.itemPoster.uri = itemData.widePosterURL
end if
return return
end if end if
if itemData.type = "UserView" and itemData.usePoster = true if itemData.type = "UserView"
m.itemPoster.width = "96" m.itemPoster.width = "96"
m.itemPoster.height = "96" m.itemPoster.height = "96"
m.itemPoster.translation = "[192, 88]" m.itemPoster.translation = "[192, 88]"
m.itemText.text = itemData.name m.itemText.text = itemData.name
if itemData.isSmall = true
m.itemText.translation = [8, 10]
end if
m.itemPoster.uri = itemData.widePosterURL m.itemPoster.uri = itemData.widePosterURL
return return
end if end if
@ -64,7 +52,8 @@ sub itemContentChanged()
m.itemText.height = 34 m.itemText.height = 34
m.itemText.font.size = 25 m.itemText.font.size = 25
m.itemText.horizAlign = "left" m.itemText.horizAlign = "left"
' m.itemText.vertAlign = "bottom" m.itemText.vertAlign = "bottom"
m.itemTextExtra.visible = true
m.itemTextExtra.font.size = 22 m.itemTextExtra.font.size = 22
' "Program" is from clicking on an "On Now" item on the Home Screen ' "Program" is from clicking on an "On Now" item on the Home Screen
@ -86,12 +75,9 @@ sub itemContentChanged()
m.itemText.text = itemData.json.SeriesName m.itemText.text = itemData.json.SeriesName
if itemData.usePoster = true if itemData.usePoster = true
m.itemPoster.uri = itemData.posterURL
else
m.itemPoster.uri = itemData.widePosterURL m.itemPoster.uri = itemData.widePosterURL
end if else
if itemData.json.ImageURL <> invalid m.itemPoster.uri = itemData.thumbnailURL
m.itemPoster.uri = itemData.json.ImageURL
end if end if
' Set Series and Episode Number for Extra Text ' Set Series and Episode Number for Extra Text
@ -119,9 +105,6 @@ sub itemContentChanged()
else else
m.itemPoster.uri = itemData.thumbnailURL m.itemPoster.uri = itemData.thumbnailURL
end if end if
if itemData.json.ImageURL <> invalid
m.itemPoster.uri = itemData.json.ImageURL
end if
' Set Release Year and Age Rating for Extra Text ' Set Release Year and Age Rating for Extra Text
textExtra = "" textExtra = ""
@ -148,17 +131,22 @@ sub itemContentChanged()
else else
m.itemPoster.uri = itemData.thumbnailURL m.itemPoster.uri = itemData.thumbnailURL
end if end if
if itemData.json.ImageURL <> invalid
m.itemPoster.uri = itemData.json.ImageURL
end if
return return
end if end if
if itemData.type = "Series" if itemData.type = "Series"
m.itemText.text = itemData.name m.itemText.text = itemData.name
m.itemPoster.uri = itemData.posterURL
if itemData.json.ImageURL <> invalid if itemData.usePoster = true
m.itemPoster.uri = itemData.json.ImageURL if itemData.imageWidth = 180
m.itemPoster.uri = itemData.posterURL
else
m.itemPoster.uri = itemData.widePosterURL
end if
else
m.itemPoster.uri = itemData.thumbnailURL
end if end if
textExtra = "" textExtra = ""
if itemData.json.ProductionYear <> invalid if itemData.json.ProductionYear <> invalid
textExtra = StrI(itemData.json.ProductionYear).trim() textExtra = StrI(itemData.json.ProductionYear).trim()
@ -175,25 +163,10 @@ sub itemContentChanged()
return return
end if end if
if itemData.type = "MusicAlbum" or itemData.type = "Audio" or itemData.type = "Book" or itemData.type = "AudioBook" if itemData.type = "MusicAlbum"
m.itemText.text = itemData.name m.itemText.text = itemData.name
m.itemTextExtra.text = itemData.json.AlbumArtist m.itemTextExtra.text = itemData.json.AlbumArtist
if itemData.usePoster = true m.itemPoster.uri = itemData.posterURL
if itemData.posterURL <> ""
m.itemPoster.uri = itemData.posterURL
else
m.itemPoster.uri = itemData.json.ImageURL
end if
end if
return
end if
if itemData.type = "Photo" or itemData.type = "PhotoAlbum"
if itemData.usePoster = true
if itemData.json.ImageURL <> invalid
m.itemPoster.uri = itemData.json.ImageURL
end if
end if
return return
end if end if
@ -215,13 +188,11 @@ end sub
'Hide backdrop and icon when poster loaded 'Hide backdrop and icon when poster loaded
sub onPosterLoadStatusChanged() sub onPosterLoadStatusChanged()
if m.itemPoster <> invalid if m.itemPoster.loadStatus = "ready" and m.itemPoster.uri <> ""
if m.itemPoster.loadStatus = "ready" and m.itemPoster.uri <> "" m.backdrop.visible = false
m.backdrop.visible = false m.itemIcon.visible = false
m.itemIcon.visible = false else
else m.backdrop.visible = true
m.backdrop.visible = true m.itemIcon.visible = true
m.itemIcon.visible = true
end if
end if end if
end sub end sub

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<component name="HomeItem" extends="Group"> <component name="HomeItem" extends="Group">
<children> <children>
<Rectangle id="backdrop" width="464" height="270" translation="[8,0]" /> <Rectangle id="backdrop" width="464" height="261" translation="[8,5]" />
<Poster id="itemIcon" width="100" height="100" translation="[190,85]" loadDisplayMode="scaleToFit" /> <Poster id="itemIcon" width="100" height="100" translation="[190,85]" loadDisplayMode="scaleToFit" />
<Poster id="itemPoster" width="464" height="270" translation="[0,0]" loadDisplayMode="scaleToZoom" /> <Poster id="itemPoster" width="464" height="261" translation="[8,5]" loadDisplayMode="scaleToZoom" />
<ScrollingLabel id="itemText" horizAlign="center" vertAlign="center" font="font:SmallBoldSystemFont" height="64" maxWidth="456" translation="[8,280]" repeatCount="0" /> <ScrollingLabel id="itemText" horizAlign="center" vertAlign="center" font="font:SmallBoldSystemFont" height="64" maxWidth="456" translation="[8,267]" repeatCount="0" />
<Label id="itemTextExtra" horizAlign="left" vertAlign="center" font="font:SmallBoldSystemFont" height="32" width="456" translation="[8,319]" visible="true" color="#777777FF" /> <Label id="itemTextExtra" horizAlign="left" vertAlign="center" font="font:SmallBoldSystemFont" height="32" width="456" translation="[8,300]" visible="false" color="#777777FF" />
</children> </children>
<interface> <interface>
<field id="itemContent" type="node" onChange="itemContentChanged" /> <field id="itemContent" type="node" onChange="itemContentChanged" />

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<component name="HomeRow" extends="ContentNode"> <component name="HomeRow" extends="ContentNode">
<interface> <interface>
<field id="imageWidth" type="integer" value="200" /> <field id="imageWidth" type="integer" value="464" />
<field id="stretch" type="bool" value="false" />
<field id="usePoster" type="bool" value="false" /> <field id="usePoster" type="bool" value="false" />
</interface> </interface>
</component> </component>

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,5 @@
<function name="updateHomeRows" /> <function name="updateHomeRows" />
<function name="loadLibraries" /> <function name="loadLibraries" />
</interface> </interface>
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
<script type="text/brightscript" uri="HomeRows.brs"/> <script type="text/brightscript" uri="HomeRows.brs"/>
</component> </component>

View File

@ -13,9 +13,12 @@ sub loadItems()
resp = APIRequest(url) resp = APIRequest(url)
data = getJson(resp) data = getJson(resp)
for each item in data.Items for each item in data.Items
tmp = CreateObject("roSGNode", "HomeData") ' Skip Books for now as we don't support it (issue #525)
tmp.json = item if item.CollectionType <> "books"
results.push(tmp) tmp = CreateObject("roSGNode", "HomeData")
tmp.json = item
results.push(tmp)
end if
end for end for
' Load Latest Additions to Libraries ' Load Latest Additions to Libraries
@ -27,20 +30,17 @@ sub loadItems()
params["ParentId"] = m.top.itemId params["ParentId"] = m.top.itemId
params["EnableImageTypes"] = "Primary,Backdrop,Thumb" params["EnableImageTypes"] = "Primary,Backdrop,Thumb"
params["ImageTypeLimit"] = 1 params["ImageTypeLimit"] = 1
params["fields"] = "PrimaryImageAspectRatio,BasicSyncInfo,Path"
params["MaxWidth"] = 416
params["MaxHeight"] = 416
resp = APIRequest(url, params) resp = APIRequest(url, params)
data = getJson(resp) data = getJson(resp)
for each item in data for each item in data
tmp = CreateObject("roSGNode", "HomeData") ' Skip Books for now as we don't support it (issue #525)
item.ImageURL = ImageURL(item.Id, "Primary", params) if item.Type <> "Book"
if item.type = "Episode" tmp = CreateObject("roSGNode", "HomeData")
item.ImageURL = ImageURL(item.Id) tmp.json = item
results.push(tmp)
end if end if
tmp.json = item
results.push(tmp)
end for end for
' Load Next Up ' Load Next Up
@ -48,7 +48,6 @@ sub loadItems()
url = "Shows/NextUp" url = "Shows/NextUp"
params = {} params = {}
params["Limit"] = 30
params["recursive"] = true params["recursive"] = true
params["SortBy"] = "DatePlayed" params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending" params["SortOrder"] = "Descending"
@ -59,96 +58,38 @@ sub loadItems()
data = getJson(resp) data = getJson(resp)
for each item in data.Items for each item in data.Items
tmp = CreateObject("roSGNode", "HomeData") tmp = CreateObject("roSGNode", "HomeData")
if item.type = "Episode"
item.ImageURL = ImageURL(item.SeriesId, "Backdrop")
else
item.ImageURL = ImageURL(item.Id, "Backdrop")
end if
item.stretch = true
tmp.json = item tmp.json = item
results.push(tmp) results.push(tmp)
end for end for
' Load Continue Watching ' Load Continue Watching
else if m.top.itemsToLoad = "continueVideo" else if m.top.itemsToLoad = "continue"
url = Substitute("Users/{0}/Items/Resume", get_setting("active_user")) url = Substitute("Users/{0}/Items/Resume", get_setting("active_user"))
params = {} params = {}
params["Limit"] = 30
params["recursive"] = true params["recursive"] = true
params["SortBy"] = "DatePlayed" params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending" params["SortOrder"] = "Descending"
params["Filters"] = "IsResumable" params["Filters"] = "IsResumable"
params["MediaTypes"] = "Video"
params["EnableImageTypes"] = "Primary,Backdrop,Thumb"
params["ImageTypeLimit"] = 1
resp = APIRequest(url, params) resp = APIRequest(url, params)
data = getJson(resp) data = getJson(resp)
for each item in data.Items for each item in data.Items
tmp = CreateObject("roSGNode", "HomeData") ' Skip Books for now as we don't support it (issue #558)
if item.type = "Episode" if item.Type <> "Book"
item.ImageURL = ImageURL(item.SeriesId, "Backdrop") tmp = CreateObject("roSGNode", "HomeData")
else tmp.json = item
item.ImageURL = ImageURL(item.Id, "Backdrop") results.push(tmp)
end if end if
item.stretch = true
tmp.json = item
results.push(tmp)
end for
else if m.top.itemsToLoad = "continueAudio"
url = Substitute("Users/{0}/Items/Resume", get_setting("active_user"))
params = {}
params["Limit"] = 30
params["recursive"] = true
params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending"
params["Filters"] = "IsResumable"
params["MediaTypes"] = "Audio"
params["EnableImageTypes"] = "Primary,Backdrop,Thumb"
params["ImageTypeLimit"] = 1
resp = APIRequest(url, params)
data = getJson(resp)
for each item in data.Items
tmp = CreateObject("roSGNode", "HomeData")
item.ImageURL = ImageURL(item.Id, "Backdrop")
tmp.json = item
results.push(tmp)
end for
else if m.top.itemsToLoad = "continueBook"
url = Substitute("Users/{0}/Items/Resume", get_setting("active_user"))
params = {}
params["Limit"] = 30
params["recursive"] = true
params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending"
params["Filters"] = "IsResumable"
params["MediaTypes"] = "Book"
params["EnableImageTypes"] = "Primary,Backdrop,Thumb"
resp = APIRequest(url, params)
data = getJson(resp)
for each item in data.Items
tmp = CreateObject("roSGNode", "HomeData")
item.ImageURL = ImageURL(item.Id, "Backdrop")
tmp.json = item
results.push(tmp)
end for end for
else if m.top.itemsToLoad = "onNow" else if m.top.itemsToLoad = "onNow"
url = "LiveTv/Programs/Recommended" url = "LiveTv/Programs/Recommended"
params = {} params = {}
params["Limit"] = 30
params["userId"] = get_setting("active_user") params["userId"] = get_setting("active_user")
params["isAiring"] = true params["isAiring"] = true
params["limit"] = 16 ' 16 to be consistent with "Latest In"
params["imageTypeLimit"] = 1 params["imageTypeLimit"] = 1
params["enableImageTypes"] = "Primary,Thumb,Backdrop" params["enableImageTypes"] = "Primary,Thumb,Backdrop"
params["enableTotalRecordCount"] = false params["enableTotalRecordCount"] = false

View File

@ -4,7 +4,6 @@
<interface> <interface>
<field id="itemsToLoad" type="string" value="libraries" /> <field id="itemsToLoad" type="string" value="libraries" />
<field id="itemId" type="string" /> <field id="itemId" type="string" />
<field id="nodeNumber" type="integer" />
<field id="metadata" type="assocarray" /> <field id="metadata" type="assocarray" />
<field id="peopleList" type="array" /> <field id="peopleList" type="array" />
<field id="content" type="array" /> <field id="content" type="array" />

View File

@ -166,7 +166,7 @@ sub Main (args as dynamic) as void
group = CreateAudioPlayerGroup([selectedItem.json]) group = CreateAudioPlayerGroup([selectedItem.json])
else else
' TODO - switch on more node types ' TODO - switch on more node types
message_dialog(Substitute(tr("{0} support is coming soon!"), selectedItem.type)) message_dialog("This type is not yet supported: " + selectedItem.type + ".")
end if end if
else if isNodeEvent(msg, "movieSelected") else if isNodeEvent(msg, "movieSelected")
' If you select a movie from ANYWHERE, follow this flow ' If you select a movie from ANYWHERE, follow this flow

View File

@ -38,15 +38,15 @@ end function
function ImageURL(id, version = "Primary", params = {}) function ImageURL(id, version = "Primary", params = {})
' set defaults ' set defaults
if params.maxHeight = invalid if params.maxHeight = invalid
param = { "fillHeight": "331" } param = { "maxHeight": "384" }
params.append(param) params.append(param)
end if end if
if params.maxWidth = invalid if params.maxWidth = invalid
param = { "fillWidth": "464" } param = { "maxWidth": "196" }
params.append(param) params.append(param)
end if end if
if params.quality = invalid if params.quality = invalid
param = { "quality": "96" } param = { "quality": "90" }
params.append(param) params.append(param)
end if end if
url = Substitute("Items/{0}/Images/{1}", id, version) url = Substitute("Items/{0}/Images/{1}", id, version)
@ -63,7 +63,7 @@ function UserImageURL(id, params = {})
params.append({ "maxWidth": "300" }) params.append({ "maxWidth": "300" })
end if end if
if params.quality = invalid if params.quality = invalid
params.append({ "quality": "96" }) params.append({ "quality": "90" })
end if end if
url = Substitute("Users/{0}/Images/Primary", id) url = Substitute("Users/{0}/Images/Primary", id)

View File

@ -141,52 +141,11 @@ sub LoadUserPreferences()
resp = APIRequest(url) resp = APIRequest(url)
jsonResponse = getJson(resp) jsonResponse = getJson(resp)
if jsonResponse <> invalid and jsonResponse.CustomPrefs <> invalid if jsonResponse <> invalid and jsonResponse.CustomPrefs <> invalid and jsonResponse.CustomPrefs["landing-livetv"] <> invalid
if jsonResponse.CustomPrefs["landing-livetv"] <> invalid set_user_setting("display.livetv.landing", jsonResponse.CustomPrefs["landing-livetv"])
set_user_setting("display.livetv.landing", jsonResponse.CustomPrefs["landing-livetv"])
end if
' Take into account nones, if nones are in the middle then resort them to the end to be removed later in HomeRows
nones = 0
for i = 0 to 6
if jsonResponse.CustomPrefs["homesection" + i.ToStr()] <> invalid
if jsonResponse.CustomPrefs["homesection" + i.ToStr()] = "none"
nones += 1
else
if nones > 0
j = i - nones
set_user_setting("display.homesection" + i.ToStr(), "none")
set_user_setting("display.homesection" + j.ToStr(), jsonResponse.CustomPrefs["homesection" + i.ToStr()])
else
set_user_setting("display.homesection" + i.ToStr(), jsonResponse.CustomPrefs["homesection" + i.ToStr()])
end if
end if
end if
end for
if jsonResponse.CustomPrefs["homesection0"] = invalid
setHomeScreenDefaults()
end if
else else
setHomeScreenDefaults() unset_user_setting("display.livetv.landing")
end if end if
' Actual user settings for getting ordered views
url = Substitute("Users/{0}", id)
resp = APIRequest(url)
jsonResponse = getJson(resp)
if jsonResponse <> invalid
set_user_setting("display.userConfig", FormatJson(jsonResponse))
end if
end sub
sub setHomeScreenDefaults()
set_user_setting("display.homesection0", "smalllibrarytiles")
set_user_setting("display.homesection1", "resume")
set_user_setting("display.homesection2", "resumeaudio")
set_user_setting("display.homesection3", "resumebook")
set_user_setting("display.homesection4", "livetv")
set_user_setting("display.homesection5", "nextup")
set_user_setting("display.homesection6", "latestmedia")
end sub end sub
sub LoadUserAbilities(user) sub LoadUserAbilities(user)