Kaynağa Gözat

show warning when trying to stop recording twice instead of error + add cd recording status display

Noah Vogt 11 ay önce
ebeveyn
işleme
11c5713778

+ 4 - 0
README.md

@@ -380,6 +380,10 @@ These are some issues and possible changes that will be addressed or at least co
 - add not-yet-public streaming workflow scripts
 - for sermon segment generating: Check if file duration and type roughly match the target to avoid useless regenerating. Also, parallelization.
 - make multiplatform ejecting of cd drives possible
+- fix cd recording edge cases when:
+    - a new day starts during the recording
+    - changing timezone
+    - changing time
 
 ## Licensing
 

+ 21 - 0
cd_recording_status_webserver.py

@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+
+# Copyright © 2024 Noah Vogt <noah@noahvogt.com>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from recording import cd_recording_status_webserver
+
+if __name__ == "__main__":
+    cd_recording_status_webserver.run()

+ 3 - 1
input/parse_file.py

@@ -95,7 +95,7 @@ def parse_songtext(slidegen) -> None:
     slidegen.songtext = output_dict
 
 
-def get_cachefile_content(cachefile: str) -> list:
+def get_cachefile_content(cachefile: str, suppress_error=False) -> list:
     expanded_path = expand_dir(cachefile)
     try:
         with open(
@@ -103,6 +103,8 @@ def get_cachefile_content(cachefile: str) -> list:
         ) as cachefile_reader:
             cachefile_content = cachefile_reader.readlines()
     except (FileNotFoundError, PermissionError, IOError) as error:
+        if suppress_error:
+            return ["0", "0", "0", "0", "0", "0"]
         app = QApplication
         InfoMsgBox(
             QMessageBox.Critical,

+ 3 - 0
recording/__init__.py

@@ -20,6 +20,9 @@ from .sermon import (
 from .verify import (
     is_valid_cd_record_checkfile,
     make_sure_there_is_no_ongoing_cd_recording,
+    ongoing_cd_recording_detected,
+    calc_cuesheet_timestamp,
 )
 from .cd import mark_end_of_recording, burn_cds_of_day
 from .gui import choose_sermon_day, choose_cd_day
+from .status_display import cd_recording_status_webserver

+ 122 - 0
recording/status_display.py

@@ -0,0 +1,122 @@
+# Copyright © 2024 Noah Vogt <noah@noahvogt.com>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from flask import Flask, render_template_string
+
+import config as const
+from input import get_cachefile_content
+from utils import get_unix_milis
+
+from .verify import ongoing_cd_recording_detected, calc_cuesheet_timestamp
+
+cd_recording_status_webserver = Flask(__name__)
+
+
+def get_cd_marker_count(active_recording: bool, cachefile_content: list) -> int:
+    if not active_recording:
+        return 0
+
+    return int(cachefile_content[1].strip())
+
+
+def get_cd_count(active_recording: bool, cachefile_content: list) -> int:
+    if not active_recording:
+        return 0
+
+    return int(cachefile_content[5].strip())
+
+
+def get_full_rec_time(active_recording: bool, cachefile_content: list) -> str:
+    if not active_recording:
+        return "00:00"
+
+    return calc_cuesheet_timestamp(
+        int(cachefile_content[3].strip()), get_unix_milis()
+    )[:5]
+
+
+def get_track_rec_time(active_recording: bool, cachefile_content: list) -> str:
+    if not active_recording:
+        return "00:00"
+
+    return calc_cuesheet_timestamp(
+        int(cachefile_content[4].strip()), get_unix_milis()
+    )[:5]
+
+
+@cd_recording_status_webserver.route("/")
+def index():
+    recording_active = ongoing_cd_recording_detected()
+    cachefile_content = get_cachefile_content(
+        const.CD_RECORD_CACHEFILE, suppress_error=True
+    )
+    cd_marker_count = get_cd_marker_count(recording_active, cachefile_content)
+    cd_count = get_cd_count(recording_active, cachefile_content)
+    cd_rec_time = get_full_rec_time(recording_active, cachefile_content)
+    track_rec_time = get_track_rec_time(recording_active, cachefile_content)
+
+    background_color = "green" if recording_active else "grey"
+
+    html = f"""
+    <!DOCTYPE html>
+    <html lang="en">
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <title>CD Recording Status</title>
+        <style>
+            body {{
+                background-color: {background_color};
+                color: white;
+                text-align: center;
+                font-family: Arial, sans-serif;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                height: 100vh;
+                margin: 0;
+                overflow: hidden;
+            }}
+            .content {{
+                font-size: 4vw; /* Responsive font size */
+                text-align: center;
+                max-width: 90vw; /* Prevents overflow on very large text */
+            }}
+            p {{
+                margin: 0.5em 0;
+            }}
+            .timestamp {{
+                font-family: monospace;
+            }}
+        </style>
+        <script>
+            setTimeout(function() {{
+                window.location.reload(1);
+            }}, 300);
+        </script>
+    </head>
+    <body>
+        <div class="content">
+            <h1>CD Recording Status</h1>
+            <p>Recording is currently {'active' if recording_active else 'not active'}.</p>
+            <p>{f"CD Count: {cd_count}" if recording_active else '&ZeroWidthSpace;'}</p>
+            <p>{f"CD Marker Count: {cd_marker_count}" if recording_active else '&ZeroWidthSpace;'}</p>
+            <p>{f"Recording Time: <span class=timestamp>{cd_rec_time}</span> (Track: <span class=timestamp>{track_rec_time})</span>" if recording_active else '&ZeroWidthSpace;'}</p>
+        </div>
+    </body>
+    </html>
+    """
+
+    return render_template_string(html)

+ 30 - 10
recording/verify.py

@@ -21,11 +21,23 @@ from PyQt5.QtWidgets import (  # pylint: disable=no-name-in-module
     QMessageBox,
 )
 
+from psutil import pid_exists
+
 import config as const
-from utils import get_yyyy_mm_dd_date, InfoMsgBox
+from utils import get_yyyy_mm_dd_date, InfoMsgBox, expand_dir
 from input import get_cachefile_content
 
 
+def calc_cuesheet_timestamp(start_milis: int, current_milis: int) -> str:
+    milis_diff = current_milis - start_milis
+    mins = milis_diff // 60000
+    milis_diff -= 60000 * mins
+    secs = int(milis_diff / 1000)
+    milis_diff -= 1000 * secs
+    frames = int(75 / 1000 * milis_diff)
+    return "{:02d}:{:02d}:{:02d}\n".format(mins, secs, frames)
+
+
 def is_valid_cd_record_checkfile(
     cachefile_content: list, yyyy_mm_dd: str
 ) -> bool:
@@ -48,19 +60,27 @@ def is_valid_cd_record_checkfile(
     )
 
 
-def make_sure_there_is_no_ongoing_cd_recording() -> None:
-    if path.isfile(const.CD_RECORD_CACHEFILE):
+def ongoing_cd_recording_detected() -> bool:
+    if path.isfile(expand_dir(const.CD_RECORD_CACHEFILE)):
         cachefile_content = get_cachefile_content(const.CD_RECORD_CACHEFILE)
         if is_valid_cd_record_checkfile(
             cachefile_content, get_yyyy_mm_dd_date()
         ):
-            if cachefile_content[1].strip() != "9001":
-                InfoMsgBox(
-                    QMessageBox.Critical,
-                    "Error",
-                    "Error: Ongoing CD Recording detected",
-                )
-                sys.exit(1)
+            if cachefile_content[1].strip() != "9001" and pid_exists(
+                int(cachefile_content[2].strip())
+            ):
+                return True
+    return False
+
+
+def make_sure_there_is_no_ongoing_cd_recording() -> None:
+    if ongoing_cd_recording_detected():
+        InfoMsgBox(
+            QMessageBox.Critical,
+            "Error",
+            "Error: Ongoing CD Recording detected",
+        )
+        sys.exit(1)
 
 
 def is_legal_sheet_filename(filename: str) -> bool:

+ 2 - 0
requirements-unix.txt

@@ -5,3 +5,5 @@ pyautogui
 PyQt5
 pycdio
 soundfile
+Flask
+psutil

+ 2 - 0
requirements-win32.txt

@@ -5,3 +5,5 @@ pyautogui
 PyQt5
 wmi
 soundfile
+Flask
+psutil

+ 12 - 13
set_cd_marker.py

@@ -38,7 +38,12 @@ from utils import (
 )
 from input import get_cachefile_content, validate_cd_record_config
 import config as const
-from recording import is_valid_cd_record_checkfile, mark_end_of_recording
+from recording import (
+    is_valid_cd_record_checkfile,
+    mark_end_of_recording,
+    ongoing_cd_recording_detected,
+    calc_cuesheet_timestamp,
+)
 
 
 def get_reset_marker(yyyy_mm_dd: str) -> int:
@@ -254,12 +259,7 @@ def update_cue_sheet(
             )
             return
 
-        milis_diff = unix_milis - start_milis
-        mins = milis_diff // 60000
-        milis_diff -= 60000 * mins
-        secs = int(milis_diff / 1000)
-        milis_diff -= 1000 * secs
-        frames = int(75 / 1000 * milis_diff)
+        timestamp = calc_cuesheet_timestamp(start_milis, unix_milis)
 
         log("updating cue sheet...")
         try:
@@ -267,11 +267,7 @@ def update_cue_sheet(
                 cue_sheet_path, mode="a", encoding="utf-8"
             ) as file_writer:
                 file_writer.write("  TRACK {:02d} AUDIO\n".format(marker))
-                file_writer.write(
-                    "    INDEX 01 {:02d}:{:02d}:{:02d}\n".format(
-                        mins, secs, frames
-                    )
-                )
+                file_writer.write(f"    INDEX 01 {timestamp}\n")
         except (FileNotFoundError, PermissionError, IOError) as error:
             app = QApplication
             InfoMsgBox(
@@ -291,7 +287,10 @@ def set_cd_marker() -> None:
     unix_milis = get_unix_milis()
     cachefile_and_time_data = (cachefile_content, yyyy_mm_dd, unix_milis)
 
-    if is_valid_cd_record_checkfile(*cachefile_and_time_data[:-1]):
+    if (
+        is_valid_cd_record_checkfile(*cachefile_and_time_data[:-1])
+        and ongoing_cd_recording_detected()
+    ):
         create_cachefile_for_marker(*cachefile_and_time_data)
         update_cue_sheet(*cachefile_and_time_data)
     else:

+ 10 - 0
stop_cd_recording.py

@@ -68,6 +68,16 @@ def stop_cd_recording() -> None:
             sys.exit(1)
         mark_end_of_recording(cachefile_content)
     else:
+        if cachefile_content[1].strip() == "9001":
+            app = QApplication
+            InfoMsgBox(
+                QMessageBox.Warning,
+                "Warning",
+                "CD Recording stopped already.",
+            )
+            del app
+            sys.exit(0)
+
         app = QApplication
         InfoMsgBox(
             QMessageBox.Critical,