From: Niki Roo Date: Fri, 28 Mar 2025 00:00:51 +0000 (+0100) Subject: game: add session support X-Git-Url: http://git.nikiroo.be/?a=commitdiff_plain;h=cc62a7f1ae5cdf60c53ff68bd833af5648df1ec8;p=gamiki.git game: add session support --- diff --git a/gamiki.py b/gamiki.py index 6c1d478..d141a77 100755 --- a/gamiki.py +++ b/gamiki.py @@ -123,5 +123,5 @@ elif (args.info_icon): if ("Icon" in game): print(game["Icon"]) else: - game.start() + game.run() diff --git a/gamiki/game.py b/gamiki/game.py index 26132ef..fc4d9cb 100644 --- a/gamiki/game.py +++ b/gamiki/game.py @@ -1,19 +1,25 @@ -from typing import Optional, Any -from pathlib import PurePath, Path +from sys import stderr +from typing import Any +from pathlib import PurePath, Path +from time import time +from datetime import datetime class Game(dict): """Game object generated from a directory.""" - dir : PurePath = None - name : str = "" - code : str = None - src : str = "" - tags : list = None - desc : str = "" - icon : Path = None # "main" icon - icons : dict = None # all found icons - support : Any = None # should be gamiki.Support - library : Any = None # should be gamiki.Library + dir : PurePath = None + name : str = "" + code : str = None + src : str = "" + tags : list = None + desc : str = "" + total_time : int = 0 + total_sessions : int = 0 + sessions : list = None # list of (YYYY-MM-DD, HH:MM, time in sec) + icon : Path = None # "main" icon + icons : dict = None # all found icons + support : Any = None # should be gamiki.Support + library : Any = None # should be gamiki.Library def __init__(self, library, dir: PurePath): self.dir = dir @@ -22,7 +28,7 @@ class Game(dict): self.tags = [] self.library = library - self._read_info(self.dir.joinpath("gameinfo.txt")) + self._read_info() self._init() self._init_icons() @@ -42,7 +48,8 @@ class Game(dict): icon = self._read_icon("-" + key) if (icon): self.icons[key] = icon - def _read_info(self, file: PurePath): + def _read_info(self): + file = self.dir.joinpath("gameinfo.txt") try: with open(file, encoding="utf-8") as data: for ln in data: @@ -56,6 +63,21 @@ class Game(dict): self[key] = val except FileNotFoundError: pass + + file = self.dir.joinpath("sessions.total") + try: + with open(file, encoding="utf-8") as data: + ln = data.readline() # number of sessions + sessions = int(ln.split(":", maxsplit=1)[1].strip()) + ln = data.readline() # total time in human formated time + ln = data.readline() # total time in seconds + total = int(ln.split(":", maxsplit=1)[1].strip()) + self.total_time = total + self.total_sessions = sessions + except FileNotFoundError: + pass + except Exception as e: + print(f"Error when querying {file.as_posix()}: {e}", file=stderr) def _read_icon(self, suffix: str = "") -> PurePath: """ @@ -88,10 +110,84 @@ class Game(dict): def get_icon(self, key: str) -> PurePath: return self.icons.get(key, self.icon) + + def _read_sessions(self) -> list: + self.sessions = [] + file = self.dir.joinpath("sessions.txt") + if (file.exists()): + try: + with open(file, "r", encoding="utf-8") as data: + for ln in data: + start_d = "" + start_h = "" + total = 0 + tab = ln.strip().split(" ") + if (len(tab) >= 4): + start_d = tab[2] + start_h = tab[3] + tab = ln.strip().split(":") + if (len(tab) == 5): + total += int(tab[2]) * 3600 + total += int(tab[3]) * 60 + total += int(tab[4]) + if (start_d and start_h): + self.sessions.append((start_d, start_h, total)) + except Exception as e: + print(f"Error when querying {file.as_posix()}: {e}",file=stderr) + + def add_session(self, when: datetime, elapsed: int): + def human(seconds: int) -> str: + ss = (seconds % 60) + mm = (seconds // 60) % 60 + hh = (seconds // 3600) + return f"{hh:02}:{mm:02}:{ss:02}" + + start_d = when.strftime('%Y-%m-%d') + start_h = when.strftime('%H:%M') + text = f"Session of {start_d} ({start_h}) lasted: {human(elapsed)}" + print(f"[{self.name}]: {text}") + + if (not self.sessions): + self._read_sessions() + + self.sessions.append((start_d, start_h, elapsed)) + + total = 0 + for session in self.sessions: + total += session[2] + print( + f"[{self.name}]: Total time: " + f"{human(total)} (in {len(self.sessions)} session(s))" + ) + + try: + file = self.dir.joinpath("sessions.txt") + with open(file, "a", encoding="utf-8") as data: + data.write(text) + data.write("\n") + except Exception as e: + print("Cannot save session time:", e, file=stderr) + total = None + + try: + if (total): + file = self.dir.joinpath("sessions.total") + with open(file, "w", encoding="utf-8") as data: + data.write(f"Sessions: {len(self.sessions)}\n") + data.write(f"Total time: {human(total)}\n") + data.write(f"...in seconds: {total}\n") + except: + print("Cannot save total time", file=stderr) - def start(self, params: list = None): - if (self.support == None): + def run(self, params: list = None): + if (not self.support): raise RuntimeError("Unsupported game was called: " + game.name) - self.support.start(self, params) + begin = time() + today = datetime.now() + + self.support._start(self, params) + + elapsed = int(time() - begin) + self.add_session(today, elapsed) diff --git a/gamiki/qt/utils.py b/gamiki/qt/utils.py index 001e11e..dd2407e 100644 --- a/gamiki/qt/utils.py +++ b/gamiki/qt/utils.py @@ -10,9 +10,9 @@ except: from PyQt4.QtGui import * pool = QThreadPool() -def start(startable): +def start(runnable): class Worker(QRunnable): - def run(self): startable.start() + def run(self): runnable.run() worker = Worker() pool.start(worker) diff --git a/gamiki/support/support.py b/gamiki/support/support.py index 0bc974d..553ea69 100644 --- a/gamiki/support/support.py +++ b/gamiki/support/support.py @@ -10,13 +10,13 @@ class Support: def supports(self, game: Game) -> bool: return False - def start(self, game: Game, params: list = None): + def _start(self, game: Game, params: list = None): if (not self.supports(game)): raise RuntimeError("Unsupported game was called: " + game.name) - def running(self, game: Game): + def _running(self, game: Game): print("Running", game.name) - def error(self, game: Game, rc: int): + def _error(self, game: Game, rc: int): print("\nError when running", game.name + ":", "RC", rc) diff --git a/gamiki/support/support_dos.py b/gamiki/support/support_dos.py index e5d86f5..c323b93 100644 --- a/gamiki/support/support_dos.py +++ b/gamiki/support/support_dos.py @@ -11,9 +11,9 @@ class SupportDos(Support): and not game.dir.joinpath("C", "WINDOWS").exists() ) - def start(self, game: Game, params: list = None): - self.running(game) + def _start(self, game: Game, params: list = None): + self._running(game) rep = dosbox(game.dir.resolve(), game.dir.resolve()) if (rep != 0): - self.error(game, rep) + self._error(game, rep) diff --git a/gamiki/support/support_ext.py b/gamiki/support/support_ext.py index 483e7c6..a07900d 100644 --- a/gamiki/support/support_ext.py +++ b/gamiki/support/support_ext.py @@ -8,11 +8,11 @@ class SupportExt(Support): def supports(self, game: Game): return game.dir.joinpath("start.sh").exists() - def start(self, game: Game, params: list = None): + def _start(self, game: Game, params: list = None): start = game.dir.resolve().joinpath("start.sh") - self.running(game) + self._running(game) rep = start_sh(start) if (rep != 0): - self.error(game, rep) + self._error(game, rep) diff --git a/gamiki/support/support_gog.py b/gamiki/support/support_gog.py index a980c63..c3f1bbc 100644 --- a/gamiki/support/support_gog.py +++ b/gamiki/support/support_gog.py @@ -8,15 +8,15 @@ class SupportGog(Support): def supports(self, game: Game): return game.dir.joinpath("gameinfo").exists() - def start(self, game: Game, params: list = None): + def _start(self, game: Game, params: list = None): start = game.dir.resolve().joinpath("start.sh") if (not start.exists()): raise RuntimeError("GoG game without a start.sh script") # No running, GoG launcher already does it: - #self.running(game) + #self._running(game) rep = start_sh(start) if (rep != 0): - self.error(game, rep) + self._error(game, rep) diff --git a/gamiki/support/support_win.py b/gamiki/support/support_win.py index 0a41c3c..44b2d17 100644 --- a/gamiki/support/support_win.py +++ b/gamiki/support/support_win.py @@ -8,14 +8,14 @@ class SupportWin(Support): def supports(self, game: Game): return game.dir.joinpath("wine.bat").exists() - def start(self, game: Game, params: list = None): + def _start(self, game: Game, params: list = None): dir = game.dir.resolve() if (not dir.joinpath("wine.prefix").exists()): raise RuntimeError("Windows game without a wine.prefix") - self.running(game) + self._running(game) rep = wine(dir) if (rep != 0): - self.error(game, rep) + self._error(game, rep) diff --git a/gamiki/support/support_win31.py b/gamiki/support/support_win31.py index c0a9aee..c45dd6a 100644 --- a/gamiki/support/support_win31.py +++ b/gamiki/support/support_win31.py @@ -11,9 +11,9 @@ class SupportWin31(Support): and game.dir.joinpath("C", "WINDOWS").exists() ) - def start(self, game: Game, params: list = None): - self.running(game) + def _start(self, game: Game, params: list = None): + self._running(game) rep = dosbox(game.dir.resolve(), game.library.dir.resolve()) if (rep != 0): - self.error(game, rep) + self._error(game, rep)