瀏覽代碼

can record multitple cd's + started working on burning multiple cd's

Noah Vogt 1 年之前
父節點
當前提交
d34525e326
共有 8 個文件被更改,包括 215 次插入66 次删除
  1. 2 2
      burn_cds_of_today.py
  2. 4 4
      choose_cd_dialog.py
  3. 17 0
      config/__init__.py
  4. 18 0
      config/constants.py
  5. 89 29
      set_cd_marker.py
  6. 1 19
      stop_cd_recording.py
  7. 2 1
      utils/__init__.py
  8. 82 11
      utils/scripts.py

+ 2 - 2
burn_cd_of_today.py → burn_cds_of_today.py

@@ -17,7 +17,7 @@ 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 utils import burn_cd_of_day, get_yyyy_mm_dd_date
+from utils import burn_cds_of_day, get_yyyy_mm_dd_date
 
 if __name__ == "__main__":
-    burn_cd_of_day(get_yyyy_mm_dd_date())
+    burn_cds_of_day(get_yyyy_mm_dd_date())

+ 4 - 4
choose_cd_dialog.py

@@ -27,7 +27,7 @@ from PyQt5.QtWidgets import (  # pylint: disable=no-name-in-module
 )
 
 from utils import (
-    burn_cd_of_day,
+    burn_cds_of_day,
     log,
 )
 from input import (
@@ -38,7 +38,7 @@ from input import (
 import config as const
 
 
-def choose_cd() -> list[str]:
+def choose_cd_day() -> list[str]:
     # pylint: disable=unused-variable
     app = QApplication([])
     try:
@@ -66,9 +66,9 @@ def choose_cd() -> list[str]:
 
 
 def choose_and_burn_cd():
-    msg, yyyy_mm_dd = choose_cd()
+    msg, yyyy_mm_dd = choose_cd_day()
     if msg == "":
-        burn_cd_of_day(yyyy_mm_dd)
+        burn_cds_of_day(yyyy_mm_dd)
     elif msg != "ignore":
         InfoMsgBox(QMessageBox.Critical, "Error", msg)
 

+ 17 - 0
config/__init__.py

@@ -1,3 +1,20 @@
+# Copyright © 2022 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 .constants import *
+
 from .default_config import *
 
 try:

+ 18 - 0
config/constants.py

@@ -0,0 +1,18 @@
+"""
+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/>.
+"""
+
+CD_RECORD_FILENAME_ZFILL = 7

+ 89 - 29
set_cd_marker.py

@@ -30,34 +30,87 @@ from utils import (
     warn,
     error_msg,
     expand_dir,
+    mark_end_of_recording,
 )
 from input import get_cachefile_content, validate_cd_record_config
 import config as const
 
 
-def start_cd_recording() -> int:
+def start_cd_recording() -> None:
+    cachefile_content = get_cachefile_content(const.CD_RECORD_CACHEFILE)
     date = get_yyyy_mm_dd_date()
-    filename = path.join(
-        const.CD_RECORD_OUTPUT_BASEDIR,
-        date,
-        "{}.wav".format(date),
-    )
+    cd_num = int(cachefile_content[5].strip())
 
-    log("starting cd recording...")
-    cmd = "ffmpeg -y {} -ar 44100 -t {} {}".format(
-        const.CD_RECORD_FFMPEG_INPUT_ARGS,
-        const.CD_RECORD_MAX_SECONDS,
-        filename,
-    )
-    process = Popen(split(cmd))
-    return process.pid
+    ensure_output_dir_exists(date)
+
+    while cachefile_content[1].strip() != "9001":
+        filename = path.join(
+            const.CD_RECORD_OUTPUT_BASEDIR,
+            date,
+            f"{date}-{cd_num:0{const.CD_RECORD_FILENAME_ZFILL}}.wav",
+        )
+
+        unix_milis = get_unix_milis()
+
+        log(f"starting cd #{cd_num} recording...")
+        cmd = "ffmpeg -y {} -ar 44100 -t {} {}".format(
+            const.CD_RECORD_FFMPEG_INPUT_ARGS,
+            const.CD_RECORD_MAX_SECONDS,
+            filename,
+        )
+        process = Popen(split(cmd))
+
+        cachefile = expand_dir(const.CD_RECORD_CACHEFILE)
+        log("updating active ffmpeg pid")
+        try:
+            with open(
+                cachefile, mode="w+", encoding="utf-8-sig"
+            ) as file_writer:
+                file_writer.write(cachefile_content[0].strip() + "\n")
+                # reset marker to 1
+                file_writer.write("1\n")
+                file_writer.write(f"{process.pid}\n")
+                file_writer.write(f"{unix_milis}\n")
+                file_writer.write(f"{unix_milis}\n")
+                file_writer.write(f"{cd_num}\n")
+        except (FileNotFoundError, PermissionError, IOError) as error:
+            error_msg(
+                "Failed to write to cachefile '{}'. Reason: {}".format(
+                    cachefile, error
+                )
+            )
+        fresh_cachefile_content = get_cachefile_content(
+            const.CD_RECORD_CACHEFILE
+        )
+        update_cue_sheet(
+            fresh_cachefile_content, date, unix_milis, initial_run=True
+        )
+
+        _ = process.communicate()[0]  # wait for subprocess to end
+        cachefile_content = get_cachefile_content(const.CD_RECORD_CACHEFILE)
+        cd_num += 1
+        if process.returncode not in [255, 0]:
+            mark_end_of_recording(cachefile_content)
+            error_msg(f"ffmpeg terminated with exit code {process.returncode}")
+
+
+def ensure_output_dir_exists(date):
+    cue_sheet_dir = path.join(expand_dir(const.CD_RECORD_OUTPUT_BASEDIR), date)
+    try:
+        if not path.exists(cue_sheet_dir):
+            mkdir(cue_sheet_dir)
+    except (FileNotFoundError, PermissionError, IOError) as error:
+        error_msg(
+            "Failed to create to cue sheet directory '{}'. Reason: {}".format(
+                cue_sheet_dir, error
+            )
+        )
 
 
 def create_cachefile_for_marker(
     cachefile_content: list,
     yyyy_mm_dd: str,
     unix_milis: int,
-    *ffmpeg_recording_pid: int,
     initial_run=False,
 ) -> None:
     cachefile = expand_dir(const.CD_RECORD_CACHEFILE)
@@ -78,15 +131,19 @@ def create_cachefile_for_marker(
     log("writing cd marker {} to cachefile...".format(marker))
     try:
         with open(cachefile, mode="w+", encoding="utf-8-sig") as file_writer:
-            file_writer.write(yyyy_mm_dd + "\n")
-            file_writer.write(str(marker) + "\n")
+            file_writer.write(f"{yyyy_mm_dd}\n")
+            file_writer.write(f"{marker}\n")
             if initial_run:
-                file_writer.write("{}\n".format(ffmpeg_recording_pid[0]))
-                file_writer.write(str(unix_milis) + "\n")
+                file_writer.write("000\n")  # fake pid, gets overriden later
+                file_writer.write(f"{unix_milis}\n")
             else:
-                file_writer.write(cachefile_content[2].strip() + "\n")
-                file_writer.write(cachefile_content[3].strip() + "\n")
-            file_writer.write(str(unix_milis) + "\n")
+                file_writer.write(f"{cachefile_content[2].strip()}\n")
+                file_writer.write(f"{cachefile_content[3].strip()}\n")
+            file_writer.write(f"{unix_milis}\n")
+            if initial_run:
+                file_writer.write("1\n")
+            else:
+                file_writer.write(f"{cachefile_content[5].strip()}\n")
     except (FileNotFoundError, PermissionError, IOError) as error:
         error_msg(
             "Failed to write to cachefile '{}'. Reason: {}".format(
@@ -101,8 +158,13 @@ def update_cue_sheet(
     cue_sheet_dir = path.join(
         expand_dir(const.CD_RECORD_OUTPUT_BASEDIR), yyyy_mm_dd
     )
-    cue_sheet_path = path.join(cue_sheet_dir, "sheet.cue")
-    wave_path = path.join(cue_sheet_dir, "{}.wav".format(yyyy_mm_dd))
+    # use current cachefile data for here cd_num only
+    fresh_cachefile_content = get_cachefile_content(const.CD_RECORD_CACHEFILE)
+    cd_num = (
+        fresh_cachefile_content[5].strip().zfill(const.CD_RECORD_FILENAME_ZFILL)
+    )
+    cue_sheet_path = path.join(cue_sheet_dir, f"sheet-{cd_num}.cue")
+    wave_path = path.join(cue_sheet_dir, f"{yyyy_mm_dd}-{cd_num}.wav")
     if initial_run:
         log("updating cue sheet...")
         try:
@@ -111,7 +173,7 @@ def update_cue_sheet(
             with open(
                 cue_sheet_path, mode="w+", encoding="utf-8-sig"
             ) as file_writer:
-                file_writer.write('FILE "{}" WAVE\n'.format(wave_path))
+                file_writer.write(f'FILE "{wave_path}" WAVE\n')
                 file_writer.write("  TRACK 01 AUDIO\n")
                 file_writer.write("    INDEX 01 00:00:00\n")
         except (FileNotFoundError, PermissionError, IOError) as error:
@@ -172,11 +234,9 @@ def set_cd_marker() -> None:
         create_cachefile_for_marker(*cachefile_and_time_data)
         update_cue_sheet(*cachefile_and_time_data)
     else:
-        pid = start_cd_recording()
-        create_cachefile_for_marker(
-            *cachefile_and_time_data, pid, initial_run=True
-        )
+        create_cachefile_for_marker(*cachefile_and_time_data, initial_run=True)
         update_cue_sheet(*cachefile_and_time_data, initial_run=True)
+        start_cd_recording()
 
 
 def main() -> None:

+ 1 - 19
stop_cd_recording.py

@@ -27,30 +27,12 @@ from utils import (
     log,
     error_msg,
     expand_dir,
+    mark_end_of_recording,
 )
 from input import get_cachefile_content, validate_cd_record_config
 import config as const
 
 
-def mark_end_of_recording(cachefile_content: list) -> None:
-    cachefile = expand_dir(const.CD_RECORD_CACHEFILE)
-
-    log("marking end of recording...")
-    try:
-        with open(cachefile, mode="w+", encoding="utf-8-sig") as file_writer:
-            file_writer.write(cachefile_content[0].strip() + "\n")
-            file_writer.write("9001\n")
-            file_writer.write(cachefile_content[2].strip() + "\n")
-            file_writer.write(cachefile_content[3].strip() + "\n")
-            file_writer.write(cachefile_content[4].strip() + "\n")
-    except (FileNotFoundError, PermissionError, IOError) as error:
-        error_msg(
-            "Failed to write to cachefile '{}'. Reason: {}".format(
-                cachefile, error
-            )
-        )
-
-
 def stop_cd_recording() -> None:
     cachefile_content = get_cachefile_content(const.CD_RECORD_CACHEFILE)
     yyyy_mm_dd = get_yyyy_mm_dd_date()

+ 2 - 1
utils/__init__.py

@@ -30,5 +30,6 @@ from .scripts import (
     make_sure_file_exists,
     switch_to_song,
     is_valid_cd_record_checkfile,
-    burn_cd_of_day,
+    burn_cds_of_day,
+    mark_end_of_recording,
 )

+ 82 - 11
utils/scripts.py

@@ -16,7 +16,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 """
 
 import sys
-from os import path
+from os import path, listdir
 from subprocess import Popen
 from shlex import split
 from time import sleep
@@ -80,13 +80,15 @@ def choose_right_cd_drive(drives: list) -> str:
     return drives[0]
 
 
-def get_burn_cmd(cd_drive: str, yyyy_mm_dd) -> str:
+def get_burn_cmd(cd_drive: str, yyyy_mm_dd, padded_zfill_num: str) -> str:
     cue_sheet_path = path.join(
-        expand_dir(const.CD_RECORD_OUTPUT_BASEDIR), yyyy_mm_dd, "sheet.cue"
+        expand_dir(const.CD_RECORD_OUTPUT_BASEDIR),
+        yyyy_mm_dd,
+        f"sheet-{padded_zfill_num}.cue",
     )
     return (
         f"cdrecord -pad dev={cd_drive} -dao -swab -text -audio "
-        + f"-cuefile={cue_sheet_path}"
+        + f"-cuefile='{cue_sheet_path}'"
     )
 
 
@@ -128,31 +130,54 @@ def create_cachfile_for_song(song) -> None:
         )
 
 
+def mark_end_of_recording(cachefile_content: list) -> None:
+    cachefile = expand_dir(const.CD_RECORD_CACHEFILE)
+
+    log("marking end of recording...")
+    try:
+        with open(cachefile, mode="w+", encoding="utf-8-sig") as file_writer:
+            file_writer.write(cachefile_content[0].strip() + "\n")
+            file_writer.write("9001\n")
+            file_writer.write(cachefile_content[2].strip() + "\n")
+            file_writer.write(cachefile_content[3].strip() + "\n")
+            file_writer.write(cachefile_content[4].strip() + "\n")
+            file_writer.write(cachefile_content[5].strip() + "\n")
+    except (FileNotFoundError, PermissionError, IOError) as error:
+        error_msg(
+            "Failed to write to cachefile '{}'. Reason: {}".format(
+                cachefile, error
+            )
+        )
+
+
 def is_valid_cd_record_checkfile(
     cachefile_content: list, yyyy_mm_dd: str
 ) -> bool:
     return (
-        len(cachefile_content) == 5
+        len(cachefile_content) == 6
         # YYYY-MM-DD
         and bool(match(r"[0-9]{4}-[0-9]{2}-[0-9]{2}$", cachefile_content[0]))
-        # last marker
-        and bool(match(r"^[0-9]+$", cachefile_content[1]))
+        # last set marker
+        and bool(match(r"^[0-9][0-9]?$", cachefile_content[1]))
         # pid of ffmpeg recording instance
         and bool(match(r"^[0-9]+$", cachefile_content[2]))
         # unix milis @ recording start
         and bool(match(r"^[0-9]+$", cachefile_content[3]))
         # unix milis @ last track
         and bool(match(r"^[0-9]+$", cachefile_content[4]))
+        # cd number
+        and bool(match(r"^[0-9]+$", cachefile_content[5]))
         # date matches today
         and cachefile_content[0].strip() == yyyy_mm_dd
     )
 
 
 class CDBurnerGUI:
-    def __init__(self, cd_drive: str, yyyy_mm_dd: str):
+    def __init__(self, cd_drive: str, yyyy_mm_dd: str, cd_num: str):
         self.app = QApplication([])
         self.drive = cd_drive
         self.yyyy_mm_dd = yyyy_mm_dd
+        self.cd_num = cd_num
         self.exit_code = 1
         self.show_burning_msg_box()
         self.start_burn_subprocess()
@@ -176,7 +201,9 @@ class CDBurnerGUI:
         self.message_box.show()
 
     def start_burn_subprocess(self):
-        process = Popen(split(get_burn_cmd(self.drive, self.yyyy_mm_dd)))
+        process = Popen(
+            split(get_burn_cmd(self.drive, self.yyyy_mm_dd, self.cd_num))
+        )
 
         while process.poll() is None:
             QApplication.processEvents()
@@ -187,10 +214,52 @@ class CDBurnerGUI:
         self.exit_code = process.returncode
 
 
-def burn_cd_of_day(yyyy_mm_dd: str) -> None:
+def burn_cds_of_day(yyyy_mm_dd: str) -> None:
     validate_cd_record_config()
     make_sure_file_exists(const.CD_RECORD_CACHEFILE)
 
+    try:
+        target_dir = path.join(
+            expand_dir(const.CD_RECORD_OUTPUT_BASEDIR), yyyy_mm_dd
+        )
+        if not path.isfile(target_dir):
+            exit_as_no_cds_found(target_dir)
+
+        target_files = sorted(listdir(target_dir))
+        cue_sheets = []
+        for file in target_files:
+            if file.endswith(".cue") and len(file) == 17:
+                cue_sheets.append(file)
+
+        if not target_files:
+            exit_as_no_cds_found(target_dir)
+
+        if len(cue_sheets) == 1:
+            burn_and_eject_cd(yyyy_mm_dd, "0000001")
+        else:
+            for num, file in enumerate(cue_sheets):
+                log(f"file found: {file}")
+
+    except (FileNotFoundError, PermissionError, IOError):
+        InfoMsgBox(
+            QMessageBox.Critical,
+            "Error",
+            "Error: Could not access directory: "
+            + f"'{const.CD_RECORD_OUTPUT_BASEDIR}'",
+        )
+        sys.exit(1)
+
+
+def exit_as_no_cds_found(target_dir):
+    InfoMsgBox(
+        QMessageBox.Critical,
+        "Error",
+        f"Error: Did not find any CD's in: {target_dir}.",
+    )
+    sys.exit(1)
+
+
+def burn_and_eject_cd(yyyy_mm_dd: str, padded_cd_num: str) -> None:
     cd_drives = get_cd_drives()
     if not cd_drives:
         InfoMsgBox(
@@ -201,7 +270,9 @@ def burn_cd_of_day(yyyy_mm_dd: str) -> None:
         sys.exit(1)
     drive = choose_right_cd_drive(cd_drives)
 
-    burn_success = CDBurnerGUI(drive, yyyy_mm_dd).burning_successful()
+    burn_success = CDBurnerGUI(
+        drive, yyyy_mm_dd, padded_cd_num
+    ).burning_successful()
     if burn_success:
         InfoMsgBox(QMessageBox.Info, "Info", "Successfully burned CD.")
     else: