From: Guillaume Pellerin Date: Fri, 7 Sep 2007 16:18:22 +0000 (+0000) Subject: * Got the stream working for a recursive and looped playlist X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=eedb22180abe649ddfe506d664724920147e3da5;p=deefuzzer.git * Got the stream working for a recursive and looped playlist * Created Main() * Begun README --- diff --git a/AUTHORS b/AUTHORS index a3c9349..76499bd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,10 +1 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2007-2007 Guillaume Pellerin -# All rights reserved. -# -# This software is licensed as described in the file COPYING, which -# you should have received as part of this distribution. The terms -# are also available at http://svn.parisson.org/d-fuzz/DFuzzLicense. -# -# Author: Guillaume Pellerin \ No newline at end of file +Guillaume Pellerin \ No newline at end of file diff --git a/README b/README index a3c9349..5efba7c 100644 --- a/README +++ b/README @@ -1,10 +1,56 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2007-2007 Guillaume Pellerin -# All rights reserved. -# -# This software is licensed as described in the file COPYING, which -# you should have received as part of this distribution. The terms -# are also available at http://svn.parisson.org/d-fuzz/DFuzzLicense. -# -# Author: Guillaume Pellerin \ No newline at end of file +# README +# ====== + +d-fuzz : a lightweight icecast streaming client written in python + + +# 1. Introduction +# =============== + +D-Fuzz is a light python program that streams media data from disks to icecast2 with some metadata. It supports MP3, OGG, SPEEX and THEORA media. + +It is neccessary to provide a config file which sets all needed parameters (see myfuzz.xml for example). + + +# 2. Installation +# =============== + +see INSTALL + + +# 3. License +# =============== + + This software is licensed as described in the file COPYING, which + you should have received as part of this distribution. The terms + are also available at http://svn.parisson.org/d-fuzz/DFuzzLicense + + +# 4. Usage +# ========= + +Usage : d-fuzz $1 + + where $1 is the path for a XML config file + ex: d-fuzz ./myfuzz.xml + +Note that you must edit the config file with right parameter before executing... + + +# 5. Author +# ========= + +see AUTHORS + + +# 6. Aknowledgements +# ================== + +This work is inspired by the great - C - Oddsock's streaming program : Ezstream. +Since I needed to patch it in order to modify the playlist (randomize for example) and make external batch tools to create multiple channels, I decided to rewrite it from scratch in python. Some parts of this work are also taken from another Parisson's project : Telemeta (see http://svn.parisson.org/telemeta). + + +# 7. Contact / Infos +# ================== + +see http://parisson.com/d-fuzz/ for more details diff --git a/d-fuzz.py b/d-fuzz.py index 397395d..ddaafeb 100755 --- a/d-fuzz.py +++ b/d-fuzz.py @@ -14,61 +14,73 @@ import os import sys import shout import string +import subprocess from tools import * from xmltodict import xmltodict from mutagen.oggvorbis import OggVorbis +class ExportProcessError: + + def __init__(self, message, command, subprocess): + self.message = message + self.command = str(command) + self.subprocess = subprocess + + def __str__(self): + if self.subprocess.stderr != None: + error = self.subprocess.stderr.read() + else: + error = '' + return "%s ; command: %s; error: %s" % (self.message, + self.command, + error) + class DFuzz: - """A d-fuzz station""" + """A D-Fuzz station""" def __init__(self): self.work_dir = os.environ['HOME']+'.d-fuzz' self.conf = [] - self.id = 0 + self.id = 999999 self.buffer_size = 0xFFFF - def prog_info(self): return """ - d-fuzz : easy and light streaming tool - - Copyright (c) 2007-2007 Guillaume Pellerin - All rights reserved. - - This software is licensed as described in the file COPYING, which - you should have received as part of this distribution. The terms - are also available at http://svn.parisson.org/d-fuzz/DFuzzLicense.2 - - depends : ezstream (patched), icecast2, python, - - Usage : d-fuzz $1 - where $1 is the path for a config file - ex: d-fuzz /etc/d-fuzz/myfuzz.conf - - see http://parisson.com/d-fuzz/ for more details + d-fuzz : easy and light streaming tool + + Copyright (c) 2007-2007 Guillaume Pellerin + All rights reserved. + + This software is licensed as described in the file COPYING, which + you should have received as part of this distribution. The terms + are also available at http://svn.parisson.org/d-fuzz/DFuzzLicense + + depends : python, python-xml, shout-python, libshout3, icecast2 + recommends : python-mutagen + provides : shout-python + + Usage : d-fuzz $1 + where $1 is the path for a XML config file + ex: d-fuzz /etc/d-fuzz/myfuzz.xml + + see http://parisson.com/d-fuzz/ for more details """ def get_conf_dict(self): confile = open(self.conf_file,'r') - conf_xml = confile.readlines() - self.conf = xmltodict(conf_xml) + conf_xml = confile.read() + self.conf = xmltodict(conf_xml,'utf-8') confile.close() def get_station_names(self): return self.conf['station']['name'] - - def check_work_dir(self): if not os.isdir.exists(self.work_dir): os.mkdir(self.work_dir) - - def get_playlist_length(self): - pass - def get_playlist(self): file_list = [] for root, dirs, files in os.walk(self.media_dir): @@ -76,16 +88,24 @@ class DFuzz: file_list.append(root + os.sep + file) return file_list - def get_next_media(self): - playlist = self.get_playlist() + def get_next_media(self, playlist): lp = len(playlist) - if self.id > lp: + if self.id >= (lp - 1) : self.id = 0 else: self.id = self.id + 1 - yield playlist[self.id] - + #print self.id + return playlist[self.id] + def get_random_media(self, playlist): + lp = len(playlist) + if self.id > lp: + self.id = 0 + else: + rand = randrange(0,lp) + self.id = self.id + 1 + print self.id + return playlist[self.id] def core_process(self, command, buffer_size): """Apply command and stream data through a generator. @@ -102,15 +122,15 @@ class DFuzz: stdout = subprocess.PIPE, close_fds = True) except: - raise ExportProcessError('Command failure:', command, proc) - - + raise IOError('Command failure:', command, proc) + #pass + # Core processing while True: __chunk = proc.stdout.read(buffer_size) status = proc.poll() - if status != None and status != 0: - raise ExportProcessError('Command failure:', command, proc) + #if status != None and status != 0: + #raise ExportProcessError('Command failure:', command, proc) if len(__chunk) == 0: break yield __chunk @@ -121,62 +141,82 @@ class DFuzz: def stream(self, conf_file): self.conf_file = conf_file self.get_conf_dict() -# for station in conf_dict['station']: - chi = 0 + + #for station in conf_dict['station']: + station = self.conf['station'] + print station s = shout.Shout() print "Using libshout version %s" % shout.version() - - self.media_dir = station['media']['media_dir'][chi] - - s.host = station['server']['host'][chi] - s.port = station['server']['port'][chi] + + # Media + self.media_dir = station['media']['dir'] + format = station['media']['format'] + s.format = format + + # Server + s.protocol = 'http' # | 'xaudiocast' | 'icy' + s.host = station['server']['host'] + s.port = int(station['server']['port']) s.user = 'source' - s.password = station['server']['sourcepassword'][chi] - s.mount = station['server']['mountpoint'][chi] - s.format = station['media']['format'][chi] - s.protocol = 'http' - # | 'xaudiocast' | 'icy' + s.password = station['server']['sourcepassword'] + s.mount = '/' + station['infos']['short_name'] + '.' + format + s.public = int(station['server']['public']) + + # Infos s.name = station['infos']['name'] s.genre = station['infos']['genre'] - # s.url = '' - # s.public = 0 | 1 + s.description = station['infos']['description'] + s.url = station['infos']['url'] + # s.audio_info = { 'key': 'val', ... } # (keys are shout.SHOUT_AI_BITRATE, shout.SHOUT_AI_SAMPLERATE, # shout.SHOUT_AI_CHANNELS, shout.SHOUT_AI_QUALITY) - - s.open() - total = 0 - st = time.time() + #total = 0 + #st = time.time() command = 'cat ' - - for media in self.get_next_media(): - print "opening file %s" % media - command = 'cat '+media_dir - stream = self.core_process(command, self.buffer_size) - #s.set_metadata({'song': fa}) + + while True: + playlist = self.get_playlist() + print 'Playlist :' + print playlist + lp = len(playlist) + if lp == 0: + break + + media = self.get_next_media(playlist) + print 'opening file : %s' % media + file_name = string.replace(media, self.media_dir + os.sep, '') + print 'streaming file : %s' % file_name + s.set_metadata({'song': file_name}) + command = 'cat "%s"' % media + stream = self.core_process(command, self.buffer_size) + for chunk in stream: - total = total + len(self.buffer_size) + #total = total + len(self.buffer_size) s.send(chunk) s.sync() - et = time.time() - br = total*0.008/(et-st) - print "Sent %d bytes in %d seconds (%f kbps)" % (total, et-st, br) + #et = time.time() + #br = total*0.008/(et-st) + #print "Sent %d bytes in %d seconds (%f kbps)" % (total, et-st, br) print s.close() - -if len(sys.argv) == 2: +def main(): station = DFuzz() - station.stream(sys.argv[1]) -else: - sys.exit('No way :(') - + if len(sys.argv) == 2: + station.stream(sys.argv[1]) + else: + text = station.prog_info() + sys.exit(text) + +if __name__ == '__main__': + main() diff --git a/myfuzz.xml b/myfuzz.xml index f159369..597b54f 100644 --- a/myfuzz.xml +++ b/myfuzz.xml @@ -5,7 +5,7 @@ - Cellar Playlist + Cellar_Playlist Cellar @ D-Fuzz, Paris (100% Mix Techno, House and Groove !) @@ -14,7 +14,7 @@ http://cellar.parisson.com Techno/House - 2 + 2 diff --git a/xmltodict2.py b/xmltodict2.py index 8299361..f630d07 100644 --- a/xmltodict2.py +++ b/xmltodict2.py @@ -10,18 +10,18 @@ import locale from xml.parsers import expat # If we're in Dabo, get the default encoding. -import dabo -import dabo.lib.DesignerUtils as desUtil -from dabo.dLocalize import _ -from dabo.lib.utils import resolvePath -app = dabo.dAppRef -if app is not None: - default_encoding = app.Encoding -else: - enc = locale.getlocale()[1] - if enc is None: - enc = dabo.defaultEncoding - default_encoding = enc +#import dabo +#import dabo.lib.DesignerUtils as desUtil +#from dabo.dLocalize import _ +#from dabo.lib.utils import resolvePath +#app = dabo.dAppRef +#if app is not None: + #default_encoding = app.Encoding +#else: + #enc = locale.getlocale()[1] + #if enc is None: + #enc = dabo.defaultEncoding + #default_encoding = enc # Python seems to need to compile code with \n linesep: code_linesep = "\n" @@ -386,12 +386,12 @@ def addInheritedInfo(src, super, updateCode=False): -if __name__ == "__main__": - test_dict = {"name": "test", "attributes":{"path": "c:\\temp\\name", - "problemChars": "Welcome to \xc2\xae".decode("latin-1")}} - print "test_dict:", test_dict - xml = dicttoxml(test_dict) - print "xml:", xml - test_dict2 = xmltodict(xml) - print "test_dict2:", test_dict2 - print "same?:", test_dict == test_dict2 +#if __name__ == "__main__": + #test_dict = {"name": "test", "attributes":{"path": "c:\\temp\\name", + #"problemChars": "Welcome to \xc2\xae".decode("latin-1")}} + #print "test_dict:", test_dict + #xml = dicttoxml(test_dict) + #print "xml:", xml + #test_dict2 = xmltodict(xml) + #print "test_dict2:", test_dict2 + #print "same?:", test_dict == test_dict2