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 2596 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
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BookmarkManager">
<bookmark url="file://$PROJECT_DIR$/guru.py" line="135" mnemonic="8" />
<bookmark url="file://$PROJECT_DIR$/modules/scheduling/scheduler.py" line="258" mnemonic="1" />
</component>
<component name="ChangeListManager">
<list default="true" id="c58ca86a-4167-4474-accb-41439a535514" name="Default" comment="">
<change beforePath="" afterPath="$PROJECT_DIR$/libraries/exceptions/exception_logger.py" />
<change beforePath="" afterPath="$PROJECT_DIR$/test.py" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
<change beforePath="$PROJECT_DIR$/configuration/aura.ini" afterPath="$PROJECT_DIR$/configuration/aura.ini" />
<change beforePath="$PROJECT_DIR$/guru.py" afterPath="$PROJECT_DIR$/guru.py" />
<change beforePath="$PROJECT_DIR$/libraries/base/calendar.py" afterPath="$PROJECT_DIR$/libraries/base/calendar.py" />
<change beforePath="$PROJECT_DIR$/libraries/database/broadcasts.py" afterPath="$PROJECT_DIR$/libraries/database/broadcasts.py" />
<change beforePath="$PROJECT_DIR$/libraries/database/database.py" afterPath="$PROJECT_DIR$/libraries/database/database.py" />
<change beforePath="$PROJECT_DIR$/libraries/database/statestore.py" afterPath="$PROJECT_DIR$/libraries/database/statestore.py" />
<change beforePath="$PROJECT_DIR$/modules/communication/liquidsoap/communicator.py" afterPath="$PROJECT_DIR$/modules/communication/liquidsoap/communicator.py" />
<change beforePath="$PROJECT_DIR$/modules/liquidsoap/simplestmixer.log" afterPath="$PROJECT_DIR$/modules/liquidsoap/simplestmixer.log" />
<change beforePath="$PROJECT_DIR$/modules/scheduling/scheduler.py" afterPath="$PROJECT_DIR$/modules/scheduling/scheduler.py" />
<change beforePath="$PROJECT_DIR$/modules/tools/padavan.py" afterPath="$PROJECT_DIR$/modules/tools/padavan.py" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file leaf-file-name="controller.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/modules/controller/controller.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1377">
<caret line="96" column="59" lean-forward="false" selection-start-line="96" selection-start-column="58" selection-end-line="96" selection-end-column="59" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="broadcasts.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/libraries/database/broadcasts.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4522">
<caret line="272" column="36" lean-forward="false" selection-start-line="272" selection-start-column="36" selection-end-line="272" selection-end-column="36" />
<folding>
<element signature="e#43#75#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="database.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/libraries/database/database.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="323">
<caret line="24" column="52" lean-forward="false" selection-start-line="24" selection-start-column="52" selection-end-line="24" selection-end-column="52" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="scheduler.py" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/modules/scheduling/scheduler.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="394">
<caret line="579" column="0" lean-forward="true" selection-start-line="579" selection-start-column="0" selection-end-line="579" selection-end-column="0" />
<folding>
<element signature="e#1093#1106#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="statestore.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/libraries/database/statestore.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4777">
<caret line="286" column="0" lean-forward="false" selection-start-line="286" selection-start-column="0" selection-end-line="286" selection-end-column="0" />
<folding>
<element signature="e#25#37#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="guru.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/guru.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="18" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="18" />
<folding>
<element signature="e#20#31#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="test.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/test.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="306">
<caret line="20" column="40" lean-forward="false" selection-start-line="20" selection-start-column="40" selection-end-line="20" selection-end-column="40" />
<folding>
<element signature="e#20#63#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="calendar.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/libraries/base/calendar.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="3672">
<caret line="227" column="29" lean-forward="false" selection-start-line="227" selection-start-column="29" selection-end-line="227" selection-end-column="29" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="padavan.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/modules/tools/padavan.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2074">
<caret line="125" column="12" lean-forward="false" selection-start-line="125" selection-start-column="12" selection-end-line="125" selection-end-column="12" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>get_act_programme</find>
<find>store(</find>
<find>ini</find>
<find>def warning</find>
<find>schedule is</find>
<find>alchemyenc</find>
<find>asd</find>
<find>warning(</find>
<find>alchemyen</find>
<find>simplejson</find>
<find>json.</find>
<find>alchemyencoder</find>
<find>import json</find>
<find>encod</find>
<find>printing i</find>
<find>load_</find>
<find>alchemye</find>
<find>swap</find>
<find>asdict</find>
<find>relations</find>
<find>band-aid</find>
<find>db.session.commit</find>
<find>relation</find>
<find>select_all</find>
<find>init db</find>
<find>haveto</find>
<find>havetoad</find>
<find>params</find>
<find>holding the</find>
<find>init__</find>
</findStrings>
<replaceStrings>
<replace>&quot;</replace>
</replaceStrings>
<dirStrings>
<dir>$PROJECT_DIR$/modules/liquidsoap</dir>
</dirStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/liquidcontroller.py" />
<option value="$PROJECT_DIR$/errormessages/controller_error.js" />
<option value="$PROJECT_DIR$/configuration/scheduler.xml" />
<option value="$PROJECT_DIR$/libraries/exceptions/auraexceptions" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/InitThread.py" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/LiquidSoapPlayerClient.py" />
<option value="$PROJECT_DIR$/modules/liquidsoap/serversettings.liq" />
<option value="$PROJECT_DIR$/modules/liquidsoap/helpers/nextfrompool.py" />
<option value="$PROJECT_DIR$/modules/liquidsoap/helpers/nextfromfallback.py" />
<option value="$PROJECT_DIR$/modules/liquidsoap/helpers/message.py" />
<option value="$PROJECT_DIR$/modules/liquidsoap/settings.liq" />
<option value="$PROJECT_DIR$/modules/liquidsoap/serverfunctions.liq" />
<option value="$PROJECT_DIR$/modules/communication/zmq/zmqadapter.py" />
<option value="$PROJECT_DIR$/modules/liquidsoap/simpleplayer.liq" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/LiquidSoapInitThread.py" />
<option value="$PROJECT_DIR$/libraries/reporting/statestore.py" />
<option value="$PROJECT_DIR$/libraries/exceptions/auraexceptions.py" />
<option value="$PROJECT_DIR$/libraries/reporting/messenger.py" />
<option value="$PROJECT_DIR$/libraries/color/terminal.py" />
<option value="$PROJECT_DIR$/libraries/color/console.py" />
<option value="$PROJECT_DIR$/libraries/base/config.py" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/LiquidSoapClient.py" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/LiquidSoapCommunicator.py" />
<option value="$PROJECT_DIR$/modules/communication/redis/redisadapter.py" />
<option value="$PROJECT_DIR$/libraries/client/auraclient.py" />
<option value="$PROJECT_DIR$/modules/liquidsoap/problem.liq" />
<option value="$PROJECT_DIR$/libraries/enum/consolecolor.py" />
<option value="$PROJECT_DIR$/libraries/database/combadb.py" />
<option value="$PROJECT_DIR$/modules/liquidsoap/fallback.liq" />
<option value="$PROJECT_DIR$/modules/liquidsoap/simplestmixer.liq" />
<option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/client.py" />
<option value="$PROJECT_DIR$/libraries/enum/redischannels.py" />
<option value="$PROJECT_DIR$/modules/communication/redis/messenger.py" />
<option value="$PROJECT_DIR$/modules/communication/redis/adapter.py" />
<option value="$PROJECT_DIR$/modules/web/routes.py" />
<option value="$PROJECT_DIR$/aura.py" />
<option value="$PROJECT_DIR$/libraries/database/trackservice.py" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/initthread.py" />
<option value="$PROJECT_DIR$/modules/controller/controller.py" />
<option value="$PROJECT_DIR$/configuration/aura.ini" />
<option value="$PROJECT_DIR$/libraries/exceptions/exception_logger.py" />
<option value="$PROJECT_DIR$/modules/communication/liquidsoap/communicator.py" />
<option value="$PROJECT_DIR$/libraries/database/statestore.py" />
<option value="$PROJECT_DIR$/guru.py" />
<option value="$PROJECT_DIR$/modules/tools/padavan.py" />
<option value="$PROJECT_DIR$/test.py" />
<option value="$PROJECT_DIR$/libraries/database/database.py" />
<option value="$PROJECT_DIR$/libraries/database/broadcasts.py" />
<option value="$PROJECT_DIR$/libraries/base/calendar.py" />
<option value="$PROJECT_DIR$/modules/scheduling/scheduler.py" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="645" />
<option name="y" value="31" />
<option name="width" value="971" />
<option name="height" value="1000" />
</component>
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id />
</State>
</expanded-state>
<selected-state>
<State>
<id>Properties Files</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
<manualOrder />
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scratches" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="engine" type="b2602c69:ProjectViewProjectNode" />
<item name="engine" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="engine" type="b2602c69:ProjectViewProjectNode" />
<item name="engine" type="462c0819:PsiDirectoryNode" />
<item name="libraries" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="engine" type="b2602c69:ProjectViewProjectNode" />
<item name="engine" type="462c0819:PsiDirectoryNode" />
<item name="libraries" type="462c0819:PsiDirectoryNode" />
<item name="database" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="engine" type="b2602c69:ProjectViewProjectNode" />
<item name="engine" type="462c0819:PsiDirectoryNode" />
<item name="modules" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="engine" type="b2602c69:ProjectViewProjectNode" />
<item name="engine" type="462c0819:PsiDirectoryNode" />
<item name="modules" type="462c0819:PsiDirectoryNode" />
<item name="controller" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="engine" type="b2602c69:ProjectViewProjectNode" />
<item name="engine" type="462c0819:PsiDirectoryNode" />
<item name="modules" type="462c0819:PsiDirectoryNode" />
<item name="scheduling" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="settings.editor.selected.configurable" value="Errors" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="settings.editor.splitter.proportion" value="0.2759277" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/libraries/client" />
<recent name="$PROJECT_DIR$/errormessages" />
<recent name="$PROJECT_DIR$/libraries/base" />
<recent name="$PROJECT_DIR$/modules/scheduling/error" />
<recent name="$PROJECT_DIR$/modules/communication/scheduling" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/libraries/enum" />
<recent name="$PROJECT_DIR$/libraries/database" />
<recent name="$PROJECT_DIR$/modules/communication/redis" />
<recent name="$PROJECT_DIR$/modules/communication/liquidsoap" />
<recent name="$PROJECT_DIR$/modules/communication/zmq" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager" selected="Python.aura">
<configuration default="true" type="PythonConfigurationType" factoryName="Python">
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="/usr/bin/python3.5" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<module name="aura" />
<option name="SCRIPT_NAME" value="" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
</configuration>
<configuration name="aura" type="PythonConfigurationType" factoryName="Python" temporary="true">
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="/usr/bin/python3.5" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<module name="aura" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/aura.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
</configuration>
<configuration name="guru" type="PythonConfigurationType" factoryName="Python" temporary="true">
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<module name="aura" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/guru.py" />
<option name="PARAMETERS" value="-pap" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
</configuration>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="Python.guru" />
<item index="1" class="java.lang.String" itemvalue="Python.aura" />
</list>
<recent_temporary>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="Python.aura" />
<item index="1" class="java.lang.String" itemvalue="Python.guru" />
</list>
</recent_temporary>
</component>
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="c58ca86a-4167-4474-accb-41439a535514" name="Default" comment="" />
<created>1509437402378</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1509437402378</updated>
</task>
<task id="LOCAL-00001" summary="verbindung zwischen guru und auraserver bei fetchNewProgrammes">
<created>1510515005716</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1510515005716</updated>
</task>
<task id="LOCAL-00002" summary="fetch new programmes and print act programme">
<created>1510609939051</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1510609939051</updated>
</task>
<option name="localTasksCounter" value="3" />
<servers />
</component>
<component name="ToolWindowManager">
<frame x="645" y="31" width="1926" height="1052" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.16640337" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.46312365" sideWeight="0.5018431" order="13" side_tool="true" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32936078" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="true" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.30802602" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.46312365" sideWeight="0.49815693" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Debug Logs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Execute Anonymous" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.43275487" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Data View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Salesforce" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" />
</layout>
<layout-to-restore>
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Debug Logs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.46312365" sideWeight="0.5018431" order="13" side_tool="true" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33188722" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32936078" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
<window_info id="Execute Anonymous" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.16692996" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.43275487" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.46312365" sideWeight="0.49815693" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Data View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Salesforce" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" />
</layout-to-restore>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="verbindung zwischen guru und auraserver bei fetchNewProgrammes" />
<MESSAGE value="fetch new programmes and print act programme" />
<option name="LAST_COMMIT_MESSAGE" value="fetch new programmes and print act programme" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/modules/scheduling/scheduler.py</url>
<line>414</line>
<option name="timeStamp" value="35" />
</line-breakpoint>
</breakpoints>
<breakpoints-dialog>
<breakpoints-dialog />
</breakpoints-dialog>
<option name="time" value="38" />
</breakpoint-manager>
<watches-manager />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/helpers/nextfromfallback.py" />
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/readini.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="272">
<caret line="16" column="12" lean-forward="false" selection-start-line="16" selection-start-column="12" selection-end-line="16" selection-end-column="12" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/pulse.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/playd.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="3281">
<caret line="193" column="0" lean-forward="false" selection-start-line="193" selection-start-column="0" selection-end-line="198" selection-end-column="1" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/serverfunctions.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="85">
<caret line="5" column="1" lean-forward="false" selection-start-line="5" selection-start-column="1" selection-end-line="5" selection-end-column="1" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/communication/zmq/adapter.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-150">
<caret line="255" column="8" lean-forward="false" selection-start-line="255" selection-start-column="8" selection-end-line="255" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/simpleplayer.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="290">
<caret line="24" column="78" lean-forward="false" selection-start-line="24" selection-start-column="78" selection-end-line="24" selection-end-column="78" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/settings.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="323">
<caret line="19" column="0" lean-forward="false" selection-start-line="19" selection-start-column="0" selection-end-line="19" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/exceptions/auraexceptions.py" />
<entry file="file://$PROJECT_DIR$/libraries/color/terminal.py" />
<entry file="file://$PROJECT_DIR$/libraries/base/config.py" />
<entry file="file://$PROJECT_DIR$/libraries/client/auraclient.py" />
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/test.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/alsa_settings_tester.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="68">
<caret line="4" column="31" lean-forward="false" selection-start-line="4" selection-start-column="31" selection-end-line="4" selection-end-column="31" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/enum/consolecolor.py" />
<entry file="file:///usr/local/lib/python3.5/dist-packages/redis/client.py" />
<entry file="file://$USER_HOME$/.PyCharmCE2017.3/system/python_stubs/-1247971764/sys.py" />
<entry file="file:///usr/lib/python3.5/threading.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="185">
<caret line="1164" column="8" lean-forward="false" selection-start-line="1164" selection-start-column="8" selection-end-line="1164" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/fallback.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2040">
<caret line="120" column="119" lean-forward="false" selection-start-line="120" selection-start-column="119" selection-end-line="120" selection-end-column="119" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/helpers/message.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="884">
<caret line="54" column="40" lean-forward="false" selection-start-line="54" selection-start-column="40" selection-end-line="54" selection-end-column="40" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/liquidsoap/simplestmixer.liq">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="119">
<caret line="7" column="20" lean-forward="false" selection-start-line="7" selection-start-column="20" selection-end-line="7" selection-end-column="20" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/communication/liquidsoap/playerclient.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4998">
<caret line="294" column="40" lean-forward="false" selection-start-line="294" selection-start-column="40" selection-end-line="294" selection-end-column="40" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file:///usr/local/lib/python3.5/dist-packages/sqlalchemy/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/communication/liquidsoap/client.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="119">
<caret line="7" column="19" lean-forward="false" selection-start-line="7" selection-start-column="19" selection-end-line="7" selection-end-column="19" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/enum/redischannels.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="85">
<caret line="5" column="1" lean-forward="false" selection-start-line="5" selection-start-column="1" selection-end-line="5" selection-end-column="1" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/database/combadb.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/scheduling/models.py" />
<entry file="file:///usr/local/lib/python3.5/dist-packages/flask/app.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="149">
<caret line="1597" column="0" lean-forward="false" selection-start-line="1597" selection-start-column="0" selection-end-line="1597" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/web/routes.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="255">
<caret line="15" column="21" lean-forward="false" selection-start-line="15" selection-start-column="21" selection-end-line="15" selection-end-column="21" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/database/trackservice.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="119">
<caret line="7" column="0" lean-forward="false" selection-start-line="7" selection-start-column="0" selection-end-line="7" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/communication/liquidsoap/initthread.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="197">
<caret line="37" column="68" lean-forward="false" selection-start-line="37" selection-start-column="68" selection-end-line="37" selection-end-column="68" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/aura.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="306">
<caret line="27" column="33" lean-forward="true" selection-start-line="27" selection-start-column="33" selection-end-line="27" selection-end-column="33" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/communication/redis/adapter.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="221">
<caret line="132" column="82" lean-forward="false" selection-start-line="132" selection-start-column="82" selection-end-line="132" selection-end-column="82" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/base/schedulerconfig.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="476">
<caret line="32" column="8" lean-forward="false" selection-start-line="32" selection-start-column="8" selection-end-line="32" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/communication/redis/messenger.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4505">
<caret line="272" column="75" lean-forward="false" selection-start-line="272" selection-start-column="75" selection-end-line="272" selection-end-column="75" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/configuration/aura.ini">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="151">
<caret line="69" column="19" lean-forward="false" selection-start-line="69" selection-start-column="16" selection-end-line="69" selection-end-column="19" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/communication/liquidsoap/communicator.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="273">
<caret line="1056" column="23" lean-forward="false" selection-start-line="1056" selection-start-column="23" selection-end-line="1056" selection-end-column="23" />
</state>
</provider>
</entry>
<entry file="file:///usr/lib/python3.5/json/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-2652">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file:///usr/lib/python3.5/socketserver.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="155">
<caret line="453" column="0" lean-forward="false" selection-start-line="453" selection-start-column="0" selection-end-line="453" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file:///usr/local/lib/python3.5/dist-packages/simplejson/encoder.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="308">
<caret line="372" column="0" lean-forward="false" selection-start-line="372" selection-start-column="0" selection-end-line="372" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/exceptions/exception_logger.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1173">
<caret line="69" column="31" lean-forward="false" selection-start-line="69" selection-start-column="31" selection-end-line="69" selection-end-column="31" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/database/statestore.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4777">
<caret line="286" column="0" lean-forward="false" selection-start-line="286" selection-start-column="0" selection-end-line="286" selection-end-column="0" />
<folding>
<element signature="e#25#37#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/guru.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="18" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="18" />
<folding>
<element signature="e#20#31#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/tools/padavan.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2074">
<caret line="125" column="12" lean-forward="false" selection-start-line="125" selection-start-column="12" selection-end-line="125" selection-end-column="12" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="306">
<caret line="20" column="40" lean-forward="false" selection-start-line="20" selection-start-column="40" selection-end-line="20" selection-end-column="40" />
<folding>
<element signature="e#20#63#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/database/database.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="323">
<caret line="24" column="52" lean-forward="false" selection-start-line="24" selection-start-column="52" selection-end-line="24" selection-end-column="52" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/controller/controller.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1377">
<caret line="96" column="59" lean-forward="false" selection-start-line="96" selection-start-column="58" selection-end-line="96" selection-end-column="59" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/base/calendar.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="3672">
<caret line="227" column="29" lean-forward="false" selection-start-line="227" selection-start-column="29" selection-end-line="227" selection-end-column="29" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/libraries/database/broadcasts.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4522">
<caret line="272" column="36" lean-forward="false" selection-start-line="272" selection-start-column="36" selection-end-line="272" selection-end-column="36" />
<folding>
<element signature="e#43#75#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/modules/scheduling/scheduler.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="394">
<caret line="579" column="0" lean-forward="true" selection-start-line="579" selection-start-column="0" selection-end-line="579" selection-end-column="0" />
<folding>
<element signature="e#1093#1106#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
</project>
\ No newline at end of file
# 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()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# combac.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
import codecs
import os
import sys
import threading
import simplejson
import queue
import traceback
from datetime import datetime, timedelta
from libraries.database.broadcasts import Schedule, ScheduleEntry
from modules.communication.redis.messenger import RedisMessenger
class AuraCalendarService(threading.Thread):
messenger = RedisMessenger()
calendarurl = ""
audiobase = ""
playlistdir = ""
until = ''
xmlplaylist = range(0)
queue = None
config = None
debug = False
_stop_event = None
"""
Fetching playlist data, write it into the database and notify service
"""
def __init__(self, config, datefrom="", dateto="", debug=True):
threading.Thread.__init__(self)
self.config = config
self.debug = debug
self.messenger.set_channel('aura')
self.messenger.set_section('calendar')
self.messenger.set_mail_addresses(self.config.get('frommail'), self.config.get('adminmail'))
self.dateto = dateto
self.datefrom = str(datefrom)
self.queue = queue.Queue()
self._stop_event = threading.Event()
# ------------------------------------------------------------------------------------------ #
def set_date_from(self, date):
self.datefrom = str(date).replace(" ", "T")
# ------------------------------------------------------------------------------------------ #
def set_date_to(self, date):
self.dateto = str(date).replace(" ", "T")
# ------------------------------------------------------------------------------------------ #
def set_until_time(self, timestring):
self.until = timestring
# ------------------------------------------------------------------------------------------ #
def set_calendar_url(self, url):
self.calendarurl = url
# ------------------------------------------------------------------------------------------ #
def set_audio_path(self, path):
self.audiobase = path
# ------------------------------------------------------------------------------------------ #
def set_playlist_store(self, path):
self.playlistdir = path
# ------------------------------------------------------------------------------------------ #
def get_duration(self, start, end):
return self.__calc_duration__(start, end)
# ------------------------------------------------------------------------------------------ #
def get_queue(self):
return self.queue
# ------------------------------------------------------------------------------------------ #
def get_uri(self):
if not self.playlistdir:
return False
if not self.datefrom:
return False
if not self.__calc_date_to__():
return
hostname = self.get('servername');
port = self.get('serviceport');
date_from = self.datefrom[0:16] + ':00';
date_to = self.dateto[0:16] + ':00';
uri = 'http://' + hostname + ':' + port + '/playlist/' + date_from + '/' + date_to
return uri
# ------------------------------------------------------------------------------------------ #
def run(self):
"""
Fetch calendar data and store it in the database
"""
self.__set_url__()
# fetch upcoming schedules from ENGINE
self.__fetch_schedule_data__()
# fetch playlist and fallbacks to the schedules from TANK
self.__fetch_schedule_entry_data__()
for schedule in self.fetched_schedule_data:
if 'schedule_start' not in schedule:
print("WARNING: no start of schedule given. skipping the schedule")
continue
if 'schedule_end' not in schedule:
print("WARNING: no end of schedule given. skipping the schedule")
continue
# store the schedule
self.store_schedule(schedule)
# store playlists to play
entrynum = 0
for entry in schedule["playlist"]["entries"]:
self.store_schedule_entry(schedule, entry, entrynum)
entrynum = entrynum + 1
# release the mutex
self.queue.put("fetching_finished")
# terminate the thread
return
def store_schedule(self, schedule):
from libraries.database.database import db
#schedule_db = Schedule.query.filter(Schedule.schedule_id == schedule['schedule_id']).first()
schedule_db = db.session.query(Schedule).filter(Schedule.schedule_id == schedule['schedule_id']).first()
# schedule_db = Schedule.get_by_id(schedule['schedule_id'])
if not schedule_db:
print("no schedule with given schedule id in database => create new")
schedule_db = Schedule()
schedule_db.store(add=True, commit=False)
# calc duration
duration = self.__calc_duration__(schedule['schedule_start'], schedule['schedule_end'])
schedule['duration'] = timedelta(seconds=duration).__str__()
schedule_db.show_id = schedule["show_id"]
schedule_db.schedule_id = schedule["schedule_id"]
schedule_db.schedule_start = schedule["schedule_start"]
schedule_db.schedule_end = schedule["schedule_end"]
schedule_db.show_name = schedule["show_name"]
schedule_db.show_hosts = schedule["show_hosts"]
schedule_db.is_repetition = schedule["is_repetition"]
schedule_db.rtr_category = schedule["rtr_category"]
schedule_db.comment = schedule["comment"]
schedule_db.languages = schedule["languages"]
schedule_db.type = schedule["type"]
schedule_db.category = schedule["category"]
schedule_db.topic = schedule["topic"]
schedule_db.musicfocus = schedule["musicfocus"]
schedule_db.playlist_id = schedule["playlist_id"]
schedule_db.schedule_fallback_id = schedule["schedule_fallback_id"]
schedule_db.show_fallback_id = schedule["show_fallback_id"]
schedule_db.station_fallback_id = schedule["station_fallback_id"]
schedule_db.store(add=False, commit=True)
event = {'job': 'dump', 'location': schedule["schedule_id"], 'length': duration}
self.messenger.queue_add_event('dumpstart', schedule["schedule_start"], event)
event = {'job': 'dump', 'location': schedule["schedule_id"], 'length': duration}
self.messenger.queue_add_event('dumpend', schedule["schedule_end"], event)
return schedule_db
# ------------------------------------------------------------------------------------------ #
def store_schedule_entry(self, schedule, entry, entrynum):
schedule_entry = ScheduleEntry.select_one(schedule["playlist_id"], entrynum)
havetoadd = False
if not schedule_entry:
if self.debug:
print("no scheduleentry with id " + str(schedule["playlist_id"]) + " and pos " + str(entrynum) + " in database => creating a new one")
schedule_entry = ScheduleEntry()
havetoadd = True
schedule_entry.playlist_id = schedule["playlist_id"]
schedule_entry.entry_num = entrynum
schedule_entry.schedule_id = schedule["schedule_id"]
schedule_entry.entry_start = entry["entry_start"]
schedule_entry.entry_end = entry["entry_end"]
schedule_entry.source = entry["source"]
schedule_entry.artist = entry["artist"]
schedule_entry.track = entry["track"]
schedule_entry.albumname = entry["albumname"]
schedule_entry.genre = entry["genre"]
schedule_entry.tracknum = entry["tracknum"]
schedule_entry.cdnum = entry["cdnum"]
schedule_entry.year = entry["year"]
if schedule_entry.year == '':
schedule_entry.year = 0
if self.debug:
print("Storing entries... playlist_id: " + str(schedule["playlist_id"]) + " schedule_id: " + str(
schedule["schedule_id"]) + " num: " + str(entrynum))
schedule_entry.store(add=havetoadd, commit=True)
return schedule_entry
# ------------------------------------------------------------------------------------------ #
def __calc_date_to__(self):
if self.dateto:
return True
if not self.until:
return False
if not self.datefrom:
return False
date_start = datetime.strptime(self.datefrom.replace('T',' '), "%Y-%m-%d %H:%M")
time_start = date_start.strftime('%H:%M')
day_offset = 1 if (time_start > self.until) else 0
end_date = date_start + timedelta(day_offset)
self.dateto = end_date.strftime('%F') + 'T' + self.until
return True
# ------------------------------------------------------------------------------------------ #
@staticmethod
def __calc_duration__(start, end):
"""
Berechnet Zeit in Sekunden aus Differenz zwischen Start und Enddatum
@type start: datetime
@param start: Startzeit
@type end: datetime
@param end: Endzeit
@rtype: int
@return: Zeit in Sekunden
"""
sec1 = int(datetime.strptime(start[0:16].replace(" ","T"),"%Y-%m-%dT%H:%M").strftime("%s"));
sec2 = int(datetime.strptime(end[0:16].replace(" ","T"),"%Y-%m-%dT%H:%M").strftime("%s"));
return (sec2 - sec1);
# ------------------------------------------------------------------------------------------ #
def __fetch_schedule_entry_data__(self):
# store fetched entries => do not have to fetch playlist_id more than once
fetched_entries=[]
try:
url = self.get_url()
data = self.get_data()
except IOError as e:
self.messenger.send("Could not connect to service " + self.dataURL, '1101', 'error', 'fetchCalenderData', self.__get_error_data__(), 'getcalendar')
try:
for schedule in self.fetched_schedule_data:
# retrieve playlist and the fallbacks for every schedule
# if a playlist is already fetched, it is not fetched again
schedule["playlist"] = self.__fetch_schedule_entries__(schedule["schedule_id"], schedule["playlist_id"], fetched_entries)
schedule["schedule_fallback"] = self.__fetch_schedule_entries__(schedule["schedule_id"], schedule["schedule_fallback_id"], fetched_entries)
schedule["show_fallback"] = self.__fetch_schedule_entries__(schedule["schedule_id"], schedule["show_fallback_id"], fetched_entries)
schedule["station_fallback"] = self.__fetch_schedule_entries__(schedule["schedule_id"], schedule["station_fallback_id"], fetched_entries)
if self.debug:
print("fetched for schedule_id: "+str(schedule["schedule_id"]))
print("playlist: "+str(schedule["playlist"]))
print("schedule_fallback: "+str(schedule["schedule_fallback"]))
print("show_fallback: "+str(schedule["show_fallback"]))
print("station_fallback: "+str(schedule["station_fallback"]))
except Exception as e:
traceback.print_exc()
print("houston we have a problem")
# ------------------------------------------------------------------------------------------ #
def __fetch_schedule_entries__(self, schedule_id, playlist_id, fetched_schedule_entries):
for entry in fetched_schedule_entries:
if entry["playlist_id"] == playlist_id:
if self.debug:
print(str(playlist_id) + " already fetched")
return entry
if self.debug:
print("Wanna fetch playlist/pool id " + str(playlist_id) + " of schedule #" + str(schedule_id))
# HARDCODED Testdata
if playlist_id == 1: # station fallback
schedule_entries = simplejson.loads('{"playlist_id":1,"entries":[{"source":"pool://easylistening","artist":"station wide fallback pool","track":"","albumname":"","genre":"","tracknum":"","cdnum":"","year":""}]}')
if playlist_id == 2: # playlist to play for schedule #1 => linein
schedule_entries = simplejson.loads('{"playlist_id":2,"entries":[{"entry_start":"'+(datetime.now()+timedelta(hours=0)).strftime('%Y-%m-%d %H:00:00')+'","entry_end":"'+(datetime.now()+timedelta(hours=0)).strftime('%Y-%m-%d %H:05:00')+'","source":"/var/audio/jingles/jingle.flac","artist":"jingle","track":"","albumname":"","genre":"","tracknum":"","cdnum":"","year":"1999"},{"entry_start":"'+(datetime.now()+timedelta(hours=0)).strftime('%Y-%m-%d %H:05:00')+'","entry_end":"'+(datetime.now()+timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00')+'","source":"linein://0","artist":"live","track":"","albumname":"","genre":"","tracknum":"","cdnum":"","year":""}]}')
if playlist_id == 4: # playlist to play for schedule #2 => from file system
schedule_entries = simplejson.loads('{"playlist_id":4,"entries":[{"entry_start":"'+(datetime.now()+timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00')+'","entry_end":"'+(datetime.now()+timedelta(hours=1)).strftime('%Y-%m-%d %H:05:00')+'","source":"file:///var/audio/jingles/jingle.flac","artist":"jingle","track":"","albumname":"","genre":"","tracknum":"","cdnum":"","year":"1999"},{"entry_start":"'+(datetime.now()+timedelta(hours=1)).strftime('%Y-%m-%d %H:05:00')+'","entry_end":"'+(datetime.now()+timedelta(hours=2)).strftime('%Y-%m-%d %H:00:00')+'","source":"file:///var/audio/preprod.flac","artist":"live","track":"","albumname":"","genre":"","tracknum":"","cdnum":"","year":""}]}')
if playlist_id == 6: # playlist to play for schedule #3 => http stream
schedule_entries = simplejson.loads('{"playlist_id":6,"entries":[{"entry_start":"'+(datetime.now()+timedelta(hours=2)).strftime('%Y-%m-%d %H:00:00')+'","entry_end":"'+(datetime.now()+timedelta(hours=3)).strftime('%Y-%m-%d %H:00:00')+'","source":"http://stream.fro.at:80/fro-128.ogg","artist":"Stream Übernahme von Radio FRO","track":"","albumname":"","genre":"","tracknum":"","cdnum":"","year":""}]}')
if playlist_id == 12: # schedule #0 fallback
schedule_entries = simplejson.loads('{"playlist_id":12,"entries":[{"source":"pool:///drumandbass"}]}')
if playlist_id == 22: # schedule #1 fallback
schedule_entries = simplejson.loads('{"playlist_id":22,"entries":[{"source":"pool:///chillout"}]}')
if playlist_id == 32: # schedule #2 fallback
schedule_entries = simplejson.loads('{"playlist_id":22,"entries":[{"source":"pool:///rock"}]}')
if playlist_id == 92: # show fallback for schedule #0
schedule_entries = simplejson.loads('{"playlist_id":92,"entries":[{"source":"file:///var/audio/playlist/playlist1.m3u"}]}')
if playlist_id == 102: # show fallback for schedule #1
schedule_entries = simplejson.loads('{"playlist_id":102,"entries":[{"source":"file:///var/audio/playlist/playlist2.m3u"}]}')
if playlist_id == 112: # show fallback for schedule #2
schedule_entries = simplejson.loads('{"playlist_id":112,"entries":[{"source":"file:///var/audio/playlist/playlist3.m3u"}]}')
for entry in schedule_entries["entries"]:
if entry["source"].startswith("file"):
if not os.path.isfile(entry["source"]):
print("WARNING: File", entry["source"], "does not exist!")
# elif entry["source"].startswith("pool"):
fetched_schedule_entries.append(schedule_entries)
return schedule_entries
# ------------------------------------------------------------------------------------------ #
def __fetch_schedule_data__(self):
# Now open Calendar Url
try:
url = self.get_url()
data = self.get_data()
#request = urllib.request.Request(url, data)
#with urllib.request.urlopen(request) as response:
# html_response = response.read()
html_response = ''
except IOError as e:
self.messenger.send("Could not connect to service " + self.dataURL, '1101', 'error', 'fetchCalenderData',
self.__get_error_data__(), 'getcalendar')
#TODO: so gehts nicht
sys.exit()
else:
# Read in data
#print(html_response[0:100])
#jsondata = html_response # .read()
jsonstring = '[{"schedule_id":1,"schedule_start":"' + (datetime.now() + timedelta(hours=0)).strftime('%Y-%m-%d %H:00:00') + '","schedule_end":"' + (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + '","show_id":9,"show_name":"FROzine","show_hosts":"Sandra Hochholzer, Martina Schweiger","is_repetition":false,"playlist_id":2,"schedule_fallback_id":12,"show_fallback_id":92,"station_fallback_id":1,"rtr_category":"string","comment":"Kommentar","languages":"Sprachen","type":"Typ","category":"Kategorie","topic":"Topic","musicfocus":"Fokus"},{"schedule_id":2,"schedule_start":"' + (datetime.now()+timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + '","schedule_end":"' + (datetime.now()+timedelta(hours=2)).strftime('%Y-%m-%d %H:00:00') + '","show_id":10,"show_name":"FROMat","show_hosts":"Sandra Hochholzer, Martina Schweiger","is_repetition":false,"playlist_id":4,"schedule_fallback_id":22,"show_fallback_id":102,"station_fallback_id":1,"rtr_category":"string","comment":"Kommentar","languages":"Sprachen","type":"Typ","category":"Kategorie","topic":"Topic","musicfocus":"Fokus"},{"schedule_id":3,"schedule_start":"' + (datetime.now()+timedelta(hours=2)).strftime('%Y-%m-%d %H:00:00') + '","schedule_end":"' + (datetime.now() + timedelta(hours=3)).strftime('%Y-%m-%d %H:00:00') + '","show_id":11,"show_name":"Radio für Senioren","show_hosts":"Sandra Hochholzer, Martina Schweiger","is_repetition":false,"playlist_id":6,"schedule_fallback_id":32,"show_fallback_id":112,"station_fallback_id":1,"rtr_category":"string","comment":"Kommentar","languages":"Sprachen","type":"Typ","category":"Kategorie","topic":"Topic","musicfocus":"Fokus"}]'
try:
self.fetched_schedule_data = simplejson.loads(jsonstring)
except Exception as e:
self.messenger.send("Could not decode calender data from service " + self.dataURL + "! Exception: " + e, '1102', 'error',
'fetchCalenderData', self.__get_error_data__(), 'getcalendar')
sys.exit()
else:
# check data
try:
print("WARNING: Hardcoded Response && no json data checks. i believe what i get here")
#print(self.data)
#print(len(self.data))
except KeyError as e:
self.messenger.send("Could not decode calender data from service " + self.dataURL + "! Exception: " + e, '1102', 'error',
'fetchCalenderData', self.__get_error_data__(), 'getcalendar')
sys.exit()
except Exception as e:
self.messenger.send("Could not decode calender data from service " + self.dataURL + "! Exception: " + e, '1102', 'error',
'fetchCalenderData', self.__get_error_data__(), 'getcalendar')
sys.exit()
else:
return self.fetched_schedule_data
# ------------------------------------------------------------------------------------------ #
def __get_error_data__(self):
"""
Basisdaten als dict liefern
"""
return {'from': str(self.datefrom), 'dateto': str(self.dateto), 'path': "self.playlistpath", 'url': self.calendarurl}
# ------------------------------------------------------------------------------------------ #
def __set_url__(self):
"""
Die URL auf den Zappa-Kalender ermitteln
"""
self.dataURL = self.calendarurl.replace('#datefrom#', self.datefrom.replace(' ', 'T')).replace('#dateto#', self.dateto.replace(' ', 'T'))
# ------------------------------------------------------------------------------------------ #
def get_url(self):
url = self.dataURL[0:self.dataURL.find('&')]
return url
# ------------------------------------------------------------------------------------------ #
def get_data(self):
params = self.dataURL[self.dataURL.find('&'):]
print("params: "+params)
# ------------------------------------------------------------------------------------------ #
def stop(self):
self._stop_event.set()
# ------------------------------------------------------------------------------------------ #
def get_calendar_data(self):
return self.fetched_schedule_data
#!/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