mirror of
https://github.com/iv-org/invidious.git
synced 2024-12-23 14:03:37 +00:00
Add ecr for individual compilation items (videos)
This commit is contained in:
parent
bac4fd9097
commit
74d42c18dd
48
assets/js/compilation_widget.js
Normal file
48
assets/js/compilation_widget.js
Normal file
@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
var compilation_data = JSON.parse(document.getElementById('compilation_data').textContent);
|
||||
var payload = 'csrf_token=' + compilation_data.csrf_token;
|
||||
|
||||
function add_compilation_video(target) {
|
||||
var select = target.parentNode.children[0].children[1];
|
||||
var option = select.children[select.selectedIndex];
|
||||
|
||||
var url = '/compilation_ajax?action_add_video=1&redirect=false' +
|
||||
'&video_id=' + target.getAttribute('data-id') +
|
||||
'&compilation_id=' + option.getAttribute('data-compid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
on200: function (response) {
|
||||
option.textContent = '✓' + option.textContent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add_compilation_item(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
|
||||
var url = '/compilation_ajax?action_add_video=1&redirect=false' +
|
||||
'&video_id=' + target.getAttribute('data-id') +
|
||||
'&compilation_id=' + target.getAttribute('data-compid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
tile.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function remove_compilation_item(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
|
||||
var url = '/compilation_ajax?action_remove_video=1&redirect=false' +
|
||||
'&set_video_id=' + target.getAttribute('data-index') +
|
||||
'&compilation_id=' + target.getAttribute('data-compid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
tile.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
@ -483,6 +483,14 @@ hmac_key: "CHANGE_ME!!"
|
||||
##
|
||||
#playlist_length_limit: 500
|
||||
|
||||
##
|
||||
## Maximum custom compilation length limit.
|
||||
##
|
||||
## Accepted values: Integer
|
||||
## Default: 500
|
||||
##
|
||||
#compilation_length_limit: 500
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Default user preferences
|
||||
|
@ -177,6 +177,7 @@
|
||||
"Create compilation": "Create compilation",
|
||||
"Title": "Title",
|
||||
"Playlist privacy": "Playlist privacy",
|
||||
"Compilation privacy": "Compilation privacy",
|
||||
"Editing playlist `x`": "Editing playlist `x`",
|
||||
"playlist_button_add_items": "Add videos",
|
||||
"Show more": "Show more",
|
||||
|
410
src/invidious/routes/compilations.cr
Normal file
410
src/invidious/routes/compilations.cr
Normal file
@ -0,0 +1,410 @@
|
||||
{% skip_file if flag?(:api_only) %}
|
||||
|
||||
module Invidious::Routes::Compilations
|
||||
def self.new(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect "/" if user.nil?
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
csrf_token = generate_response(sid, {":create_compilation"}, HMAC_KEY)
|
||||
|
||||
templated "create_compilation"
|
||||
end
|
||||
|
||||
def self.create(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect "/" if user.nil?
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
token = env.params.body["csrf_token"]?
|
||||
|
||||
begin
|
||||
validate_request(token, sid, env.request, HMAC_KEY, locale)
|
||||
rescue ex
|
||||
return error_template(400, ex)
|
||||
end
|
||||
|
||||
title = env.params.body["title"]?.try &.as(String)
|
||||
if !title || title.empty?
|
||||
return error_template(400, "Title cannot be empty.")
|
||||
end
|
||||
|
||||
privacy = CompilationPrivacy.parse?(env.params.body["privacy"]?.try &.as(String) || "")
|
||||
if !privacy
|
||||
return error_template(400, "Invalid privacy setting.")
|
||||
end
|
||||
|
||||
if Invidious::Database::Compilations.count_owned_by(user.email) >= 100
|
||||
return error_template(400, "User cannot have more than 100 compilations.")
|
||||
end
|
||||
|
||||
compilation = create_compilation(title, privacy, user)
|
||||
|
||||
env.redirect "/compilation?list=#{compilation.id}"
|
||||
end
|
||||
|
||||
def self.delete_page(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect "/" if user.nil?
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
|
||||
compid = env.params.query["list"]?
|
||||
if !compid || compid.empty?
|
||||
return error_template(400, "A compilation ID is required")
|
||||
end
|
||||
|
||||
compilation = Invidious::Database::Compilations.select(id: compid)
|
||||
if !compilation || compilation.author != user.email
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
csrf_token = generate_response(sid, {":delete_compilation"}, HMAC_KEY)
|
||||
|
||||
templated "delete_compilation"
|
||||
end
|
||||
|
||||
def self.delete(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect "/" if user.nil?
|
||||
|
||||
compid = env.params.query["list"]?
|
||||
return env.redirect referer if compid.nil?
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
token = env.params.body["csrf_token"]?
|
||||
|
||||
begin
|
||||
validate_request(token, sid, env.request, HMAC_KEY, locale)
|
||||
rescue ex
|
||||
return error_template(400, ex)
|
||||
end
|
||||
|
||||
compilation = Invidious::Database::Compilations.select(id: compid)
|
||||
if !compilation || compilation.author != user.email
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
Invidious::Database::Compilations.delete(compid)
|
||||
|
||||
env.redirect "/feed/compilations"
|
||||
end
|
||||
|
||||
def self.edit(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect "/" if user.nil?
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
|
||||
compid = env.params.query["list"]?
|
||||
if !compid || !compid.starts_with?("IV")
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
page = env.params.query["page"]?.try &.to_i?
|
||||
page ||= 1
|
||||
|
||||
compilation = Invidious::Database::Compilations.select(id: compid)
|
||||
if !compilation || compilation.author != user.email
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
begin
|
||||
videos = get_compilation_videos(compilation, offset: (page - 1) * 100)
|
||||
rescue ex
|
||||
videos = [] of CompilationVideo
|
||||
end
|
||||
|
||||
csrf_token = generate_response(sid, {":edit_compilation"}, HMAC_KEY)
|
||||
|
||||
templated "edit_compilation"
|
||||
end
|
||||
|
||||
def self.update(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect "/" if user.nil?
|
||||
|
||||
compid = env.params.query["list"]?
|
||||
return env.redirect referer if compid.nil?
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
token = env.params.body["csrf_token"]?
|
||||
|
||||
begin
|
||||
validate_request(token, sid, env.request, HMAC_KEY, locale)
|
||||
rescue ex
|
||||
return error_template(400, ex)
|
||||
end
|
||||
|
||||
compilation = Invidious::Database::Compilations.select(id: compid)
|
||||
if !compilation || compilation.author != user.email
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
title = env.params.body["title"]?.try &.delete("<>") || ""
|
||||
privacy = CompilationPrivacy.parse(env.params.body["privacy"]? || "Unlisted")
|
||||
description = env.params.body["description"]?.try &.delete("\r") || ""
|
||||
|
||||
if title != compilation.title ||
|
||||
compilation != compilation.privacy ||
|
||||
description != compilation.description
|
||||
updated = Time.utc
|
||||
else
|
||||
updated = compilation.updated
|
||||
end
|
||||
|
||||
Invidious::Database::Compilations.update(compid, title, privacy, description, updated)
|
||||
|
||||
env.redirect "/compilation?list=#{compid}"
|
||||
end
|
||||
|
||||
def self.add_compilation_items_page(env)
|
||||
prefs = env.get("preferences").as(Preferences)
|
||||
locale = prefs.locale
|
||||
|
||||
region = env.params.query["region"]? || prefs.region
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect "/" if user.nil?
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
|
||||
compid = env.params.query["list"]?
|
||||
if !compid || !compid.starts_with?("IV")
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
page = env.params.query["page"]?.try &.to_i?
|
||||
page ||= 1
|
||||
|
||||
compilation = Invidious::Database::Compilations.select(id: compid)
|
||||
if !compilation || compilation.author != user.email
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
begin
|
||||
query = Invidious::Search::Query.new(env.params.query, :compilation, region)
|
||||
videos = query.process.select(SearchVideo).map(&.as(SearchVideo))
|
||||
rescue ex
|
||||
videos = [] of SearchVideo
|
||||
end
|
||||
|
||||
env.set "add_compilation_items", compid
|
||||
templated "add_compilation_items"
|
||||
end
|
||||
|
||||
def self.compilation_ajax(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env, "/")
|
||||
|
||||
redirect = env.params.query["redirect"]?
|
||||
redirect ||= "true"
|
||||
redirect = redirect == "true"
|
||||
if !user
|
||||
if redirect
|
||||
return env.redirect referer
|
||||
else
|
||||
return error_json(403, "No such user")
|
||||
end
|
||||
end
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
token = env.params.body["csrf_token"]?
|
||||
|
||||
begin
|
||||
validate_request(token, sid, env.request, HMAC_KEY, locale)
|
||||
rescue ex
|
||||
if redirect
|
||||
return error_template(400, ex)
|
||||
else
|
||||
return error_json(400, ex)
|
||||
end
|
||||
end
|
||||
|
||||
if env.params.query["action_create_compilation"]?
|
||||
action = "action_create_compilation"
|
||||
elsif env.params.query["action_delete_compilation"]?
|
||||
action = "action_delete_compilation"
|
||||
elsif env.params.query["action_edit_compilation"]?
|
||||
action = "action_edit_compilation"
|
||||
elsif env.params.query["action_add_video"]?
|
||||
action = "action_add_video"
|
||||
video_id = env.params.query["video_id"]
|
||||
elsif env.params.query["action_remove_video"]?
|
||||
action = "action_remove_video"
|
||||
elsif env.params.query["action_move_video_before"]?
|
||||
action = "action_move_video_before"
|
||||
else
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
begin
|
||||
compilation_id = env.params.query["compilation_id"]
|
||||
compilation = get_compilation(compilation_id).as(InvidiousCompilation)
|
||||
raise "Invalid user" if compilation.author != user.email
|
||||
rescue ex : NotFoundException
|
||||
return error_json(404, ex)
|
||||
rescue ex
|
||||
if redirect
|
||||
return error_template(400, ex)
|
||||
else
|
||||
return error_json(400, ex)
|
||||
end
|
||||
end
|
||||
|
||||
email = user.email
|
||||
|
||||
case action
|
||||
when "action_edit_compilation"
|
||||
# TODO: Compilation stub
|
||||
when "action_add_video"
|
||||
if compilation.index.size >= CONFIG.compilation_length_limit
|
||||
if redirect
|
||||
return error_template(400, "Compilation cannot have more than #{CONFIG.compilation_length_limit} videos")
|
||||
else
|
||||
return error_json(400, "Compilation cannot have more than #{CONFIG.compilation_length_limit} videos")
|
||||
end
|
||||
end
|
||||
|
||||
video_id = env.params.query["video_id"]
|
||||
|
||||
begin
|
||||
video = get_video(video_id)
|
||||
rescue ex : NotFoundException
|
||||
return error_json(404, ex)
|
||||
rescue ex
|
||||
if redirect
|
||||
return error_template(500, ex)
|
||||
else
|
||||
return error_json(500, ex)
|
||||
end
|
||||
end
|
||||
|
||||
compilation_video = CompilationVideo.new({
|
||||
title: video.title,
|
||||
id: video.id,
|
||||
author: video.author,
|
||||
ucid: video.ucid,
|
||||
length_seconds: video.length_seconds,
|
||||
starting_timestamp_seconds: video.length_seconds,
|
||||
ending_timestamp_seconds: video.length_seconds,
|
||||
published: video.published,
|
||||
compid: compilation_id,
|
||||
live_now: video.live_now,
|
||||
index: Random::Secure.rand(0_i64..Int64::MAX),
|
||||
})
|
||||
|
||||
Invidious::Database::CompilationVideos.insert(compilation_video)
|
||||
Invidious::Database::Compilations.update_video_added(compilation_id, compilation_video.index)
|
||||
when "action_remove_video"
|
||||
index = env.params.query["set_video_id"]
|
||||
Invidious::Database::CompilationVideos.delete(index)
|
||||
Invidious::Database::Compilations.update_video_removed(compilation_id, index)
|
||||
when "action_move_video_before"
|
||||
# TODO: Compilation stub
|
||||
else
|
||||
return error_json(400, "Unsupported action #{action}")
|
||||
end
|
||||
|
||||
if redirect
|
||||
env.redirect referer
|
||||
else
|
||||
env.response.content_type = "application/json"
|
||||
"{}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.show(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get?("user").try &.as(User)
|
||||
referer = get_referer(env)
|
||||
|
||||
compid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "")
|
||||
if !compid
|
||||
return env.redirect "/"
|
||||
end
|
||||
|
||||
page = env.params.query["page"]?.try &.to_i?
|
||||
page ||= 1
|
||||
|
||||
if compid.starts_with? "RD"
|
||||
return env.redirect "/mix?list=#{compid}"
|
||||
end
|
||||
|
||||
begin
|
||||
compilation = get_compilation(compid)
|
||||
rescue ex : NotFoundException
|
||||
return error_template(404, ex)
|
||||
rescue ex
|
||||
return error_template(500, ex)
|
||||
end
|
||||
|
||||
page_count = (compilation.video_count / 200).to_i
|
||||
page_count += 1 if (compilation.video_count % 200) > 0
|
||||
|
||||
if page > page_count
|
||||
return env.redirect "/compilation?list=#{compid}&page=#{page_count}"
|
||||
end
|
||||
|
||||
if compilation.privacy == CompilationPrivacy::Private && compilation.author != user.try &.email
|
||||
return error_template(403, "This compilation is private.")
|
||||
end
|
||||
|
||||
begin
|
||||
videos = get_compilation_videos(compilation, offset: (page - 1) * 200)
|
||||
rescue ex
|
||||
return error_template(500, "Error encountered while retrieving compilation videos.<br>#{ex.message}")
|
||||
end
|
||||
|
||||
if compilation.author == user.try &.email
|
||||
env.set "remove_compilation_items", compid
|
||||
end
|
||||
|
||||
templated "compilation"
|
||||
end
|
||||
end
|
40
src/invidious/views/add_compilation_items.ecr
Normal file
40
src/invidious/views/add_compilation_items.ecr
Normal file
@ -0,0 +1,40 @@
|
||||
<% content_for "header" do %>
|
||||
<title><%= compilation.title %> - Invidious</title>
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/compilation/<%= compid %>" />
|
||||
<% end %>
|
||||
|
||||
<div class="pure-g">
|
||||
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
||||
<div class="pure-u-1 pure-u-lg-3-5">
|
||||
<div class="h-box">
|
||||
<form class="pure-form pure-form-aligned" action="/add_compilation_items" method="get">
|
||||
<legend><a href="/compilation?list=<%= compilation.id %>"><%= translate(locale, "Editing compilation `x`", %|"#{HTML.escape(compilation.title)}"|) %></a></legend>
|
||||
|
||||
<fieldset>
|
||||
<input class="pure-input-1" type="search" name="q"
|
||||
<% if query %>value="<%= HTML.escape(query.text) %>"<% end %>
|
||||
placeholder="<%= translate(locale, "Search for videos") %>">
|
||||
<input type="hidden" name="list" value="<%= compid %>">
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
||||
</div>
|
||||
|
||||
<script id="compilation_data" type="application/json">
|
||||
<%=
|
||||
{
|
||||
"csrf_token" => URI.encode_www_form(env.get?("csrf_token").try &.as(String) || "")
|
||||
}.to_pretty_json
|
||||
%>
|
||||
</script>
|
||||
<script src="/js/compilation_widget.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
|
||||
<div class="pure-g">
|
||||
<% videos.each_slice(4) do |slice| %>
|
||||
<% slice.each do |item| %>
|
||||
<%= rendered "components/item" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
@ -1,7 +1,7 @@
|
||||
<div class="feed-menu">
|
||||
<% feed_menu = env.get("preferences").as(Preferences).feed_menu.dup %>
|
||||
<% if !env.get?("user") %>
|
||||
<% feed_menu.reject! {|item| {"Subscriptions", "Playlists"}.includes? item} %>
|
||||
<% feed_menu.reject! {|item| {"Subscriptions", "Playlists", "Compilations"}.includes? item} %>
|
||||
<% end %>
|
||||
<% feed_menu.each do |feed| %>
|
||||
<a href="/feed/<%= feed.downcase %>" class="feed-menu-item pure-menu-heading">
|
||||
|
@ -1,3 +1,39 @@
|
||||
<% content_for "header" do %>
|
||||
<title><%= translate(locale, "Create compilation") %> - Invidious</title>
|
||||
<% end %>
|
||||
|
||||
<div class="pure-g">
|
||||
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
||||
<div class="pure-u-1 pure-u-lg-3-5">
|
||||
<div class="h-box">
|
||||
<form class="pure-form pure-form-aligned" action="/create_compilation?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||
<fieldset>
|
||||
<legend><%= translate(locale, "Create compilation") %></legend>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="title"><%= translate(locale, "Title") %> :</label>
|
||||
<input required name="title" type="text" placeholder="<%= translate(locale, "Title") %>">
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="privacy"><%= translate(locale, "Compilation privacy") %> :</label>
|
||||
<select name="privacy" id="privacy">
|
||||
<% CompilationPrivacy.names.each do |option| %>
|
||||
<option value="<%= option %>" <% if option == "Unlisted" %> selected <% end %>><%= translate(locale, option) %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="pure-controls">
|
||||
<button type="submit" name="action" value="create_compilation" class="pure-button pure-button-primary">
|
||||
<%= translate(locale, "Create compilation") %>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="csrf_token" value="<%= HTML.escape(csrf_token) %>">
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user