game: add session support
authorNiki Roo <niki@nikiroo.be>
Fri, 28 Mar 2025 00:00:51 +0000 (01:00 +0100)
committerNiki Roo <niki@nikiroo.be>
Fri, 28 Mar 2025 00:00:51 +0000 (01:00 +0100)
gamiki.py
gamiki/game.py
gamiki/qt/utils.py
gamiki/support/support.py
gamiki/support/support_dos.py
gamiki/support/support_ext.py
gamiki/support/support_gog.py
gamiki/support/support_win.py
gamiki/support/support_win31.py

index 6c1d478b16489b0a3a99f942cf9f4c0f7647684b..d141a7759c1bf03b6dff057759a8ecec7a63d3ec 100755 (executable)
--- 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()
 
index 26132ef979cd743d162eafa9c2b7a30a869fb55e..fc4d9cb813cbb9897ec66a85c7acd16d48654580 100644 (file)
@@ -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)
 
index 001e11e2584af141d2782158825fa50d6a5c9c77..dd2407ef5391bc8031341c2a344136563b82110b 100644 (file)
@@ -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)
 
index 0bc974d100fab77bf7a33e57502e612002998b34..553ea69dc8fe73c5fd8f3be1e052b9680546483d 100644 (file)
@@ -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)
 
index e5d86f57ee13dc3ca9030c5ab08b261072239a4a..c323b93b2065b8f00bc50aa6168f3087a0cf6d86 100644 (file)
@@ -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)
 
index 483e7c6d877ccd3a3a1c50a97e3fa485927eca71..a07900d6839a1c6b7f6c788e87111faf06d2164d 100644 (file)
@@ -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)
 
index a980c63242a1b51909ada34386b64a377ec7b777..c3f1bbce5439bba7360a58c8334ff439c65d4216 100644 (file)
@@ -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)
 
index 0a41c3c0f94684facd16a4166965dd0d1f64a0e6..44b2d179d0c39519c48178578a0095caa2f939a6 100644 (file)
@@ -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)
 
index c0a9aeedbcba7620164655312bacd78617ad040f..c45dd6a04cf2110b042534437be0be82a03786bd 100644 (file)
@@ -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)