metadata

Check-in [9e3a6abf61]
Login

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

Overview
Comment:Bringup
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:9e3a6abf61109a67f48106cfbf831b6c3e0496648e5dc967e428eb934fd76a14
User & Date: vashon 2018-06-18 22:58:31
Context
2018-06-18
23:53
Bringup; getting metadata from Radio Paradise out to clients check-in: 3d69813d77 user: vashon tags: trunk
22:58
Bringup check-in: 9e3a6abf61 user: vashon tags: trunk
22:40
Idle shutdown of stream monitoring check-in: 8a1cc96ebf user: vashon tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to etc/config.


1
2
3
4
5
6
7


serve http
    port 2020

# Radio Paradise
stream rp http://stream-tx3.radioparadise.com:80/mp3-32

>







1
2
3
4
5
6
7
8
service metadata

serve http
    port 2020

# Radio Paradise
stream rp http://stream-tx3.radioparadise.com:80/mp3-32

Changes to get.py.

42
43
44
45
46
47
48
49
		gen = int(self.vals["gen"])
	    except:
		return True,self.send_error(400)
	else:
	    gen = 0

	# Handle this request for this stream
	return strm.serve(gen)







|
42
43
44
45
46
47
48
49
		gen = int(self.vals["gen"])
	    except:
		return True,self.send_error(400)
	else:
	    gen = 0

	# Handle this request for this stream
	return strm.serve(self, gen)

Changes to main.py.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
58
59
60
61
62
63
64







65
66
67
68
from get import GET_mixin
from stream import Stream
import pdb

# Tie our various handlers together
class App_Handler(chore.handlers.Chore_Handler, GET_mixin):
    def __init__(self, conn, tup, approot):
	Chore_Handler.__init__(self, conn, tup, approot,
	    (GET_mixin.__init__,))

# Load our configuration file
#
# This includes configuring our config file elements,
#  then processing the supplied file.
def load_cfg(fn):

................................................................................
    chore.www.add_config(c)

    # Sources of metadata
    #  stream <symbol> <url>
    # i.e.,
    #  stream rp http://stream-tx3.radioparadise.com:80/mp3-32
    c.args.add( ("stream",) )


    # Parse the input
    cfg = c.load_cfg(fn)

    return cfg

# Root of our app server
class App(chore.server.Server):

    def __init__(self, config):
	global DBlove

	# Let Chore handle most things
	super().__init__(self, config, App_Handler);

	# State per stream
	self.streams = {}

	# Set initial dict contents
	for sym,url in config["stream"]:
	    s = Stream(sym, url)
................................................................................
	    self.streams[sym] = s

if __name__ == "__main__":
    if len(sys.argv) != 2:
	sys.stderr.write("Usage is: %s <config-file>\n" %
	    (sys.argv[0],))
	sys.exit(1)








    # CLI if needed
    while True:
	time.sleep(60)







<
|







 







>













|







 







>
>
>
>
>
>
>




10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from get import GET_mixin
from stream import Stream
import pdb

# Tie our various handlers together
class App_Handler(chore.handlers.Chore_Handler, GET_mixin):
    def __init__(self, conn, tup, approot):

	super().__init__(conn, tup, approot, (GET_mixin.__init__,))

# Load our configuration file
#
# This includes configuring our config file elements,
#  then processing the supplied file.
def load_cfg(fn):

................................................................................
    chore.www.add_config(c)

    # Sources of metadata
    #  stream <symbol> <url>
    # i.e.,
    #  stream rp http://stream-tx3.radioparadise.com:80/mp3-32
    c.args.add( ("stream",) )
    c.mults.add( ("stream",) )

    # Parse the input
    cfg = c.load_cfg(fn)

    return cfg

# Root of our app server
class App(chore.server.Server):

    def __init__(self, config):
	global DBlove

	# Let Chore handle most things
	super().__init__(config, App_Handler);

	# State per stream
	self.streams = {}

	# Set initial dict contents
	for sym,url in config["stream"]:
	    s = Stream(sym, url)
................................................................................
	    self.streams[sym] = s

if __name__ == "__main__":
    if len(sys.argv) != 2:
	sys.stderr.write("Usage is: %s <config-file>\n" %
	    (sys.argv[0],))
	sys.exit(1)

    # Create the server with config
    cfg = load_cfg(sys.argv[1])
    app = App(cfg)

    # Web service
    app.start_http()

    # CLI if needed
    while True:
	time.sleep(60)

Changes to stream.py.

1
2
3
4
5
6
7
8
9
10
11
12
..
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#
# stream.py
#	Code for serving metadata on a  given stream
#
import sys, threading, json, urlparse, time

# 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
User-Agent: mplayer-metadata\r
................................................................................
	# Current generation of content
	self.gen = 0

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

	# Waiters for new self.gen value
	self.queue = []

	# Thread pulling on the stream
	self.thread = None

	# Last time we served a user
	self.idle = time.time()

................................................................................
	    if c == '\n':
		return res
	    res += c

    # Wake up all waiters
    def wakeups(self):
	ws = tuple(self.waiters)
	del self.waters[:]
	while ws:
	    sema = self.waiters.pop()
	    sema.release()

    # Pull from a stream with Icecast metadata.
    # Because Icecast metadata is kind of a hairball of legacy and
    #  modern, we just talk TCP to it ourselves.
    # This function only returns on errors like aborted streams.
    # On idle timeout, it simply exits after clearing itself
................................................................................
	# 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.queue.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")




|







 







|







 







|
|
<







 







|













1
2
3
4
5
6
7
8
9
10
11
12
..
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
..
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#
# stream.py
#	Code for serving metadata on a  given stream
#
import sys, threading, json, urlparse, time, socket

# 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
User-Agent: mplayer-metadata\r
................................................................................
	# Current generation of content
	self.gen = 0

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

	# Waiters for new self.gen value
	self.waiters = []

	# Thread pulling on the stream
	self.thread = None

	# Last time we served a user
	self.idle = time.time()

................................................................................
	    if c == '\n':
		return res
	    res += c

    # Wake up all waiters
    def wakeups(self):
	ws = tuple(self.waiters)
	del self.waiters[:]
	for sema in ws:

	    sema.release()

    # Pull from a stream with Icecast metadata.
    # Because Icecast metadata is kind of a hairball of legacy and
    #  modern, we just talk TCP to it ourselves.
    # This function only returns on errors like aborted streams.
    # On idle timeout, it simply exits after clearing itself
................................................................................
	# 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")