+++ /dev/null
-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.
+++ /dev/null
-# -*- 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 :)
-
--- /dev/null
+# -*- 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 :)
+
--- /dev/null
+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.
+++ /dev/null
-================
-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.
-
--- /dev/null
+================
+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.
+
+++ /dev/null
-#!/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()
--- /dev/null
+from tools import *
--- /dev/null
+"""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
--- /dev/null
+# -*- 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 *
--- /dev/null
+#!/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 ''
+
--- /dev/null
+#!/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)
+
--- /dev/null
+#!/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')
+
+
--- /dev/null
+#!/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
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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)
--- /dev/null
+#!/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
--- /dev/null
+#!/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()
--- /dev/null
+#!/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
+
+
+
--- /dev/null
+#!/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()
--- /dev/null
+#!/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
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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)
+
+
--- /dev/null
+# -*- 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("<", "<")
+ 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("&", "&&")
+ # Escape any internal quotes
+ val = val.replace('"', '"').replace("'", "'")
+ # 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("<", "<").replace(">", ">")
+ 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("<", "<")
+
+ 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
+++ /dev/null
-#!/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 !
- """
-
--- /dev/null
+#!/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()
--- /dev/null
+#!/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()
+++ /dev/null
-"""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
+++ /dev/null
-# -*- 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 *
+++ /dev/null
-#!/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 ''
-
+++ /dev/null
-#!/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)
-
+++ /dev/null
-#!/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')
-
-
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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)
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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()
+++ /dev/null
-#!/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
-
-
-
+++ /dev/null
-#!/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()
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
-
+++ /dev/null
-#!/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)
-
-
+++ /dev/null
-# -*- 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("<", "<")
- 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("&", "&&")
- # Escape any internal quotes
- val = val.replace('"', '"').replace("'", "'")
- # 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("<", "<").replace(">", ">")
- 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("<", "<")
-
- 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