From 5a46fae590d83add9f8642f0f375baa475ce62ba Mon Sep 17 00:00:00 2001 From: achbed Date: Thu, 20 Nov 2014 22:36:52 -0600 Subject: [PATCH] Added documentation Revised init handling of preferences for consistency Fixed issue where some boolean flags (ie, modes) were not being handled properly Signed-off-by: achbed --- deefuzzer/core.py | 141 ++++++++++++------- deefuzzer/station.py | 326 ++++++++++++++++++++++++++++--------------- 2 files changed, 304 insertions(+), 163 deletions(-) diff --git a/deefuzzer/core.py b/deefuzzer/core.py index eadeabf..9f401c5 100644 --- a/deefuzzer/core.py +++ b/deefuzzer/core.py @@ -51,51 +51,52 @@ mimetypes.add_type('application/x-yaml','.yaml') class DeeFuzzer(Thread): """a DeeFuzzer diffuser""" + logger = None m3u = None rss = None + station = [] + stations = [] def __init__(self, conf_file): Thread.__init__(self) self.conf_file = conf_file self.conf = get_conf_dict(self.conf_file) + + # Get the log setting first (if possible) + log_file = str(self.conf.pop('log', '')) + log_dir = os.sep.join(log_file.split(os.sep)[:-1]) + if not os.path.exists(log_dir) and log_dir: + os.makedirs(m3u_dir) + self.logger = Logger(log_file) + for key in self.conf['deefuzzer'].keys(): - if key == 'log': - log_file = self.conf['deefuzzer']['log'] - log_dir = os.sep.join(log_file.split(os.sep)[:-1]) - if not os.path.exists(log_dir) and log_dir: - os.makedirs(m3u_dir) - self.logger = Logger(log_file) if key == 'm3u': - self.m3u = self.conf['deefuzzer']['m3u'] + self.m3u = str(self.conf['deefuzzer'][key]) + + elif key == 'station': + # Load station definitions from the main config file + if not isinstance(self.conf['deefuzzer'][key], list): + self.add_station(self.conf['deefuzzer'][key]) + else: + for s in self.conf['deefuzzer'][key]: + self.add_station(s) + + elif key == 'stationconfig': + # Load additional station definitions from the requested folder + self.load_stations_fromconfig(self.conf['deefuzzer'][key]) + + elif key == 'stationfolder': + # Create stations automagically from a folder structure + if isinstance(self.conf['deefuzzer'][key], dict): + self.create_stations_fromfolder(self.conf['deefuzzer'][key]) else: setattr(self, key, self.conf['deefuzzer'][key]) - # Fix wrong type data from xmltodict when one station (*) - if 'station' not in self.conf['deefuzzer']: - self.conf['deefuzzer']['station'] = [] - else: - if not isinstance(self.conf['deefuzzer']['station'], list): - s = self.conf['deefuzzer']['station'] - self.conf['deefuzzer']['station'] = [] - self.conf['deefuzzer']['station'].append(s) - - # Load additional station definitions from the requested folder - if 'stationconfig' in self.conf['deefuzzer']: - self.load_stations(self.conf['deefuzzer']['stationconfig']) - - # Create stations automagically from a folder structure - if 'stationfolder' in self.conf['deefuzzer'].keys() and isinstance(self.conf['deefuzzer']['stationfolder'], dict): - self.create_stations_fromfolder(self.conf['deefuzzer']['stationfolder']) - - self.nb_stations = len(self.conf['deefuzzer']['station']) - # Set the deefuzzer logger self.logger.write_info('Starting DeeFuzzer') self.logger.write_info('Using libshout version %s' % shout.version()) + self.logger.write_info('Number of stations : ' + str(len(self.station))) - # Init all Stations - self.stations = [] - self.logger.write_info('Number of stations : ' + str(self.nb_stations)) def set_m3u_playlist(self): m3u_dir = os.sep.join(self.m3u.split(os.sep)[:-1]) @@ -110,10 +111,19 @@ class DeeFuzzer(Thread): self.logger.write_info('Writing M3U file to : ' + self.m3u) def create_stations_fromfolder(self, options): + """Scan a folder for subfolders containing media, and make stations from them all.""" + if not 'folder' in options.keys(): + # We have no folder specified. Bail. return + folder = str(options['folder']) - self.logger.write_info('Scanning folder ' + folder + ' for stations...') + if not os.path.isdir(folder): + # The specified path is not a folder. Bail. + return + + self.logger.write_info('Scanning folder ' + folder + ' for stations') + files = os.listdir(folder) for file in files: filepath = os.path.join(folder, file) @@ -122,6 +132,8 @@ class DeeFuzzer(Thread): self.create_station(filepath, options) def create_station(self, folder, options): + """Create a station definition for a folder given the specified options.""" + self.logger.write_info('Creating station for folder ' + folder) s = {} path, name = os.path.split(folder) @@ -132,43 +144,70 @@ class DeeFuzzer(Thread): if not 'media' in s.keys(): s['media'] = {} s['media']['dir'] = folder - self.conf['deefuzzer']['station'].append(s) - - - def load_stations(self, folder): - if isinstance(folder, dict): + self.add_station(s) + + def load_stations_fromconfig(self, folder): + """Load one or more configuration files looking for stations.""" + + if isinstance(folder, dict) or isinstance(folder, list): + # We were given a list or dictionary. Loop though it and load em all for f in folder: - self.load_stations(f) + self.load_station_configs(f) return + if os.path.isfile(folder): + # We have a file specified. Load just that file. + self.load_station_config(folder) + return + if not os.path.isdir(folder): + # Whatever we have, it's not either a file or folder. Bail. return - + self.logger.write_info('Loading station config files in ' + folder) files = os.listdir(folder) for file in files: filepath = os.path.join(folder, file) if os.path.isfile(filepath): self.load_station_config(filepath) - + def load_station_config(self, file): + """Load station configuration(s) from a config file.""" + self.logger.write_info('Loading station config file ' + file) stationdef = get_conf_dict(file) if isinstance(stationdef, dict): - if 'station' in stationdef.keys() and isinstance(stationdef['station'], dict): - self.conf['deefuzzer']['station'].append(stationdef['station']) + if 'station' in stationdef.keys(): + if isinstance(stationdef['station'], dict): + self.add_station(stationdef['station']) + elif isinstance(stationdef['station'], list): + for s in stationdef['station']: + self.add_station(s) + + def add_station(self, this_station): + """Adds a station configuration to the list of stations.""" + try: + # We should probably test to see if we're putting the same station in multiple times + # Same in this case probably means the same media folder, server, and mountpoint + self.station.append(this_station) + except Exception: + return def run(self): q = Queue.Queue(1) - for i in range(0,self.nb_stations): - station = self.conf['deefuzzer']['station'][i] + ns = len(self.station) + for i in range(0, ns): + try: + station = self.station[i] - # Apply station defaults if they exist - if 'stationdefaults' in self.conf['deefuzzer']: - if isinstance(self.conf['deefuzzer']['stationdefaults'], dict): - station = merge_defaults(station, self.conf['deefuzzer']['stationdefaults']) - self.stations.append(Station(station, q, self.logger, self.m3u)) + # Apply station defaults if they exist + if 'stationdefaults' in self.conf['deefuzzer']: + if isinstance(self.conf['deefuzzer']['stationdefaults'], dict): + station = merge_defaults(station, self.conf['deefuzzer']['stationdefaults']) + self.stations.append(Station(station, q, self.logger, self.m3u)) + except Exception: + continue if self.m3u: self.set_m3u_playlist() @@ -176,9 +215,13 @@ class DeeFuzzer(Thread): p = Producer(q) p.start() + ns = len(self.stations) # Start the Stations - for i in range(0,self.nb_stations): - self.stations[i].start() + for i in range(0, ns): + try: + self.stations[i].start() + except Exception: + continue class Producer(Thread): diff --git a/deefuzzer/station.py b/deefuzzer/station.py index f1fec09..20f0b75 100644 --- a/deefuzzer/station.py +++ b/deefuzzer/station.py @@ -67,10 +67,16 @@ class Station(Thread): lp = 1 player_mode = 0 osc_control_mode = 0 + osc_control_port = 16001 twitter_mode = 0 jingles_mode = 0 + jingles_shuffle = 1 + jingles_dir = '' relay_mode = 0 + relay_url = '' + relay_author = '' record_mode = 0 + record_dir = '' run_mode = 1 station_dir = None appendtype = 1 @@ -78,6 +84,11 @@ class Station(Thread): feeds_rss = 1 feeds_mode = 1 feeds_playlist = 1 + media_dir = '' + m3u_playlist_file = [] + mountpoint = 'default' + type = 'icecast' + feeds_media_url = '' def __init__(self, station, q, logger, m3u): Thread.__init__(self) @@ -86,39 +97,31 @@ class Station(Thread): self.logger = logger self.m3u = m3u - if 'station_dir' in self.station: - self.station_dir = self.station['station_dir'] - - # Media - self.media_dir = self.station['media']['dir'] - self.media_format = self.station['media']['format'] - self.shuffle_mode = int(self.station['media']['shuffle']) - self.bitrate = self.station['media']['bitrate'] - self.ogg_quality = self.station['media']['ogg_quality'] - self.samplerate = self.station['media']['samplerate'] - self.voices = self.station['media']['voices'] - self.m3u_playlist_file = [] - if 'm3u' in self.station['media'].keys(): - self.m3u_playlist_file = self.station['media']['m3u'] - - # Server - if 'mountpoint' in self.station['server'].keys(): - self.mountpoint = self.station['server']['mountpoint'] - elif 'short_name' in self.station['infos'].keys(): - self.mountpoint = self.station['infos']['short_name'] - else: - self.mountpoint = 'default' + # Port the feeds key into the rss key if it exists (for backwards compatibility) + if 'feeds' in self.station.keys(): + self.station['rss'] = self.station['feeds'] + self.station.pop('feeds') + + # Get the mountpoint and short name first + if 'server' in self.station.keys(): + # Try to get the mountpoint as defined by the server.mountpoint parameter + self.mountpoint = str(self.station['server'].pop('mountpoint',self.mountpoint)) + self.appendtype = int(self.station['server'].pop('appendtype',self.appendtype)) + self.type = str(self.station['server'].pop('type',self.type)) + + # Get our media format since we need to to define a stream object + if 'media' in self.station.keys(): + self.media_format = str(self.station['media'].pop('format',self.media_format)) + + # We don't have a non-default mount point yet, so get info.short_name and try that + if self.mountpoint == 'default': + if 'infos' in self.station.keys(): + self.mountpoint = str(self.station['infos'].pop('short_name','default')) + # Set the short name based on the mount point. self.short_name = self.mountpoint - - if 'appendtype' in self.station['server'].keys(): - self.appendtype = self.station['server']['appendtype'] - - if 'type' in self.station['server']: - self.type = self.station['server']['type'] # 'icecast' | 'stream-m' - else: - self.type = 'icecast' - + + # Set up the stream channel if 'stream-m' in self.type: self.channel = HTTPStreamer() self.channel.mount = '/publish/' + self.mountpoint @@ -129,19 +132,161 @@ class Station(Thread): self.channel.mount = self.channel.mount + '.' + self.media_format else: sys.exit('Not a compatible server type. Choose "stream-m" or "icecast".') - - self.channel.url = self.station['infos']['url'] - self.channel.name = self.station['infos']['name'] - self.channel.genre = self.station['infos']['genre'] - self.channel.description = self.station['infos']['description'] + + # Apply defaults for the channel self.channel.format = self.media_format - self.channel.host = self.station['server']['host'] - self.channel.port = int(self.station['server']['port']) self.channel.user = 'source' - self.channel.password = self.station['server']['sourcepassword'] - self.channel.public = int(self.station['server']['public']) - self.channel.genre = self.station['infos']['genre'] - self.channel.description = self.station['infos']['description'] + + # Parse the rest of the key structure + for key in self.station.keys(): + if key == 'station_dir': + self.station_dir = self.station['station_dir'] + + if key == 'media': + # Media + for subkey in self.station[key].keys(): + if subkey == 'dir': + self.media_dir = str(self.station[key][subkey]) + + elif subkey == 'shuffle': + self.shuffle_mode = int(self.station[key][subkey]) + + elif subkey == 'bitrate': + self.bitrate = int(self.station[key][subkey]) + + elif subkey == 'ogg_quality': + self.ogg_quality = int(self.station[key][subkey]) + + elif subkey == 'samplerate': + self.samplerate = int(self.station[key][subkey]) + + elif subkey == 'voices': + self.voices = int(self.station[key][subkey]) + + elif subkey == 'm3u': + self.m3u_playlist_file = self.station[key][subkey] + + if key == 'infos': + # Stream Info + for subkey in self.station[key].keys(): + if subkey == 'url': + self.channel.url = str(self.station[key][subkey]) + + elif subkey == 'name': + self.channel.name = str(self.station[key][subkey]) + + elif subkey == 'genre': + self.channel.genre = str(self.station[key][subkey]) + + elif subkey == 'description': + self.channel.description = str(self.station[key][subkey]) + + if key == 'server': + # Server Info + for subkey in self.station[key].keys(): + if subkey == 'host': + self.channel.host = str(self.station[key][subkey]) + + elif subkey == 'port': + self.channel.port = str(self.station[key][subkey]) + + elif subkey == 'sourceuser': + self.channel.user = str(self.station[key][subkey]) + + elif subkey == 'sourcepassword': + self.channel.password = str(self.station[key][subkey]) + + elif subkey == 'public': + self.channel.public = str(self.station[key][subkey]) + + if key == 'rss': + # Server Info + for subkey in self.station[key].keys(): + if subkey == 'mode': + self.feeds_mode = int(self.station[key][subkey]) + + elif subkey == 'dir': + self.feeds_dir = str(self.station[key][subkey]) + + elif subkey == 'enclosure': + self.feeds_enclosure = int(self.station[key][subkey]) + + elif subkey == 'json': + self.feeds_json = int(self.station[key][subkey]) + + elif subkey == 'rss': + self.feeds_rss = int(self.station[key][subkey]) + + elif subkey == 'playlist': + self.feeds_playlist = int(self.station[key][subkey]) + + elif subkey == 'media_url': + self.feeds_media_url = str(self.station[key][subkey]) + + if key == 'control': + # Server Info + for subkey in self.station[key].keys(): + if subkey == 'mode': + self.osc_control_mode = int(self.station[key][subkey]) + + elif subkey == 'port': + self.osc_control_port = int(self.station[key][subkey]) + + if key == 'jingles': + # Server Info + for subkey in self.station[key].keys(): + if subkey == 'mode': + self.jingles_mode = int(self.station[key][subkey]) + + elif subkey == 'shuffle': + self.jingles_shuffle = int(self.station[key][subkey]) + + elif subkey == 'dir': + self.jingles_dir = str(self.station[key][subkey]) + + if key == 'relay': + # Server Info + for subkey in self.station[key].keys(): + if subkey == 'mode': + self.relay_mode = int(self.station[key][subkey]) + + elif subkey == 'url': + self.relay_url = str(self.station[key][subkey]) + + elif subkey == 'author': + self.relay_author = str(self.station[key][subkey]) + + if key == 'twitter': + # Server Info + for subkey in self.station[key].keys(): + if subkey == 'mode': + self.twitter_mode = int(self.station[key][subkey]) + + elif subkey == 'key': + self.twitter_key = str(self.station[key][subkey]) + + elif subkey == 'secret': + self.twitter_secret = str(self.station[key][subkey]) + + elif subkey == 'tags': + self.twitter_tags = str(self.station[key][subkey]).split(' ') + + elif subkey == 'messages': + self.twitter_messages = self.station[key][subkey] + if isinstance(self.twitter_messages, dict): + self.twitter_messages = list(self.twitter_messages) + + if key == 'record': + # Server Info + for subkey in self.station[key].keys(): + if subkey == 'mode': + self.record_mode = int(self.station[key][subkey]) + + elif subkey == 'dir': + self.record_dir = str(self.station[key][subkey]) + + + if self.channel.format == 'mp3': self.channel.audio_info = { 'bitrate': str(self.bitrate), 'samplerate': str(self.samplerate), @@ -156,29 +301,12 @@ class Station(Thread): self.channel_url = self.server_url + self.channel.mount # RSS - if 'feeds' in self.station: - self.station['rss'] = self.station['feeds'] - - if 'rss' in self.station: - if 'mode' in self.station['rss']: - self.feeds_mode = int(self.station['rss']['mode']) - self.feeds_dir = self.station['rss']['dir'] - self.feeds_enclosure = int(self.station['rss']['enclosure']) - if 'json' in self.station['rss']: - self.feeds_json = int(self.station['rss']['json']) - if 'rss' in self.station['rss']: - self.feeds_rss = int(self.station['rss']['rss']) - if 'playlist' in self.station['rss']: - self.feeds_playlist = int(self.station['rss']['playlist']) - + if self.feeds_media_url == '': self.feeds_media_url = self.channel.url + '/media/' - if 'media_url' in self.station['rss']: - if self.station['rss']['media_url']: - self.feeds_media_url = self.station['rss']['media_url'] self.base_name = self.feeds_dir + os.sep + self.short_name + '_' + self.channel.format - self.feeds_current_file = self.base_name + '_current.xml' - self.feeds_playlist_file = self.base_name + '_playlist.xml' + self.feeds_current_file = self.base_name + '_current' + self.feeds_playlist_file = self.base_name + '_playlist' # Logging self.logger.write_info('Opening ' + self.short_name + ' - ' + self.channel.name + \ @@ -194,60 +322,33 @@ class Station(Thread): self.player = Player(self.type) # OSCing - # mode = 0 means Off, mode = 1 means On - if 'control' in self.station: - self.osc_control_mode = int(self.station['control']['mode']) - if self.osc_control_mode: - self.osc_port = self.station['control']['port'] - self.osc_controller = OSCController(self.osc_port) - # OSC paths and callbacks - self.osc_controller.add_method('/media/next', 'i', self.media_next_callback) - self.osc_controller.add_method('/media/relay', 'i', self.relay_callback) - self.osc_controller.add_method('/twitter', 'i', self.twitter_callback) - self.osc_controller.add_method('/jingles', 'i', self.jingles_callback) - self.osc_controller.add_method('/record', 'i', self.record_callback) - self.osc_controller.add_method('/player', 'i', self.player_callback) - self.osc_controller.add_method('/run', 'i', self.run_callback) - self.osc_controller.start() + if self.osc_control_mode and self.osc_control_port: + self.osc_controller = OSCController(self.osc_control_port) + # OSC paths and callbacks + self.osc_controller.add_method('/media/next', 'i', self.media_next_callback) + self.osc_controller.add_method('/media/relay', 'i', self.relay_callback) + self.osc_controller.add_method('/twitter', 'i', self.twitter_callback) + self.osc_controller.add_method('/jingles', 'i', self.jingles_callback) + self.osc_controller.add_method('/record', 'i', self.record_callback) + self.osc_controller.add_method('/player', 'i', self.player_callback) + self.osc_controller.add_method('/run', 'i', self.run_callback) + self.osc_controller.start() # Jingling between each media. - if 'jingles' in self.station: - self.jingles_mode = int(self.station['jingles']['mode']) - self.jingles_shuffle = self.station['jingles']['shuffle'] - self.jingles_dir = self.station['jingles']['dir'] - if self.jingles_mode == 1: - self.jingles_callback('/jingles', [1]) + if self.jingles_mode: + self.jingles_callback('/jingles', [1]) # Relaying - if 'relay' in self.station: - self.relay_mode = int(self.station['relay']['mode']) - self.relay_url = self.station['relay']['url'] - self.relay_author = self.station['relay']['author'] - if self.relay_mode == 1: - self.relay_callback('/media/relay', [1]) + if self.relay_mode: + self.relay_callback('/media/relay', [1]) # Twitting - if 'twitter' in self.station: - self.twitter_mode = int(self.station['twitter']['mode']) - self.twitter_key = self.station['twitter']['key'] - self.twitter_secret = self.station['twitter']['secret'] - self.twitter_tags = self.station['twitter']['tags'].split(' ') - try: - self.twitter_messages = self.station['twitter']['message'] - if isinstance(self.twitter_messages, dict): - self.twitter_messages = list(self.twitter_messages) - except: - pass - - if self.twitter_mode: - self.twitter_callback('/twitter', [1]) + if self.twitter_mode: + self.twitter_callback('/twitter', [1]) # Recording - if 'record' in self.station: - self.record_mode = int(self.station['record']['mode']) - self.record_dir = self.station['record']['dir'] - if self.record_mode: - self.record_callback('/record', [1]) + if self.record_mode: + self.record_callback('/record', [1]) def run_callback(self, path, value): value = value[0] @@ -429,7 +530,7 @@ class Station(Thread): new_tracks = new_playlist_set - playlist_set self.new_tracks = list(new_tracks.copy()) - if self.twitter_mode == 1 and self.counter: + if self.twitter_mode and self.counter: self.tweet() # Shake it, Fuzz it ! @@ -554,15 +655,12 @@ class Station(Thread): items = rss_item_list,) if self.feeds_rss: - f = open(rss_file, 'w') + f = open(rss_file + '.xml', 'w') rss.write_xml(f, 'utf-8') f.close() if self.feeds_json: - path, fn = os.path.split(rss_file) - base, ext = os.path.splitext(fn) - json_file = os.path.join(self.feeds_dir, base + '.json') - f = open(json_file, 'w') + f = open(rss_file + '.json', 'w') f.write(json.dumps(json_data, separators=(',',':'))) f.close() -- 2.39.5