{% block css %}
-#status { text-align: center; font-weight: bold; }
table { width: 100%; }
+#status { text-align: center; font-weight: bold; }
th { text-align: center; }
td { vertical-align: top; }
td.history { width: 50%; }
<input type="submit" name="next" value="next">
<input type="submit" name="stop" value="{% if running %}stop{% else %}start{% endif %}">
<input type="submit" name="reload" value="reload">
-</form>
</td></tr>
<tr><th>past</th><th>future</th></tr>
<tr>
<td class="history">
-<ul>{% for file in prev_files %}
-<li><a href="/{{page_names.file}}/{{file.rel_path_b64}}">{{ file.basename }}</a>
-{% endfor %}</ul>
+<table>
+{% for idx, file in prev_files_w_idx %}
+<tr>
+<td><input type="submit" name="jump_{{idx}}" value=">" /></td>
+<td><a href="/{{page_names.file}}/{{file.rel_path_b64}}">{{ file.basename }}</a></td>
+</tr>
+{% endfor %}
+</table>
</td>
<td class="history">
-<ul>{% for file in next_files %}
-<li><a href="/{{page_names.file}}/{{file.rel_path_b64}}">{{ file.basename }}</a>
-{% endfor %}</ul>
+<table>
+{% for idx, file in next_files_w_idx %}
+<tr>
+<td><input type="submit" name="jump_{{idx}}" value=">" /></td>
+<td><a href="/{{page_names.file}}/{{file.rel_path_b64}}">{{ file.basename }}</a></td>
+</tr>
+{% endfor %}
+</table>
</td>
</tr>
+</form>
</table>
{% endblock %}
B64Str = NewType('B64Str', str)
PageNames: TypeAlias = dict[str, PathStr]
DownloadsIndex: TypeAlias = dict[YoutubeId, PathStr]
+FilesWithIndex: TypeAlias = list[tuple[int, 'VideoFile']]
TemplateContext: TypeAlias = dict[
str,
None | bool
- | PageNames | PathStr | PlayerUpdateId | QueryText | QuotaCost
- | 'VideoFile' | YoutubeId | 'YoutubeVideo'
+ | FilesWithIndex | PageNames | PathStr | PlayerUpdateId | QueryText
+ | QuotaCost | 'VideoFile' | YoutubeId | 'YoutubeVideo'
| list[FlagName] | list['VideoFile'] | list['YoutubeVideo']
| list['YoutubeQuery']
]
@staticmethod
def _if_mpv_available(f) -> Callable:
- def wrapper(self):
- return f(self) if self._mpv else None
+ def wrapper(self, *args, **kwargs):
+ return f(self, *args, **kwargs) if self._mpv else None
return wrapper
def _signal_update(self) -> None:
return self._files[self._idx]
@property
- def prev_files(self) -> list[VideoFile]:
+ def _files_w_idx(self) -> FilesWithIndex:
+ return list(enumerate(self._files))
+
+ @property
+ def prev_files_w_idx(self) -> FilesWithIndex:
"""List 'past' files of playlist."""
- return list(reversed(self._files[:self._idx]))
+ return list(reversed(self._files_w_idx[:self._idx]))
@property
- def next_files(self) -> list[VideoFile]:
+ def next_files_w_idx(self) -> FilesWithIndex:
"""List 'coming' files of playlist."""
- return self._files[self._idx + 1:]
+ return self._files_w_idx[self._idx + 1:]
@property
def is_running(self) -> bool:
else:
self._mpv.command('playlist-play-index', max_idx)
+ @_if_mpv_available
+ def jump_to(self, target_idx: int) -> None:
+ """Move player to target_idx position in playlist."""
+ assert self._mpv is not None
+ self._mpv.command('playlist-play_index', target_idx)
+
def reload(self) -> None:
"""Close MPV, re-read (and re-shuffle) filenames, then re-start MPV."""
self._kill_mpv()
self.server.player.toggle_run()
elif 'reload' == command:
self.server.player.reload()
+ elif command.startswith('jump_'):
+ self.server.player.jump_to(int(command.split('_')[1]))
sleep(0.5) # avoid redir happening before current_file update
self._redirect(PathStr('/'))
'running': self.server.player.is_running,
'paused': self.server.player.is_paused,
'current_video': self.server.player.current_file,
- 'prev_files': self.server.player.prev_files,
- 'next_files': self.server.player.next_files})
+ 'prev_files_w_idx': self.server.player.prev_files_w_idx,
+ 'next_files_w_idx': self.server.player.next_files_w_idx})