else {
console.log("Error does not seem connection-related, therefore aborting."); }}}
-async function send_to(data, target, verbose=false) {
- if (verbose) { console.log(`Trying to send to ${target}:`, data); }
+async function wrapped_fetch(target, fetch_kwargs=null, verbose=false) {
+ if (verbose) {
+ console.log(`Trying to fetch ${target}, kwargs:`, fetch_kwargs);
+ }
try {
- const response = await fetch(target, {
- method: "POST",
- headers: {"Content-Type": "application/json"},
- body: JSON.stringify(data) });
+ const response = await (fetch_kwargs ? fetch(target, fetch_kwargs) : fetch(target));
if (200 != response.status) {
- console.log(`Got unexpected response on sending to ${target}:`, response); }
- else if (verbose) { console.log("Got response:", response); }}
- catch(error) {
- console.log(`Error on sending to ${target}:`, error); }}
+ console.log(`Got unexpected fetch response from ${target}:`, response);
+ } else {
+ return response;
+ }
+ } catch(error) {
+ console.log(`Error on sending to ${target}:`, error);
+ }
+}
+
+async function post_to(data, target, verbose=false) {
+ const fetch_kwargs = {
+ method: "POST",
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify(data)
+ };
+ wrapped_fetch(target, fetch_kwargs, verbose);
+}
function player_command(command) {
- send_to({command: [command]}, PATH_PLAYER); }
+ post_to({command: [command]}, PATH_PLAYER);
+}
event_handlers.push(function(data) { // update player state
for (const [id, text] of [
{% block script %}
{{ macros.js_new_child_to() }}
+const PATH_FILES_JSON = "/{{page_names.files_json}}";
const all_tags = {{showable_tags|tojson|safe}};
-var needed_tags = {{needed_tags|tojson|safe}};
+var needed_tags = [];
function select_tag() {
if (tags_select.selectedIndex < 1) {
}
const chosen_tag = document.getElementById('tags_select').value;
needed_tags.push(chosen_tag);
- reload_selector();
+ update_filter_inputs();
}
-function reload_selector() {
+function update_filter_inputs() {
const tags_select = document.getElementById('tags_select');
while (tags_select.options.length > 0) {
tags_select.remove(0);
}
- new_child_to('option', tags_select, 'add tag');
+ new_child_to('option', tags_select, 'add');
all_tags.forEach((tag) => {
if (needed_tags.includes(tag)) {
return;
tag_text_node.remove();
btn_del.remove();
needed_tags = needed_tags.filter(tag => tag !== chosen_tag);
- reload_selector();
+ update_filter_inputs();
};
- const input = new_child_to('input', tags_div);
- input.type = 'hidden';
- input.name = 'needed_tag';
- input.value = chosen_tag;
});
+ update_files_list();
}
-window.addEventListener('load', reload_selector);
+async function update_files_list() {
+ const filter_path = encodeURIComponent(document.getElementById("input_filter_path").value);
+ let target = `${PATH_FILES_JSON}?filter_path=${filter_path}`;
+ if (document.getElementById("input_show_absent").checked) { target = `${target}&show_absent=1`; }
+ needed_tags.forEach((tag) => target = `${target}&needed_tag=${encodeURIComponent(tag)}`);
+ const files_data = await wrapped_fetch(target).then((response) => response.json());
+ document.getElementById("files_count").textContent = `${files_data.length}`;
+ const table = document.getElementById("files_table");
+ Array.from(document.getElementsByClassName("file_row")).forEach((row) => row.remove());
+ files_data.forEach((file) => {
+ const tr = new_child_to('tr', table);
+ tr.classList.add("file_row");
+ new_child_to('td', tr, file.size);
+ const td_play = new_child_to('td', tr);
+ const btn_play = new_child_to('input', td_play);
+ btn_play.type = 'submit';
+ btn_play.name = `play_${file.digest}`;
+ btn_play.value = 'play';
+ btn_play.disabled = !file.present;
+ new_child_to('td', tr, file.tags_showable.join(", "));
+ const td_link = new_child_to('td', tr);
+ const a = new_child_to('a', td_link, file.rel_path);
+ a.href = `${PATH_PREFIX_FILE}${file.digest}`;
+ });
+}
+
+window.addEventListener('load', update_filter_inputs);
{% endblock %}
{% block body %}
-<form method="GET">
-<input type="checkbox" name="show_absent" {% if show_absent %}checked{% endif %}/> show absent<br />
-filename: <input name="filter_path" value="{{filter_path}}" /><br />
-tags: <span id="tags"></span><br />
-<select id="tags_select" onchange="select_tag()"></select><br />
-<input type="submit" value="filter" />
-</form>
-<p>known files (shown: {{files|length}}):</p>
+filename pattern: <input id="input_filter_path" oninput="update_files_list()" /><br />
+show absent: <input id="input_show_absent" type="checkbox" onclick="update_files_list()" /><br />
+needed tags: <select id="tags_select" onchange="select_tag()"></select><br />
+<span id="tags"></span><br />
+<hr />
+<p>known files (shown: <span id="files_count">?</span>):</p>
<form action="/{{page_names.files}}" method="POST">
<input type="hidden" name="redir_target" value="{{redir_target}}" />
-<table>
+<table id="files_table">
<tr><th>size</th><th>actions</th><th>tags</th><th>path</th></tr>
-{% for file in files %}
-<tr>
-<td>{{ file.size | round(1) }}</td>
-<td><input type="submit" name="play_{{file.digest.b64}}" value="play" {% if not file.present %}disabled {% endif %}/></td>
-<td>{{file.tags_showable.joined}}</td>
-<td><a href="/{{page_names.file}}/{{file.digest.b64}}">{{file.rel_path}}</a></td>
-</tr>
-{% endfor %}
</table>
</form>
{% endblock %}
a_file.href = `${PATH_PREFIX_FILE}${file.digest}`; }})
function redo_playlist() {
- send_to({filter_path: [document.getElementsByName('filter_path')[0].value],
- needed_tags: [document.getElementsByName('needed_tags')[0].value]},
- PATH_PLAYER);
- player_command('reload'); }
+ post_to({filter_path: [document.getElementsByName('filter_path')[0].value],
+ needed_tags: [document.getElementsByName('needed_tags')[0].value]},
+ PATH_PLAYER);
+ player_command('reload');
+}
{% endblock %}
'events': 'events',
'file': 'file',
'files': 'files',
+ 'files_json': 'files.json',
'missing': 'missing',
'player': 'player',
'playlist': 'playlist',
self._send_file_data()
elif self.pagename == PAGE_NAMES['files']:
self._send_files_index()
+ elif self.pagename == PAGE_NAMES['files_json']:
+ self._send_files_json()
elif self.pagename == PAGE_NAMES['missing']:
self._send_missing_json()
elif self.pagename == PAGE_NAMES['thumbnails']:
'unused_tags': unused_tags})
def _send_files_index(self) -> None:
- filter_path = FilterStr(self.params.first_for('filter_path'))
- show_absent = bool(self.params.first_for('show_absent'))
- needed_tags_list = self.params.all_for('needed_tag')
+ with DbConn() as conn:
+ showable_tags = sorted(list(VideoFile.all_tags_showable(conn)))
+ self._send_rendered_template(_NAME_TEMPLATE_FILES,
+ {'showable_tags': showable_tags})
+
+ def _send_files_json(self) -> None:
with DbConn() as conn:
files = VideoFile.get_filtered(
conn,
- filter_path,
- TagSet(set(needed_tags_list)),
- show_absent)
- showable_tags = sorted(list(VideoFile.all_tags_showable(conn)))
+ FilterStr(self.params.first_for('filter_path')),
+ TagSet(set(self.params.all_for('needed_tag'))),
+ bool(self.params.first_for('show_absent')))
files.sort(key=lambda t: t.rel_path)
- self._send_rendered_template(_NAME_TEMPLATE_FILES,
- {'files': files,
- 'selected': 'files',
- 'filter_path': filter_path,
- 'showable_tags': showable_tags,
- 'needed_tags': needed_tags_list,
- 'show_absent': show_absent})
+ self.send_http(bytes(json_dumps([f.as_dict for f in files]),
+ encoding='utf8'),
+ headers=[(_HEADER_CONTENT_TYPE, MIME_APP_JSON)])
def _send_missing_json(self) -> None:
with DbConn() as conn:
def _renew_last_update(self):
self.last_update = DatetimeStr(datetime.now().strftime(TIMESTAMP_FMT))
+ @property
+ def as_dict(self) -> dict[str, bool | str | list[str]]:
+ """Return dict of values relevant for /files."""
+ return {
+ 'digest': self.digest.b64,
+ 'present': self.present,
+ 'rel_path': str(self.rel_path),
+ 'size': f'{self.size:.2f}',
+ 'tags_showable': self.tags_showable.as_str_list,
+ }
+
def save(self, conn: DbConn) -> None:
"""Extend super().save by new .last_update if sufficient changes."""
if hash(self) != self._hash_on_last_update: