]> git.parisson.com Git - deefuzzer.git/commitdiff
reorganize the python package, get a real setup
authorGuillaume Pellerin <yomguy@parisson.com>
Tue, 31 May 2011 13:14:40 +0000 (13:14 +0000)
committerGuillaume Pellerin <yomguy@parisson.com>
Tue, 31 May 2011 13:14:40 +0000 (13:14 +0000)
63 files changed:
COPYING [deleted file]
INSTALL [deleted file]
INSTALL.rst [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README [deleted file]
README.rst [new file with mode: 0644]
deefuzzer.py [deleted file]
deefuzzer/__init__.py [new file with mode: 0644]
deefuzzer/tools/PyRSS2Gen.py [new file with mode: 0644]
deefuzzer/tools/__init__.py [new file with mode: 0644]
deefuzzer/tools/get_access_token.py [new file with mode: 0644]
deefuzzer/tools/logger.py [new file with mode: 0644]
deefuzzer/tools/mp3.py [new file with mode: 0644]
deefuzzer/tools/ogg.py [new file with mode: 0644]
deefuzzer/tools/osc.py [new file with mode: 0644]
deefuzzer/tools/osc_jingles_start.py [new file with mode: 0644]
deefuzzer/tools/osc_jingles_stop.py [new file with mode: 0644]
deefuzzer/tools/osc_player_fast.py [new file with mode: 0644]
deefuzzer/tools/osc_player_next.py [new file with mode: 0644]
deefuzzer/tools/osc_player_slow.py [new file with mode: 0644]
deefuzzer/tools/osc_record_start.py [new file with mode: 0644]
deefuzzer/tools/osc_record_stop.py [new file with mode: 0644]
deefuzzer/tools/osc_relay_start.py [new file with mode: 0644]
deefuzzer/tools/osc_relay_stop.py [new file with mode: 0644]
deefuzzer/tools/osc_twitter_start.py [new file with mode: 0644]
deefuzzer/tools/osc_twitter_stop.py [new file with mode: 0644]
deefuzzer/tools/player.py [new file with mode: 0644]
deefuzzer/tools/recorder.py [new file with mode: 0644]
deefuzzer/tools/relay.py [new file with mode: 0644]
deefuzzer/tools/station.py [new file with mode: 0644]
deefuzzer/tools/tools.py [new file with mode: 0644]
deefuzzer/tools/twitt.py [new file with mode: 0644]
deefuzzer/tools/xmltodict.py [new file with mode: 0644]
deefuzzer/tools/xmltodict2.py [new file with mode: 0644]
install.py [deleted file]
scripts/deefuzzer [new file with mode: 0755]
setup.py [new file with mode: 0644]
tools/PyRSS2Gen.py [deleted file]
tools/__init__.py [deleted file]
tools/get_access_token.py [deleted file]
tools/logger.py [deleted file]
tools/mp3.py [deleted file]
tools/ogg.py [deleted file]
tools/osc.py [deleted file]
tools/osc_jingles_start.py [deleted file]
tools/osc_jingles_stop.py [deleted file]
tools/osc_player_fast.py [deleted file]
tools/osc_player_next.py [deleted file]
tools/osc_player_slow.py [deleted file]
tools/osc_record_start.py [deleted file]
tools/osc_record_stop.py [deleted file]
tools/osc_relay_start.py [deleted file]
tools/osc_relay_stop.py [deleted file]
tools/osc_twitter_start.py [deleted file]
tools/osc_twitter_stop.py [deleted file]
tools/player.py [deleted file]
tools/recorder.py [deleted file]
tools/relay.py [deleted file]
tools/station.py [deleted file]
tools/tools.py [deleted file]
tools/twitt.py [deleted file]
tools/xmltodict.py [deleted file]
tools/xmltodict2.py [deleted file]

diff --git a/COPYING b/COPYING
deleted file mode 100644 (file)
index ad5f994..0000000
--- a/COPYING
+++ /dev/null
@@ -1,505 +0,0 @@
-CeCILL FREE SOFTWARE LICENSE AGREEMENT
-
-
-    Notice
-
-This Agreement is a Free Software license agreement that is the result
-of discussions between its authors in order to ensure compliance with
-the two main principles guiding its drafting:
-
-    * firstly, compliance with the principles governing the distribution
-      of Free Software: access to source code, broad rights granted to
-      users,
-    * secondly, the election of a governing law, French law, with which
-      it is conformant, both as regards the law of torts and
-      intellectual property law, and the protection that it offers to
-      both authors and holders of the economic rights over software.
-
-The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
-license are:
-
-Commissariat à l'Energie Atomique - CEA, a public scientific, technical
-and industrial research establishment, having its principal place of
-business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
-
-Centre National de la Recherche Scientifique - CNRS, a public scientific
-and technological establishment, having its principal place of business
-at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
-
-Institut National de Recherche en Informatique et en Automatique -
-INRIA, a public scientific and technological establishment, having its
-principal place of business at Domaine de Voluceau, Rocquencourt, BP
-105, 78153 Le Chesnay cedex, France.
-
-
-    Preamble
-
-The purpose of this Free Software license agreement is to grant users
-the right to modify and redistribute the software governed by this
-license within the framework of an open source distribution model.
-
-The exercising of these rights is conditional upon certain obligations
-for users so as to preserve this status for all subsequent redistributions.
-
-In consideration of access to the source code and the rights to copy,
-modify and redistribute granted by the license, users are provided only
-with a limited warranty and the software's author, the holder of the
-economic rights, and the successive licensors only have limited liability.
-
-In this respect, the risks associated with loading, using, modifying
-and/or developing or reproducing the software by the user are brought to
-the user's attention, given its Free Software status, which may make it
-complicated to use, with the result that its use is reserved for
-developers and experienced professionals having in-depth computer
-knowledge. Users are therefore encouraged to load and test the
-suitability of the software as regards their requirements in conditions
-enabling the security of their systems and/or data to be ensured and,
-more generally, to use and operate it in the same conditions of
-security. This Agreement may be freely reproduced and published,
-provided it is not altered, and that no provisions are either added or
-removed herefrom.
-
-This Agreement may apply to any or all software for which the holder of
-the economic rights decides to submit the use thereof to its provisions.
-
-
-    Article 1 - DEFINITIONS
-
-For the purpose of this Agreement, when the following expressions
-commence with a capital letter, they shall have the following meaning:
-
-Agreement: means this license agreement, and its possible subsequent
-versions and annexes.
-
-Software: means the software in its Object Code and/or Source Code form
-and, where applicable, its documentation, "as is" when the Licensee
-accepts the Agreement.
-
-Initial Software: means the Software in its Source Code and possibly its
-Object Code form and, where applicable, its documentation, "as is" when
-it is first distributed under the terms and conditions of the Agreement.
-
-Modified Software: means the Software modified by at least one
-Contribution.
-
-Source Code: means all the Software's instructions and program lines to
-which access is required so as to modify the Software.
-
-Object Code: means the binary files originating from the compilation of
-the Source Code.
-
-Holder: means the holder(s) of the economic rights over the Initial
-Software.
-
-Licensee: means the Software user(s) having accepted the Agreement.
-
-Contributor: means a Licensee having made at least one Contribution.
-
-Licensor: means the Holder, or any other individual or legal entity, who
-distributes the Software under the Agreement.
-
-Contribution: means any or all modifications, corrections, translations,
-adaptations and/or new functions integrated into the Software by any or
-all Contributors, as well as any or all Internal Modules.
-
-Module: means a set of sources files including their documentation that
-enables supplementary functions or services in addition to those offered
-by the Software.
-
-External Module: means any or all Modules, not derived from the
-Software, so that this Module and the Software run in separate address
-spaces, with one calling the other when they are run.
-
-Internal Module: means any or all Module, connected to the Software so
-that they both execute in the same address space.
-
-GNU GPL: means the GNU General Public License version 2 or any
-subsequent version, as published by the Free Software Foundation Inc.
-
-Parties: mean both the Licensee and the Licensor.
-
-These expressions may be used both in singular and plural form.
-
-
-    Article 2 - PURPOSE
-
-The purpose of the Agreement is the grant by the Licensor to the
-Licensee of a non-exclusive, transferable and worldwide license for the
-Software as set forth in Article 5 hereinafter for the whole term of the
-protection granted by the rights over said Software. 
-
-
-    Article 3 - ACCEPTANCE
-
-3.1 The Licensee shall be deemed as having accepted the terms and
-conditions of this Agreement upon the occurrence of the first of the
-following events:
-
-    * (i) loading the Software by any or all means, notably, by
-      downloading from a remote server, or by loading from a physical
-      medium;
-    * (ii) the first time the Licensee exercises any of the rights
-      granted hereunder.
-
-3.2 One copy of the Agreement, containing a notice relating to the
-characteristics of the Software, to the limited warranty, and to the
-fact that its use is restricted to experienced users has been provided
-to the Licensee prior to its acceptance as set forth in Article 3.1
-hereinabove, and the Licensee hereby acknowledges that it has read and
-understood it.
-
-
-    Article 4 - EFFECTIVE DATE AND TERM
-
-
-      4.1 EFFECTIVE DATE
-
-The Agreement shall become effective on the date when it is accepted by
-the Licensee as set forth in Article 3.1.
-
-
-      4.2 TERM
-
-The Agreement shall remain in force for the entire legal term of
-protection of the economic rights over the Software.
-
-
-    Article 5 - SCOPE OF RIGHTS GRANTED
-
-The Licensor hereby grants to the Licensee, who accepts, the following
-rights over the Software for any or all use, and for the term of the
-Agreement, on the basis of the terms and conditions set forth hereinafter.
-
-Besides, if the Licensor owns or comes to own one or more patents
-protecting all or part of the functions of the Software or of its
-components, the Licensor undertakes not to enforce the rights granted by
-these patents against successive Licensees using, exploiting or
-modifying the Software. If these patents are transferred, the Licensor
-undertakes to have the transferees subscribe to the obligations set
-forth in this paragraph.
-
-
-      5.1 RIGHT OF USE
-
-The Licensee is authorized to use the Software, without any limitation
-as to its fields of application, with it being hereinafter specified
-that this comprises:
-
-   1. permanent or temporary reproduction of all or part of the Software
-      by any or all means and in any or all form.
-
-   2. loading, displaying, running, or storing the Software on any or
-      all medium.
-
-   3. entitlement to observe, study or test its operation so as to
-      determine the ideas and principles behind any or all constituent
-      elements of said Software. This shall apply when the Licensee
-      carries out any or all loading, displaying, running, transmission
-      or storage operation as regards the Software, that it is entitled
-      to carry out hereunder.
-
-
-      5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
-
-The right to make Contributions includes the right to translate, adapt,
-arrange, or make any or all modifications to the Software, and the right
-to reproduce the resulting software.
-
-The Licensee is authorized to make any or all Contributions to the
-Software provided that it includes an explicit notice that it is the
-author of said Contribution and indicates the date of the creation thereof.
-
-
-      5.3 RIGHT OF DISTRIBUTION
-
-In particular, the right of distribution includes the right to publish,
-transmit and communicate the Software to the general public on any or
-all medium, and by any or all means, and the right to market, either in
-consideration of a fee, or free of charge, one or more copies of the
-Software by any means.
-
-The Licensee is further authorized to distribute copies of the modified
-or unmodified Software to third parties according to the terms and
-conditions set forth hereinafter.
-
-
-        5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
-
-The Licensee is authorized to distribute true copies of the Software in
-Source Code or Object Code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
-   1. a copy of the Agreement,
-
-   2. a notice relating to the limitation of both the Licensor's
-      warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the Object Code of the Software is
-redistributed, the Licensee allows future Licensees unhindered access to
-the full Source Code of the Software by indicating how to access it, it
-being understood that the additional cost of acquiring the Source Code
-shall not exceed the cost of transferring the data.
-
-
-        5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
-
-When the Licensee makes a Contribution to the Software, the terms and
-conditions for the distribution of the resulting Modified Software
-become subject to all the provisions of this Agreement.
-
-The Licensee is authorized to distribute the Modified Software, in
-source code or object code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
-   1. a copy of the Agreement,
-
-   2. a notice relating to the limitation of both the Licensor's
-      warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the object code of the Modified
-Software is redistributed, the Licensee allows future Licensees
-unhindered access to the full source code of the Modified Software by
-indicating how to access it, it being understood that the additional
-cost of acquiring the source code shall not exceed the cost of
-transferring the data.
-
-
-        5.3.3 DISTRIBUTION OF EXTERNAL MODULES
-
-When the Licensee has developed an External Module, the terms and
-conditions of this Agreement do not apply to said External Module, that
-may be distributed under a separate license agreement.
-
-
-        5.3.4 COMPATIBILITY WITH THE GNU GPL
-
-The Licensee can include a code that is subject to the provisions of one
-of the versions of the GNU GPL in the Modified or unmodified Software,
-and distribute that entire code under the terms of the same version of
-the GNU GPL.
-
-The Licensee can include the Modified or unmodified Software in a code
-that is subject to the provisions of one of the versions of the GNU GPL,
-and distribute that entire code under the terms of the same version of
-the GNU GPL.
-
-
-    Article 6 - INTELLECTUAL PROPERTY
-
-
-      6.1 OVER THE INITIAL SOFTWARE
-
-The Holder owns the economic rights over the Initial Software. Any or
-all use of the Initial Software is subject to compliance with the terms
-and conditions under which the Holder has elected to distribute its work
-and no one shall be entitled to modify the terms and conditions for the
-distribution of said Initial Software.
-
-The Holder undertakes that the Initial Software will remain ruled at
-least by this Agreement, for the duration set forth in Article 4.2.
-
-
-      6.2 OVER THE CONTRIBUTIONS
-
-The Licensee who develops a Contribution is the owner of the
-intellectual property rights over this Contribution as defined by
-applicable law.
-
-
-      6.3 OVER THE EXTERNAL MODULES
-
-The Licensee who develops an External Module is the owner of the
-intellectual property rights over this External Module as defined by
-applicable law and is free to choose the type of agreement that shall
-govern its distribution.
-
-
-      6.4 JOINT PROVISIONS
-
-The Licensee expressly undertakes:
-
-   1. not to remove, or modify, in any manner, the intellectual property
-      notices attached to the Software;
-
-   2. to reproduce said notices, in an identical manner, in the copies
-      of the Software modified or not.
-
-The Licensee undertakes not to directly or indirectly infringe the
-intellectual property rights of the Holder and/or Contributors on the
-Software and to take, where applicable, vis-�-vis its staff, any and all
-measures required to ensure respect of said intellectual property rights
-of the Holder and/or Contributors.
-
-
-    Article 7 - RELATED SERVICES
-
-7.1 Under no circumstances shall the Agreement oblige the Licensor to
-provide technical assistance or maintenance services for the Software.
-
-However, the Licensor is entitled to offer this type of services. The
-terms and conditions of such technical assistance, and/or such
-maintenance, shall be set forth in a separate instrument. Only the
-Licensor offering said maintenance and/or technical assistance services
-shall incur liability therefor.
-
-7.2 Similarly, any Licensor is entitled to offer to its licensees, under
-its sole responsibility, a warranty, that shall only be binding upon
-itself, for the redistribution of the Software and/or the Modified
-Software, under terms and conditions that it is free to decide. Said
-warranty, and the financial terms and conditions of its application,
-shall be subject of a separate instrument executed between the Licensor
-and the Licensee.
-
-
-    Article 8 - LIABILITY
-
-8.1 Subject to the provisions of Article 8.2, the Licensee shall be
-entitled to claim compensation for any direct loss it may have suffered
-from the Software as a result of a fault on the part of the relevant
-Licensor, subject to providing evidence thereof.
-
-8.2 The Licensor's liability is limited to the commitments made under
-this Agreement and shall not be incurred as a result of in particular:
-(i) loss due the Licensee's total or partial failure to fulfill its
-obligations, (ii) direct or consequential loss that is suffered by the
-Licensee due to the use or performance of the Software, and (iii) more
-generally, any consequential loss. In particular the Parties expressly
-agree that any or all pecuniary or business loss (i.e. loss of data,
-loss of profits, operating loss, loss of customers or orders,
-opportunity cost, any disturbance to business activities) or any or all
-legal proceedings instituted against the Licensee by a third party,
-shall constitute consequential loss and shall not provide entitlement to
-any or all compensation from the Licensor.
-
-
-    Article 9 - WARRANTY
-
-9.1 The Licensee acknowledges that the scientific and technical
-state-of-the-art when the Software was distributed did not enable all
-possible uses to be tested and verified, nor for the presence of
-possible defects to be detected. In this respect, the Licensee's
-attention has been drawn to the risks associated with loading, using,
-modifying and/or developing and reproducing the Software which are
-reserved for experienced users.
-
-The Licensee shall be responsible for verifying, by any or all means,
-the suitability of the product for its requirements, its good working
-order, and for ensuring that it shall not cause damage to either persons
-or properties.
-
-9.2 The Licensor hereby represents, in good faith, that it is entitled
-to grant all the rights over the Software (including in particular the
-rights set forth in Article 5).
-
-9.3 The Licensee acknowledges that the Software is supplied "as is" by
-the Licensor without any other express or tacit warranty, other than
-that provided for in Article 9.2 and, in particular, without any warranty 
-as to its commercial value, its secured, safe, innovative or relevant
-nature.
-
-Specifically, the Licensor does not warrant that the Software is free
-from any error, that it will operate without interruption, that it will
-be compatible with the Licensee's own equipment and software
-configuration, nor that it will meet the Licensee's requirements.
-
-9.4 The Licensor does not either expressly or tacitly warrant that the
-Software does not infringe any third party intellectual property right
-relating to a patent, software or any other property right. Therefore,
-the Licensor disclaims any and all liability towards the Licensee
-arising out of any or all proceedings for infringement that may be
-instituted in respect of the use, modification and redistribution of the
-Software. Nevertheless, should such proceedings be instituted against
-the Licensee, the Licensor shall provide it with technical and legal
-assistance for its defense. Such technical and legal assistance shall be
-decided on a case-by-case basis between the relevant Licensor and the
-Licensee pursuant to a memorandum of understanding. The Licensor
-disclaims any and all liability as regards the Licensee's use of the
-name of the Software. No warranty is given as regards the existence of
-prior rights over the name of the Software or as regards the existence
-of a trademark.
-
-
-    Article 10 - TERMINATION
-
-10.1 In the event of a breach by the Licensee of its obligations
-hereunder, the Licensor may automatically terminate this Agreement
-thirty (30) days after notice has been sent to the Licensee and has
-remained ineffective.
-
-10.2 A Licensee whose Agreement is terminated shall no longer be
-authorized to use, modify or distribute the Software. However, any
-licenses that it may have granted prior to termination of the Agreement
-shall remain valid subject to their having been granted in compliance
-with the terms and conditions hereof.
-
-
-    Article 11 - MISCELLANEOUS
-
-
-      11.1 EXCUSABLE EVENTS
-
-Neither Party shall be liable for any or all delay, or failure to
-perform the Agreement, that may be attributable to an event of force
-majeure, an act of God or an outside cause, such as defective
-functioning or interruptions of the electricity or telecommunications
-networks, network paralysis following a virus attack, intervention by
-government authorities, natural disasters, water damage, earthquakes,
-fire, explosions, strikes and labor unrest, war, etc.
-
-11.2 Any failure by either Party, on one or more occasions, to invoke
-one or more of the provisions hereof, shall under no circumstances be
-interpreted as being a waiver by the interested Party of its right to
-invoke said provision(s) subsequently.
-
-11.3 The Agreement cancels and replaces any or all previous agreements,
-whether written or oral, between the Parties and having the same
-purpose, and constitutes the entirety of the agreement between said
-Parties concerning said purpose. No supplement or modification to the
-terms and conditions hereof shall be effective as between the Parties
-unless it is made in writing and signed by their duly authorized
-representatives.
-
-11.4 In the event that one or more of the provisions hereof were to
-conflict with a current or future applicable act or legislative text,
-said act or legislative text shall prevail, and the Parties shall make
-the necessary amendments so as to comply with said act or legislative
-text. All other provisions shall remain effective. Similarly, invalidity
-of a provision of the Agreement, for any reason whatsoever, shall not
-cause the Agreement as a whole to be invalid.
-
-
-      11.5 LANGUAGE
-
-The Agreement is drafted in both French and English and both versions
-are deemed authentic.
-
-
-    Article 12 - NEW VERSIONS OF THE AGREEMENT
-
-12.1 Any person is authorized to duplicate and distribute copies of this
-Agreement.
-
-12.2 So as to ensure coherence, the wording of this Agreement is
-protected and may only be modified by the authors of the License, who
-reserve the right to periodically publish updates or new versions of the
-Agreement, each with a separate number. These subsequent versions may
-address new issues encountered by Free Software.
-
-12.3 Any Software distributed under a given version of the Agreement may
-only be subsequently distributed under the same version of the Agreement
-or a subsequent version, subject to the provisions of Article 5.3.4.
-
-
-    Article 13 - GOVERNING LAW AND JURISDICTION
-
-13.1 The Agreement is governed by French law. The Parties agree to
-endeavor to seek an amicable solution to any disagreements or disputes
-that may arise during the performance of the Agreement.
-
-13.2 Failing an amicable solution within two (2) months as from their
-occurrence, and unless emergency proceedings are necessary, the
-disagreements or disputes shall be referred to the Paris Courts having
-jurisdiction, by the more diligent Party.
-
-
-Version 2.0 dated 2006-09-05.
diff --git a/INSTALL b/INSTALL
deleted file mode 100644 (file)
index a0bd121..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2010 Guillaume Pellerin <pellerin@parisson.com>
-# 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/deefuzzer/DeeFuzzLicense.
-#
-# Author: Guillaume Pellerin <pellerin@parisson.com>
-
-
-Dependencies
-=============
-
-depends:  python, python-dev, python-xml, python-shout | shout-python, libshout3,
-         libshout3-dev, python-mutagen
-
-provides: shout-python | python-shout, python-tinyurl
-
-optional: python-twitter, python-liblo | pyliblo (>= 0.26)
-
-recommends: icecast2, python-setuptools
-
-
-Install
-=========
-
-To install the DeeFuzzer, go to the main deefuzzer app directory, for example::
-    
-    cd deefuzzer-0.5.0
-    
-and run the python installer::
-    
-    sudo python install.py
-
-For more informations, see http://svn.parisson.org/deefuzzer/
-
-
-Twitter (optional)
-=====================
-
-To get track twitting, please install python-twitter, python-oauth2 and all their dependencies.
-
-Install python-oauth2::
-    
-    sudo easy_install oauth2
-
-Get and install python-twitter (>= 0.8.1)::
-    
-    wget http://python-twitter.googlecode.com/files/python-twitter-0.8.1.tar.gz
-    tar xzf python-twitter-0.8.1.tar.gz
-    cd python-twitter-0.8.1
-    sudo python setup.py install
-
-As Twitter access requires oauth keys since 07/2010, you need to get your own access token key pair.
-Please run the dedicated script to do this from the main deefuzzer app directory::
-    
-    python tools/get_access_token.py
-    
-You will be invited to copy/paste an URL in your browser to get a pin code.
-Then copy/paste this code into the console and press ENTER.
-The script gives you a pair of keys : one access token key and one access token secret key.
-
-Change the <twitter> block options in your deefuzzer XML config file, giving the 2 keys. 
-For example::
-    
-    <twitter>
-            <mode>1</mode>
-            <key>85039615-H6yAtXXCx7NobF5W40FV0c8epGZsQGkE7MG6XRjD2</key>
-            <secret>A1YW3llB9H9qVbjH8zOQTOkMlhVqh2a7LnA9Lt0b6Gc</secret>
-            <tags>Music Groove</tags>
-    </twitter>
-    
-Your DeeFuzzer will now tweet the currently playing track and new tracks on your profile.
-
-
-OSC Control
-=============
-
-Working, but no doc yet. Please read the code :)
-
diff --git a/INSTALL.rst b/INSTALL.rst
new file mode 100644 (file)
index 0000000..a0bd121
--- /dev/null
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2010 Guillaume Pellerin <pellerin@parisson.com>
+# 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/deefuzzer/DeeFuzzLicense.
+#
+# Author: Guillaume Pellerin <pellerin@parisson.com>
+
+
+Dependencies
+=============
+
+depends:  python, python-dev, python-xml, python-shout | shout-python, libshout3,
+         libshout3-dev, python-mutagen
+
+provides: shout-python | python-shout, python-tinyurl
+
+optional: python-twitter, python-liblo | pyliblo (>= 0.26)
+
+recommends: icecast2, python-setuptools
+
+
+Install
+=========
+
+To install the DeeFuzzer, go to the main deefuzzer app directory, for example::
+    
+    cd deefuzzer-0.5.0
+    
+and run the python installer::
+    
+    sudo python install.py
+
+For more informations, see http://svn.parisson.org/deefuzzer/
+
+
+Twitter (optional)
+=====================
+
+To get track twitting, please install python-twitter, python-oauth2 and all their dependencies.
+
+Install python-oauth2::
+    
+    sudo easy_install oauth2
+
+Get and install python-twitter (>= 0.8.1)::
+    
+    wget http://python-twitter.googlecode.com/files/python-twitter-0.8.1.tar.gz
+    tar xzf python-twitter-0.8.1.tar.gz
+    cd python-twitter-0.8.1
+    sudo python setup.py install
+
+As Twitter access requires oauth keys since 07/2010, you need to get your own access token key pair.
+Please run the dedicated script to do this from the main deefuzzer app directory::
+    
+    python tools/get_access_token.py
+    
+You will be invited to copy/paste an URL in your browser to get a pin code.
+Then copy/paste this code into the console and press ENTER.
+The script gives you a pair of keys : one access token key and one access token secret key.
+
+Change the <twitter> block options in your deefuzzer XML config file, giving the 2 keys. 
+For example::
+    
+    <twitter>
+            <mode>1</mode>
+            <key>85039615-H6yAtXXCx7NobF5W40FV0c8epGZsQGkE7MG6XRjD2</key>
+            <secret>A1YW3llB9H9qVbjH8zOQTOkMlhVqh2a7LnA9Lt0b6Gc</secret>
+            <tags>Music Groove</tags>
+    </twitter>
+    
+Your DeeFuzzer will now tweet the currently playing track and new tracks on your profile.
+
+
+OSC Control
+=============
+
+Working, but no doc yet. Please read the code :)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..ad5f994
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,505 @@
+CeCILL FREE SOFTWARE LICENSE AGREEMENT
+
+
+    Notice
+
+This Agreement is a Free Software license agreement that is the result
+of discussions between its authors in order to ensure compliance with
+the two main principles guiding its drafting:
+
+    * firstly, compliance with the principles governing the distribution
+      of Free Software: access to source code, broad rights granted to
+      users,
+    * secondly, the election of a governing law, French law, with which
+      it is conformant, both as regards the law of torts and
+      intellectual property law, and the protection that it offers to
+      both authors and holders of the economic rights over software.
+
+The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
+license are:
+
+Commissariat à l'Energie Atomique - CEA, a public scientific, technical
+and industrial research establishment, having its principal place of
+business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
+
+Centre National de la Recherche Scientifique - CNRS, a public scientific
+and technological establishment, having its principal place of business
+at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
+
+Institut National de Recherche en Informatique et en Automatique -
+INRIA, a public scientific and technological establishment, having its
+principal place of business at Domaine de Voluceau, Rocquencourt, BP
+105, 78153 Le Chesnay cedex, France.
+
+
+    Preamble
+
+The purpose of this Free Software license agreement is to grant users
+the right to modify and redistribute the software governed by this
+license within the framework of an open source distribution model.
+
+The exercising of these rights is conditional upon certain obligations
+for users so as to preserve this status for all subsequent redistributions.
+
+In consideration of access to the source code and the rights to copy,
+modify and redistribute granted by the license, users are provided only
+with a limited warranty and the software's author, the holder of the
+economic rights, and the successive licensors only have limited liability.
+
+In this respect, the risks associated with loading, using, modifying
+and/or developing or reproducing the software by the user are brought to
+the user's attention, given its Free Software status, which may make it
+complicated to use, with the result that its use is reserved for
+developers and experienced professionals having in-depth computer
+knowledge. Users are therefore encouraged to load and test the
+suitability of the software as regards their requirements in conditions
+enabling the security of their systems and/or data to be ensured and,
+more generally, to use and operate it in the same conditions of
+security. This Agreement may be freely reproduced and published,
+provided it is not altered, and that no provisions are either added or
+removed herefrom.
+
+This Agreement may apply to any or all software for which the holder of
+the economic rights decides to submit the use thereof to its provisions.
+
+
+    Article 1 - DEFINITIONS
+
+For the purpose of this Agreement, when the following expressions
+commence with a capital letter, they shall have the following meaning:
+
+Agreement: means this license agreement, and its possible subsequent
+versions and annexes.
+
+Software: means the software in its Object Code and/or Source Code form
+and, where applicable, its documentation, "as is" when the Licensee
+accepts the Agreement.
+
+Initial Software: means the Software in its Source Code and possibly its
+Object Code form and, where applicable, its documentation, "as is" when
+it is first distributed under the terms and conditions of the Agreement.
+
+Modified Software: means the Software modified by at least one
+Contribution.
+
+Source Code: means all the Software's instructions and program lines to
+which access is required so as to modify the Software.
+
+Object Code: means the binary files originating from the compilation of
+the Source Code.
+
+Holder: means the holder(s) of the economic rights over the Initial
+Software.
+
+Licensee: means the Software user(s) having accepted the Agreement.
+
+Contributor: means a Licensee having made at least one Contribution.
+
+Licensor: means the Holder, or any other individual or legal entity, who
+distributes the Software under the Agreement.
+
+Contribution: means any or all modifications, corrections, translations,
+adaptations and/or new functions integrated into the Software by any or
+all Contributors, as well as any or all Internal Modules.
+
+Module: means a set of sources files including their documentation that
+enables supplementary functions or services in addition to those offered
+by the Software.
+
+External Module: means any or all Modules, not derived from the
+Software, so that this Module and the Software run in separate address
+spaces, with one calling the other when they are run.
+
+Internal Module: means any or all Module, connected to the Software so
+that they both execute in the same address space.
+
+GNU GPL: means the GNU General Public License version 2 or any
+subsequent version, as published by the Free Software Foundation Inc.
+
+Parties: mean both the Licensee and the Licensor.
+
+These expressions may be used both in singular and plural form.
+
+
+    Article 2 - PURPOSE
+
+The purpose of the Agreement is the grant by the Licensor to the
+Licensee of a non-exclusive, transferable and worldwide license for the
+Software as set forth in Article 5 hereinafter for the whole term of the
+protection granted by the rights over said Software. 
+
+
+    Article 3 - ACCEPTANCE
+
+3.1 The Licensee shall be deemed as having accepted the terms and
+conditions of this Agreement upon the occurrence of the first of the
+following events:
+
+    * (i) loading the Software by any or all means, notably, by
+      downloading from a remote server, or by loading from a physical
+      medium;
+    * (ii) the first time the Licensee exercises any of the rights
+      granted hereunder.
+
+3.2 One copy of the Agreement, containing a notice relating to the
+characteristics of the Software, to the limited warranty, and to the
+fact that its use is restricted to experienced users has been provided
+to the Licensee prior to its acceptance as set forth in Article 3.1
+hereinabove, and the Licensee hereby acknowledges that it has read and
+understood it.
+
+
+    Article 4 - EFFECTIVE DATE AND TERM
+
+
+      4.1 EFFECTIVE DATE
+
+The Agreement shall become effective on the date when it is accepted by
+the Licensee as set forth in Article 3.1.
+
+
+      4.2 TERM
+
+The Agreement shall remain in force for the entire legal term of
+protection of the economic rights over the Software.
+
+
+    Article 5 - SCOPE OF RIGHTS GRANTED
+
+The Licensor hereby grants to the Licensee, who accepts, the following
+rights over the Software for any or all use, and for the term of the
+Agreement, on the basis of the terms and conditions set forth hereinafter.
+
+Besides, if the Licensor owns or comes to own one or more patents
+protecting all or part of the functions of the Software or of its
+components, the Licensor undertakes not to enforce the rights granted by
+these patents against successive Licensees using, exploiting or
+modifying the Software. If these patents are transferred, the Licensor
+undertakes to have the transferees subscribe to the obligations set
+forth in this paragraph.
+
+
+      5.1 RIGHT OF USE
+
+The Licensee is authorized to use the Software, without any limitation
+as to its fields of application, with it being hereinafter specified
+that this comprises:
+
+   1. permanent or temporary reproduction of all or part of the Software
+      by any or all means and in any or all form.
+
+   2. loading, displaying, running, or storing the Software on any or
+      all medium.
+
+   3. entitlement to observe, study or test its operation so as to
+      determine the ideas and principles behind any or all constituent
+      elements of said Software. This shall apply when the Licensee
+      carries out any or all loading, displaying, running, transmission
+      or storage operation as regards the Software, that it is entitled
+      to carry out hereunder.
+
+
+      5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
+
+The right to make Contributions includes the right to translate, adapt,
+arrange, or make any or all modifications to the Software, and the right
+to reproduce the resulting software.
+
+The Licensee is authorized to make any or all Contributions to the
+Software provided that it includes an explicit notice that it is the
+author of said Contribution and indicates the date of the creation thereof.
+
+
+      5.3 RIGHT OF DISTRIBUTION
+
+In particular, the right of distribution includes the right to publish,
+transmit and communicate the Software to the general public on any or
+all medium, and by any or all means, and the right to market, either in
+consideration of a fee, or free of charge, one or more copies of the
+Software by any means.
+
+The Licensee is further authorized to distribute copies of the modified
+or unmodified Software to third parties according to the terms and
+conditions set forth hereinafter.
+
+
+        5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
+
+The Licensee is authorized to distribute true copies of the Software in
+Source Code or Object Code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+   1. a copy of the Agreement,
+
+   2. a notice relating to the limitation of both the Licensor's
+      warranty and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the Object Code of the Software is
+redistributed, the Licensee allows future Licensees unhindered access to
+the full Source Code of the Software by indicating how to access it, it
+being understood that the additional cost of acquiring the Source Code
+shall not exceed the cost of transferring the data.
+
+
+        5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
+
+When the Licensee makes a Contribution to the Software, the terms and
+conditions for the distribution of the resulting Modified Software
+become subject to all the provisions of this Agreement.
+
+The Licensee is authorized to distribute the Modified Software, in
+source code or object code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+   1. a copy of the Agreement,
+
+   2. a notice relating to the limitation of both the Licensor's
+      warranty and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the object code of the Modified
+Software is redistributed, the Licensee allows future Licensees
+unhindered access to the full source code of the Modified Software by
+indicating how to access it, it being understood that the additional
+cost of acquiring the source code shall not exceed the cost of
+transferring the data.
+
+
+        5.3.3 DISTRIBUTION OF EXTERNAL MODULES
+
+When the Licensee has developed an External Module, the terms and
+conditions of this Agreement do not apply to said External Module, that
+may be distributed under a separate license agreement.
+
+
+        5.3.4 COMPATIBILITY WITH THE GNU GPL
+
+The Licensee can include a code that is subject to the provisions of one
+of the versions of the GNU GPL in the Modified or unmodified Software,
+and distribute that entire code under the terms of the same version of
+the GNU GPL.
+
+The Licensee can include the Modified or unmodified Software in a code
+that is subject to the provisions of one of the versions of the GNU GPL,
+and distribute that entire code under the terms of the same version of
+the GNU GPL.
+
+
+    Article 6 - INTELLECTUAL PROPERTY
+
+
+      6.1 OVER THE INITIAL SOFTWARE
+
+The Holder owns the economic rights over the Initial Software. Any or
+all use of the Initial Software is subject to compliance with the terms
+and conditions under which the Holder has elected to distribute its work
+and no one shall be entitled to modify the terms and conditions for the
+distribution of said Initial Software.
+
+The Holder undertakes that the Initial Software will remain ruled at
+least by this Agreement, for the duration set forth in Article 4.2.
+
+
+      6.2 OVER THE CONTRIBUTIONS
+
+The Licensee who develops a Contribution is the owner of the
+intellectual property rights over this Contribution as defined by
+applicable law.
+
+
+      6.3 OVER THE EXTERNAL MODULES
+
+The Licensee who develops an External Module is the owner of the
+intellectual property rights over this External Module as defined by
+applicable law and is free to choose the type of agreement that shall
+govern its distribution.
+
+
+      6.4 JOINT PROVISIONS
+
+The Licensee expressly undertakes:
+
+   1. not to remove, or modify, in any manner, the intellectual property
+      notices attached to the Software;
+
+   2. to reproduce said notices, in an identical manner, in the copies
+      of the Software modified or not.
+
+The Licensee undertakes not to directly or indirectly infringe the
+intellectual property rights of the Holder and/or Contributors on the
+Software and to take, where applicable, vis-�-vis its staff, any and all
+measures required to ensure respect of said intellectual property rights
+of the Holder and/or Contributors.
+
+
+    Article 7 - RELATED SERVICES
+
+7.1 Under no circumstances shall the Agreement oblige the Licensor to
+provide technical assistance or maintenance services for the Software.
+
+However, the Licensor is entitled to offer this type of services. The
+terms and conditions of such technical assistance, and/or such
+maintenance, shall be set forth in a separate instrument. Only the
+Licensor offering said maintenance and/or technical assistance services
+shall incur liability therefor.
+
+7.2 Similarly, any Licensor is entitled to offer to its licensees, under
+its sole responsibility, a warranty, that shall only be binding upon
+itself, for the redistribution of the Software and/or the Modified
+Software, under terms and conditions that it is free to decide. Said
+warranty, and the financial terms and conditions of its application,
+shall be subject of a separate instrument executed between the Licensor
+and the Licensee.
+
+
+    Article 8 - LIABILITY
+
+8.1 Subject to the provisions of Article 8.2, the Licensee shall be
+entitled to claim compensation for any direct loss it may have suffered
+from the Software as a result of a fault on the part of the relevant
+Licensor, subject to providing evidence thereof.
+
+8.2 The Licensor's liability is limited to the commitments made under
+this Agreement and shall not be incurred as a result of in particular:
+(i) loss due the Licensee's total or partial failure to fulfill its
+obligations, (ii) direct or consequential loss that is suffered by the
+Licensee due to the use or performance of the Software, and (iii) more
+generally, any consequential loss. In particular the Parties expressly
+agree that any or all pecuniary or business loss (i.e. loss of data,
+loss of profits, operating loss, loss of customers or orders,
+opportunity cost, any disturbance to business activities) or any or all
+legal proceedings instituted against the Licensee by a third party,
+shall constitute consequential loss and shall not provide entitlement to
+any or all compensation from the Licensor.
+
+
+    Article 9 - WARRANTY
+
+9.1 The Licensee acknowledges that the scientific and technical
+state-of-the-art when the Software was distributed did not enable all
+possible uses to be tested and verified, nor for the presence of
+possible defects to be detected. In this respect, the Licensee's
+attention has been drawn to the risks associated with loading, using,
+modifying and/or developing and reproducing the Software which are
+reserved for experienced users.
+
+The Licensee shall be responsible for verifying, by any or all means,
+the suitability of the product for its requirements, its good working
+order, and for ensuring that it shall not cause damage to either persons
+or properties.
+
+9.2 The Licensor hereby represents, in good faith, that it is entitled
+to grant all the rights over the Software (including in particular the
+rights set forth in Article 5).
+
+9.3 The Licensee acknowledges that the Software is supplied "as is" by
+the Licensor without any other express or tacit warranty, other than
+that provided for in Article 9.2 and, in particular, without any warranty 
+as to its commercial value, its secured, safe, innovative or relevant
+nature.
+
+Specifically, the Licensor does not warrant that the Software is free
+from any error, that it will operate without interruption, that it will
+be compatible with the Licensee's own equipment and software
+configuration, nor that it will meet the Licensee's requirements.
+
+9.4 The Licensor does not either expressly or tacitly warrant that the
+Software does not infringe any third party intellectual property right
+relating to a patent, software or any other property right. Therefore,
+the Licensor disclaims any and all liability towards the Licensee
+arising out of any or all proceedings for infringement that may be
+instituted in respect of the use, modification and redistribution of the
+Software. Nevertheless, should such proceedings be instituted against
+the Licensee, the Licensor shall provide it with technical and legal
+assistance for its defense. Such technical and legal assistance shall be
+decided on a case-by-case basis between the relevant Licensor and the
+Licensee pursuant to a memorandum of understanding. The Licensor
+disclaims any and all liability as regards the Licensee's use of the
+name of the Software. No warranty is given as regards the existence of
+prior rights over the name of the Software or as regards the existence
+of a trademark.
+
+
+    Article 10 - TERMINATION
+
+10.1 In the event of a breach by the Licensee of its obligations
+hereunder, the Licensor may automatically terminate this Agreement
+thirty (30) days after notice has been sent to the Licensee and has
+remained ineffective.
+
+10.2 A Licensee whose Agreement is terminated shall no longer be
+authorized to use, modify or distribute the Software. However, any
+licenses that it may have granted prior to termination of the Agreement
+shall remain valid subject to their having been granted in compliance
+with the terms and conditions hereof.
+
+
+    Article 11 - MISCELLANEOUS
+
+
+      11.1 EXCUSABLE EVENTS
+
+Neither Party shall be liable for any or all delay, or failure to
+perform the Agreement, that may be attributable to an event of force
+majeure, an act of God or an outside cause, such as defective
+functioning or interruptions of the electricity or telecommunications
+networks, network paralysis following a virus attack, intervention by
+government authorities, natural disasters, water damage, earthquakes,
+fire, explosions, strikes and labor unrest, war, etc.
+
+11.2 Any failure by either Party, on one or more occasions, to invoke
+one or more of the provisions hereof, shall under no circumstances be
+interpreted as being a waiver by the interested Party of its right to
+invoke said provision(s) subsequently.
+
+11.3 The Agreement cancels and replaces any or all previous agreements,
+whether written or oral, between the Parties and having the same
+purpose, and constitutes the entirety of the agreement between said
+Parties concerning said purpose. No supplement or modification to the
+terms and conditions hereof shall be effective as between the Parties
+unless it is made in writing and signed by their duly authorized
+representatives.
+
+11.4 In the event that one or more of the provisions hereof were to
+conflict with a current or future applicable act or legislative text,
+said act or legislative text shall prevail, and the Parties shall make
+the necessary amendments so as to comply with said act or legislative
+text. All other provisions shall remain effective. Similarly, invalidity
+of a provision of the Agreement, for any reason whatsoever, shall not
+cause the Agreement as a whole to be invalid.
+
+
+      11.5 LANGUAGE
+
+The Agreement is drafted in both French and English and both versions
+are deemed authentic.
+
+
+    Article 12 - NEW VERSIONS OF THE AGREEMENT
+
+12.1 Any person is authorized to duplicate and distribute copies of this
+Agreement.
+
+12.2 So as to ensure coherence, the wording of this Agreement is
+protected and may only be modified by the authors of the License, who
+reserve the right to periodically publish updates or new versions of the
+Agreement, each with a separate number. These subsequent versions may
+address new issues encountered by Free Software.
+
+12.3 Any Software distributed under a given version of the Agreement may
+only be subsequently distributed under the same version of the Agreement
+or a subsequent version, subject to the provisions of Article 5.3.4.
+
+
+    Article 13 - GOVERNING LAW AND JURISDICTION
+
+13.1 The Agreement is governed by French law. The Parties agree to
+endeavor to seek an amicable solution to any disagreements or disputes
+that may arise during the performance of the Agreement.
+
+13.2 Failing an amicable solution within two (2) months as from their
+occurrence, and unless emergency proceedings are necessary, the
+disagreements or disputes shall be referred to the Paris Courts having
+jurisdiction, by the more diligent Party.
+
+
+Version 2.0 dated 2006-09-05.
diff --git a/README b/README
deleted file mode 100644 (file)
index 7cb265f..0000000
--- a/README
+++ /dev/null
@@ -1,144 +0,0 @@
-================
-DeeFuzzer README
-================
-
-deefuzzer : an easy and instant media streaming tool
-
-
-Introduction
-============
-
-DeeFuzzer is a new light and instant software made for streaming audio and video over internet. 
-It is dedicated to people who wants to create playlisted web radios or web TVs with rich media contents including some metadata.
-
-Here are the main features of the deefuzzer:
-
- * MP3 and OGG (audio & video) file streaming over internet (Icecast)
- * Full metadata encapsulation and management
- * RSS podcast generator (current tracks and playlists)
- * M3U playlist generator
- * Recursive, random (shuffled) or pre-defined playlists
- * Multi-threaded architecture : multiple station streaming with one config file
- * Auto Twitter posting of the current playing and new tracks
- * Jingling between main tracks
- * OSC controller : control the main functions from a distant terminal
- * Station relaying : stream other stations like *LIVE* sessions !
- * Very light and optimized streaming process
-
-It is neccessary to provide a config file which sets all needed parameters
-Please see example/myfuzz.xml for an example.
-
-
-Installation
-============
-
-see INSTALL
-
-
-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/deefuzzer/DeeFuzzerLicense
-
-
-Usage
-=====
-
-Usage : deefuzzer CONFIGFILE
-
-where CONFIGFILE is the path for a XML config file. For example::
-
-    deefuzzer example/deefuzzer.xml
-
-To make the deefuzzer act as a deamon, just play it in the background::
-
-    deefuzzer example/deefuzzer.xml &
-
-Note that you must edit the config file with right parameters before playing.
-You can find an example for a draft XML file in the directory "example/" of this
-application (maybe in /usr/share/deefuzzer/example/ if installed with the help of install.py)
-
-Since 0.3, deefuzzer doesn't print anything into the shell, then a right <log> parameter
-is needed in the XML config file.
-
-BE CAREFULL : at the moment, the multi-threaded implementation of deefuzzer instances
-avoids shutting down the streams with CTRL+C... You have to kill them manually,
-after a CTRL+Z, making this::
-
-    pkill -9 deefuzzer
-
-
-XML Configuration
-=================
-
-See example/deefuzz_doc.xml in a text editor.
-The inline comments should help you to configure your stations.
-
-
-OSC Control
-===========
-
-We can now control some functions of the deefuzzer with external commands
-coming from an OSC client. These are accessible only if the "control" tag is
-set up in the config file (see example/myfuzz.xml again...).
-
-Next track::
-
-    python tools/osc_next.py
-
-Start twitting::
-
-    python tools/osc_twitter_start.py
-
-Stop twitting::
-
-    python see tools/osc_twitter_stop.py
-
-Start relaying::
-
-    python tools/osc_relay_start.py
-
-Stop relaying::
-
-    python see tools/osc_relay_stop.py
-
-Start jingling::
-
-    python see tools/osc_jingles_start.py
-
-Stop jingling::
-
-    python see tools/osc_jingles_stop.py
-
-
-Author
-======
-
-Guillaume Pellerin <yomguy@parisson.com>
-
-
-Licence
-=======
-
-See COPYING
-
-
-Aknowledgements
-===============
-
-This work is inspired by the great - C coded - 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://telemeta.org).
-
-
-Contact / Infos
-===============
-
-see http://svn.parisson.org/deefuzzer/ or http://parisson.com for more info.
-
diff --git a/README.rst b/README.rst
new file mode 100644 (file)
index 0000000..7cb265f
--- /dev/null
@@ -0,0 +1,144 @@
+================
+DeeFuzzer README
+================
+
+deefuzzer : an easy and instant media streaming tool
+
+
+Introduction
+============
+
+DeeFuzzer is a new light and instant software made for streaming audio and video over internet. 
+It is dedicated to people who wants to create playlisted web radios or web TVs with rich media contents including some metadata.
+
+Here are the main features of the deefuzzer:
+
+ * MP3 and OGG (audio & video) file streaming over internet (Icecast)
+ * Full metadata encapsulation and management
+ * RSS podcast generator (current tracks and playlists)
+ * M3U playlist generator
+ * Recursive, random (shuffled) or pre-defined playlists
+ * Multi-threaded architecture : multiple station streaming with one config file
+ * Auto Twitter posting of the current playing and new tracks
+ * Jingling between main tracks
+ * OSC controller : control the main functions from a distant terminal
+ * Station relaying : stream other stations like *LIVE* sessions !
+ * Very light and optimized streaming process
+
+It is neccessary to provide a config file which sets all needed parameters
+Please see example/myfuzz.xml for an example.
+
+
+Installation
+============
+
+see INSTALL
+
+
+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/deefuzzer/DeeFuzzerLicense
+
+
+Usage
+=====
+
+Usage : deefuzzer CONFIGFILE
+
+where CONFIGFILE is the path for a XML config file. For example::
+
+    deefuzzer example/deefuzzer.xml
+
+To make the deefuzzer act as a deamon, just play it in the background::
+
+    deefuzzer example/deefuzzer.xml &
+
+Note that you must edit the config file with right parameters before playing.
+You can find an example for a draft XML file in the directory "example/" of this
+application (maybe in /usr/share/deefuzzer/example/ if installed with the help of install.py)
+
+Since 0.3, deefuzzer doesn't print anything into the shell, then a right <log> parameter
+is needed in the XML config file.
+
+BE CAREFULL : at the moment, the multi-threaded implementation of deefuzzer instances
+avoids shutting down the streams with CTRL+C... You have to kill them manually,
+after a CTRL+Z, making this::
+
+    pkill -9 deefuzzer
+
+
+XML Configuration
+=================
+
+See example/deefuzz_doc.xml in a text editor.
+The inline comments should help you to configure your stations.
+
+
+OSC Control
+===========
+
+We can now control some functions of the deefuzzer with external commands
+coming from an OSC client. These are accessible only if the "control" tag is
+set up in the config file (see example/myfuzz.xml again...).
+
+Next track::
+
+    python tools/osc_next.py
+
+Start twitting::
+
+    python tools/osc_twitter_start.py
+
+Stop twitting::
+
+    python see tools/osc_twitter_stop.py
+
+Start relaying::
+
+    python tools/osc_relay_start.py
+
+Stop relaying::
+
+    python see tools/osc_relay_stop.py
+
+Start jingling::
+
+    python see tools/osc_jingles_start.py
+
+Stop jingling::
+
+    python see tools/osc_jingles_stop.py
+
+
+Author
+======
+
+Guillaume Pellerin <yomguy@parisson.com>
+
+
+Licence
+=======
+
+See COPYING
+
+
+Aknowledgements
+===============
+
+This work is inspired by the great - C coded - 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://telemeta.org).
+
+
+Contact / Infos
+===============
+
+see http://svn.parisson.org/deefuzzer/ or http://parisson.com for more info.
+
diff --git a/deefuzzer.py b/deefuzzer.py
deleted file mode 100755 (executable)
index ee84231..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2006-2009 Guillaume Pellerin
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import sys
-import shout
-import Queue
-import datetime
-import platform
-from threading import Thread
-from tools import *
-
-version = '0.5.1'
-year = datetime.datetime.now().strftime("%Y")
-platform_system = platform.system()
-
-
-def prog_info():
-    desc = """ deefuzzer : easy and instant media streaming tool
- version : %s
- running on system : %s
-
- Copyright (c) 2007-%s Guillaume Pellerin <yomguy@parisson.com>
- 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/deefuzzer/DeeFuzzerLicense
-
- depends:  python, python-dev, python-xml, python-shout | shout-python, libshout3,
-           libshout3-dev, icecast2, python-mutagen
-
- recommends: python-twitter, python-tinyurl, python-liblo | pyliblo (>= 0.26)
-
- provides:  shout-python
-
- Usage : deefuzzer $1
-  where $1 is the path for a XML config file
-  ex: deefuzzer example/myfuzz.xml
-
- see http://svn.parisson.org/deefuzzer/ for more details
-        """
-
-    return desc % (version, platform_system, year)
-
-
-class DeeFuzzer(Thread):
-    """a DeeFuzzer diffuser"""
-
-    def __init__(self, conf_file):
-        Thread.__init__(self)
-        self.conf_file = conf_file
-        self.conf = self.get_conf_dict()
-        if 'log' in self.conf['deefuzzer'].keys():
-            self.logger = Logger(self.conf['deefuzzer']['log'])
-        else:
-            self.logger = Logger('.' + os.sep + 'deefuzzer.log')
-        if 'm3u' in self.conf['deefuzzer'].keys():
-            self.m3u = self.conf['deefuzzer']['m3u']
-        else:
-            self.m3u = '.' + os.sep + 'deefuzzer.m3u'
-
-        if isinstance(self.conf['deefuzzer']['station'], dict):
-            # Fix wrong type data from xmltodict when one station (*)
-            self.nb_stations = 1
-        else:
-            self.nb_stations = len(self.conf['deefuzzer']['station'])
-
-        # Set the deefuzzer logger
-        self.logger.write_info('Starting DeeFuzzer v' + version)
-        self.logger.write_info('Using libshout version %s' % shout.version())
-
-        # Init all Stations
-        self.stations = []
-        self.logger.write_info('Number of stations : ' + str(self.nb_stations))
-
-    def get_conf_dict(self):
-        confile = open(self.conf_file,'r')
-        conf_xml = confile.read()
-        confile.close()
-        return xmltodict(conf_xml,'utf-8')
-
-    def set_m3u_playlist(self):
-        m3u_dir = os.sep.join(self.m3u.split(os.sep)[:-1])
-        if not os.path.exists(m3u_dir):
-            os.makedirs(m3u_dir)
-        m3u = open(self.m3u, 'w')
-        m3u.write('#EXTM3U\n')
-        for s in self.stations:
-            info = '#EXTINF:%s,%s - %s\n' % ('-1',s.short_name, s.channel.name)
-            url =  s.channel.protocol + '://' + s.channel.host + ':' + str(s.channel.port) + s.channel.mount + '\n'
-            m3u.write(info)
-            m3u.write(url)
-        m3u.close()
-        self.logger.write_info('Writing M3U file to : ' + self.m3u)
-
-
-    def run(self):
-        q = Queue.Queue(1)
-
-        for i in range(0,self.nb_stations):
-            if isinstance(self.conf['deefuzzer']['station'], dict):
-                station = self.conf['deefuzzer']['station']
-            else:
-                station = self.conf['deefuzzer']['station'][i]
-            self.stations.append(Station(station, q, self.logger, self.m3u))
-
-        self.set_m3u_playlist()
-        p = Producer(q)
-        p.start()
-
-        # Start the Stations
-        for i in range(0,self.nb_stations):
-            self.stations[i].start()
-
-
-class Producer(Thread):
-    """a DeeFuzzer Producer master thread"""
-
-    def __init__(self, q):
-        Thread.__init__(self)
-        self.q = q
-
-    def run(self):
-        i=0
-        q = self.q
-        while True:
-            q.put(i,1)
-            i+=1
-
-
-def main():
-    if len(sys.argv) >= 2:
-        d = DeeFuzzer(sys.argv[-1])
-        d.start()
-    else:
-        text = prog_info()
-        sys.exit(text)
-
-if __name__ == '__main__':
-    main()
diff --git a/deefuzzer/__init__.py b/deefuzzer/__init__.py
new file mode 100644 (file)
index 0000000..3777607
--- /dev/null
@@ -0,0 +1 @@
+from tools import *
diff --git a/deefuzzer/tools/PyRSS2Gen.py b/deefuzzer/tools/PyRSS2Gen.py
new file mode 100644 (file)
index 0000000..fc1f1cf
--- /dev/null
@@ -0,0 +1,443 @@
+"""PyRSS2Gen - A Python library for generating RSS 2.0 feeds."""
+
+__name__ = "PyRSS2Gen"
+__version__ = (1, 0, 0)
+__author__ = "Andrew Dalke <dalke@dalkescientific.com>"
+
+_generator_name = __name__ + "-" + ".".join(map(str, __version__))
+
+import datetime
+
+# Could make this the base class; will need to add 'publish'
+class WriteXmlMixin:
+    def write_xml(self, outfile, encoding = "iso-8859-1"):
+        from xml.sax import saxutils
+        handler = saxutils.XMLGenerator(outfile, encoding)
+        handler.startDocument()
+        self.publish(handler)
+        handler.endDocument()
+
+    def to_xml(self, encoding = "iso-8859-1"):
+        try:
+            import cStringIO as StringIO
+        except ImportError:
+            import StringIO
+        f = StringIO.StringIO()
+        self.write_xml(f, encoding)
+        return f.getvalue()
+
+
+def _element(handler, name, obj, d = {}):
+    if isinstance(obj, basestring) or obj is None:
+        # special-case handling to make the API easier
+        # to use for the common case.
+        handler.startElement(name, d)
+        if obj is not None:
+            handler.characters(obj)
+        handler.endElement(name)
+    else:
+        # It better know how to emit the correct XML.
+        obj.publish(handler)
+
+def _opt_element(handler, name, obj):
+    if obj is None:
+        return
+    _element(handler, name, obj)
+
+
+def _format_date(dt):
+    """convert a datetime into an RFC 822 formatted date
+
+    Input date must be in GMT.
+    """
+    # Looks like:
+    #   Sat, 07 Sep 2002 00:00:01 GMT
+    # Can't use strftime because that's locale dependent
+    #
+    # Isn't there a standard way to do this for Python?  The
+    # rfc822 and email.Utils modules assume a timestamp.  The
+    # following is based on the rfc822 module.
+    return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
+            ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()],
+            dt.day,
+            ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
+             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][dt.month-1],
+            dt.year, dt.hour, dt.minute, dt.second)
+
+        
+##
+# A couple simple wrapper objects for the fields which
+# take a simple value other than a string.
+class IntElement:
+    """implements the 'publish' API for integers
+
+    Takes the tag name and the integer value to publish.
+    
+    (Could be used for anything which uses str() to be published
+    to text for XML.)
+    """
+    element_attrs = {}
+    def __init__(self, name, val):
+        self.name = name
+        self.val = val
+    def publish(self, handler):
+        handler.startElement(self.name, self.element_attrs)
+        handler.characters(str(self.val))
+        handler.endElement(self.name)
+
+class DateElement:
+    """implements the 'publish' API for a datetime.datetime
+
+    Takes the tag name and the datetime to publish.
+
+    Converts the datetime to RFC 2822 timestamp (4-digit year).
+    """
+    def __init__(self, name, dt):
+        self.name = name
+        self.dt = dt
+    def publish(self, handler):
+        _element(handler, self.name, _format_date(self.dt))
+####
+
+class Category:
+    """Publish a category element"""
+    def __init__(self, category, domain = None):
+        self.category = category
+        self.domain = domain
+    def publish(self, handler):
+        d = {}
+        if self.domain is not None:
+            d["domain"] = self.domain
+        _element(handler, "category", self.category, d)
+
+class Cloud:
+    """Publish a cloud"""
+    def __init__(self, domain, port, path,
+                 registerProcedure, protocol):
+        self.domain = domain
+        self.port = port
+        self.path = path
+        self.registerProcedure = registerProcedure
+        self.protocol = protocol
+    def publish(self, handler):
+        _element(handler, "cloud", None, {
+            "domain": self.domain,
+            "port": str(self.port),
+            "path": self.path,
+            "registerProcedure": self.registerProcedure,
+            "protocol": self.protocol})
+
+class Image:
+    """Publish a channel Image"""
+    element_attrs = {}
+    def __init__(self, url, title, link,
+                 width = None, height = None, description = None):
+        self.url = url
+        self.title = title
+        self.link = link
+        self.width = width
+        self.height = height
+        self.description = description
+        
+    def publish(self, handler):
+        handler.startElement("image", self.element_attrs)
+
+        _element(handler, "url", self.url)
+        _element(handler, "title", self.title)
+        _element(handler, "link", self.link)
+
+        width = self.width
+        if isinstance(width, int):
+            width = IntElement("width", width)
+        _opt_element(handler, "width", width)
+        
+        height = self.height
+        if isinstance(height, int):
+            height = IntElement("height", height)
+        _opt_element(handler, "height", height)
+
+        _opt_element(handler, "description", self.description)
+
+        handler.endElement("image")
+
+class Guid:
+    """Publish a guid
+
+    Defaults to being a permalink, which is the assumption if it's
+    omitted.  Hence strings are always permalinks.
+    """
+    def __init__(self, guid, isPermaLink = 1):
+        self.guid = guid
+        self.isPermaLink = isPermaLink
+    def publish(self, handler):
+        d = {}
+        if self.isPermaLink:
+            d["isPermaLink"] = "true"
+        else:
+            d["isPermaLink"] = "false"
+        _element(handler, "guid", self.guid, d)
+
+class TextInput:
+    """Publish a textInput
+
+    Apparently this is rarely used.
+    """
+    element_attrs = {}
+    def __init__(self, title, description, name, link):
+        self.title = title
+        self.description = description
+        self.name = name
+        self.link = link
+
+    def publish(self, handler):
+        handler.startElement("textInput", self.element_attrs)
+        _element(handler, "title", self.title)
+        _element(handler, "description", self.description)
+        _element(handler, "name", self.name)
+        _element(handler, "link", self.link)
+        handler.endElement("textInput")
+        
+
+class Enclosure:
+    """Publish an enclosure"""
+    def __init__(self, url, length, type):
+        self.url = url
+        self.length = length
+        self.type = type
+    def publish(self, handler):
+        _element(handler, "enclosure", None,
+                 {"url": self.url,
+                  "length": str(self.length),
+                  "type": self.type,
+                  })
+
+class Source:
+    """Publish the item's original source, used by aggregators"""
+    def __init__(self, name, url):
+        self.name = name
+        self.url = url
+    def publish(self, handler):
+        _element(handler, "source", self.name, {"url": self.url})
+
+class SkipHours:
+    """Publish the skipHours
+
+    This takes a list of hours, as integers.
+    """
+    element_attrs = {}
+    def __init__(self, hours):
+        self.hours = hours
+    def publish(self, handler):
+        if self.hours:
+            handler.startElement("skipHours", self.element_attrs)
+            for hour in self.hours:
+                _element(handler, "hour", str(hour))
+            handler.endElement("skipHours")
+
+class SkipDays:
+    """Publish the skipDays
+
+    This takes a list of days as strings.
+    """
+    element_attrs = {}
+    def __init__(self, days):
+        self.days = days
+    def publish(self, handler):
+        if self.days:
+            handler.startElement("skipDays", self.element_attrs)
+            for day in self.days:
+                _element(handler, "day", day)
+            handler.endElement("skipDays")
+
+class RSS2(WriteXmlMixin):
+    """The main RSS class.
+
+    Stores the channel attributes, with the "category" elements under
+    ".categories" and the RSS items under ".items".
+    """
+    
+    rss_attrs = {"version": "2.0"}
+    element_attrs = {}
+    def __init__(self,
+                 title,
+                 link,
+                 description,
+
+                 language = None,
+                 copyright = None,
+                 managingEditor = None,
+                 webMaster = None,
+                 pubDate = None,  # a datetime, *in* *GMT*
+                 lastBuildDate = None, # a datetime
+                 
+                 categories = None, # list of strings or Category
+                 generator = _generator_name,
+                 docs = "http://blogs.law.harvard.edu/tech/rss",
+                 cloud = None,    # a Cloud
+                 ttl = None,      # integer number of minutes
+
+                 image = None,     # an Image
+                 rating = None,    # a string; I don't know how it's used
+                 textInput = None, # a TextInput
+                 skipHours = None, # a SkipHours with a list of integers
+                 skipDays = None,  # a SkipDays with a list of strings
+
+                 items = None,     # list of RSSItems
+                 ):
+        self.title = title
+        self.link = link
+        self.description = description
+        self.language = language
+        self.copyright = copyright
+        self.managingEditor = managingEditor
+
+        self.webMaster = webMaster
+        self.pubDate = pubDate
+        self.lastBuildDate = lastBuildDate
+        
+        if categories is None:
+            categories = []
+        self.categories = categories
+        self.generator = generator
+        self.docs = docs
+        self.cloud = cloud
+        self.ttl = ttl
+        self.image = image
+        self.rating = rating
+        self.textInput = textInput
+        self.skipHours = skipHours
+        self.skipDays = skipDays
+
+        if items is None:
+            items = []
+        self.items = items
+
+    def publish(self, handler):
+        handler.startElement("rss", self.rss_attrs)
+        handler.startElement("channel", self.element_attrs)
+        _element(handler, "title", self.title)
+        _element(handler, "link", self.link)
+        _element(handler, "description", self.description)
+
+        self.publish_extensions(handler)
+        
+        _opt_element(handler, "language", self.language)
+        _opt_element(handler, "copyright", self.copyright)
+        _opt_element(handler, "managingEditor", self.managingEditor)
+        _opt_element(handler, "webMaster", self.webMaster)
+
+        pubDate = self.pubDate
+        if isinstance(pubDate, datetime.datetime):
+            pubDate = DateElement("pubDate", pubDate)
+        _opt_element(handler, "pubDate", pubDate)
+
+        lastBuildDate = self.lastBuildDate
+        if isinstance(lastBuildDate, datetime.datetime):
+            lastBuildDate = DateElement("lastBuildDate", lastBuildDate)
+        _opt_element(handler, "lastBuildDate", lastBuildDate)
+
+        for category in self.categories:
+            if isinstance(category, basestring):
+                category = Category(category)
+            category.publish(handler)
+
+        _opt_element(handler, "generator", self.generator)
+        _opt_element(handler, "docs", self.docs)
+
+        if self.cloud is not None:
+            self.cloud.publish(handler)
+
+        ttl = self.ttl
+        if isinstance(self.ttl, int):
+            ttl = IntElement("ttl", ttl)
+        _opt_element(handler, "tt", ttl)
+
+        if self.image is not None:
+            self.image.publish(handler)
+
+        _opt_element(handler, "rating", self.rating)
+        if self.textInput is not None:
+            self.textInput.publish(handler)
+        if self.skipHours is not None:
+            self.skipHours.publish(handler)
+        if self.skipDays is not None:
+            self.skipDays.publish(handler)
+
+        for item in self.items:
+            item.publish(handler)
+
+        handler.endElement("channel")
+        handler.endElement("rss")
+
+    def publish_extensions(self, handler):
+        # Derived classes can hook into this to insert
+        # output after the three required fields.
+        pass
+
+    
+    
+class RSSItem(WriteXmlMixin):
+    """Publish an RSS Item"""
+    element_attrs = {}
+    def __init__(self,
+                 title = None,  # string
+                 link = None,   # url as string
+                 description = None, # string
+                 author = None,      # email address as string
+                 categories = None,  # list of string or Category
+                 comments = None,  # url as string
+                 enclosure = None, # an Enclosure
+                 guid = None,    # a unique string
+                 pubDate = None, # a datetime
+                 source = None,  # a Source
+                 ):
+        
+        if title is None and description is None:
+            raise TypeError(
+                "must define at least one of 'title' or 'description'")
+        self.title = title
+        self.link = link
+        self.description = description
+        self.author = author
+        if categories is None:
+            categories = []
+        self.categories = categories
+        self.comments = comments
+        self.enclosure = enclosure
+        self.guid = guid
+        self.pubDate = pubDate
+        self.source = source
+        # It sure does get tedious typing these names three times...
+
+    def publish(self, handler):
+        handler.startElement("item", self.element_attrs)
+        _opt_element(handler, "title", self.title)
+        _opt_element(handler, "link", self.link)
+        self.publish_extensions(handler)
+        _opt_element(handler, "description", self.description)
+        _opt_element(handler, "author", self.author)
+
+        for category in self.categories:
+            if isinstance(category, basestring):
+                category = Category(category)
+            category.publish(handler)
+        
+        _opt_element(handler, "comments", self.comments)
+        if self.enclosure is not None:
+            self.enclosure.publish(handler)
+        _opt_element(handler, "guid", self.guid)
+
+        pubDate = self.pubDate
+        if isinstance(pubDate, datetime.datetime):
+            pubDate = DateElement("pubDate", pubDate)
+        _opt_element(handler, "pubDate", pubDate)
+
+        if self.source is not None:
+            self.source.publish(handler)
+        
+        handler.endElement("item")
+
+    def publish_extensions(self, handler):
+        # Derived classes can hook into this to insert
+        # output after the title and link elements
+        pass
diff --git a/deefuzzer/tools/__init__.py b/deefuzzer/tools/__init__.py
new file mode 100644 (file)
index 0000000..cca19e3
--- /dev/null
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+from station import *
+from xmltodict import *
+from PyRSS2Gen import *
+from mp3 import *
+from ogg import *
+from logger import *
+from player import *
+from recorder import *
+from osc import *
+from twitt import *
+from relay import *
+from tools import *
diff --git a/deefuzzer/tools/get_access_token.py b/deefuzzer/tools/get_access_token.py
new file mode 100644 (file)
index 0000000..a614173
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2007 The Python-Twitter Developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import os
+import sys
+
+# parse_qsl moved to urlparse module in v2.6
+try:
+  from urlparse import parse_qsl
+except:
+  from cgi import parse_qsl
+
+import oauth2 as oauth
+
+REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
+ACCESS_TOKEN_URL  = 'https://api.twitter.com/oauth/access_token'
+AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
+SIGNIN_URL        = 'https://api.twitter.com/oauth/authenticate'
+
+consumer_key    = 'ozs9cPS2ci6eYQzzMSTb4g'
+consumer_secret = '1kNEffHgGSXO2gMNTr8HRum5s2ofx3VQnJyfd0es'
+
+if consumer_key is None or consumer_secret is None:
+  print 'You need to edit this script and provide values for the'
+  print 'consumer_key and also consumer_secret.'
+  print ''
+  print 'The values you need come from Twitter - you need to register'
+  print 'as a developer your "application".  This is needed only until'
+  print 'Twitter finishes the idea they have of a way to allow open-source'
+  print 'based libraries to have a token that can be used to generate a'
+  print 'one-time use key that will allow the library to make the request'
+  print 'on your behalf.'
+  print ''
+  sys.exit(1)
+
+signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1()
+oauth_consumer             = oauth.Consumer(key=consumer_key, secret=consumer_secret)
+oauth_client               = oauth.Client(oauth_consumer)
+
+print 'Requesting temp token from Twitter'
+
+resp, content = oauth_client.request(REQUEST_TOKEN_URL, 'GET')
+
+if resp['status'] != '200':
+  print 'Invalid respond from Twitter requesting temp token: %s' % resp['status']
+else:
+  request_token = dict(parse_qsl(content))
+
+  print ''
+  print 'Please visit this Twitter page and retrieve the pincode to be used'
+  print 'in the next step to obtaining an Authentication Token:'
+  print ''
+  print '%s?oauth_token=%s' % (AUTHORIZATION_URL, request_token['oauth_token'])
+  print ''
+
+  pincode = raw_input('Pincode? ')
+
+  token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
+  token.set_verifier(pincode)
+
+  print ''
+  print 'Generating and signing request for an access token'
+  print ''
+
+  oauth_client  = oauth.Client(oauth_consumer, token)
+  resp, content = oauth_client.request(ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % pincode)
+  access_token  = dict(parse_qsl(content))
+
+  if resp['status'] != '200':
+    print 'The request for a Token did not succeed: %s' % resp['status']
+    print access_token
+  else:
+    print 'Your Twitter Access Token key: %s' % access_token['oauth_token']
+    print '          Access Token secret: %s' % access_token['oauth_token_secret']
+    print ''
+
diff --git a/deefuzzer/tools/logger.py b/deefuzzer/tools/logger.py
new file mode 100644 (file)
index 0000000..2f68c4d
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import logging
+
+class Logger:
+    """A logging object"""
+
+    def __init__(self, file):
+        self.logger = logging.getLogger('myapp')
+        self.hdlr = logging.FileHandler(file)
+        self.formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
+        self.hdlr.setFormatter(self.formatter)
+        self.logger.addHandler(self.hdlr)
+        self.logger.setLevel(logging.INFO)
+
+    def write_info(self, message):
+        self.logger.info(message)
+
+    def write_error(self, message):
+        self.logger.error(message)
+
diff --git a/deefuzzer/tools/mp3.py b/deefuzzer/tools/mp3.py
new file mode 100644 (file)
index 0000000..4af6c73
--- /dev/null
@@ -0,0 +1,150 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright Guillaume Pellerin (2006-2009)
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL  license under French law and
+# abiding by the rules of distribution of free software.  You can  use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty  and the software's author,  the holder of the
+# economic rights,  and the successive licensors  have only  limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading,  using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean  that it is complicated to manipulate,  and  that  also
+# therefore means  that it is reserved for developers  and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and,  more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import datetime
+from mutagen.easyid3 import EasyID3
+from mutagen.mp3 import MP3, MPEGInfo
+from mutagen import id3
+from tools import *
+
+EasyID3.valid_keys["comment"]="COMM::'XXX'"
+EasyID3.valid_keys["copyright"]="TCOP::'XXX'"
+
+class Mp3:
+    """A MP3 file object"""
+
+    def __init__(self, media):
+        self.media = media
+        self.item_id = ''
+        self.source = self.media
+        self.options = {}
+        self.bitrate_default = '192'
+        self.cache_dir = os.sep + 'tmp'
+        self.keys2id3 = {'title': 'TIT2',
+                    'artist': 'TPE1',
+                    'album': 'TALB',
+                    'date': 'TDRC',
+                    'comment': 'COMM',
+                    'genre': 'TCON',
+                    'copyright': 'TCOP',
+                    }
+        self.mp3 = MP3(self.media, ID3=EasyID3)
+        self.info = self.mp3.info
+        self.bitrate = int(str(self.info.bitrate)[:-3])
+        self.length = datetime.timedelta(0,self.info.length)
+        try:
+            self.metadata = self.get_file_metadata()
+        except:
+            self.metadata = {'title': '',
+                    'artist': '',
+                    'album': '',
+                    'date': '',
+                    'comment': '',
+                    'genre': '',
+                    'copyright': '',
+                    }
+
+        self.description = self.get_description()
+        self.mime_type = self.get_mime_type()
+        self.media_info = get_file_info(self.media)
+        self.file_name = self.media_info[0]
+        self.file_title = self.media_info[1]
+        self.file_ext = self.media_info[2]
+        self.extension = self.get_file_extension()
+        self.size = os.path.getsize(media)
+        #self.args = self.get_args()
+
+    def get_format(self):
+        return 'MP3'
+
+    def get_file_extension(self):
+        return 'mp3'
+
+    def get_mime_type(self):
+        return 'audio/mpeg'
+
+    def get_description(self):
+        return "MPEG audio Layer III"
+
+    def get_file_metadata(self):
+        metadata = {}
+        for key in self.keys2id3.keys():
+            try:
+                metadata[key] = self.mp3[key][0]
+            except:
+                metadata[key] = ''
+        return metadata
+
+    def write_tags(self):
+        """Write all ID3v2.4 tags by mapping dub2id3_dict dictionnary with the
+            respect of mutagen classes and methods"""
+
+        self.mp3.add_tags()
+        self.mp3.tags['TIT2'] = id3.TIT2(encoding=2, text=u'text')
+        self.mp3.save()
+
+        #media_id3 = id3.ID3(self.media)
+        #for tag in self.metadata.keys():
+            #if tag in self.dub2id3_dict.keys():
+                #frame_text = self.dub2id3_dict[tag]
+                #value = self.metadata[tag]
+                #frame = mutagen.id3.Frames[frame_text](3,value)
+            #try:
+                #media_id3.add(frame)
+            #except:
+                #raise IOError('ExporterError: cannot tag "'+tag+'"')
+
+        #try:
+            #media_id3.save()
+        #except:
+            #raise IOError('ExporterError: cannot write tags')
+
+        media = id3.ID3(self.media)
+        media.add(id3.TIT2(encoding=3, text=self.metadata['title'].decode('utf8')))
+        media.add(id3.TP1(encoding=3, text=self.metadata['artist'].decode('utf8')))
+        media.add(id3.TAL(encoding=3, text=self.metadata['album'].decode('utf8')))
+        media.add(id3.TDRC(encoding=3, text=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
+        media.add(id3.TCO(encoding=3, text=self.metadata['genre'].decode('utf8')))
+        try:
+            media.save()
+        except:
+            raise IOError('ExporterError: cannot write tags')
+
+
diff --git a/deefuzzer/tools/ogg.py b/deefuzzer/tools/ogg.py
new file mode 100644 (file)
index 0000000..d7305b0
--- /dev/null
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright Guillaume Pellerin (2006-2009)
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL  license under French law and
+# abiding by the rules of distribution of free software.  You can  use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty  and the software's author,  the holder of the
+# economic rights,  and the successive licensors  have only  limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading,  using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean  that it is complicated to manipulate,  and  that  also
+# therefore means  that it is reserved for developers  and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and,  more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import string
+import datetime
+from mutagen.oggvorbis import OggVorbis
+from tools import *
+
+
+class Ogg:
+    """An OGG file object"""
+
+    def __init__(self, media):
+        self.media = media
+        self.ogg = OggVorbis(self.media)
+        self.item_id = ''
+        self.source = self.media
+        self.options = {}
+        self.bitrate_default = '192'
+        self.cache_dir = os.sep + 'tmp'
+        self.keys2ogg = {'title': 'title',
+                    'artist': 'artist',
+                    'album': 'album',
+                    'date': 'date',
+                    'comment': 'comment',
+                    'genre': 'genre',
+                    'copyright': 'copyright',
+                    }
+        self.info = self.ogg.info
+        self.bitrate = int(str(self.info.bitrate)[:-3])
+        self.length = datetime.timedelta(0,self.info.length)
+        self.metadata = self.get_file_metadata()
+        self.description = self.get_description()
+        self.mime_type = self.get_mime_type()
+        self.media_info = get_file_info(self.media)
+        self.file_name = self.media_info[0]
+        self.file_title = self.media_info[1]
+        self.file_ext = self.media_info[2]
+        self.extension = self.get_file_extension()
+        self.size = os.path.getsize(media)
+        #self.args = self.get_args()
+
+    def get_format(self):
+        return 'OGG'
+
+    def get_file_extension(self):
+        return 'ogg'
+
+    def get_mime_type(self):
+        return 'application/ogg'
+
+    def get_description(self):
+        return 'FIXME'
+
+    def get_file_info(self):
+        try:
+            file_out1, file_out2 = os.popen4('ogginfo "'+self.dest+'"')
+            info = []
+            for line in file_out2.readlines():
+                info.append(clean_word(line[:-1]))
+            self.info = info
+            return self.info
+        except:
+            raise IOError('ExporterError: file does not exist.')
+
+    def set_cache_dir(self,path):
+       self.cache_dir = path
+
+    def get_file_metadata(self):
+        metadata = {}
+        for key in self.keys2ogg.keys():
+            try:
+                metadata[key] = self.ogg[key][0]
+            except:
+                metadata[key] = ''
+        return metadata
+
+    def decode(self):
+        try:
+            os.system('oggdec -o "'+self.cache_dir+os.sep+self.item_id+
+                      '.wav" "'+self.source+'"')
+            return self.cache_dir+os.sep+self.item_id+'.wav'
+        except:
+            raise IOError('ExporterError: decoder is not compatible.')
+
+    def write_tags(self):
+        #self.ogg.add_tags()
+        for tag in self.metadata.keys():
+            self.ogg[tag] = str(self.metadata[tag])
+        self.ogg.save()
+
+    def get_args(self,options=None):
+        """Get process options and return arguments for the encoder"""
+        args = []
+        if not options is None:
+            self.options = options
+            if not ('verbose' in self.options and self.options['verbose'] != '0'):
+                args.append('-Q ')
+            if 'ogg_bitrate' in self.options:
+                args.append('-b '+self.options['ogg_bitrate'])
+            elif 'ogg_quality' in self.options:
+                args.append('-q '+self.options['ogg_quality'])
+            else:
+                args.append('-b '+self.bitrate_default)
+        else:
+            args.append('-Q -b '+self.bitrate_default)
+
+        for tag in self.metadata.keys():
+            value = clean_word(self.metadata[tag])
+            args.append('-c %s="%s"' % (tag, value))
+            if tag in self.dub2args_dict.keys():
+                arg = self.dub2args_dict[tag]
+                args.append('-c %s="%s"' % (arg, value))
+
+        return args
diff --git a/deefuzzer/tools/osc.py b/deefuzzer/tools/osc.py
new file mode 100644 (file)
index 0000000..5e471a9
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Guillaume Pellerin
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+from threading import Thread
+
+
+class OSCController(Thread):
+
+    def __init__(self, port):
+        Thread.__init__(self)
+        import liblo
+        self.port = port
+        try:
+            self.server = liblo.Server(self.port)
+        except liblo.ServerError, err:
+            print str(err)
+
+    def add_method(self, path, type, method):
+        self.server.add_method(path, type, method)
+
+    def run(self):
+        while True:
+            self.server.recv(100)
diff --git a/deefuzzer/tools/osc_jingles_start.py b/deefuzzer/tools/osc_jingles_start.py
new file mode 100644 (file)
index 0000000..3f2ebf0
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/jingles", 1)
diff --git a/deefuzzer/tools/osc_jingles_stop.py b/deefuzzer/tools/osc_jingles_stop.py
new file mode 100644 (file)
index 0000000..d29f721
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/jingles", 0)
diff --git a/deefuzzer/tools/osc_player_fast.py b/deefuzzer/tools/osc_player_fast.py
new file mode 100644 (file)
index 0000000..92a60fe
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/player", 1)
diff --git a/deefuzzer/tools/osc_player_next.py b/deefuzzer/tools/osc_player_next.py
new file mode 100644 (file)
index 0000000..21a91ee
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    sys.exit(err)
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/media/next", 1)
diff --git a/deefuzzer/tools/osc_player_slow.py b/deefuzzer/tools/osc_player_slow.py
new file mode 100644 (file)
index 0000000..02948e0
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/player", 0)
diff --git a/deefuzzer/tools/osc_record_start.py b/deefuzzer/tools/osc_record_start.py
new file mode 100644 (file)
index 0000000..779e90b
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/record", 1)
diff --git a/deefuzzer/tools/osc_record_stop.py b/deefuzzer/tools/osc_record_stop.py
new file mode 100644 (file)
index 0000000..8056c69
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/record", 0)
diff --git a/deefuzzer/tools/osc_relay_start.py b/deefuzzer/tools/osc_relay_start.py
new file mode 100644 (file)
index 0000000..14bcb69
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/media/relay", 1)
diff --git a/deefuzzer/tools/osc_relay_stop.py b/deefuzzer/tools/osc_relay_stop.py
new file mode 100644 (file)
index 0000000..eaefe1a
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/media/relay", 0)
diff --git a/deefuzzer/tools/osc_twitter_start.py b/deefuzzer/tools/osc_twitter_start.py
new file mode 100644 (file)
index 0000000..c298be6
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/twitter", 1)
diff --git a/deefuzzer/tools/osc_twitter_stop.py b/deefuzzer/tools/osc_twitter_stop.py
new file mode 100644 (file)
index 0000000..3470fcc
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import liblo, sys
+
+# send all messages to port 1234 on the local machine
+try:
+    target = liblo.Address(1234)
+except liblo.AddressError, err:
+    print str(err)
+    sys.exit()
+
+# send message "/foo/message1" with int, float and string arguments
+liblo.send(target, "/twitter", 0)
diff --git a/deefuzzer/tools/player.py b/deefuzzer/tools/player.py
new file mode 100644 (file)
index 0000000..85985c7
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Guillaume Pellerin
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+from relay import *
+
+class Player:
+    """A file streaming iterator"""
+
+    def __init__(self):
+        self.main_buffer_size = 0x100000
+        self.relay_buffer_size = 0x40000
+        self.sub_buffer_size = 0x10000
+
+    def set_media(self, media):
+        self.media = media
+
+    def start_relay(self, url):
+        self.url = url
+        self.relay = Relay(self.sub_buffer_size, self.relay_buffer_size)
+        self.relay.set_url(self.url)
+        self.relay.open()
+        self.relay.start()
+        self.queue = self.relay.queue
+
+    def stop_relay(self):
+        self.relay.close()
+
+    def file_read_fast(self):
+        """Read media and stream data through a generator."""
+        m = open(self.media, 'r')
+        while True:
+            __main_chunk = m.read(self.sub_buffer_size)
+            if not __main_chunk:
+                break
+            yield __main_chunk
+        m.close()
+
+    def file_read_slow(self):
+        """Read a bigger part of the media and stream the little parts
+         of the data through a generator"""
+        m = open(self.media, 'r')
+        while True:
+            self.main_chunk = m.read(self.main_buffer_size)
+            if not self.main_chunk:
+                break
+            i = 0
+            while True:
+                start = i * self.sub_buffer_size
+                end = self.sub_buffer_size + (i * self.sub_buffer_size)
+                self.sub_chunk = self.main_chunk[start:end]
+                if not self.sub_chunk:
+                    break
+                yield self.sub_chunk
+                i += 1
+        self.main_chunk = 0
+        self.sub_chunk = 0
+        m.close()
+
+    def relay_read(self):
+        """Read a distant media through its URL"""
+        while True:
+            self.sub_chunk = self.queue.get(self.sub_buffer_size)
+            if not self.sub_chunk:
+                break
+            yield self.sub_chunk
+            self.queue.task_done()
+        self.sub_chunk = 0
diff --git a/deefuzzer/tools/recorder.py b/deefuzzer/tools/recorder.py
new file mode 100644 (file)
index 0000000..665632d
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Guillaume Pellerin
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+
+class Recorder:
+    """A file streaming iterator"""
+
+    def __init__(self, path):
+        self.path = path
+
+    def open(self, filename):
+        self.filename = filename
+        self.media = open(self.path + os.sep + self.filename, 'w')
+
+    def write(self, chunk):
+        try:
+            self.media.write(chunk)
+            self.media.flush()
+        except:
+            pass
+
+    def close(self):
+        self.media.close()
diff --git a/deefuzzer/tools/relay.py b/deefuzzer/tools/relay.py
new file mode 100644 (file)
index 0000000..831989e
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Guillaume Pellerin
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+from threading import Thread
+import Queue
+import urllib
+
+class Relay(Thread):
+
+    def __init__(self, sub_buffer_size, main_buffer_size):
+        Thread.__init__(self)
+        self.sub_buffer_size = sub_buffer_size
+        self.main_buffer_size = main_buffer_size
+        self.queue = Queue.Queue(self.main_buffer_size)
+
+    def set_url(self, url):
+        self.url = url
+
+    def open(self):
+        try:
+            self.stream = urllib.urlopen(self.url)
+            self.isopen = True
+        except:
+            self.isopen = False
+
+    def close(self):
+        if self.stream:
+            self.isopen = False
+
+    def run(self):
+        while True:
+            if self.isopen:
+                self.chunk = self.stream.read(self.sub_buffer_size)
+                self.queue.put_nowait(self.chunk)
+            else:
+                self.stream.close()
+                break
+
+
+
diff --git a/deefuzzer/tools/station.py b/deefuzzer/tools/station.py
new file mode 100644 (file)
index 0000000..49f7ed4
--- /dev/null
@@ -0,0 +1,537 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Guillaume Pellerin
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import sys
+import time
+import datetime
+import string
+import random
+import shout
+from threading import Thread
+from __init__ import *
+
+
+class Station(Thread):
+    """a DeeFuzzer shouting station thread"""
+
+    def __init__(self, station, q, logger, m3u):
+        Thread.__init__(self)
+        self.station = station
+        self.q = q
+        self.logger = logger
+        self.channel = shout.Shout()
+        self.id = 999999
+        self.counter = 0
+        self.command = 'cat '
+        self.delay = 0
+
+       # Media
+        self.media_dir = self.station['media']['dir']
+        self.channel.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']
+
+        # RSS
+        self.rss_dir = self.station['rss']['dir']
+        self.rss_enclosure = self.station['rss']['enclosure']
+
+        # Infos
+        self.channel.url = self.station['infos']['url']
+        self.short_name = self.station['infos']['short_name']
+        self.channel.name = self.station['infos']['name'] + ' : ' + self.channel.url
+        self.channel.genre = self.station['infos']['genre']
+        self.channel.description = self.station['infos']['description']
+        self.base_name = self.rss_dir + os.sep + self.short_name + '_' + self.channel.format
+        self.rss_current_file = self.base_name + '_current.xml'
+        self.rss_playlist_file = self.base_name + '_playlist.xml'
+        self.m3u = m3u
+
+        # Server
+        self.channel.protocol = 'http'     # | 'xaudiocast' | 'icy'
+        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.mount = '/' + self.short_name + '.' + self.channel.format
+        self.channel.public = int(self.station['server']['public'])
+        self.channel.audio_info = { 'bitrate': self.bitrate,
+                                    'samplerate': self.samplerate,
+                                    'quality': self.ogg_quality,
+                                    'channels': self.voices,}
+        self.playlist = self.get_playlist()
+        self.lp = len(self.playlist)
+
+        # Logging
+        self.logger.write_info('Opening ' + self.short_name + ' - ' + self.channel.name + \
+                ' (' + str(self.lp) + ' tracks)...')
+
+        self.metadata_relative_dir = 'metadata'
+        self.metadata_url = self.channel.url + '/rss/' + self.metadata_relative_dir
+        self.metadata_dir = self.rss_dir + os.sep + self.metadata_relative_dir
+        if not os.path.exists(self.metadata_dir):
+            os.makedirs(self.metadata_dir)
+
+        # The station's player
+        self.player = Player()
+        self.player_mode = 0
+
+        # Jingling between each media.
+        # mode = 0 means Off, mode = 1 means On
+        self.jingles_mode = 0
+        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])
+
+        # Relaying
+        # mode = 0 means Off, mode = 1 means On
+        self.relay_mode = 0
+        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('/relay', [1])
+
+        # Twitting
+        # mode = 0 means Off, mode = 1 means On
+        self.twitter_mode = 0
+        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(' ')
+            if self.twitter_mode == 1:
+                self.twitter_callback('/twitter', [1])
+
+        # Recording
+        # mode = 0 means Off, mode = 1 means On
+        self.record_mode = 0
+        if 'record' in self.station:
+            self.record_mode = int(self.station['record']['mode'])
+            self.record_dir = self.station['record']['dir']
+            if self.record_mode == 1:
+                self.record_callback('/record', [1])
+
+        # Running
+        # mode = 0 means Off, mode = 1 means On
+        self.run_mode = 1
+
+        # OSCing
+        self.osc_control_mode = 0
+        # mode = 0 means Off, mode = 1 means On
+        if 'control' in self.station:
+            self.osc_control_mode = int(self.station['control']['mode'])
+            self.osc_port = self.station['control']['port']
+            if self.osc_control_mode == 1:
+                self.osc_controller = OSCController(self.osc_port)
+                self.osc_controller.start()
+                # 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)
+
+    def run_callback(self, path, value):
+        value = value[0]
+        self.run_mode = value
+        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
+        self.logger.write_info(message)
+
+    def media_next_callback(self, path, value):
+        value = value[0]
+        self.next_media = value
+        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
+        self.logger.write_info(message)
+
+    def relay_callback(self, path, value):
+        value = value[0]
+        if value == 1:
+            self.relay_mode = 1
+            self.player.start_relay(self.relay_url)
+        elif value == 0:
+            self.relay_mode = 0
+            self.player.stop_relay()
+        self.id = 0
+        self.next_media = 1
+        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
+        self.logger.write_info(message)
+        message = "Relaying : %s" % self.relay_url
+        self.logger.write_info(message)
+
+    def twitter_callback(self, path, value):
+        value = value[0]
+        import tinyurl
+        self.twitter = Twitter(self.twitter_key, self.twitter_secret)
+        self.twitter_mode = value
+        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
+        self.m3u_tinyurl = tinyurl.create_one(self.channel.url + '/m3u/' + self.m3u.split(os.sep)[-1])
+        self.rss_tinyurl = tinyurl.create_one(self.channel.url + '/rss/' + self.rss_playlist_file.split(os.sep)[-1])
+        self.logger.write_info(message)
+
+    def jingles_callback(self, path, value):
+        value = value[0]
+        if value == 1:
+            self.jingles_list = self.get_jingles()
+            self.jingles_length = len(self.jingles_list)
+            self.jingle_id = 0
+        self.jingles_mode = value
+        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
+        self.logger.write_info(message)
+
+    def record_callback(self, path, value):
+        value = value[0]
+        if value == 1:
+            if not os.path.exists(self.record_dir):
+                os.makedirs(self.record_dir)
+            self.rec_file = self.short_name + '-' + \
+              datetime.datetime.now().strftime("%x-%X").replace('/', '_') + '.' + self.channel.format
+            self.recorder = Recorder(self.record_dir)
+            self.recorder.open(self.rec_file)
+        elif value == 0:
+            self.recorder.close()
+            date = datetime.datetime.now().strftime("%Y")
+            if self.channel.format == 'mp3':
+                media = Mp3(self.record_dir + os.sep + self.rec_file)
+            if self.channel.format == 'ogg':
+                media = Ogg(self.record_dir + os.sep + self.rec_file)
+            media.metadata = {'artist': self.artist.encode('utf-8'),
+                                'title': self.title.encode('utf-8'),
+                                'album': self.short_name.encode('utf-8'),
+                                'genre': self.channel.genre.encode('utf-8'),
+                                'date' : date.encode('utf-8'),}
+            media.write_tags()
+        self.record_mode = value
+        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
+        self.logger.write_info(message)
+
+    def player_callback(self, path, value):
+        value = value[0]
+        self.player_mode = value
+        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
+        self.logger.write_info(message)
+
+    def get_playlist(self):
+        file_list = []
+        for root, dirs, files in os.walk(self.media_dir):
+            for file in files:
+                s = file.split('.')
+                ext = s[len(s)-1]
+                if ext.lower() == self.channel.format and not os.sep+'.' in file:
+                    file_list.append(root + os.sep + file)
+        file_list.sort()
+        return file_list
+
+    def get_jingles(self):
+        file_list = []
+        for root, dirs, files in os.walk(self.jingles_dir):
+            for file in files:
+                s = file.split('.')
+                ext = s[len(s)-1]
+                if ext.lower() == self.channel.format and not os.sep+'.' in file:
+                    file_list.append(root + os.sep + file)
+        file_list.sort()
+        return file_list
+
+    def get_next_media(self):
+        # Init playlist
+        if self.lp != 0:
+            playlist = self.playlist
+            new_playlist = self.get_playlist()
+            lp_new = len(new_playlist)
+
+            if lp_new != self.lp or self.counter == 0:
+                self.id = 0
+                self.lp = lp_new
+
+                # Twitting new tracks
+                new_playlist_set = set(new_playlist)
+                playlist_set = set(playlist)
+                new_tracks = new_playlist_set - playlist_set
+                self.new_tracks = list(new_tracks.copy())
+                
+                if len(new_tracks) != 0:
+                    new_tracks_objs = self.media_to_objs(self.new_tracks)
+                    for media_obj in new_tracks_objs:
+                        title = media_obj.metadata['title']
+                        artist = media_obj.metadata['artist']
+                        if not (title or artist):
+                            song = str(media_obj.file_name)
+                        else:
+                            song = artist + ' : ' + title
+                        song = song.encode('utf-8')
+                        artist = artist.encode('utf-8')
+                        if self.twitter_mode == 1:
+                            artist_names = artist.split(' ')
+                            artist_tags = ' #'.join(list(set(artist_names)-set(['&', '-'])))
+                            message = '#newtrack ! %s #%s on #%s RSS: ' % (song.replace('_', ' '), artist_tags, self.short_name)
+                            message = message[:113] + self.rss_tinyurl
+                            self.update_twitter(message)
+                
+                # Shake it, Fuzz it !
+                if self.shuffle_mode == 1:
+                    random.shuffle(playlist)
+                    
+                # Play new tracks first
+                for track in self.new_tracks:
+                    playlist.insert(0, track)
+                self.playlist = playlist
+                
+                self.logger.write_info('Station ' + self.short_name + \
+                                 ' : generating new playlist (' + str(self.lp) + ' tracks)')
+                self.update_rss(self.media_to_objs(self.playlist), self.rss_playlist_file, '(playlist)')
+
+            if self.jingles_mode == 1 and (self.counter % 2) == 0 and not self.jingles_length == 0:
+                media = self.jingles_list[self.jingle_id]
+                self.jingle_id = (self.jingle_id + 1) % self.jingles_length
+            else:
+                media = self.playlist[self.id]
+                self.id = (self.id + 1) % self.lp
+            return media
+        else:
+            mess = 'No media in media_dir !'
+            self.logger.write_error(mess)
+            sys.exit(mess)
+
+    def media_to_objs(self, media_list):
+        media_objs = []
+        for media in media_list:
+            file_name, file_title, file_ext = get_file_info(media)
+            if file_ext.lower() == 'mp3':
+                media_objs.append(Mp3(media))
+            elif file_ext.lower() == 'ogg':
+                media_objs.append(Ogg(media))
+        return media_objs
+
+    def update_rss(self, media_list, rss_file, sub_title):
+        rss_item_list = []
+        if not os.path.exists(self.rss_dir):
+            os.makedirs(self.rss_dir)
+        channel_subtitle = self.channel.name + ' ' + sub_title
+        _date_now = datetime.datetime.now()
+        date_now = str(_date_now)
+        media_absolute_playtime = _date_now
+
+        for media in media_list:
+            media_stats = os.stat(media.media)
+            media_date = time.localtime(media_stats[8])
+            media_date = time.strftime("%a, %d %b %Y %H:%M:%S +0200", media_date)
+            media.metadata['Duration'] = str(media.length).split('.')[0]
+            media.metadata['Bitrate'] = str(media.bitrate) + ' kbps'
+            media.metadata['Next play'] = str(media_absolute_playtime).split('.')[0]
+
+            media_description = '<table>'
+            media_description_item = '<tr><td>%s:   </td><td><b>%s</b></td></tr>'
+            for key in media.metadata.keys():
+                if media.metadata[key] != '':
+                    media_description += media_description_item % (key.capitalize(), media.metadata[key])
+            media_description += '</table>'
+
+            title = media.metadata['title']
+            artist = media.metadata['artist']
+            if not (title or artist):
+                song = str(media.file_title)
+            else:
+                song = artist + ' : ' + title
+
+            media_absolute_playtime += media.length
+
+            if self.rss_enclosure == '1':
+                media_link = self.channel.url + '/media/' + media.file_name
+                media_link = media_link.decode('utf-8')
+                rss_item_list.append(RSSItem(
+                    title = song,
+                    link = media_link,
+                    description = media_description,
+                    enclosure = Enclosure(media_link, str(media.size), 'audio/mpeg'),
+                    guid = Guid(media_link),
+                    pubDate = media_date,)
+                    )
+            else:
+                media_link = self.metadata_url + '/' + media.file_name + '.xml'
+                media_link = media_link.decode('utf-8')
+                rss_item_list.append(RSSItem(
+                    title = song,
+                    link = media_link,
+                    description = media_description,
+                    guid = Guid(media_link),
+                    pubDate = media_date,)
+                    )
+
+        rss = RSS2(title = channel_subtitle,
+                            link = self.channel.url,
+                            description = self.channel.description.decode('utf-8'),
+                            lastBuildDate = date_now,
+                            items = rss_item_list,)
+        f = open(rss_file, 'w')
+        rss.write_xml(f, 'utf-8')
+        f.close()
+
+    def update_twitter(self, message):
+        try:
+            self.twitter.post(message.decode('utf8'))
+            self.logger.write_info('Twitting : "' + message + '"')
+        except:
+            self.logger.write_error('Twitting : "' + message + '"')
+            pass
+
+    def set_relay_mode(self):
+        self.prefix = '#nowplaying (relaying #LIVE)'
+        self.title = self.channel.description.encode('utf-8')
+        self.artist = self.relay_author.encode('utf-8')
+        self.title = self.title.replace('_', ' ')
+        self.artist = self.artist.replace('_', ' ')
+        self.song = self.artist + ' : ' + self.title
+        self.stream = self.player.relay_read()
+
+    def set_read_mode(self):
+        self.prefix = '#nowplaying'
+        self.current_media_obj = self.media_to_objs([self.media])
+        self.title = self.current_media_obj[0].metadata['title']
+        self.artist = self.current_media_obj[0].metadata['artist']
+        self.title = self.title.replace('_', ' ')
+        self.artist = self.artist.replace('_', ' ')
+        if not (self.title or self.artist):
+            song = str(self.current_media_obj[0].file_name)
+        else:
+            song = self.artist + ' : ' + self.title
+        self.song = song.encode('utf-8')
+        self.artist = self.artist.encode('utf-8')
+        self.metadata_file = self.metadata_dir + os.sep + self.current_media_obj[0].file_name + '.xml'
+        self.update_rss(self.current_media_obj, self.metadata_file, '')
+        self.update_rss(self.current_media_obj, self.rss_current_file, '(currently playing)')
+        self.logger.write_info('DeeFuzzing on %s :  id = %s, name = %s' \
+            % (self.short_name, self.id, self.current_media_obj[0].file_name))
+        self.player.set_media(self.media)
+        if self.player_mode == 0:
+            self.stream = self.player.file_read_slow()
+        elif self.player_mode == 1:
+            self.stream = self.player.file_read_fast()
+
+    def update_twitter_current(self):
+        artist_names = self.artist.split(' ')
+        artist_tags = ' #'.join(list(set(artist_names)-set(['&', '-'])))
+        message = '%s %s on #%s' % (self.prefix, self.song, self.short_name)
+        tags = '#' + ' #'.join(self.twitter_tags)
+        message = message + ' ' + tags
+        message = message[:108] + ' M3U: ' + self.m3u_tinyurl
+        self.update_twitter(message)
+
+    def channel_open(self):
+        self.channel.open()
+        self.channel_delay = self.channel.delay()
+    
+    def run(self):
+        try:
+            self.channel_open()
+        except:
+            pass
+            
+        while self.run_mode:
+            self.q.get(1)
+            self.next_media = 0
+            self.media = self.get_next_media()
+            self.counter += 1
+            if self.relay_mode:
+                self.set_relay_mode()
+            elif os.path.exists(self.media) and not os.sep+'.' in self.media:
+                if self.lp == 0:
+                    self.logger.write_error('Station ' + self.short_name + ' has no media to stream !')
+                    break
+                self.set_read_mode()
+            self.q.task_done()
+
+            self.q.get(1)
+            if (not (self.jingles_mode and (self.counter % 2)) or self.relay_mode) and self.twitter_mode:
+                try:
+                    self.update_twitter_current()
+                except:
+                    continue
+            try:
+                self.channel.set_metadata({'song': self.song, 'charset': 'utf-8',})
+            except:
+                continue
+            self.q.task_done()
+
+            for self.chunk in self.stream:
+                self.q.get(1)
+                if self.next_media or not self.run_mode:
+                    break
+                try:
+                    if self.record_mode:
+                        self.recorder.write(self.chunk)
+                except:
+                    self.logger.write_error('Station ' + self.short_name + ' : could not write the buffer to the file')
+                    continue
+                try:
+                    self.channel.send(self.chunk)
+                    self.channel.sync()
+                except:
+                    self.logger.write_error('Station ' + self.short_name + ' : could not send the buffer')
+                    try:
+                        self.channel.close()
+                    except:
+                        self.logger.write_error('Station ' + self.short_name + ' : could not close the channel')
+                        try:
+                            self.channel.open()
+                            self.channel.set_metadata({'song': self.song, 'charset': 'utf8',})
+                            self.channel.send(self.chunk)
+                            self.channel.sync()
+                        except:
+                            self.logger.write_error('Station ' + self.short_name + ' : could not restart the channel')
+                            continue
+                        continue
+                    continue
+                self.q.task_done()
+        
+        if not self.run_mode:
+            self.q.task_done()
+
+        if self.record_mode:
+            self.recorder.close()
+
+        self.channel.close()
diff --git a/deefuzzer/tools/tools.py b/deefuzzer/tools/tools.py
new file mode 100644 (file)
index 0000000..8282cfc
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+# 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/deefuzz/wiki/DefuzzLicense.
+#
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import re
+import string
+
+def clean_word(word) :
+    """ Return the word without excessive blank spaces, underscores and
+    characters causing problem to exporters"""
+    word = re.sub("^[^\w]+","",word)    #trim the beginning
+    word = re.sub("[^\w]+$","",word)    #trim the end
+    word = re.sub("_+","_",word)        #squeeze continuous _ to one _
+    word = re.sub("^[^\w]+","",word)    #trim the beginning _
+    #word = string.replace(word,' ','_')
+    #word = string.capitalize(word)
+    dict = '&[];"*:,'
+    for letter in dict:
+        word = string.replace(word,letter,'_')
+    return word
+
+def get_file_info(media):
+    file_name = media.split(os.sep)[-1]
+    file_title = file_name.split('.')[:-1]
+    file_title = '.'.join(file_title)
+    file_ext = file_name.split('.')[-1]
+    return file_name, file_title, file_ext
diff --git a/deefuzzer/tools/twitt.py b/deefuzzer/tools/twitt.py
new file mode 100644 (file)
index 0000000..da26092
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Guillaume Pellerin
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+# Twitter DeeFuzzer keys
+DEEFUZZER_CONSUMER_KEY = 'ozs9cPS2ci6eYQzzMSTb4g'
+DEEFUZZER_CONSUMER_SECRET = '1kNEffHgGSXO2gMNTr8HRum5s2ofx3VQnJyfd0es'
+
+
+class Twitter:
+
+    def __init__(self, key, secret):
+        import twitter
+        self.consumer_key = DEEFUZZER_CONSUMER_KEY
+        self.consumer_secret = DEEFUZZER_CONSUMER_SECRET
+        self.access_token_key = key
+        self.access_token_secret = secret
+        self.api = twitter.Api(consumer_key=self.consumer_key,
+                               consumer_secret=self.consumer_secret,
+                               access_token_key=self.access_token_key,
+                               access_token_secret=self.access_token_secret)
+
+    def post(self, message):
+        try:
+            self.api.PostUpdate(message)
+        except:
+            pass
+
diff --git a/deefuzzer/tools/xmltodict.py b/deefuzzer/tools/xmltodict.py
new file mode 100644 (file)
index 0000000..b85d556
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+# Easily import simple XML data to Python dictionary
+# http://www.gmta.info/publications/parsing-simple-xml-structure-to-a-python-dictionary
+
+import xml.dom.minidom
+
+def haschilds(dom):
+    # Checks whether an element has any childs
+    # containing real tags opposed to just text.
+    for childnode in dom.childNodes:
+        if childnode.nodeName != "#text" and \
+            childnode.nodeName != "#cdata-section":
+            return True
+    return False
+
+def indexchilds(dom, enc):
+    childsdict = dict()
+    for childnode in dom.childNodes:
+        name = childnode.nodeName.encode(enc)
+        if name == "#text" or name == "#cdata-section":
+            # ignore whitespaces
+            continue
+        if haschilds(childnode):
+            v = indexchilds(childnode, enc)
+        else:
+            v = childnode.childNodes[0].nodeValue.encode(enc)
+        if name in childsdict:
+            if isinstance(childsdict[name], dict):
+                # there is multiple instances of this node - convert to list
+                childsdict[name] = [childsdict[name]]
+            childsdict[name].append(v)
+        else:
+            childsdict[name] = v
+    return childsdict
+
+def xmltodict(data, enc=None):
+    dom = xml.dom.minidom.parseString(data.strip())
+    return indexchilds(dom, enc)
+
+
diff --git a/deefuzzer/tools/xmltodict2.py b/deefuzzer/tools/xmltodict2.py
new file mode 100644 (file)
index 0000000..b669239
--- /dev/null
@@ -0,0 +1,397 @@
+# -*- coding: utf-8 -*-
+""" xmltodict(): convert xml into tree of Python dicts.
+
+This was copied and modified from John Bair's recipe at aspn.activestate.com:
+       http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/149368
+"""
+import os
+import string
+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
+
+# Python seems to need to compile code with \n linesep:
+code_linesep = "\n"
+eol = os.linesep
+
+
+class Xml2Obj:
+       """XML to Object"""
+       def __init__(self):
+               self.root = None
+               self.nodeStack = []
+               self.attsToSkip = []
+               self._inCode = False
+               self._mthdName = ""
+               self._mthdCode = ""
+               self._codeDict = None
+               self._inProp = False
+               self._propName = ""
+               self._propData = ""
+               self._propDict = None
+               self._currPropAtt = ""
+               self._currPropDict = None
+
+
+       def StartElement(self, name, attributes):
+               """SAX start element even handler"""
+               if name == "code":
+                       # This is code for the parent element
+                       self._inCode = True
+                       parent = self.nodeStack[-1]
+                       if not parent.has_key("code"):
+                               parent["code"] = {}
+                               self._codeDict = parent["code"]
+
+               elif name == "properties":
+                       # These are the custom property definitions
+                       self._inProp = True
+                       self._propName = ""
+                       self._propData = ""
+                       parent = self.nodeStack[-1]
+                       if not parent.has_key("properties"):
+                               parent["properties"] = {}
+                               self._propDict = parent["properties"]
+
+               else:
+                       if self._inCode:
+                               self._mthdName = name.encode()
+                       elif self._inProp:
+                               if self._propName:
+                                       # In the middle of a prop definition
+                                       self._currPropAtt = name.encode()
+                               else:
+                                       self._propName = name.encode()
+                                       self._currPropDict = {}
+                                       self._currPropAtt = ""
+                       else:
+                               element = {"name": name.encode()}
+                               if len(attributes) > 0:
+                                       for att in self.attsToSkip:
+                                               if attributes.has_key(att):
+                                                       del attributes[att]
+                                       element["attributes"] = attributes
+
+                               # Push element onto the stack and make it a child of parent
+                               if len(self.nodeStack) > 0:
+                                       parent = self.nodeStack[-1]
+                                       if not parent.has_key("children"):
+                                               parent["children"] = []
+                                       parent["children"].append(element)
+                               else:
+                                       self.root = element
+                               self.nodeStack.append(element)
+
+
+       def EndElement(self, name):
+               """SAX end element event handler"""
+               if self._inCode:
+                       if name == "code":
+                               self._inCode = False
+                               self._codeDict = None
+                       else:
+                               # End of an individual method
+                               mth = self._mthdCode.strip()
+                               if not mth.endswith("\n"):
+                                       mth += "\n"
+                               self._codeDict[self._mthdName] = mth
+                               self._mthdName = ""
+                               self._mthdCode = ""
+               elif self._inProp:
+                       if name == "properties":
+                               self._inProp = False
+                               self._propDict = None
+                       elif name == self._propName:
+                               # End of an individual prop definition
+                               self._propDict[self._propName] = self._currPropDict
+                               self._propName = ""
+                       else:
+                               # end of a property attribute
+                               self._currPropDict[self._currPropAtt] = self._propData
+                               self._propData = self._currPropAtt = ""
+               else:
+                       self.nodeStack = self.nodeStack[:-1]
+
+
+       def CharacterData(self, data):
+               """SAX character data event handler"""
+               if self._inCode or data.strip():
+                       data = data.replace("&lt;", "<")
+                       data = data.encode()
+                       if self._inCode:
+                               if self._mthdCode:
+                                       self._mthdCode += data
+                               else:
+                                       self._mthdCode = data
+                       elif self._inProp:
+                               self._propData += data
+                       else:
+                               element = self.nodeStack[-1]
+                               if not element.has_key("cdata"):
+                                       element["cdata"] = ""
+                               element["cdata"] += data
+
+
+       def Parse(self, xml):
+               # Create a SAX parser
+               Parser = expat.ParserCreate()
+               # SAX event handlers
+               Parser.StartElementHandler = self.StartElement
+               Parser.EndElementHandler = self.EndElement
+               Parser.CharacterDataHandler = self.CharacterData
+               # Parse the XML File
+               ParserStatus = Parser.Parse(xml, 1)
+               return self.root
+
+
+       def ParseFromFile(self, filename):
+               return self.Parse(open(filename,"r").read())
+
+
+def xmltodict(xml, attsToSkip=[], addCodeFile=False):
+       """Given an xml string or file, return a Python dictionary."""
+       parser = Xml2Obj()
+       parser.attsToSkip = attsToSkip
+       isPath = os.path.exists(xml)
+       errmsg = ""
+       if eol not in xml and isPath:
+               # argument was a file
+               try:
+                       ret = parser.ParseFromFile(xml)
+               except expat.ExpatError, e:
+                       errmsg = _("The XML in '%s' is not well-formed and cannot be parsed: %s") % (xml, e)
+       else:
+               # argument must have been raw xml:
+               if not xml.strip().startswith("<?xml "):
+                       # it's a bad file name
+                       errmsg = _("The file '%s' could not be found") % xml
+               else:
+                       try:
+                               ret = parser.Parse(xml)
+                       except expat.ExpatError:
+                               errmsg = _("An invalid XML string was encountered")
+       if errmsg:
+               raise dabo.dException.XmlException, errmsg
+       if addCodeFile and isPath:
+               # Get the associated code file, if any
+               codePth = "%s-code.py" % os.path.splitext(xml)[0]
+               if os.path.exists(codePth):
+                       try:
+                               codeDict = desUtil.parseCodeFile(open(codePth).read())
+                               desUtil.addCodeToClassDict(ret, codeDict)
+                       except StandardError, e:
+                               print "Failed to parse code file:", e
+       return ret
+
+
+def escQuote(val, noEscape=False, noQuote=False):
+       """Add surrounding quotes to the string, and escape
+       any illegal XML characters.
+       """
+       if not isinstance(val, basestring):
+               val = str(val)
+       if not isinstance(val, unicode):
+               val = unicode(val, default_encoding)
+       if noQuote:
+               qt = ''
+       else:
+               qt = '"'
+       slsh = "\\"
+#      val = val.replace(slsh, slsh+slsh)
+       if not noEscape:
+               # First escape internal ampersands. We need to double them up due to a
+               # quirk in wxPython and the way it displays this character.
+               val = val.replace("&", "&amp;&amp;")
+               # Escape any internal quotes
+               val = val.replace('"', '&quot;').replace("'", "&apos;")
+               # Escape any high-order characters
+               chars = []
+               for pos, char in enumerate(list(val)):
+                       if ord(char) > 127:
+                               chars.append("&#%s;" % ord(char))
+                       else:
+                                       chars.append(char)
+               val = "".join(chars)
+       val = val.replace("<", "&#060;").replace(">", "&#062;")
+       return "%s%s%s" % (qt, val, qt)
+
+
+def dicttoxml(dct, level=0, header=None, linesep=None):
+       """Given a Python dictionary, return an xml string.
+
+       The dictionary must be in the format returned by dicttoxml(), with keys
+       on "attributes", "code", "cdata", "name", and "children".
+
+       Send your own XML header, otherwise a default one will be used.
+
+       The linesep argument is a dictionary, with keys on levels, allowing the
+       developer to add extra whitespace depending on the level.
+       """
+       att = ""
+       ret = ""
+
+       if dct.has_key("attributes"):
+               for key, val in dct["attributes"].items():
+                       # Some keys are already handled.
+                       noEscape = key in ("sizerInfo",)
+                       val = escQuote(val, noEscape)
+                       att += " %s=%s" % (key, val)
+       ret += "%s<%s%s" % ("\t" * level, dct["name"], att)
+
+       if (not dct.has_key("cdata") and not dct.has_key("children")
+                       and not dct.has_key("code") and not dct.has_key("properties")):
+               ret += " />%s" % eol
+       else:
+               ret += ">"
+               if dct.has_key("cdata"):
+                       ret += "%s" % dct["cdata"].replace("<", "&lt;")
+
+               if dct.has_key("code"):
+                       if len(dct["code"].keys()):
+                               ret += "%s%s<code>%s" % (eol, "\t" * (level+1), eol)
+                               methodTab = "\t" * (level+2)
+                               for mthd, cd in dct["code"].items():
+                                       # Convert \n's in the code to eol:
+                                       cd = eol.join(cd.splitlines())
+
+                                       # Make sure that the code ends with a linefeed
+                                       if not cd.endswith(eol):
+                                               cd += eol
+
+                                       ret += "%s<%s><![CDATA[%s%s]]>%s%s</%s>%s" % (methodTab,
+                                                       mthd, eol, cd, eol,
+                                                       methodTab, mthd, eol)
+                               ret += "%s</code>%s"    % ("\t" * (level+1), eol)
+
+               if dct.has_key("properties"):
+                       if len(dct["properties"].keys()):
+                               ret += "%s%s<properties>%s" % (eol, "\t" * (level+1), eol)
+                               currTab = "\t" * (level+2)
+                               for prop, val in dct["properties"].items():
+                                       ret += "%s<%s>%s" % (currTab, prop, eol)
+                                       for propItm, itmVal in val.items():
+                                               itmTab = "\t" * (level+3)
+                                               ret += "%s<%s>%s</%s>%s" % (itmTab, propItm, itmVal,
+                                                               propItm, eol)
+                                       ret += "%s</%s>%s" % (currTab, prop, eol)
+                               ret += "%s</properties>%s"      % ("\t" * (level+1), eol)
+
+               if dct.has_key("children") and len(dct["children"]) > 0:
+                       ret += eol
+                       for child in dct["children"]:
+                               ret += dicttoxml(child, level+1, linesep=linesep)
+               indnt = ""
+               if ret.endswith(eol):
+                       # Indent the closing tag
+                       indnt = ("\t" * level)
+               ret += "%s</%s>%s" % (indnt, dct["name"], eol)
+
+               if linesep:
+                       ret += linesep.get(level, "")
+
+       if level == 0:
+               if header is None:
+                       header = '<?xml version="1.0" encoding="%s" standalone="no"?>%s' \
+                                       % (default_encoding, eol)
+               ret = header + ret
+
+       return ret
+
+
+def flattenClassDict(cd, retDict=None):
+       """Given a dict containing a series of nested objects such as would
+       be created by restoring from a cdxml file, returns a dict with all classIDs
+       as keys, and a dict as the corresponding value. The dict value will have
+       keys for the attributes and/or code, depending on what was in the original
+       dict. The end result is to take a nested dict structure and return a flattened
+       dict with all objects at the top level.
+       """
+       if retDict is None:
+               retDict = {}
+       atts = cd.get("attributes", {})
+       props = cd.get("properties", {})
+       kids = cd.get("children", [])
+       code = cd.get("code", {})
+       classID = atts.get("classID", "")
+       classFile = resolvePath(atts.get("designerClass", ""))
+       superclass = resolvePath(atts.get("superclass", ""))
+       superclassID = atts.get("superclassID", "")
+       if superclassID and os.path.exists(superclass):
+               # Get the superclass info
+               superCD = xmltodict(superclass, addCodeFile=True)
+               flattenClassDict(superCD, retDict)
+       if classID:
+               if os.path.exists(classFile):
+                       # Get the class info
+                       classCD = xmltodict(classFile, addCodeFile=True)
+                       classAtts = classCD.get("attributes", {})
+                       classProps = classCD.get("properties", {})
+                       classCode = classCD.get("code", {})
+                       classKids = classCD.get("children", [])
+                       currDict = retDict.get(classID, {})
+                       retDict[classID] = {"attributes": classAtts, "code": classCode,
+                                       "properties": classProps}
+                       retDict[classID].update(currDict)
+                       # Now update the child objects in the dict
+                       for kid in classKids:
+                               flattenClassDict(kid, retDict)
+               else:
+                       # Not a file; most likely just a component in another class
+                       currDict = retDict.get(classID, {})
+                       retDict[classID] = {"attributes": atts, "code": code,
+                                       "properties": props}
+                       retDict[classID].update(currDict)
+       if kids:
+               for kid in kids:
+                       flattenClassDict(kid, retDict)
+       return retDict
+
+
+def addInheritedInfo(src, super, updateCode=False):
+       """Called recursively on the class container structure, modifying
+       the attributes to incorporate superclass information. When the
+       'updateCode' parameter is True, superclass code is added to the
+       object's code
+       """
+       atts = src.get("attributes", {})
+       props = src.get("properties", {})
+       kids = src.get("children", [])
+       code = src.get("code", {})
+       classID = atts.get("classID", "")
+       if classID:
+               superInfo = super.get(classID, {"attributes": {}, "code": {}, "properties": {}})
+               src["attributes"] = superInfo["attributes"].copy()
+               src["attributes"].update(atts)
+               src["properties"] = superInfo.get("properties", {}).copy()
+               src["properties"].update(props)
+               if updateCode:
+                       src["code"] = superInfo["code"].copy()
+                       src["code"].update(code)
+       if kids:
+               for kid in kids:
+                       addInheritedInfo(kid, super, updateCode)
+
+
+
+#if __name__ == "__main__":
+       #test_dict = {"name": "test", "attributes":{"path": "c:\\temp\\name",
+                       #"problemChars": "Welcome to <Jos\xc3\xa9's \ Stuff!>\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
diff --git a/install.py b/install.py
deleted file mode 100644 (file)
index a49f2ba..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright Guillaume Pellerin (2006-2009)
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL  license under French law and
-# abiding by the rules of distribution of free software.  You can  use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty  and the software's author,  the holder of the
-# economic rights,  and the successive licensors  have only  limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading,  using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean  that it is complicated to manipulate,  and  that  also
-# therefore means  that it is reserved for developers  and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and,  more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-# ONLY FOR LINUX / UNIX
-
-import os, sys
-import platform
-
-if len(sys.argv) == 1:
-    install_dir = '/usr/share/deefuzzer/'
-elif len(sys.argv) > 2:
-    sys.exit('Give just one directory to install the DeeFuzzer, or none.')
-else:
-    install_dir = sys.argv[1]
-
-if not os.path.exists(install_dir):
-    os.mkdir(install_dir)
-
-os.system('cp -ra ./* '+install_dir+os.sep)
-
-# Install shout-python
-os.chdir('shout-python')
-os.system('python setup.py install')
-os.chdir('..')
-
-os.system('easy_install tinyurl')
-
-if os.path.exists('/usr/bin/deefuzzer'):
-    os.system('rm -r /usr/bin/deefuzzer')
-
-os.system('ln -s '+install_dir+os.sep+'deefuzzer.py '+'/usr/bin/deefuzzer')
-
-print """
-   DeeFuzzer installation successfull !
-   """
-
diff --git a/scripts/deefuzzer b/scripts/deefuzzer
new file mode 100755 (executable)
index 0000000..247732d
--- /dev/null
@@ -0,0 +1,177 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Guillaume Pellerin
+
+# <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to stream audio
+# and video data through icecast2 servers.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+# Author: Guillaume Pellerin <yomguy@parisson.com>
+
+import os
+import sys
+import shout
+import Queue
+import datetime
+import platform
+from threading import Thread
+import deefuzzer
+from deefuzzer import *
+
+version = '0.5.2'
+year = datetime.datetime.now().strftime("%Y")
+platform_system = platform.system()
+
+
+def prog_info():
+    desc = """ deefuzzer : easy and instant media streaming tool
+ version : %s
+ running on system : %s
+
+ Copyright (c) 2007-%s Guillaume Pellerin <yomguy@parisson.com>
+ 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/deefuzzer/DeeFuzzerLicense
+
+ depends:  python, python-dev, python-xml, python-shout | shout-python, libshout3,
+           libshout3-dev, icecast2, python-mutagen
+
+ recommends: python-twitter, python-tinyurl, python-liblo | pyliblo (>= 0.26)
+
+ provides:  shout-python
+
+ Usage : deefuzzer $1
+  where $1 is the path for a XML config file
+  ex: deefuzzer example/myfuzz.xml
+
+ see http://svn.parisson.org/deefuzzer/ for more details
+        """
+
+    return desc % (version, platform_system, year)
+
+
+class DeeFuzzer(Thread):
+    """a DeeFuzzer diffuser"""
+
+    def __init__(self, conf_file):
+        Thread.__init__(self)
+        self.conf_file = conf_file
+        self.conf = self.get_conf_dict()
+        if 'log' in self.conf['deefuzzer'].keys():
+            self.logger = Logger(self.conf['deefuzzer']['log'])
+        else:
+            self.logger = Logger('.' + os.sep + 'deefuzzer.log')
+        if 'm3u' in self.conf['deefuzzer'].keys():
+            self.m3u = self.conf['deefuzzer']['m3u']
+        else:
+            self.m3u = '.' + os.sep + 'deefuzzer.m3u'
+
+        if isinstance(self.conf['deefuzzer']['station'], dict):
+            # Fix wrong type data from xmltodict when one station (*)
+            self.nb_stations = 1
+        else:
+            self.nb_stations = len(self.conf['deefuzzer']['station'])
+
+        # Set the deefuzzer logger
+        self.logger.write_info('Starting DeeFuzzer v' + version)
+        self.logger.write_info('Using libshout version %s' % shout.version())
+
+        # Init all Stations
+        self.stations = []
+        self.logger.write_info('Number of stations : ' + str(self.nb_stations))
+
+    def get_conf_dict(self):
+        confile = open(self.conf_file,'r')
+        conf_xml = confile.read()
+        confile.close()
+        return xmltodict(conf_xml,'utf-8')
+
+    def set_m3u_playlist(self):
+        m3u_dir = os.sep.join(self.m3u.split(os.sep)[:-1])
+        if not os.path.exists(m3u_dir):
+            os.makedirs(m3u_dir)
+        m3u = open(self.m3u, 'w')
+        m3u.write('#EXTM3U\n')
+        for s in self.stations:
+            info = '#EXTINF:%s,%s - %s\n' % ('-1',s.short_name, s.channel.name)
+            url =  s.channel.protocol + '://' + s.channel.host + ':' + str(s.channel.port) + s.channel.mount + '\n'
+            m3u.write(info)
+            m3u.write(url)
+        m3u.close()
+        self.logger.write_info('Writing M3U file to : ' + self.m3u)
+
+
+    def run(self):
+        q = Queue.Queue(1)
+
+        for i in range(0,self.nb_stations):
+            if isinstance(self.conf['deefuzzer']['station'], dict):
+                station = self.conf['deefuzzer']['station']
+            else:
+                station = self.conf['deefuzzer']['station'][i]
+            self.stations.append(Station(station, q, self.logger, self.m3u))
+
+        self.set_m3u_playlist()
+        p = Producer(q)
+        p.start()
+
+        # Start the Stations
+        for i in range(0,self.nb_stations):
+            self.stations[i].start()
+
+
+class Producer(Thread):
+    """a DeeFuzzer Producer master thread"""
+
+    def __init__(self, q):
+        Thread.__init__(self)
+        self.q = q
+
+    def run(self):
+        i=0
+        q = self.q
+        while True:
+            q.put(i,1)
+            i+=1
+
+
+def main():
+    if len(sys.argv) >= 2:
+        d = DeeFuzzer(sys.argv[-1])
+        d.start()
+    else:
+        text = prog_info()
+        sys.exit(text)
+
+if __name__ == '__main__':
+    main()
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..de5b465
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+'''The setup and build script for the python-twitter library.'''
+
+import os
+
+__author__ = 'yomguy@parisson.com'
+__version__ = '0.5.2'
+
+
+# The base package metadata to be used by both distutils and setuptools
+METADATA = dict(
+  name = "DeeFuzzer",
+  version = __version__,
+  py_modules = ['deefuzzer'],
+  description='an easy and instant media streaming tool',
+  author='Guillaume Pellerin', 
+  author_email='yomguy@parisson.com',
+  license='Gnu Public License V2',
+  url='http://svn.parisson.org/deefuzzer',
+  keywords='audio wideo streaming broadcast shout',
+  install_requires = ['setuptools','tinyurl'],
+  packages=['deefuzzer'], 
+  include_package_data = True,
+  scripts=['scripts/deefuzzer'], 
+  classifiers = ['Programming Language :: Python', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Multimedia :: Sound/Audio', 'Topic :: Multimedia :: Sound/Audio :: Players',],
+)
+
+
+def Main():
+  # Use setuptools if available, otherwise fallback and use distutils
+  try:
+    import setuptools
+    setuptools.setup(**METADATA)
+  except ImportError:
+    import distutils.core
+    distutils.core.setup(**METADATA)
+
+
+if __name__ == '__main__':
+    os.chdir('shout-python')
+    os.system('python setup.py install')
+    os.chdir('..')
+    Main()
diff --git a/tools/PyRSS2Gen.py b/tools/PyRSS2Gen.py
deleted file mode 100644 (file)
index fc1f1cf..0000000
+++ /dev/null
@@ -1,443 +0,0 @@
-"""PyRSS2Gen - A Python library for generating RSS 2.0 feeds."""
-
-__name__ = "PyRSS2Gen"
-__version__ = (1, 0, 0)
-__author__ = "Andrew Dalke <dalke@dalkescientific.com>"
-
-_generator_name = __name__ + "-" + ".".join(map(str, __version__))
-
-import datetime
-
-# Could make this the base class; will need to add 'publish'
-class WriteXmlMixin:
-    def write_xml(self, outfile, encoding = "iso-8859-1"):
-        from xml.sax import saxutils
-        handler = saxutils.XMLGenerator(outfile, encoding)
-        handler.startDocument()
-        self.publish(handler)
-        handler.endDocument()
-
-    def to_xml(self, encoding = "iso-8859-1"):
-        try:
-            import cStringIO as StringIO
-        except ImportError:
-            import StringIO
-        f = StringIO.StringIO()
-        self.write_xml(f, encoding)
-        return f.getvalue()
-
-
-def _element(handler, name, obj, d = {}):
-    if isinstance(obj, basestring) or obj is None:
-        # special-case handling to make the API easier
-        # to use for the common case.
-        handler.startElement(name, d)
-        if obj is not None:
-            handler.characters(obj)
-        handler.endElement(name)
-    else:
-        # It better know how to emit the correct XML.
-        obj.publish(handler)
-
-def _opt_element(handler, name, obj):
-    if obj is None:
-        return
-    _element(handler, name, obj)
-
-
-def _format_date(dt):
-    """convert a datetime into an RFC 822 formatted date
-
-    Input date must be in GMT.
-    """
-    # Looks like:
-    #   Sat, 07 Sep 2002 00:00:01 GMT
-    # Can't use strftime because that's locale dependent
-    #
-    # Isn't there a standard way to do this for Python?  The
-    # rfc822 and email.Utils modules assume a timestamp.  The
-    # following is based on the rfc822 module.
-    return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
-            ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()],
-            dt.day,
-            ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
-             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][dt.month-1],
-            dt.year, dt.hour, dt.minute, dt.second)
-
-        
-##
-# A couple simple wrapper objects for the fields which
-# take a simple value other than a string.
-class IntElement:
-    """implements the 'publish' API for integers
-
-    Takes the tag name and the integer value to publish.
-    
-    (Could be used for anything which uses str() to be published
-    to text for XML.)
-    """
-    element_attrs = {}
-    def __init__(self, name, val):
-        self.name = name
-        self.val = val
-    def publish(self, handler):
-        handler.startElement(self.name, self.element_attrs)
-        handler.characters(str(self.val))
-        handler.endElement(self.name)
-
-class DateElement:
-    """implements the 'publish' API for a datetime.datetime
-
-    Takes the tag name and the datetime to publish.
-
-    Converts the datetime to RFC 2822 timestamp (4-digit year).
-    """
-    def __init__(self, name, dt):
-        self.name = name
-        self.dt = dt
-    def publish(self, handler):
-        _element(handler, self.name, _format_date(self.dt))
-####
-
-class Category:
-    """Publish a category element"""
-    def __init__(self, category, domain = None):
-        self.category = category
-        self.domain = domain
-    def publish(self, handler):
-        d = {}
-        if self.domain is not None:
-            d["domain"] = self.domain
-        _element(handler, "category", self.category, d)
-
-class Cloud:
-    """Publish a cloud"""
-    def __init__(self, domain, port, path,
-                 registerProcedure, protocol):
-        self.domain = domain
-        self.port = port
-        self.path = path
-        self.registerProcedure = registerProcedure
-        self.protocol = protocol
-    def publish(self, handler):
-        _element(handler, "cloud", None, {
-            "domain": self.domain,
-            "port": str(self.port),
-            "path": self.path,
-            "registerProcedure": self.registerProcedure,
-            "protocol": self.protocol})
-
-class Image:
-    """Publish a channel Image"""
-    element_attrs = {}
-    def __init__(self, url, title, link,
-                 width = None, height = None, description = None):
-        self.url = url
-        self.title = title
-        self.link = link
-        self.width = width
-        self.height = height
-        self.description = description
-        
-    def publish(self, handler):
-        handler.startElement("image", self.element_attrs)
-
-        _element(handler, "url", self.url)
-        _element(handler, "title", self.title)
-        _element(handler, "link", self.link)
-
-        width = self.width
-        if isinstance(width, int):
-            width = IntElement("width", width)
-        _opt_element(handler, "width", width)
-        
-        height = self.height
-        if isinstance(height, int):
-            height = IntElement("height", height)
-        _opt_element(handler, "height", height)
-
-        _opt_element(handler, "description", self.description)
-
-        handler.endElement("image")
-
-class Guid:
-    """Publish a guid
-
-    Defaults to being a permalink, which is the assumption if it's
-    omitted.  Hence strings are always permalinks.
-    """
-    def __init__(self, guid, isPermaLink = 1):
-        self.guid = guid
-        self.isPermaLink = isPermaLink
-    def publish(self, handler):
-        d = {}
-        if self.isPermaLink:
-            d["isPermaLink"] = "true"
-        else:
-            d["isPermaLink"] = "false"
-        _element(handler, "guid", self.guid, d)
-
-class TextInput:
-    """Publish a textInput
-
-    Apparently this is rarely used.
-    """
-    element_attrs = {}
-    def __init__(self, title, description, name, link):
-        self.title = title
-        self.description = description
-        self.name = name
-        self.link = link
-
-    def publish(self, handler):
-        handler.startElement("textInput", self.element_attrs)
-        _element(handler, "title", self.title)
-        _element(handler, "description", self.description)
-        _element(handler, "name", self.name)
-        _element(handler, "link", self.link)
-        handler.endElement("textInput")
-        
-
-class Enclosure:
-    """Publish an enclosure"""
-    def __init__(self, url, length, type):
-        self.url = url
-        self.length = length
-        self.type = type
-    def publish(self, handler):
-        _element(handler, "enclosure", None,
-                 {"url": self.url,
-                  "length": str(self.length),
-                  "type": self.type,
-                  })
-
-class Source:
-    """Publish the item's original source, used by aggregators"""
-    def __init__(self, name, url):
-        self.name = name
-        self.url = url
-    def publish(self, handler):
-        _element(handler, "source", self.name, {"url": self.url})
-
-class SkipHours:
-    """Publish the skipHours
-
-    This takes a list of hours, as integers.
-    """
-    element_attrs = {}
-    def __init__(self, hours):
-        self.hours = hours
-    def publish(self, handler):
-        if self.hours:
-            handler.startElement("skipHours", self.element_attrs)
-            for hour in self.hours:
-                _element(handler, "hour", str(hour))
-            handler.endElement("skipHours")
-
-class SkipDays:
-    """Publish the skipDays
-
-    This takes a list of days as strings.
-    """
-    element_attrs = {}
-    def __init__(self, days):
-        self.days = days
-    def publish(self, handler):
-        if self.days:
-            handler.startElement("skipDays", self.element_attrs)
-            for day in self.days:
-                _element(handler, "day", day)
-            handler.endElement("skipDays")
-
-class RSS2(WriteXmlMixin):
-    """The main RSS class.
-
-    Stores the channel attributes, with the "category" elements under
-    ".categories" and the RSS items under ".items".
-    """
-    
-    rss_attrs = {"version": "2.0"}
-    element_attrs = {}
-    def __init__(self,
-                 title,
-                 link,
-                 description,
-
-                 language = None,
-                 copyright = None,
-                 managingEditor = None,
-                 webMaster = None,
-                 pubDate = None,  # a datetime, *in* *GMT*
-                 lastBuildDate = None, # a datetime
-                 
-                 categories = None, # list of strings or Category
-                 generator = _generator_name,
-                 docs = "http://blogs.law.harvard.edu/tech/rss",
-                 cloud = None,    # a Cloud
-                 ttl = None,      # integer number of minutes
-
-                 image = None,     # an Image
-                 rating = None,    # a string; I don't know how it's used
-                 textInput = None, # a TextInput
-                 skipHours = None, # a SkipHours with a list of integers
-                 skipDays = None,  # a SkipDays with a list of strings
-
-                 items = None,     # list of RSSItems
-                 ):
-        self.title = title
-        self.link = link
-        self.description = description
-        self.language = language
-        self.copyright = copyright
-        self.managingEditor = managingEditor
-
-        self.webMaster = webMaster
-        self.pubDate = pubDate
-        self.lastBuildDate = lastBuildDate
-        
-        if categories is None:
-            categories = []
-        self.categories = categories
-        self.generator = generator
-        self.docs = docs
-        self.cloud = cloud
-        self.ttl = ttl
-        self.image = image
-        self.rating = rating
-        self.textInput = textInput
-        self.skipHours = skipHours
-        self.skipDays = skipDays
-
-        if items is None:
-            items = []
-        self.items = items
-
-    def publish(self, handler):
-        handler.startElement("rss", self.rss_attrs)
-        handler.startElement("channel", self.element_attrs)
-        _element(handler, "title", self.title)
-        _element(handler, "link", self.link)
-        _element(handler, "description", self.description)
-
-        self.publish_extensions(handler)
-        
-        _opt_element(handler, "language", self.language)
-        _opt_element(handler, "copyright", self.copyright)
-        _opt_element(handler, "managingEditor", self.managingEditor)
-        _opt_element(handler, "webMaster", self.webMaster)
-
-        pubDate = self.pubDate
-        if isinstance(pubDate, datetime.datetime):
-            pubDate = DateElement("pubDate", pubDate)
-        _opt_element(handler, "pubDate", pubDate)
-
-        lastBuildDate = self.lastBuildDate
-        if isinstance(lastBuildDate, datetime.datetime):
-            lastBuildDate = DateElement("lastBuildDate", lastBuildDate)
-        _opt_element(handler, "lastBuildDate", lastBuildDate)
-
-        for category in self.categories:
-            if isinstance(category, basestring):
-                category = Category(category)
-            category.publish(handler)
-
-        _opt_element(handler, "generator", self.generator)
-        _opt_element(handler, "docs", self.docs)
-
-        if self.cloud is not None:
-            self.cloud.publish(handler)
-
-        ttl = self.ttl
-        if isinstance(self.ttl, int):
-            ttl = IntElement("ttl", ttl)
-        _opt_element(handler, "tt", ttl)
-
-        if self.image is not None:
-            self.image.publish(handler)
-
-        _opt_element(handler, "rating", self.rating)
-        if self.textInput is not None:
-            self.textInput.publish(handler)
-        if self.skipHours is not None:
-            self.skipHours.publish(handler)
-        if self.skipDays is not None:
-            self.skipDays.publish(handler)
-
-        for item in self.items:
-            item.publish(handler)
-
-        handler.endElement("channel")
-        handler.endElement("rss")
-
-    def publish_extensions(self, handler):
-        # Derived classes can hook into this to insert
-        # output after the three required fields.
-        pass
-
-    
-    
-class RSSItem(WriteXmlMixin):
-    """Publish an RSS Item"""
-    element_attrs = {}
-    def __init__(self,
-                 title = None,  # string
-                 link = None,   # url as string
-                 description = None, # string
-                 author = None,      # email address as string
-                 categories = None,  # list of string or Category
-                 comments = None,  # url as string
-                 enclosure = None, # an Enclosure
-                 guid = None,    # a unique string
-                 pubDate = None, # a datetime
-                 source = None,  # a Source
-                 ):
-        
-        if title is None and description is None:
-            raise TypeError(
-                "must define at least one of 'title' or 'description'")
-        self.title = title
-        self.link = link
-        self.description = description
-        self.author = author
-        if categories is None:
-            categories = []
-        self.categories = categories
-        self.comments = comments
-        self.enclosure = enclosure
-        self.guid = guid
-        self.pubDate = pubDate
-        self.source = source
-        # It sure does get tedious typing these names three times...
-
-    def publish(self, handler):
-        handler.startElement("item", self.element_attrs)
-        _opt_element(handler, "title", self.title)
-        _opt_element(handler, "link", self.link)
-        self.publish_extensions(handler)
-        _opt_element(handler, "description", self.description)
-        _opt_element(handler, "author", self.author)
-
-        for category in self.categories:
-            if isinstance(category, basestring):
-                category = Category(category)
-            category.publish(handler)
-        
-        _opt_element(handler, "comments", self.comments)
-        if self.enclosure is not None:
-            self.enclosure.publish(handler)
-        _opt_element(handler, "guid", self.guid)
-
-        pubDate = self.pubDate
-        if isinstance(pubDate, datetime.datetime):
-            pubDate = DateElement("pubDate", pubDate)
-        _opt_element(handler, "pubDate", pubDate)
-
-        if self.source is not None:
-            self.source.publish(handler)
-        
-        handler.endElement("item")
-
-    def publish_extensions(self, handler):
-        # Derived classes can hook into this to insert
-        # output after the title and link elements
-        pass
diff --git a/tools/__init__.py b/tools/__init__.py
deleted file mode 100644 (file)
index cca19e3..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- coding: utf-8 -*-
-from station import *
-from xmltodict import *
-from PyRSS2Gen import *
-from mp3 import *
-from ogg import *
-from logger import *
-from player import *
-from recorder import *
-from osc import *
-from twitt import *
-from relay import *
-from tools import *
diff --git a/tools/get_access_token.py b/tools/get_access_token.py
deleted file mode 100644 (file)
index a614173..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2007 The Python-Twitter Developers
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-import os
-import sys
-
-# parse_qsl moved to urlparse module in v2.6
-try:
-  from urlparse import parse_qsl
-except:
-  from cgi import parse_qsl
-
-import oauth2 as oauth
-
-REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
-ACCESS_TOKEN_URL  = 'https://api.twitter.com/oauth/access_token'
-AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
-SIGNIN_URL        = 'https://api.twitter.com/oauth/authenticate'
-
-consumer_key    = 'ozs9cPS2ci6eYQzzMSTb4g'
-consumer_secret = '1kNEffHgGSXO2gMNTr8HRum5s2ofx3VQnJyfd0es'
-
-if consumer_key is None or consumer_secret is None:
-  print 'You need to edit this script and provide values for the'
-  print 'consumer_key and also consumer_secret.'
-  print ''
-  print 'The values you need come from Twitter - you need to register'
-  print 'as a developer your "application".  This is needed only until'
-  print 'Twitter finishes the idea they have of a way to allow open-source'
-  print 'based libraries to have a token that can be used to generate a'
-  print 'one-time use key that will allow the library to make the request'
-  print 'on your behalf.'
-  print ''
-  sys.exit(1)
-
-signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1()
-oauth_consumer             = oauth.Consumer(key=consumer_key, secret=consumer_secret)
-oauth_client               = oauth.Client(oauth_consumer)
-
-print 'Requesting temp token from Twitter'
-
-resp, content = oauth_client.request(REQUEST_TOKEN_URL, 'GET')
-
-if resp['status'] != '200':
-  print 'Invalid respond from Twitter requesting temp token: %s' % resp['status']
-else:
-  request_token = dict(parse_qsl(content))
-
-  print ''
-  print 'Please visit this Twitter page and retrieve the pincode to be used'
-  print 'in the next step to obtaining an Authentication Token:'
-  print ''
-  print '%s?oauth_token=%s' % (AUTHORIZATION_URL, request_token['oauth_token'])
-  print ''
-
-  pincode = raw_input('Pincode? ')
-
-  token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
-  token.set_verifier(pincode)
-
-  print ''
-  print 'Generating and signing request for an access token'
-  print ''
-
-  oauth_client  = oauth.Client(oauth_consumer, token)
-  resp, content = oauth_client.request(ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % pincode)
-  access_token  = dict(parse_qsl(content))
-
-  if resp['status'] != '200':
-    print 'The request for a Token did not succeed: %s' % resp['status']
-    print access_token
-  else:
-    print 'Your Twitter Access Token key: %s' % access_token['oauth_token']
-    print '          Access Token secret: %s' % access_token['oauth_token_secret']
-    print ''
-
diff --git a/tools/logger.py b/tools/logger.py
deleted file mode 100644 (file)
index 2f68c4d..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-import logging
-
-class Logger:
-    """A logging object"""
-
-    def __init__(self, file):
-        self.logger = logging.getLogger('myapp')
-        self.hdlr = logging.FileHandler(file)
-        self.formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
-        self.hdlr.setFormatter(self.formatter)
-        self.logger.addHandler(self.hdlr)
-        self.logger.setLevel(logging.INFO)
-
-    def write_info(self, message):
-        self.logger.info(message)
-
-    def write_error(self, message):
-        self.logger.error(message)
-
diff --git a/tools/mp3.py b/tools/mp3.py
deleted file mode 100644 (file)
index 4af6c73..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright Guillaume Pellerin (2006-2009)
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL  license under French law and
-# abiding by the rules of distribution of free software.  You can  use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty  and the software's author,  the holder of the
-# economic rights,  and the successive licensors  have only  limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading,  using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean  that it is complicated to manipulate,  and  that  also
-# therefore means  that it is reserved for developers  and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and,  more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import string
-import datetime
-from mutagen.easyid3 import EasyID3
-from mutagen.mp3 import MP3, MPEGInfo
-from mutagen import id3
-from tools import *
-
-EasyID3.valid_keys["comment"]="COMM::'XXX'"
-EasyID3.valid_keys["copyright"]="TCOP::'XXX'"
-
-class Mp3:
-    """A MP3 file object"""
-
-    def __init__(self, media):
-        self.media = media
-        self.item_id = ''
-        self.source = self.media
-        self.options = {}
-        self.bitrate_default = '192'
-        self.cache_dir = os.sep + 'tmp'
-        self.keys2id3 = {'title': 'TIT2',
-                    'artist': 'TPE1',
-                    'album': 'TALB',
-                    'date': 'TDRC',
-                    'comment': 'COMM',
-                    'genre': 'TCON',
-                    'copyright': 'TCOP',
-                    }
-        self.mp3 = MP3(self.media, ID3=EasyID3)
-        self.info = self.mp3.info
-        self.bitrate = int(str(self.info.bitrate)[:-3])
-        self.length = datetime.timedelta(0,self.info.length)
-        try:
-            self.metadata = self.get_file_metadata()
-        except:
-            self.metadata = {'title': '',
-                    'artist': '',
-                    'album': '',
-                    'date': '',
-                    'comment': '',
-                    'genre': '',
-                    'copyright': '',
-                    }
-
-        self.description = self.get_description()
-        self.mime_type = self.get_mime_type()
-        self.media_info = get_file_info(self.media)
-        self.file_name = self.media_info[0]
-        self.file_title = self.media_info[1]
-        self.file_ext = self.media_info[2]
-        self.extension = self.get_file_extension()
-        self.size = os.path.getsize(media)
-        #self.args = self.get_args()
-
-    def get_format(self):
-        return 'MP3'
-
-    def get_file_extension(self):
-        return 'mp3'
-
-    def get_mime_type(self):
-        return 'audio/mpeg'
-
-    def get_description(self):
-        return "MPEG audio Layer III"
-
-    def get_file_metadata(self):
-        metadata = {}
-        for key in self.keys2id3.keys():
-            try:
-                metadata[key] = self.mp3[key][0]
-            except:
-                metadata[key] = ''
-        return metadata
-
-    def write_tags(self):
-        """Write all ID3v2.4 tags by mapping dub2id3_dict dictionnary with the
-            respect of mutagen classes and methods"""
-
-        self.mp3.add_tags()
-        self.mp3.tags['TIT2'] = id3.TIT2(encoding=2, text=u'text')
-        self.mp3.save()
-
-        #media_id3 = id3.ID3(self.media)
-        #for tag in self.metadata.keys():
-            #if tag in self.dub2id3_dict.keys():
-                #frame_text = self.dub2id3_dict[tag]
-                #value = self.metadata[tag]
-                #frame = mutagen.id3.Frames[frame_text](3,value)
-            #try:
-                #media_id3.add(frame)
-            #except:
-                #raise IOError('ExporterError: cannot tag "'+tag+'"')
-
-        #try:
-            #media_id3.save()
-        #except:
-            #raise IOError('ExporterError: cannot write tags')
-
-        media = id3.ID3(self.media)
-        media.add(id3.TIT2(encoding=3, text=self.metadata['title'].decode('utf8')))
-        media.add(id3.TP1(encoding=3, text=self.metadata['artist'].decode('utf8')))
-        media.add(id3.TAL(encoding=3, text=self.metadata['album'].decode('utf8')))
-        media.add(id3.TDRC(encoding=3, text=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
-        media.add(id3.TCO(encoding=3, text=self.metadata['genre'].decode('utf8')))
-        try:
-            media.save()
-        except:
-            raise IOError('ExporterError: cannot write tags')
-
-
diff --git a/tools/ogg.py b/tools/ogg.py
deleted file mode 100644 (file)
index d7305b0..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright Guillaume Pellerin (2006-2009)
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL  license under French law and
-# abiding by the rules of distribution of free software.  You can  use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty  and the software's author,  the holder of the
-# economic rights,  and the successive licensors  have only  limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading,  using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean  that it is complicated to manipulate,  and  that  also
-# therefore means  that it is reserved for developers  and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and,  more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import string
-import datetime
-from mutagen.oggvorbis import OggVorbis
-from tools import *
-
-
-class Ogg:
-    """An OGG file object"""
-
-    def __init__(self, media):
-        self.media = media
-        self.ogg = OggVorbis(self.media)
-        self.item_id = ''
-        self.source = self.media
-        self.options = {}
-        self.bitrate_default = '192'
-        self.cache_dir = os.sep + 'tmp'
-        self.keys2ogg = {'title': 'title',
-                    'artist': 'artist',
-                    'album': 'album',
-                    'date': 'date',
-                    'comment': 'comment',
-                    'genre': 'genre',
-                    'copyright': 'copyright',
-                    }
-        self.info = self.ogg.info
-        self.bitrate = int(str(self.info.bitrate)[:-3])
-        self.length = datetime.timedelta(0,self.info.length)
-        self.metadata = self.get_file_metadata()
-        self.description = self.get_description()
-        self.mime_type = self.get_mime_type()
-        self.media_info = get_file_info(self.media)
-        self.file_name = self.media_info[0]
-        self.file_title = self.media_info[1]
-        self.file_ext = self.media_info[2]
-        self.extension = self.get_file_extension()
-        self.size = os.path.getsize(media)
-        #self.args = self.get_args()
-
-    def get_format(self):
-        return 'OGG'
-
-    def get_file_extension(self):
-        return 'ogg'
-
-    def get_mime_type(self):
-        return 'application/ogg'
-
-    def get_description(self):
-        return 'FIXME'
-
-    def get_file_info(self):
-        try:
-            file_out1, file_out2 = os.popen4('ogginfo "'+self.dest+'"')
-            info = []
-            for line in file_out2.readlines():
-                info.append(clean_word(line[:-1]))
-            self.info = info
-            return self.info
-        except:
-            raise IOError('ExporterError: file does not exist.')
-
-    def set_cache_dir(self,path):
-       self.cache_dir = path
-
-    def get_file_metadata(self):
-        metadata = {}
-        for key in self.keys2ogg.keys():
-            try:
-                metadata[key] = self.ogg[key][0]
-            except:
-                metadata[key] = ''
-        return metadata
-
-    def decode(self):
-        try:
-            os.system('oggdec -o "'+self.cache_dir+os.sep+self.item_id+
-                      '.wav" "'+self.source+'"')
-            return self.cache_dir+os.sep+self.item_id+'.wav'
-        except:
-            raise IOError('ExporterError: decoder is not compatible.')
-
-    def write_tags(self):
-        #self.ogg.add_tags()
-        for tag in self.metadata.keys():
-            self.ogg[tag] = str(self.metadata[tag])
-        self.ogg.save()
-
-    def get_args(self,options=None):
-        """Get process options and return arguments for the encoder"""
-        args = []
-        if not options is None:
-            self.options = options
-            if not ('verbose' in self.options and self.options['verbose'] != '0'):
-                args.append('-Q ')
-            if 'ogg_bitrate' in self.options:
-                args.append('-b '+self.options['ogg_bitrate'])
-            elif 'ogg_quality' in self.options:
-                args.append('-q '+self.options['ogg_quality'])
-            else:
-                args.append('-b '+self.bitrate_default)
-        else:
-            args.append('-Q -b '+self.bitrate_default)
-
-        for tag in self.metadata.keys():
-            value = clean_word(self.metadata[tag])
-            args.append('-c %s="%s"' % (tag, value))
-            if tag in self.dub2args_dict.keys():
-                arg = self.dub2args_dict[tag]
-                args.append('-c %s="%s"' % (arg, value))
-
-        return args
diff --git a/tools/osc.py b/tools/osc.py
deleted file mode 100644 (file)
index 5e471a9..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2006-2009 Guillaume Pellerin
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-from threading import Thread
-
-
-class OSCController(Thread):
-
-    def __init__(self, port):
-        Thread.__init__(self)
-        import liblo
-        self.port = port
-        try:
-            self.server = liblo.Server(self.port)
-        except liblo.ServerError, err:
-            print str(err)
-
-    def add_method(self, path, type, method):
-        self.server.add_method(path, type, method)
-
-    def run(self):
-        while True:
-            self.server.recv(100)
diff --git a/tools/osc_jingles_start.py b/tools/osc_jingles_start.py
deleted file mode 100644 (file)
index 3f2ebf0..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/jingles", 1)
diff --git a/tools/osc_jingles_stop.py b/tools/osc_jingles_stop.py
deleted file mode 100644 (file)
index d29f721..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/jingles", 0)
diff --git a/tools/osc_player_fast.py b/tools/osc_player_fast.py
deleted file mode 100644 (file)
index 92a60fe..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/player", 1)
diff --git a/tools/osc_player_next.py b/tools/osc_player_next.py
deleted file mode 100644 (file)
index 21a91ee..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    sys.exit(err)
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/media/next", 1)
diff --git a/tools/osc_player_slow.py b/tools/osc_player_slow.py
deleted file mode 100644 (file)
index 02948e0..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/player", 0)
diff --git a/tools/osc_record_start.py b/tools/osc_record_start.py
deleted file mode 100644 (file)
index 779e90b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/record", 1)
diff --git a/tools/osc_record_stop.py b/tools/osc_record_stop.py
deleted file mode 100644 (file)
index 8056c69..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/record", 0)
diff --git a/tools/osc_relay_start.py b/tools/osc_relay_start.py
deleted file mode 100644 (file)
index 14bcb69..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/media/relay", 1)
diff --git a/tools/osc_relay_stop.py b/tools/osc_relay_stop.py
deleted file mode 100644 (file)
index eaefe1a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/media/relay", 0)
diff --git a/tools/osc_twitter_start.py b/tools/osc_twitter_start.py
deleted file mode 100644 (file)
index c298be6..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/twitter", 1)
diff --git a/tools/osc_twitter_stop.py b/tools/osc_twitter_stop.py
deleted file mode 100644 (file)
index 3470fcc..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import liblo, sys
-
-# send all messages to port 1234 on the local machine
-try:
-    target = liblo.Address(1234)
-except liblo.AddressError, err:
-    print str(err)
-    sys.exit()
-
-# send message "/foo/message1" with int, float and string arguments
-liblo.send(target, "/twitter", 0)
diff --git a/tools/player.py b/tools/player.py
deleted file mode 100644 (file)
index 85985c7..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2006-2009 Guillaume Pellerin
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-from relay import *
-
-class Player:
-    """A file streaming iterator"""
-
-    def __init__(self):
-        self.main_buffer_size = 0x100000
-        self.relay_buffer_size = 0x40000
-        self.sub_buffer_size = 0x10000
-
-    def set_media(self, media):
-        self.media = media
-
-    def start_relay(self, url):
-        self.url = url
-        self.relay = Relay(self.sub_buffer_size, self.relay_buffer_size)
-        self.relay.set_url(self.url)
-        self.relay.open()
-        self.relay.start()
-        self.queue = self.relay.queue
-
-    def stop_relay(self):
-        self.relay.close()
-
-    def file_read_fast(self):
-        """Read media and stream data through a generator."""
-        m = open(self.media, 'r')
-        while True:
-            __main_chunk = m.read(self.sub_buffer_size)
-            if not __main_chunk:
-                break
-            yield __main_chunk
-        m.close()
-
-    def file_read_slow(self):
-        """Read a bigger part of the media and stream the little parts
-         of the data through a generator"""
-        m = open(self.media, 'r')
-        while True:
-            self.main_chunk = m.read(self.main_buffer_size)
-            if not self.main_chunk:
-                break
-            i = 0
-            while True:
-                start = i * self.sub_buffer_size
-                end = self.sub_buffer_size + (i * self.sub_buffer_size)
-                self.sub_chunk = self.main_chunk[start:end]
-                if not self.sub_chunk:
-                    break
-                yield self.sub_chunk
-                i += 1
-        self.main_chunk = 0
-        self.sub_chunk = 0
-        m.close()
-
-    def relay_read(self):
-        """Read a distant media through its URL"""
-        while True:
-            self.sub_chunk = self.queue.get(self.sub_buffer_size)
-            if not self.sub_chunk:
-                break
-            yield self.sub_chunk
-            self.queue.task_done()
-        self.sub_chunk = 0
diff --git a/tools/recorder.py b/tools/recorder.py
deleted file mode 100644 (file)
index 665632d..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2006-2009 Guillaume Pellerin
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-
-class Recorder:
-    """A file streaming iterator"""
-
-    def __init__(self, path):
-        self.path = path
-
-    def open(self, filename):
-        self.filename = filename
-        self.media = open(self.path + os.sep + self.filename, 'w')
-
-    def write(self, chunk):
-        try:
-            self.media.write(chunk)
-            self.media.flush()
-        except:
-            pass
-
-    def close(self):
-        self.media.close()
diff --git a/tools/relay.py b/tools/relay.py
deleted file mode 100644 (file)
index 831989e..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2006-2009 Guillaume Pellerin
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-from threading import Thread
-import Queue
-import urllib
-
-class Relay(Thread):
-
-    def __init__(self, sub_buffer_size, main_buffer_size):
-        Thread.__init__(self)
-        self.sub_buffer_size = sub_buffer_size
-        self.main_buffer_size = main_buffer_size
-        self.queue = Queue.Queue(self.main_buffer_size)
-
-    def set_url(self, url):
-        self.url = url
-
-    def open(self):
-        try:
-            self.stream = urllib.urlopen(self.url)
-            self.isopen = True
-        except:
-            self.isopen = False
-
-    def close(self):
-        if self.stream:
-            self.isopen = False
-
-    def run(self):
-        while True:
-            if self.isopen:
-                self.chunk = self.stream.read(self.sub_buffer_size)
-                self.queue.put_nowait(self.chunk)
-            else:
-                self.stream.close()
-                break
-
-
-
diff --git a/tools/station.py b/tools/station.py
deleted file mode 100644 (file)
index 49f7ed4..0000000
+++ /dev/null
@@ -1,537 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2006-2009 Guillaume Pellerin
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import sys
-import time
-import datetime
-import string
-import random
-import shout
-from threading import Thread
-from __init__ import *
-
-
-class Station(Thread):
-    """a DeeFuzzer shouting station thread"""
-
-    def __init__(self, station, q, logger, m3u):
-        Thread.__init__(self)
-        self.station = station
-        self.q = q
-        self.logger = logger
-        self.channel = shout.Shout()
-        self.id = 999999
-        self.counter = 0
-        self.command = 'cat '
-        self.delay = 0
-
-       # Media
-        self.media_dir = self.station['media']['dir']
-        self.channel.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']
-
-        # RSS
-        self.rss_dir = self.station['rss']['dir']
-        self.rss_enclosure = self.station['rss']['enclosure']
-
-        # Infos
-        self.channel.url = self.station['infos']['url']
-        self.short_name = self.station['infos']['short_name']
-        self.channel.name = self.station['infos']['name'] + ' : ' + self.channel.url
-        self.channel.genre = self.station['infos']['genre']
-        self.channel.description = self.station['infos']['description']
-        self.base_name = self.rss_dir + os.sep + self.short_name + '_' + self.channel.format
-        self.rss_current_file = self.base_name + '_current.xml'
-        self.rss_playlist_file = self.base_name + '_playlist.xml'
-        self.m3u = m3u
-
-        # Server
-        self.channel.protocol = 'http'     # | 'xaudiocast' | 'icy'
-        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.mount = '/' + self.short_name + '.' + self.channel.format
-        self.channel.public = int(self.station['server']['public'])
-        self.channel.audio_info = { 'bitrate': self.bitrate,
-                                    'samplerate': self.samplerate,
-                                    'quality': self.ogg_quality,
-                                    'channels': self.voices,}
-        self.playlist = self.get_playlist()
-        self.lp = len(self.playlist)
-
-        # Logging
-        self.logger.write_info('Opening ' + self.short_name + ' - ' + self.channel.name + \
-                ' (' + str(self.lp) + ' tracks)...')
-
-        self.metadata_relative_dir = 'metadata'
-        self.metadata_url = self.channel.url + '/rss/' + self.metadata_relative_dir
-        self.metadata_dir = self.rss_dir + os.sep + self.metadata_relative_dir
-        if not os.path.exists(self.metadata_dir):
-            os.makedirs(self.metadata_dir)
-
-        # The station's player
-        self.player = Player()
-        self.player_mode = 0
-
-        # Jingling between each media.
-        # mode = 0 means Off, mode = 1 means On
-        self.jingles_mode = 0
-        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])
-
-        # Relaying
-        # mode = 0 means Off, mode = 1 means On
-        self.relay_mode = 0
-        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('/relay', [1])
-
-        # Twitting
-        # mode = 0 means Off, mode = 1 means On
-        self.twitter_mode = 0
-        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(' ')
-            if self.twitter_mode == 1:
-                self.twitter_callback('/twitter', [1])
-
-        # Recording
-        # mode = 0 means Off, mode = 1 means On
-        self.record_mode = 0
-        if 'record' in self.station:
-            self.record_mode = int(self.station['record']['mode'])
-            self.record_dir = self.station['record']['dir']
-            if self.record_mode == 1:
-                self.record_callback('/record', [1])
-
-        # Running
-        # mode = 0 means Off, mode = 1 means On
-        self.run_mode = 1
-
-        # OSCing
-        self.osc_control_mode = 0
-        # mode = 0 means Off, mode = 1 means On
-        if 'control' in self.station:
-            self.osc_control_mode = int(self.station['control']['mode'])
-            self.osc_port = self.station['control']['port']
-            if self.osc_control_mode == 1:
-                self.osc_controller = OSCController(self.osc_port)
-                self.osc_controller.start()
-                # 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)
-
-    def run_callback(self, path, value):
-        value = value[0]
-        self.run_mode = value
-        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
-        self.logger.write_info(message)
-
-    def media_next_callback(self, path, value):
-        value = value[0]
-        self.next_media = value
-        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
-        self.logger.write_info(message)
-
-    def relay_callback(self, path, value):
-        value = value[0]
-        if value == 1:
-            self.relay_mode = 1
-            self.player.start_relay(self.relay_url)
-        elif value == 0:
-            self.relay_mode = 0
-            self.player.stop_relay()
-        self.id = 0
-        self.next_media = 1
-        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
-        self.logger.write_info(message)
-        message = "Relaying : %s" % self.relay_url
-        self.logger.write_info(message)
-
-    def twitter_callback(self, path, value):
-        value = value[0]
-        import tinyurl
-        self.twitter = Twitter(self.twitter_key, self.twitter_secret)
-        self.twitter_mode = value
-        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
-        self.m3u_tinyurl = tinyurl.create_one(self.channel.url + '/m3u/' + self.m3u.split(os.sep)[-1])
-        self.rss_tinyurl = tinyurl.create_one(self.channel.url + '/rss/' + self.rss_playlist_file.split(os.sep)[-1])
-        self.logger.write_info(message)
-
-    def jingles_callback(self, path, value):
-        value = value[0]
-        if value == 1:
-            self.jingles_list = self.get_jingles()
-            self.jingles_length = len(self.jingles_list)
-            self.jingle_id = 0
-        self.jingles_mode = value
-        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
-        self.logger.write_info(message)
-
-    def record_callback(self, path, value):
-        value = value[0]
-        if value == 1:
-            if not os.path.exists(self.record_dir):
-                os.makedirs(self.record_dir)
-            self.rec_file = self.short_name + '-' + \
-              datetime.datetime.now().strftime("%x-%X").replace('/', '_') + '.' + self.channel.format
-            self.recorder = Recorder(self.record_dir)
-            self.recorder.open(self.rec_file)
-        elif value == 0:
-            self.recorder.close()
-            date = datetime.datetime.now().strftime("%Y")
-            if self.channel.format == 'mp3':
-                media = Mp3(self.record_dir + os.sep + self.rec_file)
-            if self.channel.format == 'ogg':
-                media = Ogg(self.record_dir + os.sep + self.rec_file)
-            media.metadata = {'artist': self.artist.encode('utf-8'),
-                                'title': self.title.encode('utf-8'),
-                                'album': self.short_name.encode('utf-8'),
-                                'genre': self.channel.genre.encode('utf-8'),
-                                'date' : date.encode('utf-8'),}
-            media.write_tags()
-        self.record_mode = value
-        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
-        self.logger.write_info(message)
-
-    def player_callback(self, path, value):
-        value = value[0]
-        self.player_mode = value
-        message = "Received OSC message '%s' with arguments '%d'" % (path, value)
-        self.logger.write_info(message)
-
-    def get_playlist(self):
-        file_list = []
-        for root, dirs, files in os.walk(self.media_dir):
-            for file in files:
-                s = file.split('.')
-                ext = s[len(s)-1]
-                if ext.lower() == self.channel.format and not os.sep+'.' in file:
-                    file_list.append(root + os.sep + file)
-        file_list.sort()
-        return file_list
-
-    def get_jingles(self):
-        file_list = []
-        for root, dirs, files in os.walk(self.jingles_dir):
-            for file in files:
-                s = file.split('.')
-                ext = s[len(s)-1]
-                if ext.lower() == self.channel.format and not os.sep+'.' in file:
-                    file_list.append(root + os.sep + file)
-        file_list.sort()
-        return file_list
-
-    def get_next_media(self):
-        # Init playlist
-        if self.lp != 0:
-            playlist = self.playlist
-            new_playlist = self.get_playlist()
-            lp_new = len(new_playlist)
-
-            if lp_new != self.lp or self.counter == 0:
-                self.id = 0
-                self.lp = lp_new
-
-                # Twitting new tracks
-                new_playlist_set = set(new_playlist)
-                playlist_set = set(playlist)
-                new_tracks = new_playlist_set - playlist_set
-                self.new_tracks = list(new_tracks.copy())
-                
-                if len(new_tracks) != 0:
-                    new_tracks_objs = self.media_to_objs(self.new_tracks)
-                    for media_obj in new_tracks_objs:
-                        title = media_obj.metadata['title']
-                        artist = media_obj.metadata['artist']
-                        if not (title or artist):
-                            song = str(media_obj.file_name)
-                        else:
-                            song = artist + ' : ' + title
-                        song = song.encode('utf-8')
-                        artist = artist.encode('utf-8')
-                        if self.twitter_mode == 1:
-                            artist_names = artist.split(' ')
-                            artist_tags = ' #'.join(list(set(artist_names)-set(['&', '-'])))
-                            message = '#newtrack ! %s #%s on #%s RSS: ' % (song.replace('_', ' '), artist_tags, self.short_name)
-                            message = message[:113] + self.rss_tinyurl
-                            self.update_twitter(message)
-                
-                # Shake it, Fuzz it !
-                if self.shuffle_mode == 1:
-                    random.shuffle(playlist)
-                    
-                # Play new tracks first
-                for track in self.new_tracks:
-                    playlist.insert(0, track)
-                self.playlist = playlist
-                
-                self.logger.write_info('Station ' + self.short_name + \
-                                 ' : generating new playlist (' + str(self.lp) + ' tracks)')
-                self.update_rss(self.media_to_objs(self.playlist), self.rss_playlist_file, '(playlist)')
-
-            if self.jingles_mode == 1 and (self.counter % 2) == 0 and not self.jingles_length == 0:
-                media = self.jingles_list[self.jingle_id]
-                self.jingle_id = (self.jingle_id + 1) % self.jingles_length
-            else:
-                media = self.playlist[self.id]
-                self.id = (self.id + 1) % self.lp
-            return media
-        else:
-            mess = 'No media in media_dir !'
-            self.logger.write_error(mess)
-            sys.exit(mess)
-
-    def media_to_objs(self, media_list):
-        media_objs = []
-        for media in media_list:
-            file_name, file_title, file_ext = get_file_info(media)
-            if file_ext.lower() == 'mp3':
-                media_objs.append(Mp3(media))
-            elif file_ext.lower() == 'ogg':
-                media_objs.append(Ogg(media))
-        return media_objs
-
-    def update_rss(self, media_list, rss_file, sub_title):
-        rss_item_list = []
-        if not os.path.exists(self.rss_dir):
-            os.makedirs(self.rss_dir)
-        channel_subtitle = self.channel.name + ' ' + sub_title
-        _date_now = datetime.datetime.now()
-        date_now = str(_date_now)
-        media_absolute_playtime = _date_now
-
-        for media in media_list:
-            media_stats = os.stat(media.media)
-            media_date = time.localtime(media_stats[8])
-            media_date = time.strftime("%a, %d %b %Y %H:%M:%S +0200", media_date)
-            media.metadata['Duration'] = str(media.length).split('.')[0]
-            media.metadata['Bitrate'] = str(media.bitrate) + ' kbps'
-            media.metadata['Next play'] = str(media_absolute_playtime).split('.')[0]
-
-            media_description = '<table>'
-            media_description_item = '<tr><td>%s:   </td><td><b>%s</b></td></tr>'
-            for key in media.metadata.keys():
-                if media.metadata[key] != '':
-                    media_description += media_description_item % (key.capitalize(), media.metadata[key])
-            media_description += '</table>'
-
-            title = media.metadata['title']
-            artist = media.metadata['artist']
-            if not (title or artist):
-                song = str(media.file_title)
-            else:
-                song = artist + ' : ' + title
-
-            media_absolute_playtime += media.length
-
-            if self.rss_enclosure == '1':
-                media_link = self.channel.url + '/media/' + media.file_name
-                media_link = media_link.decode('utf-8')
-                rss_item_list.append(RSSItem(
-                    title = song,
-                    link = media_link,
-                    description = media_description,
-                    enclosure = Enclosure(media_link, str(media.size), 'audio/mpeg'),
-                    guid = Guid(media_link),
-                    pubDate = media_date,)
-                    )
-            else:
-                media_link = self.metadata_url + '/' + media.file_name + '.xml'
-                media_link = media_link.decode('utf-8')
-                rss_item_list.append(RSSItem(
-                    title = song,
-                    link = media_link,
-                    description = media_description,
-                    guid = Guid(media_link),
-                    pubDate = media_date,)
-                    )
-
-        rss = RSS2(title = channel_subtitle,
-                            link = self.channel.url,
-                            description = self.channel.description.decode('utf-8'),
-                            lastBuildDate = date_now,
-                            items = rss_item_list,)
-        f = open(rss_file, 'w')
-        rss.write_xml(f, 'utf-8')
-        f.close()
-
-    def update_twitter(self, message):
-        try:
-            self.twitter.post(message.decode('utf8'))
-            self.logger.write_info('Twitting : "' + message + '"')
-        except:
-            self.logger.write_error('Twitting : "' + message + '"')
-            pass
-
-    def set_relay_mode(self):
-        self.prefix = '#nowplaying (relaying #LIVE)'
-        self.title = self.channel.description.encode('utf-8')
-        self.artist = self.relay_author.encode('utf-8')
-        self.title = self.title.replace('_', ' ')
-        self.artist = self.artist.replace('_', ' ')
-        self.song = self.artist + ' : ' + self.title
-        self.stream = self.player.relay_read()
-
-    def set_read_mode(self):
-        self.prefix = '#nowplaying'
-        self.current_media_obj = self.media_to_objs([self.media])
-        self.title = self.current_media_obj[0].metadata['title']
-        self.artist = self.current_media_obj[0].metadata['artist']
-        self.title = self.title.replace('_', ' ')
-        self.artist = self.artist.replace('_', ' ')
-        if not (self.title or self.artist):
-            song = str(self.current_media_obj[0].file_name)
-        else:
-            song = self.artist + ' : ' + self.title
-        self.song = song.encode('utf-8')
-        self.artist = self.artist.encode('utf-8')
-        self.metadata_file = self.metadata_dir + os.sep + self.current_media_obj[0].file_name + '.xml'
-        self.update_rss(self.current_media_obj, self.metadata_file, '')
-        self.update_rss(self.current_media_obj, self.rss_current_file, '(currently playing)')
-        self.logger.write_info('DeeFuzzing on %s :  id = %s, name = %s' \
-            % (self.short_name, self.id, self.current_media_obj[0].file_name))
-        self.player.set_media(self.media)
-        if self.player_mode == 0:
-            self.stream = self.player.file_read_slow()
-        elif self.player_mode == 1:
-            self.stream = self.player.file_read_fast()
-
-    def update_twitter_current(self):
-        artist_names = self.artist.split(' ')
-        artist_tags = ' #'.join(list(set(artist_names)-set(['&', '-'])))
-        message = '%s %s on #%s' % (self.prefix, self.song, self.short_name)
-        tags = '#' + ' #'.join(self.twitter_tags)
-        message = message + ' ' + tags
-        message = message[:108] + ' M3U: ' + self.m3u_tinyurl
-        self.update_twitter(message)
-
-    def channel_open(self):
-        self.channel.open()
-        self.channel_delay = self.channel.delay()
-    
-    def run(self):
-        try:
-            self.channel_open()
-        except:
-            pass
-            
-        while self.run_mode:
-            self.q.get(1)
-            self.next_media = 0
-            self.media = self.get_next_media()
-            self.counter += 1
-            if self.relay_mode:
-                self.set_relay_mode()
-            elif os.path.exists(self.media) and not os.sep+'.' in self.media:
-                if self.lp == 0:
-                    self.logger.write_error('Station ' + self.short_name + ' has no media to stream !')
-                    break
-                self.set_read_mode()
-            self.q.task_done()
-
-            self.q.get(1)
-            if (not (self.jingles_mode and (self.counter % 2)) or self.relay_mode) and self.twitter_mode:
-                try:
-                    self.update_twitter_current()
-                except:
-                    continue
-            try:
-                self.channel.set_metadata({'song': self.song, 'charset': 'utf-8',})
-            except:
-                continue
-            self.q.task_done()
-
-            for self.chunk in self.stream:
-                self.q.get(1)
-                if self.next_media or not self.run_mode:
-                    break
-                try:
-                    if self.record_mode:
-                        self.recorder.write(self.chunk)
-                except:
-                    self.logger.write_error('Station ' + self.short_name + ' : could not write the buffer to the file')
-                    continue
-                try:
-                    self.channel.send(self.chunk)
-                    self.channel.sync()
-                except:
-                    self.logger.write_error('Station ' + self.short_name + ' : could not send the buffer')
-                    try:
-                        self.channel.close()
-                    except:
-                        self.logger.write_error('Station ' + self.short_name + ' : could not close the channel')
-                        try:
-                            self.channel.open()
-                            self.channel.set_metadata({'song': self.song, 'charset': 'utf8',})
-                            self.channel.send(self.chunk)
-                            self.channel.sync()
-                        except:
-                            self.logger.write_error('Station ' + self.short_name + ' : could not restart the channel')
-                            continue
-                        continue
-                    continue
-                self.q.task_done()
-        
-        if not self.run_mode:
-            self.q.task_done()
-
-        if self.record_mode:
-            self.recorder.close()
-
-        self.channel.close()
diff --git a/tools/tools.py b/tools/tools.py
deleted file mode 100644 (file)
index 8282cfc..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
-# 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/deefuzz/wiki/DefuzzLicense.
-#
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-import os
-import re
-import string
-
-def clean_word(word) :
-    """ Return the word without excessive blank spaces, underscores and
-    characters causing problem to exporters"""
-    word = re.sub("^[^\w]+","",word)    #trim the beginning
-    word = re.sub("[^\w]+$","",word)    #trim the end
-    word = re.sub("_+","_",word)        #squeeze continuous _ to one _
-    word = re.sub("^[^\w]+","",word)    #trim the beginning _
-    #word = string.replace(word,' ','_')
-    #word = string.capitalize(word)
-    dict = '&[];"*:,'
-    for letter in dict:
-        word = string.replace(word,letter,'_')
-    return word
-
-def get_file_info(media):
-    file_name = media.split(os.sep)[-1]
-    file_title = file_name.split('.')[:-1]
-    file_title = '.'.join(file_title)
-    file_ext = file_name.split('.')[-1]
-    return file_name, file_title, file_ext
diff --git a/tools/twitt.py b/tools/twitt.py
deleted file mode 100644 (file)
index da26092..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2006-2009 Guillaume Pellerin
-
-# <yomguy@parisson.com>
-
-# This software is a computer program whose purpose is to stream audio
-# and video data through icecast2 servers.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and  rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using,  modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and  experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-# Author: Guillaume Pellerin <yomguy@parisson.com>
-
-# Twitter DeeFuzzer keys
-DEEFUZZER_CONSUMER_KEY = 'ozs9cPS2ci6eYQzzMSTb4g'
-DEEFUZZER_CONSUMER_SECRET = '1kNEffHgGSXO2gMNTr8HRum5s2ofx3VQnJyfd0es'
-
-
-class Twitter:
-
-    def __init__(self, key, secret):
-        import twitter
-        self.consumer_key = DEEFUZZER_CONSUMER_KEY
-        self.consumer_secret = DEEFUZZER_CONSUMER_SECRET
-        self.access_token_key = key
-        self.access_token_secret = secret
-        self.api = twitter.Api(consumer_key=self.consumer_key,
-                               consumer_secret=self.consumer_secret,
-                               access_token_key=self.access_token_key,
-                               access_token_secret=self.access_token_secret)
-
-    def post(self, message):
-        try:
-            self.api.PostUpdate(message)
-        except:
-            pass
-
diff --git a/tools/xmltodict.py b/tools/xmltodict.py
deleted file mode 100644 (file)
index b85d556..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python
-
-# Easily import simple XML data to Python dictionary
-# http://www.gmta.info/publications/parsing-simple-xml-structure-to-a-python-dictionary
-
-import xml.dom.minidom
-
-def haschilds(dom):
-    # Checks whether an element has any childs
-    # containing real tags opposed to just text.
-    for childnode in dom.childNodes:
-        if childnode.nodeName != "#text" and \
-            childnode.nodeName != "#cdata-section":
-            return True
-    return False
-
-def indexchilds(dom, enc):
-    childsdict = dict()
-    for childnode in dom.childNodes:
-        name = childnode.nodeName.encode(enc)
-        if name == "#text" or name == "#cdata-section":
-            # ignore whitespaces
-            continue
-        if haschilds(childnode):
-            v = indexchilds(childnode, enc)
-        else:
-            v = childnode.childNodes[0].nodeValue.encode(enc)
-        if name in childsdict:
-            if isinstance(childsdict[name], dict):
-                # there is multiple instances of this node - convert to list
-                childsdict[name] = [childsdict[name]]
-            childsdict[name].append(v)
-        else:
-            childsdict[name] = v
-    return childsdict
-
-def xmltodict(data, enc=None):
-    dom = xml.dom.minidom.parseString(data.strip())
-    return indexchilds(dom, enc)
-
-
diff --git a/tools/xmltodict2.py b/tools/xmltodict2.py
deleted file mode 100644 (file)
index b669239..0000000
+++ /dev/null
@@ -1,397 +0,0 @@
-# -*- coding: utf-8 -*-
-""" xmltodict(): convert xml into tree of Python dicts.
-
-This was copied and modified from John Bair's recipe at aspn.activestate.com:
-       http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/149368
-"""
-import os
-import string
-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
-
-# Python seems to need to compile code with \n linesep:
-code_linesep = "\n"
-eol = os.linesep
-
-
-class Xml2Obj:
-       """XML to Object"""
-       def __init__(self):
-               self.root = None
-               self.nodeStack = []
-               self.attsToSkip = []
-               self._inCode = False
-               self._mthdName = ""
-               self._mthdCode = ""
-               self._codeDict = None
-               self._inProp = False
-               self._propName = ""
-               self._propData = ""
-               self._propDict = None
-               self._currPropAtt = ""
-               self._currPropDict = None
-
-
-       def StartElement(self, name, attributes):
-               """SAX start element even handler"""
-               if name == "code":
-                       # This is code for the parent element
-                       self._inCode = True
-                       parent = self.nodeStack[-1]
-                       if not parent.has_key("code"):
-                               parent["code"] = {}
-                               self._codeDict = parent["code"]
-
-               elif name == "properties":
-                       # These are the custom property definitions
-                       self._inProp = True
-                       self._propName = ""
-                       self._propData = ""
-                       parent = self.nodeStack[-1]
-                       if not parent.has_key("properties"):
-                               parent["properties"] = {}
-                               self._propDict = parent["properties"]
-
-               else:
-                       if self._inCode:
-                               self._mthdName = name.encode()
-                       elif self._inProp:
-                               if self._propName:
-                                       # In the middle of a prop definition
-                                       self._currPropAtt = name.encode()
-                               else:
-                                       self._propName = name.encode()
-                                       self._currPropDict = {}
-                                       self._currPropAtt = ""
-                       else:
-                               element = {"name": name.encode()}
-                               if len(attributes) > 0:
-                                       for att in self.attsToSkip:
-                                               if attributes.has_key(att):
-                                                       del attributes[att]
-                                       element["attributes"] = attributes
-
-                               # Push element onto the stack and make it a child of parent
-                               if len(self.nodeStack) > 0:
-                                       parent = self.nodeStack[-1]
-                                       if not parent.has_key("children"):
-                                               parent["children"] = []
-                                       parent["children"].append(element)
-                               else:
-                                       self.root = element
-                               self.nodeStack.append(element)
-
-
-       def EndElement(self, name):
-               """SAX end element event handler"""
-               if self._inCode:
-                       if name == "code":
-                               self._inCode = False
-                               self._codeDict = None
-                       else:
-                               # End of an individual method
-                               mth = self._mthdCode.strip()
-                               if not mth.endswith("\n"):
-                                       mth += "\n"
-                               self._codeDict[self._mthdName] = mth
-                               self._mthdName = ""
-                               self._mthdCode = ""
-               elif self._inProp:
-                       if name == "properties":
-                               self._inProp = False
-                               self._propDict = None
-                       elif name == self._propName:
-                               # End of an individual prop definition
-                               self._propDict[self._propName] = self._currPropDict
-                               self._propName = ""
-                       else:
-                               # end of a property attribute
-                               self._currPropDict[self._currPropAtt] = self._propData
-                               self._propData = self._currPropAtt = ""
-               else:
-                       self.nodeStack = self.nodeStack[:-1]
-
-
-       def CharacterData(self, data):
-               """SAX character data event handler"""
-               if self._inCode or data.strip():
-                       data = data.replace("&lt;", "<")
-                       data = data.encode()
-                       if self._inCode:
-                               if self._mthdCode:
-                                       self._mthdCode += data
-                               else:
-                                       self._mthdCode = data
-                       elif self._inProp:
-                               self._propData += data
-                       else:
-                               element = self.nodeStack[-1]
-                               if not element.has_key("cdata"):
-                                       element["cdata"] = ""
-                               element["cdata"] += data
-
-
-       def Parse(self, xml):
-               # Create a SAX parser
-               Parser = expat.ParserCreate()
-               # SAX event handlers
-               Parser.StartElementHandler = self.StartElement
-               Parser.EndElementHandler = self.EndElement
-               Parser.CharacterDataHandler = self.CharacterData
-               # Parse the XML File
-               ParserStatus = Parser.Parse(xml, 1)
-               return self.root
-
-
-       def ParseFromFile(self, filename):
-               return self.Parse(open(filename,"r").read())
-
-
-def xmltodict(xml, attsToSkip=[], addCodeFile=False):
-       """Given an xml string or file, return a Python dictionary."""
-       parser = Xml2Obj()
-       parser.attsToSkip = attsToSkip
-       isPath = os.path.exists(xml)
-       errmsg = ""
-       if eol not in xml and isPath:
-               # argument was a file
-               try:
-                       ret = parser.ParseFromFile(xml)
-               except expat.ExpatError, e:
-                       errmsg = _("The XML in '%s' is not well-formed and cannot be parsed: %s") % (xml, e)
-       else:
-               # argument must have been raw xml:
-               if not xml.strip().startswith("<?xml "):
-                       # it's a bad file name
-                       errmsg = _("The file '%s' could not be found") % xml
-               else:
-                       try:
-                               ret = parser.Parse(xml)
-                       except expat.ExpatError:
-                               errmsg = _("An invalid XML string was encountered")
-       if errmsg:
-               raise dabo.dException.XmlException, errmsg
-       if addCodeFile and isPath:
-               # Get the associated code file, if any
-               codePth = "%s-code.py" % os.path.splitext(xml)[0]
-               if os.path.exists(codePth):
-                       try:
-                               codeDict = desUtil.parseCodeFile(open(codePth).read())
-                               desUtil.addCodeToClassDict(ret, codeDict)
-                       except StandardError, e:
-                               print "Failed to parse code file:", e
-       return ret
-
-
-def escQuote(val, noEscape=False, noQuote=False):
-       """Add surrounding quotes to the string, and escape
-       any illegal XML characters.
-       """
-       if not isinstance(val, basestring):
-               val = str(val)
-       if not isinstance(val, unicode):
-               val = unicode(val, default_encoding)
-       if noQuote:
-               qt = ''
-       else:
-               qt = '"'
-       slsh = "\\"
-#      val = val.replace(slsh, slsh+slsh)
-       if not noEscape:
-               # First escape internal ampersands. We need to double them up due to a
-               # quirk in wxPython and the way it displays this character.
-               val = val.replace("&", "&amp;&amp;")
-               # Escape any internal quotes
-               val = val.replace('"', '&quot;').replace("'", "&apos;")
-               # Escape any high-order characters
-               chars = []
-               for pos, char in enumerate(list(val)):
-                       if ord(char) > 127:
-                               chars.append("&#%s;" % ord(char))
-                       else:
-                                       chars.append(char)
-               val = "".join(chars)
-       val = val.replace("<", "&#060;").replace(">", "&#062;")
-       return "%s%s%s" % (qt, val, qt)
-
-
-def dicttoxml(dct, level=0, header=None, linesep=None):
-       """Given a Python dictionary, return an xml string.
-
-       The dictionary must be in the format returned by dicttoxml(), with keys
-       on "attributes", "code", "cdata", "name", and "children".
-
-       Send your own XML header, otherwise a default one will be used.
-
-       The linesep argument is a dictionary, with keys on levels, allowing the
-       developer to add extra whitespace depending on the level.
-       """
-       att = ""
-       ret = ""
-
-       if dct.has_key("attributes"):
-               for key, val in dct["attributes"].items():
-                       # Some keys are already handled.
-                       noEscape = key in ("sizerInfo",)
-                       val = escQuote(val, noEscape)
-                       att += " %s=%s" % (key, val)
-       ret += "%s<%s%s" % ("\t" * level, dct["name"], att)
-
-       if (not dct.has_key("cdata") and not dct.has_key("children")
-                       and not dct.has_key("code") and not dct.has_key("properties")):
-               ret += " />%s" % eol
-       else:
-               ret += ">"
-               if dct.has_key("cdata"):
-                       ret += "%s" % dct["cdata"].replace("<", "&lt;")
-
-               if dct.has_key("code"):
-                       if len(dct["code"].keys()):
-                               ret += "%s%s<code>%s" % (eol, "\t" * (level+1), eol)
-                               methodTab = "\t" * (level+2)
-                               for mthd, cd in dct["code"].items():
-                                       # Convert \n's in the code to eol:
-                                       cd = eol.join(cd.splitlines())
-
-                                       # Make sure that the code ends with a linefeed
-                                       if not cd.endswith(eol):
-                                               cd += eol
-
-                                       ret += "%s<%s><![CDATA[%s%s]]>%s%s</%s>%s" % (methodTab,
-                                                       mthd, eol, cd, eol,
-                                                       methodTab, mthd, eol)
-                               ret += "%s</code>%s"    % ("\t" * (level+1), eol)
-
-               if dct.has_key("properties"):
-                       if len(dct["properties"].keys()):
-                               ret += "%s%s<properties>%s" % (eol, "\t" * (level+1), eol)
-                               currTab = "\t" * (level+2)
-                               for prop, val in dct["properties"].items():
-                                       ret += "%s<%s>%s" % (currTab, prop, eol)
-                                       for propItm, itmVal in val.items():
-                                               itmTab = "\t" * (level+3)
-                                               ret += "%s<%s>%s</%s>%s" % (itmTab, propItm, itmVal,
-                                                               propItm, eol)
-                                       ret += "%s</%s>%s" % (currTab, prop, eol)
-                               ret += "%s</properties>%s"      % ("\t" * (level+1), eol)
-
-               if dct.has_key("children") and len(dct["children"]) > 0:
-                       ret += eol
-                       for child in dct["children"]:
-                               ret += dicttoxml(child, level+1, linesep=linesep)
-               indnt = ""
-               if ret.endswith(eol):
-                       # Indent the closing tag
-                       indnt = ("\t" * level)
-               ret += "%s</%s>%s" % (indnt, dct["name"], eol)
-
-               if linesep:
-                       ret += linesep.get(level, "")
-
-       if level == 0:
-               if header is None:
-                       header = '<?xml version="1.0" encoding="%s" standalone="no"?>%s' \
-                                       % (default_encoding, eol)
-               ret = header + ret
-
-       return ret
-
-
-def flattenClassDict(cd, retDict=None):
-       """Given a dict containing a series of nested objects such as would
-       be created by restoring from a cdxml file, returns a dict with all classIDs
-       as keys, and a dict as the corresponding value. The dict value will have
-       keys for the attributes and/or code, depending on what was in the original
-       dict. The end result is to take a nested dict structure and return a flattened
-       dict with all objects at the top level.
-       """
-       if retDict is None:
-               retDict = {}
-       atts = cd.get("attributes", {})
-       props = cd.get("properties", {})
-       kids = cd.get("children", [])
-       code = cd.get("code", {})
-       classID = atts.get("classID", "")
-       classFile = resolvePath(atts.get("designerClass", ""))
-       superclass = resolvePath(atts.get("superclass", ""))
-       superclassID = atts.get("superclassID", "")
-       if superclassID and os.path.exists(superclass):
-               # Get the superclass info
-               superCD = xmltodict(superclass, addCodeFile=True)
-               flattenClassDict(superCD, retDict)
-       if classID:
-               if os.path.exists(classFile):
-                       # Get the class info
-                       classCD = xmltodict(classFile, addCodeFile=True)
-                       classAtts = classCD.get("attributes", {})
-                       classProps = classCD.get("properties", {})
-                       classCode = classCD.get("code", {})
-                       classKids = classCD.get("children", [])
-                       currDict = retDict.get(classID, {})
-                       retDict[classID] = {"attributes": classAtts, "code": classCode,
-                                       "properties": classProps}
-                       retDict[classID].update(currDict)
-                       # Now update the child objects in the dict
-                       for kid in classKids:
-                               flattenClassDict(kid, retDict)
-               else:
-                       # Not a file; most likely just a component in another class
-                       currDict = retDict.get(classID, {})
-                       retDict[classID] = {"attributes": atts, "code": code,
-                                       "properties": props}
-                       retDict[classID].update(currDict)
-       if kids:
-               for kid in kids:
-                       flattenClassDict(kid, retDict)
-       return retDict
-
-
-def addInheritedInfo(src, super, updateCode=False):
-       """Called recursively on the class container structure, modifying
-       the attributes to incorporate superclass information. When the
-       'updateCode' parameter is True, superclass code is added to the
-       object's code
-       """
-       atts = src.get("attributes", {})
-       props = src.get("properties", {})
-       kids = src.get("children", [])
-       code = src.get("code", {})
-       classID = atts.get("classID", "")
-       if classID:
-               superInfo = super.get(classID, {"attributes": {}, "code": {}, "properties": {}})
-               src["attributes"] = superInfo["attributes"].copy()
-               src["attributes"].update(atts)
-               src["properties"] = superInfo.get("properties", {}).copy()
-               src["properties"].update(props)
-               if updateCode:
-                       src["code"] = superInfo["code"].copy()
-                       src["code"].update(code)
-       if kids:
-               for kid in kids:
-                       addInheritedInfo(kid, super, updateCode)
-
-
-
-#if __name__ == "__main__":
-       #test_dict = {"name": "test", "attributes":{"path": "c:\\temp\\name",
-                       #"problemChars": "Welcome to <Jos\xc3\xa9's \ Stuff!>\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