From 621e53e647dd093eebee7d594b6ab713212d10cc Mon Sep 17 00:00:00 2001
From: candry7731 <83685828+candry7731@users.noreply.github.com>
Date: Mon, 5 Sep 2022 01:50:13 -0500
Subject: [PATCH] Redesign Search page with voice search (#593)
---
components/ItemGrid/ItemGrid.brs | 146 +++++++++++++++++++-----
components/ItemGrid/ItemGrid.xml | 11 +-
components/ItemGrid/ItemGridOptions.brs | 2 +-
components/ItemGrid/LoadItemsTask2.brs | 29 ++++-
components/ItemGrid/LoadItemsTask2.xml | 1 +
components/ListPoster.brs | 12 +-
components/ListPoster.xml | 6 +
components/PersonDetails.brs | 2 +-
components/SearchBox.brs | 57 +++------
components/SearchBox.xml | 11 +-
components/config/ConfigList.brs | 9 +-
components/config/SetServerScreen.brs | 5 +-
components/data/CustomAddressKDF.json | 96 ++++++++++++++++
components/data/TVEpisodeData.xml | 1 +
components/liveTv/LoadChannelsTask.brs | 30 ++++-
components/liveTv/LoadChannelsTask.xml | 6 +-
components/liveTv/schedule.brs | 106 ++++++++++-------
components/liveTv/schedule.xml | 2 +
components/movies/MovieDetails.brs | 5 +-
components/music/NowPlaying.brs | 2 +-
components/search/SearchResults.brs | 57 +++++++++
components/search/SearchResults.xml | 23 +++-
components/search/SearchRow.brs | 25 ++--
components/search/SearchRow.xml | 7 +-
components/search/SearchTask.brs | 9 ++
components/search/SearchTask.xml | 14 +++
images/icons/mic_icon.png | Bin 0 -> 1045 bytes
locale/en_US/translations.ts | 15 +++
source/Main.brs | 27 ++++-
source/ShowScenes.brs | 8 +-
source/api/Items.brs | 56 +++++----
31 files changed, 599 insertions(+), 181 deletions(-)
create mode 100644 components/data/CustomAddressKDF.json
create mode 100644 components/search/SearchTask.brs
create mode 100644 components/search/SearchTask.xml
create mode 100644 images/icons/mic_icon.png
diff --git a/components/ItemGrid/ItemGrid.brs b/components/ItemGrid/ItemGrid.brs
index 4aaf1fd6..a637138c 100644
--- a/components/ItemGrid/ItemGrid.brs
+++ b/components/ItemGrid/ItemGrid.brs
@@ -25,7 +25,17 @@ sub init()
m.itemGrid.observeField("itemFocused", "onItemFocused")
m.itemGrid.observeField("itemSelected", "onItemSelected")
- m.itemGrid.observeField("AlphaSelected", "onItemAlphaSelected")
+ m.itemGrid.observeField("alphaSelected", "onItemalphaSelected")
+
+ 'Voice filter setup
+ m.voiceBox = m.top.findNode("voiceBox")
+ m.voiceBox.voiceEnabled = true
+ m.voiceBox.active = true
+ m.voiceBox.observeField("text", "onvoiceFilter")
+ 'set voice help text
+ m.voiceBox.hintText = tr("Use voice remote to search")
+
+ 'backdrop
m.newBackdrop.observeField("loadStatus", "newBGLoaded")
'Background Image Queued for loading
@@ -45,19 +55,35 @@ sub init()
m.spinner = m.top.findNode("spinner")
m.spinner.visible = true
+
m.Alpha = m.top.findNode("AlphaMenu")
m.AlphaSelected = m.top.findNode("AlphaSelected")
'Get reset folder setting
m.resetGrid = get_user_setting("itemgrid.reset") = "true"
+
+ 'Check if device has voice remote
+ devinfo = CreateObject("roDeviceInfo")
+ m.deviFeature = devinfo.HasFeature("voice_remote")
+ m.micButton = m.top.findNode("micButton")
+ m.micButtonText = m.top.findNode("micButtonText")
+ 'Hide voice search if device does not have voice remote
+ if m.deviFeature = false
+ m.micButton.visible = false
+ m.micButtonText.visible = false
+ end if
end sub
'
'Load initial set of Data
sub loadInitialItems()
+ m.loadItemsTask.control = "stop"
+ m.spinner.visible = true
+
if m.top.parentItem.json.Type = "CollectionFolder" 'or m.top.parentItem.json.Type = "Folder"
m.top.HomeLibraryItem = m.top.parentItem.Id
end if
+
if m.top.parentItem.backdropUrl <> invalid
SetBackground(m.top.parentItem.backdropUrl)
end if
@@ -66,6 +92,14 @@ sub loadInitialItems()
if m.top.parentItem.collectionType = "livetv"
' Translate between app and server nomenclature
viewSetting = get_user_setting("display.livetv.landing")
+ 'Move mic to be visiable on TV Guide screen
+ if m.deviFeature = true
+ m.micButton.translation = "[1540, 92]"
+ m.micButtonText.visible = true
+ m.micButtonText.translation = "[1600,130]"
+ m.micButtonText.font.size = 22
+ m.micButtonText.text = tr("Search")
+ end if
if viewSetting = "guide"
m.view = "tvGuide"
else
@@ -112,24 +146,31 @@ sub loadInitialItems()
end if
updateTitle()
- m.loadItemsTask.nameStartsWith = m.top.AlphaSelected
+ m.loadItemsTask.nameStartsWith = m.top.alphaSelected
+ m.loadItemsTask.searchTerm = m.voiceBox.text
m.emptyText.visible = false
m.loadItemsTask.sortField = m.sortField
m.loadItemsTask.sortAscending = m.sortAscending
m.loadItemsTask.filter = m.filter
m.loadItemsTask.startIndex = 0
+
' Load Item Types
- if m.top.parentItem.collectionType = "movies"
+ if getCollectionType() = "movies"
m.loadItemsTask.itemType = "Movie"
m.loadItemsTask.itemId = m.top.parentItem.Id
- else if m.top.parentItem.collectionType = "tvshows"
+ else if getCollectionType() = "tvshows"
m.loadItemsTask.itemType = "Series"
m.loadItemsTask.itemId = m.top.parentItem.Id
- else if m.top.parentItem.collectionType = "music"
+ else if getCollectionType() = "music"
' Default Settings
- m.loadItemsTask.recursive = false
- m.itemGrid.itemSize = "[290, 290]"
- m.itemGrid.itemSpacing = "[ 0, 20]"
+
+ if m.voiceBox.text <> ""
+ m.loadItemsTask.recursive = true
+ else
+ m.loadItemsTask.recursive = false
+ m.itemGrid.itemSize = "[290, 290]"
+ end if
+
m.loadItemsTask.itemType = "MusicArtist,MusicAlbum"
m.loadItemsTask.itemId = m.top.parentItem.Id
@@ -143,19 +184,21 @@ sub loadInitialItems()
m.loadItemsTask.recursive = true
end if
else if m.top.parentItem.collectionType = "livetv"
- m.loadItemsTask.itemType = "LiveTV"
-
+ m.loadItemsTask.itemType = "TvChannel"
+ m.loadItemsTask.itemId = " "
' For LiveTV, we want to "Fit" the item images, not zoom
m.top.imageDisplayMode = "scaleToFit"
if get_user_setting("display.livetv.landing") = "guide" and m.options.view <> "livetv"
showTvGuide()
end if
- else if m.top.parentItem.collectionType = "CollectionFolder" or m.top.parentItem.type = "CollectionFolder" or m.top.parentItem.collectionType = "boxsets" or m.top.parentItem.Type = "Boxset" or m.top.parentItem.Type = "Folder" or m.top.parentItem.Type = "Channel"
- ' Non-recursive, to not show subfolder contents
- m.loadItemsTask.recursive = false
- else if m.top.parentItem.Type = "Channel"
- m.top.imageDisplayMode = "scaleToFit"
+ else if m.top.parentItem.collectionType = "CollectionFolder" or m.top.parentItem.type = "CollectionFolder" or m.top.parentItem.collectionType = "boxsets" or m.top.parentItem.Type = "Boxset" or m.top.parentItem.Type = "Boxsets" or m.top.parentItem.Type = "Folder" or m.top.parentItem.Type = "Channel"
+ if m.voiceBox.text <> ""
+ m.loadItemsTask.recursive = true
+ else
+ ' non recursive for collections (folders, boxsets, photo albums, etc)
+ m.loadItemsTask.recursive = false
+ end if
else if m.top.parentItem.json.type = "Studio"
m.loadItemsTask.itemId = m.top.parentItem.parentFolder
m.loadItemsTask.itemType = "Series,Movie"
@@ -491,13 +534,33 @@ sub onItemSelected()
m.top.selectedItem = m.itemGrid.content.getChild(m.itemGrid.itemSelected)
end sub
-sub onItemAlphaSelected()
- m.loadedRows = 0
- m.loadedItems = 0
- m.data = CreateObject("roSGNode", "ContentNode")
- m.itemGrid.content = m.data
- m.spinner.visible = true
- loadInitialItems()
+sub onItemalphaSelected()
+ if m.top.alphaSelected <> ""
+ m.loadedRows = 0
+ m.loadedItems = 0
+ m.data = CreateObject("roSGNode", "ContentNode")
+ m.itemGrid.content = m.data
+ m.loadItemsTask.searchTerm = ""
+ m.VoiceBox.text = ""
+ m.loadItemsTask.nameStartsWith = m.alpha.itemAlphaSelected
+ m.spinner.visible = true
+ loadInitialItems()
+ end if
+end sub
+
+sub onvoiceFilter()
+ if m.VoiceBox.text <> ""
+ m.loadedRows = 0
+ m.loadedItems = 0
+ m.data = CreateObject("roSGNode", "ContentNode")
+ m.itemGrid.content = m.data
+ m.top.alphaSelected = ""
+ m.loadItemsTask.NameStartsWith = " "
+ m.loadItemsTask.searchTerm = m.voiceBox.text
+ m.loadItemsTask.recursive = true
+ m.spinner.visible = true
+ loadInitialItems()
+ end if
end sub
@@ -598,6 +661,7 @@ sub optionsClosed()
if m.tvGuide <> invalid
m.tvGuide.lastFocus.setFocus(true)
end if
+
end sub
sub showTVGuide()
@@ -608,6 +672,7 @@ sub showTVGuide()
m.tvGuide.observeField("focusedChannel", "onChannelFocused")
end if
m.tvGuide.filter = m.filter
+ m.tvGuide.searchTerm = m.voiceBox.text
m.top.appendChild(m.tvGuide)
m.tvGuide.lastFocus.setFocus(true)
end sub
@@ -629,6 +694,12 @@ end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
topGrp = m.top.findNode("itemGrid")
+ searchGrp = m.top.findNode("voiceBox")
+
+ if key = "left" and searchGrp.isinFocusChain()
+ topGrp.setFocus(true)
+ searchGrp.setFocus(false)
+ end if
if key = "options"
if m.options.visible = true
m.options.visible = false
@@ -655,9 +726,13 @@ function onKeyEvent(key as string, press as boolean) as boolean
m.options.visible = false
optionsClosed()
return true
+ else
+ m.global.sceneManager.callfunc("popScene")
+ m.loadItemsTask.control = "stop"
+ return true
end if
else if key = "play" or key = "OK"
- markupGrid = m.top.getChild(2)
+ markupGrid = m.top.findNode("itemGrid")
itemToPlay = markupGrid.content.getChild(markupGrid.itemFocused)
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
@@ -673,9 +748,10 @@ function onKeyEvent(key as string, press as boolean) as boolean
else if key = "left" and topGrp.isinFocusChain()
m.top.alphaActive = true
topGrp.setFocus(false)
- alpha = m.Alpha.getChild(0).findNode("Alphamenu")
+ alpha = m.alpha.getChild(0).findNode("Alphamenu")
alpha.setFocus(true)
return true
+
else if key = "right" and m.Alpha.isinFocusChain()
m.top.alphaActive = false
m.Alpha.setFocus(false)
@@ -690,6 +766,20 @@ function onKeyEvent(key as string, press as boolean) as boolean
end if
end if
+ if key = "replay"
+ m.spinner.visible = true
+ m.loadItemsTask.searchTerm = ""
+ m.loadItemsTask.nameStartsWith = ""
+ m.voiceBox.text = ""
+ m.top.alphaSelected = ""
+ m.loadItemsTask.filter = "All"
+ m.filter = "All"
+ m.data = CreateObject("roSGNode", "ContentNode")
+ m.itemGrid.content = m.data
+ loadInitialItems()
+ return true
+ end if
+
return false
end function
@@ -699,9 +789,11 @@ sub updateTitle()
else if m.filter = "Favorites"
m.top.overhangTitle = m.top.parentItem.title + " " + tr("(Favorites)")
end if
-
- if m.top.AlphaSelected <> ""
- m.top.overhangTitle = m.top.parentItem.title + " " + tr("(Filtered)")
+ if m.voiceBox.text <> ""
+ m.top.overhangTitle = m.top.parentItem.title + tr(" (Filtered by ") + m.loadItemsTask.searchTerm + ")"
+ end if
+ if m.top.alphaSelected <> ""
+ m.top.overhangTitle = m.top.parentItem.title + tr(" (Filtered by ") + m.loadItemsTask.nameStartsWith + ")"
end if
if m.options.view = "Networks" or m.view = "Networks"
diff --git a/components/ItemGrid/ItemGrid.xml b/components/ItemGrid/ItemGrid.xml
index 40a620a6..ec528c44 100644
--- a/components/ItemGrid/ItemGrid.xml
+++ b/components/ItemGrid/ItemGrid.xml
@@ -1,7 +1,8 @@
-
+
+
-
+ drawFocusFeedback = "false" />
+
+
+
-
+
diff --git a/components/ItemGrid/ItemGridOptions.brs b/components/ItemGrid/ItemGridOptions.brs
index 64d6ecb0..87aa6b8e 100644
--- a/components/ItemGrid/ItemGridOptions.brs
+++ b/components/ItemGrid/ItemGridOptions.brs
@@ -1,7 +1,7 @@
sub init()
m.buttons = m.top.findNode("buttons")
- m.buttons.buttons = [tr("TAB_VIEW"), tr("TAB_SORT"), tr("TAB_FILTER")]
+ m.buttons.buttons = [tr("View"), tr("Sort"), tr("Filter")]
m.buttons.selectedIndex = 1
m.buttons.setFocus(true)
diff --git a/components/ItemGrid/LoadItemsTask2.brs b/components/ItemGrid/LoadItemsTask2.brs
index 6f99cdce..bf9d0312 100644
--- a/components/ItemGrid/LoadItemsTask2.brs
+++ b/components/ItemGrid/LoadItemsTask2.brs
@@ -1,9 +1,15 @@
sub init()
m.top.functionName = "loadItems"
+
+ m.top.limit = 60
+ usersettingLimit = get_user_setting("itemgrid.Limit")
+
+ if usersettingLimit <> invalid
+ m.top.limit = usersettingLimit
+ end if
end sub
sub loadItems()
-
results = []
sort_field = m.top.sortField
@@ -26,15 +32,30 @@ sub loadItems()
StudioIds: m.top.studioIds,
genreIds: m.top.genreIds
}
+
' Handle special case when getting names starting with numeral
if m.top.NameStartsWith <> ""
if m.top.NameStartsWith = "#"
- params.NameLessThan = "A"
+ if m.top.ItemType = "LiveTV" or m.top.ItemType = "TvChannel"
+ params.searchterm = "A"
+ params.append({ parentid: " " })
+ else
+ params.NameLessThan = "A"
+ end if
else
- params.NameStartsWith = m.top.nameStartsWith
+ if m.top.ItemType = "LiveTV" or m.top.ItemType = "TvChannel"
+ params.searchterm = m.top.nameStartsWith
+ params.append({ parentid: " " })
+ else
+ params.NameStartsWith = m.top.nameStartsWith
+ end if
end if
end if
+ if m.top.searchTerm <> ""
+ params.searchTerm = m.top.searchTerm
+ end if
+
filter = m.top.filter
if filter = "All" or filter = "all"
' do nothing
@@ -71,7 +92,7 @@ sub loadItems()
tmp = CreateObject("roSGNode", "MovieData")
else if item.Type = "Series"
tmp = CreateObject("roSGNode", "SeriesData")
- else if item.Type = "BoxSet"
+ else if item.Type = "BoxSet" or item.Type = "ManualPlaylistsFolder"
tmp = CreateObject("roSGNode", "CollectionData")
else if item.Type = "TvChannel"
tmp = CreateObject("roSGNode", "ChannelData")
diff --git a/components/ItemGrid/LoadItemsTask2.xml b/components/ItemGrid/LoadItemsTask2.xml
index 38cc0759..e828e6e3 100644
--- a/components/ItemGrid/LoadItemsTask2.xml
+++ b/components/ItemGrid/LoadItemsTask2.xml
@@ -12,6 +12,7 @@
+
diff --git a/components/ListPoster.brs b/components/ListPoster.brs
index e9be86cf..001f840e 100644
--- a/components/ListPoster.brs
+++ b/components/ListPoster.brs
@@ -1,6 +1,7 @@
sub init()
m.title = m.top.findNode("title")
m.staticTitle = m.top.findNode("staticTitle")
+ m.series = m.top.findNode("Series")
m.poster = m.top.findNode("poster")
m.backdrop = m.top.findNode("backdrop")
@@ -39,6 +40,8 @@ sub updateSize()
m.staticTitle.height = m.title.height
m.staticTitle.translation = m.title.translation
+ m.series.maxWidth = maxSize[0]
+
m.poster.width = int(maxSize[0]) - 4
m.poster.height = int(maxSize[1]) - m.title.height 'Set poster height to available space
@@ -54,9 +57,14 @@ sub itemContentChanged() as void
if itemData.json.lookup("Type") = "Episode" and itemData.json.IndexNumber <> invalid
m.title.text = StrI(itemData.json.IndexNumber) + ". " + m.title.text
+
+ m.series.text = itemData.json.Series
+ m.series.visible = true
+
else if itemData.json.lookup("Type") = "MusicAlbum"
m.title.font = "font:SmallestSystemFont"
m.staticTitle.font = "font:SmallestSystemFont"
+
end if
m.staticTitle.text = m.title.text
@@ -64,7 +72,7 @@ sub itemContentChanged() as void
if get_user_setting("ui.tvshows.blurunwatched") = "true"
- if itemData.json.lookup("Type") = "Episode"
+ if itemData.json.lookup("Type") = "Episode" and itemData.json.userdata <> invalid
if not itemData.json.userdata.played
imageUrl = imageUrl + "&blur=15"
end if
@@ -82,6 +90,7 @@ sub focusChanged()
if m.top.itemHasFocus = true
m.title.repeatCount = -1
+ m.series.repeatCount = -1
m.staticTitle.visible = false
m.title.visible = true
@@ -94,6 +103,7 @@ sub focusChanged()
else
m.title.repeatCount = 0
+ m.series.repeatCount = 0
m.staticTitle.visible = true
m.title.visible = false
end if
diff --git a/components/ListPoster.xml b/components/ListPoster.xml
index 990f2682..fe512c3b 100644
--- a/components/ListPoster.xml
+++ b/components/ListPoster.xml
@@ -2,6 +2,12 @@
+ 1
+ m.searchText.textEditBox.leadingEllipsis = true
+ else
+ m.searchText.textEditBox.leadingEllipsis = false
end if
-
- return false
-end function
-
-function onDialogButton()
- d = m.top.getScene().dialog
- button_text = d.buttons[d.buttonSelected]
-
- if button_text = tr("Search")
- m.top.search_value = d.text
- dismiss_dialog()
- return true
- else if button_text = tr("Cancel")
- dismiss_dialog()
- return true
- end if
-
- return false
-end function
-
-sub show_dialog()
- dialog = CreateObject("roSGNode", "KeyboardDialog")
- dialog.title = tr("Search")
- dialog.buttons = [tr("Search"), tr("Cancel")]
-
- m.top.getScene().dialog = dialog
-
- dialog.observeField("buttonselected", "onDialogButton")
-end sub
-
-sub dismiss_dialog()
- m.top.getScene().dialog.close = true
end sub
diff --git a/components/SearchBox.xml b/components/SearchBox.xml
index de79ce05..21789e95 100644
--- a/components/SearchBox.xml
+++ b/components/SearchBox.xml
@@ -1,11 +1,16 @@
-
-
+
+
-
+
+
+
+
+
+
diff --git a/components/config/ConfigList.brs b/components/config/ConfigList.brs
index 5d827d20..5d32025d 100644
--- a/components/config/ConfigList.brs
+++ b/components/config/ConfigList.brs
@@ -45,13 +45,18 @@ end function
sub show_dialog(configField)
- dialog = createObject("roSGNode", "KeyboardDialog")
+ dialog = createObject("roSGNode", "StandardKeyboardDialog")
m.configField = configField
dialog.title = "Enter the " + configField.label
dialog.buttons = [tr("OK"), tr("Cancel")]
+ m.greenPalette = createObject("roSGNode", "RSGPalette")
+ m.greenPalette.colors = {
+ DialogBackgroundColor: "#2A2B2A"
+ }
+ dialog.palette = m.greenPalette
if configField.type = "password"
- dialog.keyboard.textEditBox.secureMode = true
+ dialog.textEditBox.secureMode = true
end if
if configField.value <> ""
diff --git a/components/config/SetServerScreen.brs b/components/config/SetServerScreen.brs
index f9493808..a0b029d4 100644
--- a/components/config/SetServerScreen.brs
+++ b/components/config/SetServerScreen.brs
@@ -107,10 +107,13 @@ sub ScanForServersComplete(event)
end sub
sub ShowKeyboard()
- dialog = createObject("roSGNode", "KeyboardDialog")
+ dialog = createObject("roSGNode", "StandardKeyboardDialog")
dialog.title = tr("Enter the server name or ip address")
dialog.buttons = [tr("OK"), tr("Cancel")]
dialog.text = m.serverUrlTextbox.text
+ greenPalette = createObject("roSGNode", "RSGPalette")
+ greenPalette.colors = { DialogBackgroundColor: "#2A2B2A" }
+ dialog.palette = greenPalette
m.top.getscene().dialog = dialog
m.dialog = dialog
diff --git a/components/data/CustomAddressKDF.json b/components/data/CustomAddressKDF.json
new file mode 100644
index 00000000..72c63871
--- /dev/null
+++ b/components/data/CustomAddressKDF.json
@@ -0,0 +1,96 @@
+{
+ "keyboardWidthFHD": 374,
+ "keyboardHeightFHD": 409,
+ "keyboardWidthHD": 250,
+ "keyboardHeightHD": 273,
+ "sections": [
+ {
+ "grids": [
+ {
+ "rows": [
+ {
+ "keys": [
+ { "label": "a" },
+ { "label": "b" },
+ { "label": "c" },
+ { "label": "d" },
+ { "label": "e" },
+ { "label": "f" }
+ ]
+ },
+ {
+ "keys": [
+ { "label": "g" },
+ { "label": "h" },
+ { "label": "i" },
+ { "label": "j" },
+ { "label": "k" },
+ { "label": "l" }
+ ]
+ },
+ {
+ "keys": [
+ { "label": "m" },
+ { "label": "n" },
+ { "label": "o" },
+ { "label": "p" },
+ { "label": "q" },
+ { "label": "r" }
+ ]
+ },
+ {
+ "keys": [
+ { "label": "s" },
+ { "label": "t" },
+ { "label": "u" },
+ { "label": "v" },
+ { "label": "w" },
+ { "label": "x" }
+ ]
+ },
+ {
+ "keys": [
+ { "label": "y" },
+ { "label": "z" },
+ { "label": "1" },
+ { "label": "2" },
+ { "label": "3" },
+ { "label": "4" }
+ ]
+ },
+ {
+ "keys": [
+ { "label": "5" },
+ { "label": "6" },
+ { "label": "7" },
+ { "label": "8" },
+ { "label": "9" },
+ { "label": "0" }
+ ]
+ },
+ {
+ "keys": [
+ {
+ "icon": "theme:DKB_ClearKeyBitmap",
+ "focusIcon": "theme:DKB_ClearKeyFocusBitmap",
+ "strOut": "clear"
+ },
+ {
+ "icon": "theme:DKB_SpaceKeyBitmap",
+ "focusIcon": "theme:DKB_SpaceKeyFocusBitmap",
+ "strOut": "space"
+ },
+ {
+ "icon": "theme:DKB_DeleteKeyBitmap",
+ "focusIcon": "theme:DKB_DeleteKeyFocusBitmap",
+ "autoRepeat": 1,
+ "strOut": "backspace"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/components/data/TVEpisodeData.xml b/components/data/TVEpisodeData.xml
index 85e8bcf4..a136abce 100644
--- a/components/data/TVEpisodeData.xml
+++ b/components/data/TVEpisodeData.xml
@@ -11,6 +11,7 @@
+
diff --git a/components/liveTv/LoadChannelsTask.brs b/components/liveTv/LoadChannelsTask.brs
index 49378788..8d8cfd55 100644
--- a/components/liveTv/LoadChannelsTask.brs
+++ b/components/liveTv/LoadChannelsTask.brs
@@ -6,15 +6,41 @@ sub loadChannels()
results = []
+ sort_field = m.top.sortField
+
+ if m.top.sortAscending = true
+ sort_order = "Ascending"
+ else
+ sort_order = "Descending"
+ end if
+
params = {
+ includeItemTypes: "LiveTvChannel",
+ SortBy: sort_field,
+ SortOrder: sort_order,
+ recursive: m.top.recursive,
UserId: get_setting("active_user")
}
+ ' Handle special case when getting names starting with numeral
+ if m.top.NameStartsWith <> ""
+ if m.top.NameStartsWith = "#"
+ params.searchterm = "A"
+ else
+ params.searchterm = m.top.nameStartsWith
+ end if
+ end if
+
+ 'Append voice search when there is text
+ if m.top.searchTerm <> ""
+ params.searchTerm = m.top.searchTerm
+ end if
+
if m.top.filter = "Favorites"
params.append({ isFavorite: true })
end if
- url = "LiveTv/Channels"
+ url = Substitute("Users/{0}/Items/", get_setting("active_user"))
resp = APIRequest(url, params)
data = getJson(resp)
@@ -39,7 +65,5 @@ sub loadChannels()
results.push(channel)
end if
end for
-
m.top.channels = results
-
end sub
diff --git a/components/liveTv/LoadChannelsTask.xml b/components/liveTv/LoadChannelsTask.xml
index b0318bcd..334eecc2 100644
--- a/components/liveTv/LoadChannelsTask.xml
+++ b/components/liveTv/LoadChannelsTask.xml
@@ -5,7 +5,11 @@
-
+
+
+
+
+
diff --git a/components/liveTv/schedule.brs b/components/liveTv/schedule.brs
index cea0cebc..870bdaa9 100644
--- a/components/liveTv/schedule.brs
+++ b/components/liveTv/schedule.brs
@@ -1,5 +1,4 @@
sub init()
-
m.EPGLaunchCompleteSignaled = false
m.scheduleGrid = m.top.findNode("scheduleGrid")
m.detailsPane = m.top.findNode("detailsPane")
@@ -7,7 +6,6 @@ sub init()
m.detailsPane.observeField("watchSelectedChannel", "onWatchChannelSelected")
m.detailsPane.observeField("recordSelectedChannel", "onRecordChannelSelected")
m.detailsPane.observeField("recordSeriesSelectedChannel", "onRecordSeriesChannelSelected")
-
m.gridStartDate = CreateObject("roDateTime")
m.scheduleGrid.contentStartTime = m.gridStartDate.AsSeconds() - 1800
m.gridEndDate = createObject("roDateTime")
@@ -28,10 +26,11 @@ sub init()
m.top.lastFocus = m.scheduleGrid
m.channelIndex = {}
+
+ m.spinner = m.top.findNode("spinner")
end sub
sub channelFilterSet()
- print "Channel Filter set"
m.scheduleGrid.jumpToChannel = 0
if m.top.filter <> invalid and m.LoadChannelsTask.filter <> m.top.filter
if m.LoadChannelsTask.state = "run" then m.LoadChannelsTask.control = "stop"
@@ -42,6 +41,19 @@ sub channelFilterSet()
end sub
+'Voice Search set
+sub channelsearchTermSet()
+ m.scheduleGrid.jumpToChannel = 0
+ if m.top.searchTerm <> invalid and m.LoadChannelsTask.searchTerm <> m.top.searchTerm
+ if m.LoadChannelsTask.state = "run" then m.LoadChannelsTask.control = "stop"
+
+ m.LoadChannelsTask.searchTerm = m.top.searchTerm
+ m.spinner.visible = true
+ m.LoadChannelsTask.control = "RUN"
+ end if
+
+end sub
+
' Initial list of channels loaded
sub onChannelsLoaded()
gridData = createObject("roSGNode", "ContentNode")
@@ -49,32 +61,36 @@ sub onChannelsLoaded()
counter = 0
channelIdList = ""
- for each item in m.LoadChannelsTask.channels
- gridData.appendChild(item)
- m.channelIndex[item.Id] = counter
- counter = counter + 1
- channelIdList = channelIdList + item.Id + ","
- end for
+ 'if search returns channels
+ if m.LoadChannelsTask.channels.count() > 0
+ for each item in m.LoadChannelsTask.channels
+ gridData.appendChild(item)
+ m.channelIndex[item.Id] = counter
+ counter = counter + 1
+ channelIdList = channelIdList + item.Id + ","
+ end for
+ m.scheduleGrid.content = gridData
- m.scheduleGrid.content = gridData
+ m.LoadScheduleTask = createObject("roSGNode", "LoadScheduleTask")
+ m.LoadScheduleTask.observeField("schedule", "onScheduleLoaded")
- m.LoadScheduleTask = createObject("roSGNode", "LoadScheduleTask")
- m.LoadScheduleTask.observeField("schedule", "onScheduleLoaded")
+ m.LoadScheduleTask.startTime = m.gridStartDate.ToISOString()
+ m.LoadScheduleTask.endTime = m.gridEndDate.ToISOString()
+ m.LoadScheduleTask.channelIds = channelIdList
+ m.LoadScheduleTask.control = "RUN"
- m.LoadScheduleTask.startTime = m.gridStartDate.ToISOString()
- m.LoadScheduleTask.endTime = m.gridEndDate.ToISOString()
- m.LoadScheduleTask.channelIds = channelIdList
- m.LoadScheduleTask.control = "RUN"
+ m.LoadProgramDetailsTask = createObject("roSGNode", "LoadProgramDetailsTask")
+ m.LoadProgramDetailsTask.observeField("programDetails", "onProgramDetailsLoaded")
- m.LoadProgramDetailsTask = createObject("roSGNode", "LoadProgramDetailsTask")
- m.LoadProgramDetailsTask.observeField("programDetails", "onProgramDetailsLoaded")
+ m.scheduleGrid.setFocus(true)
+ if m.EPGLaunchCompleteSignaled = false
+ m.top.signalBeacon("EPGLaunchComplete") ' Required Roku Performance monitoring
+ m.EPGLaunchCompleteSignaled = true
+ end if
+ m.LoadChannelsTask.channels = []
- m.scheduleGrid.setFocus(true)
- if m.EPGLaunchCompleteSignaled = false
- m.top.signalBeacon("EPGLaunchComplete") ' Required Roku Performance monitoring
- m.EPGLaunchCompleteSignaled = true
end if
- m.LoadChannelsTask.channels = []
+
end sub
' When LoadScheduleTask completes (initial or more data) and we have a schedule to display
@@ -102,6 +118,7 @@ sub onScheduleLoaded()
m.scheduleGrid.showLoadingDataFeedback = false
m.scheduleGrid.setFocus(true)
m.LoadScheduleTask.schedule = []
+ m.spinner.visible = false
end sub
sub onProgramFocused()
@@ -118,7 +135,9 @@ sub onProgramFocused()
m.top.focusedChannel = channel
' Exit if Channels not yet loaded
+
if channel = invalid or channel.getChildCount() = 0
+
m.detailsPane.programDetails = invalid
return
end if
@@ -195,6 +214,22 @@ sub onWatchChannelSelected()
m.top.watchChannel = m.detailsPane.channel
end sub
+' As user scrolls grid, check if more data requries to be loaded
+sub onGridScrolled()
+
+ ' If we're within 12 hours of end of grid, load next 24hrs of data
+ if m.scheduleGrid.leftEdgeTargetTime + (12 * 60 * 60) > m.gridEndDate.AsSeconds()
+
+ ' Ensure the task is not already (still) running,
+ if m.LoadScheduleTask.state <> "run"
+ m.LoadScheduleTask.startTime = m.gridEndDate.ToISOString()
+ m.gridEndDate.FromSeconds(m.gridEndDate.AsSeconds() + (24 * 60 * 60))
+ m.LoadScheduleTask.endTime = m.gridEndDate.ToISOString()
+ m.LoadScheduleTask.control = "RUN"
+ end if
+ end if
+end sub
+
' Handle user selecting "Record Channel" from Program Details
sub onRecordChannelSelected()
if m.detailsPane.recordSelectedChannel = false then return
@@ -242,27 +277,18 @@ sub onRecordOperationDone()
end if
end sub
-' As user scrolls grid, check if more data requries to be loaded
-sub onGridScrolled()
-
- ' If we're within 12 hours of end of grid, load next 24hrs of data
- if m.scheduleGrid.leftEdgeTargetTime + (12 * 60 * 60) > m.gridEndDate.AsSeconds()
-
- ' Ensure the task is not already (still) running,
- if m.LoadScheduleTask.state <> "run"
- m.LoadScheduleTask.startTime = m.gridEndDate.ToISOString()
- m.gridEndDate.FromSeconds(m.gridEndDate.AsSeconds() + (24 * 60 * 60))
- m.LoadScheduleTask.endTime = m.gridEndDate.ToISOString()
- m.LoadScheduleTask.control = "RUN"
- end if
- end if
-end sub
-
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
+ detailsGrp = m.top.findNode("detailsPane")
+ gridGrp = m.top.findNode("scheduleGrid")
- if key = "back" and m.detailsPane.isInFocusChain()
+ if key = "back" and detailsGrp.isInFocusChain()
focusProgramDetails(false)
+ detailsGrp.setFocus(false)
+ gridGrp.setFocus(true)
+ return true
+ else if key = "back"
+ m.global.sceneManager.callFunc("popScene")
return true
end if
diff --git a/components/liveTv/schedule.xml b/components/liveTv/schedule.xml
index 9e669bf9..83a43047 100644
--- a/components/liveTv/schedule.xml
+++ b/components/liveTv/schedule.xml
@@ -12,6 +12,7 @@
programTitleFocusedColor="#ffffff" iconFocusedColor="#ff0000"
showPastTimeScreen="true" pastTimeScreenBlendColor="#555555"
/>
+
@@ -20,6 +21,7 @@
+
diff --git a/components/movies/MovieDetails.brs b/components/movies/MovieDetails.brs
index 8576b4d8..837b35be 100644
--- a/components/movies/MovieDetails.brs
+++ b/components/movies/MovieDetails.brs
@@ -46,8 +46,9 @@ sub itemContentChanged()
m.top.findNode("moviePoster").uri = m.top.itemContent.posterURL
' Set default video source
- m.top.selectedVideoStreamId = itemData.MediaSources[0].id
-
+ if itemData.MediaSources <> invalid
+ m.top.selectedVideoStreamId = itemData.MediaSources[0].id
+ end if
' Find first Audio Stream and set that as default
SetDefaultAudioTrack(itemData)
diff --git a/components/music/NowPlaying.brs b/components/music/NowPlaying.brs
index 00c7fd90..f2d0a277 100644
--- a/components/music/NowPlaying.brs
+++ b/components/music/NowPlaying.brs
@@ -408,7 +408,7 @@ sub onMetaDataLoaded()
if data <> invalid and data.count() > 0
' Use metadata to load backdrop image
- if isvalid(data?.json?.ArtistItems?[0].id)
+ if isvalid(data?.json?.ArtistItems?[0]?.id)
m.LoadBackdropImageTask.itemId = data.json.ArtistItems[0].id
m.LoadBackdropImageTask.observeField("content", "onBackdropImageLoaded")
m.LoadBackdropImageTask.control = "RUN"
diff --git a/components/search/SearchResults.brs b/components/search/SearchResults.brs
index 498f96ec..8fb9ba3c 100644
--- a/components/search/SearchResults.brs
+++ b/components/search/SearchResults.brs
@@ -1,3 +1,60 @@
sub init()
m.top.optionsAvailable = false
+ m.searchSpinner = m.top.findnode("searchSpinner")
+ m.searchSelect = m.top.findnode("searchSelect")
+ m.searchTask = CreateObject("roSGNode", "SearchTask")
+
+ 'set label text
+ m.searchHelpText = m.top.findNode("SearchHelpText")
+ m.searchHelpText.text = tr("You can search for Titles, People, Live TV Channels and more")
+
end sub
+
+sub searchMedias()
+ query = m.top.searchAlpha
+ 'if user deletes the search string hide the spinner
+ if query.len() = 0
+ m.searchSpinner.visible = false
+ end if
+ 'if search task is running and user selectes another letter stop the search and load the next letter
+ m.searchTask.control = "stop"
+ if query <> invalid and query <> ""
+ m.searchSpinner.visible = true
+ end if
+ m.searchTask.observeField("results", "loadResults")
+ m.searchTask.query = query
+ m.top.overhangTitle = tr("Search") + ": " + query
+ m.searchTask.control = "RUN"
+
+end sub
+
+sub loadResults()
+ m.searchTask.unobserveField("results")
+
+ m.searchSpinner.visible = false
+ m.searchSelect.itemdata = m.searchTask.results
+ m.searchSelect.query = m.top.SearchAlpha
+ m.searchHelpText.visible = false
+ m.searchAlphabox = m.top.findnode("searchResults")
+ m.searchAlphabox.translation = "[470, 85]"
+end sub
+
+function onKeyEvent(key as string, press as boolean) as boolean
+
+ m.searchAlphabox = m.top.findNode("search_Key")
+ if m.searchAlphabox.textEditBox.hasFocus()
+ m.searchAlphabox.textEditBox.translation = "[0, -150]"
+ else
+ m.searchAlphabox.textEditBox.translation = "[0, 0]"
+ end if
+
+ if key = "left" and m.searchSelect.isinFocusChain() and (m.searchSelect.currFocusColumn = -1 or m.searchSelect.currFocusColumn = 0)
+ m.searchAlphabox.setFocus(true)
+ return true
+ else if key = "right"
+ m.searchSelect.setFocus(true)
+ return true
+ end if
+ return false
+
+end function
diff --git a/components/search/SearchResults.xml b/components/search/SearchResults.xml
index cd5b97d3..4cba8afd 100644
--- a/components/search/SearchResults.xml
+++ b/components/search/SearchResults.xml
@@ -1,13 +1,28 @@
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/search/SearchRow.brs b/components/search/SearchRow.brs
index 78fc181c..61d9bf8c 100644
--- a/components/search/SearchRow.brs
+++ b/components/search/SearchRow.brs
@@ -11,27 +11,28 @@ sub init()
' TODO - Define a failed to load image background
' m.top.failedBitmapURI
- m.top.setFocus(true)
end sub
sub updateSize()
' In search results, rowSize only dictates how many are on screen at once
- m.top.rowSize = 5
+ m.top.rowSize = 3
dimensions = m.top.getScene().currentDesignResolution
- border = 75
+ border = 50
m.top.translation = [border, border + 115]
textHeight = 80
- itemWidth = (dimensions["width"] - border * 2) / m.top.rowSize
- itemHeight = itemWidth * 1.5 + textHeight
+ itemWidth = (dimensions["width"] - border) / 6
+ itemHeight = itemWidth + (textHeight / 2.3)
- m.top.itemSize = [dimensions["width"] - border * 2, itemHeight]
- m.top.itemSpacing = [0, 50]
+ m.top.itemSize = [1350, itemHeight] ' this is used for setting the row size
+ m.top.itemSpacing = [0, 105]
m.top.rowItemSize = [itemWidth, itemHeight]
m.top.rowItemSpacing = [0, 0]
+ m.top.numRows = 2
+ m.top.translation = "[12,18]"
end sub
function getData()
@@ -45,16 +46,17 @@ function getData()
' todo - Or get the old data? I can't remember...
data = CreateObject("roSGNode", "ContentNode")
' Do this to keep the ordering, AssociateArrays have no order
- type_array = ["Movie", "Series", "TvChannel", "Episode", "AlbumArtist", "Album", "Audio", "Person"]
+ type_array = ["Movie", "Series", "TvChannel", "Episode", "MusicArtist", "MusicAlbum", "Audio", "Person", "PlaylistsFolder"]
content_types = {
"TvChannel": { "label": "Channels", "count": 0 },
"Movie": { "label": "Movies", "count": 0 },
"Series": { "label": "Shows", "count": 0 },
"Episode": { "label": "Episodes", "count": 0 },
- "AlbumArtist": { "label": "Artists", "count": 0 },
- "Album": { "label": "Albums", "count": 0 },
+ "MusicArtist": { "label": "Artists", "count": 0 },
+ "MusicAlbum": { "label": "Albums", "count": 0 },
"Audio": { "label": "Songs", "count": 0 },
- "Person": { "label": "People", "count": 0 }
+ "Person": { "label": "People", "count": 0 },
+ "PlaylistsFolder": { "label": "Playlist", "count": 0 }
}
for each item in itemData.searchHints
@@ -84,3 +86,4 @@ sub addRow(data, title, type_filter)
end if
end for
end sub
+
diff --git a/components/search/SearchRow.xml b/components/search/SearchRow.xml
index 81d77815..f6ab9be1 100644
--- a/components/search/SearchRow.xml
+++ b/components/search/SearchRow.xml
@@ -4,7 +4,12 @@
-
+
+
+
+
+
+
diff --git a/components/search/SearchTask.brs b/components/search/SearchTask.brs
new file mode 100644
index 00000000..ddf8b6aa
--- /dev/null
+++ b/components/search/SearchTask.brs
@@ -0,0 +1,9 @@
+sub init()
+ m.top.functionName = "search"
+end sub
+
+sub search()
+ if m.top.query <> invalid and m.top.query <> ""
+ m.top.results = searchMedia(m.top.query)
+ end if
+end sub
diff --git a/components/search/SearchTask.xml b/components/search/SearchTask.xml
new file mode 100644
index 00000000..400e4c82
--- /dev/null
+++ b/components/search/SearchTask.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/images/icons/mic_icon.png b/images/icons/mic_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ce18223b0ff20e3aa1060d54b2ce1eceef2c8f4
GIT binary patch
literal 1045
zcmeAS@N?(olHy`uVBq!ia0vp^{6H+m!3-odE}CfrDVB6cUq=Rpjs4tz5?O(Kg=CK)
zUj~LMH3o);76yi2K%s^g3=E|P3=FRl7#OT(FffQ0%-I!a!@$5;9^ez=3RLjt-=9%L
z&k*?k|9|YoG9zHnSC<6&1v4=4l->J%zlM*sL-6tWd
vF{#0`sDHnZ%uZ<-_y$*E4c{6zjY-(cL&XpGig?=nTdr
zZ+DlPi`x|9fgJV{PhVH|XY3Np0%{x2-dF{a&oA-|VLZR6d=`++?&;zfB5~RF+)L#{
z1u|?O-a99so4SQh^k|U}`>`!5;qSE1OpHEJ`}E)6&$puHg`a<3`BgwLAUON+t5=l`
zClZ{KgNwaaCzQ`%zVIxkyfd%=diu`hpUfO(oAgdAioJ8|KcW=+{oBrq_6jGbu}4~T
z%{b!2+@b!+SwgN*>uH{ak=3%k)tz47%~>e6!A4L$w-%W6YCer!8!ud4Rs>HRq`
zl~${9{bF`sX3zRgQEW3Xd{j$ZBT7;dOH!?pi&B9UgOP!ek*Go to episode
Continue Watching Popup Menu - Navigate to the Episode Detail Page
+
+
+ Use voice remote to search
+ Help text in search voice text box
+
+
+
+ Search now
+ Help text in search Box
+
+
+
+ You can search for Titles, People, Live TV Channels and more
+ Help text in search results
+ %1 of %2
diff --git a/source/Main.brs b/source/Main.brs
index 272964e5..1fe9c793 100644
--- a/source/Main.brs
+++ b/source/Main.brs
@@ -250,8 +250,29 @@ sub Main (args as dynamic) as void
' types: [ Series (Show), Episode, Movie, Audio, Person, Studio, MusicArtist ]
if node.type = "Series"
group = CreateSeriesDetailsGroup(node)
- else
+ else if node.type = "Movie"
group = CreateMovieDetailsGroup(node)
+ else if node.type = "MusicArtist"
+ group = CreateArtistView(node.json)
+ else if node.type = "MusicAlbum"
+ group = CreateAlbumView(node.json)
+ else if node.type = "Audio"
+ group = CreateAudioPlayerGroup([node.json])
+ else if node.type = "Person"
+ group = CreatePersonView(node)
+ else if node.type = "TvChannel"
+ group = CreateVideoPlayerGroup(node.id)
+ sceneManager.callFunc("pushScene", group)
+ else if node.type = "Episode"
+ group = CreateVideoPlayerGroup(node.id)
+ sceneManager.callFunc("pushScene", group)
+ else if node.type = "Audio"
+ selectedIndex = msg.getData()
+ screenContent = msg.getRoSGNode()
+ group = CreateAudioPlayerGroup([screenContent.albumData.items[node.id]])
+ else
+ ' TODO - switch on more node types
+ message_dialog("This type is not yet supported: " + node.type + ".")
end if
else if isNodeEvent(msg, "buttonSelected")
' If a button is selected, we have some determining to do
@@ -334,8 +355,8 @@ sub Main (args as dynamic) as void
end if
group = CreateSearchPage()
sceneManager.callFunc("pushScene", group)
- group.findNode("SearchBox").findNode("search-input").setFocus(true)
- group.findNode("SearchBox").findNode("search-input").active = true
+ group.findNode("SearchBox").findNode("search_Key").setFocus(true)
+ group.findNode("SearchBox").findNode("search_Key").active = true
else if button.id = "change_server"
unset_setting("server")
unset_setting("port")
diff --git a/source/ShowScenes.brs b/source/ShowScenes.brs
index 1f9123d3..cee0cb8d 100644
--- a/source/ShowScenes.brs
+++ b/source/ShowScenes.brs
@@ -446,12 +446,8 @@ end function
function CreateSearchPage()
' Search + Results Page
- group = CreateObject("roSGNode", "SearchResults")
-
- search = group.findNode("SearchBox")
- search.observeField("search_value", m.port)
-
- options = group.findNode("SearchSelect")
+ group = CreateObject("roSGNode", "searchResults")
+ options = group.findNode("searchSelect")
options.observeField("itemSelected", m.port)
return group
diff --git a/source/api/Items.brs b/source/api/Items.brs
index accc46df..0590bd1b 100644
--- a/source/api/Items.brs
+++ b/source/api/Items.brs
@@ -34,34 +34,40 @@ function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioT
end function
' Search across all libraries
-function SearchMedia(query as string)
+function searchMedia(query as string)
' This appears to be done differently on the web now
' For each potential type, a separate query is done:
' varying item types, and artists, and people
- resp = APIRequest(Substitute("Users/{0}/Items", get_setting("active_user")), {
- "searchTerm": query,
- "IncludePeople": true,
- "IncludeMedia": true,
- "IncludeShows": true,
- "IncludeGenres": false,
- "IncludeStudios": false,
- "IncludeArtists": false,
- "IncludeItemTypes": "TvChannel,Movie,BoxSet,Series,Episode,Video",
- "EnableTotalRecordCount": false,
- "ImageTypeLimit": 1,
- "Recursive": true
- })
- data = getJson(resp)
- results = []
- for each item in data.Items
- tmp = CreateObject("roSGNode", "SearchData")
- tmp.image = PosterImage(item.id)
- tmp.json = item
- results.push(tmp)
- end for
- data.SearchHints = results
- return data
+ if query <> ""
+ resp = APIRequest(Substitute("Search/Hints", get_setting("active_user")), {
+ "searchTerm": query,
+ "IncludePeople": true,
+ "IncludeMedia": true,
+ "IncludeShows": true,
+ "IncludeGenres": true,
+ "IncludeStudios": true,
+ "IncludeArtists": true,
+ "IncludeItemTypes": "LiveTvChannel,Movie,BoxSet,Series,Episode,Video,Person,Audio,MusicAlbum,MusicArtist,Playlist",
+ "EnableTotalRecordCount": false,
+ "ImageTypeLimit": 1,
+ "Recursive": true,
+ "limit": 100
+ })
+
+
+ data = getJson(resp)
+ results = []
+ for each item in data.SearchHints
+ tmp = CreateObject("roSGNode", "SearchData")
+ tmp.image = PosterImage(item.id)
+ tmp.json = item
+ results.push(tmp)
+ end for
+ data.SearchHints = results
+ return data
+ end if
+ return []
end function
' MetaData about an item
@@ -94,7 +100,7 @@ function ItemMetaData(id as string)
tmp.image = PosterImage(data.id, imgParams)
tmp.json = data
return tmp
- else if data.type = "BoxSet"
+ else if data.type = "BoxSet" or data.type = "Playlist"
tmp = CreateObject("roSGNode", "CollectionData")
tmp.image = PosterImage(data.id, imgParams)
tmp.json = data