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 import os
40 import sys
41 import time
42 import datetime
43 import string
44 import random
45 import shout
46 import urllib
47 import mimetypes
48 import json
49
50 from threading import Thread
51 from player import *
52 from recorder import *
53 from relay import *
54 from streamer import *
55 from tools import *
56
57
59 """a DeeFuzzer shouting station thread"""
60
61 id = 0
62 valid = False
63 counter = 0
64 delay = 0
65 start_time = time.time()
66 server_ping = False
67 playlist = []
68 media_source = None
69 lp = 1
70 player_mode = 1
71 osc_control_mode = 0
72 twitter_mode = 0
73 jingles_mode = 0
74 relay_mode = 0
75 record_mode = 0
76 run_mode = 1
77 appendtype = 0
78 feeds_json = 0
79 feeds_rss = 1
80 feeds_mode = 1
81 feeds_playlist = 1
82 feeds_showfilepath = 0
83 feeds_showfilename = 0
84 short_name = ''
85 channelIsOpen = False
86 starting_id = -1
87 jingles_frequency = 2
88 statusfile = ''
89 base_directory = ''
90
91 - def __init__(self, station, q, logqueue, m3u):
92 Thread.__init__(self)
93 self.station = station
94 self.q = q
95 self.logqueue = logqueue
96 self.m3u = m3u
97
98 self.current_media_obj = MediaBase()
99
100 if 'station_statusfile' in self.station:
101 self.statusfile = station['station_statusfile']
102 try:
103 if os.path.exists(self.statusfile):
104 f = open(self.statusfile, 'r')
105 self.starting_id = int(f.read())
106 f.close()
107 except:
108 pass
109
110 if 'base_dir' in self.station:
111 self.base_directory = self.station['base_dir'].strip()
112
113
114 if 'm3u' in self.station['media']:
115 if not self.station['media']['m3u'].strip() == '':
116 self.media_source = self._path_add_base(self.station['media']['m3u'])
117
118 if 'dir' in self.station['media']:
119 if not self.station['media']['dir'].strip() == '':
120 self.media_source = self._path_add_base(self.station['media']['dir'])
121
122 if 'source' in self.station['media']:
123 if not self.station['media']['source'].strip() == '':
124 self.media_source = self._path_add_base(self.station['media']['source'])
125
126 self.media_format = self.station['media']['format']
127 self.shuffle_mode = int(self.station['media']['shuffle'])
128 self.bitrate = int(self.station['media']['bitrate'])
129 self.ogg_quality = int(self.station['media']['ogg_quality'])
130 self.samplerate = int(self.station['media']['samplerate'])
131 self.voices = int(self.station['media']['voices'])
132
133
134 if 'mountpoint' in self.station['server']:
135 self.mountpoint = self.station['server']['mountpoint']
136 elif 'short_name' in self.station['infos']:
137 self.mountpoint = self.station['infos']['short_name']
138 else:
139 self.mountpoint = 'default'
140
141 self.short_name = self.mountpoint
142
143 if 'appendtype' in self.station['server']:
144 self.appendtype = int(self.station['server']['appendtype'])
145
146 if 'type' in self.station['server']:
147 self.type = self.station['server']['type']
148 else:
149 self.type = 'icecast'
150
151 if 'stream-m' in self.type:
152 self.channel = HTTPStreamer()
153 self.channel.mount = '/publish/' + self.mountpoint
154 elif 'icecast' in self.type:
155 self.channel = shout.Shout()
156 self.channel.mount = '/' + self.mountpoint
157 if self.appendtype:
158 self.channel.mount = self.channel.mount + '.' + self.media_format
159 else:
160 self._err('Not a compatible server type. Choose "stream-m" or "icecast".')
161 return
162
163 self.channel.url = self.station['infos']['url']
164 self.channel.name = self.station['infos']['name']
165 self.channel.genre = self.station['infos']['genre']
166 self.channel.description = self.station['infos']['description']
167 self.channel.format = self.media_format
168 self.channel.host = self.station['server']['host']
169 self.channel.port = int(self.station['server']['port'])
170 self.channel.user = 'source'
171 self.channel.password = self.station['server']['sourcepassword']
172 self.channel.public = int(self.station['server']['public'])
173 if self.channel.format == 'mp3':
174 self.channel.audio_info = {'bitrate': str(self.bitrate),
175 'samplerate': str(self.samplerate),
176 'channels': str(self.voices), }
177 else:
178 self.channel.audio_info = {'bitrate': str(self.bitrate),
179 'samplerate': str(self.samplerate),
180 'quality': str(self.ogg_quality),
181 'channels': str(self.voices), }
182
183 self.server_url = 'http://' + self.channel.host + ':' + str(self.channel.port)
184 self.channel_url = self.server_url + self.channel.mount
185
186
187 if 'feeds' in self.station:
188 self.station['rss'] = self.station['feeds']
189
190 if 'rss' in self.station:
191 if 'mode' in self.station['rss']:
192 self.feeds_mode = int(self.station['rss']['mode'])
193 self.feeds_dir = self._path_add_base(self.station['rss']['dir'])
194 self.feeds_enclosure = int(self.station['rss']['enclosure'])
195 if 'json' in self.station['rss']:
196 self.feeds_json = int(self.station['rss']['json'])
197 if 'rss' in self.station['rss']:
198 self.feeds_rss = int(self.station['rss']['rss'])
199 if 'playlist' in self.station['rss']:
200 self.feeds_playlist = int(self.station['rss']['playlist'])
201 if 'showfilename' in self.station['rss']:
202 self.feeds_showfilename = int(self.station['rss']['showfilename'])
203 if 'showfilepath' in self.station['rss']:
204 self.feeds_showfilepath = int(self.station['rss']['showfilepath'])
205
206 self.feeds_media_url = self.channel.url + '/media/'
207 if 'media_url' in self.station['rss']:
208 if not self.station['rss']['media_url'] == '':
209 self.feeds_media_url = self.station['rss']['media_url']
210
211 self.base_name = self.feeds_dir + os.sep + self.short_name + '_' + self.channel.format
212 self.feeds_current_file = self.base_name + '_current'
213 self.feeds_playlist_file = self.base_name + '_playlist'
214
215
216 self._info('Opening ' + self.short_name + ' - ' + self.channel.name)
217
218 self.metadata_relative_dir = 'metadata'
219 self.metadata_url = self.channel.url + '/rss/' + self.metadata_relative_dir
220 self.metadata_dir = self.feeds_dir + os.sep + self.metadata_relative_dir
221 if not os.path.exists(self.metadata_dir):
222 os.makedirs(self.metadata_dir)
223
224
225 self.player = Player(self.type)
226
227
228
229 if 'control' in self.station:
230 self.osc_control_mode = int(self.station['control']['mode'])
231 if self.osc_control_mode:
232 self.osc_port = int(self.station['control']['port'])
233 self.osc_controller = OSCController(self.osc_port)
234
235 self.osc_controller.add_method('/media/next', 'i', self.media_next_callback)
236 self.osc_controller.add_method('/media/relay', 'i', self.relay_callback)
237 self.osc_controller.add_method('/twitter', 'i', self.twitter_callback)
238 self.osc_controller.add_method('/jingles', 'i', self.jingles_callback)
239 self.osc_controller.add_method('/record', 'i', self.record_callback)
240 self.osc_controller.add_method('/player', 'i', self.player_callback)
241 self.osc_controller.add_method('/run', 'i', self.run_callback)
242 self.osc_controller.start()
243
244
245 if 'jingles' in self.station:
246 if 'mode' in self.station['jingles']:
247 self.jingles_mode = int(self.station['jingles']['mode'])
248 if 'shuffle' in self.station['jingles']:
249 self.jingles_shuffle = int(self.station['jingles']['shuffle'])
250 if 'frequency' in self.station['jingles']:
251 self.jingles_frequency = int(self.station['jingles']['frequency'])
252 if 'dir' in self.station['jingles']:
253 self.jingles_dir = self._path_add_base(self.station['jingles']['dir'])
254 if self.jingles_mode == 1:
255 self.jingles_callback('/jingles', [1])
256
257
258 if 'relay' in self.station:
259 self.relay_mode = int(self.station['relay']['mode'])
260 self.relay_url = self.station['relay']['url']
261 self.relay_author = self.station['relay']['author']
262 if self.relay_mode == 1:
263 self.relay_callback('/media/relay', [1])
264
265
266 if 'twitter' in self.station:
267 self.twitter_mode = int(self.station['twitter']['mode'])
268 self.twitter_key = self.station['twitter']['key']
269 self.twitter_secret = self.station['twitter']['secret']
270 self.twitter_tags = self.station['twitter']['tags'].split(' ')
271 try:
272 self.twitter_messages = self.station['twitter']['message']
273 if isinstance(self.twitter_messages, dict):
274 self.twitter_messages = list(self.twitter_messages)
275 except:
276 pass
277
278 if self.twitter_mode:
279 self.twitter_callback('/twitter', [1])
280
281
282 if 'record' in self.station:
283 self.record_mode = int(self.station['record']['mode'])
284 self.record_dir = self._path_add_base(self.station['record']['dir'])
285 if self.record_mode:
286 self.record_callback('/record', [1])
287
288 self.valid = True
289
292
294 return os.path.join(os.path.dirname(self.source), a)
295
296 - def _log(self, level, msg):
297 try:
298 obj = {'msg': 'Station ' + str(self.channel_url) + ': ' + str(msg), 'level': str(level)}
299 self.logqueue.put(obj)
300 except:
301 pass
302
304 self._log('info', msg)
305
306 - def _err(self, msg):
307 self._log('err', msg)
308
310 value = value[0]
311 self.run_mode = value
312 message = "received OSC message '%s' with arguments '%d'" % (path, value)
313 self._info(message)
314
320
322 value = value[0]
323 if value:
324 self.relay_mode = 1
325 if self.type == 'icecast':
326 self.player.start_relay(self.relay_url)
327 else:
328 self.relay_mode = 0
329 if self.type == 'icecast':
330 self.player.stop_relay()
331
332 self.id = 0
333 self.next_media = 1
334 message = "received OSC message '%s' with arguments '%d'" % (path, value)
335 self._info(message)
336 message = "relaying : %s" % self.relay_url
337 self._info(message)
338
340 value = value[0]
341 self.twitter = Twitter(self.twitter_key, self.twitter_secret)
342 self.twitter_mode = value
343 message = "received OSC message '%s' with arguments '%d'" % (path, value)
344
345
346
347 self.m3u_url = self.channel.url + '/m3u/' + self.m3u.split(os.sep)[-1]
348 self.feeds_url = self.channel.url + '/rss/' + self.feeds_playlist_file.split(os.sep)[-1]
349 self._info(message)
350
352 value = value[0]
353 if value:
354 self.jingles_list = self.get_jingles()
355 self.jingles_length = len(self.jingles_list)
356 self.jingle_id = 0
357 self.jingles_mode = value
358 message = "received OSC message '%s' with arguments '%d'" % (path, value)
359 self._info(message)
360
362 value = value[0]
363 if value:
364 if not os.path.exists(self.record_dir):
365 os.makedirs(self.record_dir)
366 self.rec_file = self.short_name.replace('/', '_') + '-'
367 self.rec_file += datetime.datetime.now().strftime("%x-%X").replace('/', '_')
368 self.rec_file += '.' + self.channel.format
369 self.recorder = Recorder(self.record_dir)
370 self.recorder.open(self.rec_file)
371 else:
372 try:
373 self.recorder.recording = False
374 self.recorder.close()
375 except:
376 pass
377
378 if self.type == 'icecast':
379 date = datetime.datetime.now().strftime("%Y")
380 media = None
381 if self.channel.format == 'mp3':
382 media = Mp3(self.record_dir + os.sep + self.rec_file)
383 if self.channel.format == 'ogg':
384 media = Ogg(self.record_dir + os.sep + self.rec_file)
385 if media:
386 media.metadata = {'artist': self.artist.encode('utf-8'),
387 'title': self.title.encode('utf-8'),
388 'album': self.short_name.encode('utf-8'),
389 'genre': self.channel.genre.encode('utf-8'),
390 'date': date.encode('utf-8'), }
391 media.write_tags()
392
393 self.record_mode = value
394 message = "received OSC message '%s' with arguments '%d'" % (path, value)
395 self._info(message)
396
398 value = value[0]
399 self.player_mode = value
400 message = "received OSC message '%s' with arguments '%d'" % (path, value)
401 self._info(message)
402
404 file_list = []
405
406 try:
407 if os.path.isdir(self.media_source):
408 self.q.get(1)
409 try:
410 for root, dirs, files in os.walk(self.media_source):
411 for file in files:
412 s = file.split('.')
413 ext = s[len(s) - 1]
414 if ext.lower() == self.channel.format and not os.sep + '.' in file:
415 file_list.append(root + os.sep + file)
416 file_list.sort()
417 except:
418 pass
419 self.q.task_done()
420
421 if os.path.isfile(self.media_source):
422 self.q.get(1)
423 try:
424 f = open(self.media_source, 'r')
425 try:
426 for path in f.readlines():
427 path = path.strip()
428 if '#' != path[0]:
429 fp = self._path_m3u_rel(path)
430 if os.path.isfile(fp):
431 file_list.append(fp)
432 except:
433 f.close()
434 except:
435 pass
436 self.q.task_done()
437 except:
438 pass
439
440 return file_list
441
443 file_list = []
444 for root, dirs, files in os.walk(self.jingles_dir):
445 for file in files:
446 s = file.split('.')
447 ext = s[len(s) - 1]
448 if ext.lower() == self.channel.format and not os.sep + '.' in file:
449 file_list.append(root + os.sep + file)
450 file_list.sort()
451 return file_list
452
454 if len(self.new_tracks):
455 new_tracks_objs = self.media_to_objs(self.new_tracks)
456 for media_obj in new_tracks_objs:
457 title, artist, song = self.get_songmeta(media_obj)
458
459 artist_names = artist.split(' ')
460 artist_tags = ' #'.join(list(set(artist_names) - {'&', '-'}))
461 message = '#NEWTRACK ! %s %s on #%s' % \
462 (song, artist_tags.strip(), self.short_name)
463 message = message[:113] + self.feeds_url
464 self.update_twitter(message)
465
536
557
559 if not self.feeds_mode:
560 return
561
562 rss_item_list = []
563 if not os.path.exists(self.feeds_dir):
564 os.makedirs(self.feeds_dir)
565 channel_subtitle = self.channel.name + ' ' + sub_title
566 _date_now = datetime.datetime.now()
567 date_now = str(_date_now)
568 media_absolute_playtime = _date_now
569 json_data = []
570
571 for media in media_list:
572 json_item = {}
573 media_stats = os.stat(media.media)
574 media_date = time.localtime(media_stats[8])
575 media_date = time.strftime("%a, %d %b %Y %H:%M:%S +0200", media_date)
576 media.metadata['Duration'] = str(media.length).split('.')[0]
577 media.metadata['Bitrate'] = str(media.bitrate) + ' kbps'
578 media.metadata['Next play'] = str(media_absolute_playtime).split('.')[0]
579
580 media_description = '<table>'
581 media_description_item = '<tr><td>%s: </td><td><b>%s</b></td></tr>'
582
583 for key in media.metadata.keys():
584 if media.metadata[key] != '':
585 if key == 'filepath' and not self.feeds_showfilepath:
586 continue
587 if key == 'filename' and not self.feeds_showfilename:
588 continue
589 media_description += media_description_item % (key.capitalize(),
590 media.metadata[key])
591 json_item[key] = media.metadata[key]
592 if self.feeds_showfilepath:
593 media_description += media_description_item % ('Filepath', media.media)
594 json_item['filepath'] = media.media
595 if self.feeds_showfilename:
596 media_description += media_description_item % ('Filename', media.file_name)
597 json_item['filename'] = media.file_name
598 media_description += '</table>'
599
600 title, artist, song = self.get_songmeta(media)
601 media_absolute_playtime += media.length
602
603 if self.feeds_enclosure == '1':
604 media_link = self.feeds_media_url + media.file_name
605 media_link = media_link.decode('utf-8')
606 rss_item_list.append(RSSItem(
607 title=song,
608 link=media_link,
609 description=media_description,
610 enclosure=Enclosure(media_link, str(media.size), 'audio/mpeg'),
611 guid=Guid(media_link),
612 pubDate=media_date, )
613 )
614 else:
615 media_link = self.metadata_url + '/' + media.file_name + '.xml'
616 try:
617 media_link = media_link.decode('utf-8')
618 except:
619 continue
620 rss_item_list.append(RSSItem(
621 title=song,
622 link=media_link,
623 description=media_description,
624 guid=Guid(media_link),
625 pubDate=media_date, )
626 )
627 json_data.append(json_item)
628
629 rss = RSS2(title=channel_subtitle,
630 link=self.channel.url,
631 description=self.channel.description.decode('utf-8'),
632 lastBuildDate=date_now,
633 items=rss_item_list, )
634 self.q.get(1)
635 try:
636 if self.feeds_rss:
637 f = open(rss_file + '.xml', 'w')
638 rss.write_xml(f, 'utf-8')
639 f.close()
640 except:
641 pass
642
643 try:
644 if self.feeds_json:
645 f = open(rss_file + '.json', 'w')
646 f.write(json.dumps(json_data, separators=(',', ':')))
647 f.close()
648 except:
649 pass
650 self.q.task_done()
651
653 try:
654 self.twitter.post(message.decode('utf8'))
655 self._info('Twitting : "' + message + '"')
656 except:
657 self._err('Twitting : "' + message + '"')
658
670
684
699
701 self.prefix = '#nowplaying'
702
703 try:
704 self.get_currentsongmeta()
705 fn = self.current_media_obj.file_name
706 if fn:
707 self.metadata_file = self.metadata_dir + os.sep + fn + '.xml'
708 self.update_feeds([self.current_media_obj], self.feeds_current_file, '(currently playing)')
709 if fn:
710 self._info('DeeFuzzing: id = %s, name = %s' % (self.id, fn))
711 except:
712 pass
713 self.player.set_media(self.media)
714
715 self.q.get(1)
716 try:
717 if self.player_mode:
718 self.stream = self.player.file_read_slow()
719 else:
720 self.stream = self.player.file_read_fast()
721 except:
722 pass
723 self.q.task_done()
724
727
729 if not self.__twitter_should_update():
730 return
731 message = '%s %s on #%s' % (self.prefix, self.song, self.short_name)
732 tags = '#' + ' #'.join(self.twitter_tags)
733 message = message + ' ' + tags
734 message = message[:108] + ' M3U: ' + self.m3u_url
735 self.update_twitter(message)
736
738 if self.channelIsOpen:
739 return True
740
741 try:
742 self.channel.open()
743 self.channel_delay = self.channel.delay()
744 self._info('channel connected')
745 self.channelIsOpen = True
746 return True
747 except:
748 self._err('channel could not be opened')
749
750 return False
751
753 self.channelIsOpen = False
754 try:
755 self.channel.close()
756 self._info('channel closed')
757 except:
758 self._err('channel could not be closed')
759
761 log = True
762
763 while not self.server_ping:
764 try:
765 server = urllib.urlopen(self.server_url)
766 self.server_ping = True
767 self._info('Channel available.')
768 except:
769 time.sleep(1)
770 if log:
771 self._err('Could not connect the channel. Waiting for channel to become available.')
772 log = False
773
792
807
816
895