metadata

Check-in [61c081ac82]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Get resumption after idle shut-off working.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:61c081ac822105a9580af3396db41ccab1b055aac1339015c080de771e713e8c
User & Date: vashon 2018-06-19 23:56:56
Context
2018-06-19
23:57
Prep for putting on sources.vsta.org check-in: 71c1004e97 user: vashon tags: trunk
23:56
Get resumption after idle shut-off working. check-in: 61c081ac82 user: vashon tags: trunk
16:55
Tighten whitespace stripping. Test idle timeout. check-in: a0023c3b13 user: vashon tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to stream.py.

4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
..
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
...
224
225
226
227
228
229
230





231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
...
252
253
254
255
256
257
258




259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#
import sys, threading, json, urlparse, time, socket, string

# We want to strip null bytes as well
Whitespace = string.whitespace + "\x00"

# How many seconds of non-use of a stream before we shut it down
IDLE=600.0


# HTTP headers to get Icecast metadata
Args = """Icy-Metadata: 1\r
Access-Control-Allow-Origin: *\r
User-Agent: mplayer-metadata\r
"""

................................................................................
    def __init__(self, sym, url):
	# Sym and URL
	self.sym = sym
	r = urlparse.urlparse(url)
	self.host = r.hostname
	self.port = r.port or 80
	self.path = r.path

	# Connect to the stream
	s = self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect( (self.host, self.port) )


	# Current generation of content
	self.gen = 0

	# Current JSON result for the self.gen value
	self.json = None

................................................................................
    # This function only returns on errors like aborted streams.
    # On idle timeout, it simply exits after clearing itself
    #  from the stream state.
    def watch(self):
	global IDLE, Args, Whitespace

	# Request it
	s = self.sock
	sys.stderr.write("sending to %s\n" % (self.host,))
	args = Args + ("%s:%d" % (self.host, self.port)) + "\r\n"
	s.send("GET %s HTTP/1.0\r\n%s\r\n" %
	    (self.path, args))

	# Assemble the HTTP response headers.  We'll likely have
	#  more than that, which will be passed down to the loop
	#  below.
	headers = {}
	while True:
................................................................................
	    buf = self.recv(intvl)
	    if buf is None:
		return

	    # An initial byte is the number of 16-byte chunks in
	    #  the stream.
	    try:
		c = s.recv(1)
	    except:
		return
	    if not c:
		sys.stderr.write("No metadata size byte\n")
		return
	    paksize = (ord(c) << 4)
	    if not paksize:
		continue

	    # No metadata update needed yet
	    if not paksize:
		continue
	    try:
		buf = s.recv(paksize)
	    except:
		return
	    if len(buf) != paksize:

		sys.stderr.write("metadata got %d\n" % (len(buf),))
		return

	    # Decode metadata into a dict for JSON purposes
	    sys.stderr.write(" metadata: %s\n" % (buf,))
	    res = {}
	    for p in buf.split(';'):
		p = p.strip(Whitespace)
................................................................................
		sys.stderr.write("idle reached for %s\n" % (self.host,))
		return

    # Our main worker is self.watch(), right above.  This wraps it,
    #  so that it can return here on network failure, and we can
    #  tear down this thread.
    def run_watch(self):





	try:
	    self.watch()
	except:
	    pass
	self.thread = None

	# Shut off the stream
	try:
	    self.sock.close()
	except:
	    pass
	self.sock = None

	# Kick free any pending waiters
	res = {"gen": self.gen, "err": "closed"}
	self.gen += 1
................................................................................

    # We're running on a thread for a given HTTP request.
    # Send back metadata, now, or in the future when it
    #  changes (long polling)
    def serve(self, req, gen):

	# If we already have the answer they want, send it back




	if self.gen and (gen < self.gen):
	    return True,req.send_result(self.json, "application/json")

	# Put ourselves on the queue now; we'll sleep once we've
	#  perhaps started up a service thread
	s = threading.Semaphore(0)
	self.waiters.append(s)

	# Start a thread if needed
	if self.thread is None:
	    self.thread = t = \
		threading.Thread(target=self.run_watch)
	    t.start()

	# Wait for the thread to find metadata and wake us up
	s.acquire()

	# We're back, send the contents
	assert self.json
	return True,req.send_result(self.json, "application/json")







|
>







 







<
<
<
<
>







 







<


|







 







|












<
|
<
<
<
>
|







 







>
>
>
>
>








|







 







>
>
>
>
|
|



|
|








|




4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
23
24
25
26
27
28
29




30
31
32
33
34
35
36
37
..
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

173



174
175
176
177
178
179
180
181
182
...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#
import sys, threading, json, urlparse, time, socket, string

# We want to strip null bytes as well
Whitespace = string.whitespace + "\x00"

# How many seconds of non-use of a stream before we shut it down
# IDLE=600.0
IDLE=150.0

# HTTP headers to get Icecast metadata
Args = """Icy-Metadata: 1\r
Access-Control-Allow-Origin: *\r
User-Agent: mplayer-metadata\r
"""

................................................................................
    def __init__(self, sym, url):
	# Sym and URL
	self.sym = sym
	r = urlparse.urlparse(url)
	self.host = r.hostname
	self.port = r.port or 80
	self.path = r.path




	self.sock = None

	# Current generation of content
	self.gen = 0

	# Current JSON result for the self.gen value
	self.json = None

................................................................................
    # This function only returns on errors like aborted streams.
    # On idle timeout, it simply exits after clearing itself
    #  from the stream state.
    def watch(self):
	global IDLE, Args, Whitespace

	# Request it

	sys.stderr.write("sending to %s\n" % (self.host,))
	args = Args + ("%s:%d" % (self.host, self.port)) + "\r\n"
	self.sock.send("GET %s HTTP/1.0\r\n%s\r\n" %
	    (self.path, args))

	# Assemble the HTTP response headers.  We'll likely have
	#  more than that, which will be passed down to the loop
	#  below.
	headers = {}
	while True:
................................................................................
	    buf = self.recv(intvl)
	    if buf is None:
		return

	    # An initial byte is the number of 16-byte chunks in
	    #  the stream.
	    try:
		c = self.sock.recv(1)
	    except:
		return
	    if not c:
		sys.stderr.write("No metadata size byte\n")
		return
	    paksize = (ord(c) << 4)
	    if not paksize:
		continue

	    # No metadata update needed yet
	    if not paksize:
		continue

	    buf = self.recv(paksize)



	    if not buf:
		sys.stderr.write(" metadata not received\n")
		return

	    # Decode metadata into a dict for JSON purposes
	    sys.stderr.write(" metadata: %s\n" % (buf,))
	    res = {}
	    for p in buf.split(';'):
		p = p.strip(Whitespace)
................................................................................
		sys.stderr.write("idle reached for %s\n" % (self.host,))
		return

    # Our main worker is self.watch(), right above.  This wraps it,
    #  so that it can return here on network failure, and we can
    #  tear down this thread.
    def run_watch(self):

	# Connect to the stream
	s = self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect( (self.host, self.port) )

	try:
	    self.watch()
	except:
	    pass
	self.thread = None

	# Shut off the stream
	try:
	    s.close()
	except:
	    pass
	self.sock = None

	# Kick free any pending waiters
	res = {"gen": self.gen, "err": "closed"}
	self.gen += 1
................................................................................

    # We're running on a thread for a given HTTP request.
    # Send back metadata, now, or in the future when it
    #  changes (long polling)
    def serve(self, req, gen):

	# If we already have the answer they want, send it back
	# If there's an answer but no thread, this is an idle timeout
	#  from the past.  We wait until we get a new, relevant
	#  answer.
	if self.thread is not None:
	    if self.gen and (gen < self.gen):
		return True,req.send_result(self.json, "application/json")

	# Put ourselves on the queue now; we'll sleep once we've
	#  perhaps started up a service thread
	sema = threading.Semaphore(0)
	self.waiters.append(sema)

	# Start a thread if needed
	if self.thread is None:
	    self.thread = t = \
		threading.Thread(target=self.run_watch)
	    t.start()

	# Wait for the thread to find metadata and wake us up
	sema.acquire()

	# We're back, send the contents
	assert self.json
	return True,req.send_result(self.json, "application/json")