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