From 3d23034f0a5952ee24769c0da7d6b18ece278a63 Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Thu, 26 Feb 2026 22:57:25 -0600 Subject: [PATCH] Created Models for Itch/Steam Game App Data --- lib/models/itch_io_app_data.gd | 87 ++++++++++ lib/models/itch_io_app_data.gd.uid | 1 + lib/models/steam_app_data.gd | 248 +++++++++++++++++++++++++++++ lib/models/steam_app_data.gd.uid | 1 + 4 files changed, 337 insertions(+) create mode 100644 lib/models/itch_io_app_data.gd create mode 100644 lib/models/itch_io_app_data.gd.uid create mode 100644 lib/models/steam_app_data.gd create mode 100644 lib/models/steam_app_data.gd.uid diff --git a/lib/models/itch_io_app_data.gd b/lib/models/itch_io_app_data.gd new file mode 100644 index 00000000..0b8af92c --- /dev/null +++ b/lib/models/itch_io_app_data.gd @@ -0,0 +1,87 @@ +@tool +extends SQLiteObject +class_name ItchIOAppData + +@export var id: int +@export var title: String +@export var authors: Array[Dictionary] = [] +@export var tags: Array[String] = [] +@export var links: Dictionary = {} +@export var cover_image: String = "" +@export var price: String = "" +@export var description: String = "" +@export var screenshots_thumbnails: Array[String] = [] +@export var screenshots_full: Array[String] = [] +@export var added: float + +var is_free: bool: + get(): return currency_to_float(price) == 0.0 + +var url: String: + get(): + return links.get("self", "") + +var comments_url: String: + get(): + return links.get("comments", "") + +static func _setup() -> void: + set_table_name(ItchIOAppData, "itch_apps") + set_column_flags(ItchIOAppData, "id", Flags.PRIMARY_KEY|Flags.NOT_NULL) + ignore_column(ItchIOAppData, "is_free") + ignore_column(ItchIOAppData, "url") + ignore_column(ItchIOAppData, "comments_url") + + +func to_json() -> Dictionary: + var data := {} + data["id"] = id + data["title"] = title + data["authors"] = authors.duplicate() + data["tags"] = tags.duplicate() + data["links"] = links.duplicate() + data["cover_image"] = cover_image + data["price"] = price + data["description"] = description + data["screenshots_thumbnails"] = [] + for _url: String in screenshots_thumbnails: + data["screenshots_thumbnails"].append(_url) + data["screenshots_full"] = [] + for _url: String in screenshots_full: + data["screenshots_full"].append(_url) + return data + + +static func from_json(data: Dictionary) -> ItchIOAppData: + var app := ItchIOAppData.new() + app.id = data.get("id", 0) + app.title = data.get("title", "") + app.cover_image = data.get("cover_image", "") + app.price = data.get("price", "") + # Authors + for author in data.get("authors", []): + if typeof(author) == TYPE_DICTIONARY: + app.authors.append(author) + # Tags + for tag in data.get("tags", []): + app.tags.append(str(tag)) + # Links + app.links = data.get("links", {}).duplicate() + + app.description = data.get("description", "") + for _url: String in data.get("screenshots_thumbnails", []): + app.screenshots_thumbnails.append(str(_url)) + for _url: String in data.get("screenshots_full", []): + app.screenshots_full.append(str(_url)) + return app + + +static func currency_to_float(currency_str: String) -> float: + var regex := RegEx.new() + regex.compile(r"[\d\.]+") + + var currency_match := regex.search(currency_str) + if currency_match: + return currency_match.get_string().to_float() + else: + return 0.0 diff --git a/lib/models/itch_io_app_data.gd.uid b/lib/models/itch_io_app_data.gd.uid new file mode 100644 index 00000000..3ef20b55 --- /dev/null +++ b/lib/models/itch_io_app_data.gd.uid @@ -0,0 +1 @@ +uid://dve2trcxkua0d diff --git a/lib/models/steam_app_data.gd b/lib/models/steam_app_data.gd new file mode 100644 index 00000000..ac3b8467 --- /dev/null +++ b/lib/models/steam_app_data.gd @@ -0,0 +1,248 @@ +@tool +extends SQLiteObject +class_name SteamAppData + +@export var steam_app_id: int +@export var type: String +@export var name: String +@export var required_age: int +@export var is_free: bool +@export var short_description: String +@export var detailed_description: String +var detailed_description_bbcode: String: + get(): return Util.convert_html_to_bbcode(detailed_description) +@export var header_image: String +@export var capsule_image: String +@export var capsule_imagev5: String +@export var website: String +@export var developers: Array[String] = [] +@export var publishers: Array[String] = [] +@export var platforms: Dictionary +@export var genres: Array[Dictionary] = [] +@export var release_date: Dictionary + +@export var screenshots_full: Array[String] = [] +@export var screenshots_thumbs: Array[String] = [] + +@export var trailer_mp4: Dictionary = {} +@export var trailer_webm: Dictionary = {} +@export var trailer_thumbnail: String + +@export var price_currency: String +@export var price_initial: int +@export var price_final: int +@export var price_discount_percent: int +@export var price_initial_formatted: String +@export var price_final_formatted: String + +@export var categories: Array[Dictionary] = [] +@export var dlc: Array[int] = [] +@export var packages: Array[int] = [] + +@export var total_achievements: int = 0 +@export var highlighted_achievements: Array[Dictionary] = [] + +@export var pc_requirements: String = "" +@export var mac_requirements: String = "" +@export var linux_requirements: String = "" + +@export var support_email: String = "" +@export var support_url: String = "" + +@export var background_image: String = "" +@export var background_image_raw: String = "" + +var short_url: String: + get(): return "https://store.steampowered.com/app/%d" % steam_app_id + +var seo_url: String: + get(): + if name.is_empty(): + return short_url + var slug = name.strip_edges().replace(" ","_") + return "https://store.steampowered.com/app/%d/%s" % [steam_app_id, slug] + +var protocol_url: String: + get(): return "steam://store/%d" % steam_app_id + +var s_team_url: String: + get(): return "https://s.team/a/%d" % steam_app_id + +var utm_url: String: + get(): + return "%s?utm_source=default&utm_medium=link&utm_campaign=default" % short_url + +static func _setup() -> void: + set_table_name(SteamAppData, "steam_apps") + set_column_flags(SteamAppData, "steam_app_id", Flags.PRIMARY_KEY|Flags.NOT_NULL) + ignore_column(SteamAppData, "detailed_description_bbcode") + ignore_column(SteamAppData, "short_url") + ignore_column(SteamAppData, "seo_url") + ignore_column(SteamAppData, "protocol_url") + ignore_column(SteamAppData, "s_team_url") + ignore_column(SteamAppData, "utm_url") + +func to_json() -> Dictionary: + var data := {} + data["type"] = type + data["name"] = name + data["steam_app_id"] = steam_app_id + data["required_age"] = required_age + data["is_free"] = is_free + data["short_description"] = short_description + data["detailed_description"] = detailed_description + data["header_image"] = header_image + data["capsule_image"] = capsule_image + data["capsule_imagev5"] = capsule_imagev5 + data["website"] = website + data["platforms"] = platforms + data["release_date"] = release_date + data["developers"] = developers + data["publishers"] = publishers + data["genres"] = genres + data["categories"] = categories + data["dlc"] = dlc + data["packages"] = packages + # Price overview + data["price_overview"] = { + "currency": price_currency, + "initial": price_initial, + "final": price_final, + "discount_percent": price_discount_percent, + "initial_formatted": price_initial_formatted, + "final_formatted": price_final_formatted + } + # Screenshots + var screenshots := [] + for i in screenshots_full.size(): + screenshots.append({ + "path_full": screenshots_full[i], + "path_thumbnail": screenshots_thumbs[i] if i < screenshots_thumbs.size() else "" + }) + data["screenshots"] = screenshots + # Trailer + if trailer_mp4.size() > 0 or trailer_webm.size() > 0 or not trailer_thumbnail.is_empty(): + data["movies"] = [{ + "mp4": trailer_mp4, + "webm": trailer_webm, + "thumbnail": trailer_thumbnail + }] + # Achievements + data["achievements"] = { + "total": total_achievements, + "highlighted": highlighted_achievements.duplicate() + } + # Requirements + data["pc_requirements"] = { "minimum": pc_requirements } + data["mac_requirements"] = mac_requirements + data["linux_requirements"] = linux_requirements + # Support info + data["support_info"] = { + "email": support_email, + "url": support_url + } + # Background + data["background"] = background_image + data["background_raw"] = background_image_raw + return data + + +func save_as_file(filepath: String) -> void: + var file = FileAccess.open(filepath, FileAccess.WRITE) + if file: + var json_text = JSON.stringify(to_json(), "\t") # Pretty print with tabs + file.store_string(json_text) + file.close() + else: + push_error("Failed to open file for writing: %s" % filepath) + + +static func from_json(data: Dictionary) -> SteamAppData: + var app = SteamAppData.new() + app.type = data.get("type", "") + app.name = data.get("name", "") + app.steam_app_id = data.get("steam_app_id", 0) + app.required_age = data.get("required_age", 0) + app.is_free = data.get("is_free", false) + app.short_description = data.get("short_description", "") + app.detailed_description = data.get("detailed_description", "") + app.header_image = data.get("header_image", "") + app.capsule_image = data.get("capsule_image", "") + app.capsule_imagev5 = data.get("capsule_imagev5", "") + app.website = data.get("website", "") if data.get("website", "") else "" + app.platforms = data.get("platforms", {}) + app.release_date = data.get("release_date", {}) + for d in data.get("developers", []): + app.developers.append(str(d)) + for p in data.get("publishers", []): + app.publishers.append(str(p)) + for g in data.get("genres", []): + if typeof(g) == TYPE_DICTIONARY: + app.genres.append(g) + # Screenshots: full + thumbnails + for ss in data.get("screenshots", []): + app.screenshots_full.append(str(ss.get("path_full", ""))) + app.screenshots_thumbs.append(str(ss.get("path_thumbnail", ""))) + # Trailer (first movie only) + var movies = data.get("movies", []) + if movies.size() > 0: + var trailer = movies[0] + app.trailer_mp4 = trailer.get("mp4", {}) + app.trailer_webm = trailer.get("webm", {}) + app.trailer_thumbnail = trailer.get("thumbnail", "") + # Pricing + var price = data.get("price_overview", {}) + app.price_currency = price.get("currency", "") + app.price_initial = price.get("initial", 0) + app.price_final = price.get("final", 0) + app.price_discount_percent = price.get("discount_percent", 0) + app.price_initial_formatted = price.get("initial_formatted", "") + app.price_final_formatted = price.get("final_formatted", "") + # DLC and Packages + for d in data.get("dlc", []): + app.dlc.append(int(d)) + for p in data.get("packages", []): + app.packages.append(int(p)) + # Categories + for cat in data.get("categories", []): + if typeof(cat) == TYPE_DICTIONARY: + app.categories.append(cat) + # Achievements + var ach = data.get("achievements", {}) + app.total_achievements = int(ach.get("total", 0)) + for h in ach.get("highlighted", []): + app.highlighted_achievements.append(h) + # System Requirements + app.pc_requirements = data.get("pc_requirements", {}).get("minimum", "") + app.mac_requirements = str(data.get("mac_requirements", "")) + app.linux_requirements = str(data.get("linux_requirements", "")) + # Support Info + var support = data.get("support_info", {}) + app.support_email = support.get("email", "") + app.support_url = support.get("url", "") + # Background Images + app.background_image = data.get("background", "") + app.background_image_raw = data.get("background_raw", "") + return app + + +static func from_file(filepath: String) -> SteamAppData: + if not FileAccess.file_exists(filepath): + push_error("File does not exist: %s" % filepath) + return null + + var file = FileAccess.open(filepath, FileAccess.READ) + if not file: + push_error("Failed to open file for reading: %s" % filepath) + return null + + var content = file.get_as_text() + file.close() + + var result = JSON.parse_string(content) + if typeof(result) != TYPE_DICTIONARY: + push_error("Failed to parse JSON or invalid format in: %s" % filepath) + return null + + return SteamAppData.from_json(result) + diff --git a/lib/models/steam_app_data.gd.uid b/lib/models/steam_app_data.gd.uid new file mode 100644 index 00000000..877c6d35 --- /dev/null +++ b/lib/models/steam_app_data.gd.uid @@ -0,0 +1 @@ +uid://bllsv2cy6komw