|
@@ -43,8 +43,8 @@ from PyQt5.QtCore import ( # pylint: disable=no-name-in-module
|
|
QTimer,
|
|
QTimer,
|
|
)
|
|
)
|
|
|
|
|
|
-from audio import SermonSegment
|
|
|
|
-from utils import CustomException, get_wave_duration_in_secs, log
|
|
|
|
|
|
+from audio import AudioSourceFileType, ChosenAudio, get_wave_duration_in_secs
|
|
|
|
+from utils import CustomException, log
|
|
import config as const
|
|
import config as const
|
|
|
|
|
|
|
|
|
|
@@ -56,7 +56,8 @@ class LabelConstruct:
|
|
|
|
|
|
@dataclass
|
|
@dataclass
|
|
class CheckBoxConstruct:
|
|
class CheckBoxConstruct:
|
|
- rel_path: str
|
|
|
|
|
|
+ rel_sheet_path: str
|
|
|
|
+ abs_wave_path: str
|
|
check_box: QCheckBox
|
|
check_box: QCheckBox
|
|
|
|
|
|
|
|
|
|
@@ -128,9 +129,15 @@ class RadioButtonDialog(QDialog): # pylint: disable=too-few-public-methods
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
-class SheetAndPreviewChooser(QDialog): # pylint: disable=too-few-public-methods
|
|
|
|
|
|
+class WaveAndSheetPreviewChooserGUI(
|
|
|
|
+ QDialog
|
|
|
|
+): # pylint: disable=too-few-public-methods
|
|
def __init__(
|
|
def __init__(
|
|
- self, base_dir: str, options: list[str], window_title: str
|
|
|
|
|
|
+ self,
|
|
|
|
+ base_dir: str,
|
|
|
|
+ options: list[str],
|
|
|
|
+ window_title: str,
|
|
|
|
+ source_type: AudioSourceFileType,
|
|
) -> None:
|
|
) -> None:
|
|
super().__init__()
|
|
super().__init__()
|
|
self.base_dir = base_dir
|
|
self.base_dir = base_dir
|
|
@@ -144,213 +151,27 @@ class SheetAndPreviewChooser(QDialog): # pylint: disable=too-few-public-methods
|
|
|
|
|
|
self.player = QMediaPlayer()
|
|
self.player = QMediaPlayer()
|
|
|
|
|
|
- self.chosen_sheets = []
|
|
|
|
|
|
+ self.chosen_audios = []
|
|
self.position_labels = []
|
|
self.position_labels = []
|
|
for num, item in enumerate(options):
|
|
for num, item in enumerate(options):
|
|
- rel_wave_path = self.get_wav_relative_path_from_cue_sheet(item)
|
|
|
|
-
|
|
|
|
- check_box = QCheckBox(item)
|
|
|
|
- if num == 0:
|
|
|
|
|
|
+ if source_type is AudioSourceFileType.WAVE:
|
|
|
|
+ rel_wave_path = item
|
|
|
|
+ else:
|
|
|
|
+ rel_wave_path = self.get_wav_relative_path_from_cue_sheet(item)
|
|
|
|
+ abs_wave_path = path.join(base_dir, rel_wave_path)
|
|
|
|
+
|
|
|
|
+ if source_type is AudioSourceFileType.WAVE:
|
|
|
|
+ check_box = QCheckBox(
|
|
|
|
+ f"Segment {(num+1):0{const.CD_RECORD_FILENAME_ZFILL}}"
|
|
|
|
+ )
|
|
|
|
+ else:
|
|
|
|
+ check_box = QCheckBox(item)
|
|
|
|
+ if num == 0 and source_type is AudioSourceFileType.CUESHEET:
|
|
check_box.setChecked(True)
|
|
check_box.setChecked(True)
|
|
button_layout = QHBoxLayout()
|
|
button_layout = QHBoxLayout()
|
|
- check_box_construct = CheckBoxConstruct(item, check_box)
|
|
|
|
- self.check_buttons.append(check_box_construct)
|
|
|
|
- button_layout.addWidget(check_box)
|
|
|
|
-
|
|
|
|
- play_button = self.get_player_button("SP_MediaPlay")
|
|
|
|
- play_button.setToolTip("Play CD Preview")
|
|
|
|
- play_button.clicked.connect(
|
|
|
|
- lambda _, x=rel_wave_path: self.play_audio(x)
|
|
|
|
|
|
+ check_box_construct = CheckBoxConstruct(
|
|
|
|
+ item, abs_wave_path, check_box
|
|
)
|
|
)
|
|
-
|
|
|
|
- pause_button = self.get_player_button("SP_MediaPause")
|
|
|
|
- pause_button.setToolTip("Pause CD Preview")
|
|
|
|
- pause_button.clicked.connect(
|
|
|
|
- lambda _, x=rel_wave_path: self.pause_player(x)
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- stop_button = self.get_player_button("SP_MediaStop")
|
|
|
|
- stop_button.setToolTip("Stop CD Preview")
|
|
|
|
- stop_button.clicked.connect(
|
|
|
|
- lambda _, x=rel_wave_path: self.stop_player(x)
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- seek_bwd_button = self.get_player_button("SP_MediaSeekBackward")
|
|
|
|
- seek_bwd_button.setToolTip("Seek 10 seconds backwards")
|
|
|
|
- seek_bwd_button.clicked.connect(
|
|
|
|
- lambda _, x=rel_wave_path: self.seek_bwd_10secs(x)
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- seek_fwd_button = self.get_player_button("SP_MediaSeekForward")
|
|
|
|
- seek_fwd_button.setToolTip("Seek 10 seconds forwards")
|
|
|
|
- seek_fwd_button.clicked.connect(
|
|
|
|
- lambda _, x=rel_wave_path: self.seek_fwd_10secs(x)
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- button_layout.addWidget(play_button)
|
|
|
|
- button_layout.addWidget(pause_button)
|
|
|
|
- button_layout.addWidget(stop_button)
|
|
|
|
- button_layout.addWidget(seek_bwd_button)
|
|
|
|
- button_layout.addWidget(seek_fwd_button)
|
|
|
|
-
|
|
|
|
- secs = get_wave_duration_in_secs(path.join(base_dir, rel_wave_path))
|
|
|
|
- mins = secs // 60
|
|
|
|
- secs %= 60
|
|
|
|
- time_label = QLabel(f"00:00 / {mins:02}:{secs:02}")
|
|
|
|
- label_construct = LabelConstruct(rel_wave_path, time_label)
|
|
|
|
- self.position_labels.append(label_construct)
|
|
|
|
- button_layout.addWidget(time_label)
|
|
|
|
- timer = QTimer(self)
|
|
|
|
- timer.timeout.connect(self.update_position)
|
|
|
|
- timer.start(1000)
|
|
|
|
-
|
|
|
|
- scroll_area_layout.addLayout(button_layout)
|
|
|
|
-
|
|
|
|
- ok_button = QPushButton("OK")
|
|
|
|
- ok_button.clicked.connect(self.accept)
|
|
|
|
- self.master_layout.addWidget(ok_button)
|
|
|
|
-
|
|
|
|
- def play_audio(self, rel_path: str) -> None:
|
|
|
|
- if self.player.state() == QMediaPlayer.PausedState: # pyright: ignore
|
|
|
|
- media = self.player.media()
|
|
|
|
- if media.isNull():
|
|
|
|
- return
|
|
|
|
- if path.split(media.canonicalUrl().toString())[1] == rel_path:
|
|
|
|
- self.player.play()
|
|
|
|
- else:
|
|
|
|
- url = QUrl.fromLocalFile(path.join(self.base_dir, rel_path))
|
|
|
|
- content = QMediaContent(url)
|
|
|
|
- self.player.setMedia(content)
|
|
|
|
- self.player.play()
|
|
|
|
-
|
|
|
|
- def update_position(self) -> None:
|
|
|
|
- media = self.player.media()
|
|
|
|
- if media.isNull():
|
|
|
|
- return
|
|
|
|
- playing_path = path.split(media.canonicalUrl().toString())[1]
|
|
|
|
- for label_construct in self.position_labels:
|
|
|
|
- if label_construct.rel_path == playing_path:
|
|
|
|
- old_text = label_construct.label.text()
|
|
|
|
- old_text = old_text[old_text.find(" / ") :]
|
|
|
|
- secs = self.player.position() // 1000
|
|
|
|
- mins = secs // 60
|
|
|
|
- secs %= 60
|
|
|
|
- label_construct.label.setText(f"{mins:02}:{secs:02}{old_text}")
|
|
|
|
-
|
|
|
|
- def stop_player(self, rel_path: str) -> None:
|
|
|
|
- media = self.player.media()
|
|
|
|
- if media.isNull():
|
|
|
|
- return
|
|
|
|
- if path.split(media.canonicalUrl().toString())[1] == rel_path:
|
|
|
|
- self.player.stop()
|
|
|
|
- self.update_position()
|
|
|
|
-
|
|
|
|
- def seek_by(self, rel_path: str, seek_by_milis) -> None:
|
|
|
|
- media = self.player.media()
|
|
|
|
- if media.isNull():
|
|
|
|
- return
|
|
|
|
- if path.split(media.canonicalUrl().toString())[1] == rel_path:
|
|
|
|
- position = self.player.position()
|
|
|
|
- self.player.setPosition(position + seek_by_milis)
|
|
|
|
- self.update_position()
|
|
|
|
-
|
|
|
|
- def seek_fwd_10secs(self, rel_path: str) -> None:
|
|
|
|
- self.seek_by(rel_path, 10000)
|
|
|
|
-
|
|
|
|
- def seek_bwd_10secs(self, rel_path: str) -> None:
|
|
|
|
- self.seek_by(rel_path, -10000)
|
|
|
|
-
|
|
|
|
- def pause_player(self, rel_path: str) -> None:
|
|
|
|
- media = self.player.media()
|
|
|
|
- if media.isNull():
|
|
|
|
- return
|
|
|
|
- if path.split(media.canonicalUrl().toString())[1] == rel_path:
|
|
|
|
- self.player.pause()
|
|
|
|
-
|
|
|
|
- def get_scroll_area_layout(self):
|
|
|
|
- scroll_area = QScrollArea()
|
|
|
|
- scroll_area.setWidgetResizable(True)
|
|
|
|
- self.master_layout.addWidget(scroll_area)
|
|
|
|
-
|
|
|
|
- scroll_content = QWidget()
|
|
|
|
- scroll_area.setWidget(scroll_content)
|
|
|
|
- scroll_area_layout = QVBoxLayout(scroll_content)
|
|
|
|
- return scroll_area_layout
|
|
|
|
-
|
|
|
|
- def get_player_button(self, icon_name: str) -> QPushButton:
|
|
|
|
- stop_button = QPushButton("")
|
|
|
|
- stop_button.setMinimumSize(QSize(40, 40))
|
|
|
|
- stop_button.setMaximumSize(QSize(40, 40))
|
|
|
|
- pixmapi = getattr(QStyle, icon_name)
|
|
|
|
- icon = self.style().standardIcon(pixmapi) # pyright: ignore
|
|
|
|
- stop_button.setIcon(icon)
|
|
|
|
- return stop_button
|
|
|
|
-
|
|
|
|
- def accept(self) -> None:
|
|
|
|
- for check_box_construct in self.check_buttons:
|
|
|
|
- if check_box_construct.check_box.isChecked():
|
|
|
|
- self.chosen_sheets.append(check_box_construct.rel_path)
|
|
|
|
- super().accept()
|
|
|
|
-
|
|
|
|
- def get_wav_relative_path_from_cue_sheet(
|
|
|
|
- self, sheet_relative_path: str
|
|
|
|
- ) -> str:
|
|
|
|
- full_path = path.join(self.base_dir, sheet_relative_path)
|
|
|
|
-
|
|
|
|
- try:
|
|
|
|
- with open(
|
|
|
|
- full_path,
|
|
|
|
- mode="r",
|
|
|
|
- encoding="utf-8-sig",
|
|
|
|
- ) as cachefile_reader:
|
|
|
|
- cachefile_content = cachefile_reader.readlines()
|
|
|
|
- first_line = cachefile_content[0].strip()
|
|
|
|
- if not match(r"^FILE \".+\" WAVE$", first_line):
|
|
|
|
- raise CustomException("invalid first cue sheet line")
|
|
|
|
- full_path_found = first_line[first_line.find('"') + 1 :]
|
|
|
|
- full_path_found = full_path_found[: full_path_found.rfind('"')]
|
|
|
|
- return path.split(full_path_found)[1]
|
|
|
|
- except (
|
|
|
|
- FileNotFoundError,
|
|
|
|
- PermissionError,
|
|
|
|
- IOError,
|
|
|
|
- IndexError,
|
|
|
|
- CustomException,
|
|
|
|
- ) as error:
|
|
|
|
- QMessageBox.critical(
|
|
|
|
- self,
|
|
|
|
- "Error",
|
|
|
|
- f"Could not parse cue sheet: '{full_path}', Reason: {error}",
|
|
|
|
- )
|
|
|
|
- sys.exit(1)
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-class SegmentChooser(QDialog): # pylint: disable=too-few-public-methods
|
|
|
|
- def __init__(
|
|
|
|
- self, base_dir: str, options: list[SermonSegment], window_title: str
|
|
|
|
- ) -> None:
|
|
|
|
- super().__init__()
|
|
|
|
- self.base_dir = base_dir
|
|
|
|
- self.setWindowTitle(window_title)
|
|
|
|
-
|
|
|
|
- self.master_layout = QVBoxLayout(self)
|
|
|
|
-
|
|
|
|
- scroll_area_layout = self.get_scroll_area_layout()
|
|
|
|
-
|
|
|
|
- self.check_buttons = []
|
|
|
|
-
|
|
|
|
- self.player = QMediaPlayer()
|
|
|
|
-
|
|
|
|
- self.chosen_sheets = []
|
|
|
|
- self.position_labels = []
|
|
|
|
- for num, item in enumerate(options):
|
|
|
|
- rel_wave_path = self.get_wav_relative_path_from_cue_sheet(item)
|
|
|
|
-
|
|
|
|
- check_box = QCheckBox(item)
|
|
|
|
- if num == 0:
|
|
|
|
- check_box.setChecked(True)
|
|
|
|
- button_layout = QHBoxLayout()
|
|
|
|
- check_box_construct = CheckBoxConstruct(item, check_box)
|
|
|
|
self.check_buttons.append(check_box_construct)
|
|
self.check_buttons.append(check_box_construct)
|
|
button_layout.addWidget(check_box)
|
|
button_layout.addWidget(check_box)
|
|
|
|
|
|
@@ -390,10 +211,7 @@ class SegmentChooser(QDialog): # pylint: disable=too-few-public-methods
|
|
button_layout.addWidget(seek_bwd_button)
|
|
button_layout.addWidget(seek_bwd_button)
|
|
button_layout.addWidget(seek_fwd_button)
|
|
button_layout.addWidget(seek_fwd_button)
|
|
|
|
|
|
- secs = get_wave_duration_in_secs(path.join(base_dir, rel_wave_path))
|
|
|
|
- mins = secs // 60
|
|
|
|
- secs %= 60
|
|
|
|
- time_label = QLabel(f"00:00 / {mins:02}:{secs:02}")
|
|
|
|
|
|
+ time_label = self.get_initial_time_label(abs_wave_path)
|
|
label_construct = LabelConstruct(rel_wave_path, time_label)
|
|
label_construct = LabelConstruct(rel_wave_path, time_label)
|
|
self.position_labels.append(label_construct)
|
|
self.position_labels.append(label_construct)
|
|
button_layout.addWidget(time_label)
|
|
button_layout.addWidget(time_label)
|
|
@@ -407,6 +225,13 @@ class SegmentChooser(QDialog): # pylint: disable=too-few-public-methods
|
|
ok_button.clicked.connect(self.accept)
|
|
ok_button.clicked.connect(self.accept)
|
|
self.master_layout.addWidget(ok_button)
|
|
self.master_layout.addWidget(ok_button)
|
|
|
|
|
|
|
|
+ def get_initial_time_label(self, abs_wave_path):
|
|
|
|
+ secs = get_wave_duration_in_secs(abs_wave_path)
|
|
|
|
+ mins = secs // 60
|
|
|
|
+ secs %= 60
|
|
|
|
+ time_label = QLabel(f"00:00 / {mins:02}:{secs:02}")
|
|
|
|
+ return time_label
|
|
|
|
+
|
|
def play_audio(self, rel_path: str) -> None:
|
|
def play_audio(self, rel_path: str) -> None:
|
|
if self.player.state() == QMediaPlayer.PausedState: # pyright: ignore
|
|
if self.player.state() == QMediaPlayer.PausedState: # pyright: ignore
|
|
media = self.player.media()
|
|
media = self.player.media()
|
|
@@ -486,7 +311,12 @@ class SegmentChooser(QDialog): # pylint: disable=too-few-public-methods
|
|
def accept(self) -> None:
|
|
def accept(self) -> None:
|
|
for check_box_construct in self.check_buttons:
|
|
for check_box_construct in self.check_buttons:
|
|
if check_box_construct.check_box.isChecked():
|
|
if check_box_construct.check_box.isChecked():
|
|
- self.chosen_sheets.append(check_box_construct.rel_path)
|
|
|
|
|
|
+ self.chosen_audios.append(
|
|
|
|
+ ChosenAudio(
|
|
|
|
+ check_box_construct.rel_sheet_path,
|
|
|
|
+ check_box_construct.abs_wave_path,
|
|
|
|
+ )
|
|
|
|
+ )
|
|
super().accept()
|
|
super().accept()
|
|
|
|
|
|
def get_wav_relative_path_from_cue_sheet(
|
|
def get_wav_relative_path_from_cue_sheet(
|
|
@@ -521,12 +351,14 @@ class SegmentChooser(QDialog): # pylint: disable=too-few-public-methods
|
|
)
|
|
)
|
|
sys.exit(1)
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
+
|
|
@dataclass
|
|
@dataclass
|
|
class ArchiveTypeStrings:
|
|
class ArchiveTypeStrings:
|
|
archive_type_plural: str
|
|
archive_type_plural: str
|
|
action_to_choose: str
|
|
action_to_choose: str
|
|
action_ing_form: str
|
|
action_ing_form: str
|
|
|
|
|
|
|
|
+
|
|
def choose_cd_day() -> list[str]:
|
|
def choose_cd_day() -> list[str]:
|
|
strings = ArchiveTypeStrings("CD's", "CD day to Burn", "Burning CD for day")
|
|
strings = ArchiveTypeStrings("CD's", "CD day to Burn", "Burning CD for day")
|
|
return choose_archive_day(strings)
|
|
return choose_archive_day(strings)
|
|
@@ -567,3 +399,7 @@ def choose_archive_day(strings: ArchiveTypeStrings) -> list[str]:
|
|
f"Failed to access directory: {const.CD_RECORD_OUTPUT_BASEDIR}.",
|
|
f"Failed to access directory: {const.CD_RECORD_OUTPUT_BASEDIR}.",
|
|
"",
|
|
"",
|
|
]
|
|
]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+# class SheetAndPreviewChooser(WaveAndSheetPreviewChooserGUI):
|
|
|
|
+# def __init__(self, )
|