forked from shane-mason/FieldStation42
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfield_player.py
247 lines (197 loc) · 7.65 KB
/
field_player.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
from python_mpv_jsonipc import MPV
from enum import Enum
import time
import datetime
import json
from timings import *
channel_socket = "runtime/channel.socket"
class ReceptionStatus:
def __init__(self, chaos=0, thresh=0.01):
self.chaos = chaos
self.thresh = thresh
def is_perfect(self):
return self.chaos == 0.0
def is_degraded(self):
return self.chaos > threshhold
def is_fully_degraded(self):
return self.chaos == 1.0
def degrade(self, amount=.02):
self.chaos += amount
if self.chaos > 1.0:
self.chaos = 1.0
def improve(self, amount=.02):
self.chaos-=amount
if self.chaos < self.thresh:
self.chaos = 0.0
def filter(self):
if self.chaos > self.thresh:
#between 0 and 100
noise = self.chaos * 100
#between 0 and .5
v_scroll = self.chaos * .5
return f"lavfi=[noise=alls={noise}:allf=t+u, scroll=h=0:v={v_scroll}]"
else:
return ""
class PlayStatus(Enum):
FAILED = 1
EXITED = 2
SUCCESS = 3
CHANNEL_CHANGE =4
class FieldPlayer:
def __init__(self, runtime_path, mpv=None):
if not mpv:
#command on client: mpv --input-ipc-server=/tmp/mpvsocket --idle --force-window
self.mpv = MPV(start_mpv=True, ipc_socket="/tmp/mpvsocket", input_default_bindings=False, fs=True, idle=True, force_window=True)
self.runtime_path = runtime_path
#self.playlist = self.read_json(runtime_filepath)
self.index = 0
def update_filters(self):
self.mpv.vf = reception.filter()
def update_reception(self):
if not reception.is_perfect():
reception.improve()
#did that get us below threshhold?
if reception.is_perfect():
self.mpv.vf = ""
else:
self.mpv.vf = reception.filter()
def play_file(self, file_path):
self.mpv.play(file_path)
self.mpv.wait_for_property("duration")
return
def play_image(self, duration):
pass
def play_slot(self, the_day, the_hour, offset=0, runtime_path=None):
if runtime_path:
self.runtime_path = runtime_path
fpath = f"{self.runtime_path}/{the_day}_{the_hour}.json";
print("Playing: " + fpath)
self.playlist = self.read_json(fpath)
return self.start_playing(offset)
def read_json(self, file_path):
playlist = None
with open(file_path, "r") as f:
as_str = f.read()
playlist = json.loads(as_str)
return playlist
def start_playing(self, block_offset=0):
offset_in_index = 0
if block_offset:
print("Getting block offset")
(index, offset) = self._find_index_at_offset(block_offset)
print(f"index,offset = {index},{offset}")
self.index = index
offset_in_index = offset
return self._play_from_index(offset_in_index)
def _find_index_at_offset(self, offset):
abs_start = 0
abs_end = 0
index = 0
for _entry in self.playlist:
abs_start = abs_end
abs_end = abs_start + _entry['duration']
if offset > abs_start and offset <= abs_end:
d2 = offset - abs_start
return(index, d2)
index += 1
#returns true if play is interrupted
def _play_from_index(self, offet_in_index=0):
if self.index < len(self.playlist):
#iterate over the slice from index to end
for entry in self.playlist[self.index:]:
print("Playing: ", entry)
self.mpv.play(entry["path"])
self.mpv.wait_for_property("duration")
wait_dur = entry['duration']
seek_dur = 0
#do any initial seek
if entry['start'] != 0:
seek_dur += entry['start']
if offet_in_index:
seek_dur += offet_in_index
wait_dur -= offet_in_index
#only on first index we process, so toggle it off
offet_in_index = 0
if seek_dur:
self.mpv.seek(seek_dur)
print(f"seeking for: {seek_dur}")
if wait_dur:
print(f"monitoring for: {wait_dur}")
#this is our main event loop
keep_waiting = True
stop_time = datetime.datetime.now() + datetime.timedelta(seconds=wait_dur)
while keep_waiting:
self.update_reception()
now = datetime.datetime.now()
if now >= stop_time:
keep_waiting = False
else:
#debounce time
time.sleep(.05)
r_sock = open(channel_socket, "r")
contents = r_sock.read()
r_sock.close()
if len(contents):
print("Contents updated - resetting socket file")
with open(channel_socket, 'w'):
pass
return PlayStatus.CHANNEL_CHANGE
print("Done playing block")
return PlayStatus.SUCCESS
else:
return PlayStatus.FAILED
reception = ReceptionStatus()
def main_loop():
#get the channels and runtimes
from confs.fieldStation42_conf import main_conf
station_runtimes = []
for c in main_conf["stations"]:
#go through each station config and get its runtime
station_runtimes.append(c['runtime_dir'])
global channel_socket
channel_socket = main_conf['channel_socket']
#go ahead and clear the channel socket (or create if it doesn't exist)
with open(channel_socket, 'w'):
pass
channel = 0
if not len(station_runtimes):
print("Could not find any station runtimes - do you have your channels configured?")
player = FieldPlayer(station_runtimes[channel])
reception.degrade(1)
player.update_filters()
while True:
print(f"Playing station: {station_runtimes[channel]}" )
now = datetime.datetime.now()
#how far are we from the next hour?
if now.minute == 59:
#then just play some filler until next hour +1 second
to_fill = (MIN_1-now.second)+1
print(f"Filling time between blocks: {to_fill}")
time.sleep(to_fill)
now = datetime.datetime.now()
week_day = DAYS[now.weekday()]
hour = now.hour
skip = now.minute * MIN_1 + now.second
outcome = player.play_slot(week_day, hour, skip, runtime_path=station_runtimes[channel])
if outcome == PlayStatus.CHANNEL_CHANGE:
channel+=1
if channel>=len(station_runtimes):
channel = 0
#add noise to current channel
while not reception.is_fully_degraded():
reception.degrade()
player.update_filters()
time.sleep(.05)
#reception.improve(1)
player.play_file("runtime/static.mp4")
while not reception.is_perfect():
reception.improve(.1)
player.update_filters()
time.sleep(.1)
time.sleep(1)
while not reception.is_fully_degraded():
reception.degrade(.1)
player.update_filters()
time.sleep(.1)
if __name__ == "__main__":
main_loop()