Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
aura-engine
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Lars Kruse
aura-engine
Commits
298ebf3c
Commit
298ebf3c
authored
4 years ago
by
David Trattnig
Browse files
Options
Downloads
Patches
Plain Diff
Pro-active fallback handling.
parent
57f97f77
No related branches found
No related tags found
No related merge requests found
Changes
2
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
modules/scheduling/fallback_manager.py
+108
-29
108 additions, 29 deletions
modules/scheduling/fallback_manager.py
modules/scheduling/scheduler.py
+278
-151
278 additions, 151 deletions
modules/scheduling/scheduler.py
with
386 additions
and
180 deletions
modules/scheduling/fallback_manager.py
+
108
−
29
View file @
298ebf3c
...
@@ -21,15 +21,16 @@
...
@@ -21,15 +21,16 @@
import
os
,
os
.
path
import
os
,
os
.
path
import
ntpath
import
logging
import
logging
import
random
import
random
import
librosa
import
librosa
from
accessify
import
private
,
protected
from
accessify
import
private
,
protected
from
modules.base.enum
import
Fallback
Type
from
modules.base.enum
import
Playlist
Type
from
modules.base.utils
import
SimpleUtil
from
modules.base.utils
import
SimpleUtil
,
EngineUtil
from
modules.communication.mail
import
AuraMailer
from
modules.communication.mail
import
AuraMailer
from
modules.base.enum
import
ChannelType
class
FallbackManager
:
class
FallbackManager
:
...
@@ -73,39 +74,83 @@ class FallbackManager:
...
@@ -73,39 +74,83 @@ class FallbackManager:
# PUBLIC METHODS
# PUBLIC METHODS
#
#
def
resolve_playlist
(
self
,
schedule
):
def
get_fallback
(
self
,
schedule
,
type
):
"""
"""
Checks if the given schedule is valid and returns a valid fallback
Resolves the (fallback) playlist for the given schedule in case of pro-active fallback scenarios.
if required.
A resolved playlist represents the state how it would currently be aired. For example the `FallbackManager`
evaluated, that the actually planned playlist cannot be played for various reasons (e.g. entries n/a).
Instead one of the fallback playlists should be played. If the method is called some time later,
it actually planned playlist might be valid, thus returned as the resolved playlist.
As long the adressed schedule is still within the scheduling window, the resolved playlist can
always change.
This method also updates `schedule.fallback_state` to the current fallback type (`PlaylistType`).
Args:
schedule (Schedule): The schedule to resolve the playlist for
Returns:
(Playlist): The resolved playlist
"""
"""
playlist
=
None
type
=
None
type
=
None
playlist
_id
=
schedule
.
playlist
_id
self
.
logger
.
info
(
"
Resolving
playlist
for
schedule
#%s ...
"
%
schedule
.
schedule
_id
)
if
not
schedule
.
playlist
_id
:
if
not
self
.
validate_playlist
(
schedule
,
"
playlist
"
)
:
if
not
schedule
.
show_fallback
_id
:
if
not
self
.
validate_playlist
(
schedule
,
"
show_fallback
"
)
:
if
not
schedule
.
schedule_fallback
_id
:
if
not
self
.
validate_playlist
(
schedule
,
"
schedule_fallback
"
)
:
if
not
schedule
.
station_fallback
_id
:
if
not
self
.
validate_playlist
(
schedule
,
"
station_fallback
"
)
:
raise
Exception
raise
Exception
(
"
No (fallback) playlists for schedule #%s available - not even a single one!
"
%
schedule
.
schedule_id
)
else
:
else
:
type
=
Fallback
Type
.
STATION
type
=
Playlist
Type
.
STATION
playlist
_id
=
schedule
.
station_fallback
_id
playlist
=
schedule
.
station_fallback
else
:
else
:
type
=
Fallback
Type
.
TIMESLOT
type
=
Playlist
Type
.
TIMESLOT
playlist
_id
=
schedule
.
schedule_fallback
_id
playlist
=
schedule
.
schedule_fallback
else
:
else
:
type
=
FallbackType
.
SHOW
type
=
PlaylistType
.
SHOW
playlist_id
=
schedule
.
show_fallback_id
playlist
=
schedule
.
show_fallback
else
:
type
=
PlaylistType
.
DEFAULT
playlist
=
schedule
.
playlist
if
type
:
if
type
and
type
!=
PlaylistType
.
DEFAULT
:
self
.
logger
.
warn
(
"
Detected fallback type
'
%s
'
required for schedule %s
"
%
(
type
,
str
(
schedule
)))
previous_type
=
schedule
.
fallback_state
if
type
==
previous_type
:
self
.
logger
.
info
(
"
Fallback state for schedule #%s is still
'
%s
'"
%
(
schedule
.
schedule_id
,
type
))
else
:
self
.
logger
.
warn
(
"
Detected fallback type switch from
'
%s
'
to
'
%s
'
is required for schedule %s.
"
%
(
previous_type
,
type
,
str
(
schedule
)))
schedule
.
fallback_state
=
type
return
playlist
[
0
]
return
(
type
,
playlist_id
)
def
handle_proactive_fallback
(
self
,
scheduler
,
playlist
):
"""
This is the 1st level strategy for fallback handling. When playlist entries are pre-rolled their
state is validated. If any of them doesn
'
t become
"
ready to play
"
in time, some fallback entries
are queued.
"""
resolved_playlist
=
self
.
resolve_playlist
(
playlist
.
schedule
)
if
playlist
!=
resolved_playlist
:
self
.
logger
.
info
(
"
Switching from playlist #%s to fallback playlist #%s ...
"
%
(
playlist
.
playlist_id
,
resolved_playlist
.
playlist_id
))
# Destroy any existing queue timers
for
entry
in
playlist
.
entries
:
scheduler
.
stop_timer
(
entry
.
switchtimer
)
self
.
logger
.
info
(
"
Stopped existing timers for entries
"
)
# Queue the fallback playlist
scheduler
.
queue_playlist_entries
(
resolved_playlist
.
schedule
,
resolved_playlist
.
entries
,
False
,
True
)
self
.
logger
.
info
(
"
Queued fallback playlist entries (Fallback type: %s)
"
%
playlist
.
type
)
else
:
self
.
logger
.
critical
(
SimpleUtil
.
red
(
"
For some strange reason the fallback playlist equals the currently failed one?!
"
))
def
validate_playlist
(
self
,
playlist_id
):
pass
def
get_fallback_for
(
self
,
fallbackname
):
def
get_fallback_for
(
self
,
fallbackname
):
...
@@ -186,11 +231,45 @@ class FallbackManager:
...
@@ -186,11 +231,45 @@ class FallbackManager:
duration
=
librosa
.
get_duration
(
y
=
y
,
sr
=
sr
)
duration
=
librosa
.
get_duration
(
y
=
y
,
sr
=
sr
)
return
duration
return
duration
#
#
# PRIVATE METHODS
# PRIVATE METHODS
#
#
def
validate_playlist
(
self
,
schedule
,
playlist_type
):
"""
Checks if a playlist is valid for play-out.
"""
playlist
=
getattr
(
schedule
,
playlist_type
)
if
playlist
\
and
isinstance
(
playlist
,
list
)
\
and
playlist
[
0
].
entries
\
and
len
(
playlist
[
0
].
entries
)
>
0
:
return
self
.
validate_entries
(
playlist
[
0
].
entries
)
return
False
def
validate_entries
(
self
,
entries
):
"""
Checks if playlist entries are valid for play-out.
"""
for
entry
in
entries
:
if
entry
.
get_type
()
==
ChannelType
.
FILESYSTEM
:
audio_store
=
self
.
config
.
get
(
"
audiofolder
"
)
filepath
=
EngineUtil
.
uri_to_filepath
(
audio_store
,
entry
.
source
)
if
not
self
.
is_audio_file
(
filepath
):
self
.
logger
.
warn
(
"
Invalid filesystem path
'
%s
'
in entry
'
%s
'"
%
(
filepath
,
str
(
entry
)))
return
False
return
True
def
get_playlist_items
(
self
,
schedule
,
fallback_key
):
def
get_playlist_items
(
self
,
schedule
,
fallback_key
):
"""
"""
Retrieves the list of tracks from a playlist defined by `fallback_key`.
Retrieves the list of tracks from a playlist defined by `fallback_key`.
...
@@ -218,7 +297,7 @@ class FallbackManager:
...
@@ -218,7 +297,7 @@ class FallbackManager:
"""
"""
dir
=
self
.
config
.
fallback_music_folder
dir
=
self
.
config
.
fallback_music_folder
files
=
os
.
listdir
(
dir
)
files
=
os
.
listdir
(
dir
)
audio_files
=
list
(
filter
(
lambda
f
:
self
.
is_audio_file
(
dir
,
f
),
files
))
audio_files
=
list
(
filter
(
lambda
f
:
self
.
is_audio_file
(
os
.
path
.
join
(
dir
,
f
)
)
,
files
))
if
not
dir
or
not
audio_files
:
if
not
dir
or
not
audio_files
:
self
.
logger
.
error
(
"
Folder
'
fallback_music_folder = %s
'
is empty!
"
%
dir
)
self
.
logger
.
error
(
"
Folder
'
fallback_music_folder = %s
'
is empty!
"
%
dir
)
...
@@ -253,22 +332,22 @@ class FallbackManager:
...
@@ -253,22 +332,22 @@ class FallbackManager:
def
is_audio_file
(
self
,
dir
,
file
):
def
is_audio_file
(
self
,
file
):
"""
"""
Checks if the passed file is an audio file i.e. has a file-extension
Checks if the passed file is an audio file i.e. has a file-extension
known for audio files.
known for audio files.
Args:
Args:
(File): file: the file object.
dir (String):
file (File): the file object.
Returns:
Returns:
(Boolean): True, if it
'
s an audio file.
(Boolean): True, if it
'
s an audio file.
"""
"""
audio_extensions
=
[
"
.wav
"
,
"
.flac
"
,
"
.mp3
"
,
"
.ogg
"
,
"
.m4a
"
]
audio_extensions
=
[
"
.wav
"
,
"
.flac
"
,
"
.mp3
"
,
"
.ogg
"
,
"
.m4a
"
]
ext
=
os
.
path
.
splitext
(
file
)[
1
]
ext
=
os
.
path
.
splitext
(
file
)[
1
]
abs_path
=
os
.
path
.
join
(
dir
,
file
)
if
os
.
path
.
isfile
(
abs_path
):
if
os
.
path
.
isfile
(
file
):
if
any
(
ext
in
s
for
s
in
audio_extensions
):
if
any
(
ext
in
s
for
s
in
audio_extensions
):
return
True
return
True
return
False
return
False
\ No newline at end of file
This diff is collapsed.
Click to expand it.
modules/scheduling/scheduler.py
+
278
−
151
View file @
298ebf3c
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment