From 555de5f4efae72226af37e07a1e769f1fbf63b12 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 11 Aug 2021 01:30:45 +0200 Subject: [PATCH] Use enum where possible Use enum for the theme ('dark_mode'), the home pages, the player styles and the sort option. Video qualities still require 'quality' and 'quality_dash' to be merged. --- src/invidious.cr | 8 +- src/invidious/helpers/utils.cr | 13 - src/invidious/routes/misc.cr | 14 +- src/invidious/routes/preferences.cr | 47 ++-- src/invidious/user/preferences.cr | 256 +++++++++++++++---- src/invidious/users.cr | 32 +-- src/invidious/views/components/feed_menu.ecr | 6 +- src/invidious/views/preferences.ecr | 24 +- src/invidious/views/template.ecr | 10 +- 9 files changed, 263 insertions(+), 147 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 1d183637..629cb013 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -25,11 +25,13 @@ require "xml" require "yaml" require "compress/zip" require "protodec/utils" + require "./invidious/helpers/*" require "./invidious/*" require "./invidious/channels/*" require "./invidious/routes/**" require "./invidious/jobs/**" +require "./invidious/user/*" CONFIG = Config.load HMAC_KEY = CONFIG.hmac_key || Random::Secure.hex(32) @@ -281,12 +283,14 @@ before_all do |env| end end - dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s + theme = env.params.query["dark_mode"]? + theme = Settings::Converters::Theme.from_s(theme) if theme + preferences.dark_mode = theme if theme + thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s thin_mode = thin_mode == "true" locale = env.params.query["hl"]? || preferences.locale - preferences.dark_mode = dark_mode preferences.thin_mode = thin_mode preferences.locale = locale env.set "preferences", preferences diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 6ee07d7a..1c8f4e34 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -397,19 +397,6 @@ def parse_range(range) return 0_i64, nil end -def convert_theme(theme) - case theme - when "true" - "dark" - when "false" - "light" - when "", nil - nil - else - theme - end -end - def fetch_random_instance begin instance_api_client = HTTP::Client.new(URI.parse("https://api.invidious.io")) diff --git a/src/invidious/routes/misc.cr b/src/invidious/routes/misc.cr index 336f7e33..fb1c1b52 100644 --- a/src/invidious/routes/misc.cr +++ b/src/invidious/routes/misc.cr @@ -5,24 +5,22 @@ class Invidious::Routes::Misc < Invidious::Routes::BaseRoute user = env.get? "user" case preferences.default_home - when "Popular" - env.redirect "/feed/popular" - when "Trending" - env.redirect "/feed/trending" - when "Subscriptions" + when Settings::HomePages::Popular ; env.redirect "/feed/popular" + when Settings::HomePages::Trending; env.redirect "/feed/trending" + when Settings::UserHomePages::Subscriptions if user env.redirect "/feed/subscriptions" else env.redirect "/feed/popular" end - when "Playlists" + when Settings::UserHomePages::Playlists if user env.redirect "/view_all_playlists" else env.redirect "/feed/popular" end - else - templated "search_homepage", navbar_search: false + else # Settings::HomePages::Search + env.redirect "/search" end end diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index 21d79218..5d1273dd 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -48,8 +48,8 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute speed = env.params.body["speed"]?.try &.as(String).to_f32? speed ||= CONFIG.default_user_preferences.speed - player_style = env.params.body["player_style"]?.try &.as(String) - player_style ||= CONFIG.default_user_preferences.player_style + player_style = env.params.body["player_style"]?.try { |x| Settings::PlayerStyles.parse?(x) } + player_style = CONFIG.default_user_preferences.player_style if !player_style quality = env.params.body["quality"]?.try &.as(String) quality ||= CONFIG.default_user_preferences.quality @@ -86,7 +86,8 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute related_videos ||= "off" related_videos = related_videos == "on" - default_home = env.params.body["default_home"]?.try &.as(String) || CONFIG.default_user_preferences.default_home + default_home = env.params.body["default_home"]?.try { |x| Settings::HomePages.parse?(x) || Settings::UserHomePages.parse?(x) } + default_home = CONFIG.default_user_preferences.default_home.to_s if !default_home feed_menu = [] of String 4.times do |index| @@ -103,8 +104,8 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute locale = env.params.body["locale"]?.try &.as(String) locale ||= CONFIG.default_user_preferences.locale - dark_mode = env.params.body["dark_mode"]?.try &.as(String) - dark_mode ||= CONFIG.default_user_preferences.dark_mode + theme = env.params.body["dark_mode"]?.try { |x| Settings::Themes.parse?(x) } + theme = CONFIG.default_user_preferences.dark_mode if !theme thin_mode = env.params.body["thin_mode"]?.try &.as(String) thin_mode ||= "off" @@ -113,8 +114,8 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute max_results = env.params.body["max_results"]?.try &.as(String).to_i? max_results ||= CONFIG.default_user_preferences.max_results - sort = env.params.body["sort"]?.try &.as(String) - sort ||= CONFIG.default_user_preferences.sort + sort = env.params.body["sort"]?.try { |x| Settings::SortOptions.parse?(x) } + sort = CONFIG.default_user_preferences.sort if !sort latest_only = env.params.body["latest_only"]?.try &.as(String) latest_only ||= "off" @@ -137,7 +138,7 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute comments: comments, continue: continue, continue_autoplay: continue_autoplay, - dark_mode: dark_mode, + dark_mode: theme, latest_only: latest_only, listen: listen, local: local, @@ -167,14 +168,13 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email) if CONFIG.admins.includes? user.email - CONFIG.default_user_preferences.default_home = env.params.body["admin_default_home"]?.try &.as(String) || CONFIG.default_user_preferences.default_home + default_home = env.params.body["admin_default_home"]?.try { |x| Settings::HomePages.parse?(x) || Settings::UserHomePages.parse?(x) } + CONFIG.default_user_preferences.default_home = default_home if default_home - admin_feed_menu = [] of String + admin_feed_menu = [] of Settings::AnyHomePages 4.times do |index| - option = env.params.body["admin_feed_menu[#{index}]"]?.try &.as(String) || "" - if !option.empty? - admin_feed_menu << option - end + option = env.params.body["admin_feed_menu[#{index}]"]?.try { |x| Settings::HomePages.parse?(x) || Settings::UserHomePages.parse?(x) } + admin_feed_menu << option if option end CONFIG.default_user_preferences.feed_menu = admin_feed_menu @@ -229,28 +229,15 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute if user = env.get? "user" user = user.as(User) + preferences = user.preferences - - case preferences.dark_mode - when "dark" - preferences.dark_mode = "light" - else - preferences.dark_mode = "dark" - end - + preferences.toggle_theme preferences = preferences.to_json PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email) else preferences = env.get("preferences").as(Preferences) - - case preferences.dark_mode - when "dark" - preferences.dark_mode = "light" - else - preferences.dark_mode = "dark" - end - + preferences.toggle_theme preferences = preferences.to_json if Kemal.config.ssl || CONFIG.https_only diff --git a/src/invidious/user/preferences.cr b/src/invidious/user/preferences.cr index b800dd69..e051aa78 100644 --- a/src/invidious/user/preferences.cr +++ b/src/invidious/user/preferences.cr @@ -5,6 +5,84 @@ require "json" require "yaml" +require "html" + +# +# Enumerates types and constants +# + +module Settings + ALLOWED_SPEED_VALUES = { + 2.00_f32, # Double speed + 1.75_f32, + 1.50_f32, + 1.25_f32, + 1.00_f32, # Normal + 0.75_f32, + 0.50_f32, # Half speed + 0.25_f32, + } + + enum Themes + Auto # I.e use the system's settings with media queries + Light + Dark + end + + enum PlayerStyles + Invidious + Youtube + end + + # General + enum HomePages + Search + Popular + Trending + end + + # Authenticated + enum UserHomePages + Subscriptions + Playlists + end + + alias AnyHomePages = HomePages | UserHomePages + + enum SortOptions + Alphabetically + Alphabetically_Reverse + Channel_Name + Channel_Name_Reverse + Publication_Date + Publication_Date_Reverse + end + + # Unused for now, reuires merging + enum VideoQualities + # Normal + HD720 + Medium + Small + # Dash + DASH_Auto + DASH_Best + DASH_4320p + DASH_2160p + DASH_1440p + DASH_1080p + DASH_720p + DASH_480p + DASH_360p + DASH_240p + DASH_144p + DASH_Worst + end +end + +# +# Data structure that stores a user's preferences. +# class Preferences include JSON::Serializable @@ -12,6 +90,7 @@ class Preferences property annotations : Bool = false property annotations_subscribed : Bool = false + property autoplay : Bool = false property automatic_instance_redirect : Bool = false @@ -22,12 +101,13 @@ class Preferences @[JSON::Field(converter: Preferences::StringToArray)] @[YAML::Field(converter: Preferences::StringToArray)] property comments : Array(String) = ["youtube", ""] + property continue : Bool = false property continue_autoplay : Bool = true - @[JSON::Field(converter: Preferences::BoolToString)] - @[YAML::Field(converter: Preferences::BoolToString)] - property dark_mode : String = "" + @[JSON::Field(converter: Settings::Converters::Generic(Settings::Themes))] + @[YAML::Field(converter: Settings::Converters::Generic(Settings::Themes))] + property dark_mode : Settings::Themes = Settings::Themes::Dark property latest_only : Bool = false @@ -43,20 +123,34 @@ class Preferences property max_results : Int32 = 40 property notifications_only : Bool = false - @[JSON::Field(converter: Preferences::ProcessString)] - property player_style : String = "invidious" + @[JSON::Field(converter: Settings::Converters::Generic(Settings::PlayerStyles))] + @[YAML::Field(converter: Settings::Converters::Generic(Settings::PlayerStyles))] + property player_style : Settings::PlayerStyles = Settings::PlayerStyles::Invidious @[JSON::Field(converter: Preferences::ProcessString)] property quality : String = "hd720" @[JSON::Field(converter: Preferences::ProcessString)] property quality_dash : String = "auto" - property default_home : String? = "Popular" - property feed_menu : Array(String) = ["Popular", "Trending", "Subscriptions", "Playlists"] + @[JSON::Field(converter: Settings::Converters::Generic(Settings::AnyHomePages))] + @[YAML::Field(converter: Settings::Converters::Generic(Settings::AnyHomePages))] + property default_home : Settings::AnyHomePages = Settings::HomePages::Popular + + @[JSON::Field(converter: Settings::Converters::Generic(Array(Settings::AnyHomePages)))] + @[YAML::Field(converter: Settings::Converters::Generic(Array(Settings::AnyHomePages)))] + property feed_menu : Array(Settings::AnyHomePages) = [ + Settings::HomePages::Popular, + Settings::HomePages::Trending, + Settings::UserHomePages::Subscriptions, + Settings::UserHomePages::Playlists, + ] + property related_videos : Bool = true - @[JSON::Field(converter: Preferences::ProcessString)] - property sort : String = "published" + @[JSON::Field(converter: Settings::Converters::Generic(Settings::SortOptions))] + @[YAML::Field(converter: Settings::Converters::Generic(Settings::SortOptions))] + property sort : Settings::SortOptions = Settings::SortOptions::Publication_Date + property speed : Float32 = 1.0_f32 property thin_mode : Bool = false @@ -103,49 +197,9 @@ class Preferences {% end %} end - module BoolToString - def self.to_json(value : String, json : JSON::Builder) - json.string value - end - - def self.from_json(value : JSON::PullParser) : String - begin - result = value.read_string - - if result.empty? - CONFIG.default_user_preferences.dark_mode - else - result - end - rescue ex - if value.read_bool - "dark" - else - "light" - end - end - end - - def self.to_yaml(value : String, yaml : YAML::Nodes::Builder) - yaml.scalar value - end - - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : String - unless node.is_a?(YAML::Nodes::Scalar) - node.raise "Expected scalar, not #{node.class}" - end - - case node.value - when "true" - "dark" - when "false" - "light" - when "" - CONFIG.default_user_preferences.dark_mode - else - node.value - end - end + def toggle_theme + old = @dark_mode + @dark_mode = (old.dark?) ? Settings::Themes::Light : Settings::Themes::Dark end module ClampInt @@ -247,7 +301,99 @@ class Preferences } {% end %} end -end +end # class Data + +# +# Datatype converters (also act as data validators) +# + +module Settings::Converters + # + # Generic enum conversion + # + module Generic(T) + extend self + + # From/To JSON + + def to_json(value : T, json : JSON::Builder) + value.to_json json + end + + def from_json(value : JSON::PullParser) : T? + begin + T.new value + rescue e : JSON::ParseException + # Be silent on invalid data and return nil + # This will fallback to the default value + end + end + + # From/To YAML + + def to_yaml(value : T, yaml : YAML::Nodes::Builder) + value.to_yaml yaml + end + + def from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : T? + begin + T.new ctx, node + rescue e : YAML::ParseException + # Be silent on invalid data and return nil + # This will fallback to the default value + end + end + end # module Generic + + # + # Themes enum conversion + # + module Theme + extend self + + # From String (.to_s is native of Enum type) + + def from_s(input : String?) : Themes? + return if !input + + case input.downcase + when "auto" ; Themes::Auto + when "light"; Themes::Light + when "dark" ; Themes::Dark + # Compatibility with old 'dark_mode' values + when "false"; Themes::Light + when "true" ; Themes::Dark + else + # Nothing, use default from initialization + end + end + + # From/To JSON + + def to_json(value : Themes, json : JSON::Builder) + json.string value.to_s + end + + def from_json(value : JSON::PullParser) : Themes? + return self.from_s(value.read_string) + end + + # From/To YAML + + def to_yaml(value : Themes, yaml : YAML::Nodes::Builder) + yaml.scalar value.to_s + end + + def from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Themes? + unless node.is_a?(YAML::Nodes::Scalar) + node.raise "Expected scalar, not #{node.class}" + end + + return self.from_s(node.value) + end + end # module theme + +end # module Settings::Converters struct VideoPreferences include JSON::Serializable @@ -305,7 +451,7 @@ def process_video_params(query, preferences) continue_autoplay ||= preferences.continue_autoplay.to_unsafe listen ||= preferences.listen.to_unsafe local ||= preferences.local.to_unsafe - player_style ||= preferences.player_style + player_style ||= preferences.player_style.to_s preferred_captions ||= preferences.captions quality ||= preferences.quality quality_dash ||= preferences.quality_dash @@ -324,7 +470,7 @@ def process_video_params(query, preferences) continue_autoplay ||= CONFIG.default_user_preferences.continue_autoplay.to_unsafe listen ||= CONFIG.default_user_preferences.listen.to_unsafe local ||= CONFIG.default_user_preferences.local.to_unsafe - player_style ||= CONFIG.default_user_preferences.player_style + player_style ||= CONFIG.default_user_preferences.player_style.to_s preferred_captions ||= CONFIG.default_user_preferences.captions quality ||= CONFIG.default_user_preferences.quality quality_dash ||= CONFIG.default_user_preferences.quality_dash diff --git a/src/invidious/users.cr b/src/invidious/users.cr index 8ea7bd4a..4f46548f 100644 --- a/src/invidious/users.cr +++ b/src/invidious/users.cr @@ -251,15 +251,12 @@ def get_subscription_feed(db, user, max_results = 40, page = 1) notifications.sort_by! { |video| video.published }.reverse! case user.preferences.sort - when "alphabetically" - notifications.sort_by! { |video| video.title } - when "alphabetically - reverse" - notifications.sort_by! { |video| video.title }.reverse! - when "channel name" - notifications.sort_by! { |video| video.author } - when "channel name - reverse" - notifications.sort_by! { |video| video.author }.reverse! - else nil # Ignore + when .alphabetically? ; notifications.sort_by! { |video| video.title } + when .alphabetically_reverse?; notifications.sort_by! { |video| video.title }.reverse! + when .channel_name? ; notifications.sort_by! { |video| video.author } + when .channel_name_reverse? ; notifications.sort_by! { |video| video.author }.reverse! + else + nil # Ignore end else if user.preferences.latest_only @@ -298,16 +295,13 @@ def get_subscription_feed(db, user, max_results = 40, page = 1) end case user.preferences.sort - when "published - reverse" - videos.sort_by! { |video| video.published } - when "alphabetically" - videos.sort_by! { |video| video.title } - when "alphabetically - reverse" - videos.sort_by! { |video| video.title }.reverse! - when "channel name" - videos.sort_by! { |video| video.author } - when "channel name - reverse" - videos.sort_by! { |video| video.author }.reverse! + when .alphabetically? ; videos.sort_by! { |video| video.title } + when .alphabetically_reverse?; videos.sort_by! { |video| video.title }.reverse! + when .channel_name? ; videos.sort_by! { |video| video.author } + when .channel_name_reverse? ; videos.sort_by! { |video| video.author }.reverse! + when .publication_date? ; videos.sort_by! { |video| video.published } + # when .publication_date_reverse?; videos.sort_by! { |video| video.published }.reverse! + # "Date reverse" wasn't here originally (why????) else nil # Ignore end diff --git a/src/invidious/views/components/feed_menu.ecr b/src/invidious/views/components/feed_menu.ecr index 3dbeaf37..55c123ec 100644 --- a/src/invidious/views/components/feed_menu.ecr +++ b/src/invidious/views/components/feed_menu.ecr @@ -1,11 +1,11 @@
<% 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| Settings::UserHomePages.names.includes? item} %> <% end %> <% feed_menu.each do |feed| %> - - <%= translate(locale, feed) %> + + <%= translate(locale, feed.to_s) %> <% end %>
diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr index d98c3bb5..f0e49ac9 100644 --- a/src/invidious/views/preferences.ecr +++ b/src/invidious/views/preferences.ecr @@ -40,7 +40,7 @@
@@ -130,8 +130,8 @@
@@ -139,8 +139,8 @@
@@ -150,17 +150,16 @@ checked<% end %>>
- <% if env.get?("user") %> - <% feed_options = {"", "Popular", "Trending", "Subscriptions", "Playlists"} %> - <% else %> - <% feed_options = {"", "Popular", "Trending"} %> - <% end %> + <% + feed_options = Settings::HomePages.names + feed_options.concat(Settings::UserHomePages.names) if env.get?("user") + %>
@@ -170,11 +169,12 @@ <% (feed_options.size - 1).times do |index| %> <% end %> + <% if env.get? "user" %>
diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index d0bdd742..45ccfb6e 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -20,10 +20,10 @@ <% locale = LOCALES[env.get("preferences").as(Preferences).locale]? %> -<% dark_mode = env.get("preferences").as(Preferences).dark_mode %> +<% theme = env.get("preferences").as(Preferences).dark_mode %> --theme"> - +-theme"> +