require "json" require "csv" require "../rename" macro string_properties(names) {% for name in names %} property {{name.id}} = "" {% end %} end macro parse_strings_from_json(names) {% for name in names %} @{{name.id}} = obj[{{name}}].as_s {% end %} end macro properties_to_hash(names) { {% for name in names %} "{{name.id}}" => @{{name.id}}.to_s, {% end %} } end module MangaDex class Chapter string_properties ["lang_code", "title", "volume", "chapter"] property manga : Manga property time = Time.local property id : String property full_title = "" property language = "" property pages = [] of {String, String} # filename, url property groups = [] of {Int32, String} # group_id, group_name def initialize(@id, json_obj : JSON::Any, @manga, lang : Hash(String, String)) self.parse_json json_obj, lang end def to_info_json JSON.build do |json| json.object do {% for name in ["id", "title", "volume", "chapter", "language", "full_title"] %} json.field {{name}}, @{{name.id}} {% end %} json.field "time", @time.to_unix.to_s json.field "manga_title", @manga.title json.field "manga_id", @manga.id json.field "groups" do json.object do @groups.each do |gid, gname| json.field gname, gid end end end end end end def parse_json(obj, lang) parse_strings_from_json ["lang_code", "title", "volume", "chapter"] language = lang[@lang_code]? @language = language if language @time = Time.unix obj["timestamp"].as_i suffixes = ["", "_2", "_3"] suffixes.each do |s| gid = obj["group_id#{s}"].as_i next if gid == 0 gname = obj["group_name#{s}"].as_s @groups << {gid, gname} end rename_rule = Rename::Rule.new \ Config.current.mangadex["chapter_rename_rule"].to_s @full_title = rename rename_rule rescue e raise "failed to parse json: #{e}" end def rename(rule : Rename::Rule) hash = properties_to_hash ["id", "title", "volume", "chapter", "lang_code", "language", "pages"] hash["groups"] = @groups.map { |g| g[1] }.join "," rule.render hash end end class Manga string_properties ["cover_url", "description", "title", "author", "artist"] property chapters = [] of Chapter property id : String def initialize(@id, json_obj : JSON::Any) self.parse_json json_obj end def to_info_json(with_chapters = true) JSON.build do |json| json.object do {% for name in ["id", "title", "description", "author", "artist", "cover_url"] %} json.field {{name}}, @{{name.id}} {% end %} if with_chapters json.field "chapters" do json.array do @chapters.each do |c| json.raw c.to_info_json end end end end end end end def parse_json(obj) parse_strings_from_json ["cover_url", "description", "title", "author", "artist"] rescue e raise "failed to parse json: #{e}" end def rename(rule : Rename::Rule) rule.render properties_to_hash ["id", "title", "author", "artist"] end end class API use_default def initialize @base_url = Config.current.mangadex["api_url"].to_s || "https://mangadex.org/api/" @lang = {} of String => String CSV.each_row {{read_file "src/assets/lang_codes.csv"}} do |row| @lang[row[1]] = row[0] end end def get(url) headers = HTTP::Headers{ "User-agent" => "Mangadex.cr", } res = HTTP::Client.get url, headers raise "Failed to get #{url}. [#{res.status_code}] " \ "#{res.status_message}" if !res.success? JSON.parse res.body end def get_manga(id) obj = self.get File.join @base_url, "manga/#{id}" if obj["status"]? != "OK" raise "Expecting `OK` in the `status` field. Got `#{obj["status"]?}`" end begin manga = Manga.new id, obj["manga"] obj["chapter"].as_h.map do |k, v| chapter = Chapter.new k, v, manga, @lang manga.chapters << chapter end manga rescue raise "Failed to parse JSON" end end def get_chapter(chapter : Chapter) obj = self.get File.join @base_url, "chapter/#{chapter.id}" if obj["status"]? == "external" raise "This chapter is hosted on an external site " \ "#{obj["external"]?}, and Mango does not support " \ "external chapters." end if obj["status"]? != "OK" raise "Expecting `OK` in the `status` field. Got `#{obj["status"]?}`" end begin server = obj["server"].as_s hash = obj["hash"].as_s chapter.pages = obj["page_array"].as_a.map do |fn| { fn.as_s, "#{server}#{hash}/#{fn.as_s}", } end rescue raise "Failed to parse JSON" end end def get_chapter(id : String) obj = self.get File.join @base_url, "chapter/#{id}" if obj["status"]? == "external" raise "This chapter is hosted on an external site " \ "#{obj["external"]?}, and Mango does not support " \ "external chapters." end if obj["status"]? != "OK" raise "Expecting `OK` in the `status` field. Got `#{obj["status"]?}`" end manga_id = "" begin manga_id = obj["manga_id"].as_i.to_s rescue raise "Failed to parse JSON" end manga = self.get_manga manga_id chapter = manga.chapters.find { |c| c.id == id }.not_nil! self.get_chapter chapter chapter end end end