summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--assets/css/default.css16
-rw-r--r--assets/js/watch.js52
-rw-r--r--src/invidious/views/watch.ecr28
3 files changed, 96 insertions, 0 deletions
diff --git a/assets/css/default.css b/assets/css/default.css
index 5fac9c2c..c9893190 100644
--- a/assets/css/default.css
+++ b/assets/css/default.css
@@ -267,3 +267,19 @@ img.thumbnail {
margin-right: 2em;
height: 0;
}
+
+#progress-container {
+ width: 100%;
+ border-radius: 2px;
+ background: #aaa;
+ display: none;
+}
+
+#download-progress {
+ width: 0%;
+ border-radius: 2px;
+ height: 10px;
+ background-color: #0078e7;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
diff --git a/assets/js/watch.js b/assets/js/watch.js
index 99500686..d8167114 100644
--- a/assets/js/watch.js
+++ b/assets/js/watch.js
@@ -50,3 +50,55 @@ function hide_youtube_replies(target) {
target.innerHTML = "Show replies";
target.setAttribute("onclick", "show_youtube_replies(this)");
}
+
+function download_video(title) {
+ var children = document.getElementById("download_widget").children;
+ var progress = document.getElementById("download-progress");
+ var url = "";
+
+ document.getElementById("progress-container").style.display = "";
+
+ for (i = 0; i < children.length; i++) {
+ if (children[i].selected) {
+ url = children[i].getAttribute("data-url");
+ }
+ }
+
+ url = "/videoplayback" + url.split("/videoplayback")[1];
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url);
+ xhr.responseType = "arraybuffer";
+
+ xhr.onprogress = function(event) {
+ if (event.lengthComputable) {
+ progress.style.width = "" + (event.loaded / event.total)*100 + "%";
+ }
+ };
+
+ xhr.onload = function(event) {
+ if (event.currentTarget.status != 200) {
+ console.log("Downloading " + title + " failed.")
+ return;
+ }
+
+ var data = new Blob([xhr.response], {'type' : 'video/mp4'});
+ var videoFile = window.URL.createObjectURL(data);
+
+ var link = document.createElement('a');
+ link.href = videoFile;
+ link.setAttribute('download', title);
+ document.body.appendChild(link);
+
+ window.requestAnimationFrame(function() {
+ var event = new MouseEvent('click');
+ link.dispatchEvent(event);
+ document.body.removeChild(link);
+ });
+
+ document.getElementById("progress-container").style.display = "none";
+ progress.style.width = "0%";
+ };
+
+ xhr.send(null);
+} \ No newline at end of file
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr
index db7dcb7b..3f1ff3bc 100644
--- a/src/invidious/views/watch.ecr
+++ b/src/invidious/views/watch.ecr
@@ -53,6 +53,34 @@
<div class="pure-u-1 pure-u-md-1-5">
<div class="h-box">
<p><a href="https://www.youtube.com/watch?v=<%= video.id %>"><%= translate(locale, "Watch video on Youtube") %></a></p>
+
+ <form class="pure-form pure-form-stacked">
+ <div class="pure-control-group">
+ <label for="download_widget"><%= translate(locale, "Download as: ") %></label>
+ <select style="width:100%" name="download_widget" id="download_widget">
+ <% video_streams.each do |option| %>
+ <option data-url="<%= option["url"] %>"><%= option["quality_label"] %> - <%= option["type"].split(";")[0] %> @ <%= option["fps"] %>fps - video only</option>
+ <% end %>
+ <% audio_streams.each do |option| %>
+ <option data-url="<%= option["url"] %>"><%= option["type"].split(";")[0] %> @ <%= option["bitrate"] %>k - audio only</option>
+ <% end %>
+ <% fmt_stream.each do |option| %>
+ <option data-url="<%= option["url"] %>"><%= itag_to_metadata?(option["itag"]).try &.["height"]? || "~240" %>p - <%= option["type"].split(";")[0] %></option>
+ <% end %>
+ </select>
+ </div>
+
+ <div id="progress-container" style="width:100%; display:none">
+ <div id="download-progress">
+ </div>
+ </div>
+
+ <button type="button" onclick='download_video("<%= video.title.dump_unquoted %>-<%= video.id %>.mp4")'
+ class="pure-button pure-button-primary">
+ <%= translate(locale, "Download") %>
+ </button>
+ </form>
+
<p><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p>
<p><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p>
<p><i class="icon ion-ios-thumbs-down"></i> <%= number_with_separator(video.dislikes) %></p>