Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • aura/engine
  • hermannschwaerzler/engine
  • sumpfralle/aura-engine
3 results
Show changes
Commits on Source (24)
Showing
with 1326 additions and 0 deletions
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>
\ No newline at end of file
<component name="ProjectDictionaryState">
<dictionary name="gg" />
</component>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_EMOJI_URL" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>
\ No newline at end of file
<component name="MarkdownNavigator.ProfileManager">
<settings default="" pdf-export="" />
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.5.3 (/usr/bin/python3.5)" project-jdk-type="Python SDK" />
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/aura.iml" filepath="$PROJECT_DIR$/.idea/aura.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PreferredVcsStorage">
<preferredVcsName>ApexVCS</preferredVcsName>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
# AURA Engine
This piece of Software is part of 'AURA - AUtomated RAdio'.
AURA Engine does:
* requesting the programme from an external Source
* switches the soundserver at the correct time to a given source for a specific show
* records what is broadcasted
## Installation
### Software
#### Operating System
Any sound supporting linux system should work. It is tested and coded on a **debian stretch**
#### Packages
On a debian machine:
```bash
sudo apt install \
git \
python3 python3-pip \
redis-server redis-tools \
liquidsoap liquidsoap-plugin-alsa liquidsoap-plugin-flac liquidsoap-plugin-icecast liquidsoap-plugin-pulseaudio \
libev4 libev-dev \
mariadb-server libmariadbclient-dev
```
#### Python Packages
```
sudo pip3 install \
Flask Flask-Babel Flask-SQLAlchemy Flask-WTF \
mysqlclient redis simplejson
pyev python-dateutil
```
#### get the code
git clone https://gitlab.servus.at/autoradio/engine
#### Set Up a database
mysql -u root -p
CREATE DATABASE aura_engine CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'aura'@'localhost' IDENTIFIED BY 'secure-password';
GRANT ALL PRIVILEGES ON aura_engine.* TO 'aura'@'localhost';
#### Files
Create the audio folder defined in your aura.ini
mkdir /var/audio
mkdir /etc/aura
cp {where you cloned the repo}/configuration/aura.ini /etc/aura/aura.ini
edit installation dir and database settings in aura.ini
#### aura.py
It is the server which is connected to the external programme source, to liquidsoap and is listening for redis pubsub messages.
#### Guru
The commandline tool for interacting with the server.
#### Liquidsoap
The heart of AURA Engine. It uses the built in mixer, to switch between different sources. A source can be a stream, the filesystem or linein
### Hardware
#### Soundcard
AURA Engine ist tested with an ASUS Xonar DGX. It should work with every by ALSA supported soundcard. PulseAudio support is planned.
#### Hard/Soft
When you use ALSA, you will have to play around with ALSA settings. In the folder ./modules/liquidsoap is a scipt called alsa_settings_tester.liq. You can start it with 'liquidsoap -v --debug alsa_settings_tester.liq'. Changing and playing with settings can help you to find correct ALSA settings.
import signal
import sys
import threading
from libraries.base.config import ConfigReader
#from libraries.reporting.messenger import RedisListener
from modules.controller.controller import AuraController
#from modules.communication.zmq.zmqadapter import ServerZMQAdapter
from modules.communication.redis.adapter import ServerRedisAdapter
from modules.web.routes import Routes
class Aura():
server = None
config = None
messenger = None
controller = None
# ------------------------------------------------------------------------------------------ #
def __init__(self):
self.config = ConfigReader()
self.config.loadConfig()
server = object
self.controller = AuraController(self.config)
self.messenger = ServerRedisAdapter()
self.messenger.set_controller(self.controller)
self.messenger.set_config(self.config)
def receive_signal(signum, stack):
print("received signal")
server.reload()
signal.signal(signal.SIGUSR1, receive_signal)
def join_comm(self):
# start listener thread
self.messenger.start()
def start_web_service(self):
r = Routes()
#r.app.run()
# # ## ## ## ## ## # #
# # ENTRY FUNCTION # #
# # ## ## ## ## ## # #
def main():
aura = Aura()
aura.join_comm()
aura.start_web_service()
# # ## ## ## ## ## ## # #
# # End ENTRY FUNCTION # #
# # ## ## ## ## ## ## # #
if __name__ == "__main__":
main()
\ No newline at end of file
station_name="Radio FRO"
station_logo="/etc/aura/stationlogo.jpg"
station_fallback_pool="/var/audio/station_fallback_pool"
#Change this settings
daemongroup="gg"
daemonuser="gg"
scheduler_config_file="/etc/aura/scheduler.xml"
# SOUND CARD SETTINGS
line_in_count=1
line_out_count=1
input_device[0]="hw:0" # make it comma separated!!
output_device[0]="hw:0"
# DATABASE SETTINGS
db_user="aura"
db_name="aura"
db_pass="aura"
db_host="localhost"
# ALSA SETTINGS
# if you have no idea what to do here => set use_alsa to "n", then pulseaudio is used
use_alsa="y"
# alsa_buffer => int
alsa_buffer="16000"
# alsa_buffer_length => int
alsa_buffer_length="5"
# alsa_periods => int
alsa_periods="0"
# frame_duration => double
frame_duration="0.4"
# frame_size => int
frame_size=""
socketdir="/home/gg/PycharmProjects/engine/modules/liquidsoap"
logdir="/var/log/aura"
# channelnames for mixing
# leave this alone if you do not know what you are doing
http_channels="http,http2"
line_in_channels="live,live2"
filesystem_channels="filesystem"
adminmail="gogo@servus.at"
playlistdir="/var/audio/playlists/"
#calendarurl="http://localhost/index.php?option=com_jimtawl&view=calendar&format=json&from=#datefrom#&to=#dateto#"
calendarurl="http://bermudafunk-kalender.critmass.de/index.php?option=com_jimtawl&view=calendar&format=json&from=#datefrom#&to=#dateto#"
# how many days in future should the calendar be stored in database - default=7
#calendar_precache_days=7
audiobase="/var/audio/rec"
altaudiobase="/var/audio/preprod"
# hardware settings
# SOUNDCARD FROM STWST:
# HOME CARD
# was player_input_device:
# recinput="soundcard"
# altrecinput="soundcard"
# altrecorder_device="soundcard"
# recorder_device="soundcard"
# track_sensitive => fallback_folder track sensitivity
# max_blank => maximum time of blank from source
# min_noise => minimum duration of noise on source to switch back over
# threshold => power in dB under which the stream is considered silent
fallback_audio_folder="/var/audio/fallback/NightmaresOnWax/Smokers Delight"
fallback_max_blank="5"
fallback_min_noise="30"
fallback_threshold="-40"
stream="y"
stream_type="harbor"
#stream_type="icecast"
stream_bitrate="128"
stream_port="8000"
stream_mountpoint="aura"
stream_user="source"
stream_password="eegah5Hi"
stream_host="localhost"
stream_url="http://www.fro.at"
stream_name="Comba Test Stream"
stream_genre="mixed"
stream_description="Test Stream"
stream_admin_user="admin"
stream_admin_password="ahZ4caeg"
# ZeroMessagingQueue SETTINGS
communication="zmq"
zmqhostip="127.0.0.1"
zmqport="9099"
loglevel="info"
webservice_mode="apache"
#servername=""
#serviceport=""
install_dir="/home/gg/PycharmProjects/engine"
debug="y"
<Config>
<Jobs multiple="true">
<job>
<time>00:00</time>
<until>23:00</until>
<job>play_playlist</job>
<params>no_stop</params>
</job>
<job>
<job>start_recording</job>
<until>00:00</until>
<day>all</day>
<time>00:00</time>
<params>no_stop</params>
</job>
<job>
<daysolder>4</daysolder>
<job>clean_cached</job>
<day>1</day>
<time>00:03</time>
<params></params>
</job>
<job>
<time>01:00</time>
<day>all</day>
<job>precache</job>
<params></params>
</job>
</Jobs>
</Config>
{
"allData": {
"id": "01",
"00": "Global Metadata delivered",
"01": "Could not get Data from Sound Engine"
},
"channel_insert": {
"id": "02",
"00": "On Channel ::channel:: insert ::uri:: at position ::pos::",
"02": "On Channel ::channel:: could not insert ::uri:: at position ::pos::"
},
"channel_move": {
"id": "03",
"00": "On Channel ::channel:: moved Item from ::fromPos:: to position ::toPos::",
"01": "Warning: Position ::fromPos:: out of range",
"02": "Warning: Cannot move to same position",
"03": "On Channel ::channel:: could not move from position ::fromPos:: to position ::toPos::"
},
"channel_off": {
"id": "04",
"00": "Channel ::channel:: off",
"01": "Could not activate Channel ::channel::"
},
"channel_on": {
"id": "05",
"00": "Channel ::channel:: on",
"01": "Could not deactivate Channel ::channel::"
},
"channel_queue": {
"id": "06",
"00": "Channel Queue for ::channel:: delivered",
"01": "Could not get channel queue from channel ::channel::",
"02": "Could not get channel queue from channel ::channel::",
"03": "Could not get channel queue from channel ::channel::"
},
"channel_remove": {
"id": "07",
"00": "Removed item on position ::pos:: from channel ::channel::",
"01": "Could not remove item on position ::pos:: from channel ::channel::",
"02": "Warning: position ::pos:: out of range'"
},
"channel_seek": {
"id": "08",
"00": "Seeked channel ::channel:: ::duration:: seconds",
"01": "Could not seek channel ::channel:: ::duration:: seconds"
},
"channel_skip": {
"id": "09",
"00": "Skipped channel ::channel::",
"01": "0 Channels listed",
"02": "Could not get channels from sound engine",
"03": "Could not skip ::channel::"
},
"channel_volume": {
"id": "10",
"00": "Volume ::volume::% set on channel ::channel::",
"01": "Could not set volume to ::volume::% on channel ::channel::",
"02": "0 Channels listed",
"03": "Could not get channels from sound engine"
},
"currentData": {
"id": "11",
"00": "Current track metadata delivered",
"01": "Nothing seems to be on air",
"02": "Could not detect metadata"
},
"help": {
"id": "12",
"00": "none",
"01": "Could not open help file"
},
"listChannels": {
"id": "13",
"00": "Listed Channels",
"01": "0 Channels listed",
"02": "Could not get channels from sound engine"
},
"message": {
"id": "14",
"00": "none"
},
"playlist_data": {
"id": "15",
"00": "Playlist data delivered"
},
"playlist_flush": {
"id": "16",
"00": "Flushed playlist",
"01": "Could not flush playlist"
},
"playlist_insert":{
"id": "17",
"00": "Insert track ::uri:: on position ::pos::"
},
"playlist_load": {
"id": "18",
"00": "Load Playlist ::uri::",
"01": "Could not load Playlist ::uri::",
"02": "Playlist is not well formed XML"
},
"playlist_move": {
"id": "19",
"00": "Moved playlist track from position ::fromPos:: to ::toPos::"
},
"playlist_pause": {
"id": "20",
"00": "Playlist paused",
"01": "Playlist already paused"
},
"playlist_stop": {
"id": "21",
"00": "Playlist stopped",
"01": "Playlist already stopped"
},
"playlist_play": {
"id": "22",
"00": "Playlist started",
"01": "Playlist already playing",
"02": "0 Channels listed",
"03": "Could not get channels from sound engine"
},
"playlist_push": {
"id": "23",
"00": "Playlist: pushed ::uri::",
"01": "Could not push ::uri::"
},
"playlist_remove":{
"id": "24",
"00": "Removed track on position ::pos:: from playlist",
"01": "Could not remove track on position ::pos:: from playlist"
},
"playlist_seek": {
"id": "25",
"00": "Seeked playlist ::duration:: seconds",
"01": "Could not seek playlist ::duration:: seconds"
},
"playlist_skip": {
"id": "26",
"00": "Skipped playlist",
"00": "Could not skip playlist"
},
"recorder_data": {
"id": "27",
"00": "Delivered recorder data",
"01": "Could not deliver recorder data"
},
"recorder_start": {
"id": "28",
"00": "Recorder started",
"01": "Could not start recorder"
},
"recorder_stop": {
"id": "29",
"00": "Recorder stopped",
"01": "Could not stop recorder"
},
"scheduler_reload": {
"id": "30",
"00": "Reload signal was sent to scheduler",
"01": "Could not find the scheduler process"
},
"sendLqcCommand": {
"id": "31",
"01": "Soundengine not running",
"02": "Recorder not running"
},
"get_channel_state" : {
"id": "32",
"00": "Channels ::channel:: state",
"01": "Could not get channel state from channel ::channel::"
},
"setPassword": {
"id": "33",
"00": "Successfull set password",
"01": "Not enough access rights for this operation"
},
"addUser": {
"id": "34",
"00": "Successfull add user ::username::",
"01": "Not enough access rights for this operation"
},
"delUser": {
"id": "35",
"00": "Successfull removed user ::username::",
"01": "Not enough access rights for this operation"
},
"scheduler_data": {
"id": "36",
"00": "Successfull delivered scheduler config",
"01": "Scheduler config seems to be broken"
},
"scheduler_store": {
"id": "37",
"00": "Successfull stored scheduler config",
"01": "Not enough access rights for this operation",
"02": "Could not store a valid scheduler XML"
},
"getUserlist": {
"id": "38",
"00": "Userlist was successfully delivered",
"01": "Not enough access rights for this operation"
},
"get_act_programme": {
"id": "39",
"00": "Successfully fetched the program",
"01": "Cannot fetch actual program"
}
}
{
"exec_job": {
"id": "01",
"00": "Execute job ::job::",
"01": "Fatal: Could not execute job ::job::. Command ::exec:: results in Exception ::Exception::. Stopped watcher"
},
"schedule_job": {
"id": "02",
"00": "Scheduled job ::job:: for ::scheduled_for:: at ::scheduled_at::",
"01": "Could not execute job"
},
"load_playlist": {
"id": "03",
"00": "Load playlist ::uri::",
"01": "Could not load playlist ::uri::. File does not exist!",
"02": "Controller failed to load playlist ::uri::. Message was '::message::'"
},
"play_playlist": {
"id": "04",
"00": "Started playlist",
"01": "Controller failed to start playlist. Message was '::message::'"
},
"stop_playlist": {
"id": "05",
"00": "Started playlist",
"01": "Controller failed to start playlist. Message was '::message::'"
},
"start_recording": {
"id": "06",
"00": "Started recording",
"01": "Controller failed to start recording. Message was '::message::'"
},
"stop_recording": {
"id": "07",
"00": "Stopped recording",
"01": "Controller failed to stop recording. Message was '::message::'" },
"precache": {
"id": "08",
"00": "Precached playlists",
"01": "Could not precache playlist."
},
"clean_cached": {
"id": "09",
"00": "Cleaned cache",
"01": "Could not clean cache"
},
"on_start": {
"id": "10",
"00": "Do initial jobs",
"01": "Could not do initial jobs"
},
"lookup_prearranged": {
"id": "11",
"00": "Lookup for prearranged tracks",
"01": "No system channel available"
},
"start_prearranged": {
"id": "12",
"00": "Started preaarranged tracks"
},
"end_prearranged": {
"id": "13",
"00": "Stopped preaarranged tracks"
}
}
#!/usr/bin/python3
import time
import sys
import redis
from argparse import ArgumentParser
# own libs
from libraries.base.config import ConfigReader
from modules.tools.padavan import Padavan
from libraries.exceptions.auraexceptions import FallbackException
class Guru:
config = ConfigReader()
config.loadConfig()
# ------------------------------------------------------------------------------------------ #
def __init__(self):
try:
parser = ArgumentParser()
# options
parser.add_argument("-sep", "--stop-execution-time", action="store_true", dest="stoptime", default=False,
help="Prints the execution time at the end of the skript")
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False,
help="Just the result will outputed to stout")
# getter
parser.add_argument("-gam", "--get-active-mixer", action="store_true", dest="get_active_mixer", default=False,
help="Which mixer is activated?")
parser.add_argument("-pms", "--print-mixer-status", action="store_true", dest="get_mixer_status", default=False,
help="Prints all mixer sources and their states")
parser.add_argument("-pap", "--print-act-programme", action="store_true", dest="get_act_programme", default=False,
help="Prints the actual Programme, the controller holds")
# liquid manipulation
parser.add_argument("-am", "--select-mixer", action="store", dest="select_mixer", default=-1, metavar="MIXERNUM",
help="Which mixer should be activated?", type=int)
parser.add_argument("-dm", "--de-select-mixer", action="store", dest="deselect_mixer", default=-1, metavar="MIXERNUM",
help="Which mixer should be activated?", type=int)
parser.add_argument("-vm", "--volume", action="store", dest="set_volume", default=0, metavar=("MIXERNUM","VOLUME"), nargs=2,
help="Set volume of a mixer source", type=int)
#parser.add_argument("-as", "--add-source", action="store", dest="add_source", default=False,
# help="Add new source to LiquidSoap mixer [Experimental]")
# playlist manipulation
parser.add_argument("-fnp", "--fetch-new-programmes", action="store_true", dest="fetch_new_programme",
default=False, help="Fetch new programmes from calendarurl in comba.ini")
parser.add_argument("-spe", "--swap-playlist-entries", action="store", dest="swap_playlist_entries", default=0, metavar=("FROM", "TO"), nargs=2,
help="Swaps two Playlistentries")
parser.add_argument("-dpe", "--delete-playlist-entry", action="store", dest="delete_playlist_entry", default=0, metavar="INDEX",
help="Delete Playlistentry at INDEX")
parser.add_argument("-ipe", "--insert-playlist-entry", action="store", dest="insert_playlist_entry", default=0, metavar=("FROMTIME", "SOURCE"), nargs=2,
help="Add a new Playlistentry at a given index") # , type=valid_playlist_entry)
parser.add_argument("-pmq", "--print-message-queue", action="store_true", dest="print_message_queue", default=False,
help="Prints message queue")
# send a redis message
parser.add_argument("-rm", "--redis-message", action="store", dest="redis_message", default=False, metavar=("CHANNEL", "MESSAGE"), nargs=2,
help="Send a redis message to the Listeners")
# calls from liquidsoap
parser.add_argument("-gnf", "--get-next-fallback-file-for", action="store", dest="get_fallback_for", default=False, metavar="FALLBACKTYPE",
help="For which type you wanna GET a next audio file?")
parser.add_argument("-snf", "--set-next-fallback-file-for", action="store", dest="set_fallback_for", default=False, metavar=("FALLBACKTYPE", "FILE"), nargs=2,
help="For which type you wanna SET a next audio file?")
parser.add_argument("-ip", "--init-player", action="store_true", dest="init_player", default=False,
help="Reset liquidsoap volume and mixer activations?")
args = parser.parse_args()
if len(sys.argv) == 1:
raise ValueError("No Argument passed!")
except ValueError as e:
parser.print_help()
print()
print(e)
exit(1)
except TypeError as e:
parser.print_help()
print()
print(e)
exit(2)
if args.stoptime:
start = time.time()
if not args.quiet:
print("Guru thinking...")
try:
p = Padavan(args, self.config)
p.meditate()
except FallbackException as fe:
print(fe)
exit(4)
except redis.exceptions.TimeoutError as te:
print(te)
print("Timeout when waiting for redis message. Is AURA daemon running? Exiting...")
exit(3)
if not args.quiet:
print("...result: ")
if p.stringreply != "":
if p.stringreply[len(p.stringreply)-1] == "\n":
print(p.stringreply[0:len(p.stringreply) - 1])
else:
print(p.stringreply[0:len(p.stringreply)])
if args.stoptime:
end = time.time()
exectime = end-start
print("execution time: "+str(exectime)+"s")
def valid_playlist_entry(argument):
from datetime import datetime
import argparse
try:
index = int(argument[0])
fromtime = datetime.strptime(argument[1], "%Y-%m-%d")
source = argument[2]
return index, fromtime, source
except:
msg = "Not a valid date: '{0}'.".format(argument[0])
raise
# # ## ## ## ## ## # #
# # ENTRY FUNCTION # #
# # ## ## ## ## ## # #
def main():
Guru()
# # ## ## ## ## ## ## # #
# # End ENTRY FUNCTION # #
# # ## ## ## ## ## ## # #
if __name__ == "__main__":
main()
This diff is collapsed.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# combabase.py
#
# Copyright 2014 BFR <info@freie-radios.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; Version 3 of the License
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, the license can be downloaded here:
#
# http://www.gnu.org/licenses/gpl.html
# Meta
__version__ = '0.1.1'
__license__ = "GNU General Public License (GPL) Version 3"
__version_info__ = (0, 1, 1)
__author__ = 'Michael Liebler <michael-liebler@radio-z.net>'
# massively enhanced by Gottfried Gaisbauer <gottfried.gaisbauer@servus.at>
"""
Comba Base Class - lade Config
"""
import os
import sys
#import StringIO
#import ConfigParser
import socket
from io import StringIO
from configparser import RawConfigParser
from configparser import ConfigParser
class ConfigReader(object):
def set(self, key, value):
"""
Eine property setzen
@type key: string
@param key: Der Key
@type value: mixed
@param value: Beliebiger Wert
"""
if(key == "securitylevel"):
self.__dict__[key] = int(value)
else:
self.__dict__[key] = value
# ------------------------------------------------------------------------------------------ #
def get(self, key, default=None):
"""
Eine property holen
@type key: string
@param key: Der Key
@type default: mixed
@param default: Beliebiger Wert
"""
if key not in self.__dict__:
if default:
self.set(key, default)
else:
print("WARNING: Key "+key+" not found!")
return None
if key == "debug":
return self.__dict__[key].count("y")
return self.__dict__[key]
# ------------------------------------------------------------------------------------------ #
def loadConfig(self):
"""
Set config defaults and load settings from file
:return:
"""
ini_path = self.get('configpath', '/etc/aura/aura.ini')
if not os.path.isfile(ini_path):
print(ini_path + " not found :(")
sys.exit(1)
# INI einlesen
f = open(ini_path, 'r')
ini_str = '[root]\n' + f.read()
f.close()
self.configDefaults = {
'secondspertrack' : '1800',
'audiobase' : '/var/audio/rec',
'altaudiobase' : '/var/audio/preprod',
'calendarurl' : 'http://localhost/index.php?option=com_jimtawl&view=calendar&format=json&from=#datefrom#&to=#dateto#',
'communication': 'zmq',
'playlistdir': '/var/audio/playlists',
'archivebase': '/var/audio/archive/',
'controllerport': '9099',
'logdir' : '/var/log/comba',
'loglevel': 'info',
'securitylevel': '0',
'adminmail': '',
'frommail': '',
'calendar_precache_days': '7',
'servername': socket.getfqdn(),
'serviceport': '8080',
'stream' : "",
'stream_type' : "",
'stream_host' : "",
'stream_port' : "",
'stream_mountpoint' : "",
'stream_admin_user' : "",
'stream_admin_password' : "",
}
# readfp is deprecated since 3.2
if sys.version_info <= (3, 2):
ini_str = StringIO(ini_str)
config = RawConfigParser(self.configDefaults)
config.readfp(ini_str)
else:
config = ConfigParser(self.configDefaults)
config.read_string(ini_str)
for key, value in config.items('root'):
self.set(key, config.get('root', key).replace('"', '').strip())
#!/usr/bin/env python
"Makes working with XML feel like you are working with JSON"
from xml.parsers import expat
from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesImpl
try: # pragma no cover
from cStringIO import StringIO
except ImportError: # pragma no cover
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
try: # pragma no cover
from collections import OrderedDict
except ImportError: # pragma no cover
try:
from ordereddict import OrderedDict
except ImportError:
OrderedDict = dict
try: # pragma no cover
_basestring = str #basestring
except NameError: # pragma no cover
_basestring = bytes #str
try: # pragma no cover
_unicode = str #unicode
except NameError: # pragma no cover
_unicode = bytes #str
__author__ = 'Martin Blech'
__version__ = '0.9.0'
__license__ = 'MIT'
class ParsingInterrupted(Exception):
pass
class _DictSAXHandler(object):
def __init__(self,
item_depth=0,
item_callback=lambda *args: True,
xml_attribs=True,
attr_prefix='@',
cdata_key='#text',
force_cdata=False,
cdata_separator='',
postprocessor=None,
dict_constructor=OrderedDict,
strip_whitespace=True,
namespace_separator=':',
namespaces=None):
self.path = []
self.stack = []
self.data = None
self.item = None
self.item_depth = item_depth
self.xml_attribs = xml_attribs
self.item_callback = item_callback
self.attr_prefix = attr_prefix
self.cdata_key = cdata_key
self.force_cdata = force_cdata
self.cdata_separator = cdata_separator
self.postprocessor = postprocessor
self.dict_constructor = dict_constructor
self.strip_whitespace = strip_whitespace
self.namespace_separator = namespace_separator
self.namespaces = namespaces
def _build_name(self, full_name):
if not self.namespaces:
return full_name
i = full_name.rfind(self.namespace_separator)
if i == -1:
return full_name
namespace, name = full_name[:i], full_name[i+1:]
short_namespace = self.namespaces.get(namespace, namespace)
if not short_namespace:
return name
else:
return self.namespace_separator.join((short_namespace, name))
def _attrs_to_dict(self, attrs):
if isinstance(attrs, dict):
return attrs
return self.dict_constructor(zip(attrs[0::2], attrs[1::2]))
def startElement(self, full_name, attrs):
name = self._build_name(full_name)
attrs = self._attrs_to_dict(attrs)
self.path.append((name, attrs or None))
if len(self.path) > self.item_depth:
self.stack.append((self.item, self.data))
if self.xml_attribs:
attrs = self.dict_constructor(
(self.attr_prefix+key, value)
for (key, value) in attrs.items())
else:
attrs = None
self.item = attrs or None
self.data = None
def endElement(self, full_name):
name = self._build_name(full_name)
if len(self.path) == self.item_depth:
item = self.item
if item is None:
item = self.data
should_continue = self.item_callback(self.path, item)
if not should_continue:
raise ParsingInterrupted()
if len(self.stack):
item, data = self.item, self.data
self.item, self.data = self.stack.pop()
if self.strip_whitespace and data is not None:
data = data.strip() or None
if data and self.force_cdata and item is None:
item = self.dict_constructor()
if item is not None:
if data:
self.push_data(item, self.cdata_key, data)
self.item = self.push_data(self.item, name, item)
else:
self.item = self.push_data(self.item, name, data)
else:
self.item = self.data = None
self.path.pop()
def characters(self, data):
if not self.data:
self.data = data
else:
self.data += self.cdata_separator + data
def push_data(self, item, key, data):
if self.postprocessor is not None:
result = self.postprocessor(self.path, key, data)
if result is None:
return item
key, data = result
if item is None:
item = self.dict_constructor()
try:
value = item[key]
if isinstance(value, list):
value.append(data)
else:
item[key] = [value, data]
except KeyError:
item[key] = data
return item
def parsexml(xml_input, encoding=None, expat=expat, process_namespaces=False,
namespace_separator=':', **kwargs):
"""Parse the given XML input and convert it into a dictionary.
`xml_input` can either be a `string` or a file-like object.
If `xml_attribs` is `True`, element attributes are put in the dictionary
among regular child elements, using `@` as a prefix to avoid collisions. If
set to `False`, they are just ignored.
Simple example::
>>> import xmltodict
>>> doc = xmltodict.parse(\"\"\"
... <a prop="x">
... <b>1</b>
... <b>2</b>
... </a>
... \"\"\")
>>> doc['a']['@prop']
u'x'
>>> doc['a']['b']
[u'1', u'2']
If `item_depth` is `0`, the function returns a dictionary for the root
element (default behavior). Otherwise, it calls `item_callback` every time
an item at the specified depth is found and returns `None` in the end
(streaming mode).
The callback function receives two parameters: the `path` from the document
root to the item (name-attribs pairs), and the `item` (dict). If the
callback's return value is false-ish, parsing will be stopped with the
:class:`ParsingInterrupted` exception.
Streaming example::
>>> def handle(path, item):
... print 'path:%s item:%s' % (path, item)
... return True
...
>>> xmltodict.parse(\"\"\"
... <a prop="x">
... <b>1</b>
... <b>2</b>
... </a>\"\"\", item_depth=2, item_callback=handle)
path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:1
path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:2
The optional argument `postprocessor` is a function that takes `path`,
`key` and `value` as positional arguments and returns a new `(key, value)`
pair where both `key` and `value` may have changed. Usage example::
>>> def postprocessor(path, key, value):
... try:
... return key + ':int', int(value)
... except (ValueError, TypeError):
... return key, value
>>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>',
... postprocessor=postprocessor)
OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))])
You can pass an alternate version of `expat` (such as `defusedexpat`) by
using the `expat` parameter. E.g:
>>> import defusedexpat
>>> xmltodict.parse('<a>hello</a>', expat=defusedexpat.pyexpat)
OrderedDict([(u'a', u'hello')])
"""
handler = _DictSAXHandler(namespace_separator=namespace_separator,
**kwargs)
if isinstance(xml_input, _unicode):
if not encoding:
encoding = 'utf-8'
xml_input = xml_input.encode(encoding)
if not process_namespaces:
namespace_separator = None
parser = expat.ParserCreate(
encoding,
namespace_separator
)
try:
parser.ordered_attributes = True
except AttributeError:
# Jython's expat does not support ordered_attributes
pass
parser.StartElementHandler = handler.startElement
parser.EndElementHandler = handler.endElement
parser.CharacterDataHandler = handler.characters
parser.buffer_text = True
try:
parser.ParseFile(xml_input)
except (TypeError, AttributeError):
parser.Parse(xml_input, True)
return handler.item
def _emit(key, value, content_handler,
attr_prefix='@',
cdata_key='#text',
depth=0,
preprocessor=None,
pretty=False,
newl='\n',
indent='\t'):
if preprocessor is not None:
result = preprocessor(key, value)
if result is None:
return
key, value = result
if not isinstance(value, (list, tuple)):
value = [value]
if depth == 0 and len(value) > 1:
raise ValueError('document with multiple roots')
for v in value:
if v is None:
v = OrderedDict()
elif not isinstance(v, dict):
v = _unicode(v)
if isinstance(v, _basestring):
v = OrderedDict(((cdata_key, v),))
cdata = None
attrs = OrderedDict()
children = []
for ik, iv in v.items():
if ik == cdata_key:
cdata = iv
continue
if ik.startswith(attr_prefix):
attrs[ik[len(attr_prefix):]] = iv
continue
children.append((ik, iv))
if pretty:
content_handler.ignorableWhitespace(depth * indent)
content_handler.startElement(key, AttributesImpl(attrs))
if pretty and children:
content_handler.ignorableWhitespace(newl)
for child_key, child_value in children:
_emit(child_key, child_value, content_handler,
attr_prefix, cdata_key, depth+1, preprocessor,
pretty, newl, indent)
if cdata is not None:
content_handler.characters(cdata)
if pretty and children:
content_handler.ignorableWhitespace(depth * indent)
content_handler.endElement(key)
if pretty and depth:
content_handler.ignorableWhitespace(newl)
def unparse(input_dict, output=None, encoding='utf-8', full_document=True, **kwargs):
"""Emit an XML document for the given `input_dict` (reverse of `parse`).
The resulting XML document is returned as a string, but if `output`
(afile-like object) is specified, it is written there instead.
Dictionary keys prefixed with `attr_prefix` (default=`'@'`) are interpreted
as XML node attributes, whereas keys equal to `cdata_key`
(default=`'#text'`) are treated as character data.
The `pretty` parameter (default=`False`) enables pretty-printing. In this
mode, lines are terminated with `'\n'` and indented with `'\t'`, but this
can be customized with the `newl` and `indent` parameters.
"""
((key, value),) = input_dict.items()
must_return = False
if output is None:
output = StringIO()
must_return = True
content_handler = XMLGenerator(output, encoding)
if full_document:
content_handler.startDocument()
_emit(key, value, content_handler, **kwargs)
if full_document:
content_handler.endDocument()
if must_return:
value = output.getvalue()
try: # pragma no cover
value = value.decode(encoding)
except AttributeError: # pragma no cover
pass
return value
if __name__ == '__main__': # pragma: no cover
import sys
import marshal
(item_depth,) = sys.argv[1:]
item_depth = int(item_depth)
def handle_item(path, item):
marshal.dump((path, item), sys.stdout)
return True
try:
root = parsexml(sys.stdin,
item_depth=item_depth,
item_callback=handle_item,
dict_constructor=dict)
if item_depth == 0:
handle_item([], root)
except KeyboardInterrupt:
pass