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