ycdl/frontends/ycdl_flask/templates/channel.html
Ethan Dalool c667ebd872 Batch requests & filterbox & update metadata
- Download / Ignore on multiple videos is batched into a single
  request, like it should have been all along.
- Text box at the top of the page lets you search for terms live
  instead of loading various ?q urls.
- Doing a force refresh will actually update the metadata of old
  videos instead of skipping them.
2019-01-23 21:22:09 -08:00

397 lines
12 KiB
HTML

<!DOCTYPE html5>
<html>
<head>
{% import "header.html" as header %}
<title>{{channel['name']}}</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/common.css">
<script src="/static/common.js"></script>
<style>
#content_body
{
display: flex;
flex-direction: column;
min-width: 200px;
width: 100%;
max-width: 1440px;
margin-left: auto;
margin-right: auto;
}
.video_card
{
position: relative;
margin: 8px;
padding: 10px;
border-radius: 4px;
border: 1px solid #000;
}
.video_card:hover
{
box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25);
}
.video_card_pending
{
background-color: #ffffaa;
}
.video_card_ignored
{
background-color: #ffc886;
}
.video_card_selected
{
background-color: #13f4ff !important;
}
.video_card_downloaded
{
background-color: #aaffaa;
}
.action_toolbox
{
float: right;
display: inline-flex;
flex-direction: row;
position: relative;
}
.video_action_dropdown
{
z-index: 1;
background-color: #fff;
padding: 4px;
border: 1px solid #000;
position: absolute;
top: 100%;
right: 0;
display: none;
flex-direction: column;
}
</style>
</head>
<body>
{{header.make_header()}}
<div id="content_body">
{% if channel is not none %}
<span><button class="refresh_button"
onclick="refresh_channel('{{channel['id']}}', false, function(){location.reload()})">Refresh new videos</button></span>
<span><button class="refresh_button"
onclick="refresh_channel('{{channel['id']}}', true, function(){location.reload()})">Refresh everything</button></span>
<span><a href="/channel/{{channel['id']}}">All</a> <a href="/channel/{{channel['id']}}{{query_string}}">(?q)</a></span>
<span><a href="/channel/{{channel['id']}}/pending">Pending</a> <a href="/channel/{{channel['id']}}/pending{{query_string}}">(?q)</a></span>
<span><a href="/channel/{{channel['id']}}/ignored">Ignored</a> <a href="/channel/{{channel['id']}}/ignored{{query_string}}">(?q)</a></span>
<span><a href="/channel/{{channel['id']}}/downloaded">Downloaded</a> <a href="/channel/{{channel['id']}}/downloaded{{query_string}}">(?q)</a></span>
{% else %}
<span><a href="/videos">All</a> <a href="/videos{{query_string}}">(?q)</a></span>
<span><a href="/videos/pending">Pending</a> <a href="/videos/pending{{query_string}}">(?q)</a></span>
<span><a href="/videos/ignored">Ignored</a> <a href="/videos/ignored{{query_string}}">(?q)</a></span>
<span><a href="/videos/downloaded">Downloaded</a> <a href="/videos/downloaded{{query_string}}">(?q)</a></span>
{% endif %}
<span>{{videos|length}} items</span>
<center><input type="text" id="search_filter"/></center>
<center><span id="search_filter_count">{{videos|length}}</span> items</center>
<div id="video_cards">
{% for video in videos %}
<div id="video_card_{{video['id']}}"
data-ytid="{{video['id']}}"
onclick="onclick_select(event)"
class="video_card video_card_{{video['download']}}"
>
<img src="http://i3.ytimg.com/vi/{{video['id']}}/default.jpg" height="100px">
<a class="video_title" href="https://www.youtube.com/watch?v={{video['id']}}">{{video['_published_str']}} - {{video['title']}}</a>
<span>({{video['duration'] | seconds_to_hms}})</span>
{% if channel is none %}
<a href="/channel/{{video['author_id']}}">(Chan)</a>
{% endif %}
<div class="action_toolbox">
<button
{% if video['download'] == "pending" %}
class="video_action_pending hidden"
{% else %}
class="video_action_pending"
{% endif %}
onclick="action_button_passthrough(event, mark_video_state, 'pending')"
>Revert to Pending</button>
<button
{% if video['download'] == "pending" %}
class="video_action_download"
{% else %}
class="video_action_download hidden"
{% endif %}
onclick="action_button_passthrough(event, start_download)"
>Download</button>
<button
{% if video['download'] == "pending" %}
class="video_action_ignore"
{% else %}
class="video_action_ignore hidden"
{% endif %}
onclick="action_button_passthrough(event, mark_video_state, 'ignored')"
>Ignore</button>
</div>
<br>
<button class="show_embed_button" onclick="toggle_embed_video('{{video.id}}');">Embed</button>
<button class="hide_embed_button hidden" onclick="toggle_embed_video('{{video.id}}');">Unembed</button>
<br>
</div>
{% endfor %}
</div>
</div>
</body>
<script type="text/javascript">
var video_card_first_selected = null;
var video_card_selections = [];
var search_filter_box = document.getElementById("search_filter");
var search_filter_hook = function(event)
{
filter_video_cards(search_filter_box.value);
}
search_filter_box.addEventListener("keyup", search_filter_hook);
function filter_video_cards(search_term)
{
var count = 0;
video_cards = document.getElementById("video_cards");
video_cards.classList.add("hidden");
search_term = search_term.toLocaleLowerCase();
var cards = video_cards.children;
for (var index = 0; index < cards.length; index += 1)
{
var video_card = cards[index];
var title = video_card.getElementsByClassName("video_title")[0].innerText.toLocaleLowerCase();
if (search_term !== "" && title.indexOf(search_term) == -1)
{
video_card.classList.add("hidden");
}
else
{
video_card.classList.remove("hidden");
count += 1;
}
}
video_cards.classList.remove("hidden");
document.getElementById("search_filter_count").innerText = count;
}
function toggle_embed_video(video_id)
{
var video_card = document.getElementById("video_card_" + video_id);
var show_button = video_card.getElementsByClassName("show_embed_button")[0];
var hide_button = video_card.getElementsByClassName("hide_embed_button")[0];
var embeds = video_card.getElementsByTagName("iframe");
if (embeds.length == 0)
{
var html = `<iframe width="711" height="400" src="https://www.youtube.com/embed/${video_id}" frameborder="0" allow="encrypted-media" allowfullscreen></iframe>`
var embed = html_to_element(html);
video_card.appendChild(embed);
show_button.classList.add("hidden");
hide_button.classList.remove("hidden");
}
else
{
video_card.removeChild(embeds[0]);
show_button.classList.remove("hidden");
hide_button.classList.add("hidden");
}
}
function deselect_all()
{
var video_card_first_selected = null;
for (var index = 0; index < video_card_selections.length; index +=1)
{
video_card_selections[index].classList.remove("video_card_selected");
}
video_card_selections = [];
}
function html_to_element(html)
{
var template = document.createElement("template");
template.innerHTML = html;
return template.content.firstChild;
}
function onclick_select(event)
{
if (!event.target.classList.contains("video_card"))
{
return;
}
if (video_card_first_selected === null)
{
video_card_first_selected = event.target;
}
var video_cards = Array.from(document.getElementById("video_cards").children);
if (event.shiftKey === false && event.ctrlKey === false)
{
video_card_selections = [];
video_card_selections.push(event.target);
video_card_first_selected = event.target;
}
else if (event.shiftKey === true)
{
video_card_selections = [];
var start_index = video_cards.indexOf(video_card_first_selected);
var end_index = video_cards.indexOf(event.target);
if (end_index < start_index)
{
var temp = start_index;
start_index = end_index;
end_index = temp;
}
for (var index = start_index; index <= end_index; index += 1)
{
if (video_cards[index].classList.contains("hidden"))
{
continue;
}
video_card_selections.push(video_cards[index]);
}
}
else if (event.ctrlKey === true)
{
var existing_index = video_card_selections.indexOf(event.target)
if (existing_index == -1)
{
video_card_first_selected = event.target;
video_card_selections.push(event.target);
}
else
{
video_card_selections.splice(existing_index, 1);
}
}
for (var index = 0; index < video_cards.length; index += 1)
{
card = video_cards[index];
if (video_card_selections.indexOf(card) > -1)
{
card.classList.add("video_card_selected");
}
else
{
card.classList.remove("video_card_selected");
}
}
return false;
}
function action_button_passthrough(event, action_function, action_argument)
{
var elements;
var this_card = event.target.parentElement.parentElement;
if (video_card_selections.length > 0 && video_card_selections.indexOf(this_card) > -1)
{
elements = video_card_selections;
}
else
{
// Button -> button toolbox -> video card
elements = [this_card];
}
var video_ids = [];
for (var index = 0; index < elements.length; index += 1)
{
video_ids.push(elements[index].dataset["ytid"]);
}
video_ids = video_ids.join(",");
if (action_argument === undefined)
{
action_function(video_ids, receive_action_response);
}
else
{
action_function(video_ids, action_argument, receive_action_response);
}
deselect_all();
}
function give_action_buttons(video_card_div)
{
var button_pending = video_card_div.getElementsByClassName("video_action_pending")[0];
var button_download = video_card_div.getElementsByClassName("video_action_download")[0];
var button_ignore = video_card_div.getElementsByClassName("video_action_ignore")[0];
if (video_card_div.classList.contains("video_card_pending"))
{
button_download.classList.remove("hidden");
button_ignore.classList.remove("hidden");
button_pending.classList.add("hidden");
}
else
{
button_download.classList.add("hidden");
button_ignore.classList.add("hidden");
button_pending.classList.remove("hidden");
}
}
function receive_action_response(response)
{
var video_ids = response['video_ids'];
for (var index = 0; index < video_ids.length; index += 1)
{
var video_id = video_ids[index];
var state = response['state'];
var card = document.getElementById("video_card_" + video_id);
if (state == 'pending')
{
card.classList = ["video_card", "video_card_pending"].join(" ");
card.style.backgroundColor = "#ffffaa";
}
else if (state == 'ignored')
{
card.classList = ["video_card", "video_card_ignored"].join(" ");
card.style.backgroundColor = "#ffc886";
}
else if (state == 'downloaded')
{
card.classList = ["video_card", "video_card_downloaded"].join(" ");
card.style.backgroundColor = "#aaffaa";
}
give_action_buttons(card);
}
}
function refresh_channel(channel_id, force, callback)
{
var url = "/refresh_channel";
data = new FormData();
data.append("channel_id", channel_id);
data.append("force", force)
return post(url, data, callback);
}
function mark_video_state(video_ids, state, callback)
{
var url = "/mark_video_state";
data = new FormData();
data.append("video_ids", video_ids);
data.append("state", state);
return post(url, data, callback);
}
function start_download(video_ids, callback)
{
var url = "/start_download";
data = new FormData();
data.append("video_ids", video_ids);
return post(url, data, callback);
}
</script>
</html>