diff options
| author | Omar Roth <omarroth@hotmail.com> | 2019-03-01 16:06:45 -0600 |
|---|---|---|
| committer | Omar Roth <omarroth@hotmail.com> | 2019-03-01 16:06:45 -0600 |
| commit | a39b1583dacff31664830fd4f8966014f8869fa0 (patch) | |
| tree | a2f137aef43680d918c60b33215cee9c7c03d8e9 /src | |
| parent | 2fe545e19a9e3669b5c4b7b5c53e559afb864341 (diff) | |
| download | invidious-a39b1583dacff31664830fd4f8966014f8869fa0.tar.gz invidious-a39b1583dacff31664830fd4f8966014f8869fa0.tar.bz2 invidious-a39b1583dacff31664830fd4f8966014f8869fa0.zip | |
Add administrator preferences
Diffstat (limited to 'src')
| -rw-r--r-- | src/invidious.cr | 239 | ||||
| -rw-r--r-- | src/invidious/helpers/helpers.cr | 19 | ||||
| -rw-r--r-- | src/invidious/jobs.cr | 6 | ||||
| -rw-r--r-- | src/invidious/users.cr | 6 | ||||
| -rw-r--r-- | src/invidious/views/components/feed_menu.ecr | 10 | ||||
| -rw-r--r-- | src/invidious/views/index.ecr | 14 | ||||
| -rw-r--r-- | src/invidious/views/login.ecr | 56 | ||||
| -rw-r--r-- | src/invidious/views/popular.ecr | 2 | ||||
| -rw-r--r-- | src/invidious/views/preferences.ecr | 82 | ||||
| -rw-r--r-- | src/invidious/views/template.ecr | 2 | ||||
| -rw-r--r-- | src/invidious/views/top.ecr | 2 | ||||
| -rw-r--r-- | src/invidious/views/trending.ecr | 2 |
12 files changed, 266 insertions, 174 deletions
diff --git a/src/invidious.cr b/src/invidious.cr index e6e8340b..36450676 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -31,42 +31,38 @@ require "./invidious/*" CONFIG = Config.from_yaml(File.read("config/config.yml")) HMAC_KEY = CONFIG.hmac_key || Random::Secure.random_bytes(32) -crawl_threads = CONFIG.crawl_threads -channel_threads = CONFIG.channel_threads -feed_threads = CONFIG.feed_threads -video_threads = CONFIG.video_threads - +config = CONFIG logger = Invidious::LogHandler.new Kemal.config.extra_options do |parser| parser.banner = "Usage: invidious [arguments]" - parser.on("-t THREADS", "--crawl-threads=THREADS", "Number of threads for crawling YouTube (default: #{crawl_threads})") do |number| + parser.on("-t THREADS", "--crawl-threads=THREADS", "Number of threads for crawling YouTube (default: #{config.crawl_threads})") do |number| begin - crawl_threads = number.to_i + config.crawl_threads = number.to_i rescue ex puts "THREADS must be integer" exit end end - parser.on("-c THREADS", "--channel-threads=THREADS", "Number of threads for refreshing channels (default: #{channel_threads})") do |number| + parser.on("-c THREADS", "--channel-threads=THREADS", "Number of threads for refreshing channels (default: #{config.channel_threads})") do |number| begin - channel_threads = number.to_i + config.channel_threads = number.to_i rescue ex puts "THREADS must be integer" exit end end - parser.on("-f THREADS", "--feed-threads=THREADS", "Number of threads for refreshing feeds (default: #{feed_threads})") do |number| + parser.on("-f THREADS", "--feed-threads=THREADS", "Number of threads for refreshing feeds (default: #{config.feed_threads})") do |number| begin - feed_threads = number.to_i + config.feed_threads = number.to_i rescue ex puts "THREADS must be integer" exit end end - parser.on("-v THREADS", "--video-threads=THREADS", "Number of threads for refreshing videos (default: #{video_threads})") do |number| + parser.on("-v THREADS", "--video-threads=THREADS", "Number of threads for refreshing videos (default: #{config.video_threads})") do |number| begin - video_threads = number.to_i + config.video_threads = number.to_i rescue ex puts "THREADS must be integer" exit @@ -107,28 +103,30 @@ LOCALES = { "ru" => load_locale("ru"), } -crawl_threads.times do +config.crawl_threads.times do spawn do crawl_videos(PG_DB, logger) end end -refresh_channels(PG_DB, logger, channel_threads, CONFIG.full_refresh) +refresh_channels(PG_DB, logger, config.channel_threads, config.full_refresh) -refresh_feeds(PG_DB, logger, feed_threads) +refresh_feeds(PG_DB, logger, config.feed_threads) -video_threads.times do |i| +config.video_threads.times do |i| spawn do refresh_videos(PG_DB, logger) end end top_videos = [] of Video -spawn do - pull_top_videos(CONFIG, PG_DB) do |videos| - top_videos = videos - sleep 1.minutes - Fiber.yield +if config.top_enabled + spawn do + pull_top_videos(config, PG_DB) do |videos| + top_videos = videos + sleep 1.minutes + Fiber.yield + end end end @@ -231,7 +229,20 @@ get "/" do |env| end end - templated "index" + case config.default_home + when "Popular" + templated "popular" + when "Top" + templated "top" + when "Trending" + env.redirect "/feed/trending" + when "Subscriptions" + if user + env.redirect "/feed/subscriptions" + else + templated "popular" + end + end end get "/licenses" do |env| @@ -367,7 +378,7 @@ get "/watch" do |env| video.description = replace_links(video.description) description = video.short_description - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) host_params = env.request.query_params host_params.delete_all("v") @@ -467,7 +478,7 @@ get "/embed/:id" do |env| video.description = replace_links(video.description) description = video.short_description - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) host_params = env.request.query_params host_params.delete_all("v") @@ -553,7 +564,7 @@ get "/opensearch.xml" do |env| locale = LOCALES[env.get("locale").as(String)]? env.response.content_type = "application/opensearchdescription+xml" - host = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host = make_host_url(Kemal.config.ssl || config.https_only, config.domain) XML.build(indent: " ", encoding: "UTF-8") do |xml| xml.element("OpenSearchDescription", xmlns: "http://a9.com/-/spec/opensearch/1.1/") do @@ -678,6 +689,11 @@ get "/login" do |env| next env.redirect "/feed/subscriptions" end + if !config.login_enabled + error_message = "Login has been disabled by administrator." + next templated "error" + end + referer = get_referer(env, "/feed/subscriptions") account_type = env.params.query["type"]? @@ -716,6 +732,11 @@ post "/login" do |env| referer = get_referer(env, "/feed/subscriptions") + if !config.login_enabled + error_message = "Login has been disabled by administrator." + next templated "error" + end + email = env.params.body["email"]? password = env.params.body["password"]? @@ -876,14 +897,14 @@ post "/login" do |env| host = URI.parse(env.request.headers["Host"]).host - if Kemal.config.ssl || CONFIG.https_only + 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 + if Kemal.config.ssl || config.https_only cookie.secure = secure else cookie.secure = secure @@ -912,54 +933,56 @@ post "/login" do |env| answer = env.params.body["answer"]? text_answer = env.params.body["text_answer"]? - if answer - answer = answer.lstrip('0') - answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer) - - challenge = env.params.body["challenge"]? - token = env.params.body["token"]? - - begin - validate_response(challenge, token, answer, "sign_in", HMAC_KEY, PG_DB, locale) - rescue ex - if ex.message == translate(locale, "Invalid user") - error_message = translate(locale, "Invalid answer") - else - error_message = ex.message - end - - next templated "error" - end - elsif text_answer - text_answer = Digest::MD5.hexdigest(text_answer.downcase.strip) + if config.captcha_enabled + if answer + answer = answer.lstrip('0') + answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer) - challenges = env.params.body.select { |k, v| k.match(/text_challenge\d+/) } - tokens = env.params.body.select { |k, v| k.match(/text_token\d+/) } + challenge = env.params.body["challenge"]? + token = env.params.body["token"]? - found_valid_captcha = false - - error_message = translate(locale, "Invalid CAPTCHA") - challenges.each_with_index do |challenge, i| begin - challenge = challenge[1] - token = tokens[i][1] - validate_response(challenge, token, text_answer, "sign_in", HMAC_KEY, PG_DB, locale) - found_valid_captcha = true + validate_response(challenge, token, answer, "sign_in", HMAC_KEY, PG_DB, locale) rescue ex if ex.message == translate(locale, "Invalid user") error_message = translate(locale, "Invalid answer") else error_message = ex.message end + + next templated "error" + end + elsif text_answer + text_answer = Digest::MD5.hexdigest(text_answer.downcase.strip) + + challenges = env.params.body.select { |k, v| k.match(/text_challenge\d+/) } + tokens = env.params.body.select { |k, v| k.match(/text_token\d+/) } + + found_valid_captcha = false + + error_message = translate(locale, "Invalid CAPTCHA") + challenges.each_with_index do |challenge, i| + begin + challenge = challenge[1] + token = tokens[i][1] + validate_response(challenge, token, text_answer, "sign_in", HMAC_KEY, PG_DB, locale) + found_valid_captcha = true + rescue ex + if ex.message == translate(locale, "Invalid user") + error_message = translate(locale, "Invalid answer") + else + error_message = ex.message + end + end end - end - if !found_valid_captcha + if !found_valid_captcha + next templated "error" + end + else + error_message = translate(locale, "CAPTCHA is a required field") next templated "error" end - else - error_message = translate(locale, "CAPTCHA is a required field") - next templated "error" end action = env.params.body["action"]? @@ -992,14 +1015,14 @@ post "/login" do |env| sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32)) PG_DB.exec("INSERT INTO session_ids VALUES ($1, $2, $3)", sid, email, Time.now) - if Kemal.config.ssl || CONFIG.https_only + if Kemal.config.ssl || config.https_only secure = true else secure = false end - if CONFIG.domain - env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: ".#{CONFIG.domain}", value: sid, expires: Time.now + 2.years, + if config.domain + env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: ".#{config.domain}", value: sid, expires: Time.now + 2.years, secure: secure, http_only: true) else env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", value: sid, expires: Time.now + 2.years, @@ -1016,6 +1039,11 @@ post "/login" do |env| secure: secure, http_only: true) end elsif action == "register" + if !config.registration_enabled + error_message = "Registration has been disabled by administrator." + next templated "error" + end + if password.empty? error_message = translate(locale, "Password cannot be empty") next templated "error" @@ -1049,14 +1077,14 @@ post "/login" do |env| ucid = ANY ((SELECT subscriptions FROM users WHERE email = E'#{user.email.gsub("'", "\\'")}')::text[]) \ ORDER BY published DESC;") - if Kemal.config.ssl || CONFIG.https_only + if Kemal.config.ssl || config.https_only secure = true else secure = false end - if CONFIG.domain - env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: ".#{CONFIG.domain}", value: sid, expires: Time.now + 2.years, + if config.domain + env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", domain: ".#{config.domain}", value: sid, expires: Time.now + 2.years, secure: secure, http_only: true) else env.response.cookies["SID"] = HTTP::Cookie.new(name: "SID", value: sid, expires: Time.now + 2.years, @@ -1153,14 +1181,15 @@ post "/preferences" do |env| 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] + comments = [] of String + 2.times do |i| + comments << (env.params.body["comments[#{i}]"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.comments[i]) + end - 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] + captions = [] of String + 3.times do |i| + captions << (env.params.body["captions[#{i}]"]?.try &.as(String) || DEFAULT_USER_PREFERENCES.captions[i]) + end related_videos = env.params.body["related_videos"]?.try &.as(String) related_videos ||= "off" @@ -1224,6 +1253,37 @@ post "/preferences" do |env| if user = env.get? "user" user = user.as(User) PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email) + + if config.admins.includes? user.email + config.default_home = env.params.body["default_home"]?.try &.as(String) || config.default_home + + feed_menu = [] of String + 4.times do |index| + option = env.params.body["feed_menu[#{index}]"]?.try &.as(String) || "" + if !option.empty? + feed_menu << option + end + end + config.feed_menu = feed_menu + + top_enabled = env.params.body["top_enabled"]?.try &.as(String) + top_enabled ||= "off" + config.top_enabled = top_enabled == "on" + + captcha_enabled = env.params.body["captcha_enabled"]?.try &.as(String) + captcha_enabled ||= "off" + config.captcha_enabled = captcha_enabled == "on" + + login_enabled = env.params.body["login_enabled"]?.try &.as(String) + login_enabled ||= "off" + config.login_enabled = login_enabled == "on" + + registration_enabled = env.params.body["registration_enabled"]?.try &.as(String) + registration_enabled ||= "off" + config.registration_enabled = registration_enabled == "on" + + File.write("config/config.yml", config.to_yaml) + end else env.response.cookies["PREFS"] = preferences end @@ -1397,7 +1457,7 @@ get "/subscription_manager" do |env| subscriptions.sort_by! { |channel| channel.author.downcase } if action_takeout - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) if format == "json" env.response.content_type = "application/json" @@ -1741,7 +1801,11 @@ end get "/feed/top" do |env| locale = LOCALES[env.get("locale").as(String)]? - templated "top" + if config.top_enabled + templated "top" + else + env.redirect "/" + end end get "/feed/popular" do |env| @@ -1984,7 +2048,7 @@ get "/feed/channel/:ucid" do |env| ) end - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) path = env.request.path feed = XML.build(indent: " ", encoding: "UTF-8") do |xml| @@ -2118,7 +2182,7 @@ get "/feed/private" do |env| videos = videos[0..max_results] end - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) path = env.request.path query = env.request.query.not_nil! @@ -2173,7 +2237,7 @@ get "/feed/playlist/:plid" do |env| plid = env.params.url["plid"] - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) path = env.request.path client = make_client(YT_URL) @@ -2487,7 +2551,7 @@ get "/api/v1/insights/:id" do |env| env.response.content_type = "application/json" error_message = {"error" => "YouTube has removed publicly-available analytics."}.to_json - halt env, status_code: 503, response: error_message + halt env, status_code: 410, response: error_message client = make_client(YT_URL) headers = HTTP::Headers.new @@ -2653,7 +2717,7 @@ get "/api/v1/videos/:id" do |env| end if video.player_response["streamingData"]?.try &.["hlsManifestUrl"]? - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) host_params = env.request.query_params host_params.delete_all("v") @@ -2871,6 +2935,11 @@ get "/api/v1/top" do |env| env.response.content_type = "application/json" + if !config.top_enabled + error_message = {"error" => "Administrator has disabled this endpoint."}.to_json + halt env, status_code: 400, response: error_message + end + videos = JSON.build do |json| json.array do top_videos.each do |video| @@ -3842,7 +3911,7 @@ get "/api/manifest/hls_variant/*" do |env| env.response.content_type = "application/x-mpegURL" env.response.headers.add("Access-Control-Allow-Origin", "*") - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) manifest = manifest.body manifest.gsub("https://www.youtube.com", host_url) @@ -3856,7 +3925,7 @@ get "/api/manifest/hls_playlist/*" do |env| halt env, status_code: manifest.status_code end - host_url = make_host_url(Kemal.config.ssl || CONFIG.https_only, CONFIG.domain) + host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) manifest = manifest.body.gsub("https://www.youtube.com", host_url) manifest = manifest.gsub(/https:\/\/r\d---.{11}\.c\.youtube\.com/, host_url) diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 45ebc4dd..26b6244c 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -1,9 +1,9 @@ class Config YAML.mapping({ + video_threads: Int32, # Number of threads to use for updating videos in cache (mostly non-functional) crawl_threads: Int32, # Number of threads to use for finding new videos from YouTube (used to populate "top" page) channel_threads: Int32, # Number of threads to use for crawling videos from channels (for updating subscriptions) feed_threads: Int32, # Number of threads to use for updating feeds - video_threads: Int32, # Number of threads to use for updating videos in cache (mostly non-functional) db: NamedTuple( # Database configuration user: String, password: String, @@ -11,11 +11,18 @@ user: String, port: Int32, dbname: String, ), - dl_api_key: String?, # DetectLanguage API Key (used to filter non-English results from "top" page), mostly non-functional - https_only: Bool?, # Used to tell Invidious it is behind a proxy, so links to resources should be https:// - hmac_key: String?, # HMAC signing key for CSRF tokens - full_refresh: Bool, # Used for crawling channels: threads should check all videos uploaded by a channel - domain: String, # Domain to be used for links to resources on the site where an absolute URL is required + full_refresh: Bool, # Used for crawling channels: threads should check all videos uploaded by a channel + https_only: Bool?, # Used to tell Invidious it is behind a proxy, so links to resources should be https:// + hmac_key: String?, # HMAC signing key for CSRF tokens + domain: String, # Domain to be used for links to resources on the site where an absolute URL is required + dl_api_key: String?, # DetectLanguage API Key (used to filter non-English results from "top" page), mostly non-functional + default_home: {type: String, default: "Top"}, + feed_menu: {type: Array(String), default: ["Popular", "Top", "Trending"]}, + top_enabled: {type: Bool, default: true}, + captcha_enabled: {type: Bool, default: true}, + login_enabled: {type: Bool, default: true}, + registration_enabled: {type: Bool, default: true}, + admins: {type: Array(String), default: [] of String}, }) end diff --git a/src/invidious/jobs.cr b/src/invidious/jobs.cr index e000540a..cc5f85d1 100644 --- a/src/invidious/jobs.cr +++ b/src/invidious/jobs.cr @@ -133,7 +133,7 @@ def refresh_feeds(db, logger, max_threads = 1) rescue ex # Create view if it doesn't exist if ex.message.try &.ends_with? "does not exist" - PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS \ + db.exec("CREATE MATERIALIZED VIEW #{view_name} AS \ SELECT * FROM channel_videos WHERE \ ucid = ANY ((SELECT subscriptions FROM users WHERE email = E'#{email.gsub("'", "\\'")}')::text[]) \ ORDER BY published DESC;") @@ -193,11 +193,11 @@ end def pull_popular_videos(db) loop do - subscriptions = PG_DB.query_all("SELECT channel FROM \ + subscriptions = db.query_all("SELECT channel FROM \ (SELECT UNNEST(subscriptions) AS channel FROM users) AS d \ GROUP BY channel ORDER BY COUNT(channel) DESC LIMIT 40", as: String) - videos = PG_DB.query_all("SELECT DISTINCT ON (ucid) * FROM \ + videos = db.query_all("SELECT DISTINCT ON (ucid) * FROM \ channel_videos WHERE ucid IN (#{arg_array(subscriptions)}) \ ORDER BY ucid, published DESC", subscriptions, as: ChannelVideo).sort_by { |video| video.published }.reverse diff --git a/src/invidious/users.cr b/src/invidious/users.cr index 48d8008f..42468228 100644 --- a/src/invidious/users.cr +++ b/src/invidious/users.cr @@ -143,7 +143,7 @@ def get_user(sid, headers, db, refresh = true) begin view_name = "subscriptions_#{sha256(user.email)[0..7]}" - PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS \ + db.exec("CREATE MATERIALIZED VIEW #{view_name} AS \ SELECT * FROM channel_videos WHERE \ ucid = ANY ((SELECT subscriptions FROM users WHERE email = E'#{user.email.gsub("'", "\\'")}')::text[]) \ ORDER BY published DESC;") @@ -165,7 +165,7 @@ def get_user(sid, headers, db, refresh = true) begin view_name = "subscriptions_#{sha256(user.email)[0..7]}" - PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS \ + db.exec("CREATE MATERIALIZED VIEW #{view_name} AS \ SELECT * FROM channel_videos WHERE \ ucid = ANY ((SELECT subscriptions FROM users WHERE email = E'#{user.email.gsub("'", "\\'")}')::text[]) \ ORDER BY published DESC;") @@ -247,7 +247,7 @@ def validate_response(challenge, token, user_id, operation, key, db, locale) raise translate(locale, "Invalid challenge") end - challenge = OpenSSL::HMAC.digest(:sha256, HMAC_KEY, challenge) + challenge = OpenSSL::HMAC.digest(:sha256, key, challenge) challenge = Base64.urlsafe_encode(challenge) if db.query_one?("SELECT EXISTS (SELECT true FROM nonces WHERE nonce = $1)", nonce, as: Bool) diff --git a/src/invidious/views/components/feed_menu.ecr b/src/invidious/views/components/feed_menu.ecr index 5c19bafc..378601a1 100644 --- a/src/invidious/views/components/feed_menu.ecr +++ b/src/invidious/views/components/feed_menu.ecr @@ -2,12 +2,12 @@ <div class="pure-u-1 pure-u-md-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" %> + <% feed_menu = config.feed_menu.dup %> + <% if !env.get?("user") %> + <% feed_menu.reject! {|feed| feed == "Subscriptions"} %> <% end %> - <% feeds.each do |feed| %> - <div class="pure-u-1-2 pure-u-md-1-<%= feeds.size %>"> + <% feed_menu.each do |feed| %> + <div class="pure-u-1-2 pure-u-md-1-<%= feed_menu.size %>"> <a href="/feed/<%= feed.downcase %>" style="text-align:center;" class="pure-menu-heading"> <%= translate(locale, feed) %> </a> diff --git a/src/invidious/views/index.ecr b/src/invidious/views/index.ecr deleted file mode 100644 index 6cc978e5..00000000 --- a/src/invidious/views/index.ecr +++ /dev/null @@ -1,14 +0,0 @@ -<% content_for "header" do %> -<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>"> -<title>Invidious</title> -<% end %> - -<%= rendered "components/feed_menu" %> - -<div class="pure-g"> -<% top_videos.each_slice(4) do |slice| %> - <% slice.each do |item| %> - <%= rendered "components/item" %> - <% end %> -<% end %> -</div> diff --git a/src/invidious/views/login.ecr b/src/invidious/views/login.ecr index f4b0ce96..20363b2c 100644 --- a/src/invidious/views/login.ecr +++ b/src/invidious/views/login.ecr @@ -27,36 +27,40 @@ <label for="password"><%= translate(locale, "Password:") %></label> <input required class="pure-input-1" name="password" type="password" placeholder="Password"> - - <% if captcha_type == "image" %> - <img style="width:100%" src='<%= captcha.not_nil![:image] %>'/> - <input type="hidden" name="token" value="<%= captcha.not_nil![:token] %>"> - <input type="hidden" name="challenge" value="<%= captcha.not_nil![:challenge] %>"> - <label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label> - <input required type="text" name="answer" type="text" placeholder="h:mm:ss"> - - <label> - <a href="/login?referer=<%= URI.escape(referer) %>&type=invidious&captcha=text"> - <%= translate(locale, "Text CAPTCHA") %> - </a> - </label> - <% else %> - <% text_captcha.not_nil![:tokens].each_with_index do |token, i| %> - <input type="hidden" name="text_challenge<%= i %>" value="<%= token[0] %>"> - <input type="hidden" name="text_token<%= i %>" value="<%= token[1] %>"> - <% end %> - <label for="text_answer"><%= text_captcha.not_nil![:question] %></label> - <input required type="text" name="text_answer" type="text" placeholder="Answer"> - <label> - <a href="/login?referer=<%= URI.escape(referer) %>&type=invidious"> - <%= translate(locale, "Image CAPTCHA") %> - </a> - </label> + <% if config.captcha_enabled %> + <% if captcha_type == "image" %> + <img style="width:100%" src='<%= captcha.not_nil![:image] %>'/> + <input type="hidden" name="token" value="<%= captcha.not_nil![:token] %>"> + <input type="hidden" name="challenge" value="<%= captcha.not_nil![:challenge] %>"> + <label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label> + <input required type="text" name="answer" type="text" placeholder="h:mm:ss"> + + <label> + <a href="/login?referer=<%= URI.escape(referer) %>&type=invidious&captcha=text"> + <%= translate(locale, "Text CAPTCHA") %> + </a> + </label> + <% else %> + <% text_captcha.not_nil![:tokens].each_with_index do |token, i| %> + <input type="hidden" name="text_challenge<%= i %>" value="<%= token[0] %>"> + <input type="hidden" name="text_token<%= i %>" value="<%= token[1] %>"> + <% end %> + <label for="text_answer"><%= text_captcha.not_nil![:question] %></label> + <input required type="text" name="text_answer" type="text" placeholder="Answer"> + + <label> + <a href="/login?referer=<%= URI.escape(referer) %>&type=invidious"> + <%= translate(locale, "Image CAPTCHA") %> + </a> + </label> + <% end %> <% end %> <button type="submit" name="action" value="signin" class="pure-button pure-button-primary"><%= translate(locale, "Sign In") %></button> + <% if config.registration_enabled %> <button type="submit" name="action" value="register" class="pure-button pure-button-primary"><%= translate(locale, "Register") %></button> + <% end %> </fieldset> </form> <% elsif account_type == "google" %> @@ -67,7 +71,7 @@ <label for="password"><%= translate(locale, "Password:") %></label> <input required class="pure-input-1" name="password" type="password" placeholder="Password"> - + <% if tfa %> <label for="tfa"><%= translate(locale, "Google verification code:") %></label> <input required class="pure-input-1" name="tfa" type="text" placeholder="Google verification code"> diff --git a/src/invidious/views/popular.ecr b/src/invidious/views/popular.ecr index f235aad8..d60ae557 100644 --- a/src/invidious/views/popular.ecr +++ b/src/invidious/views/popular.ecr @@ -1,6 +1,6 @@ <% content_for "header" do %> <meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>"> -<title><%= translate(locale, "Popular") %> - Invidious</title> +<title><% if config.default_home != "Popular" %><%= translate(locale, "Popular") %> - <% end %>Invidious</title> <% end %> <%= rendered "components/feed_menu" %> diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr index 7435c91e..ea6a84c8 100644 --- a/src/invidious/views/preferences.ecr +++ b/src/invidious/views/preferences.ecr @@ -58,45 +58,25 @@ function update_value(element) { </div> <div class="pure-control-group"> - <label for="comments_0"><%= translate(locale, "Default comments: ") %></label> - <select name="comments_0" id="comments_0"> + <label for="comments[0]"><%= translate(locale, "Default comments: ") %></label> + <% preferences.comments.each_with_index do |comments, index| %> + <select name="comments[<%= index %>]" id="comments[<%= index %>]"> <% {"", "youtube", "reddit"}.each do |option| %> - <option value="<%= option %>" <% if preferences.comments[0] == option %> selected <% end %>><%= translate(locale, option) %></option> + <option value="<%= option %>" <% if preferences.comments[index] == option %> selected <% end %>><%= translate(locale, option) %></option> <% end %> </select> - </div> - - <div class="pure-control-group"> - <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 preferences.comments[1] == option %> selected <% end %>><%= translate(locale, option) %></option> - <% end %> - </select> - </div> - - <div class="pure-control-group"> - <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 preferences.captions[0] == option %> selected <% end %>><%= translate(locale, option) %></option> <% end %> - </select> </div> <div class="pure-control-group"> - <label for="captions_fallback"><%= translate(locale, "Fallback captions: ") %></label> - <select class="pure-u-1-5" name="captions_1" id="captions_1"> + <label for="captions[0]"><%= translate(locale, "Default captions: ") %></label> + <% preferences.captions.each_with_index do |caption, index| %> + <select class="pure-u-1-6" name="captions[<%= index %>]" id="captions[<%= index %>]"> <% CAPTION_LANGUAGES.each do |option| %> - <option value="<%= option %>" <% if preferences.captions[1] == option %> selected <% end %>><%= translate(locale, option) %></option> + <option value="<%= option %>" <% if preferences.captions[index] == 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 preferences.captions[2] == option %> selected <% end %>><%= translate(locale, option) %></option> <% end %> - </select> </div> <div class="pure-control-group"> @@ -167,13 +147,57 @@ function update_value(element) { </div> <% end %> + <% if env.get?("user") && config.admins.includes? env.get?("user").as(User).email %> + <legend><%= translate(locale, "Administrator preferences") %></legend> + + <div class="pure-control-group"> + <label for="default_home"><%= translate(locale, "Default homepage: ") %></label> + <select name="default_home" id="default_home"> + <% {"Popular", "Top", "Trending", "Subscriptions"}.each do |option| %> + <option value="<%= option %>" <% if config.default_home == option %> selected <% end %>><%= translate(locale, option) %></option> + <% end %> + </select> + </div> + + <div class="pure-control-group"> + <label for="feed_menu"><%= translate(locale, "Feed menu: ") %></label> + <% 4.times do |index| %> + <select name="feed_menu[<%= index %>]" id="feed_menu[<%= index %>]"> + <% {"", "Popular", "Top", "Trending", "Subscriptions"}.each do |option| %> + <option value="<%= option %>" <% if config.feed_menu[index]? == option %> selected <% end %>><%= translate(locale, option) %></option> + <% end %> + </select> + <% end %> + </div> + + <div class="pure-control-group"> + <label for="top_enabled"><%= translate(locale, "Top enabled? ") %></label> + <input name="top_enabled" id="top_enabled" type="checkbox" <% if config.top_enabled %>checked<% end %>> + </div> + + <div class="pure-control-group"> + <label for="captcha_enabled"><%= translate(locale, "CAPTCHA enabled? ") %></label> + <input name="captcha_enabled" id="captcha_enabled" type="checkbox" <% if config.captcha_enabled %>checked<% end %>> + </div> + + <div class="pure-control-group"> + <label for="login_enabled"><%= translate(locale, "Login enabled? ") %></label> + <input name="login_enabled" id="login_enabled" type="checkbox" <% if config.login_enabled %>checked<% end %>> + </div> + + <div class="pure-control-group"> + <label for="registration_enabled"><%= translate(locale, "Registration enabled? ") %></label> + <input name="registration_enabled" id="registration_enabled" type="checkbox" <% if config.registration_enabled %>checked<% end %>> + </div> + <% end %> + <% if env.get? "user" %> <legend><%= translate(locale, "Data preferences") %></legend> <div class="pure-control-group"> <a href="/clear_watch_history?referer=<%= URI.escape(referer) %>"><%= translate(locale, "Clear watch history") %></a> </div> - + <div class="pure-control-group"> <a href="/data_control?referer=<%= URI.escape(referer) %>"><%= translate(locale, "Import/Export data") %></a> </div> diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index b58135d7..0ca794ca 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -89,12 +89,14 @@ <i class="icon ion-ios-cog"></i> </a> </div> + <% if config.login_enabled %> <div class="pure-u-1-3"> <a href="/login?referer=<%= env.get?("current_page") %>" class="pure-menu-heading"> <%= translate(locale, "Login") %> </a> </div> <% end %> + <% end %> </div> </div> <%= content %> diff --git a/src/invidious/views/top.ecr b/src/invidious/views/top.ecr index acf122a6..44faf706 100644 --- a/src/invidious/views/top.ecr +++ b/src/invidious/views/top.ecr @@ -1,6 +1,6 @@ <% content_for "header" do %> <meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>"> -<title><%= translate(locale, "Top") %> - Invidious</title> +<title><% if config.default_home != "Top" %><%= translate(locale, "Top") %> - <% end %>Invidious</title> <% end %> <%= rendered "components/feed_menu" %> diff --git a/src/invidious/views/trending.ecr b/src/invidious/views/trending.ecr index 617a9a58..efd9999a 100644 --- a/src/invidious/views/trending.ecr +++ b/src/invidious/views/trending.ecr @@ -1,6 +1,6 @@ <% content_for "header" do %> <meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>"> -<title><%= translate(locale, "Trending") %> - Invidious</title> +<title><% if config.default_home != "Trending" %><%= translate(locale, "Trending") %> - <% end %>Invidious</title> <% end %> <%= rendered "components/feed_menu" %> |
