-
Notifications
You must be signed in to change notification settings - Fork 11
/
listener.py
139 lines (118 loc) · 4.44 KB
/
listener.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# -*- coding: utf-8 -*-
import asyncore
from asynchat import async_chat
import socket
import config
from threading import Thread
import manager
import logging
def start():
global listener, thread
listener = Listener()
def wrapper():
logging.info("THREADING: Started listener")
asyncore.loop()
logging.info("THREADING: Stopped listener")
thread = Thread(target=wrapper)
thread.name = "Listener"
thread.daemon = 1
thread.start()
return listener
def shutdown():
try:
listener.close()
except (NameError):
pass
class Listener(async_chat):
READING_DATA = 0
READING_META = 1
READING_METASIZE = 2
READING_HEADERS = 3
def __init__(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((config.icecast_host, config.icecast_port))
async_chat.__init__(self, sock=sock)
self.ibuffer = []
self.obuffer = 'GET {mount} HTTP/1.1\r\nHOST: {host}\r\nUser-Agent: Hanyuu-sama\r\nIcy-MetaData: 1\r\n\r\n'.format(
mount=config.icecast_mount, host=config.icecast_host)
self.push(self.obuffer)
self.set_terminator('\r\n\r\n')
self.status = self.READING_HEADERS
self.active = True
def shutdown(self):
self.close_when_done()
thread.join()
return None
def collect_incoming_data(self, data):
self.ibuffer.append(data)
def handle_close(self):
self.active = False
self.close() # don't forget to close the original
def parse_headers(self, headers):
self.headers = {}
headers = headers.split('\r\n')
self.headers["status"] = tuple(headers[0].split(" "))
for header in headers[1:]:
if (not header):
break
splitted = header.split(":")
self.headers[splitted[0].strip()] = splitted[1].strip()
def found_terminator(self):
if (self.status == self.READING_DATA):
# We read the current chunk of data
# Flush our data, we don't need it right now
self.ibuffer = []
# Terminator to 1 to read the size of metadata incoming
self.set_terminator(1)
self.status = self.READING_METASIZE
elif (self.status == self.READING_HEADERS):
logging.debug("Reading headers")
self.reading_headers = False
self.parse_headers("".join(self.ibuffer))
self.ibuffer = []
# Get the meta int value
try:
self.metaint = int(self.headers["icy-metaint"])
except (KeyError):
# Incorrect header, kill ourself after a small timeout
self.handle_close()
# set terminator to that int
self.set_terminator(self.metaint)
# Icecast always sends data after the headers, so go to that mode
self.status = self.READING_DATA
elif (self.status == self.READING_METASIZE):
# Do an ord() and then times 16 to get the meta length in bytes
self.metalen = ord(self.ibuffer[0]) * 16
# Flush the byte afte reading it
self.ibuffer = []
if (self.metalen == 0):
self.set_terminator(self.metaint)
self.status = self.READING_DATA
else:
self.set_terminator(self.metalen)
self.status = self.READING_META
elif (self.status == self.READING_META):
# finally reading some metadata
incoming_string = "".join(self.ibuffer)
incoming_string = incoming_string.rstrip("\x00")
metadata = ""
for part in incoming_string.split("';"):
try:
if (part.strip().split("='")[0].lower() == "streamtitle"):
metadata = "='".join(part.split("='")[1:])
except (IndexError):
pass
if (metadata in config.fallbacks):
self.handle_close()
return
if (metadata != ""):
new_song = manager.Song(meta=metadata)
np = manager.NP()
if (np != new_song):
np.change(new_song)
# flush buffer
self.ibuffer = []
# Change to metaint again for reading data
self.set_terminator(self.metaint)
# go back to reading data
self.status = self.READING_DATA