diff --git a/components/config/data.xml b/components/config/data.xml
new file mode 100644
index 00000000..1a798b65
--- /dev/null
+++ b/components/config/data.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/components/config/item.xml b/components/config/item.xml
new file mode 100644
index 00000000..bf774eab
--- /dev/null
+++ b/components/config/item.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/config/list.xml b/components/config/list.xml
new file mode 100644
index 00000000..ea5404cb
--- /dev/null
+++ b/components/config/list.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
diff --git a/components/config/scene.brs b/components/config/scene.brs
new file mode 100644
index 00000000..fbd76825
--- /dev/null
+++ b/components/config/scene.brs
@@ -0,0 +1,58 @@
+sub init()
+ m.top.backgroundColor = "#000b35ff"
+ m.top.backgroundURI = ""
+
+ m.top.setFocus(true)
+end sub
+
+function onKeyEvent(key as String, press as Boolean) as Boolean
+ if not press then return false
+ list = m.top.findNode("configOptions")
+ button = m.top.findNode("submit")
+ if key = "down" and button.focusedChild = invalid
+ limit = list.content.getChildren(-1, 0).count() - 1
+
+ if limit = list.itemFocused
+ m.top.setFocus(false)
+ button.setFocus(true)
+ return true
+ end if
+ else if key = "up" and button.focusedChild <> invalid
+ list.setFocus(true)
+ return true
+ end if
+ return false
+end function
+
+function onDialogButton()
+ d = m.top.dialog
+ button_text = d.buttons[d.buttonSelected]
+
+ if button_text = "OK"
+ m.focused_item.text = d.text
+ dismiss_dialog()
+ return true
+ else if button_text = "Cancel"
+ dismiss_dialog()
+ return false
+ end if
+end function
+
+
+sub show_dialog(title as String)
+ dialog = createObject("roSGNode", "KeyboardDialog")
+ dialog.title = "Enter the " + m.focused_item.id
+ dialog.buttons = ["OK", "Cancel"]
+
+ if m.focused_item.text <> "" then
+ dialog.text = m.focused_item.text
+ end if
+
+ m.top.dialog = dialog
+
+ dialog.observeField("buttonSelected", "onDialogButton")
+end sub
+
+sub dismiss_dialog()
+ m.top.dialog.close = true
+end sub
diff --git a/components/config/scene.xml b/components/config/scene.xml
new file mode 100644
index 00000000..cfc8a8eb
--- /dev/null
+++ b/components/config/scene.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/library/data.xml b/components/library/data.xml
new file mode 100644
index 00000000..dcf7e3be
--- /dev/null
+++ b/components/library/data.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/library/item.xml b/components/library/item.xml
new file mode 100644
index 00000000..d8998261
--- /dev/null
+++ b/components/library/item.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/library/rowlist.xml b/components/library/rowlist.xml
new file mode 100644
index 00000000..a3178c46
--- /dev/null
+++ b/components/library/rowlist.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
diff --git a/components/library/scene.xml b/components/library/scene.xml
new file mode 100644
index 00000000..a535716e
--- /dev/null
+++ b/components/library/scene.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/movies/data.xml b/components/movies/data.xml
new file mode 100644
index 00000000..08e2e1a1
--- /dev/null
+++ b/components/movies/data.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/movies/details.brs b/components/movies/details.brs
new file mode 100644
index 00000000..7f8effa1
--- /dev/null
+++ b/components/movies/details.brs
@@ -0,0 +1,114 @@
+sub itemContentChanged()
+ itemData = m.top.itemContent.full_data
+
+ m.top.findNode("moviePoster").uri = m.top.itemContent.posterURL
+
+ ' Handle all "As Is" fields
+ setFieldText("title", itemData.name)
+ setFieldText("releaseYear", itemData.productionYear)
+ setFieldText("officialRating", itemData.officialRating)
+ setFieldText("communityRating", str(itemData.communityRating))
+ setFieldText("overview", itemData.overview)
+
+ setFieldText("runtime", stri(getRuntime()) + " mins")
+ setFieldText("ends-at", "Ends at " + getEndTime())
+
+ if itemData.genres.count() > 0
+ setFieldText("genres", itemData.genres.join(", "))
+ end if
+ director = invalid
+ for each person in itemData.people
+ if person.type = "Director"
+ director = person.name
+ exit for
+ end if
+ end for
+ if director <> invalid
+ setFieldText("director", "Director: " + director)
+ end if
+ setFieldText("video_codec", "Video: " + itemData.mediaStreams[0].displayTitle)
+ setFieldText("audio_codec", "Audio: " + itemData.mediaStreams[1].displayTitle)
+ ' TODO - cmon now. these are buttons, not words
+ if itemData.taglines.count() > 0
+ setFieldText("tagline", itemData.taglines[0])
+ end if
+ setFavoriteColor()
+ setWatchedColor()
+end sub
+
+sub setFieldText(field as string, value)
+ node = m.top.findNode(field)
+ if node = invalid then return
+
+ node.text = value
+end sub
+
+function getRuntime() as Integer
+ itemData = m.top.itemContent.full_data
+
+ ' A tick is .1ms, so 1/10,000,000 for ticks to seconds,
+ ' then 1/60 for seconds to minutess... 1/600,000,000
+ return round(itemData.RunTimeTicks / 600000000.0)
+end function
+
+function getEndTime() as string
+ itemData = m.top.itemContent.full_data
+
+ date = CreateObject("roDateTime")
+ duration_s = int(itemData.RunTimeTicks / 10000000.0)
+ date.fromSeconds(date.asSeconds() + duration_s)
+ date.toLocalTime()
+ hours = date.getHours()
+ meridian = "AM"
+ if hours = 0
+ hours = 12
+ meridian = "AM"
+ else if hours = 12
+ hours = 12
+ meridian = "PM"
+ else if hours > 12
+ hours = hours - 12
+ meridian = "PM"
+ end if
+
+ return Substitute("{0}:{1} {2}", stri(hours).trim(), stri(date.getMinutes()).trim(), meridian)
+end function
+
+sub setFavoriteColor()
+ fave = m.top.itemContent.favorite
+ fave_button = m.top.findNode("favorite-button")
+ if fave
+ fave_button.textColor = "#00ff00ff"
+ fave_button.focusedTextColor = "#269926ff"
+ else
+ fave_button.textColor = "0xddddddff"
+ fave_button.focusedTextColor = "#262626ff"
+ end if
+end sub
+
+sub setWatchedColor()
+ watched = m.top.itemContent.watched
+ watched_button = m.top.findNode("watched-button")
+ if watched
+ watched_button.textColor = "#ff0000ff"
+ watched_button.focusedTextColor = "#992626ff"
+ else
+ watched_button.textColor = "0xddddddff"
+ watched_button.focusedTextColor = "#262626ff"
+ end if
+end sub
+
+function round(f as Float) as Integer
+ ' BrightScript only has a "floor" round
+ ' This compares floor to floor + 1 to find which is closer
+ m = int(f)
+ n = m + 1
+ x = abs(f - m)
+ y = abs(f - n)
+ if y > x
+ return m
+ else
+ return n
+ end if
+end function
+
diff --git a/components/movies/details.xml b/components/movies/details.xml
new file mode 100644
index 00000000..52267774
--- /dev/null
+++ b/components/movies/details.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/movies/minimal-details.xml b/components/movies/minimal-details.xml
new file mode 100644
index 00000000..260f7950
--- /dev/null
+++ b/components/movies/minimal-details.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/movies/rowlist.xml b/components/movies/rowlist.xml
new file mode 100644
index 00000000..7190bec8
--- /dev/null
+++ b/components/movies/rowlist.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/movies/scene.xml b/components/movies/scene.xml
new file mode 100644
index 00000000..e67aa9c9
--- /dev/null
+++ b/components/movies/scene.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/components/search.xml b/components/search.xml
new file mode 100644
index 00000000..40863b6a
--- /dev/null
+++ b/components/search.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
diff --git a/components/tvshows/data-season.xml b/components/tvshows/data-season.xml
new file mode 100644
index 00000000..8a05df1d
--- /dev/null
+++ b/components/tvshows/data-season.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/tvshows/data-show.xml b/components/tvshows/data-show.xml
new file mode 100644
index 00000000..67d464b7
--- /dev/null
+++ b/components/tvshows/data-show.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/tvshows/details.brs b/components/tvshows/details.brs
new file mode 100644
index 00000000..818fc91b
--- /dev/null
+++ b/components/tvshows/details.brs
@@ -0,0 +1,121 @@
+sub itemContentChanged()
+ itemData = m.top.itemContent.full_data
+
+ m.top.findNode("tvshowPoster").uri = m.top.itemContent.posterURL
+
+ ' Handle all "As Is" fields
+ setFieldText("title", itemData.name)
+ setFieldText("releaseYear", itemData.productionYear)
+ setFieldText("officialRating", itemData.officialRating)
+ setFieldText("communityRating", str(itemData.communityRating))
+ setFieldText("overview", itemData.overview)
+
+ setFieldText("runtime", stri(getRuntime()) + " mins")
+
+ setFieldText("history", getHistory())
+
+ if itemData.genres.count() > 0
+ setFieldText("genres", itemData.genres.join(", "))
+ end if
+ director = invalid
+ for each person in itemData.people
+ if person.type = "Director"
+ director = person.name
+ exit for
+ end if
+ end for
+ if itemData.taglines.count() > 0
+ setFieldText("tagline", itemData.taglines[0])
+ end if
+ m.top.findNode("TVSeasonSelect").TVSeasonData = m.top.itemContent.seasons
+end sub
+
+sub setFieldText(field as string, value)
+ node = m.top.findNode(field)
+ if node = invalid then return
+
+ node.text = value
+end sub
+
+function getRuntime() as Integer
+ itemData = m.top.itemContent.full_data
+
+ ' A tick is .1ms, so 1/10,000,000 for ticks to seconds,
+ ' then 1/60 for seconds to minutess... 1/600,000,000
+ return round(itemData.RunTimeTicks / 600000000.0)
+end function
+
+function getEndTime() as string
+ itemData = m.top.itemContent.full_data
+
+ date = CreateObject("roDateTime")
+ duration_s = int(itemData.RunTimeTicks / 10000000.0)
+ date.fromSeconds(date.asSeconds() + duration_s)
+ date.toLocalTime()
+ hours = date.getHours()
+ meridian = "AM"
+ if hours = 0
+ hours = 12
+ meridian = "AM"
+ else if hours = 12
+ hours = 12
+ meridian = "PM"
+ else if hours > 12
+ hours = hours - 12
+ meridian = "PM"
+ end if
+
+ return Substitute("{0}:{1} {2}", stri(hours).trim(), stri(date.getMinutes()).trim(), meridian)
+end function
+
+function getHistory() as string
+ itemData = m.top.itemContent.full_data
+ ' Aired Fridays at 9:30 PM on ABC (US)
+
+ airwords = invalid
+ studio = invalid
+ if itemData.status = "Ended"
+ verb = "Aired"
+ else
+ verb = "Airs"
+ end if
+
+ airdays = itemData.airdays
+ airtime = itemData.airtime
+ if airtime <> invalid and airdays.count() = 1
+ airwords = airdays[0] + " at " + airtime
+ endif
+
+ if itemData.studios.count() > 0
+ studio = itemData.studios[0].name
+ end if
+
+ if studio = invalid and airwords = invalid
+ return ""
+ end if
+
+ words = verb
+ if airwords <> invalid
+ words = words + " " + airwords
+ end if
+ if studio <> invalid
+ words = words + " on " + studio
+ end if
+
+ return words
+end function
+
+function round(f as Float) as Integer
+ ' BrightScript only has a "floor" round
+ ' This compares floor to floor + 1 to find which is closer
+ m = int(f)
+ n = m + 1
+ x = abs(f - m)
+ y = abs(f - n)
+ if y > x
+ return m
+ else
+ return n
+ end if
+end function
+
diff --git a/components/tvshows/details.xml b/components/tvshows/details.xml
new file mode 100644
index 00000000..e16ea9c6
--- /dev/null
+++ b/components/tvshows/details.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/tvshows/rowlist-season.xml b/components/tvshows/rowlist-season.xml
new file mode 100644
index 00000000..83f5e5f5
--- /dev/null
+++ b/components/tvshows/rowlist-season.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
diff --git a/components/tvshows/rowlist-show.xml b/components/tvshows/rowlist-show.xml
new file mode 100644
index 00000000..e4182eea
--- /dev/null
+++ b/components/tvshows/rowlist-show.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
diff --git a/components/tvshows/scene.xml b/components/tvshows/scene.xml
new file mode 100644
index 00000000..8208395b
--- /dev/null
+++ b/components/tvshows/scene.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+