summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--assets/js/watch.js54
-rw-r--r--src/invidious.cr298
-rw-r--r--src/invidious/views/components/feed_menu.ecr19
-rw-r--r--src/invidious/views/index.ecr24
-rw-r--r--src/invidious/views/popular.ecr24
-rw-r--r--src/invidious/views/preferences.ecr52
-rw-r--r--src/invidious/views/subscriptions.ecr2
-rw-r--r--src/invidious/views/template.ecr18
-rw-r--r--src/invidious/views/top.ecr24
-rw-r--r--src/invidious/views/trending.ecr24
-rw-r--r--src/invidious/views/watch.ecr22
11 files changed, 259 insertions, 302 deletions
diff --git a/assets/js/watch.js b/assets/js/watch.js
index 37ca4e43..7a68c30c 100644
--- a/assets/js/watch.js
+++ b/assets/js/watch.js
@@ -50,57 +50,3 @@ function hide_youtube_replies(target, inner_text, sub_text) {
target.innerHTML = sub_text;
target.setAttribute("onclick", "show_youtube_replies(this, \'" + inner_text + "\', \'" + sub_text + "\')");
}
-
-function download_video(target) {
- var title = target.getAttribute("data-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");
- }
- }
-
- 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.")
- document.getElementById("progress-container").style.display = "none";
- progress.style.width = "0%";
-
- 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.cr b/src/invidious.cr
index 971f0fbd..9f0f6a3f 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -175,6 +175,7 @@ before_all do |env|
locale = user.preferences.locale
env.set "user", user
+ env.set "preferences", user.preferences
env.set "sid", sid
end
else
@@ -187,12 +188,20 @@ before_all do |env|
locale = user.preferences.locale
env.set "user", user
+ env.set "preferences", user.preferences
env.set "sid", sid
rescue ex
end
end
end
+ if env.request.cookies.has_key? "PREFS"
+ preferences = Preferences.from_json(env.request.cookies["PREFS"].value)
+
+ locale = preferences.locale
+ env.set "preferences", preferences
+ end
+
locale = env.params.query["hl"]? || locale
locale ||= "en-US"
env.set "locale", locale
@@ -268,11 +277,12 @@ get "/watch" do |env|
nojs ||= "0"
nojs = nojs == "1"
- user = env.get? "user"
- if user
- user = user.as(User)
+ if env.get? "preferences"
+ preferences = env.get("preferences").as(Preferences)
+ end
- preferences = user.preferences
+ if env.get? "user"
+ user = env.get("user").as(User)
subscriptions = user.subscriptions
watched = user.watched
end
@@ -866,17 +876,31 @@ post "/login" do |env|
host = URI.parse(env.request.headers["Host"]).host
+ if Kemal.config.ssl || CONFIG.https_only
+ secure = true
+ else
+ secure = false
+ end
+
login.cookies.each do |cookie|
if Kemal.config.ssl || CONFIG.https_only
- cookie.secure = true
+ cookie.secure = secure
else
- cookie.secure = false
+ cookie.secure = secure
end
cookie.extension = cookie.extension.not_nil!.gsub(".youtube.com", host)
cookie.extension = cookie.extension.not_nil!.gsub("Secure; ", "")
end
+ if env.request.cookies["PREFS"]?
+ preferences = env.get("preferences").as(Preferences)
+ PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email)
+
+ login.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", value: "", expires: Time.new(1990, 1, 1),
+ secure: secure, http_only: true)
+ end
+
login.cookies.add_response_headers(env.response.headers)
env.redirect referer
@@ -985,6 +1009,12 @@ post "/login" do |env|
error_message = translate(locale, "Invalid username or password")
next templated "error"
end
+
+ # Since this user has already registered, we don't want to overwrite their preferences
+ if env.request.cookies["PREFS"]?
+ env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", value: "", expires: Time.new(1990, 1, 1),
+ secure: secure, http_only: true)
+ end
elsif action == "register"
if password.empty?
error_message = translate(locale, "Password cannot be empty")
@@ -1032,6 +1062,14 @@ post "/login" do |env|
env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", value: sid, expires: Time.now + 2.years,
secure: secure, http_only: true)
end
+
+ if env.request.cookies["PREFS"]?
+ preferences = env.get("preferences").as(Preferences)
+ PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email)
+
+ env.response.cookies["PREFS"] = HTTP::Cookie.new(name: "PREFS", value: "", expires: Time.new(1990, 1, 1),
+ secure: secure, http_only: true)
+ end
end
env.redirect referer
@@ -1073,121 +1111,121 @@ end
get "/preferences" do |env|
locale = LOCALES[env.get("locale").as(String)]?
-
- user = env.get? "user"
referer = get_referer(env)
- if user
- user = user.as(User)
+ if preferences = env.get? "preferences"
+ preferences = preferences.as(Preferences)
+
templated "preferences"
else
- env.redirect referer
+ preferences = DEFAULT_USER_PREFERENCES
+
+ templated "preferences"
end
end
post "/preferences" do |env|
locale = LOCALES[env.get("locale").as(String)]?
-
- user = env.get? "user"
referer = get_referer(env)
- if user
+ video_loop = env.params.body["video_loop"]?.try &.as(String)
+ video_loop ||= "off"
+ video_loop = video_loop == "on"
+
+ autoplay = env.params.body["autoplay"]?.try &.as(String)
+ autoplay ||= "off"
+ autoplay = autoplay == "on"
+
+ continue = env.params.body["continue"]?.try &.as(String)
+ continue ||= "off"
+ continue = continue == "on"
+
+ listen = env.params.body["listen"]?.try &.as(String)
+ listen ||= "off"
+ listen = listen == "on"
+
+ speed = env.params.body["speed"]?.try &.as(String).to_f?
+ speed ||= DEFAULT_USER_PREFERENCES.speed
+
+ quality = env.params.body["quality"]?.try &.as(String)
+ quality ||= DEFAULT_USER_PREFERENCES.quality
+
+ volume = env.params.body["volume"]?.try &.as(String).to_i?
+ volume ||= DEFAULT_USER_PREFERENCES.volume
+
+ comments_0 = env.params.body["comments_0"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.comments[0]
+ comments_1 = env.params.body["comments_1"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.comments[1]
+ comments = [comments_0, comments_1]
+
+ captions_0 = env.params.body["captions_0"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.captions[0]
+ captions_1 = env.params.body["captions_1"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.captions[1]
+ captions_2 = env.params.body["captions_2"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.captions[2]
+ captions = [captions_0, captions_1, captions_2]
+
+ related_videos = env.params.body["related_videos"]?.try &.as(String)
+ related_videos ||= "off"
+ related_videos = related_videos == "on"
+
+ redirect_feed = env.params.body["redirect_feed"]?.try &.as(String)
+ redirect_feed ||= "off"
+ redirect_feed = redirect_feed == "on"
+
+ locale = env.params.body["locale"]?.try &.as(String)
+ locale ||= DEFAULT_USER_PREFERENCES.locale
+
+ dark_mode = env.params.body["dark_mode"]?.try &.as(String)
+ dark_mode ||= "off"
+ dark_mode = dark_mode == "on"
+
+ thin_mode = env.params.body["thin_mode"]?.try &.as(String)
+ thin_mode ||= "off"
+ thin_mode = thin_mode == "on"
+
+ max_results = env.params.body["max_results"]?.try &.as(String).to_i?
+ max_results ||= DEFAULT_USER_PREFERENCES.max_results
+
+ sort = env.params.body["sort"]?.try &.as(String)
+ sort ||= DEFAULT_USER_PREFERENCES.sort
+
+ latest_only = env.params.body["latest_only"]?.try &.as(String)
+ latest_only ||= "off"
+ latest_only = latest_only == "on"
+
+ unseen_only = env.params.body["unseen_only"]?.try &.as(String)
+ unseen_only ||= "off"
+ unseen_only = unseen_only == "on"
+
+ notifications_only = env.params.body["notifications_only"]?.try &.as(String)
+ notifications_only ||= "off"
+ notifications_only = notifications_only == "on"
+
+ preferences = {
+ "video_loop" => video_loop,
+ "autoplay" => autoplay,
+ "continue" => continue,
+ "listen" => listen,
+ "speed" => speed,
+ "quality" => quality,
+ "volume" => volume,
+ "comments" => comments,
+ "captions" => captions,
+ "related_videos" => related_videos,
+ "redirect_feed" => redirect_feed,
+ "locale" => locale,
+ "dark_mode" => dark_mode,
+ "thin_mode" => thin_mode,
+ "max_results" => max_results,
+ "sort" => sort,
+ "latest_only" => latest_only,
+ "unseen_only" => unseen_only,
+ "notifications_only" => notifications_only,
+ }.to_json
+
+ if user = env.get? "user"
user = user.as(User)
-
- video_loop = env.params.body["video_loop"]?.try &.as(String)
- video_loop ||= "off"
- video_loop = video_loop == "on"
-
- autoplay = env.params.body["autoplay"]?.try &.as(String)
- autoplay ||= "off"
- autoplay = autoplay == "on"
-
- continue = env.params.body["continue"]?.try &.as(String)
- continue ||= "off"
- continue = continue == "on"
-
- listen = env.params.body["listen"]?.try &.as(String)
- listen ||= "off"
- listen = listen == "on"
-
- speed = env.params.body["speed"]?.try &.as(String).to_f?
- speed ||= DEFAULT_USER_PREFERENCES.speed
-
- quality = env.params.body["quality"]?.try &.as(String)
- quality ||= DEFAULT_USER_PREFERENCES.quality
-
- volume = env.params.body["volume"]?.try &.as(String).to_i?
- volume ||= DEFAULT_USER_PREFERENCES.volume
-
- comments_0 = env.params.body["comments_0"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.comments[0]
- comments_1 = env.params.body["comments_1"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.comments[1]
- comments = [comments_0, comments_1]
-
- captions_0 = env.params.body["captions_0"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.captions[0]
- captions_1 = env.params.body["captions_1"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.captions[1]
- captions_2 = env.params.body["captions_2"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.captions[2]
- captions = [captions_0, captions_1, captions_2]
-
- related_videos = env.params.body["related_videos"]?.try &.as(String)
- related_videos ||= "off"
- related_videos = related_videos == "on"
-
- redirect_feed = env.params.body["redirect_feed"]?.try &.as(String)
- redirect_feed ||= "off"
- redirect_feed = redirect_feed == "on"
-
- locale = env.params.body["locale"]?.try &.as(String)
- locale ||= DEFAULT_USER_PREFERENCES.locale
-
- dark_mode = env.params.body["dark_mode"]?.try &.as(String)
- dark_mode ||= "off"
- dark_mode = dark_mode == "on"
-
- thin_mode = env.params.body["thin_mode"]?.try &.as(String)
- thin_mode ||= "off"
- thin_mode = thin_mode == "on"
-
- max_results = env.params.body["max_results"]?.try &.as(String).to_i?
- max_results ||= DEFAULT_USER_PREFERENCES.max_results
-
- sort = env.params.body["sort"]?.try &.as(String)
- sort ||= DEFAULT_USER_PREFERENCES.sort
-
- latest_only = env.params.body["latest_only"]?.try &.as(String)
- latest_only ||= "off"
- latest_only = latest_only == "on"
-
- unseen_only = env.params.body["unseen_only"]?.try &.as(String)
- unseen_only ||= "off"
- unseen_only = unseen_only == "on"
-
- notifications_only = env.params.body["notifications_only"]?.try &.as(String)
- notifications_only ||= "off"
- notifications_only = notifications_only == "on"
-
- preferences = {
- "video_loop" => video_loop,
- "autoplay" => autoplay,
- "continue" => continue,
- "listen" => listen,
- "speed" => speed,
- "quality" => quality,
- "volume" => volume,
- "comments" => comments,
- "captions" => captions,
- "related_videos" => related_videos,
- "redirect_feed" => redirect_feed,
- "locale" => locale,
- "dark_mode" => dark_mode,
- "thin_mode" => thin_mode,
- "max_results" => max_results,
- "sort" => sort,
- "latest_only" => latest_only,
- "unseen_only" => unseen_only,
- "notifications_only" => notifications_only,
- }.to_json
-
PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email)
+ else
+ env.response.cookies["PREFS"] = preferences
end
env.redirect referer
@@ -1195,21 +1233,24 @@ end
get "/toggle_theme" do |env|
locale = LOCALES[env.get("locale").as(String)]?
-
- user = env.get? "user"
referer = get_referer(env)
- if user
+ if user = env.get? "user"
user = user.as(User)
preferences = user.preferences
-
- if preferences.dark_mode
- preferences.dark_mode = false
- else
- preferences.dark_mode = true
- end
+ preferences.dark_mode = !preferences.dark_mode
PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences.to_json, user.email)
+ elsif preferences = env.get? "preferences"
+ preferences = preferences.as(Preferences)
+ preferences.dark_mode = !preferences.dark_mode
+
+ env.response.cookies["PREFS"] = preferences.to_json
+ else
+ preferences = DEFAULT_USER_PREFERENCES
+ preferences.dark_mode = true
+
+ env.response.cookies["PREFS"] = preferences.to_json
end
env.redirect referer
@@ -3863,12 +3904,20 @@ end
# YouTube /videoplayback links expire after 6 hours,
# so we have a mechanism here to redirect to the latest version
get "/latest_version" do |env|
- id = env.params.query["id"]?
- itag = env.params.query["itag"]?
+ if env.params.query["download_widget"]?
+ download_widget = JSON.parse(env.params.query["download_widget"])
+ id = download_widget["id"].as_s
+ itag = download_widget["itag"].as_s
+ title = download_widget["title"].as_s
+ local = "true"
+ end
+
+ id ||= env.params.query["id"]?
+ itag ||= env.params.query["itag"]?
region = env.params.query["region"]?
- local = env.params.query["local"]?
+ local ||= env.params.query["local"]?
local ||= "false"
local = local == "true"
@@ -3893,6 +3942,10 @@ get "/latest_version" do |env|
url = URI.parse(url).full_path.not_nil!
end
+ if title
+ url += "&title=#{title}"
+ end
+
env.redirect url
end
@@ -3997,6 +4050,10 @@ get "/videoplayback" do |env|
client.get(url, headers) do |response|
env.response.status_code = response.status_code
+ if title = env.params.query["title"]?
+ env.response.headers["Content-Disposition"] = "attachment; filename=#{title}"
+ end
+
response.headers.each do |key, value|
env.response.headers[key] = value
end
@@ -4210,6 +4267,7 @@ add_handler FilteredCompressHandler.new
add_handler DenyFrame.new
add_handler APIHandler.new
add_context_storage_type(User)
+add_context_storage_type(Preferences)
Kemal.config.logger = logger
Kemal.run
diff --git a/src/invidious/views/components/feed_menu.ecr b/src/invidious/views/components/feed_menu.ecr
new file mode 100644
index 00000000..5188689a
--- /dev/null
+++ b/src/invidious/views/components/feed_menu.ecr
@@ -0,0 +1,19 @@
+<div class="h-box pure-g">
+ <div class="pure-u-1-4"></div>
+ <div class="pure-u-1 pure-u-md-1-2">
+ <div class="pure-g">
+ <% feeds = ["Popular", "Top", "Trending"] %>
+ <% if env.get? "user" %>
+ <% feeds << "Subscriptions" %>
+ <% end %>
+ <% feeds.each do |feed| %>
+ <div class="pure-u-1-<%= feeds.size %>">
+ <a href="/feed/<%= feed.downcase %>" style="text-align:center;" class="pure-menu-heading">
+ <%= translate(locale, feed) %>
+ </a>
+ </div>
+ <% end %>
+ </div>
+ </div>
+ <div class="pure-u-1-4"></div>
+</div> \ No newline at end of file
diff --git a/src/invidious/views/index.ecr b/src/invidious/views/index.ecr
index a12455b0..6cc978e5 100644
--- a/src/invidious/views/index.ecr
+++ b/src/invidious/views/index.ecr
@@ -3,29 +3,7 @@
<title>Invidious</title>
<% end %>
-<div class="h-box pure-g">
- <div class="pure-u-1-4"></div>
- <div class="pure-u-1 pure-u-md-1-2">
- <div class="pure-g">
- <div class="pure-u-1-3">
- <a href="/feed/popular" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Popular") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/top" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Top") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/trending" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Trending") %>
- </a>
- </div>
- </div>
- </div>
- <div class="pure-u-1-4"></div>
-</div>
+<%= rendered "components/feed_menu" %>
<div class="pure-g">
<% top_videos.each_slice(4) do |slice| %>
diff --git a/src/invidious/views/popular.ecr b/src/invidious/views/popular.ecr
index 8d67241f..f235aad8 100644
--- a/src/invidious/views/popular.ecr
+++ b/src/invidious/views/popular.ecr
@@ -3,29 +3,7 @@
<title><%= translate(locale, "Popular") %> - Invidious</title>
<% end %>
-<div class="h-box pure-g">
- <div class="pure-u-1-4"></div>
- <div class="pure-u-1 pure-u-md-1-2">
- <div class="pure-g">
- <div class="pure-u-1-3">
- <a href="/feed/popular" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Popular") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/top" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Top") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/trending" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Trending") %>
- </a>
- </div>
- </div>
- </div>
- <div class="pure-u-1-4"></div>
-</div>
+<%= rendered "components/feed_menu" %>
<div class="pure-g">
<% popular_videos.each_slice(4) do |slice| %>
diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr
index cd752900..7435c91e 100644
--- a/src/invidious/views/preferences.ecr
+++ b/src/invidious/views/preferences.ecr
@@ -15,29 +15,29 @@ function update_value(element) {
<div class="pure-control-group">
<label for="video_loop"><%= translate(locale, "Always loop: ") %></label>
- <input name="video_loop" id="video_loop" type="checkbox" <% if user.preferences.video_loop %>checked<% end %>>
+ <input name="video_loop" id="video_loop" type="checkbox" <% if preferences.video_loop %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="autoplay"><%= translate(locale, "Autoplay: ") %></label>
- <input name="autoplay" id="autoplay" type="checkbox" <% if user.preferences.autoplay %>checked<% end %>>
+ <input name="autoplay" id="autoplay" type="checkbox" <% if preferences.autoplay %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="continue"><%= translate(locale, "Autoplay next video: ") %></label>
- <input name="continue" id="continue" type="checkbox" <% if user.preferences.continue %>checked<% end %>>
+ <input name="continue" id="continue" type="checkbox" <% if preferences.continue %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="listen"><%= translate(locale, "Listen by default: ") %></label>
- <input name="listen" id="listen" type="checkbox" <% if user.preferences.listen %>checked<% end %>>
+ <input name="listen" id="listen" type="checkbox" <% if preferences.listen %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="speed"><%= translate(locale, "Default speed: ") %></label>
<select name="speed" id="speed">
<% {2.0, 1.5, 1.0, 0.5}.each do |option| %>
- <option <% if user.preferences.speed == option %> selected <% end %>><%= option %></option>
+ <option <% if preferences.speed == option %> selected <% end %>><%= option %></option>
<% end %>
</select>
</div>
@@ -46,22 +46,22 @@ function update_value(element) {
<label for="quality"><%= translate(locale, "Preferred video quality: ") %></label>
<select name="quality" id="quality">
<% {"dash", "hd720", "medium", "small"}.each do |option| %>
- <option value="<%= option %>" <% if user.preferences.quality == option %> selected <% end %>><%= translate(locale, option) %></option>
+ <option value="<%= option %>" <% if preferences.quality == option %> selected <% end %>><%= translate(locale, option) %></option>
<% end %>
</select>
</div>
<div class="pure-control-group">
<label for="volume"><%= translate(locale, "Player volume: ") %></label>
- <input name="volume" id="volume" oninput="update_value(this);" type="range" min="0" max="100" step="5" value="<%= user.preferences.volume %>">
- <span class="pure-form-message-inline" id="volume-value"><%= user.preferences.volume %></span>
+ <input name="volume" id="volume" oninput="update_value(this);" type="range" min="0" max="100" step="5" value="<%= preferences.volume %>">
+ <span class="pure-form-message-inline" id="volume-value"><%= preferences.volume %></span>
</div>
<div class="pure-control-group">
<label for="comments_0"><%= translate(locale, "Default comments: ") %></label>
<select name="comments_0" id="comments_0">
<% {"", "youtube", "reddit"}.each do |option| %>
- <option value="<%= option %>" <% if user.preferences.comments[0] == option %> selected <% end %>><%= translate(locale, option) %></option>
+ <option value="<%= option %>" <% if preferences.comments[0] == option %> selected <% end %>><%= translate(locale, option) %></option>
<% end %>
</select>
</div>
@@ -70,7 +70,7 @@ function update_value(element) {
<label for="comments_1"><%= translate(locale, "Fallback comments: ") %></label>
<select name="comments_1" id="comments_1">
<% {"", "youtube", "reddit"}.each do |option| %>
- <option value="<%= option %>" <% if user.preferences.comments[1] == option %> selected <% end %>><%= translate(locale, option) %></option>
+ <option value="<%= option %>" <% if preferences.comments[1] == option %> selected <% end %>><%= translate(locale, option) %></option>
<% end %>
</select>
</div>
@@ -79,7 +79,7 @@ function update_value(element) {
<label for="captions_0"><%= translate(locale, "Default captions: ") %></label>
<select class="pure-u-1-5" name="captions_0" id="captions_0">
<% CAPTION_LANGUAGES.each do |option| %>
- <option value="<%= option %>" <% if user.preferences.captions[0] == option %> selected <% end %>><%= translate(locale, option) %></option>
+ <option value="<%= option %>" <% if preferences.captions[0] == option %> selected <% end %>><%= translate(locale, option) %></option>
<% end %>
</select>
</div>
@@ -88,20 +88,20 @@ function update_value(element) {
<label for="captions_fallback"><%= translate(locale, "Fallback captions: ") %></label>
<select class="pure-u-1-5" name="captions_1" id="captions_1">
<% CAPTION_LANGUAGES.each do |option| %>
- <option value="<%= option %>" <% if user.preferences.captions[1] == option %> selected <% end %>><%= translate(locale, option) %></option>
+ <option value="<%= option %>" <% if preferences.captions[1] == option %> selected <% end %>><%= translate(locale, option) %></option>
<% end %>
</select>
<select class="pure-u-1-5" name="captions_2" id="captions_2">
<% CAPTION_LANGUAGES.each do |option| %>
- <option value="<%= option %>" <% if user.preferences.captions[2] == option %> selected <% end %>><%= translate(locale, option) %></option>
+ <option value="<%= option %>" <% if preferences.captions[2] == option %> selected <% end %>><%= translate(locale, option) %></option>
<% end %>
</select>
</div>
<div class="pure-control-group">
<label for="related_videos"><%= translate(locale, "Show related videos? ") %></label>
- <input name="related_videos" id="related_videos" type="checkbox" <% if user.preferences.related_videos %>checked<% end %>>
+ <input name="related_videos" id="related_videos" type="checkbox" <% if preferences.related_videos %>checked<% end %>>
</div>
<legend><%= translate(locale, "Visual preferences") %></legend>
@@ -110,61 +110,64 @@ function update_value(element) {
<label for="locale"><%= translate(locale, "Language: ") %></label>
<select name="locale" id="locale">
<% LOCALES.each_key do |option| %>
- <option value="<%= option %>" <% if user.preferences.locale == option %> selected <% end %>><%= option %></option>
+ <option value="<%= option %>" <% if preferences.locale == option %> selected <% end %>><%= option %></option>
<% end %>
</select>
</div>
<div class="pure-control-group">
<label for="dark_mode"><%= translate(locale, "Dark mode: ") %></label>
- <input name="dark_mode" id="dark_mode" type="checkbox" <% if user.preferences.dark_mode %>checked<% end %>>
+ <input name="dark_mode" id="dark_mode" type="checkbox" <% if preferences.dark_mode %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="thin_mode"><%= translate(locale, "Thin mode: ") %></label>
- <input name="thin_mode" id="thin_mode" type="checkbox" <% if user.preferences.thin_mode %>checked<% end %>>
+ <input name="thin_mode" id="thin_mode" type="checkbox" <% if preferences.thin_mode %>checked<% end %>>
</div>
+ <% if env.get? "user" %>
<legend><%= translate(locale, "Subscription preferences") %></legend>
<div class="pure-control-group">
<label for="redirect_feed"><%= translate(locale, "Redirect homepage to feed: ") %></label>
- <input name="redirect_feed" id="redirect_feed" type="checkbox" <% if user.preferences.redirect_feed %>checked<% end %>>
+ <input name="redirect_feed" id="redirect_feed" type="checkbox" <% if preferences.redirect_feed %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="max_results"><%= translate(locale, "Number of videos shown in feed: ") %></label>
- <input name="max_results" id="max_results" type="number" value="<%= user.preferences.max_results %>">
+ <input name="max_results" id="max_results" type="number" value="<%= preferences.max_results %>">
</div>
<div class="pure-control-group">
<label for="sort"><%= translate(locale, "Sort videos by: ") %></label>
<select name="sort" id="sort">
<% {"published", "published - reverse", "alphabetically", "alphabetically - reverse", "channel name", "channel name - reverse"}.each do |option| %>
- <option value="<%= option %>" <% if user.preferences.sort == option %> selected <% end %>><%= translate(locale, option) %></option>
+ <option value="<%= option %>" <% if preferences.sort == option %> selected <% end %>><%= translate(locale, option) %></option>
<% end %>
</select>
</div>
<div class="pure-control-group">
- <% if user.preferences.unseen_only %>
+ <% if preferences.unseen_only %>
<label for="latest_only"><%= translate(locale, "Only show latest unwatched video from channel: ") %></label>
<% else %>
<label for="latest_only"><%= translate(locale, "Only show latest video from channel: ") %></label>
<% end %>
- <input name="latest_only" id="latest_only" type="checkbox" <% if user.preferences.latest_only %>checked<% end %>>
+ <input name="latest_only" id="latest_only" type="checkbox" <% if preferences.latest_only %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="unseen_only"><%= translate(locale, "Only show unwatched: ") %></label>
- <input name="unseen_only" id="unseen_only" type="checkbox" <% if user.preferences.unseen_only %>checked<% end %>>
+ <input name="unseen_only" id="unseen_only" type="checkbox" <% if preferences.unseen_only %>checked<% end %>>
</div>
<div class="pure-control-group">
<label for="notifications_only"><%= translate(locale, "Only show notifications (if there are any): ") %></label>
- <input name="notifications_only" id="notifications_only" type="checkbox" <% if user.preferences.notifications_only %>checked<% end %>>
+ <input name="notifications_only" id="notifications_only" type="checkbox" <% if preferences.notifications_only %>checked<% end %>>
</div>
+ <% end %>
+ <% if env.get? "user" %>
<legend><%= translate(locale, "Data preferences") %></legend>
<div class="pure-control-group">
@@ -186,6 +189,7 @@ function update_value(element) {
<div class="pure-control-group">
<a href="/delete_account?referer=<%= URI.escape(referer) %>"><%= translate(locale, "Delete account") %></a>
</div>
+ <% end %>
<div class="pure-controls">
<button type="submit" class="pure-button pure-button-primary"><%= translate(locale, "Save preferences") %></button>
diff --git a/src/invidious/views/subscriptions.ecr b/src/invidious/views/subscriptions.ecr
index 6679cdf9..109da18f 100644
--- a/src/invidious/views/subscriptions.ecr
+++ b/src/invidious/views/subscriptions.ecr
@@ -2,6 +2,8 @@
<title><%= translate(locale, "Subscriptions") %> - Invidious</title>
<% end %>
+<%= rendered "components/feed_menu" %>
+
<div class="pure-g h-box">
<div class="pure-u-1-3">
<h3>
diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr
index 8f2d9c9b..a7d96d72 100644
--- a/src/invidious/views/template.ecr
+++ b/src/invidious/views/template.ecr
@@ -18,7 +18,7 @@
<link rel="stylesheet" href="/css/grids-responsive-min.css">
<link rel="stylesheet" href="/css/ionicons.min.css">
<link rel="stylesheet" href="/css/default.css">
- <% if env.get?("user") && env.get("user").as(User).preferences.dark_mode %>
+ <% if env.get?("preferences").try &.as(Preferences).dark_mode %>
<link rel="stylesheet" href="/css/darktheme.css">
<% else %>
<link rel="stylesheet" href="/css/lighttheme.css">
@@ -75,9 +75,25 @@
</a>
</div>
<% else %>
+ <div class="pure-u-1-3">
+ <a href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
+ <% if env.get?("preferences").try &.as(Preferences).dark_mode %>
+ <i class="icon ion-ios-sunny"></i>
+ <% else %>
+ <i class="icon ion-ios-moon"></i>
+ <% end %>
+ </a>
+ </div>
+ <div class="pure-u-1-3">
+ <a href="/preferences?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
+ <i class="icon ion-ios-cog"></i>
+ </a>
+ </div>
+ <div class="pure-u-1-3">
<a href="/login?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
<%= translate(locale, "Login") %>
</a>
+ </div>
<% end %>
</div>
</div>
diff --git a/src/invidious/views/top.ecr b/src/invidious/views/top.ecr
index ea5679c4..acf122a6 100644
--- a/src/invidious/views/top.ecr
+++ b/src/invidious/views/top.ecr
@@ -3,29 +3,7 @@
<title><%= translate(locale, "Top") %> - Invidious</title>
<% end %>
-<div class="h-box pure-g">
- <div class="pure-u-1-4"></div>
- <div class="pure-u-1 pure-u-md-1-2">
- <div class="pure-g">
- <div class="pure-u-1-3">
- <a href="/feed/popular" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Popular") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/top" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Top") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/trending" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Trending") %>
- </a>
- </div>
- </div>
- </div>
- <div class="pure-u-1-4"></div>
-</div>
+<%= rendered "components/feed_menu" %>
<div class="pure-g">
<% top_videos.each_slice(4) do |slice| %>
diff --git a/src/invidious/views/trending.ecr b/src/invidious/views/trending.ecr
index a3db4de3..617a9a58 100644
--- a/src/invidious/views/trending.ecr
+++ b/src/invidious/views/trending.ecr
@@ -3,29 +3,7 @@
<title><%= translate(locale, "Trending") %> - Invidious</title>
<% end %>
-<div class="h-box pure-g">
- <div class="pure-u-1-4"></div>
- <div class="pure-u-1 pure-u-md-1-2">
- <div class="pure-g">
- <div class="pure-u-1-3">
- <a href="/feed/popular" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Popular") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/top" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Top") %>
- </a>
- </div>
- <div class="pure-u-1-3">
- <a href="/feed/trending" style="text-align:center;" class="pure-menu-heading">
- <%= translate(locale, "Trending") %>
- </a>
- </div>
- </div>
- </div>
- <div class="pure-u-1-4"></div>
-</div>
+<%= rendered "components/feed_menu" %>
<div class="pure-g h-box">
<div class="pure-u-2-3">
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr
index 9432eb4e..3949245d 100644
--- a/src/invidious/views/watch.ecr
+++ b/src/invidious/views/watch.ecr
@@ -54,29 +54,29 @@
<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">
+ <form class="pure-form pure-form-stacked" action="/latest_version" method="get" rel="noopener" target="_blank">
<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="/latest_version?id=<%= video.id %>&itag=<%= option["itag"] %>&local=true"><%= option["quality_label"] %> - <%= option["type"].split(";")[0] %> @ <%= option["fps"] %>fps - video only</option>
+ <option value='{"id":"<%= video.id %>","itag":"<%= option["itag"] %>","title":"<%= video.title.dump_unquoted %>-<%= video.id %>.mp4"}'>
+ <%= option["quality_label"] %> - <%= option["type"].split(";")[0] %> @ <%= option["fps"] %>fps - video only
+ </option>
<% end %>
<% audio_streams.each do |option| %>
- <option data-url="/latest_version?id=<%= video.id %>&itag=<%= option["itag"] %>&local=true"><%= option["type"].split(";")[0] %> @ <%= option["bitrate"] %>k - audio only</option>
+ <option value='{"id":"<%= video.id %>","itag":"<%= option["itag"] %>","title":"<%= video.title.dump_unquoted %>-<%= video.id %>.mp4"}'>
+ <%= option["type"].split(";")[0] %> @ <%= option["bitrate"] %>k - audio only
+ </option>
<% end %>
<% fmt_stream.each do |option| %>
- <option data-url="/latest_version?id=<%= video.id %>&itag=<%= option["itag"] %>&local=true"><%= itag_to_metadata?(option["itag"]).try &.["height"]? || "~240" %>p - <%= option["type"].split(";")[0] %></option>
+ <option value='{"id":"<%= video.id %>","itag":"<%= option["itag"] %>","title":"<%= video.title.dump_unquoted %>-<%= video.id %>.mp4"}'>
+ <%= 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" data-title="<%= video.title.dump_unquoted %>-<%= video.id %>.mp4" onclick="download_video(this)"
- class="pure-button pure-button-primary">
+ <button type="submit" class="pure-button pure-button-primary">
<b><%= translate(locale, "Download") %></b>
</button>
</form>