syndicate 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|