| Trees | Indices | Help |
|
|---|
|
|
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 #
4 # Copyright (C) 2006-2011 Guillaume Pellerin
5
6 # <yomguy@parisson.com>
7
8 # This software is a computer program whose purpose is to stream audio
9 # and video data through icecast2 servers.
10
11 # This software is governed by the CeCILL license under French law and
12 # abiding by the rules of distribution of free software. You can use,
13 # modify and/ or redistribute the software under the terms of the CeCILL
14 # license as circulated by CEA, CNRS and INRIA at the following URL
15 # "http://www.cecill.info".
16
17 # As a counterpart to the access to the source code and rights to copy,
18 # modify and redistribute granted by the license, users are provided only
19 # with a limited warranty and the software's author, the holder of the
20 # economic rights, and the successive licensors have only limited
21 # liability.
22
23 # In this respect, the user's attention is drawn to the risks associated
24 # with loading, using, modifying and/or developing or reproducing the
25 # software by the user in light of its specific status of free software,
26 # that may mean that it is complicated to manipulate, and that also
27 # therefore means that it is reserved for developers and experienced
28 # professionals having in-depth computer knowledge. Users are therefore
29 # encouraged to load and test the software's suitability as regards their
30 # requirements in conditions enabling the security of their systems and/or
31 # data to be ensured and, more generally, to use and operate it in the
32 # same conditions as regards security.
33
34 # The fact that you are presently reading this means that you have had
35 # knowledge of the CeCILL license and that you accept its terms.
36
37 # Author: Guillaume Pellerin <yomguy@parisson.com>
38
39 import os
40 import shout
41 import Queue
42 import datetime
43 import mimetypes
44 import hashlib
45 from threading import Thread
46 from deefuzzer.station import *
47 from deefuzzer.tools import *
48
49 mimetypes.add_type('application/x-yaml', '.yaml')
50
51
53 """a DeeFuzzer diffuser"""
54
55 logger = None
56 m3u = None
57 rss = None
58 station_settings = []
59 station_instances = {}
60 watchfolder = {}
61 logqueue = Queue.Queue()
62 mainLoop = False
63 ignoreErrors = False
64 maxretry = 0
65
67 Thread.__init__(self)
68 self.conf_file = conf_file
69 self.conf = get_conf_dict(self.conf_file)
70
71 if 'deefuzzer' not in self.conf:
72 return
73
74 # Get the log setting first (if possible)
75 log_file = str(self.conf['deefuzzer'].pop('log', ''))
76 self.log_dir = os.sep.join(log_file.split(os.sep)[:-1])
77 if not os.path.exists(self.log_dir) and self.log_dir:
78 os.makedirs(self.log_dir)
79 self.logger = QueueLogger(log_file, self.logqueue)
80 self.logger.start()
81
82 for key in self.conf['deefuzzer'].keys():
83 if key == 'm3u':
84 self.m3u = str(self.conf['deefuzzer'][key])
85
86 elif key == 'ignoreerrors':
87 # Ignore errors and continue as long as possible
88 self.ignoreErrors = bool(self.conf['deefuzzer'][key])
89
90 elif key == 'maxretry':
91 # Maximum number of attempts to restart the stations on crash.
92 self.maxretry = int(self.conf['deefuzzer'][key])
93
94 elif key == 'station':
95 # Load station definitions from the main config file
96 if not isinstance(self.conf['deefuzzer'][key], list):
97 self.add_station(self.conf['deefuzzer'][key])
98 else:
99 for s in self.conf['deefuzzer'][key]:
100 self.add_station(s)
101
102 elif key == 'stationconfig':
103 # Load additional station definitions from the requested folder
104 self.load_stations_fromconfig(self.conf['deefuzzer'][key])
105
106 elif key == 'stationfolder':
107 # Create stations automagically from a folder structure
108 if isinstance(self.conf['deefuzzer'][key], dict):
109 self.watchfolder = self.conf['deefuzzer'][key]
110 else:
111 setattr(self, key, self.conf['deefuzzer'][key])
112
113 # Set the deefuzzer logger
114 self._info('Starting DeeFuzzer')
115 self._info('Using libshout version %s' % shout.version())
116 self._info('Number of stations : ' + str(len(self.station_settings)))
117
119 try:
120 obj = {'msg': 'Core: ' + str(msg), 'level': level}
121 self.logqueue.put(obj)
122 except:
123 pass
124
126 self._log('info', msg)
127
129 self._log('err', msg)
130
132 m3u_dir = os.sep.join(self.m3u.split(os.sep)[:-1])
133 if not os.path.exists(m3u_dir) and m3u_dir:
134 os.makedirs(m3u_dir)
135 m3u = open(self.m3u, 'w')
136 m3u.write('#EXTM3U\n')
137 for k in self.station_instances.keys():
138 s = self.station_instances[k]
139 m3u.write('#EXTINF:%s,%s - %s\n' % ('-1', s.short_name, s.channel.name))
140 m3u.write('http://' + s.channel.host + ':' + str(s.channel.port) + s.channel.mount + '\n')
141 m3u.close()
142 self._info('Writing M3U file to : ' + self.m3u)
143
145 """Scan a folder for subfolders containing media, and make stations from them all."""
146
147 options = self.watchfolder
148 if 'folder' not in options:
149 # We have no folder specified. Bail.
150 return
151
152 if self.mainLoop:
153 if 'livecreation' not in options:
154 # We have no folder specified. Bail.
155 return
156
157 if int(options['livecreation']) == 0:
158 # Livecreation not specified. Bail.
159 return
160
161 folder = str(options['folder'])
162 if not os.path.isdir(folder):
163 # The specified path is not a folder. Bail.
164 return
165
166 # This makes the log file a lot more verbose. Commented out since we report on new stations anyway.
167 # self._info('Scanning folder ' + folder + ' for stations')
168
169 if 'infos' not in options:
170 options['infos'] = {}
171 if 'short_name' not in options['infos']:
172 options['infos']['short_name'] = '[name]'
173
174 files = os.listdir(folder)
175 for file in files:
176 filepath = os.path.join(folder, file)
177 if os.path.isdir(filepath):
178 if folder_contains_music(filepath):
179 self.create_station(filepath, options)
180
182 try:
183 for s in self.station_settings:
184 if 'infos' not in s:
185 continue
186 if 'short_name' not in s['infos']:
187 continue
188 if s['infos']['short_name'] == name:
189 return True
190 return False
191 except:
192 pass
193 return True
194
196 """Create a station definition for a folder given the specified options."""
197
198 s = {}
199 path, name = os.path.split(folder)
200 if self.station_exists(name):
201 return
202 self._info('Creating station for folder ' + folder)
203 d = dict(path=folder, name=name)
204 for i in options.keys():
205 if 'folder' not in i:
206 s[i] = replace_all(options[i], d)
207 if 'media' not in s:
208 s['media'] = {}
209 s['media']['source'] = folder
210
211 self.add_station(s)
212
214 """Load one or more configuration files looking for stations."""
215
216 if isinstance(folder, dict) or isinstance(folder, list):
217 # We were given a list or dictionary. Loop though it and load em all
218 for f in folder:
219 self.load_station_configs(f)
220 return
221
222 if os.path.isfile(folder):
223 # We have a file specified. Load just that file.
224 self.load_station_config(folder)
225 return
226
227 if not os.path.isdir(folder):
228 # Whatever we have, it's not either a file or folder. Bail.
229 return
230
231 self._info('Loading station config files in ' + folder)
232 files = os.listdir(folder)
233 for file in files:
234 filepath = os.path.join(folder, file)
235 if os.path.isfile(filepath):
236 self.load_station_config(filepath)
237
239 """Load station configuration(s) from a config file."""
240
241 self._info('Loading station config file ' + file)
242 stationdef = get_conf_dict(file)
243 if isinstance(stationdef, dict):
244 if 'station' in stationdef:
245 if isinstance(stationdef['station'], dict):
246 self.add_station(stationdef['station'])
247 elif isinstance(stationdef['station'], list):
248 for s in stationdef['station']:
249 self.add_station(s)
250
252 """Adds a station configuration to the list of stations."""
253 try:
254 # We should probably test to see if we're putting the same station in multiple times
255 # Same in this case probably means the same media folder, server, and mountpoint
256 self.station_settings.append(this_station)
257 except Exception:
258 return
259
261 q = Queue.Queue(1)
262 ns = 0
263 p = Producer(q)
264 p.start()
265 # Keep the Stations running
266 while True:
267 self.create_stations_fromfolder()
268 ns_new = len(self.station_settings)
269 if ns_new > ns:
270 self._info('Loading new stations')
271
272 for i in range(0, ns_new):
273 name = ''
274 try:
275 if 'station_name' in self.station_settings[i]:
276 name = self.station_settings[i]['station_name']
277
278 if 'retries' not in self.station_settings[i]:
279 self.station_settings[i]['retries'] = 0
280
281 try:
282 if 'station_instance' in self.station_settings[i]:
283 # Check for station running here
284 if self.station_settings[i]['station_instance'].isAlive():
285 # Station exists and is alive. Don't recreate.
286 self.station_settings[i]['retries'] = 0
287 continue
288
289 if self.maxretry >= 0 and self.station_settings[i]['retries'] <= self.maxretry:
290 # Station passed the max retries count is will not be reloaded
291 if 'station_stop_logged' not in self.station_settings[i]:
292 self._err('Station ' + name + ' is stopped and will not be restarted.')
293 self.station_settings[i]['station_stop_logged'] = True
294 continue
295
296 self.station_settings[i]['retries'] += 1
297 trynum = str(self.station_settings[i]['retries'])
298 self._info('Restarting station ' + name + ' (try ' + trynum + ')')
299 except Exception as e:
300 self._err('Error checking status for ' + name)
301 self._err(str(e))
302 if not self.ignoreErrors:
303 raise
304
305 # Apply station defaults if they exist
306 if 'stationdefaults' in self.conf['deefuzzer']:
307 if isinstance(self.conf['deefuzzer']['stationdefaults'], dict):
308 self.station_settings[i] = merge_defaults(
309 self.station_settings[i],
310 self.conf['deefuzzer']['stationdefaults']
311 )
312
313 if name == '':
314 name = 'Station ' + str(i)
315 if 'info' in self.station_settings[i]:
316 if 'short_name' in self.station_settings[i]['infos']:
317 name = self.station_settings[i]['infos']['short_name']
318 y = 1
319 while name in self.station_instances.keys():
320 y += 1
321 name = self.station_settings[i]['infos']['short_name'] + " " + str(y)
322
323 self.station_settings[i]['station_name'] = name
324 namehash = hashlib.md5(name).hexdigest()
325 self.station_settings[i]['station_statusfile'] = os.sep.join([self.log_dir, namehash])
326
327 new_station = Station(self.station_settings[i], q, self.logqueue, self.m3u)
328 if new_station.valid:
329 self.station_settings[i]['station_instance'] = new_station
330 self.station_settings[i]['station_instance'].start()
331 self._info('Started station ' + name)
332 else:
333 self._err('Error validating station ' + name)
334 except Exception:
335 self._err('Error initializing station ' + name)
336 if not self.ignoreErrors:
337 raise
338 continue
339
340 if self.m3u:
341 self.set_m3u_playlist()
342
343 ns = ns_new
344 self.mainLoop = True
345
346 time.sleep(5)
347 # end main loop
348
349
363
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Jan 31 00:38:02 2015 | http://epydoc.sourceforge.net |