Package deefuzzer :: Package deefuzzer :: Package tools :: Module station
[hide private]
[frames] | no frames]

Source Code for Module deefuzzer.deefuzzer.tools.station

  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 sys 
 41  import time 
 42  import datetime 
 43  import string 
 44  import random 
 45  import shout 
 46  import urllib 
 47  from threading import Thread 
 48  from __init__ import * 
 49   
 50   
51 -class Station(Thread):
52 """a DeeFuzzer shouting station thread""" 53
54 - def __init__(self, station, q, logger, m3u):
55 Thread.__init__(self) 56 self.station = station 57 self.q = q 58 self.logger = logger 59 self.channel = shout.Shout() 60 self.id = 999999 61 self.counter = 0 62 self.command = 'cat ' 63 self.delay = 0 64 65 # Media 66 self.media_dir = self.station['media']['dir'] 67 self.channel.format = self.station['media']['format'] 68 self.shuffle_mode = int(self.station['media']['shuffle']) 69 self.bitrate = self.station['media']['bitrate'] 70 self.ogg_quality = self.station['media']['ogg_quality'] 71 self.samplerate = self.station['media']['samplerate'] 72 self.voices = self.station['media']['voices'] 73 74 # RSS 75 self.rss_dir = self.station['rss']['dir'] 76 self.rss_enclosure = self.station['rss']['enclosure'] 77 78 # Infos 79 self.channel.url = self.station['infos']['url'] 80 self.short_name = self.station['infos']['short_name'] 81 self.channel.name = self.station['infos']['name'] + ' : ' + self.channel.url 82 self.channel.genre = self.station['infos']['genre'] 83 self.channel.description = self.station['infos']['description'] 84 self.base_name = self.rss_dir + os.sep + self.short_name + '_' + self.channel.format 85 self.rss_current_file = self.base_name + '_current.xml' 86 self.rss_playlist_file = self.base_name + '_playlist.xml' 87 self.m3u = m3u 88 89 # Server 90 self.channel.protocol = 'http' # | 'xaudiocast' | 'icy' 91 self.channel.host = self.station['server']['host'] 92 self.channel.port = int(self.station['server']['port']) 93 self.channel.user = 'source' 94 self.channel.password = self.station['server']['sourcepassword'] 95 self.channel.mount = '/' + self.short_name + '.' + self.channel.format 96 self.channel.public = int(self.station['server']['public']) 97 self.channel.audio_info = { 'bitrate': self.bitrate, 98 'samplerate': self.samplerate, 99 'quality': self.ogg_quality, 100 'channels': self.voices,} 101 self.server_url = 'http://' + self.channel.host + ':' + str(self.channel.port) 102 self.channel_url = self.server_url + self.channel.mount 103 self.server_ping = False 104 105 # Playlist 106 self.playlist = self.get_playlist() 107 self.lp = len(self.playlist) 108 109 # Logging 110 self.logger.write_info('Opening ' + self.short_name + ' - ' + self.channel.name + \ 111 ' (' + str(self.lp) + ' tracks)...') 112 113 self.metadata_relative_dir = 'metadata' 114 self.metadata_url = self.channel.url + '/rss/' + self.metadata_relative_dir 115 self.metadata_dir = self.rss_dir + os.sep + self.metadata_relative_dir 116 if not os.path.exists(self.metadata_dir): 117 os.makedirs(self.metadata_dir) 118 119 # The station's player 120 self.player = Player() 121 self.player_mode = 0 122 123 # Jingling between each media. 124 # mode = 0 means Off, mode = 1 means On 125 self.jingles_mode = 0 126 if 'jingles' in self.station: 127 self.jingles_mode = int(self.station['jingles']['mode']) 128 self.jingles_shuffle = self.station['jingles']['shuffle'] 129 self.jingles_dir = self.station['jingles']['dir'] 130 if self.jingles_mode == 1: 131 self.jingles_callback('/jingles', [1]) 132 133 # Relaying 134 # mode = 0 means Off, mode = 1 means On 135 self.relay_mode = 0 136 if 'relay' in self.station: 137 self.relay_mode = int(self.station['relay']['mode']) 138 self.relay_url = self.station['relay']['url'] 139 self.relay_author = self.station['relay']['author'] 140 if self.relay_mode == 1: 141 self.relay_callback('/media/relay', [1]) 142 143 # Twitting 144 # mode = 0 means Off, mode = 1 means On 145 self.twitter_mode = 0 146 if 'twitter' in self.station: 147 self.twitter_mode = int(self.station['twitter']['mode']) 148 self.twitter_key = self.station['twitter']['key'] 149 self.twitter_secret = self.station['twitter']['secret'] 150 self.twitter_tags = self.station['twitter']['tags'].split(' ') 151 if self.twitter_mode == 1: 152 self.twitter_callback('/twitter', [1]) 153 154 # Recording 155 # mode = 0 means Off, mode = 1 means On 156 self.record_mode = 0 157 if 'record' in self.station: 158 self.record_mode = int(self.station['record']['mode']) 159 self.record_dir = self.station['record']['dir'] 160 if self.record_mode == 1: 161 self.record_callback('/record', [1]) 162 163 # Running 164 # mode = 0 means Off, mode = 1 means On 165 self.run_mode = 1 166 167 # OSCing 168 self.osc_control_mode = 0 169 # mode = 0 means Off, mode = 1 means On 170 if 'control' in self.station: 171 self.osc_control_mode = int(self.station['control']['mode']) 172 self.osc_port = self.station['control']['port'] 173 if self.osc_control_mode == 1: 174 self.osc_controller = OSCController(self.osc_port) 175 self.osc_controller.start() 176 # OSC paths and callbacks 177 self.osc_controller.add_method('/media/next', 'i', self.media_next_callback) 178 self.osc_controller.add_method('/media/relay', 'i', self.relay_callback) 179 self.osc_controller.add_method('/twitter', 'i', self.twitter_callback) 180 self.osc_controller.add_method('/jingles', 'i', self.jingles_callback) 181 self.osc_controller.add_method('/record', 'i', self.record_callback) 182 self.osc_controller.add_method('/player', 'i', self.player_callback) 183 self.osc_controller.add_method('/run', 'i', self.run_callback)
184
185 - def run_callback(self, path, value):
186 value = value[0] 187 self.run_mode = value 188 message = "Station " + self.channel_url + " : received OSC message '%s' with arguments '%d'" % (path, value) 189 self.logger.write_info(message)
190
191 - def media_next_callback(self, path, value):
192 value = value[0] 193 self.next_media = value 194 message = "Station " + self.channel_url + " : received OSC message '%s' with arguments '%d'" % (path, value) 195 self.logger.write_info(message)
196
197 - def relay_callback(self, path, value):
198 value = value[0] 199 if value == 1: 200 self.relay_mode = 1 201 self.player.start_relay(self.relay_url) 202 elif value == 0: 203 self.relay_mode = 0 204 self.player.stop_relay() 205 self.id = 0 206 self.next_media = 1 207 message = "Station " + self.channel_url + " : received OSC message '%s' with arguments '%d'" % (path, value) 208 self.logger.write_info(message) 209 message = "Station " + self.channel_url + " : relaying : %s" % self.relay_url 210 self.logger.write_info(message)
211
212 - def twitter_callback(self, path, value):
213 value = value[0] 214 import tinyurl 215 self.twitter = Twitter(self.twitter_key, self.twitter_secret) 216 self.twitter_mode = value 217 message = "Station " + self.channel_url + " : received OSC message '%s' with arguments '%d'" % (path, value) 218 self.m3u_tinyurl = tinyurl.create_one(self.channel.url + '/m3u/' + self.m3u.split(os.sep)[-1]) 219 self.rss_tinyurl = tinyurl.create_one(self.channel.url + '/rss/' + self.rss_playlist_file.split(os.sep)[-1]) 220 self.logger.write_info(message)
221
222 - def jingles_callback(self, path, value):
223 value = value[0] 224 if value == 1: 225 self.jingles_list = self.get_jingles() 226 self.jingles_length = len(self.jingles_list) 227 self.jingle_id = 0 228 self.jingles_mode = value 229 message = "Station " + self.channel_url + " : received OSC message '%s' with arguments '%d'" % (path, value) 230 self.logger.write_info(message)
231
232 - def record_callback(self, path, value):
233 value = value[0] 234 if value == 1: 235 if not os.path.exists(self.record_dir): 236 os.makedirs(self.record_dir) 237 self.rec_file = self.short_name + '-' + \ 238 datetime.datetime.now().strftime("%x-%X").replace('/', '_') + '.' + self.channel.format 239 self.recorder = Recorder(self.record_dir) 240 self.recorder.open(self.rec_file) 241 elif value == 0: 242 self.recorder.close() 243 date = datetime.datetime.now().strftime("%Y") 244 if self.channel.format == 'mp3': 245 media = Mp3(self.record_dir + os.sep + self.rec_file) 246 if self.channel.format == 'ogg': 247 media = Ogg(self.record_dir + os.sep + self.rec_file) 248 media.metadata = {'artist': self.artist.encode('utf-8'), 249 'title': self.title.encode('utf-8'), 250 'album': self.short_name.encode('utf-8'), 251 'genre': self.channel.genre.encode('utf-8'), 252 'date' : date.encode('utf-8'),} 253 media.write_tags() 254 self.record_mode = value 255 message = "Station " + self.channel_url + " : received OSC message '%s' with arguments '%d'" % (path, value) 256 self.logger.write_info(message)
257
258 - def player_callback(self, path, value):
259 value = value[0] 260 self.player_mode = value 261 message = "Station " + self.channel_url + " : received OSC message '%s' with arguments '%d'" % (path, value) 262 self.logger.write_info(message)
263
264 - def get_playlist(self):
265 file_list = [] 266 for root, dirs, files in os.walk(self.media_dir): 267 for file in files: 268 s = file.split('.') 269 ext = s[len(s)-1] 270 if ext.lower() == self.channel.format and not os.sep+'.' in file: 271 file_list.append(root + os.sep + file) 272 file_list.sort() 273 return file_list
274
275 - def get_jingles(self):
276 file_list = [] 277 for root, dirs, files in os.walk(self.jingles_dir): 278 for file in files: 279 s = file.split('.') 280 ext = s[len(s)-1] 281 if ext.lower() == self.channel.format and not os.sep+'.' in file: 282 file_list.append(root + os.sep + file) 283 file_list.sort() 284 return file_list
285
286 - def get_next_media(self):
287 # Init playlist 288 if self.lp != 0: 289 playlist = self.playlist 290 new_playlist = self.get_playlist() 291 lp_new = len(new_playlist) 292 293 if lp_new != self.lp or self.counter == 0: 294 self.id = 0 295 self.lp = lp_new 296 297 # Twitting new tracks 298 new_playlist_set = set(new_playlist) 299 playlist_set = set(playlist) 300 new_tracks = new_playlist_set - playlist_set 301 self.new_tracks = list(new_tracks.copy()) 302 303 if len(new_tracks) != 0: 304 new_tracks_objs = self.media_to_objs(self.new_tracks) 305 for media_obj in new_tracks_objs: 306 title = media_obj.metadata['title'] 307 artist = media_obj.metadata['artist'] 308 if not (title or artist): 309 song = str(media_obj.file_name) 310 else: 311 song = artist + ' : ' + title 312 song = song.encode('utf-8') 313 artist = artist.encode('utf-8') 314 if self.twitter_mode == 1: 315 artist_names = artist.split(' ') 316 artist_tags = ' #'.join(list(set(artist_names)-set(['&', '-']))) 317 message = '#newtrack ! %s #%s on #%s RSS: ' % (song.replace('_', ' '), artist_tags, self.short_name) 318 message = message[:113] + self.rss_tinyurl 319 self.update_twitter(message) 320 321 # Shake it, Fuzz it ! 322 if self.shuffle_mode == 1: 323 random.shuffle(playlist) 324 325 # Play new tracks first 326 for track in self.new_tracks: 327 playlist.insert(0, track) 328 self.playlist = playlist 329 330 self.logger.write_info('Station ' + self.channel_url + \ 331 ' : generating new playlist (' + str(self.lp) + ' tracks)') 332 self.update_rss(self.media_to_objs(self.playlist), self.rss_playlist_file, '(playlist)') 333 334 if self.jingles_mode == 1 and (self.counter % 2) == 0 and not self.jingles_length == 0: 335 media = self.jingles_list[self.jingle_id] 336 self.jingle_id = (self.jingle_id + 1) % self.jingles_length 337 else: 338 media = self.playlist[self.id] 339 self.id = (self.id + 1) % self.lp 340 return media 341 else: 342 mess = 'No media in media_dir !' 343 self.logger.write_error(mess) 344 sys.exit(mess)
345
346 - def media_to_objs(self, media_list):
347 media_objs = [] 348 for media in media_list: 349 file_name, file_title, file_ext = get_file_info(media) 350 if file_ext.lower() == 'mp3': 351 media_objs.append(Mp3(media)) 352 elif file_ext.lower() == 'ogg': 353 media_objs.append(Ogg(media)) 354 return media_objs
355
356 - def update_rss(self, media_list, rss_file, sub_title):
357 rss_item_list = [] 358 if not os.path.exists(self.rss_dir): 359 os.makedirs(self.rss_dir) 360 channel_subtitle = self.channel.name + ' ' + sub_title 361 _date_now = datetime.datetime.now() 362 date_now = str(_date_now) 363 media_absolute_playtime = _date_now 364 365 for media in media_list: 366 media_stats = os.stat(media.media) 367 media_date = time.localtime(media_stats[8]) 368 media_date = time.strftime("%a, %d %b %Y %H:%M:%S +0200", media_date) 369 media.metadata['Duration'] = str(media.length).split('.')[0] 370 media.metadata['Bitrate'] = str(media.bitrate) + ' kbps' 371 media.metadata['Next play'] = str(media_absolute_playtime).split('.')[0] 372 373 media_description = '<table>' 374 media_description_item = '<tr><td>%s: </td><td><b>%s</b></td></tr>' 375 for key in media.metadata.keys(): 376 if media.metadata[key] != '': 377 media_description += media_description_item % (key.capitalize(), media.metadata[key]) 378 media_description += '</table>' 379 380 title = media.metadata['title'] 381 artist = media.metadata['artist'] 382 if not (title or artist): 383 song = str(media.file_title) 384 else: 385 song = artist + ' : ' + title 386 387 media_absolute_playtime += media.length 388 389 if self.rss_enclosure == '1': 390 media_link = self.channel.url + '/media/' + media.file_name 391 media_link = media_link.decode('utf-8') 392 rss_item_list.append(RSSItem( 393 title = song, 394 link = media_link, 395 description = media_description, 396 enclosure = Enclosure(media_link, str(media.size), 'audio/mpeg'), 397 guid = Guid(media_link), 398 pubDate = media_date,) 399 ) 400 else: 401 media_link = self.metadata_url + '/' + media.file_name + '.xml' 402 media_link = media_link.decode('utf-8') 403 rss_item_list.append(RSSItem( 404 title = song, 405 link = media_link, 406 description = media_description, 407 guid = Guid(media_link), 408 pubDate = media_date,) 409 ) 410 411 rss = RSS2(title = channel_subtitle, 412 link = self.channel.url, 413 description = self.channel.description.decode('utf-8'), 414 lastBuildDate = date_now, 415 items = rss_item_list,) 416 f = open(rss_file, 'w') 417 rss.write_xml(f, 'utf-8') 418 f.close()
419
420 - def update_twitter(self, message):
421 try: 422 self.twitter.post(message.decode('utf8')) 423 self.logger.write_info('Twitting : "' + message + '"') 424 except: 425 self.logger.write_error('Twitting : "' + message + '"') 426 pass
427
428 - def set_relay_mode(self):
429 self.prefix = '#nowplaying (relaying #LIVE)' 430 self.title = self.channel.description.encode('utf-8') 431 self.artist = self.relay_author.encode('utf-8') 432 self.title = self.title.replace('_', ' ') 433 self.artist = self.artist.replace('_', ' ') 434 self.song = self.artist + ' : ' + self.title 435 self.stream = self.player.relay_read()
436
437 - def set_read_mode(self):
438 self.prefix = '#nowplaying' 439 self.current_media_obj = self.media_to_objs([self.media]) 440 self.title = self.current_media_obj[0].metadata['title'] 441 self.artist = self.current_media_obj[0].metadata['artist'] 442 self.title = self.title.replace('_', ' ') 443 self.artist = self.artist.replace('_', ' ') 444 if not (self.title or self.artist): 445 song = str(self.current_media_obj[0].file_name) 446 else: 447 song = self.artist + ' : ' + self.title 448 self.song = song.encode('utf-8') 449 self.artist = self.artist.encode('utf-8') 450 self.metadata_file = self.metadata_dir + os.sep + self.current_media_obj[0].file_name + '.xml' 451 #self.update_rss(self.current_media_obj, self.metadata_file, '') 452 self.update_rss(self.current_media_obj, self.rss_current_file, '(currently playing)') 453 self.logger.write_info('DeeFuzzing on %s : id = %s, name = %s' \ 454 % (self.short_name, self.id, self.current_media_obj[0].file_name)) 455 self.player.set_media(self.media) 456 if self.player_mode == 0: 457 self.stream = self.player.file_read_slow() 458 elif self.player_mode == 1: 459 self.stream = self.player.file_read_fast()
460
461 - def update_twitter_current(self):
462 artist_names = self.artist.split(' ') 463 artist_tags = ' #'.join(list(set(artist_names)-set(['&', '-']))) 464 message = '%s %s on #%s' % (self.prefix, self.song, self.short_name) 465 tags = '#' + ' #'.join(self.twitter_tags) 466 message = message + ' ' + tags 467 message = message[:108] + ' M3U: ' + self.m3u_tinyurl 468 self.update_twitter(message)
469
470 - def channel_open(self):
471 self.channel.open() 472 self.channel_delay = self.channel.delay()
473
474 - def ping_server(self):
475 log = True 476 while not self.server_ping: 477 try: 478 self.q.get(1) 479 server = urllib.urlopen(self.server_url) 480 self.server_ping = True 481 self.logger.write_info('Station ' + self.channel_url + ' : channel available') 482 self.q.task_done() 483 except: 484 time.sleep(0.5) 485 if log: 486 self.logger.write_error('Station ' + self.channel_url + ' : could not connect the channel' ) 487 log = False 488 self.q.task_done() 489 pass
490
491 - def run(self):
492 self.ping_server() 493 self.q.get(1) 494 self.channel_open() 495 self.logger.write_info('Station ' + self.channel_url + ' : channel connected') 496 self.q.task_done() 497 498 while self.run_mode: 499 self.q.get(1) 500 self.next_media = 0 501 self.media = self.get_next_media() 502 self.counter += 1 503 if self.relay_mode: 504 self.set_relay_mode() 505 elif os.path.exists(self.media) and not os.sep+'.' in self.media: 506 if self.lp == 0: 507 self.logger.write_error('Station ' + self.channel_url + ' : has no media to stream !') 508 break 509 self.set_read_mode() 510 self.q.task_done() 511 512 self.q.get(1) 513 if (not (self.jingles_mode and (self.counter % 2)) or self.relay_mode) and self.twitter_mode: 514 try: 515 self.update_twitter_current() 516 except: 517 continue 518 try: 519 self.channel.set_metadata({'song': self.song, 'charset': 'utf-8',}) 520 except: 521 continue 522 self.q.task_done() 523 524 for self.chunk in self.stream: 525 if self.next_media or not self.run_mode: 526 break 527 if self.record_mode: 528 try: 529 self.q.get(1) 530 self.recorder.write(self.chunk) 531 self.q.task_done() 532 except: 533 self.logger.write_error('Station ' + self.channel_url + ' : could not write the buffer to the file') 534 self.q.task_done() 535 continue 536 try: 537 self.q.get(1) 538 self.channel.send(self.chunk) 539 self.channel.sync() 540 self.q.task_done() 541 except: 542 self.logger.write_error('Station ' + self.channel_url + ' : could not send the buffer') 543 self.q.task_done() 544 try: 545 self.q.get(1) 546 self.channel.close() 547 self.logger.write_info('Station ' + self.channel_url + ' : channel closed') 548 self.q.task_done() 549 except: 550 self.logger.write_error('Station ' + self.channel_url + ' : could not close the channel') 551 self.q.task_done() 552 continue 553 try: 554 self.ping_server() 555 self.q.get(1) 556 self.channel_open() 557 self.channel.set_metadata({'song': self.song, 'charset': 'utf8',}) 558 self.logger.write_info('Station ' + self.channel_url + ' : channel restarted') 559 self.q.task_done() 560 except: 561 self.logger.write_error('Station ' + self.channel_url + ' : could not restart the channel') 562 self.q.task_done() 563 continue 564 continue 565 566 if self.record_mode: 567 self.recorder.close() 568 569 self.channel.close()
570