791 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			791 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html5>
 | |
| <html>
 | |
| <head>
 | |
|     {% import "header.html" as header %}
 | |
|     <title>{{channel.name}}</title>
 | |
|     <meta charset="UTF-8">
 | |
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 | |
|     <link rel="icon" href="/favicon.png" type="image/png"/>
 | |
|     <link rel="stylesheet" href="/static/css/common.css"/>
 | |
|     <link rel="stylesheet" href="/static/css/ycdl.css"/>
 | |
|     {% if theme %}<link rel="stylesheet" href="/static/css/theme_{{theme}}.css">{% endif %}
 | |
|     <script src="/static/js/common.js"></script>
 | |
|     <script src="/static/js/api.js"></script>
 | |
|     <script src="/static/js/hotkeys.js"></script>
 | |
|     <script src="/static/js/http.js"></script>
 | |
|     <script src="/static/js/spinners.js"></script>
 | |
| 
 | |
| <style>
 | |
| .tabbed_container .tab
 | |
| {
 | |
|     display: grid;
 | |
|     grid-auto-flow: row;
 | |
|     grid-gap: 8px;
 | |
|     padding: 8px;
 | |
| }
 | |
| #video_cards
 | |
| {
 | |
|     display: flex;
 | |
|     flex-direction: column;
 | |
|     row-gap: 8px;
 | |
| }
 | |
| .video_card
 | |
| {
 | |
|     position: relative;
 | |
|     display: grid;
 | |
|     grid-template:
 | |
|         "thumbnail details toolbox" auto
 | |
|         "embed embed embed" auto
 | |
|         /auto 1fr auto;
 | |
|     grid-gap: 4px;
 | |
| 
 | |
|     padding: 8px;
 | |
| 
 | |
|     border-radius: 4px;
 | |
| }
 | |
| 
 | |
| .video_thumbnail
 | |
| {
 | |
|     grid-area: thumbnail;
 | |
|     justify-self: center;
 | |
| }
 | |
| 
 | |
| .video_title
 | |
| {
 | |
|     word-break: break-word;
 | |
| }
 | |
| 
 | |
| .video_details
 | |
| {
 | |
|     grid-area: details;
 | |
|     align-self: center;
 | |
|     /*
 | |
|     margin-right prevents the empty space of the <a> tag from swallowing
 | |
|     click events meant for the video card.
 | |
|     */
 | |
|     margin-right: auto;
 | |
| }
 | |
| 
 | |
| .embed_toolbox
 | |
| {
 | |
|     grid-area: embed;
 | |
|     /*
 | |
|     disabling pointer events on the toolbox prevents it from swallowing click
 | |
|     events meant for the video card. Then we must re-enable them for child
 | |
|     elements so the embed button is still clickable.
 | |
|     This one uses pointer-events instead of margin because margin makes the
 | |
|     whole embed too small.
 | |
|     */
 | |
|     pointer-events: none;
 | |
| }
 | |
| .embed_toolbox *
 | |
| {
 | |
|     pointer-events: auto;
 | |
| }
 | |
| 
 | |
| .action_toolbox
 | |
| {
 | |
|     grid-area: toolbox;
 | |
|     justify-self: right;
 | |
|     display: inline-flex;
 | |
|     flex-direction: row;
 | |
|     position: relative;
 | |
|     margin-top: auto;
 | |
|     margin-bottom: auto;
 | |
| }
 | |
| 
 | |
| .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;
 | |
| }
 | |
| 
 | |
| /*
 | |
| Thank you SimonSimCity
 | |
| https://stackoverflow.com/a/35153397
 | |
| */
 | |
| .video_iframe_holder
 | |
| {
 | |
|     position: relative;
 | |
|     width: 100%;
 | |
|     height: 0;
 | |
|     padding-bottom: 56.25%;
 | |
| }
 | |
| .video_iframe_holder iframe
 | |
| {
 | |
|     position: absolute;
 | |
|     width: 100%;
 | |
|     height: 100%;
 | |
|     left: 0;
 | |
|     top: 0;
 | |
| }
 | |
| 
 | |
| @media screen and (max-width: 600px)
 | |
| {
 | |
|     .video_card
 | |
|     {
 | |
|         grid-template:
 | |
|             "thumbnail details"
 | |
|             "toolbox toolbox"
 | |
|             "embed embed"
 | |
|             /auto 1fr;
 | |
|     }
 | |
| }
 | |
| </style>
 | |
| </head>
 | |
| 
 | |
| 
 | |
| <body>
 | |
| {{header.make_header()}}
 | |
| <div id="content_body">
 | |
|     {% if channel is not none %}
 | |
|     <h1 id="channel_name">{{channel.name}}</h1>
 | |
|     {% endif %}
 | |
| 
 | |
|     {% if channel is not none %}
 | |
|     <div class="tabbed_container">
 | |
|     <div class="tab" data-tab-title="Videos">
 | |
|     <div><button class="refresh_button button_with_spinner" onclick="return refresh_channel_form(false);">Refresh new videos</button></div>
 | |
|     <div><button class="refresh_button button_with_spinner" onclick="return refresh_channel_form(true);">Refresh everything</button></div>
 | |
| 
 | |
|     {% endif %}
 | |
| 
 | |
|     <div>View
 | |
|     {% if channel is not none %}
 | |
|     <a class="merge_params {{"bold" if not state else ""}}" href="/channel/{{channel.id}}">All</a>
 | |
|     {% else %}
 | |
|     <a class="merge_params {{"bold" if not state else ""}}" href="/videos">All</a>
 | |
|     {% endif %}
 | |
| 
 | |
|     {% for statename in all_states %}
 | |
|     {% if channel is not none %}
 | |
|     <a class="merge_params {{"bold" if state == statename else ""}}" href="/channel/{{channel.id}}/{{statename}}">{{statename.capitalize()}}</a>
 | |
|     {% else %}
 | |
|     <a class="merge_params {{"bold" if state == statename else ""}}" href="/videos/{{statename}}">{{statename.capitalize()}}</a>
 | |
|     {% endif %}
 | |
|     {% endfor %}
 | |
|     </div>
 | |
| 
 | |
|     <div>Sort by
 | |
|     <a class="merge_params {{"bold" if orderby == "published" or not orderby else ""}}" href="?orderby=published">Date</a>
 | |
|     <a class="merge_params {{"bold" if orderby == "duration" else ""}}" href="?orderby=duration">Duration</a>
 | |
|     <a class="merge_params {{"bold" if orderby == "views" else ""}}" href="?orderby=views">Views</a>
 | |
|     <a class="merge_params {{"bold" if orderby == "random" else ""}}" href="?orderby=random">Random</a>
 | |
|     </div>
 | |
| 
 | |
|     <div id="video_cards">
 | |
|         <center><input disabled class="enable_on_pageload" type="text" id="search_filter"/></center>
 | |
|         <center><span id="search_filter_count">{{videos|length}}</span> {{state or ""}} items</center>
 | |
| 
 | |
|         {% for video in videos %}
 | |
|         <div id="video_card_{{video.id}}"
 | |
|         data-ytid="{{video.id}}"
 | |
|         onclick="return onclick_select(event);"
 | |
|         class="video_card video_card_{{video.state}}"
 | |
|         >
 | |
|             <img class="video_thumbnail" loading="lazy" src="https://i3.ytimg.com/vi/{{video.id}}/default.jpg" height="100px">
 | |
|             <div class="video_details">
 | |
|             <a class="video_title" href="https://www.youtube.com/watch?v={{video.id}}">{{video.published_string}} - {{video.title}}</a>
 | |
|             <span>({{video.duration | seconds_to_hms}})</span>
 | |
|             <span>({{video.views}})</span>
 | |
|             {% if video.is_shorts %}<span>(shorts)</span>{% endif %}
 | |
|             {% if channel is none %}
 | |
|             <a href="/channel/{{video.author_id}}">({{video.author.name if video.author else video.author_id}})</a> <a href="/channel/{{video.author_id}}/pending">(p)</a>
 | |
|             {% endif %}
 | |
|             </div>
 | |
| 
 | |
|             <div class="action_toolbox">
 | |
|                 <button
 | |
|                 {% if video.state == "pending" %}
 | |
|                 class="video_action_pending hidden"
 | |
|                 {% else %}
 | |
|                 class="video_action_pending"
 | |
|                 {% endif %}
 | |
|                 onclick="return action_button_passthrough(event, api.videos.mark_state, 'pending');"
 | |
|                 >Revert to Pending</button>
 | |
| 
 | |
|                 {% if video.live_broadcast is none %}
 | |
|                 <button
 | |
|                 {% if video.state == "pending" %}
 | |
|                 class="video_action_download"
 | |
|                 {% else %}
 | |
|                 class="video_action_download hidden"
 | |
|                 {% endif %}
 | |
|                 onclick="return action_button_passthrough(event, api.videos.start_download);"
 | |
|                 >Download</button>
 | |
|                 {% else %}
 | |
|                 <button disabled>Video is {{video.live_broadcast}}</button>
 | |
|                 {% endif %}
 | |
| 
 | |
|                 <button
 | |
|                 {% if video.state == "pending" %}
 | |
|                 class="video_action_ignore"
 | |
|                 {% else %}
 | |
|                 class="video_action_ignore hidden"
 | |
|                 {% endif %}
 | |
|                 onclick="return action_button_passthrough(event, api.videos.mark_state, 'ignored');"
 | |
|                 >Ignore</button>
 | |
|             </div>
 | |
|             <div class="embed_toolbox">
 | |
|             <button class="toggle_embed_button" onclick="return toggle_embed_video(event);">Embed</button>
 | |
|             </div>
 | |
|         </div>
 | |
|         {% endfor %}
 | |
|     </div> <!-- video_cards -->
 | |
| 
 | |
|     {% if channel is not none %}
 | |
|     </div> <!-- tab-videos -->
 | |
| 
 | |
|     <div class="tab" data-tab-title="Settings">
 | |
|     <div>Channel ID: <code>{{channel.id}}</code></div>
 | |
| 
 | |
|     <div>
 | |
|         <input type="text" id="set_name_input" placeholder="Name" size=32 value="{{channel.name or ''}}"/>
 | |
|         <button id="set_name_button" class="button_with_spinner" onclick="return set_name_form(event);">Set name</button>
 | |
|     </div>
 | |
| 
 | |
|     <div>
 | |
|         {% set checked = 'checked' if channel.autorefresh else '' %}
 | |
|         <label><input type="checkbox" id="set_autorefresh_checkbox" {{checked}} onchange="return set_autorefresh_form(event);"/> Automatically refresh this channel regularly.</label>
 | |
|         <span id="set_autorefresh_spinner" class="hidden">Working...</span>
 | |
|     </div>
 | |
| 
 | |
|     <div>
 | |
|         New videos are:
 | |
|         <select onchange="return set_automark_form(event);">
 | |
|             <option value="pending" {{"selected" if channel.automark == "pending" else ""}}  >pending</option>
 | |
|             <option value="downloaded" {{"selected" if channel.automark == "downloaded" else ""}}  >downloaded</option>
 | |
|             <option value="ignored" {{"selected" if channel.automark == "ignored" else ""}}  >ignored</option>
 | |
|         </select>
 | |
|         <span id="set_automark_spinner" class="hidden">Working...</span>
 | |
|     </div>
 | |
| 
 | |
|     <div>
 | |
|         {% set checked = 'checked' if channel.ignore_shorts else '' %}
 | |
|         <label><input type="checkbox" id="set_ignore_shorts_checkbox" {{checked}} onchange="return set_ignore_shorts_form(event);"/> Automatically ignore shorts (short vertical videos).</label>
 | |
|         <span id="set_ignore_shorts_spinner" class="hidden">Working...</span>
 | |
|     </div>
 | |
| 
 | |
|     <div>
 | |
|         <input type="text" id="set_queuefile_extension_input" placeholder="Queuefile extension" value="{{channel.queuefile_extension or ''}}"/>
 | |
|         <button id="set_queuefile_extension_button" class="button_with_spinner" onclick="return set_queuefile_extension_form(event);">Set extension</button>
 | |
|     </div>
 | |
| 
 | |
|     <div>
 | |
|         <input type="text" id="set_download_directory_input" placeholder="Queuefile directory" value="{{channel.download_directory.absolute_path if channel.download_directory else ''}}"/>
 | |
|         <button id="set_download_directory_button" class="button_with_spinner" onclick="return set_download_directory_form(event);">Set directory</button>
 | |
|         {% if request.is_localhost %}
 | |
|         <button id="show_directory_button" onclick="return show_download_directory_form(event);">Open directory</button>
 | |
|         {% endif %}
 | |
|     </div>
 | |
| 
 | |
|     <div><a href="https://www.youtube.com/channel/{{channel.id}}/videos">Channel page</a></div>
 | |
| 
 | |
|     <div><a href="https://www.youtube.com/feeds/videos.xml?channel_id={{channel.id}}">Channel RSS</a></div>
 | |
| 
 | |
|     <button class="red_button button_with_confirm"
 | |
|     data-prompt="Delete channel and all videos?"
 | |
|     data-onclick="return delete_channel_form(event);"
 | |
|     >Delete Channel</button>
 | |
| 
 | |
|     </div> <!-- tab-settings -->
 | |
| 
 | |
|     </div> <!-- tabbed_container -->
 | |
|     {% endif %}
 | |
| </div>
 | |
| </body>
 | |
| 
 | |
| 
 | |
| <script type="text/javascript">
 | |
| const CHANNEL_ID = "{{channel.id if channel else ""}}";
 | |
| 
 | |
| const STATE = "{{state if state else ""}}";
 | |
| 
 | |
| // FILTER BOX //////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| var search_filter_wait_for_typing;
 | |
| 
 | |
| var search_filter_hook = function(event)
 | |
| {
 | |
|     clearTimeout(search_filter_wait_for_typing);
 | |
|     search_filter_wait_for_typing = setTimeout(function()
 | |
|     {
 | |
|         filter_video_cards(search_filter_box.value);
 | |
|     }, 200);
 | |
| }
 | |
| 
 | |
| function filter_video_cards(search_term)
 | |
| {
 | |
|     /*
 | |
|     Apply the current download filter (pending, ignored, downloaded) by removing
 | |
|     mismatched cards from the dom.
 | |
|     Apply the search filter textbox by hiding the mismatched cards.
 | |
|     */
 | |
|     let count = 0;
 | |
|     const video_card_list = document.getElementById("video_cards");
 | |
|     video_card_list.classList.add("hidden");
 | |
|     const search_term_lower = search_term.toLocaleLowerCase();
 | |
|     const state_class = "video_card_" + STATE;
 | |
|     const video_cards = Array.from(video_card_list.getElementsByClassName("video_card"));
 | |
|     video_cards.forEach(function(video_card)
 | |
|     {
 | |
|         if (STATE && !video_card.classList.contains(state_class))
 | |
|         {
 | |
|             video_card_list.removeChild(video_card);
 | |
|             return;
 | |
|         }
 | |
|         const title_lower = video_card.getElementsByClassName("video_title")[0].innerText.toLocaleLowerCase();
 | |
|         const matches = (
 | |
|             title_lower.indexOf(search_term_lower) > -1 ||
 | |
|             search_term === video_card.dataset.ytid
 | |
|         );
 | |
|         if (matches)
 | |
|         {
 | |
|             video_card.classList.remove("hidden");
 | |
|             count += 1;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             video_card.classList.add("hidden");
 | |
|         }
 | |
|     });
 | |
|     video_card_list.classList.remove("hidden");
 | |
|     document.getElementById("search_filter_count").innerText = count;
 | |
| }
 | |
| 
 | |
| var search_filter_box = document.getElementById("search_filter");
 | |
| search_filter_box.addEventListener("keyup", search_filter_hook);
 | |
| 
 | |
| // VIDEO CARD SELECTION ////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| var video_card_selections = document.getElementsByClassName("video_card_selected");
 | |
| var video_card_first_selected = null;
 | |
| 
 | |
| function select_all()
 | |
| {
 | |
|     video_card_first_selected = null;
 | |
|     for (const video_card of document.querySelectorAll(".video_card:not(.hidden)"))
 | |
|     {
 | |
|         video_card.classList.add("video_card_selected");
 | |
|     }
 | |
| }
 | |
| 
 | |
| function select_all_hotkey()
 | |
| {
 | |
|     if (! CHANNEL_ID)
 | |
|     {
 | |
|         select_all();
 | |
|         return;
 | |
|     }
 | |
|     const tabbed_container = document.getElementsByClassName("tabbed_container")[0];
 | |
|     if (tabbed_container.dataset.activeTabId === "Videos")
 | |
|     {
 | |
|         select_all();
 | |
|     }
 | |
| }
 | |
| 
 | |
| function deselect_all()
 | |
| {
 | |
|     video_card_first_selected = null;
 | |
|     for (const video_card of Array.from(video_card_selections))
 | |
|     {
 | |
|         video_card.classList.remove("video_card_selected");
 | |
|     }
 | |
| }
 | |
| 
 | |
| function deselect_all_hotkey()
 | |
| {
 | |
|     if (! CHANNEL_ID)
 | |
|     {
 | |
|         deselect_all();
 | |
|         return;
 | |
|     }
 | |
|     const tabbed_container = document.getElementsByClassName("tabbed_container")[0];
 | |
|     if (tabbed_container.dataset.activeTabId === "Videos")
 | |
|     {
 | |
|         deselect_all();
 | |
|     }
 | |
| }
 | |
| 
 | |
| function select_one(event)
 | |
| {
 | |
|     deselect_all();
 | |
|     event.target.classList.add("video_card_selected");
 | |
|     video_card_first_selected = event.target;
 | |
| }
 | |
| 
 | |
| function select_shift(event)
 | |
| {
 | |
|     const video_cards = Array.from(document.querySelectorAll(".video_card:not(.hidden)"));
 | |
| 
 | |
|     let start_index = video_cards.indexOf(video_card_first_selected);
 | |
|     let end_index = video_cards.indexOf(event.target);
 | |
|     if (end_index < start_index)
 | |
|     {
 | |
|         [start_index, end_index] = [end_index, start_index];
 | |
|     }
 | |
| 
 | |
|     for (let index = start_index; index <= end_index; index += 1)
 | |
|     {
 | |
|         video_cards[index].classList.add("video_card_selected");
 | |
|     }
 | |
| }
 | |
| 
 | |
| function select_ctrl(event)
 | |
| {
 | |
|     if (event.target.classList.contains("video_card_selected"))
 | |
|     {
 | |
|         event.target.classList.remove("video_card_selected");
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         video_card_first_selected = event.target;
 | |
|         event.target.classList.add("video_card_selected");
 | |
|     }
 | |
| }
 | |
| 
 | |
| function onclick_select(event)
 | |
| {
 | |
|     if (!event.target.classList.contains("video_card"))
 | |
|     {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (video_card_first_selected === null)
 | |
|     {
 | |
|         video_card_first_selected = event.target;
 | |
|     }
 | |
| 
 | |
|     if (event.shiftKey === false && event.ctrlKey === false)
 | |
|     {
 | |
|         select_one(event);
 | |
|     }
 | |
|     else if (event.shiftKey === true)
 | |
|     {
 | |
|         select_shift(event);
 | |
|     }
 | |
|     else if (event.ctrlKey === true)
 | |
|     {
 | |
|         select_ctrl(event);
 | |
|     }
 | |
| 
 | |
|     document.getSelection().removeAllRanges();
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| // VIDEO CARD BUTTONS //////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| function action_button_passthrough(event, action_function, action_argument)
 | |
| {
 | |
|     let elements;
 | |
|     const this_card = event.target.closest(".video_card");
 | |
|     if (this_card.classList.contains("video_card_selected"))
 | |
|     {
 | |
|         // The clicked card is indeed part of the current selected group.
 | |
|         elements = Array.from(video_card_selections);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         // The clicked card is actually not one of the selected, so we'll just
 | |
|         // action it by itself.
 | |
|         elements = [this_card];
 | |
|     }
 | |
| 
 | |
|     const video_ids = elements.map(element => element.dataset["ytid"]).join(",");
 | |
| 
 | |
|     if (action_argument === undefined)
 | |
|     {
 | |
|         action_function(video_ids, receive_action_response);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         action_function(video_ids, action_argument, receive_action_response);
 | |
|     }
 | |
|     if (! event.shiftKey)
 | |
|     {
 | |
|         deselect_all();
 | |
|     }
 | |
| }
 | |
| 
 | |
| function give_action_buttons(video_card_div)
 | |
| {
 | |
|     const toolbox = video_card_div.getElementsByClassName("action_toolbox")[0]
 | |
|     const buttons = Array.from(toolbox.getElementsByTagName("button"));
 | |
|     const is_pending = video_card_div.classList.contains("video_card_pending");
 | |
|     for (const button of buttons)
 | |
|     {
 | |
|         if (is_pending)
 | |
|             { button.classList.remove("hidden"); }
 | |
|         else
 | |
|             { button.classList.add("hidden"); }
 | |
|     }
 | |
| 
 | |
|     const button_pending = video_card_div.getElementsByClassName("video_action_pending")[0];
 | |
|     if (is_pending)
 | |
|         { button_pending.classList.add("hidden"); }
 | |
|     else
 | |
|         { button_pending.classList.remove("hidden"); }
 | |
| }
 | |
| 
 | |
| function receive_action_response(response)
 | |
| {
 | |
|     if (response.meta.status !== 200)
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|         return;
 | |
|     }
 | |
|     const video_ids = response.data.video_ids;
 | |
|     const state = response.data.state;
 | |
|     const state_class = "video_card_" + state;
 | |
|     for (const video_id of video_ids)
 | |
|     {
 | |
|         const card = document.getElementById("video_card_" + video_id);
 | |
|         {% for statename in all_states %}
 | |
|         card.classList.remove("video_card_{{statename}}");
 | |
|         {% endfor %}
 | |
|         card.classList.add(state_class);
 | |
|         give_action_buttons(card);
 | |
|     }
 | |
| }
 | |
| 
 | |
| function toggle_embed_video(event)
 | |
| {
 | |
|     const video_card = event.target.closest(".video_card");
 | |
|     const video_id = video_card.dataset.ytid;
 | |
|     const toggle_button = video_card.getElementsByClassName("toggle_embed_button")[0];
 | |
|     const embed_toolbox = video_card.getElementsByClassName("embed_toolbox")[0];
 | |
|     const embeds = video_card.getElementsByClassName("video_iframe_holder");
 | |
|     if (embeds.length == 0)
 | |
|     {
 | |
|         const html = `
 | |
|         <div class="video_iframe_holder">
 | |
|         <iframe width="711" height="400" frameborder="0" allow="encrypted-media" allowfullscreen
 | |
|         src="https://www.youtube.com/embed/${video_id}"></iframe>
 | |
|         </div>
 | |
|         `
 | |
|         const embed = common.html_to_element(html);
 | |
|         embed_toolbox.appendChild(embed);
 | |
|         toggle_button.innerText = "Unembed";
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         embeds[0].parentElement.removeChild(embeds[0]);
 | |
|         toggle_button.innerText = "Embed";
 | |
|     }
 | |
| }
 | |
| 
 | |
| // CHANNEL ACTIONS /////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| function delete_channel_form()
 | |
| {
 | |
|     api.channels.delete_channel(CHANNEL_ID, api.channels.callback_go_to_channels);
 | |
| }
 | |
| 
 | |
| function refresh_channel_form(force)
 | |
| {
 | |
|     console.log(`Refreshing channel ${CHANNEL_ID}, force=${force}.`);
 | |
|     api.channels.refresh_channel(CHANNEL_ID, force, refresh_channel_callback)
 | |
| }
 | |
| 
 | |
| function refresh_channel_callback(response)
 | |
| {
 | |
|     if (response.meta.status == 200)
 | |
|     {
 | |
|         common.refresh();
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|         for (let button of document.getElementsByClassName("refresh_button"))
 | |
|         {
 | |
|             window[button.dataset.spinnerCloser]();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| function set_automark_form(event)
 | |
| {
 | |
|     set_automark_spinner.show();
 | |
|     api.channels.set_automark(CHANNEL_ID, event.target.value, set_automark_callback);
 | |
| }
 | |
| 
 | |
| function set_automark_callback(response)
 | |
| {
 | |
|     if (response.meta.status != 200)
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|     }
 | |
|     set_automark_spinner.hide();
 | |
| }
 | |
| 
 | |
| function set_autorefresh_form(event)
 | |
| {
 | |
|     set_autorefresh_spinner.show();
 | |
|     api.channels.set_autorefresh(CHANNEL_ID, event.target.checked, set_autorefresh_callback);
 | |
| }
 | |
| 
 | |
| function set_autorefresh_callback(response)
 | |
| {
 | |
|     set_autorefresh_spinner.hide();
 | |
|     if (response.meta.status != 200)
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|         return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function set_download_directory_form(event)
 | |
| {
 | |
|     const download_directory = set_download_directory_input.value.trim();
 | |
|     api.channels.set_download_directory(CHANNEL_ID, download_directory, set_download_directory_callback);
 | |
| }
 | |
| 
 | |
| function set_download_directory_callback(response)
 | |
| {
 | |
|     window[set_download_directory_button.dataset.spinnerCloser]();
 | |
|     if (response.meta.status != 200 || ! response.meta.json_ok)
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const download_directory = response.data.download_directory;
 | |
|     if (download_directory === null)
 | |
|     {
 | |
|         set_download_directory_input.value = "";
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         set_download_directory_input.value = download_directory;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function set_ignore_shorts_form(event)
 | |
| {
 | |
|     set_ignore_shorts_spinner.show();
 | |
|     api.channels.set_ignore_shorts(CHANNEL_ID, event.target.checked, set_ignore_shorts_callback);
 | |
| }
 | |
| 
 | |
| function set_ignore_shorts_callback(response)
 | |
| {
 | |
|     set_ignore_shorts_spinner.hide();
 | |
|     if (response.meta.status != 200)
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|         return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function set_name_form(event)
 | |
| {
 | |
|     const name = set_name_input.value.trim();
 | |
|     api.channels.set_name(CHANNEL_ID, name, set_name_callback);
 | |
| }
 | |
| 
 | |
| function set_name_callback(response)
 | |
| {
 | |
|     window[set_name_button.dataset.spinnerCloser]();
 | |
|     if (response.meta.status != 200 || ! response.meta.json_ok)
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const name = response.data.name;
 | |
|     const h1 = document.getElementById("channel_name");
 | |
|     if (name === null)
 | |
|     {
 | |
|         set_name_input.value = "";
 | |
|         h1.innerText = response.data.id;
 | |
|         document.title = response.data.id;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         set_name_input.value = name;
 | |
|         h1.innerText = name;
 | |
|         document.title = name;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| function set_queuefile_extension_form(event)
 | |
| {
 | |
|     const extension = set_queuefile_extension_input.value.trim();
 | |
|     api.channels.set_queuefile_extension(CHANNEL_ID, extension, set_queuefile_extension_callback);
 | |
| }
 | |
| 
 | |
| function set_queuefile_extension_callback(response)
 | |
| {
 | |
|     window[set_queuefile_extension_button.dataset.spinnerCloser]();
 | |
|     if (response.meta.status != 200 || ! response.meta.json_ok)
 | |
|     {
 | |
|         alert(JSON.stringify(response));
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const extension = response.data.queuefile_extension;
 | |
|     if (extension === null)
 | |
|     {
 | |
|         set_queuefile_extension_input.value = "";
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         set_queuefile_extension_input.value = extension;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function show_download_directory_form()
 | |
| {
 | |
|     function callback(response)
 | |
|     {
 | |
|         if (response.meta.status !== 200)
 | |
|         {
 | |
|             alert(JSON.stringify(response));
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     api.channels.show_download_directory(CHANNEL_ID, callback);
 | |
| }
 | |
| 
 | |
| if (CHANNEL_ID)
 | |
| {
 | |
|     var set_name_input = document.getElementById("set_name_input");
 | |
|     var set_name_button = document.getElementById("set_name_button");
 | |
|     common.bind_box_to_button(set_name_input, set_name_button);
 | |
| 
 | |
|     var set_queuefile_extension_input = document.getElementById("set_queuefile_extension_input");
 | |
|     var set_queuefile_extension_button = document.getElementById("set_queuefile_extension_button");
 | |
|     common.bind_box_to_button(set_queuefile_extension_input, set_queuefile_extension_button);
 | |
| 
 | |
|     var set_download_directory_input = document.getElementById("set_download_directory_input");
 | |
|     var set_download_directory_button = document.getElementById("set_download_directory_button");
 | |
|     common.bind_box_to_button(set_download_directory_input, set_download_directory_button);
 | |
| 
 | |
|     var set_automark_spinner = document.getElementById("set_automark_spinner");
 | |
|     set_automark_spinner = new spinners.Spinner(set_automark_spinner);
 | |
| 
 | |
|     var set_autorefresh_spinner = document.getElementById("set_autorefresh_spinner");
 | |
|     set_autorefresh_spinner = new spinners.Spinner(set_autorefresh_spinner);
 | |
| 
 | |
|     var set_ignore_shorts_spinner = document.getElementById("set_ignore_shorts_spinner");
 | |
|     set_ignore_shorts_spinner = new spinners.Spinner(set_ignore_shorts_spinner);
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| function on_pageload()
 | |
| {
 | |
|     hotkeys.register_hotkey("ctrl a", select_all_hotkey, "Select all videos.");
 | |
|     hotkeys.register_hotkey("ctrl d", deselect_all_hotkey, "Deselect all videos.");
 | |
| }
 | |
| document.addEventListener("DOMContentLoaded", on_pageload);
 | |
| </script>
 | |
| </html>
 |