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
@@ -0,0 +1,45 @@
|
|
1
|
+
'Library of Shared Functions
|
2
|
+
|
3
|
+
'Creates connection and retrieve url over http
|
4
|
+
Function initConnection(url As String) As String
|
5
|
+
|
6
|
+
conn = CreateObject("roAssociativeArray")
|
7
|
+
|
8
|
+
conn.Url = url
|
9
|
+
print "Initializing connection to" + conn.Url
|
10
|
+
conn.Timer = CreateObject("roTimespan")
|
11
|
+
|
12
|
+
'print "created feed connection for " + conn.Url
|
13
|
+
http = NewHttp(conn.Url)
|
14
|
+
|
15
|
+
'print "Getting http response"
|
16
|
+
rsp = http.GetToStringWithRetry()
|
17
|
+
'print rsp
|
18
|
+
|
19
|
+
return rsp
|
20
|
+
End Function
|
21
|
+
|
22
|
+
'Creates XML object out of string
|
23
|
+
Function to_xml(rsp As String) As Object
|
24
|
+
xml = CreateObject("roXMLElement")
|
25
|
+
if not xml.Parse(rsp) then
|
26
|
+
'print "Invalid XML"
|
27
|
+
return invalid
|
28
|
+
endif
|
29
|
+
|
30
|
+
return xml
|
31
|
+
End Function
|
32
|
+
|
33
|
+
Function comma_seperate(string1 As String, string2 As String) As String
|
34
|
+
str = CreateObject("roString")
|
35
|
+
|
36
|
+
if (string1.Len() > 0 AND string2.Len() > 0) then
|
37
|
+
str = string1 + ", " + string2
|
38
|
+
else if (string1.Len() <= 0 AND string2.Len() > 0) then
|
39
|
+
str = string2
|
40
|
+
else if (string1.Len() > 0 AND string2.Len() <= 0) then
|
41
|
+
str = string1
|
42
|
+
end if
|
43
|
+
|
44
|
+
return str
|
45
|
+
End Function
|
@@ -0,0 +1,59 @@
|
|
1
|
+
Function getMRSS(url As Object) As Object
|
2
|
+
feed = initConnection(url)
|
3
|
+
videos = parseVideos( to_xml(feed) )
|
4
|
+
|
5
|
+
return videos
|
6
|
+
End Function
|
7
|
+
|
8
|
+
Function parseVideos(xml as Object) As Object
|
9
|
+
videos = CreateObject("roArray", 100, true)
|
10
|
+
|
11
|
+
if xml = invalid then
|
12
|
+
print "Error Retrieving XML"
|
13
|
+
return videos
|
14
|
+
endif
|
15
|
+
|
16
|
+
if xml.GetName() <> "channel" then
|
17
|
+
print "No channel tag found."
|
18
|
+
return videos
|
19
|
+
endif
|
20
|
+
|
21
|
+
'stop
|
22
|
+
|
23
|
+
for each item in xml.GetNamedElements("item")
|
24
|
+
'stop
|
25
|
+
video = initVideoItem()
|
26
|
+
'video.ContentId = curVideo.id.GetText()
|
27
|
+
title = item.GetNamedElements("media:title")
|
28
|
+
video.Title = title.GetText()
|
29
|
+
video.Description = item.description.GetText()
|
30
|
+
'video.StreamFormat = curVideo.format.GetText()
|
31
|
+
video.StreamFormat = "mp4"
|
32
|
+
'video.Length = curVideo.runtime.GetText()
|
33
|
+
'video.Quality = curVideo.quality.GetText()
|
34
|
+
'video.SDPosterUrl = curVideo.sd_image.GetText()
|
35
|
+
'video.HDPosterUrl = curVideo.hd_image.GetText()
|
36
|
+
|
37
|
+
video.StreamUrls[0] = item.content.GetAttributes().url
|
38
|
+
video.streamQualities[0] = "SD"
|
39
|
+
video.StreamBitrates = 0
|
40
|
+
'n = 0
|
41
|
+
'streams = item.medias.GetChildElements()
|
42
|
+
'for each curStream in streams
|
43
|
+
'video.StreamQualities[n] = curStream.quality.GetText()
|
44
|
+
'video.StreamBitrates[n] = curStream.bitrate.GetText()
|
45
|
+
'video.StreamUrls[n] = curStream.url.GetText()
|
46
|
+
'video.StreamContentIDs[n] = curStream.id.GetText()
|
47
|
+
'n = n + 1
|
48
|
+
'next
|
49
|
+
|
50
|
+
video.ShortDescriptionLine2 = video.Description
|
51
|
+
video.ShortDescriptionLine1 = video.Title
|
52
|
+
|
53
|
+
print video
|
54
|
+
|
55
|
+
videos.Push(video)
|
56
|
+
next
|
57
|
+
|
58
|
+
return videos
|
59
|
+
End Function
|
@@ -0,0 +1,215 @@
|
|
1
|
+
'**********************************************************
|
2
|
+
'** Video Player Example Application - URL Utilities
|
3
|
+
'** November 2009
|
4
|
+
'** Copyright (c) 2009 Roku Inc. All Rights Reserved.
|
5
|
+
'**********************************************************
|
6
|
+
|
7
|
+
REM ******************************************************
|
8
|
+
REM Constucts a URL Transfer object
|
9
|
+
REM ******************************************************
|
10
|
+
|
11
|
+
Function CreateURLTransferObject(url As String) as Object
|
12
|
+
obj = CreateObject("roUrlTransfer")
|
13
|
+
obj.SetPort(CreateObject("roMessagePort"))
|
14
|
+
obj.SetUrl(url)
|
15
|
+
obj.AddHeader("Content-Type", "application/x-www-form-urlencoded")
|
16
|
+
obj.EnableEncodings(true)
|
17
|
+
return obj
|
18
|
+
End Function
|
19
|
+
|
20
|
+
REM ******************************************************
|
21
|
+
REM Url Query builder
|
22
|
+
REM so this is a quick and dirty name/value encoder/accumulator
|
23
|
+
REM ******************************************************
|
24
|
+
|
25
|
+
Function NewHttp(url As String) as Object
|
26
|
+
obj = CreateObject("roAssociativeArray")
|
27
|
+
obj.Http = CreateURLTransferObject(url)
|
28
|
+
obj.FirstParam = true
|
29
|
+
obj.AddParam = http_add_param
|
30
|
+
obj.AddRawQuery = http_add_raw_query
|
31
|
+
obj.GetToStringWithRetry = http_get_to_string_with_retry
|
32
|
+
obj.PrepareUrlForQuery = http_prepare_url_for_query
|
33
|
+
obj.GetToStringWithTimeout = http_get_to_string_with_timeout
|
34
|
+
obj.PostFromStringWithTimeout = http_post_from_string_with_timeout
|
35
|
+
|
36
|
+
if Instr(1, url, "?") > 0 then obj.FirstParam = false
|
37
|
+
|
38
|
+
return obj
|
39
|
+
End Function
|
40
|
+
|
41
|
+
|
42
|
+
REM ******************************************************
|
43
|
+
REM Constucts a URL Transfer object 2
|
44
|
+
REM ******************************************************
|
45
|
+
|
46
|
+
Function CreateURLTransferObject2(url As String, contentHeader As String) as Object
|
47
|
+
obj = CreateObject("roUrlTransfer")
|
48
|
+
obj.SetPort(CreateObject("roMessagePort"))
|
49
|
+
obj.SetUrl(url)
|
50
|
+
obj.AddHeader("Content-Type", contentHeader)
|
51
|
+
obj.EnableEncodings(true)
|
52
|
+
return obj
|
53
|
+
End Function
|
54
|
+
|
55
|
+
REM ******************************************************
|
56
|
+
REM Url Query builder 2
|
57
|
+
REM so this is a quick and dirty name/value encoder/accumulator
|
58
|
+
REM ******************************************************
|
59
|
+
|
60
|
+
Function NewHttp2(url As String, contentHeader As String) as Object
|
61
|
+
obj = CreateObject("roAssociativeArray")
|
62
|
+
obj.Http = CreateURLTransferObject2(url, contentHeader)
|
63
|
+
obj.FirstParam = true
|
64
|
+
obj.AddParam = http_add_param
|
65
|
+
obj.AddRawQuery = http_add_raw_query
|
66
|
+
obj.GetToStringWithRetry = http_get_to_string_with_retry
|
67
|
+
obj.PrepareUrlForQuery = http_prepare_url_for_query
|
68
|
+
obj.GetToStringWithTimeout = http_get_to_string_with_timeout
|
69
|
+
obj.PostFromStringWithTimeout = http_post_from_string_with_timeout
|
70
|
+
|
71
|
+
if Instr(1, url, "?") > 0 then obj.FirstParam = false
|
72
|
+
|
73
|
+
return obj
|
74
|
+
End Function
|
75
|
+
|
76
|
+
|
77
|
+
REM ******************************************************
|
78
|
+
REM HttpEncode - just encode a string
|
79
|
+
REM ******************************************************
|
80
|
+
|
81
|
+
Function HttpEncode(str As String) As String
|
82
|
+
o = CreateObject("roUrlTransfer")
|
83
|
+
return o.Escape(str)
|
84
|
+
End Function
|
85
|
+
|
86
|
+
REM ******************************************************
|
87
|
+
REM Prepare the current url for adding query parameters
|
88
|
+
REM Automatically add a '?' or '&' as necessary
|
89
|
+
REM ******************************************************
|
90
|
+
|
91
|
+
Function http_prepare_url_for_query() As String
|
92
|
+
url = m.Http.GetUrl()
|
93
|
+
if m.FirstParam then
|
94
|
+
url = url + "?"
|
95
|
+
m.FirstParam = false
|
96
|
+
else
|
97
|
+
url = url + "&"
|
98
|
+
endif
|
99
|
+
m.Http.SetUrl(url)
|
100
|
+
return url
|
101
|
+
End Function
|
102
|
+
|
103
|
+
REM ******************************************************
|
104
|
+
REM Percent encode a name/value parameter pair and add the
|
105
|
+
REM the query portion of the current url
|
106
|
+
REM Automatically add a '?' or '&' as necessary
|
107
|
+
REM Prevent duplicate parameters
|
108
|
+
REM ******************************************************
|
109
|
+
|
110
|
+
Function http_add_param(name As String, val As String) as Void
|
111
|
+
q = m.Http.Escape(name)
|
112
|
+
q = q + "="
|
113
|
+
url = m.Http.GetUrl()
|
114
|
+
if Instr(1, url, q) > 0 return 'Parameter already present
|
115
|
+
q = q + m.Http.Escape(val)
|
116
|
+
m.AddRawQuery(q)
|
117
|
+
End Function
|
118
|
+
|
119
|
+
REM ******************************************************
|
120
|
+
REM Tack a raw query string onto the end of the current url
|
121
|
+
REM Automatically add a '?' or '&' as necessary
|
122
|
+
REM ******************************************************
|
123
|
+
|
124
|
+
Function http_add_raw_query(query As String) as Void
|
125
|
+
url = m.PrepareUrlForQuery()
|
126
|
+
url = url + query
|
127
|
+
m.Http.SetUrl(url)
|
128
|
+
End Function
|
129
|
+
|
130
|
+
REM ******************************************************
|
131
|
+
REM Performs Http.AsyncGetToString() in a retry loop
|
132
|
+
REM with exponential backoff. To the outside
|
133
|
+
REM world this appears as a synchronous API.
|
134
|
+
REM ******************************************************
|
135
|
+
|
136
|
+
Function http_get_to_string_with_retry() as String
|
137
|
+
timeout% = 1500
|
138
|
+
num_retries% = 5
|
139
|
+
|
140
|
+
str = ""
|
141
|
+
while num_retries% > 0
|
142
|
+
' print "httpget try " + itostr(num_retries%)
|
143
|
+
if (m.Http.AsyncGetToString())
|
144
|
+
event = wait(timeout%, m.Http.GetPort())
|
145
|
+
if type(event) = "roUrlEvent"
|
146
|
+
str = event.GetString()
|
147
|
+
exit while
|
148
|
+
elseif event = invalid
|
149
|
+
m.Http.AsyncCancel()
|
150
|
+
REM reset the connection on timeouts
|
151
|
+
m.Http = CreateURLTransferObject(m.Http.GetUrl())
|
152
|
+
timeout% = 2 * timeout%
|
153
|
+
else
|
154
|
+
print "roUrlTransfer::AsyncGetToString(): unknown event"
|
155
|
+
endif
|
156
|
+
endif
|
157
|
+
|
158
|
+
num_retries% = num_retries% - 1
|
159
|
+
end while
|
160
|
+
|
161
|
+
return str
|
162
|
+
End Function
|
163
|
+
|
164
|
+
REM ******************************************************
|
165
|
+
REM Performs Http.AsyncGetToString() with a single timeout in seconds
|
166
|
+
REM To the outside world this appears as a synchronous API.
|
167
|
+
REM ******************************************************
|
168
|
+
|
169
|
+
Function http_get_to_string_with_timeout(seconds as Integer) as String
|
170
|
+
timeout% = 1000 * seconds
|
171
|
+
|
172
|
+
str = ""
|
173
|
+
m.Http.EnableFreshConnection(true) 'Don't reuse existing connections
|
174
|
+
if (m.Http.AsyncGetToString())
|
175
|
+
event = wait(timeout%, m.Http.GetPort())
|
176
|
+
if type(event) = "roUrlEvent"
|
177
|
+
str = event.GetString()
|
178
|
+
elseif event = invalid
|
179
|
+
Dbg("AsyncGetToString timeout")
|
180
|
+
m.Http.AsyncCancel()
|
181
|
+
else
|
182
|
+
Dbg("AsyncGetToString unknown event", event)
|
183
|
+
endif
|
184
|
+
endif
|
185
|
+
|
186
|
+
return str
|
187
|
+
End Function
|
188
|
+
|
189
|
+
REM ******************************************************
|
190
|
+
REM Performs Http.AsyncPostFromString() with a single timeout in seconds
|
191
|
+
REM To the outside world this appears as a synchronous API.
|
192
|
+
REM ******************************************************
|
193
|
+
|
194
|
+
Function http_post_from_string_with_timeout(val As String, seconds as Integer) as String
|
195
|
+
timeout% = 1000 * seconds
|
196
|
+
|
197
|
+
str = ""
|
198
|
+
' m.Http.EnableFreshConnection(true) 'Don't reuse existing connections
|
199
|
+
if (m.Http.AsyncPostFromString(val))
|
200
|
+
event = wait(timeout%, m.Http.GetPort())
|
201
|
+
if type(event) = "roUrlEvent"
|
202
|
+
print "1"
|
203
|
+
str = event.GetString()
|
204
|
+
elseif event = invalid
|
205
|
+
print "2"
|
206
|
+
Dbg("AsyncPostFromString timeout")
|
207
|
+
m.Http.AsyncCancel()
|
208
|
+
else
|
209
|
+
print "3"
|
210
|
+
Dbg("AsyncPostFromString unknown event", event)
|
211
|
+
endif
|
212
|
+
endif
|
213
|
+
|
214
|
+
return str
|
215
|
+
End Function
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Function initVideoItem() As Object
|
2
|
+
o = CreateObject("roAssociativeArray")
|
3
|
+
|
4
|
+
o.ContentId = ""
|
5
|
+
o.Title = ""
|
6
|
+
o.Description = ""
|
7
|
+
o.StreamFormat = ""
|
8
|
+
o.Live = false
|
9
|
+
o.Length = ""
|
10
|
+
o.SDImageUrl = ""
|
11
|
+
o.HDImageUrl = ""
|
12
|
+
o.StreamQualities = CreateObject("roArray", 5, true)
|
13
|
+
o.StreamBitrates = CreateObject("roArray", 5, true)
|
14
|
+
o.StreamUrls = CreateObject("roArray", 5, true)
|
15
|
+
o.StreamContentIDs = CreateObject("roArray", 5, true)
|
16
|
+
return o
|
17
|
+
End Function
|
@@ -0,0 +1,36 @@
|
|
1
|
+
Function showMediaPlayer(video As Object)
|
2
|
+
port = CreateObject("roMessagePort")
|
3
|
+
screen = CreateObject("roVideoScreen")
|
4
|
+
screen.SetMessagePort(port)
|
5
|
+
|
6
|
+
screen.SetPositionNotificationPeriod(30)
|
7
|
+
screen.SetContent(video)
|
8
|
+
screen.Show()
|
9
|
+
|
10
|
+
'Uncomment his line to dump the contents of the video to be played
|
11
|
+
PrintAA(video)
|
12
|
+
|
13
|
+
while true
|
14
|
+
msg = wait(0, port)
|
15
|
+
|
16
|
+
if type(msg) = "roVideoScreenEvent" then
|
17
|
+
if msg.isScreenClosed()
|
18
|
+
print "Screen closed"
|
19
|
+
exit while
|
20
|
+
elseif msg.isRequestFailed()
|
21
|
+
print "Video request failure: "; msg.GetIndex(); " " msg.GetData()
|
22
|
+
elseif msg.isStatusMessage()
|
23
|
+
print "Video status: "; msg.GetIndex(); " " msg.GetData()
|
24
|
+
elseif msg.isButtonPressed()
|
25
|
+
print "Button pressed: "; msg.GetIndex(); " " msg.GetData()
|
26
|
+
elseif msg.isPlaybackPosition() then
|
27
|
+
nowpos = msg.GetIndex()
|
28
|
+
'RegWrite(video.ContentId, nowpos.toStr())
|
29
|
+
else
|
30
|
+
print "Unexpected event type: "; msg.GetType()
|
31
|
+
end if
|
32
|
+
else
|
33
|
+
print "Unexpected message class: "; type(msg)
|
34
|
+
end if
|
35
|
+
end while
|
36
|
+
End Function
|
@@ -0,0 +1,65 @@
|
|
1
|
+
'******************************************************
|
2
|
+
'** Video Player Example Application -- Poster Screen
|
3
|
+
'** November 2009
|
4
|
+
'** Copyright (c) 2009 Roku Inc. All Rights Reserved.
|
5
|
+
'******************************************************
|
6
|
+
|
7
|
+
'******************************************************
|
8
|
+
'** Perform any startup/initialization stuff prior to
|
9
|
+
'** initially showing the screen.
|
10
|
+
'******************************************************
|
11
|
+
Function prePosterScreen(breadA=invalid, breadB=invalid) As Object
|
12
|
+
port=CreateObject("roMessagePort")
|
13
|
+
screen = CreateObject("roPosterScreen")
|
14
|
+
screen.SetMessagePort(port)
|
15
|
+
if breadA<>invalid and breadB<>invalid then
|
16
|
+
screen.SetBreadcrumbText(breadA, breadB)
|
17
|
+
end if
|
18
|
+
|
19
|
+
screen.SetListStyle("flat-category")
|
20
|
+
screen.SetListDisplayMode("photo-fit")
|
21
|
+
|
22
|
+
return screen
|
23
|
+
End Function
|
24
|
+
|
25
|
+
|
26
|
+
'******************************************************
|
27
|
+
'** Display the home screen and wait for events from
|
28
|
+
'** the screen. The screen will show retreiving while
|
29
|
+
'** we fetch and parse the feeds for the game posters
|
30
|
+
'******************************************************
|
31
|
+
Function showPosterScreen(screen As Object, feedURL As String) As Integer
|
32
|
+
curVideo = 0
|
33
|
+
|
34
|
+
videos = getMRSS(feedURL)
|
35
|
+
|
36
|
+
if videos.Count() > 0 then
|
37
|
+
screen.SetContentList(videos)
|
38
|
+
else
|
39
|
+
screen.ShowMessage("No videos found for this channel")
|
40
|
+
end if
|
41
|
+
|
42
|
+
screen.Show()
|
43
|
+
|
44
|
+
while true
|
45
|
+
print "POSTER SCREEN!"
|
46
|
+
|
47
|
+
msg = wait(0, screen.GetMessagePort())
|
48
|
+
if type(msg) = "roPosterScreenEvent" then
|
49
|
+
print "showPosterScreen | msg = "; msg.GetMessage() " | index = "; msg.GetIndex()
|
50
|
+
print msg.GetMessage()
|
51
|
+
print "POSTER SCREEN! MSG"
|
52
|
+
|
53
|
+
if msg.isListFocused() then
|
54
|
+
curVideo = 0
|
55
|
+
screen.SetFocusedListItem(curVideo)
|
56
|
+
else if msg.isListItemSelected() then
|
57
|
+
curVideo = msg.GetIndex()
|
58
|
+
showMediaPlayer(videos[curVideo])
|
59
|
+
else if msg.isScreenClosed() then
|
60
|
+
print "Screen Closed!"
|
61
|
+
return -1
|
62
|
+
end if
|
63
|
+
end If
|
64
|
+
end while
|
65
|
+
End Function
|
@@ -0,0 +1,30 @@
|
|
1
|
+
'*************************************************************
|
2
|
+
'** Set the configurable theme attributes for the application
|
3
|
+
'**
|
4
|
+
'** Configure the custom overhang and Logo attributes
|
5
|
+
'** Theme attributes affect the branding of the application
|
6
|
+
'** and are artwork, colors and offsets specific to the app
|
7
|
+
'*************************************************************
|
8
|
+
|
9
|
+
Sub initTheme()
|
10
|
+
|
11
|
+
app = CreateObject("roAppManager")
|
12
|
+
theme = CreateObject("roAssociativeArray")
|
13
|
+
|
14
|
+
theme.OverhangOffsetSD_X = "45"
|
15
|
+
theme.OverhangOffsetSD_Y = "30"
|
16
|
+
theme.OverhangSliceSD = "pkg:/images/banner-sd.png"
|
17
|
+
theme.OverhangLogoSD = "pkg:/images/banner-logo-sd.png"
|
18
|
+
|
19
|
+
theme.OverhangOffsetHD_X = "75"
|
20
|
+
theme.OverhangOffsetHD_Y = "40"
|
21
|
+
theme.OverhangSliceHD = "pkg:/images/banner-hd.png"
|
22
|
+
theme.OverhangLogoHD = "pkg:/images/banner-logo-hd.png"
|
23
|
+
|
24
|
+
theme.FilterBannerActiveColor = "#FFFFFF"
|
25
|
+
theme.FilterBannerInactiveColor = "#0055a0"
|
26
|
+
theme.FilterBannerSideColor = "#0055a0"
|
27
|
+
|
28
|
+
app.SetTheme(theme)
|
29
|
+
|
30
|
+
End Sub
|