From 22b35c453ede48e36db1657c5b8e879f3cc70a56 Mon Sep 17 00:00:00 2001
From: syeopite <syeopite@syeopite.dev>
Date: Thu, 25 Jul 2024 20:12:17 -0700
Subject: [PATCH 1/4] Ameba: Fix Style/WhileTrue

---
 src/invidious/routes/video_playback.cr | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr
index ec18f3b8..24693662 100644
--- a/src/invidious/routes/video_playback.cr
+++ b/src/invidious/routes/video_playback.cr
@@ -131,7 +131,7 @@ module Invidious::Routes::VideoPlayback
       end
 
       # TODO: Record bytes written so we can restart after a chunk fails
-      while true
+      loop do
         if !range_end && content_length
           range_end = content_length
         end

From f66068976e5f077d363769055b7533cd0f85d6d0 Mon Sep 17 00:00:00 2001
From: syeopite <syeopite@syeopite.dev>
Date: Fri, 26 Jul 2024 19:19:31 -0700
Subject: [PATCH 2/4] Ameba: Fix Naming/PredicateName

---
 src/invidious/helpers/serialized_yt_data.cr |  4 ++--
 src/invidious/jsonify/api_v1/video_json.cr  |  2 +-
 src/invidious/user/imports.cr               |  4 ++--
 src/invidious/videos.cr                     | 20 ++++++++++++++++++--
 src/invidious/views/watch.ecr               |  2 +-
 5 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/src/invidious/helpers/serialized_yt_data.cr b/src/invidious/helpers/serialized_yt_data.cr
index 31a3cf44..463d5557 100644
--- a/src/invidious/helpers/serialized_yt_data.cr
+++ b/src/invidious/helpers/serialized_yt_data.cr
@@ -90,7 +90,7 @@ struct SearchVideo
       json.field "lengthSeconds", self.length_seconds
       json.field "liveNow", self.live_now
       json.field "premium", self.premium
-      json.field "isUpcoming", self.is_upcoming
+      json.field "isUpcoming", self.upcoming?
 
       if self.premiere_timestamp
         json.field "premiereTimestamp", self.premiere_timestamp.try &.to_unix
@@ -109,7 +109,7 @@ struct SearchVideo
     to_json(nil, json)
   end
 
-  def is_upcoming
+  def upcoming?
     premiere_timestamp ? true : false
   end
 end
diff --git a/src/invidious/jsonify/api_v1/video_json.cr b/src/invidious/jsonify/api_v1/video_json.cr
index 59714828..2d41ed3b 100644
--- a/src/invidious/jsonify/api_v1/video_json.cr
+++ b/src/invidious/jsonify/api_v1/video_json.cr
@@ -63,7 +63,7 @@ module Invidious::JSONify::APIv1
       json.field "isListed", video.is_listed
       json.field "liveNow", video.live_now
       json.field "isPostLiveDvr", video.post_live_dvr
-      json.field "isUpcoming", video.is_upcoming
+      json.field "isUpcoming", video.upcoming?
 
       if video.premiere_timestamp
         json.field "premiereTimestamp", video.premiere_timestamp.try &.to_unix
diff --git a/src/invidious/user/imports.cr b/src/invidious/user/imports.cr
index a70434ca..2b5f88f4 100644
--- a/src/invidious/user/imports.cr
+++ b/src/invidious/user/imports.cr
@@ -161,7 +161,7 @@ struct Invidious::User
     #  Youtube
     # -------------------
 
-    private def is_opml?(mimetype : String, extension : String)
+    private def opml?(mimetype : String, extension : String)
       opml_mimetypes = [
         "application/xml",
         "text/xml",
@@ -179,7 +179,7 @@ struct Invidious::User
     def from_youtube(user : User, body : String, filename : String, type : String) : Bool
       extension = filename.split(".").last
 
-      if is_opml?(type, extension)
+      if opml?(type, extension)
         subscriptions = XML.parse(body)
         user.subscriptions += subscriptions.xpath_nodes(%q(//outline[@type="rss"])).map do |channel|
           channel["xmlUrl"].match!(/UC[a-zA-Z0-9_-]{22}/)[0]
diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr
index 6d0cf9ba..65b07fe8 100644
--- a/src/invidious/videos.cr
+++ b/src/invidious/videos.cr
@@ -280,7 +280,7 @@ struct Video
     info["genreUcid"].try &.as_s? ? "/channel/#{info["genreUcid"]}" : nil
   end
 
-  def is_vr : Bool?
+  def vr? : Bool?
     return {"EQUIRECTANGULAR", "MESH"}.includes? self.projection_type
   end
 
@@ -361,6 +361,21 @@ struct Video
     {% if flag?(:debug_macros) %} {{debug}} {% end %}
   end
 
+  # Macro to generate ? and = accessor methods for attributes in `info`
+  private macro predicate_bool(method_name, name)
+    # Return {{name.stringify}} from `info`
+    def {{method_name.id.underscore}}? : Bool
+      return info[{{name.stringify}}]?.try &.as_bool || false
+    end
+
+    # Update {{name.stringify}} into `info`
+    def {{method_name.id.underscore}}=(value : Bool)
+      info[{{name.stringify}}] = JSON::Any.new(value)
+    end
+
+    {% if flag?(:debug_macros) %} {{debug}} {% end %}
+  end
+
   # Method definitions, using the macros above
 
   getset_string author
@@ -382,11 +397,12 @@ struct Video
   getset_i64 likes
   getset_i64 views
 
+  # TODO: Make predicate_bool the default as to adhere to Crystal conventions
   getset_bool allowRatings
   getset_bool authorVerified
   getset_bool isFamilyFriendly
   getset_bool isListed
-  getset_bool isUpcoming
+  predicate_bool upcoming, isUpcoming
 end
 
 def get_video(id, refresh = true, region = nil, force_refresh = false)
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr
index 36679bce..45c58a16 100644
--- a/src/invidious/views/watch.ecr
+++ b/src/invidious/views/watch.ecr
@@ -62,7 +62,7 @@ we're going to need to do it here in order to allow for translations.
     "params" => params,
     "preferences" => preferences,
     "premiere_timestamp" => video.premiere_timestamp.try &.to_unix,
-    "vr" => video.is_vr,
+    "vr" => video.vr?,
     "projection_type" => video.projection_type,
     "local_disabled" => CONFIG.disabled?("local"),
     "support_reddit" => true

From d1cd7903882b23eedae6ff28441c1adc40b5be7b Mon Sep 17 00:00:00 2001
From: syeopite <syeopite@syeopite.dev>
Date: Fri, 26 Jul 2024 19:20:06 -0700
Subject: [PATCH 3/4] Ameba: Fix Lint/RedundantStringCoercion

---
 src/invidious/jsonify/api_v1/video_json.cr | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/invidious/jsonify/api_v1/video_json.cr b/src/invidious/jsonify/api_v1/video_json.cr
index 2d41ed3b..3625b8f1 100644
--- a/src/invidious/jsonify/api_v1/video_json.cr
+++ b/src/invidious/jsonify/api_v1/video_json.cr
@@ -109,7 +109,7 @@ module Invidious::JSONify::APIv1
               # On livestreams, it's not present, so always fall back to the
               # current unix timestamp (up to mS precision) for compatibility.
               last_modified = fmt["lastModified"]?
-              last_modified ||= "#{Time.utc.to_unix_ms.to_s}000"
+              last_modified ||= "#{Time.utc.to_unix_ms}000"
               json.field "lmt", last_modified
 
               json.field "projectionType", fmt["projectionType"]

From ecbea0b67b7b478597e40b530c0df8cd212e4faf Mon Sep 17 00:00:00 2001
From: syeopite <syeopite@syeopite.dev>
Date: Fri, 26 Jul 2024 19:22:42 -0700
Subject: [PATCH 4/4] Ameba: Fix Lint/ShadowingOuterLocalVar

---
 src/invidious/routes/api/v1/videos.cr | 4 ++--
 src/invidious/user/imports.cr         | 2 +-
 src/invidious/videos/transcript.cr    | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/invidious/routes/api/v1/videos.cr b/src/invidious/routes/api/v1/videos.cr
index 42282f44..c49a9b7b 100644
--- a/src/invidious/routes/api/v1/videos.cr
+++ b/src/invidious/routes/api/v1/videos.cr
@@ -116,7 +116,7 @@ module Invidious::Routes::API::V1::Videos
         else
           caption_xml = XML.parse(caption_xml)
 
-          webvtt = WebVTT.build(settings_field) do |webvtt|
+          webvtt = WebVTT.build(settings_field) do |builder|
             caption_nodes = caption_xml.xpath_nodes("//transcript/text")
             caption_nodes.each_with_index do |node, i|
               start_time = node["start"].to_f.seconds
@@ -136,7 +136,7 @@ module Invidious::Routes::API::V1::Videos
                 text = "<v #{md["name"]}>#{md["text"]}</v>"
               end
 
-              webvtt.cue(start_time, end_time, text)
+              builder.cue(start_time, end_time, text)
             end
           end
         end
diff --git a/src/invidious/user/imports.cr b/src/invidious/user/imports.cr
index 2b5f88f4..533c18d9 100644
--- a/src/invidious/user/imports.cr
+++ b/src/invidious/user/imports.cr
@@ -115,7 +115,7 @@ struct Invidious::User
         playlists.each do |item|
           title = item["title"]?.try &.as_s?.try &.delete("<>")
           description = item["description"]?.try &.as_s?.try &.delete("\r")
-          privacy = item["privacy"]?.try &.as_s?.try { |privacy| PlaylistPrivacy.parse? privacy }
+          privacy = item["privacy"]?.try &.as_s?.try { |raw_pl_privacy_state| PlaylistPrivacy.parse? raw_pl_privacy_state }
 
           next if !title
           next if !description
diff --git a/src/invidious/videos/transcript.cr b/src/invidious/videos/transcript.cr
index 9cd064c5..4bd9f820 100644
--- a/src/invidious/videos/transcript.cr
+++ b/src/invidious/videos/transcript.cr
@@ -110,13 +110,13 @@ module Invidious::Videos
         "Language" => @language_code,
       }
 
-      vtt = WebVTT.build(settings_field) do |vtt|
+      vtt = WebVTT.build(settings_field) do |builder|
         @lines.each do |line|
           # Section headers are excluded from the VTT conversion as to
           # match the regular captions returned from YouTube as much as possible
           next if line.is_a? HeadingLine
 
-          vtt.cue(line.start_ms, line.end_ms, line.line)
+          builder.cue(line.start_ms, line.end_ms, line.line)
         end
       end