Implement some sort of basic rss scraping
This commit is contained in:
79
lib/db.dart
Normal file
79
lib/db.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'dart:io' show Platform, Directory;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
const settingsDir = '.gamer-updater';
|
||||
const dbFileName = 'data.db';
|
||||
|
||||
class DB {
|
||||
static late Database db;
|
||||
|
||||
// SQL schema definition
|
||||
static const String _schema = '''
|
||||
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);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_games_name_unique ON games (name);
|
||||
''';
|
||||
|
||||
static Future<String> _getDatabasePath() async {
|
||||
print('Attempting to get database path...');
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
// Get user's home directory
|
||||
final home =
|
||||
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
||||
if (home == null) {
|
||||
throw Exception('Could not find home directory');
|
||||
}
|
||||
print('Home directory found: home');
|
||||
|
||||
final dbDir = Directory(path.join(home, settingsDir));
|
||||
if (!await dbDir.exists()) {
|
||||
await dbDir.create(recursive: true);
|
||||
print('$settingsDir directory created');
|
||||
} else {
|
||||
print('$settingsDir directory already exists');
|
||||
}
|
||||
|
||||
return path.join(dbDir.path, dbFileName);
|
||||
} else {
|
||||
// Default path for other platforms
|
||||
final databasesPath = await databaseFactoryFfi.getDatabasesPath();
|
||||
print('Using default databases path: databasesPath');
|
||||
return path.join(databasesPath, dbFileName);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> init() async {
|
||||
print('Starting database initialization...');
|
||||
sqfliteFfiInit();
|
||||
|
||||
final dbPath = await _getDatabasePath();
|
||||
print('Database path: dbPath');
|
||||
|
||||
try {
|
||||
db = await databaseFactoryFfi.openDatabase(
|
||||
dbPath,
|
||||
options: OpenDatabaseOptions(
|
||||
version: 1,
|
||||
onCreate: (db, version) async {
|
||||
print('Creating database schema...');
|
||||
await db.execute(_schema);
|
||||
print('Database schema created successfully');
|
||||
},
|
||||
),
|
||||
);
|
||||
print('Database opened and initialized');
|
||||
} catch (e) {
|
||||
print('Failed to initialize database: e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
88
lib/game.dart
Normal file
88
lib/game.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
import 'package:gamer_updater/db.dart';
|
||||
import 'package:gamer_updater/utils.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:dart_rss/dart_rss.dart';
|
||||
|
||||
class Game {
|
||||
final String name;
|
||||
final String versionRegex;
|
||||
final RegExp _internalVersionRegex;
|
||||
final String lastPlayed;
|
||||
String actualVersion;
|
||||
String lastUpdated;
|
||||
final String rssFeedUrl;
|
||||
|
||||
Game({
|
||||
required this.name,
|
||||
required this.versionRegex,
|
||||
required this.lastPlayed,
|
||||
this.actualVersion = '',
|
||||
required this.rssFeedUrl,
|
||||
this.lastUpdated = '',
|
||||
}) : _internalVersionRegex = RegExp(versionRegex);
|
||||
|
||||
Future<void> updateActualVersion() async {
|
||||
final response = await http.get(Uri.parse(rssFeedUrl));
|
||||
final document = RssFeed.parse(response.body);
|
||||
final pages = document.items;
|
||||
pages.sort((a, b) {
|
||||
var lhs = parseRfc822Date(a.pubDate!);
|
||||
var rhs = parseRfc822Date(b.pubDate!);
|
||||
return rhs.compareTo(lhs);
|
||||
});
|
||||
final versions =
|
||||
pages
|
||||
.map((e) => _internalVersionRegex.firstMatch(e.title!)?.group(1))
|
||||
.toList();
|
||||
|
||||
for (int i = 0; i < versions.length; i++) {
|
||||
final version = versions[i];
|
||||
final page = pages[i];
|
||||
if (version != null) {
|
||||
actualVersion = version;
|
||||
lastUpdated = parseRfc822Date(page.pubDate!).toIso8601String();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GameRepository {
|
||||
static Future<Game> upsert(Game game) async {
|
||||
final db = await 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;
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
@@ -1,6 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gamer_updater/db.dart';
|
||||
import 'package:gamer_updater/game.dart';
|
||||
|
||||
void main() async {
|
||||
await DB.init();
|
||||
|
||||
var game = Game(
|
||||
name: 'Rimworld',
|
||||
versionRegex: r'(\d+\.\d+\.\d+)',
|
||||
lastPlayed: '1.4.3704',
|
||||
rssFeedUrl:
|
||||
'https://store.steampowered.com/feeds/news/app/294100/?cc=HR&l=english',
|
||||
);
|
||||
await game.updateActualVersion();
|
||||
await GameRepository.upsert(game);
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
@@ -14,6 +28,19 @@ class MyApp extends StatelessWidget {
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
useMaterial3: true,
|
||||
scaffoldBackgroundColor: Colors.black,
|
||||
colorSchemeSeed: Colors.black,
|
||||
highlightColor: Colors.deepPurple,
|
||||
textTheme: TextTheme(
|
||||
bodyLarge: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
||||
bodyMedium: TextStyle(fontSize: 20),
|
||||
bodySmall: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
themeMode: ThemeMode.system,
|
||||
home: const MyHomePage(title: 'Gamer Updater'),
|
||||
);
|
||||
}
|
||||
@@ -28,14 +55,6 @@ class MyHomePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
int _counter = 0;
|
||||
|
||||
void _incrementCounter() {
|
||||
setState(() {
|
||||
_counter++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -46,20 +65,9 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text('You have pushed the button this many times:'),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
children: <Widget>[const Text('Hello cyka')],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _incrementCounter,
|
||||
tooltip: 'Increment',
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
47
lib/utils.dart
Normal file
47
lib/utils.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
DateTime parseRfc822Date(String date) {
|
||||
// Split the date string into components
|
||||
final parts = date.split(' ');
|
||||
|
||||
// Map the month names to their corresponding numbers
|
||||
const monthMap = {
|
||||
'Jan': 1,
|
||||
'Feb': 2,
|
||||
'Mar': 3,
|
||||
'Apr': 4,
|
||||
'May': 5,
|
||||
'Jun': 6,
|
||||
'Jul': 7,
|
||||
'Aug': 8,
|
||||
'Sep': 9,
|
||||
'Oct': 10,
|
||||
'Nov': 11,
|
||||
'Dec': 12,
|
||||
};
|
||||
|
||||
// Extract the components
|
||||
final day = int.parse(parts[1]);
|
||||
final month = monthMap[parts[2]]!;
|
||||
final year = int.parse(parts[3]);
|
||||
final timeParts = parts[4].split(':');
|
||||
final hour = int.parse(timeParts[0]);
|
||||
final minute = int.parse(timeParts[1]);
|
||||
final second = int.parse(timeParts[2]);
|
||||
|
||||
// Handle the timezone offset
|
||||
final timezone = parts[5];
|
||||
final isNegative = timezone.startsWith('-');
|
||||
final tzHours = int.parse(timezone.substring(1, 3));
|
||||
final tzMinutes = int.parse(timezone.substring(3, 5));
|
||||
|
||||
// Create the DateTime object
|
||||
DateTime dateTime = DateTime(year, month, day, hour, minute, second);
|
||||
|
||||
// Adjust for timezone
|
||||
if (isNegative) {
|
||||
dateTime = dateTime.subtract(Duration(hours: tzHours, minutes: tzMinutes));
|
||||
} else {
|
||||
dateTime = dateTime.add(Duration(hours: tzHours, minutes: tzMinutes));
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
}
|
104
pubspec.lock
104
pubspec.lock
@@ -49,6 +49,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
dart_rss:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dart_rss
|
||||
sha256: "73539d4b7153b47beef8b51763ca55dcb6fc0bb412b29e0f5e74e93fabfd1ac6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -57,6 +65,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -75,6 +91,30 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -139,6 +179,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -152,6 +200,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.5"
|
||||
sqflite_common_ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite_common_ffi
|
||||
sha256: "1f3ef3888d3bfbb47785cc1dda0dc7dd7ebd8c1955d32a9e8e9dae1e38d1c4c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.5"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: "32b632dda27d664f85520093ed6f735ae5c49b5b75345afb8b19411bc59bb53d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.4"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -176,6 +248,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -192,6 +272,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -208,6 +296,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
sdks:
|
||||
dart: ">=3.7.0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
|
@@ -34,6 +34,9 @@ dependencies:
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.8
|
||||
sqflite_common_ffi: ^2.3.5
|
||||
http: ^1.3.0
|
||||
dart_rss: ^3.0.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user