diff --git a/src/invidious/config.cr b/src/invidious/config.cr index cd8b839c..7c189abc 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -53,7 +53,7 @@ class Config # Number of threads to use for crawling videos from channels (for updating subscriptions) property channel_threads : Int32 = 1 # Time interval between two executions of the job that crawls channel videos (subscriptions update). - @[YAML::Field(converter: Preferences::TimeSpanConverter)] + @[YAML::Field(converter: IV::Config::TimeSpanConverter)] property channel_refresh_interval : Time::Span = 30.minutes # Number of threads to use for updating feeds property feed_threads : Int32 = 1 @@ -65,7 +65,7 @@ class Config property db : IV::Config::DBConfig? = nil # Database configuration using 12-Factor "Database URL" syntax - @[YAML::Field(converter: Preferences::URIConverter)] + @[YAML::Field(converter: IV::Config::URIConverter)] property database_url : URI = URI.parse("") # Use polling to keep decryption function up to date property decrypt_polling : Bool = false @@ -111,8 +111,9 @@ class Config property modified_source_code_url : String? = nil # Connect to YouTube over 'ipv6', 'ipv4'. Will sometimes resolve fix issues with rate-limiting (see https://github.com/ytdl-org/youtube-dl/issues/21729) - @[YAML::Field(converter: Preferences::FamilyConverter)] + @[YAML::Field(converter: IV::Config::FamilyConverter)] property force_resolve : Socket::Family = Socket::Family::UNSPEC + # Port to listen for connections (overridden by command line argument) property port : Int32 = 3000 # Host to bind (overridden by command line argument) @@ -124,7 +125,7 @@ class Config property use_innertube_for_captions : Bool = false # Saved cookies in "name1=value1; name2=value2..." format - @[YAML::Field(converter: Preferences::StringToCookies)] + @[YAML::Field(converter: IV::Config::CookiesConverter)] property cookies : HTTP::Cookies = HTTP::Cookies.new # Playlist length limit diff --git a/src/invidious/config/converters.cr b/src/invidious/config/converters.cr new file mode 100644 index 00000000..62a04fc2 --- /dev/null +++ b/src/invidious/config/converters.cr @@ -0,0 +1,74 @@ +module Invidious::Config + module CookiesConverter + def self.to_yaml(value : HTTP::Cookies, yaml : YAML::Nodes::Builder) + (value.map { |c| "#{c.name}=#{c.value}" }).join("; ").to_yaml(yaml) + end + + def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : HTTP::Cookies + unless node.is_a?(YAML::Nodes::Scalar) + node.raise "Expected scalar, not #{node.class}" + end + + cookies = HTTP::Cookies.new + node.value.split(";").each do |cookie| + next if cookie.strip.empty? + name, value = cookie.split("=", 2) + cookies << HTTP::Cookie.new(name.strip, value.strip) + end + + return cookies + end + end + + module FamilyConverter + def self.to_yaml(value : Socket::Family, yaml : YAML::Nodes::Builder) + case value + when Socket::Family::UNSPEC then yaml.scalar nil + when Socket::Family::INET then yaml.scalar "ipv4" + when Socket::Family::INET6 then yaml.scalar "ipv6" + when Socket::Family::UNIX then raise "Invalid socket family #{value}" + end + end + + def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Socket::Family + if node.is_a?(YAML::Nodes::Scalar) + case node.value.downcase + when "ipv4" then Socket::Family::INET + when "ipv6" then Socket::Family::INET6 + else + Socket::Family::UNSPEC + end + else + node.raise "Expected scalar, not #{node.class}" + end + end + end + + module URIConverter + def self.to_yaml(value : URI, yaml : YAML::Nodes::Builder) + yaml.scalar value.normalize! + end + + def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : URI + if node.is_a?(YAML::Nodes::Scalar) + URI.parse node.value + else + node.raise "Expected scalar, not #{node.class}" + end + end + end + + module TimeSpanConverter + def self.to_yaml(value : Time::Span, yaml : YAML::Nodes::Builder) + return yaml.scalar value.total_minutes.to_i32 + end + + def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Time::Span + if node.is_a?(YAML::Nodes::Scalar) + return decode_interval(node.value) + else + node.raise "Expected scalar, not #{node.class}" + end + end + end +end diff --git a/src/invidious/user/preferences.cr b/src/invidious/user/preferences.cr index b3059403..c8c71f8a 100644 --- a/src/invidious/user/preferences.cr +++ b/src/invidious/user/preferences.cr @@ -1,6 +1,5 @@ struct Preferences include JSON::Serializable - include YAML::Serializable property annotations : Bool = CONFIG.default_user_preferences.annotations property annotations_subscribed : Bool = CONFIG.default_user_preferences.annotations_subscribed @@ -8,17 +7,14 @@ struct Preferences property automatic_instance_redirect : Bool = CONFIG.default_user_preferences.automatic_instance_redirect @[JSON::Field(converter: Preferences::StringToArray)] - @[YAML::Field(converter: Preferences::StringToArray)] property captions : Array(String) = CONFIG.default_user_preferences.captions @[JSON::Field(converter: Preferences::StringToArray)] - @[YAML::Field(converter: Preferences::StringToArray)] property comments : Array(String) = CONFIG.default_user_preferences.comments property continue : Bool = CONFIG.default_user_preferences.continue property continue_autoplay : Bool = CONFIG.default_user_preferences.continue_autoplay @[JSON::Field(converter: Preferences::BoolToString)] - @[YAML::Field(converter: Preferences::BoolToString)] property dark_mode : String = CONFIG.default_user_preferences.dark_mode property latest_only : Bool = CONFIG.default_user_preferences.latest_only property listen : Bool = CONFIG.default_user_preferences.listen @@ -78,27 +74,6 @@ struct Preferences 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 end module ClampInt @@ -109,58 +84,6 @@ struct Preferences def self.from_json(value : JSON::PullParser) : Int32 value.read_int.clamp(0, MAX_ITEMS_PER_PAGE).to_i32 end - - def self.to_yaml(value : Int32, yaml : YAML::Nodes::Builder) - yaml.scalar value - end - - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Int32 - node.value.clamp(0, MAX_ITEMS_PER_PAGE) - end - end - - module FamilyConverter - def self.to_yaml(value : Socket::Family, yaml : YAML::Nodes::Builder) - case value - when Socket::Family::UNSPEC - yaml.scalar nil - when Socket::Family::INET - yaml.scalar "ipv4" - when Socket::Family::INET6 - yaml.scalar "ipv6" - when Socket::Family::UNIX - raise "Invalid socket family #{value}" - end - end - - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Socket::Family - if node.is_a?(YAML::Nodes::Scalar) - case node.value.downcase - when "ipv4" - Socket::Family::INET - when "ipv6" - Socket::Family::INET6 - else - Socket::Family::UNSPEC - end - else - node.raise "Expected scalar, not #{node.class}" - end - end - end - - module URIConverter - def self.to_yaml(value : URI, yaml : YAML::Nodes::Builder) - yaml.scalar value.normalize! - end - - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : URI - if node.is_a?(YAML::Nodes::Scalar) - URI.parse node.value - else - node.raise "Expected scalar, not #{node.class}" - end - end end module ProcessString @@ -171,14 +94,6 @@ struct Preferences def self.from_json(value : JSON::PullParser) : String HTML.escape(value.read_string[0, 100]) 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 - HTML.escape(node.value[0, 100]) - end end module StringToArray @@ -202,73 +117,5 @@ struct Preferences result end - - def self.to_yaml(value : Array(String), yaml : YAML::Nodes::Builder) - yaml.sequence do - value.each do |element| - yaml.scalar element - end - end - end - - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Array(String) - begin - unless node.is_a?(YAML::Nodes::Sequence) - node.raise "Expected sequence, not #{node.class}" - end - - result = [] of String - node.nodes.each do |item| - unless item.is_a?(YAML::Nodes::Scalar) - node.raise "Expected scalar, not #{item.class}" - end - - result << HTML.escape(item.value[0, 100]) - end - rescue ex - if node.is_a?(YAML::Nodes::Scalar) - result = [HTML.escape(node.value[0, 100]), ""] - else - result = ["", ""] - end - end - - result - end - end - - module StringToCookies - def self.to_yaml(value : HTTP::Cookies, yaml : YAML::Nodes::Builder) - (value.map { |c| "#{c.name}=#{c.value}" }).join("; ").to_yaml(yaml) - end - - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : HTTP::Cookies - unless node.is_a?(YAML::Nodes::Scalar) - node.raise "Expected scalar, not #{node.class}" - end - - cookies = HTTP::Cookies.new - node.value.split(";").each do |cookie| - next if cookie.strip.empty? - name, value = cookie.split("=", 2) - cookies << HTTP::Cookie.new(name.strip, value.strip) - end - - cookies - end - end - - module TimeSpanConverter - def self.to_yaml(value : Time::Span, yaml : YAML::Nodes::Builder) - return yaml.scalar value.total_minutes.to_i32 - end - - def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Time::Span - if node.is_a?(YAML::Nodes::Scalar) - return decode_interval(node.value) - else - node.raise "Expected scalar, not #{node.class}" - end - end end end