SAC_Simple_Audio_Converter/sac.py
2023-05-12 18:25:10 +12:00

127 lines
5.9 KiB
Python

import os
from PyQt5.QtCore import QProcess, Qt
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QMessageBox, QComboBox, QRadioButton
class SAC(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('SAC - Simple Audio Converter')
self.source_file_btn = QPushButton('Browse')
self.dest_dir_btn = QPushButton('Browse')
self.convert_btn = QPushButton('Convert')
self.abort_btn = QPushButton('Abort')
self.out_codec_dropdown = QComboBox()
self.bitrate_dropdown = QComboBox()
self.stereo_radio = QRadioButton('Stereo')
self.mono_radio = QRadioButton('Mono')
self.source_file_input = QLineEdit()
self.dest_dir_input = QLineEdit()
self.out_codec_dropdown.addItems(['mp3', 'aac', 'wav', 'flac', 'opus'])
self.out_codec_dropdown.setCurrentText('mp3')
self.bitrate_dropdown.addItems(['64K', '128K', '256K', '320K'])
self.bitrate_dropdown.setCurrentText('128K')
self.stereo_radio.setChecked(True)
self.source_file_btn.clicked.connect(self.browse_source_file)
self.dest_dir_btn.clicked.connect(self.browse_dest_dir)
self.convert_btn.clicked.connect(self.convert_audio)
self.abort_btn.clicked.connect(self.abort_audio)
self.out_codec_dropdown.currentTextChanged.connect(self.toggle_bitrate)
self.source_file_input.setAcceptDrops(True)
self.dest_dir_input.setAcceptDrops(True)
layout = QGridLayout()
layout.addWidget(QLabel('Source File'), 0, 0), layout.addWidget(self.source_file_input, 0, 1), layout.addWidget(self.source_file_btn, 0, 2)
layout.addWidget(QLabel('Destination Directory'), 1, 0), layout.addWidget(self.dest_dir_input, 1, 1), layout.addWidget(self.dest_dir_btn, 1, 2)
layout.addWidget(QLabel('Output Codec'), 2, 0), layout.addWidget(self.out_codec_dropdown, 2, 1)
layout.addWidget(QLabel('Bitrate'), 3, 0), layout.addWidget(self.bitrate_dropdown, 3, 1)
layout.addWidget(QLabel('Channel'), 4, 0), layout.addWidget(self.stereo_radio, 4, 1), layout.addWidget(self.mono_radio, 4, 2)
layout.addWidget(self.convert_btn, 5, 0), layout.addWidget(self.abort_btn, 5, 1)
self.setLayout(layout)
self.process = None
self.abort_btn.setDisabled(True)
def browse_source_file(self):
filename, _ = QFileDialog.getOpenFileName(self, 'Select Source File', os.getenv('HOME'), 'Audio File (*.*)')
if filename:
self.source_file_input.setText(filename)
self.dest_dir_input.setText(os.path.dirname(filename))
def browse_dest_dir(self):
dirpath = QFileDialog.getExistingDirectory(self, 'Select Destination Directory', self.dest_dir_input.text())
if dirpath:
self.dest_dir_input.setText(dirpath)
def convert_audio(self):
input_path = self.source_file_input.text()
output_dir = self.dest_dir_input.text()
out_codec = self.out_codec_dropdown.currentText()
bitrate = self.bitrate_dropdown.currentText() if not out_codec in ['flac'] else ''
channel = '2' if self.stereo_radio.isChecked() else '1'
if not os.path.isfile(input_path) or not os.path.isdir(output_dir): return
output_filename = os.path.splitext(os.path.basename(input_path))[0] + '.' + out_codec
output_path = os.path.join(output_dir, output_filename)
if out_codec == 'opus':
cmd = f'ffmpeg -i "{input_path}" -ac {channel} -b:a {bitrate.lower()} -c:a libopus -application audio -strict -2 "{output_path}"'
elif out_codec == 'wav':
cmd = f'ffmpeg -i "{input_path}" -ac {channel} -c:a pcm_s16le "{output_path}"'
else:
cmd = f'ffmpeg -i "{input_path}" -ac {channel} "{output_path}"'
[btn.setDisabled(True) for btn in [self.convert_btn, self.source_file_btn, self.dest_dir_btn, self.out_codec_dropdown, self.bitrate_dropdown, self.stereo_radio, self.mono_radio]]
self.abort_btn.setDisabled(False)
try:
self.process = QProcess()
self.process.setProcessChannelMode(QProcess.MergedChannels)
self.process.start(cmd)
if not self.process.waitForStarted():
self.show_error('Error', 'Failed to start process')
else:
while self.process.state() == QProcess.Running:
QApplication.processEvents()
if self.process.exitCode() == 0:
QMessageBox.information(self, 'Success', f'Audio file converted successfully: {output_path}')
else:
self.show_error('Error', f'Failed to convert audio file: {output_path}')
except Exception as e:
self.show_error('Error', str(e))
finally:
[btn.setDisabled(False) for btn in [self.convert_btn, self.source_file_btn, self.dest_dir_btn, self.out_codec_dropdown, self.bitrate_dropdown, self.stereo_radio, self.mono_radio]]
self.abort_btn.setDisabled(True)
def abort_audio(self):
self.process.terminate()
def toggle_bitrate(self):
is_opus = self.out_codec_dropdown.currentText() == 'opus'
self.bitrate_dropdown.setDisabled(not is_opus)
def show_error(self, title, message):
QMessageBox.critical(self, title, message)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
def dropEvent(self, event):
if event.mimeData().hasUrls():
url = event.mimeData().urls()[0]
path = url.toLocalFile()
if os.path.isfile(path):
self.source_file_input.setText(path)
self.dest_dir_input.setText(os.path.dirname(path))
event.acceptProposedAction()
if __name__ == '__main__':
app = QApplication([])
sac = SAC()
sac.show()
app.exec_()