syndicate 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +8 -0
- data/Rakefile +2 -0
- data/bin/syndicate +5 -0
- data/examples/roku/Makefile +23 -0
- data/examples/roku/README.md +24 -0
- data/examples/roku/app.mk +89 -0
- data/examples/roku/images/banner-hd.png +0 -0
- data/examples/roku/images/banner-logo-hd.png +0 -0
- data/examples/roku/images/banner-logo-sd.png +0 -0
- data/examples/roku/images/banner-sd.png +0 -0
- data/examples/roku/images/logo-hd.jpg +0 -0
- data/examples/roku/images/logo-sd.jpg +0 -0
- data/examples/roku/manifest +9 -0
- data/examples/roku/source/appMain.brs +13 -0
- data/examples/roku/source/lib/generalDlgs.brs +150 -0
- data/examples/roku/source/lib/generalUtils.brs +681 -0
- data/examples/roku/source/lib/lib.brs +45 -0
- data/examples/roku/source/lib/mrss.brs +59 -0
- data/examples/roku/source/lib/urlUtils.brs +215 -0
- data/examples/roku/source/lib/video.brs +17 -0
- data/examples/roku/source/screens/mediaplayer.brs +36 -0
- data/examples/roku/source/screens/poster.brs +65 -0
- data/examples/roku/source/theme.brs +30 -0
- data/lib/syndicate.rb +7 -0
- data/lib/syndicate/app.rb +40 -0
- data/lib/syndicate/config.ru +6 -0
- data/lib/syndicate/lib/video.rb +37 -0
- data/lib/syndicate/version.rb +3 -0
- data/lib/syndicate/views/mrss.builder +16 -0
- data/syndicate.gemspec +21 -0
- metadata +129 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Brooke McKim
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/syndicate
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#########################################################################
|
2
|
+
# Simple makefile for packaging Roku Video Player example
|
3
|
+
#
|
4
|
+
# Makefile Usage:
|
5
|
+
# > make
|
6
|
+
# > make install
|
7
|
+
# > make remove
|
8
|
+
#
|
9
|
+
# Important Notes:
|
10
|
+
# To use the "install" and "remove" targets to install your
|
11
|
+
# application directly from the shell, you must do the following:
|
12
|
+
#
|
13
|
+
# 1) Make sure that you have the curl command line executable in your path
|
14
|
+
# 2) Set the variable ROKU_DEV_TARGET in your environment to the IP
|
15
|
+
# address of your Roku box. (e.g. export ROKU_DEV_TARGET=192.168.1.1.
|
16
|
+
# Set in your this variable in your shell startup (e.g. .bashrc)
|
17
|
+
##########################################################################
|
18
|
+
APPNAME = syndicate
|
19
|
+
VERSION = 1.0
|
20
|
+
|
21
|
+
ZIP_EXCLUDE= -x *.git/* -x .gitignore -x .DS_Store -x README.md -x zips/* -x zips/
|
22
|
+
|
23
|
+
include ./app.mk
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Roku Application which can be powered by MRSS feeds.
|
2
|
+
|
3
|
+
## Setup
|
4
|
+
|
5
|
+
1. Change url variable in appMain.brs to point to your Syndicate server.
|
6
|
+
1. Edit manifest and change Title and Subtitle accordingly.
|
7
|
+
1. Edit Makefile and change Appname accordingly.
|
8
|
+
|
9
|
+
## Graphics
|
10
|
+
|
11
|
+
All graphics are in /images
|
12
|
+
|
13
|
+
* banner - The banner at the top of the application
|
14
|
+
* banner-logo - The logo overlaid on top of the banner. Position is controlled in theme.brs
|
15
|
+
* logo - The logo used on the channel screen of the Roku. These can be used when submitting application to Roku as well.
|
16
|
+
|
17
|
+
## Install to Roku
|
18
|
+
|
19
|
+
Roku must first be set to development mode.
|
20
|
+
|
21
|
+
```bash
|
22
|
+
export ROKU_DEV_TARGET=<ip_address_of_roku>
|
23
|
+
make install
|
24
|
+
```
|
@@ -0,0 +1,89 @@
|
|
1
|
+
#########################################################################
|
2
|
+
# common include file for application Makefiles
|
3
|
+
#
|
4
|
+
# Makefile Usage:
|
5
|
+
# > make
|
6
|
+
# > make install
|
7
|
+
# > make remove
|
8
|
+
#
|
9
|
+
# to exclude certain files from being added to the zipfile during packaging
|
10
|
+
# include a line like this:ZIP_EXCLUDE= -x keys\*
|
11
|
+
# that will exclude any file who's name begins with 'keys'
|
12
|
+
# to exclude using more than one pattern use additional '-x <pattern>' arguments
|
13
|
+
# ZIP_EXCLUDE= -x \*.pkg -x storeassets\*
|
14
|
+
#
|
15
|
+
# Important Notes:
|
16
|
+
# To use the "install" and "remove" targets to install your
|
17
|
+
# application directly from the shell, you must do the following:
|
18
|
+
#
|
19
|
+
# 1) Make sure that you have the curl command line executable in your path
|
20
|
+
# 2) Set the variable ROKU_DEV_TARGET in your environment to the IP
|
21
|
+
# address of your Roku box. (e.g. export ROKU_DEV_TARGET=192.168.1.1.
|
22
|
+
# Set in your this variable in your shell startup (e.g. .bashrc)
|
23
|
+
##########################################################################
|
24
|
+
PKGREL = ./packages
|
25
|
+
ZIPREL = ./zips
|
26
|
+
SOURCEREL = ./source
|
27
|
+
|
28
|
+
.PHONY: all $(APPNAME)
|
29
|
+
|
30
|
+
$(APPNAME):
|
31
|
+
@echo "*** Creating $(APPNAME).zip ***"
|
32
|
+
|
33
|
+
@echo " >> removing old application zip $(ZIPREL)/$(APPNAME).zip"
|
34
|
+
@if [ -e "$(ZIPREL)/$(APPNAME).zip" ]; \
|
35
|
+
then \
|
36
|
+
rm $(ZIPREL)/$(APPNAME).zip; \
|
37
|
+
fi
|
38
|
+
|
39
|
+
@echo " >> creating destination directory $(ZIPREL)"
|
40
|
+
@if [ ! -d $(ZIPREL) ]; \
|
41
|
+
then \
|
42
|
+
mkdir -p $(ZIPREL); \
|
43
|
+
fi
|
44
|
+
|
45
|
+
@echo " >> setting directory permissions for $(ZIPREL)"
|
46
|
+
@if [ ! -w $(ZIPREL) ]; \
|
47
|
+
then \
|
48
|
+
chmod 755 $(ZIPREL); \
|
49
|
+
fi
|
50
|
+
|
51
|
+
# zip .png files without compression
|
52
|
+
# do not zip up Makefiles, or any files ending with '~'
|
53
|
+
@echo " >> creating application zip $(ZIPREL)/$(APPNAME).zip"
|
54
|
+
@if [ -d $(SOURCEREL)/ ]; \
|
55
|
+
then \
|
56
|
+
(zip -0 -r "$(ZIPREL)/$(APPNAME).zip" . -i \*.png $(ZIP_EXCLUDE)); \
|
57
|
+
(zip -9 -r "$(ZIPREL)/$(APPNAME).zip" . -x \*~ -x \*.png -x Makefile $(ZIP_EXCLUDE)); \
|
58
|
+
else \
|
59
|
+
echo "Source for $(APPNAME) not found at $(SOURCEREL)/"; \
|
60
|
+
fi
|
61
|
+
|
62
|
+
@echo "*** developer zip $(APPNAME) complete ***"
|
63
|
+
|
64
|
+
install: $(APPNAME)
|
65
|
+
@echo "Installing $(APPNAME) to host $(ROKU_DEV_TARGET)"
|
66
|
+
@curl -s -S -F "mysubmit=Install" -F "archive=@$(ZIPREL)/$(APPNAME).zip" -F "passwd=" http://$(ROKU_DEV_TARGET)/plugin_install | grep "<font color" | sed "s/<font color=\"red\">//"
|
67
|
+
|
68
|
+
pkg: install
|
69
|
+
@echo "*** Creating Package ***"
|
70
|
+
|
71
|
+
@echo " >> creating destination directory $(PKGREL)"
|
72
|
+
@if [ ! -d $(PKGREL) ]; \
|
73
|
+
then \
|
74
|
+
mkdir -p $(PKGREL); \
|
75
|
+
fi
|
76
|
+
|
77
|
+
@echo " >> setting directory permissions for $(PKGREL)"
|
78
|
+
@if [ ! -w $(PKGREL) ]; \
|
79
|
+
then \
|
80
|
+
chmod 755 $(PKGREL); \
|
81
|
+
fi
|
82
|
+
|
83
|
+
@echo "Packaging $(APPNAME) on host $(ROKU_DEV_TARGET)"
|
84
|
+
@read -p "Password: " REPLY ; echo $$REPLY | xargs -i curl -s -S -Fmysubmit=Package -Fapp_name=$(APPNAME)/$(VERSION) -Fpasswd={} -Fpkg_time=`expr \`date +%s\` \* 1000` "http://$(ROKU_DEV_TARGET)/plugin_package" | grep '^<font face=' | sed 's/.*href=\"\([^\"]*\)\".*/\1/' | sed 's#pkgs/##' | xargs -i curl -s -S -o $(PKGREL)/$(APPNAME)_{} http://$(ROKU_DEV_TARGET)/pkgs/{}
|
85
|
+
|
86
|
+
@echo "*** Package $(APPNAME) complete ***"
|
87
|
+
remove:
|
88
|
+
@echo "Removing $(APPNAME) from host $(ROKU_DEV_TARGET)"
|
89
|
+
@curl -s -S -F "mysubmit=Delete" -F "archive=" -F "passwd=" http://$(ROKU_DEV_TARGET)/plugin_install | grep "<font color" | sed "s/<font color=\"red\">//"
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Sub Main()
|
2
|
+
|
3
|
+
'initialize theme attributes like titles, logos and overhang color
|
4
|
+
initTheme()
|
5
|
+
|
6
|
+
'So the app launches right away instead of waiting to parse xml
|
7
|
+
screenFacade = CreateObject("roPosterScreen")
|
8
|
+
screenFacade.show()
|
9
|
+
|
10
|
+
'prepare the screen for display and get ready to begin
|
11
|
+
url = "http://10.0.1.5:3000/d/Users/bmckim/Desktop/"
|
12
|
+
showPosterScreen(prePosterScreen("", ""), url)
|
13
|
+
End Sub
|
@@ -0,0 +1,150 @@
|
|
1
|
+
'**********************************************************
|
2
|
+
'** Video Player Example Application - General Dialogs
|
3
|
+
'** November 2009
|
4
|
+
'** Copyright (c) 2009 Roku Inc. All Rights Reserved.
|
5
|
+
'**********************************************************
|
6
|
+
|
7
|
+
'******************************************************
|
8
|
+
'Show basic message dialog without buttons
|
9
|
+
'Dialog remains up until caller releases the returned object
|
10
|
+
'******************************************************
|
11
|
+
|
12
|
+
Function ShowPleaseWait(title As dynamic, text As dynamic) As Object
|
13
|
+
if not isstr(title) title = ""
|
14
|
+
if not isstr(text) text = ""
|
15
|
+
|
16
|
+
port = CreateObject("roMessagePort")
|
17
|
+
dialog = invalid
|
18
|
+
|
19
|
+
'the OneLineDialog renders a single line of text better
|
20
|
+
'than the MessageDialog.
|
21
|
+
if text = ""
|
22
|
+
dialog = CreateObject("roOneLineDialog")
|
23
|
+
else
|
24
|
+
dialog = CreateObject("roMessageDialog")
|
25
|
+
dialog.SetText(text)
|
26
|
+
endif
|
27
|
+
|
28
|
+
dialog.SetMessagePort(port)
|
29
|
+
|
30
|
+
dialog.SetTitle(title)
|
31
|
+
dialog.ShowBusyAnimation()
|
32
|
+
dialog.Show()
|
33
|
+
return dialog
|
34
|
+
End Function
|
35
|
+
|
36
|
+
'******************************************************
|
37
|
+
'Retrieve text for connection failed
|
38
|
+
'******************************************************
|
39
|
+
|
40
|
+
Function GetConnectionFailedText() as String
|
41
|
+
return "We were unable to connect to the service. Please try again in a few minutes."
|
42
|
+
End Function
|
43
|
+
|
44
|
+
'******************************************************
|
45
|
+
'Show connection error dialog
|
46
|
+
'
|
47
|
+
'Parameter: retry t/f - offer retry option
|
48
|
+
'Return 0 = retry, 1 = back
|
49
|
+
'******************************************************
|
50
|
+
|
51
|
+
Function ShowConnectionFailedRetry() as dynamic
|
52
|
+
Dbg("Connection Failed Retry")
|
53
|
+
title = "Can't connect to video service"
|
54
|
+
text = GetConnectionFailedText()
|
55
|
+
return ShowDialog2Buttons(title, text, "try again", "back")
|
56
|
+
End Function
|
57
|
+
|
58
|
+
'******************************************************
|
59
|
+
'Show Amzon connection error dialog with only an OK button
|
60
|
+
'******************************************************
|
61
|
+
|
62
|
+
Sub ShowConnectionFailed()
|
63
|
+
Dbg("Connection Failed")
|
64
|
+
title = "Can't connect to video service"
|
65
|
+
text = GetConnectionFailedText()
|
66
|
+
ShowErrorDialog(text, title)
|
67
|
+
End Sub
|
68
|
+
|
69
|
+
'******************************************************
|
70
|
+
'Show error dialog with OK button
|
71
|
+
'******************************************************
|
72
|
+
|
73
|
+
Sub ShowErrorDialog(text As dynamic, title=invalid as dynamic)
|
74
|
+
if not isstr(text) text = "Unspecified error"
|
75
|
+
if not isstr(title) title = ""
|
76
|
+
ShowDialog1Button(title, text, "Done")
|
77
|
+
End Sub
|
78
|
+
|
79
|
+
'******************************************************
|
80
|
+
'Show 1 button dialog
|
81
|
+
'Return: nothing
|
82
|
+
'******************************************************
|
83
|
+
|
84
|
+
Sub ShowDialog1Button(title As dynamic, text As dynamic, but1 As String)
|
85
|
+
if not isstr(title) title = ""
|
86
|
+
if not isstr(text) text = ""
|
87
|
+
|
88
|
+
Dbg("DIALOG1: ", title + " - " + text)
|
89
|
+
|
90
|
+
port = CreateObject("roMessagePort")
|
91
|
+
dialog = CreateObject("roMessageDialog")
|
92
|
+
dialog.SetMessagePort(port)
|
93
|
+
|
94
|
+
dialog.SetTitle(title)
|
95
|
+
dialog.SetText(text)
|
96
|
+
dialog.AddButton(0, but1)
|
97
|
+
dialog.Show()
|
98
|
+
|
99
|
+
while true
|
100
|
+
dlgMsg = wait(0, dialog.GetMessagePort())
|
101
|
+
|
102
|
+
if type(dlgMsg) = "roMessageDialogEvent"
|
103
|
+
if dlgMsg.isScreenClosed()
|
104
|
+
print "Screen closed"
|
105
|
+
return
|
106
|
+
else if dlgMsg.isButtonPressed()
|
107
|
+
print "Button pressed: "; dlgMsg.GetIndex(); " " dlgMsg.GetData()
|
108
|
+
return
|
109
|
+
endif
|
110
|
+
endif
|
111
|
+
end while
|
112
|
+
End Sub
|
113
|
+
|
114
|
+
'******************************************************
|
115
|
+
'Show 2 button dialog
|
116
|
+
'Return: 0=first button or screen closed, 1=second button
|
117
|
+
'******************************************************
|
118
|
+
|
119
|
+
Function ShowDialog2Buttons(title As dynamic, text As dynamic, but1 As String, but2 As String) As Integer
|
120
|
+
if not isstr(title) title = ""
|
121
|
+
if not isstr(text) text = ""
|
122
|
+
|
123
|
+
Dbg("DIALOG2: ", title + " - " + text)
|
124
|
+
|
125
|
+
port = CreateObject("roMessagePort")
|
126
|
+
dialog = CreateObject("roMessageDialog")
|
127
|
+
dialog.SetMessagePort(port)
|
128
|
+
|
129
|
+
dialog.SetTitle(title)
|
130
|
+
dialog.SetText(text)
|
131
|
+
dialog.AddButton(0, but1)
|
132
|
+
dialog.AddButton(1, but2)
|
133
|
+
dialog.Show()
|
134
|
+
|
135
|
+
while true
|
136
|
+
dlgMsg = wait(0, dialog.GetMessagePort())
|
137
|
+
|
138
|
+
if type(dlgMsg) = "roMessageDialogEvent"
|
139
|
+
if dlgMsg.isScreenClosed()
|
140
|
+
print "Screen closed"
|
141
|
+
dialog = invalid
|
142
|
+
return 0
|
143
|
+
else if dlgMsg.isButtonPressed()
|
144
|
+
print "Button pressed: "; dlgMsg.GetIndex(); " " dlgMsg.GetData()
|
145
|
+
dialog = invalid
|
146
|
+
return dlgMsg.GetIndex()
|
147
|
+
endif
|
148
|
+
endif
|
149
|
+
end while
|
150
|
+
End Function
|
@@ -0,0 +1,681 @@
|
|
1
|
+
'**********************************************************
|
2
|
+
'** Video Player Example Application - General Utilities
|
3
|
+
'** November 2009
|
4
|
+
'** Copyright (c) 2009 Roku Inc. All Rights Reserved.
|
5
|
+
'**********************************************************
|
6
|
+
|
7
|
+
'******************************************************
|
8
|
+
'Registry Helper Functions
|
9
|
+
'******************************************************
|
10
|
+
Function RegRead(key, section=invalid)
|
11
|
+
if section = invalid then section = "Default"
|
12
|
+
sec = CreateObject("roRegistrySection", section)
|
13
|
+
if sec.Exists(key) then return sec.Read(key)
|
14
|
+
return invalid
|
15
|
+
End Function
|
16
|
+
|
17
|
+
Function RegWrite(key, val, section=invalid)
|
18
|
+
if section = invalid then section = "Default"
|
19
|
+
sec = CreateObject("roRegistrySection", section)
|
20
|
+
sec.Write(key, val)
|
21
|
+
sec.Flush() 'commit it
|
22
|
+
End Function
|
23
|
+
|
24
|
+
Function RegDelete(key, section=invalid)
|
25
|
+
if section = invalid then section = "Default"
|
26
|
+
sec = CreateObject("roRegistrySection", section)
|
27
|
+
sec.Delete(key)
|
28
|
+
sec.Flush()
|
29
|
+
End Function
|
30
|
+
|
31
|
+
|
32
|
+
'******************************************************
|
33
|
+
'Insertion Sort
|
34
|
+
'Will sort an array directly, or use a key function
|
35
|
+
'******************************************************
|
36
|
+
Sub Sort(A as Object, key=invalid as dynamic)
|
37
|
+
|
38
|
+
if type(A)<>"roArray" then return
|
39
|
+
|
40
|
+
if (key=invalid) then
|
41
|
+
for i = 1 to A.Count()-1
|
42
|
+
value = A[i]
|
43
|
+
j = i-1
|
44
|
+
while j>= 0 and A[j] > value
|
45
|
+
A[j + 1] = A[j]
|
46
|
+
j = j-1
|
47
|
+
end while
|
48
|
+
A[j+1] = value
|
49
|
+
next
|
50
|
+
|
51
|
+
else
|
52
|
+
if type(key)<>"Function" then return
|
53
|
+
for i = 1 to A.Count()-1
|
54
|
+
valuekey = key(A[i])
|
55
|
+
value = A[i]
|
56
|
+
j = i-1
|
57
|
+
while j>= 0 and key(A[j]) > valuekey
|
58
|
+
A[j + 1] = A[j]
|
59
|
+
j = j-1
|
60
|
+
end while
|
61
|
+
A[j+1] = value
|
62
|
+
next
|
63
|
+
|
64
|
+
end if
|
65
|
+
|
66
|
+
End Sub
|
67
|
+
|
68
|
+
|
69
|
+
'******************************************************
|
70
|
+
'Convert anything to a string
|
71
|
+
'
|
72
|
+
'Always returns a string
|
73
|
+
'******************************************************
|
74
|
+
Function tostr(any)
|
75
|
+
ret = AnyToString(any)
|
76
|
+
if ret = invalid ret = type(any)
|
77
|
+
if ret = invalid ret = "unknown" 'failsafe
|
78
|
+
return ret
|
79
|
+
End Function
|
80
|
+
|
81
|
+
|
82
|
+
'******************************************************
|
83
|
+
'Get a " char as a string
|
84
|
+
'******************************************************
|
85
|
+
Function Quote()
|
86
|
+
q$ = Chr(34)
|
87
|
+
return q$
|
88
|
+
End Function
|
89
|
+
|
90
|
+
|
91
|
+
'******************************************************
|
92
|
+
'isxmlelement
|
93
|
+
'
|
94
|
+
'Determine if the given object supports the ifXMLElement interface
|
95
|
+
'******************************************************
|
96
|
+
Function isxmlelement(obj as dynamic) As Boolean
|
97
|
+
if obj = invalid return false
|
98
|
+
if GetInterface(obj, "ifXMLElement") = invalid return false
|
99
|
+
return true
|
100
|
+
End Function
|
101
|
+
|
102
|
+
|
103
|
+
'******************************************************
|
104
|
+
'islist
|
105
|
+
'
|
106
|
+
'Determine if the given object supports the ifList interface
|
107
|
+
'******************************************************
|
108
|
+
Function islist(obj as dynamic) As Boolean
|
109
|
+
if obj = invalid return false
|
110
|
+
if GetInterface(obj, "ifArray") = invalid return false
|
111
|
+
return true
|
112
|
+
End Function
|
113
|
+
|
114
|
+
|
115
|
+
'******************************************************
|
116
|
+
'isint
|
117
|
+
'
|
118
|
+
'Determine if the given object supports the ifInt interface
|
119
|
+
'******************************************************
|
120
|
+
Function isint(obj as dynamic) As Boolean
|
121
|
+
if obj = invalid return false
|
122
|
+
if GetInterface(obj, "ifInt") = invalid return false
|
123
|
+
return true
|
124
|
+
End Function
|
125
|
+
|
126
|
+
'******************************************************
|
127
|
+
' validstr
|
128
|
+
'
|
129
|
+
' always return a valid string. if the argument is
|
130
|
+
' invalid or not a string, return an empty string
|
131
|
+
'******************************************************
|
132
|
+
Function validstr(obj As Dynamic) As String
|
133
|
+
if isnonemptystr(obj) return obj
|
134
|
+
return ""
|
135
|
+
End Function
|
136
|
+
|
137
|
+
|
138
|
+
'******************************************************
|
139
|
+
'isstr
|
140
|
+
'
|
141
|
+
'Determine if the given object supports the ifString interface
|
142
|
+
'******************************************************
|
143
|
+
Function isstr(obj as dynamic) As Boolean
|
144
|
+
if obj = invalid return false
|
145
|
+
if GetInterface(obj, "ifString") = invalid return false
|
146
|
+
return true
|
147
|
+
End Function
|
148
|
+
|
149
|
+
|
150
|
+
'******************************************************
|
151
|
+
'isnonemptystr
|
152
|
+
'
|
153
|
+
'Determine if the given object supports the ifString interface
|
154
|
+
'and returns a string of non zero length
|
155
|
+
'******************************************************
|
156
|
+
Function isnonemptystr(obj)
|
157
|
+
if isnullorempty(obj) return false
|
158
|
+
return true
|
159
|
+
End Function
|
160
|
+
|
161
|
+
|
162
|
+
'******************************************************
|
163
|
+
'isnullorempty
|
164
|
+
'
|
165
|
+
'Determine if the given object is invalid or supports
|
166
|
+
'the ifString interface and returns a string of non zero length
|
167
|
+
'******************************************************
|
168
|
+
Function isnullorempty(obj)
|
169
|
+
if obj = invalid return true
|
170
|
+
if not isstr(obj) return true
|
171
|
+
if Len(obj) = 0 return true
|
172
|
+
return false
|
173
|
+
End Function
|
174
|
+
|
175
|
+
|
176
|
+
'******************************************************
|
177
|
+
'isbool
|
178
|
+
'
|
179
|
+
'Determine if the given object supports the ifBoolean interface
|
180
|
+
'******************************************************
|
181
|
+
Function isbool(obj as dynamic) As Boolean
|
182
|
+
if obj = invalid return false
|
183
|
+
if GetInterface(obj, "ifBoolean") = invalid return false
|
184
|
+
return true
|
185
|
+
End Function
|
186
|
+
|
187
|
+
|
188
|
+
'******************************************************
|
189
|
+
'isfloat
|
190
|
+
'
|
191
|
+
'Determine if the given object supports the ifFloat interface
|
192
|
+
'******************************************************
|
193
|
+
Function isfloat(obj as dynamic) As Boolean
|
194
|
+
if obj = invalid return false
|
195
|
+
if GetInterface(obj, "ifFloat") = invalid return false
|
196
|
+
return true
|
197
|
+
End Function
|
198
|
+
|
199
|
+
|
200
|
+
'******************************************************
|
201
|
+
'strtobool
|
202
|
+
'
|
203
|
+
'Convert string to boolean safely. Don't crash
|
204
|
+
'Looks for certain string values
|
205
|
+
'******************************************************
|
206
|
+
Function strtobool(obj As dynamic) As Boolean
|
207
|
+
if obj = invalid return false
|
208
|
+
if type(obj) <> "roString" return false
|
209
|
+
o = strTrim(obj)
|
210
|
+
o = Lcase(o)
|
211
|
+
if o = "true" return true
|
212
|
+
if o = "t" return true
|
213
|
+
if o = "y" return true
|
214
|
+
if o = "1" return true
|
215
|
+
return false
|
216
|
+
End Function
|
217
|
+
|
218
|
+
|
219
|
+
'******************************************************
|
220
|
+
'itostr
|
221
|
+
'
|
222
|
+
'Convert int to string. This is necessary because
|
223
|
+
'the builtin Stri(x) prepends whitespace
|
224
|
+
'******************************************************
|
225
|
+
Function itostr(i As Integer) As String
|
226
|
+
str = Stri(i)
|
227
|
+
return strTrim(str)
|
228
|
+
End Function
|
229
|
+
|
230
|
+
|
231
|
+
'******************************************************
|
232
|
+
'Get remaining hours from a total seconds
|
233
|
+
'******************************************************
|
234
|
+
Function hoursLeft(seconds As Integer) As Integer
|
235
|
+
hours% = seconds / 3600
|
236
|
+
return hours%
|
237
|
+
End Function
|
238
|
+
|
239
|
+
|
240
|
+
'******************************************************
|
241
|
+
'Get remaining minutes from a total seconds
|
242
|
+
'******************************************************
|
243
|
+
Function minutesLeft(seconds As Integer) As Integer
|
244
|
+
hours% = seconds / 3600
|
245
|
+
mins% = seconds - (hours% * 3600)
|
246
|
+
mins% = mins% / 60
|
247
|
+
return mins%
|
248
|
+
End Function
|
249
|
+
|
250
|
+
|
251
|
+
'******************************************************
|
252
|
+
'Pluralize simple strings like "1 minute" or "2 minutes"
|
253
|
+
'******************************************************
|
254
|
+
Function Pluralize(val As Integer, str As String) As String
|
255
|
+
ret = itostr(val) + " " + str
|
256
|
+
if val <> 1 ret = ret + "s"
|
257
|
+
return ret
|
258
|
+
End Function
|
259
|
+
|
260
|
+
|
261
|
+
'******************************************************
|
262
|
+
'Trim a string
|
263
|
+
'******************************************************
|
264
|
+
Function strTrim(str As String) As String
|
265
|
+
st=CreateObject("roString")
|
266
|
+
st.SetString(str)
|
267
|
+
return st.Trim()
|
268
|
+
End Function
|
269
|
+
|
270
|
+
|
271
|
+
'******************************************************
|
272
|
+
'Tokenize a string. Return roList of strings
|
273
|
+
'******************************************************
|
274
|
+
Function strTokenize(str As String, delim As String) As Object
|
275
|
+
st=CreateObject("roString")
|
276
|
+
st.SetString(str)
|
277
|
+
return st.Tokenize(delim)
|
278
|
+
End Function
|
279
|
+
|
280
|
+
|
281
|
+
'******************************************************
|
282
|
+
'Replace substrings in a string. Return new string
|
283
|
+
'******************************************************
|
284
|
+
Function strReplace(basestr As String, oldsub As String, newsub As String) As String
|
285
|
+
newstr = ""
|
286
|
+
|
287
|
+
i = 1
|
288
|
+
while i <= Len(basestr)
|
289
|
+
x = Instr(i, basestr, oldsub)
|
290
|
+
if x = 0 then
|
291
|
+
newstr = newstr + Mid(basestr, i)
|
292
|
+
exit while
|
293
|
+
endif
|
294
|
+
|
295
|
+
if x > i then
|
296
|
+
newstr = newstr + Mid(basestr, i, x-i)
|
297
|
+
i = x
|
298
|
+
endif
|
299
|
+
|
300
|
+
newstr = newstr + newsub
|
301
|
+
i = i + Len(oldsub)
|
302
|
+
end while
|
303
|
+
|
304
|
+
return newstr
|
305
|
+
End Function
|
306
|
+
|
307
|
+
|
308
|
+
'******************************************************
|
309
|
+
'Get all XML subelements by name
|
310
|
+
'
|
311
|
+
'return list of 0 or more elements
|
312
|
+
'******************************************************
|
313
|
+
Function GetXMLElementsByName(xml As Object, name As String) As Object
|
314
|
+
list = CreateObject("roArray", 100, true)
|
315
|
+
if islist(xml.GetBody()) = false return list
|
316
|
+
|
317
|
+
for each e in xml.GetBody()
|
318
|
+
if e.GetName() = name then
|
319
|
+
list.Push(e)
|
320
|
+
endif
|
321
|
+
next
|
322
|
+
|
323
|
+
return list
|
324
|
+
End Function
|
325
|
+
|
326
|
+
|
327
|
+
'******************************************************
|
328
|
+
'Get all XML subelement's string bodies by name
|
329
|
+
'
|
330
|
+
'return list of 0 or more strings
|
331
|
+
'******************************************************
|
332
|
+
Function GetXMLElementBodiesByName(xml As Object, name As String) As Object
|
333
|
+
list = CreateObject("roArray", 100, true)
|
334
|
+
if islist(xml.GetBody()) = false return list
|
335
|
+
|
336
|
+
for each e in xml.GetBody()
|
337
|
+
if e.GetName() = name then
|
338
|
+
b = e.GetBody()
|
339
|
+
if type(b) = "roString" list.Push(b)
|
340
|
+
endif
|
341
|
+
next
|
342
|
+
|
343
|
+
return list
|
344
|
+
End Function
|
345
|
+
|
346
|
+
|
347
|
+
'******************************************************
|
348
|
+
'Get first XML subelement by name
|
349
|
+
'
|
350
|
+
'return invalid if not found, else the element
|
351
|
+
'******************************************************
|
352
|
+
Function GetFirstXMLElementByName(xml As Object, name As String) As dynamic
|
353
|
+
if islist(xml.GetBody()) = false return invalid
|
354
|
+
|
355
|
+
for each e in xml.GetBody()
|
356
|
+
if e.GetName() = name return e
|
357
|
+
next
|
358
|
+
|
359
|
+
return invalid
|
360
|
+
End Function
|
361
|
+
|
362
|
+
|
363
|
+
'******************************************************
|
364
|
+
'Get first XML subelement's string body by name
|
365
|
+
'
|
366
|
+
'return invalid if not found, else the subelement's body string
|
367
|
+
'******************************************************
|
368
|
+
Function GetFirstXMLElementBodyStringByName(xml As Object, name As String) As dynamic
|
369
|
+
e = GetFirstXMLElementByName(xml, name)
|
370
|
+
if e = invalid return invalid
|
371
|
+
if type(e.GetBody()) <> "roString" return invalid
|
372
|
+
return e.GetBody()
|
373
|
+
End Function
|
374
|
+
|
375
|
+
|
376
|
+
'******************************************************
|
377
|
+
'Get the xml element as an integer
|
378
|
+
'
|
379
|
+
'return invalid if body not a string, else the integer as converted by strtoi
|
380
|
+
'******************************************************
|
381
|
+
Function GetXMLBodyAsInteger(xml As Object) As dynamic
|
382
|
+
if type(xml.GetBody()) <> "roString" return invalid
|
383
|
+
return strtoi(xml.GetBody())
|
384
|
+
End Function
|
385
|
+
|
386
|
+
|
387
|
+
'******************************************************
|
388
|
+
'Parse a string into a roXMLElement
|
389
|
+
'
|
390
|
+
'return invalid on error, else the xml object
|
391
|
+
'******************************************************
|
392
|
+
Function ParseXML(str As String) As dynamic
|
393
|
+
if str = invalid return invalid
|
394
|
+
xml=CreateObject("roXMLElement")
|
395
|
+
if not xml.Parse(str) return invalid
|
396
|
+
return xml
|
397
|
+
End Function
|
398
|
+
|
399
|
+
|
400
|
+
'******************************************************
|
401
|
+
'Get XML sub elements whose bodies are strings into an associative array.
|
402
|
+
'subelements that are themselves parents are skipped
|
403
|
+
'namespace :'s are replaced with _'s
|
404
|
+
'
|
405
|
+
'So an XML element like...
|
406
|
+
'
|
407
|
+
'<blah>
|
408
|
+
' <This>abcdefg</This>
|
409
|
+
' <Sucks>xyz</Sucks>
|
410
|
+
' <sub>
|
411
|
+
' <sub2>
|
412
|
+
' ....
|
413
|
+
' </sub2>
|
414
|
+
' </sub>
|
415
|
+
' <ns:doh>homer</ns:doh>
|
416
|
+
'</blah>
|
417
|
+
'
|
418
|
+
'returns an AA with:
|
419
|
+
'
|
420
|
+
'aa.This = "abcdefg"
|
421
|
+
'aa.Sucks = "xyz"
|
422
|
+
'aa.ns_doh = "homer"
|
423
|
+
'
|
424
|
+
'return an empty AA if nothing found
|
425
|
+
'******************************************************
|
426
|
+
Sub GetXMLintoAA(xml As Object, aa As Object)
|
427
|
+
for each e in xml.GetBody()
|
428
|
+
body = e.GetBody()
|
429
|
+
if type(body) = "roString" then
|
430
|
+
name = e.GetName()
|
431
|
+
name = strReplace(name, ":", "_")
|
432
|
+
aa.AddReplace(name, body)
|
433
|
+
endif
|
434
|
+
next
|
435
|
+
End Sub
|
436
|
+
|
437
|
+
|
438
|
+
'******************************************************
|
439
|
+
'Walk an AA and print it
|
440
|
+
'******************************************************
|
441
|
+
Sub PrintAA(aa as Object)
|
442
|
+
print "---- AA ----"
|
443
|
+
if aa = invalid
|
444
|
+
print "invalid"
|
445
|
+
return
|
446
|
+
else
|
447
|
+
cnt = 0
|
448
|
+
for each e in aa
|
449
|
+
x = aa[e]
|
450
|
+
PrintAny(0, e + ": ", aa[e])
|
451
|
+
cnt = cnt + 1
|
452
|
+
next
|
453
|
+
if cnt = 0
|
454
|
+
PrintAny(0, "Nothing from for each. Looks like :", aa)
|
455
|
+
endif
|
456
|
+
endif
|
457
|
+
print "------------"
|
458
|
+
End Sub
|
459
|
+
|
460
|
+
|
461
|
+
'******************************************************
|
462
|
+
'Walk a list and print it
|
463
|
+
'******************************************************
|
464
|
+
Sub PrintList(list as Object)
|
465
|
+
print "---- list ----"
|
466
|
+
PrintAnyList(0, list)
|
467
|
+
print "--------------"
|
468
|
+
End Sub
|
469
|
+
|
470
|
+
|
471
|
+
'******************************************************
|
472
|
+
'Print an associativearray
|
473
|
+
'******************************************************
|
474
|
+
Sub PrintAnyAA(depth As Integer, aa as Object)
|
475
|
+
for each e in aa
|
476
|
+
x = aa[e]
|
477
|
+
PrintAny(depth, e + ": ", aa[e])
|
478
|
+
next
|
479
|
+
End Sub
|
480
|
+
|
481
|
+
|
482
|
+
'******************************************************
|
483
|
+
'Print a list with indent depth
|
484
|
+
'******************************************************
|
485
|
+
Sub PrintAnyList(depth As Integer, list as Object)
|
486
|
+
i = 0
|
487
|
+
for each e in list
|
488
|
+
PrintAny(depth, "List(" + itostr(i) + ")= ", e)
|
489
|
+
i = i + 1
|
490
|
+
next
|
491
|
+
End Sub
|
492
|
+
|
493
|
+
|
494
|
+
'******************************************************
|
495
|
+
'Print anything
|
496
|
+
'******************************************************
|
497
|
+
Sub PrintAny(depth As Integer, prefix As String, any As Dynamic)
|
498
|
+
if depth >= 10
|
499
|
+
print "**** TOO DEEP " + itostr(5)
|
500
|
+
return
|
501
|
+
endif
|
502
|
+
prefix = string(depth*2," ") + prefix
|
503
|
+
depth = depth + 1
|
504
|
+
str = AnyToString(any)
|
505
|
+
if str <> invalid
|
506
|
+
print prefix + str
|
507
|
+
return
|
508
|
+
endif
|
509
|
+
if type(any) = "roAssociativeArray"
|
510
|
+
print prefix + "(assocarr)..."
|
511
|
+
PrintAnyAA(depth, any)
|
512
|
+
return
|
513
|
+
endif
|
514
|
+
if islist(any) = true
|
515
|
+
print prefix + "(list of " + itostr(any.Count()) + ")..."
|
516
|
+
PrintAnyList(depth, any)
|
517
|
+
return
|
518
|
+
endif
|
519
|
+
|
520
|
+
print prefix + "?" + type(any) + "?"
|
521
|
+
End Sub
|
522
|
+
|
523
|
+
|
524
|
+
'******************************************************
|
525
|
+
'Print an object as a string for debugging. If it is
|
526
|
+
'very long print the first 500 chars.
|
527
|
+
'******************************************************
|
528
|
+
Sub Dbg(pre As Dynamic, o=invalid As Dynamic)
|
529
|
+
p = AnyToString(pre)
|
530
|
+
if p = invalid p = ""
|
531
|
+
if o = invalid o = ""
|
532
|
+
s = AnyToString(o)
|
533
|
+
if s = invalid s = "???: " + type(o)
|
534
|
+
if Len(s) > 4000
|
535
|
+
s = Left(s, 4000)
|
536
|
+
endif
|
537
|
+
print p + s
|
538
|
+
End Sub
|
539
|
+
|
540
|
+
|
541
|
+
'******************************************************
|
542
|
+
'Try to convert anything to a string. Only works on simple items.
|
543
|
+
'
|
544
|
+
'Test with this script...
|
545
|
+
'
|
546
|
+
' s$ = "yo1"
|
547
|
+
' ss = "yo2"
|
548
|
+
' i% = 111
|
549
|
+
' ii = 222
|
550
|
+
' f! = 333.333
|
551
|
+
' ff = 444.444
|
552
|
+
' d# = 555.555
|
553
|
+
' dd = 555.555
|
554
|
+
' bb = true
|
555
|
+
'
|
556
|
+
' so = CreateObject("roString")
|
557
|
+
' so.SetString("strobj")
|
558
|
+
' io = CreateObject("roInt")
|
559
|
+
' io.SetInt(666)
|
560
|
+
' tm = CreateObject("roTimespan")
|
561
|
+
'
|
562
|
+
' Dbg("", s$ ) 'call the Dbg() function which calls AnyToString()
|
563
|
+
' Dbg("", ss )
|
564
|
+
' Dbg("", "yo3")
|
565
|
+
' Dbg("", i% )
|
566
|
+
' Dbg("", ii )
|
567
|
+
' Dbg("", 2222 )
|
568
|
+
' Dbg("", f! )
|
569
|
+
' Dbg("", ff )
|
570
|
+
' Dbg("", 3333.3333 )
|
571
|
+
' Dbg("", d# )
|
572
|
+
' Dbg("", dd )
|
573
|
+
' Dbg("", so )
|
574
|
+
' Dbg("", io )
|
575
|
+
' Dbg("", bb )
|
576
|
+
' Dbg("", true )
|
577
|
+
' Dbg("", tm )
|
578
|
+
'
|
579
|
+
'try to convert an object to a string. return invalid if can't
|
580
|
+
'******************************************************
|
581
|
+
Function AnyToString(any As Dynamic) As dynamic
|
582
|
+
if any = invalid return "invalid"
|
583
|
+
if isstr(any) return any
|
584
|
+
if isint(any) return itostr(any)
|
585
|
+
if isbool(any)
|
586
|
+
if any = true return "true"
|
587
|
+
return "false"
|
588
|
+
endif
|
589
|
+
if isfloat(any) return Str(any)
|
590
|
+
if type(any) = "roTimespan" return itostr(any.TotalMilliseconds()) + "ms"
|
591
|
+
return invalid
|
592
|
+
End Function
|
593
|
+
|
594
|
+
|
595
|
+
'******************************************************
|
596
|
+
'Walk an XML tree and print it
|
597
|
+
'******************************************************
|
598
|
+
Sub PrintXML(element As Object, depth As Integer)
|
599
|
+
print tab(depth*3);"Name: [" + element.GetName() + "]"
|
600
|
+
if invalid <> element.GetAttributes() then
|
601
|
+
print tab(depth*3);"Attributes: ";
|
602
|
+
for each a in element.GetAttributes()
|
603
|
+
print a;"=";left(element.GetAttributes()[a], 4000);
|
604
|
+
if element.GetAttributes().IsNext() then print ", ";
|
605
|
+
next
|
606
|
+
print
|
607
|
+
endif
|
608
|
+
|
609
|
+
if element.GetBody()=invalid then
|
610
|
+
' print tab(depth*3);"No Body"
|
611
|
+
else if type(element.GetBody())="roString" then
|
612
|
+
print tab(depth*3);"Contains string: [" + left(element.GetBody(), 4000) + "]"
|
613
|
+
else
|
614
|
+
print tab(depth*3);"Contains list:"
|
615
|
+
for each e in element.GetBody()
|
616
|
+
PrintXML(e, depth+1)
|
617
|
+
next
|
618
|
+
endif
|
619
|
+
print
|
620
|
+
end sub
|
621
|
+
|
622
|
+
|
623
|
+
'******************************************************
|
624
|
+
'Dump the bytes of a string
|
625
|
+
'******************************************************
|
626
|
+
Sub DumpString(str As String)
|
627
|
+
print "DUMP STRING"
|
628
|
+
print "---------------------------"
|
629
|
+
print str
|
630
|
+
print "---------------------------"
|
631
|
+
l = Len(str)-1
|
632
|
+
i = 0
|
633
|
+
for i = 0 to l
|
634
|
+
c = Mid(str, i)
|
635
|
+
val = Asc(c)
|
636
|
+
print itostr(val)
|
637
|
+
next
|
638
|
+
print "---------------------------"
|
639
|
+
End Sub
|
640
|
+
|
641
|
+
|
642
|
+
'******************************************************
|
643
|
+
'Validate parameter is the correct type
|
644
|
+
'******************************************************
|
645
|
+
Function validateParam(param As Object, paramType As String,functionName As String, allowInvalid = false) As Boolean
|
646
|
+
if type(param) = paramType then
|
647
|
+
return true
|
648
|
+
endif
|
649
|
+
|
650
|
+
if allowInvalid = true then
|
651
|
+
if type(param) = invalid then
|
652
|
+
return true
|
653
|
+
endif
|
654
|
+
endif
|
655
|
+
|
656
|
+
print "invalid parameter of type "; type(param); " for "; paramType; " in function "; functionName
|
657
|
+
return false
|
658
|
+
End Function
|
659
|
+
|
660
|
+
'**********************************************************
|
661
|
+
'** Video Player Example Application - DeviceInfo
|
662
|
+
'** November 2009
|
663
|
+
'** Copyright (c) 2009 Roku Inc. All Rights Reserved.
|
664
|
+
'**********************************************************
|
665
|
+
|
666
|
+
'******************************************************
|
667
|
+
'Get our device version
|
668
|
+
'******************************************************
|
669
|
+
|
670
|
+
Function GetDeviceVersion()
|
671
|
+
return CreateObject("roDeviceInfo").GetVersion()
|
672
|
+
End Function
|
673
|
+
|
674
|
+
'******************************************************
|
675
|
+
'Get our serial number
|
676
|
+
'******************************************************
|
677
|
+
|
678
|
+
Function GetDeviceESN()
|
679
|
+
return CreateObject("roDeviceInfo").GetDeviceUniqueId()
|
680
|
+
End Function
|
681
|
+
|