send_to({command: [command]}, PATH_PLAYER); }
event_handlers.push(function(data) { // update player state
- for (const [id, text] of [["playing_tags", data.title_tags ? `(tags: ${data.title_tags})` : ""],
- ["a_playing", data.title],
- ["player_state", data.running ? (data.paused ? "paused" : "playing") : "stopped"],
- ["btn_pause", data.paused ? "resume" : "pause"],
- ["btn_stop", data.running ? "stop" : "play"]]) {
+ for (const [id, text] of [
+ ["playing_tags", data.title_tags ? `(tags: ${data.title_tags})` : ""],
+ ["a_playing", data.title],
+ ["player_state", data.is_running ? (data.is_playing ? "playing:" : "paused:") : "stopped" + (data.title ? ':' : '')],
+ ["btn_play", data.is_playing ? "pause" : "play"]]) {
document.getElementById(id).textContent = text; }
+ for (const btn of document.getElementsByClassName("btn_if_can_play")) {
+ btn.disabled = !data.can_play; }
document.getElementById("a_playing").href = data.title_digest ? `${PATH_PREFIX_FILE}${data.title_digest}` : PATH_PLAYLIST ; })
{% block script %}
· {{ macros.link_if("files" != selected, page_names.files) }}
· {{ macros.link_if("yt_queries" != selected, page_names.yt_queries, "queries") }}
<hr />
-<button onclick="player_command('prev')">prev</button>
-<button onclick="player_command('next')">next</button>
-<button id="btn_pause" onclick="player_command('pause')"></button>
-<button id="btn_stop" onclick="player_command('stop')"></button>
-· <span id="player_state" /></span>: <a id="a_playing"></a> <span id="playing_tags"></span>
+<button class="btn_if_can_play" onclick="player_command('prev')">prev</button>
+<button class="btn_if_can_play" onclick="player_command('next')">next</button>
+<button id="btn_play" class="btn_if_can_play" onclick="player_command('play')">play</button>
+· <span id="player_state" /></span> <a id="a_playing"></a> <span id="playing_tags"></span>
<hr />
{% block body %}
{% endblock %}
def _receive_player_command(self, postvars: _ReqMap) -> None:
command = postvars.first_for('command')
- if 'pause' == command:
- self.server.player.toggle_pause()
+ if 'play' == command:
+ self.server.player.toggle_play()
elif 'prev' == command:
self.server.player.prev()
elif 'next' == command:
self.server.player.next()
- elif 'stop' == command:
- self.server.player.toggle_run()
elif 'reload' == command:
- self.server.player.load_files_and_start()
+ self.server.player.load_files_and_mpv()
elif command.startswith('jump_'):
self.server.player.jump_to(int(command.split('_')[1]))
elif command.startswith('up_'):
headers=[(_HEADER_CONTENT_TYPE, _HEADER_APP_JSON)])
def _send_playlist(self) -> None:
- if self.server.player.empty:
- self.server.player.load_files_and_start()
self._send_rendered_template(
_NAME_TEMPLATE_PLAYLIST,
{'selected': 'playlist',
self._send_http(headers=[(_HEADER_CONTENT_TYPE, 'text/event-stream'),
('Cache-Control', 'no-cache'),
('Connection', 'keep-alive')])
- playing: Optional[VideoFile] = None
+ selected: Optional[VideoFile] = None
last_sent = ''
payload: dict[str, Any] = {}
time_last_write = 0.0
time_last_write = time()
payload.clear()
if not self.server.player.current_digest:
- playing = None
- elif ((not playing)
- or (playing.digest != self.server.player.current_digest)):
+ selected = None
+ elif ((not selected)
+ or (selected.digest != self.server.player.current_digest)):
with DbConn() as conn:
- playing = VideoFile.get_one_with_whitelist_tags_display(
+ selected = VideoFile.get_one_with_whitelist_tags_display(
conn,
self.server.player.current_digest,
self.server.config.whitelist_tags_display)
if last_sent < self.server.player.last_update:
last_sent = self.server.player.last_update
title, tags, digest = '', '', ''
- if playing:
- tags = playing.tags_showable.joined
- title = str(playing.rel_path)
- digest = playing.digest.b64
- payload['last_update'] = self.server.player.last_update
- payload['running'] = self.server.player.is_running
- payload['paused'] = self.server.player.is_paused
- payload['idx'] = self.server.player.idx
+ if selected:
+ tags = selected.tags_showable.joined
+ title = str(selected.rel_path)
+ digest = selected.digest.b64
+ payload['is_running'] = self.server.player.is_running
+ payload['is_playing'] = self.server.player.is_playing
+ payload['can_play'] = self.server.player.can_play
payload['title_tags'] = tags
payload['title_digest'] = digest
payload['title'] = title
if 'playlist' in params.as_dict:
+ payload['idx'] = self.server.player.idx
payload['playlist_files'] = [
{'rel_path': str(f.rel_path), 'digest': f.digest.b64}
for f in self.server.player.playlist]
self._monitoring_kill: bool = False
self._kill_queue: Queue = Queue()
self.playlist: list[VideoFile] = []
- self.load_files_and_start()
+ self.load_files_and_mpv()
def _signal_update(self) -> None:
"""Update .last_update as signal player state has changed relevantly"""
for path in [f.full_path for f in self.playlist]:
self._mpv.command('loadfile', path, 'append')
self._idx = 0
- self._play_at_index()
- def load_files_and_start(self) -> None:
+ def load_files_and_mpv(self) -> None:
"""Collect filtered files into playlist, shuffle, start player."""
with DbConn() as conn:
known_files = {
"""Read-only access to ._idx."""
return self._idx
- @property
- def empty(self) -> bool:
- """Return if playlist empty."""
- return 0 == len(self.playlist)
-
@property
def current_digest(self) -> Optional[Hash]:
"""Return hash digest ID of currently playing file."""
@property
def is_running(self) -> bool:
- """Return if player is running/available."""
- return bool(self._mpv)
+ """Return if player is up and has "playing" (possibly paused) title."""
+ return bool(self._mpv
+ and len(self._mpv.playlist) > self._idx
+ and 'playing' in self._mpv.playlist[self._idx])
@property
- def is_paused(self) -> bool:
- """Return if player is paused."""
- if self._mpv:
- return self._mpv.pause
- return False
+ def is_playing(self) -> bool:
+ """Return if currently playing (non-paused)."""
+ return bool(self._mpv and self.is_running and not self._mpv.pause)
- def toggle_run(self) -> None:
- """Toggle player running."""
- if self._mpv:
- self._kill_mpv()
- else:
- self._start_mpv()
- self._signal_update()
+ @property
+ def can_play(self) -> bool:
+ """Return if playlist non-empty."""
+ return len(self.playlist) > 0
- def toggle_pause(self) -> None:
- """Toggle player pausing."""
- if self._mpv:
+ def toggle_play(self) -> None:
+ """Toggle playback, i.e. player pause _if_ playing, else start play."""
+ if not self._mpv:
+ self._start_mpv()
+ assert self._mpv is not None
+ if 'playing' in self._mpv.playlist[self._idx]:
self._mpv.pause = not self._mpv.pause
- self._signal_update()
+ else:
+ self._play_at_index()
def prev(self) -> None:
"""Move player to previous item in playlist."""