import 'package:gamer_updater/db.dart'; import 'package:gamer_updater/utils.dart'; import 'package:http/http.dart' as http; import 'dart:typed_data'; class Game { final String name; final String versionRegex; final RegExp _internalVersionRegex; final String lastPlayed; String actualVersion; String lastUpdated; final String rssFeedUrl; final Uint8List? imageData; Game({ required this.name, required this.versionRegex, required this.lastPlayed, this.actualVersion = '', required this.rssFeedUrl, this.lastUpdated = '', this.imageData, }) : _internalVersionRegex = RegExp(versionRegex); factory Game.fromMap(Map map) { return Game( name: map['name'], versionRegex: map['version_regex'], lastPlayed: map['last_played'], rssFeedUrl: map['rss_feed_url'], actualVersion: map['actual_version'], lastUpdated: map['last_updated'], imageData: map['image_data'], ); } Future updateActualVersion() async { if (rssFeedUrl.isEmpty) { throw Exception('No rss feed url for $name'); } final response = await http.get(Uri.parse(rssFeedUrl)); if (response.statusCode != 200) { throw Exception( 'Failed to update actual version for $name, rss responded with ${response.statusCode}', ); } final body = response.body; final match = _internalVersionRegex.firstMatch(body); if (match == null || match.groupCount == 0) { throw Exception('No version found for $name'); } final version = match.group(1); if (version == null) { throw Exception('No version found for $name'); } actualVersion = version; try { // Some sites use weird ass dogshit fucking formats // We cannot really reliably parse every single one of them // So - fuck it lastUpdated = parseRfc822Date( response.headers['last-modified'] ?? '', ).toIso8601String(); } catch (e) { lastUpdated = DateTime.now().toIso8601String(); } } } class GameRepository { static Future upsert(Game game) async { final db = DB.db; await db.rawInsert( ''' INSERT INTO games (name, actual_version, last_played, rss_feed_url, version_regex, last_updated) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(name) DO UPDATE SET actual_version = excluded.actual_version, last_played = excluded.last_played, rss_feed_url = excluded.rss_feed_url, version_regex = excluded.version_regex, last_updated = excluded.last_updated ''', [ game.name, game.actualVersion, game.lastPlayed, game.rssFeedUrl, game.versionRegex, game.lastUpdated, ], ); return game; } static Future updateImage(Game game) async { final db = DB.db; await db.rawUpdate('UPDATE games SET image_data = ? WHERE name = ?', [ game.imageData, game.name, ]); return game; } static Future> getAll() async { final db = DB.db; final games = await db.rawQuery( 'SELECT name, actual_version, last_played, rss_feed_url, version_regex, last_updated, image_data FROM games ORDER BY name', ); return games .map((e) => Game.fromMap(e)) .fold>({}, (map, game) => {...map, game.name: game}); } static Future delete(Game game) async { final db = DB.db; await db.rawDelete('DELETE FROM games WHERE name = ?', [game.name]); } } //CREATE TABLE IF NOT EXISTS games ( // id INTEGER PRIMARY KEY AUTOINCREMENT, // name TEXT NOT NULL, // actual_version TEXT NOT NULL, // last_played TEXT NOT NULL, // rss_feed_url TEXT NOT NULL, // version_regex TEXT NOT NULL, // last_updated TEXT NOT NULL //); //CREATE INDEX IF NOT EXISTS idx_games_name ON games (name);