From 360859f1f150997a456379310ff4dcdd767c5503 Mon Sep 17 00:00:00 2001 From: Al-Taie Date: Sat, 29 Jul 2023 00:43:04 +0300 Subject: [PATCH 1/7] implement progress page --- src/qml/components/ProcessPage.qml | 108 ++++++++++++++++++ .../convert/CircularProgressBar.qml | 86 ++++++++++++++ src/qml/main.qml | 12 +- 3 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 src/qml/components/ProcessPage.qml create mode 100644 src/qml/components/convert/CircularProgressBar.qml diff --git a/src/qml/components/ProcessPage.qml b/src/qml/components/ProcessPage.qml new file mode 100644 index 0000000..578e54d --- /dev/null +++ b/src/qml/components/ProcessPage.qml @@ -0,0 +1,108 @@ +import "../utils/audiohelper.mjs" as AudioHelper +import QtQuick 6.4 +import QtQuick.Controls 6.4 +import QtQuick.Dialogs +import QtQuick.Layouts 6.4 +import "convert" + +Rectangle { + id: progressPage + color: theme.background + + FontLoader { + id: poppinsFontLoader + + source: theme.font.source + } + + ListModel { + id: audioFilesModel + } + + Text { + id: title + + color: theme.fontPrimary + font.pixelSize: 40 + font.weight: Font.Bold + horizontalAlignment: Text.AlignRight + text: qsTr('تحــــويل مقــطع صوتي
إلى') + Layout.alignment: Qt.AlignRight + textFormat: Text.RichText + + anchors { + top: parent.top + right: parent.right + topMargin: 64 + rightMargin: 75 + } + } + + Image { + id: textBackground + + source: mainWindow.isLightTheme ? "qrc:/text_background_light" : "qrc:/text_background_dark" + width: 128 + + anchors { + top: parent.top + right: title.right + topMargin: title.anchors.topMargin + 48 + rightMargin: 60 + } + } + + Text { + color: "#fff" + font.pixelSize: 40 + horizontalAlignment: Text.AlignRight + text: qsTr('نـــص') + font.weight: Font.Bold + Layout.alignment: Qt.AlignRight + textFormat: Text.RichText + + anchors { + top: textBackground.top + bottom: textBackground.bottom + left: textBackground.left + right: textBackground.right + topMargin: 6 + rightMargin: 10 + } + } + + Text { + id: subtitle + + color: theme.fontSecondary + font.pixelSize: 28 + text: qsTr("تلقائياً ومجاناً") + font.weight: Font.Normal + horizontalAlignment: Text.AlignRight + Layout.alignment: Qt.AlignRight + + anchors { + top: title.bottom + right: title.right + topMargin: 16 + } + } + + CircularProgressBar { + id: progressBar + anchors.centerIn: parent + width: 290 + height: 290 + foregroundColor: theme.primary + backgroundColor: "#00000000" + } + + Connections { + target: backend + enabled: progressPage.visible + + function onProgress(progress, remaingTime) { + progressBar.value = progress; + } + } +} diff --git a/src/qml/components/convert/CircularProgressBar.qml b/src/qml/components/convert/CircularProgressBar.qml new file mode 100644 index 0000000..dbfdf7f --- /dev/null +++ b/src/qml/components/convert/CircularProgressBar.qml @@ -0,0 +1,86 @@ +import QtQuick 6.4 +import QtQuick.Controls 6.4 + + +Item { + id: progressBar + property int value: 0 + property int labelValue + property int animationDuration: 1000 + property color foregroundColor: "#2196F3" + property color backgroundColor: "#d1d1d1" + + width: 256 + height: 256 + + onValueChanged: { + canvas.degree = value; + labelValue = value + } + + Canvas { + id: canvas + antialiasing: true + anchors.fill: parent + property int degree: 0 + + onDegreeChanged: { + requestPaint(); + } + + onPaint: { + const centerX = width / 2; + const centerY = height / 2; + const radius = Math.min(width, height) * 0.4; + const startAngle = -90; // Start angle at the top (-90 degrees) + const endAngle = startAngle + (degree / 100) * 360; // Calculate the end angle based on progress + + const ctx = getContext("2d"); + ctx.clearRect(0, 0, width, height); // Clear the canvas + + // Draw the background circle + ctx.strokeStyle = progressBar.backgroundColor; + ctx.lineWidth = 20; + ctx.beginPath(); + ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI); + ctx.stroke(); + + // Draw the progress circle + ctx.strokeStyle = progressBar.foregroundColor; + ctx.beginPath(); + ctx.arc(centerX, centerY, radius, startAngle * Math.PI / 180, endAngle * Math.PI / 180); + ctx.stroke(); + } + + Behavior on degree { + NumberAnimation { + duration: progressBar.animationDuration + } + } + } + + Row { + anchors.centerIn: parent + Text { + text: progressBar.labelValue + font.pixelSize: 64 + color: "#000000" + font.bold: true + opacity: 0.6 + } + + Text { + text: "%" + font.pixelSize: 64 + color: "#000000" + font.bold: true + opacity: 0.36 + } + } + + Behavior on labelValue { + NumberAnimation { + duration: progressBar.animationDuration + } + } +} diff --git a/src/qml/main.qml b/src/qml/main.qml index c593540..036d479 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -6,6 +6,7 @@ import QtQuick.Layouts 6.4 import QtQuick.Window 6.4 import "components" import "themes" +import "components/convert" ApplicationWindow { id: mainWindow @@ -29,16 +30,18 @@ ApplicationWindow { anchors.fill: parent hoverEnabled: true - onPressed: mouse => { + onPressed: (mouse) => { clickPos = Qt.point(mouse.x, mouse.y); } - onMouseXChanged: mouse => { + onMouseXChanged: (mouse) => { if (mouse.buttons === Qt.LeftButton) mainWindow.x += (mouse.x - clickPos.x); + } - onMouseYChanged: mouse => { + onMouseYChanged: (mouse) => { if (mouse.buttons === Qt.LeftButton) mainWindow.y += (mouse.y - clickPos.y); + } } @@ -78,11 +81,12 @@ ApplicationWindow { return isLightTheme = !state; } } + ProcessPage { } Settings { id: settings - + category: "app" property alias isLightTheme: mainWindow.isLightTheme location: "file:settings.ini" From 8dc8de9fac6c2286ffca44275e6de7528fd4fd84 Mon Sep 17 00:00:00 2001 From: Al-Taie Date: Sat, 29 Jul 2023 00:43:54 +0300 Subject: [PATCH 2/7] implement backend logic and connect the ui --- settings.ini | 18 ---- src/controller.py | 50 ---------- src/domain/backend.py | 93 +++++++++++++++++++ src/{ => domain}/clipboardproxy.py | 0 src/domain/config.py | 56 +++++++++++ src/domain/progress.py | 7 ++ src/domain/threadpool.py | 51 ++++++++++ src/main.py | 12 ++- src/main.pyproject | 6 +- src/qml/components/ConvertPage.qml | 41 +++++--- src/qml/components/SettingsPage.qml | 37 ++------ src/qml/components/SideBar.qml | 9 +- src/qml/components/convert/AcceptTasks.qml | 6 +- .../components/settings/SettingsDropDown.qml | 2 +- src/qml/main.qml | 29 ++++-- src/qml/splash.qml | 2 +- 16 files changed, 287 insertions(+), 132 deletions(-) delete mode 100644 settings.ini delete mode 100644 src/controller.py create mode 100644 src/domain/backend.py rename src/{ => domain}/clipboardproxy.py (100%) create mode 100644 src/domain/config.py create mode 100644 src/domain/progress.py create mode 100644 src/domain/threadpool.py diff --git a/settings.ini b/settings.ini deleted file mode 100644 index 73b282e..0000000 --- a/settings.ini +++ /dev/null @@ -1,18 +0,0 @@ -[General] -downloadJson=false -isLightTheme=false -convertEngineIndex=0 -convertEngine=Wit -saveLocation= -wordCount=0 -isWitEngine=true -exportVtt=false -dropEmptyParts=false -maxPartLength= -witConvertKey= -whisperModel=base -whisperModelIndex=0 -convertLanguageIndex=0 -convertLanguage=ar -exportSrt=false -exportTxt=false diff --git a/src/controller.py b/src/controller.py deleted file mode 100644 index 04daf74..0000000 --- a/src/controller.py +++ /dev/null @@ -1,50 +0,0 @@ -"""The main entrypoint from UI to the business logic. - -When needed, the necessary job will be delegated to other objects. -""" - -import json -import logging - -from PySide6.QtCore import QObject, Slot - -logger = logging.getLogger(__name__) - - -class Controller(QObject): - - """Controller class for handling UI interactions and business logic.""" - - @Slot(None, result=None) - def resize(self) -> None: - logger.info("Resizing...") - - @Slot(str, result=None) - def setAudioUrls(self, json_string: str) -> None: - """Get list of audio urls from the front end. - - param: - ---- - json_string (str): audio urls in json format - """ - # Convert the JSON string back to a Python object - data_list = json.loads(json_string) - - print("The audioFiles is in python") # noqa: T201 - # Process the received ListModel - print(json.dumps(data_list, indent=4)) # noqa: T201 - - @Slot(str, result=None) - def setSettings(self, json_string: str) -> None: - """Get user defined settings from the frontend. - - param: - ---- - json_string (str): settings in json string format - """ - # Convert the JSON string back to a Python object - data_list = json.loads(json_string) - - print("The Settings is in python") # noqa: T201 - # Process the received ListModel - print(json.dumps(data_list, indent=4)) # noqa: T201 diff --git a/src/domain/backend.py b/src/domain/backend.py new file mode 100644 index 0000000..baf8a93 --- /dev/null +++ b/src/domain/backend.py @@ -0,0 +1,93 @@ +import os +from typing import List, Generator, Dict + +from PySide6.QtCore import QObject, Signal, Property, Slot, QThreadPool +from tafrigh import farrigh, Config + +from .progress import Progress +from .config import CaseSensitiveConfigParser +from .threadpool import WorkerSignals, Worker + +os.system('cls') + + +# BACKEND +class Backend(QObject): + result = Signal(dict) + progress = Signal(int, int) + error = Signal(tuple) + finish = Signal() + + def __init__(self, parent=None) -> None: + super().__init__(parent=parent) + self.signals = WorkerSignals() + self.threadpool = QThreadPool() + self._is_running = False + self._urls = [] + + def on_error(self, error) -> None: + self.error.emit(error) + self._is_running = False + + def on_result(self, result) -> None: + self.result.emit(result) + + def on_progress(self, progress: Progress) -> None: + self.progress.emit(progress.value, progress.remaining_time) + + def on_finish(self): + self.finish.emit() + + @Slot() + def start(self) -> None: + print("RUNNING") + + worker = Worker(func=self.run) + worker.signals.finished.connect(self.on_finish) + worker.signals.progress.connect(self.on_progress) + worker.signals.error.connect(self.on_error) + worker.signals.result.connect(self.on_result) + self.threadpool.start(worker) + + @Property(list) + def urls(self) -> List[str]: + return self._urls + + @urls.setter + def urls(self, value: List[str]): + self._urls = list(map(lambda x: x.replace("file:///", ""), value)) + + def run(self, *args, **kwargs) -> Generator[Dict[str, int], None, None]: + app_config = CaseSensitiveConfigParser.read_config() + whisper_model = f"whisper-{app_config.whisper_model}-ct2" + + config = Config( + urls_or_paths=self.urls, + playlist_items="", + verbose=True, + skip_if_output_exist=True, + model_name_or_path=whisper_model, + task='transcribe', + language=app_config.convert_language, + use_whisper_jax=False, + use_faster_whisper=False, + beam_size=5, + ct2_compute_type='float16', + + wit_client_access_token=app_config.wit_convert_key, + max_cutting_duration=int(app_config.max_part_length), + + min_words_per_segment=app_config.word_count, + save_files_before_compact=False, + save_yt_dlp_responses=app_config.download_json, + output_sample=0, + output_formats=app_config.get_output_formats(), + output_dir=app_config.save_location.replace("file:///", ""), + ) + + return farrigh(config) + + @Slot(str) + @staticmethod + def open_folder(path: str): + os.startfile(path) diff --git a/src/clipboardproxy.py b/src/domain/clipboardproxy.py similarity index 100% rename from src/clipboardproxy.py rename to src/domain/clipboardproxy.py diff --git a/src/domain/config.py b/src/domain/config.py new file mode 100644 index 0000000..157384e --- /dev/null +++ b/src/domain/config.py @@ -0,0 +1,56 @@ +import re +from pydantic import BaseModel +from configparser import ConfigParser +from typing import List, Type, TypeVar + +T = TypeVar('T', bound=BaseModel) + + +class AppConfig(BaseModel): + download_json: bool + convert_engine: str + save_location: str + word_count: int + is_wit_engine: bool + export_vtt: bool + drop_empty_parts: bool + max_part_length: float + wit_convert_key: str + whisper_model: str + convert_language: str + export_srt: bool + export_txt: bool + + def get_output_formats(self) -> List[str]: + formats = { + 'srt': self.export_srt, + 'vtt': self.export_vtt, + 'txt': self.export_txt + } + return [key for key, value in formats.items() if value] + + +class CaseSensitiveConfigParser(ConfigParser): + def optionxform(self, optionstr) -> str: + return optionstr + + @staticmethod + def camel_to_snake(camel_case: str) -> str: + snake_case = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', camel_case) + return snake_case.lower() + + @classmethod + def read_config( + cls, + model: Type[T] = AppConfig, + filename: str = 'settings.ini', + default_section: str = 'config' + ) -> T: + parser = cls(default_section=default_section) + parser.read(filename) + + data = { + cls.camel_to_snake(key): value for key, value in parser.defaults().items() + } + + return model(**data) diff --git a/src/domain/progress.py b/src/domain/progress.py new file mode 100644 index 0000000..4e6f436 --- /dev/null +++ b/src/domain/progress.py @@ -0,0 +1,7 @@ +from dataclasses import dataclass + + +@dataclass +class Progress: + value: float = 0.0 + remaining_time: float = None diff --git a/src/domain/threadpool.py b/src/domain/threadpool.py new file mode 100644 index 0000000..8c1529a --- /dev/null +++ b/src/domain/threadpool.py @@ -0,0 +1,51 @@ +from typing import Callable, Generator, Dict + +from PySide6.QtCore import Signal, QObject, QRunnable +import traceback +import sys + +from .progress import Progress + + +# THREAD SIGNALS +class WorkerSignals(QObject): + finished = Signal() + error = Signal(tuple) + result = Signal(dict) + progress = Signal(Progress) + + +# CUSTOM THREAD CLASS +class Worker(QRunnable): + def __init__( + self, + func: Callable[..., Generator[Dict[str, int], None, None]], + *args, + **kwargs + ) -> None: + super(Worker, self).__init__() + self.func = func + self.args = args + self.kwargs = kwargs + self.signals = WorkerSignals() + + def run(self) -> None: + try: + results = self.func(args=self.args, kwargs=self.kwargs) + for result in results: + progress_value, remaining_time = result["progress"], result["remaining_time"] + progress = Progress(value=progress_value, remaining_time=remaining_time, ) + self.signals.progress.emit(progress) + self.signals.result.emit(result) + except Exception: + traceback.print_exc() + exc_type, value = sys.exc_info()[:2] + self.signals.error.emit((exc_type, value, traceback.format_exc())) + finally: + try: + self.signals.finished.emit() + except RuntimeError: + return + + def stop(self) -> None: + self.terminate() diff --git a/src/main.py b/src/main.py index 9b622b8..fa2a028 100644 --- a/src/main.py +++ b/src/main.py @@ -10,8 +10,9 @@ from PySide6.QtQuickControls2 import QQuickStyle from PySide6.QtWidgets import QApplication -from clipboardproxy import ClipboardProxy -from controller import Controller +from domain.backend import Backend +from domain.clipboardproxy import ClipboardProxy + QQuickStyle.setStyle("Material") @@ -23,12 +24,13 @@ app.setApplicationName("Almufaragh") engine = QQmlApplicationEngine() - controller = Controller() + backend = Backend() clipboard = QApplication.clipboard() clipboard_proxy = ClipboardProxy(clipboard) # Expose the Python object to QML - engine.rootContext().setContextProperty("controller", controller) + engine.quit.connect(app.quit) + engine.rootContext().setContextProperty("backend", backend) engine.rootContext().setContextProperty("clipboard", clipboard_proxy) path: Path = Path(__file__).parent / "qml" @@ -40,7 +42,7 @@ app.exec() # Load Main Window - engine.load(QUrl.fromLocalFile(path / "main.qml")) + engine.load(QUrl.fromLocalFile((path / "main.qml"))) if not engine.rootObjects(): sys.exit(-1) diff --git a/src/main.pyproject b/src/main.pyproject index d711f71..0104c41 100644 --- a/src/main.pyproject +++ b/src/main.pyproject @@ -17,8 +17,10 @@ "qml/components/SideBar.qml", "qml/themes/DarkTheme.qml", "qml/themes/LightTheme.qml", + "qml/themes/qmldir", "qml/main.qml", + "qml/splash.qml", "qml/theme_demo.qml", - "qml/utils/audiohelper.mjs", + "qml/utils/audiohelper.mjs" ] -} +} \ No newline at end of file diff --git a/src/qml/components/ConvertPage.qml b/src/qml/components/ConvertPage.qml index 772b603..0bd2540 100644 --- a/src/qml/components/ConvertPage.qml +++ b/src/qml/components/ConvertPage.qml @@ -1,13 +1,14 @@ import "../utils/audiohelper.mjs" as AudioHelper -import QtQuick 6.4 +import QtQuick 2.15 import QtQuick.Controls 6.4 import QtQuick.Dialogs import QtQuick.Layouts 6.4 import "convert" Rectangle { + id: root //audioUrls must be in jsonstring format - signal convertRequested(string audioUrls) + signal convertRequested(list urls) color: theme.background @@ -38,6 +39,7 @@ Rectangle { topMargin: 64 rightMargin: 75 } + } Image { @@ -52,6 +54,7 @@ Rectangle { topMargin: title.anchors.topMargin + 48 rightMargin: 60 } + } Text { @@ -71,6 +74,7 @@ Rectangle { topMargin: 6 rightMargin: 10 } + } Text { @@ -88,17 +92,14 @@ Rectangle { right: title.right topMargin: 16 } + } AcceptTasks { id: acceptTasks - onAddedNewAudio: audio => { - console.log('From ConverPage ', audio); - console.log("The list ", audioFilesModel.count); - audioFilesModel.append({ - "audioUrl": audio - }); + onAddedNewAudio: (audio) => { + audioFilesModel.append({'file': audio}); } anchors { @@ -108,6 +109,7 @@ Rectangle { rightMargin: 72 leftMargin: 72 } + } Rectangle { @@ -142,7 +144,9 @@ Rectangle { audioFilesModel.remove(index); // Remove the audio file from the model } } + } + } RowLayout { @@ -162,14 +166,13 @@ Rectangle { text: qsTr("البــــدء") Layout.fillWidth: true onClicked: { - let convertData = { - "audioUrlList": [] - }; + var listData = [] for (var i = 0; i < audioFilesModel.count; i++) { - convertData.audioUrlList.push(audioFilesModel.get(i)); + var item = audioFilesModel.get(i) + listData.push(item.file) } - let jsonString = JSON.stringify(convertData); - convertRequested(jsonString); + + root.convertRequested(listData) } } @@ -180,4 +183,14 @@ Rectangle { onClicked: audioFilesModel.clear() } } + + + Connections { + target: backend + enabled: root.visible + + function onFinish() { + audioFilesModel.clear() + } + } } diff --git a/src/qml/components/SettingsPage.qml b/src/qml/components/SettingsPage.qml index e4f0a93..e0bd825 100644 --- a/src/qml/components/SettingsPage.qml +++ b/src/qml/components/SettingsPage.qml @@ -10,26 +10,10 @@ Rectangle { id: root property bool isWitEngine: true + property alias saveLocation: saveLocation.value signal themeChanged(bool state) - function getSettingsData() { - console.log("hi ", convertLanguage.currentText); - let settingsData = { - "convertLanguage": convertLanguage.selectedText, - "engineSelector": engineSelector.selectedText, - "modelSelector": modelSelector.selectedText, - "convertKey": convertKey.selectedText, - "wordCount": wordCount.selectedText, - "maxPartLength": maxPartLength.selectedText, - "dropEmptyParts": dropEmptyParts.selectedValue, - "exportFormats": exportFormats.getSelectedValue(), - "saveLocation": saveLocation.selectedValue, - "jsonLoad": jsonLoad.selectedValue - }; - return settingsData; - } - color: theme.background ColumnLayout { @@ -207,7 +191,8 @@ Rectangle { Slider { id: slider - + from: 3 + to: 17 implicitWidth: parent.width / 3 } } @@ -230,14 +215,6 @@ Rectangle { SettingsItem { id: exportFormats - function getSelectedValue() { - return { - "srt": srt.checked, - "txt": txt.checked, - "vtt": vtt.checked - }; - } - iconSource: "qrc:/export" labelText: qsTr("صيغ المخرجات") @@ -384,7 +361,7 @@ Rectangle { Settings { id: settings - + category: "config" property alias isWitEngine: root.isWitEngine property alias downloadJson: jsonCheck.checked property alias saveLocation: saveLocation.value @@ -398,6 +375,12 @@ Rectangle { property alias whisperModel: whisperModel.value property alias convertEngine: convertEngine.value property alias convertLanguage: convertLanguage.value + + location: "file:settings.ini" + } + + Settings { + category: "app" property alias whisperModelIndex: whisperModel.currentIndex property alias convertEngineIndex: convertEngine.currentIndex property alias convertLanguageIndex: convertLanguage.currentIndex diff --git a/src/qml/components/SideBar.qml b/src/qml/components/SideBar.qml index 28ad92b..ff92df1 100644 --- a/src/qml/components/SideBar.qml +++ b/src/qml/components/SideBar.qml @@ -6,6 +6,7 @@ Rectangle { property int index: 0 signal sidebarButtonClicked(int index) + signal folderClick() color: theme.primary width: 80 @@ -35,7 +36,7 @@ Rectangle { } IconImage { - source: index == 0 ? "qrc:/home_selected" : "qrc:/home_unselected" + source: index === 0 ? "qrc:/home_selected" : "qrc:/home_unselected" Layout.alignment: Qt.AlignHCenter MouseArea { @@ -50,7 +51,7 @@ Rectangle { } IconImage { - source: index == 1 ? "qrc:/settings_selected" : "qrc:/settings_unselected" + source: index === 1 ? "qrc:/settings_selected" : "qrc:/settings_unselected" Layout.alignment: Qt.AlignHCenter MouseArea { @@ -78,8 +79,8 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: { - } + onClicked: folderClick() + } } diff --git a/src/qml/components/convert/AcceptTasks.qml b/src/qml/components/convert/AcceptTasks.qml index 5a81df3..47d6fc0 100644 --- a/src/qml/components/convert/AcceptTasks.qml +++ b/src/qml/components/convert/AcceptTasks.qml @@ -40,9 +40,9 @@ DropArea { Canvas { anchors.fill: parent onPaint: { - var ctx = getContext("2d"); - var borderRadius = 24; - var halfBorderWidth = 1.5; // Half the desired border width for proper positioning + const ctx = getContext("2d"); + const borderRadius = 24; + const halfBorderWidth = 1.5; // Half the desired border width for proper positioning ctx.strokeStyle = theme.primary; ctx.lineWidth = 3; ctx.setLineDash([5, 5]); diff --git a/src/qml/components/settings/SettingsDropDown.qml b/src/qml/components/settings/SettingsDropDown.qml index 1f5d920..17f29d8 100644 --- a/src/qml/components/settings/SettingsDropDown.qml +++ b/src/qml/components/settings/SettingsDropDown.qml @@ -17,7 +17,7 @@ SettingsItem { font.family: poppinsFontLoader.font.family model: dropdownModel onActivated: index => { - var selectedValue = model.get(index)[combo.valueRole]; + const selectedValue = model.get(index)[combo.valueRole]; console.log(selectedValue); changedSelection(index, selectedValue); combo.currentIndex = index; diff --git a/src/qml/main.qml b/src/qml/main.qml index 036d479..b76a5a2 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -51,9 +51,11 @@ ApplicationWindow { anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: parent.left - onSidebarButtonClicked: index => { + onFolderClick: { + backend.open_folder(settingsPage.saveLocation) + } + onSidebarButtonClicked: (index) => { stackLayout.currentIndex = index; - console.log("lol index changed"); } } @@ -68,20 +70,24 @@ ApplicationWindow { } ConvertPage { - onConvertRequested: audioUrls => { - controller.setAudioUrls(audioUrls); - controller.setSettings(JSON.stringify(settingsPage.getSettingsData())); + onConvertRequested: (urls) => { + backend.urls = urls + backend.start() + stackLayout.currentIndex = 2 } } SettingsPage { id: settingsPage - onThemeChanged: state => { - return isLightTheme = !state; + onThemeChanged: (state) => { + isLightTheme = !state; } } + ProcessPage { + id: processPage + } } Settings { @@ -91,4 +97,13 @@ ApplicationWindow { location: "file:settings.ini" } + + Connections { + target: backend + enabled: mainWindow.visible + + function onFinish() { + stackLayout.currentIndex = 0 + } + } } diff --git a/src/qml/splash.qml b/src/qml/splash.qml index 2818bb9..db7fcc4 100644 --- a/src/qml/splash.qml +++ b/src/qml/splash.qml @@ -92,7 +92,7 @@ ApplicationWindow { Settings { id: settings - + category: "app" property alias isLightTheme: splash.isLightTheme location: "file:settings.ini" From 1f4f183982a5cb03d365c49c31d24573fb7060e9 Mon Sep 17 00:00:00 2001 From: Al-Taie Date: Sat, 29 Jul 2023 01:31:16 +0300 Subject: [PATCH 3/7] fix some issues --- src/domain/backend.py | 19 ++++++++----------- src/qml/components/ProcessPage.qml | 6 +++++- .../convert/CircularProgressBar.qml | 6 ++---- src/qml/main.qml | 12 ++++++++++++ 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/domain/backend.py b/src/domain/backend.py index baf8a93..d299531 100644 --- a/src/domain/backend.py +++ b/src/domain/backend.py @@ -5,7 +5,7 @@ from tafrigh import farrigh, Config from .progress import Progress -from .config import CaseSensitiveConfigParser +from .config import CaseSensitiveConfigParser, AppConfig from .threadpool import WorkerSignals, Worker os.system('cls') @@ -40,8 +40,6 @@ def on_finish(self): @Slot() def start(self) -> None: - print("RUNNING") - worker = Worker(func=self.run) worker.signals.finished.connect(self.on_finish) worker.signals.progress.connect(self.on_progress) @@ -58,23 +56,22 @@ def urls(self, value: List[str]): self._urls = list(map(lambda x: x.replace("file:///", ""), value)) def run(self, *args, **kwargs) -> Generator[Dict[str, int], None, None]: - app_config = CaseSensitiveConfigParser.read_config() - whisper_model = f"whisper-{app_config.whisper_model}-ct2" + app_config: AppConfig = CaseSensitiveConfigParser.read_config() config = Config( urls_or_paths=self.urls, playlist_items="", - verbose=True, + verbose=False, skip_if_output_exist=True, - model_name_or_path=whisper_model, + model_name_or_path=app_config.whisper_model, task='transcribe', language=app_config.convert_language, use_whisper_jax=False, - use_faster_whisper=False, + use_faster_whisper=True, beam_size=5, - ct2_compute_type='float16', + ct2_compute_type='default', - wit_client_access_token=app_config.wit_convert_key, + wit_client_access_token=app_config.wit_convert_key if app_config.is_wit_engine else None, max_cutting_duration=int(app_config.max_part_length), min_words_per_segment=app_config.word_count, @@ -86,7 +83,7 @@ def run(self, *args, **kwargs) -> Generator[Dict[str, int], None, None]: ) return farrigh(config) - + @Slot(str) @staticmethod def open_folder(path: str): diff --git a/src/qml/components/ProcessPage.qml b/src/qml/components/ProcessPage.qml index 578e54d..6f83108 100644 --- a/src/qml/components/ProcessPage.qml +++ b/src/qml/components/ProcessPage.qml @@ -101,8 +101,12 @@ Rectangle { target: backend enabled: progressPage.visible - function onProgress(progress, remaingTime) { + function onProgress(progress, remainingTime) { progressBar.value = progress; } + + function onFinish() { + progressBar.value = 0 + } } } diff --git a/src/qml/components/convert/CircularProgressBar.qml b/src/qml/components/convert/CircularProgressBar.qml index dbfdf7f..78283be 100644 --- a/src/qml/components/convert/CircularProgressBar.qml +++ b/src/qml/components/convert/CircularProgressBar.qml @@ -64,17 +64,15 @@ Item { Text { text: progressBar.labelValue font.pixelSize: 64 - color: "#000000" + color: theme.fontSecondary font.bold: true - opacity: 0.6 } Text { text: "%" font.pixelSize: 64 - color: "#000000" + color: theme.fontThirty font.bold: true - opacity: 0.36 } } diff --git a/src/qml/main.qml b/src/qml/main.qml index b76a5a2..85c1778 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -103,6 +103,18 @@ ApplicationWindow { enabled: mainWindow.visible function onFinish() { + timer.start() + } + } + + Timer { + id: timer + + interval: 1000 + running: false + repeat: false + onTriggered: { + timer.stop(); stackLayout.currentIndex = 0 } } From 094c429b84d60ca69b7c6766600cc2814c8c8f25 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 22:35:02 +0000 Subject: [PATCH 4/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/domain/backend.py | 6 +++--- src/domain/threadpool.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/domain/backend.py b/src/domain/backend.py index d299531..287f240 100644 --- a/src/domain/backend.py +++ b/src/domain/backend.py @@ -4,9 +4,9 @@ from PySide6.QtCore import QObject, Signal, Property, Slot, QThreadPool from tafrigh import farrigh, Config -from .progress import Progress -from .config import CaseSensitiveConfigParser, AppConfig -from .threadpool import WorkerSignals, Worker +from domain.progress import Progress +from domain.config import CaseSensitiveConfigParser, AppConfig +from domain.threadpool import WorkerSignals, Worker os.system('cls') diff --git a/src/domain/threadpool.py b/src/domain/threadpool.py index 8c1529a..d75571a 100644 --- a/src/domain/threadpool.py +++ b/src/domain/threadpool.py @@ -4,7 +4,7 @@ import traceback import sys -from .progress import Progress +from domain.progress import Progress # THREAD SIGNALS From 3558f903f4c2bffd5b4d4f7dd07080d08a2ca492 Mon Sep 17 00:00:00 2001 From: yshalsager Date: Sun, 30 Jul 2023 23:24:49 +0300 Subject: [PATCH 5/7] chore(style): format and lint Signed-off-by: yshalsager --- src/domain/__init__.py | 1 + src/domain/backend.py | 65 +++++++++++-------- src/domain/config.py | 43 ++++++------ src/domain/progress.py | 6 +- src/domain/threadpool.py | 33 ++++++---- src/qml/components/ConvertPage.qml | 29 ++++----- src/qml/components/ProcessPage.qml | 2 +- src/qml/components/SideBar.qml | 3 +- .../convert/CircularProgressBar.qml | 10 ++- src/qml/main.qml | 26 ++++---- 10 files changed, 118 insertions(+), 100 deletions(-) create mode 100644 src/domain/__init__.py diff --git a/src/domain/__init__.py b/src/domain/__init__.py new file mode 100644 index 0000000..0435d86 --- /dev/null +++ b/src/domain/__init__.py @@ -0,0 +1 @@ +"""Domain package.""" diff --git a/src/domain/backend.py b/src/domain/backend.py index 287f240..e639d4f 100644 --- a/src/domain/backend.py +++ b/src/domain/backend.py @@ -1,41 +1,45 @@ -import os -from typing import List, Generator, Dict +"""Backend that interacts with tafrigh.""" +from platform import system +from subprocess import Popen +from typing import Any -from PySide6.QtCore import QObject, Signal, Property, Slot, QThreadPool -from tafrigh import farrigh, Config +from PySide6.QtCore import Property, QObject, QThreadPool, Signal, Slot +from tafrigh import Config, farrigh +from domain.config import AppConfig, CaseSensitiveConfigParser from domain.progress import Progress -from domain.config import CaseSensitiveConfigParser, AppConfig -from domain.threadpool import WorkerSignals, Worker - -os.system('cls') +from domain.threadpool import Worker, WorkerSignals # BACKEND class Backend(QObject): + + """Backend object.""" + result = Signal(dict) progress = Signal(int, int) error = Signal(tuple) finish = Signal() - def __init__(self, parent=None) -> None: + def __init__(self, parent: QObject | None = None) -> None: + """Initialize backend object.""" super().__init__(parent=parent) self.signals = WorkerSignals() self.threadpool = QThreadPool() self._is_running = False - self._urls = [] + self._urls: list[str] = [] - def on_error(self, error) -> None: + def on_error(self, error: tuple[str, int, str]) -> None: self.error.emit(error) self._is_running = False - def on_result(self, result) -> None: + def on_result(self, result: dict[str, Any]) -> None: self.result.emit(result) def on_progress(self, progress: Progress) -> None: self.progress.emit(progress.value, progress.remaining_time) - def on_finish(self): + def on_finish(self) -> None: self.finish.emit() @Slot() @@ -48,43 +52,52 @@ def start(self) -> None: self.threadpool.start(worker) @Property(list) - def urls(self) -> List[str]: + def urls(self) -> list[str]: return self._urls - @urls.setter - def urls(self, value: List[str]): - self._urls = list(map(lambda x: x.replace("file:///", ""), value)) + @urls.setter # type: ignore[no-redef] + def urls(self, value: list[str]): + self._urls = [x.replace("file:///", "") for x in value] - def run(self, *args, **kwargs) -> Generator[Dict[str, int], None, None]: + def run(self, *args: Any, **kwargs: Any) -> Any: app_config: AppConfig = CaseSensitiveConfigParser.read_config() config = Config( + *args, urls_or_paths=self.urls, playlist_items="", verbose=False, skip_if_output_exist=True, model_name_or_path=app_config.whisper_model, - task='transcribe', + task="transcribe", language=app_config.convert_language, use_whisper_jax=False, use_faster_whisper=True, beam_size=5, - ct2_compute_type='default', - - wit_client_access_token=app_config.wit_convert_key if app_config.is_wit_engine else None, + ct2_compute_type="default", + wit_client_access_token=app_config.wit_convert_key + if app_config.is_wit_engine + else None, max_cutting_duration=int(app_config.max_part_length), - min_words_per_segment=app_config.word_count, save_files_before_compact=False, save_yt_dlp_responses=app_config.download_json, output_sample=0, output_formats=app_config.get_output_formats(), output_dir=app_config.save_location.replace("file:///", ""), + **kwargs, ) return farrigh(config) - @Slot(str) @staticmethod - def open_folder(path: str): - os.startfile(path) + @Slot(str) + def open_folder(path: str) -> None: + if system() == "Windows": + from os import startfile # type: ignore[attr-defined] + + startfile(path) # noqa: S606 + elif system() == "Darwin": + Popen(["open", path], shell=False) # noqa: S603, S607 + else: + Popen(["xdg-open", path], shell=False) # noqa: S603, S607 diff --git a/src/domain/config.py b/src/domain/config.py index 157384e..adb05eb 100644 --- a/src/domain/config.py +++ b/src/domain/config.py @@ -1,12 +1,20 @@ +"""A module contains the configuration parser. + +It is used to read the settings.ini file and convert it to a pydantic model. +""" import re -from pydantic import BaseModel from configparser import ConfigParser -from typing import List, Type, TypeVar +from typing import TypeVar, cast + +from pydantic import BaseModel -T = TypeVar('T', bound=BaseModel) +T = TypeVar("T", bound=BaseModel) class AppConfig(BaseModel): + + """App configuration model.""" + download_json: bool convert_engine: str save_location: str @@ -21,36 +29,33 @@ class AppConfig(BaseModel): export_srt: bool export_txt: bool - def get_output_formats(self) -> List[str]: - formats = { - 'srt': self.export_srt, - 'vtt': self.export_vtt, - 'txt': self.export_txt - } + def get_output_formats(self) -> list[str]: + formats = {"srt": self.export_srt, "vtt": self.export_vtt, "txt": self.export_txt} return [key for key, value in formats.items() if value] class CaseSensitiveConfigParser(ConfigParser): - def optionxform(self, optionstr) -> str: - return optionstr + + """A case sensitive config parser.""" + + def optionxform(self, option_str: str) -> str: + return option_str @staticmethod def camel_to_snake(camel_case: str) -> str: - snake_case = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', camel_case) + snake_case = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", camel_case) return snake_case.lower() @classmethod def read_config( cls, - model: Type[T] = AppConfig, - filename: str = 'settings.ini', - default_section: str = 'config' + model: type[T] = AppConfig, + filename: str = "settings.ini", + default_section: str = "config", ) -> T: parser = cls(default_section=default_section) parser.read(filename) - data = { - cls.camel_to_snake(key): value for key, value in parser.defaults().items() - } + data = {cls.camel_to_snake(key): value for key, value in parser.defaults().items()} - return model(**data) + return cast(T, model(**data)) diff --git a/src/domain/progress.py b/src/domain/progress.py index 4e6f436..bfc7ced 100644 --- a/src/domain/progress.py +++ b/src/domain/progress.py @@ -1,7 +1,11 @@ +"""Progress domain model.""" from dataclasses import dataclass @dataclass class Progress: + + """Progress data class.""" + value: float = 0.0 - remaining_time: float = None + remaining_time: float | None = None diff --git a/src/domain/threadpool.py b/src/domain/threadpool.py index d75571a..46c696c 100644 --- a/src/domain/threadpool.py +++ b/src/domain/threadpool.py @@ -1,29 +1,33 @@ -from typing import Callable, Generator, Dict - -from PySide6.QtCore import Signal, QObject, QRunnable -import traceback +"""Custom thread class and signals emitted by worker threads.""" import sys +import traceback +from collections.abc import Callable, Generator +from typing import Any + +from PySide6.QtCore import QObject, QRunnable, Signal from domain.progress import Progress -# THREAD SIGNALS class WorkerSignals(QObject): + + """Signals emitted by worker threads.""" + finished = Signal() error = Signal(tuple) result = Signal(dict) progress = Signal(Progress) -# CUSTOM THREAD CLASS class Worker(QRunnable): + + """Custom thread class.""" + def __init__( - self, - func: Callable[..., Generator[Dict[str, int], None, None]], - *args, - **kwargs + self, func: Callable[..., Generator[dict[str, int], None, None]], *args: Any, **kwargs: Any ) -> None: - super(Worker, self).__init__() + """Initialize worker object.""" + super().__init__() self.func = func self.args = args self.kwargs = kwargs @@ -34,10 +38,13 @@ def run(self) -> None: results = self.func(args=self.args, kwargs=self.kwargs) for result in results: progress_value, remaining_time = result["progress"], result["remaining_time"] - progress = Progress(value=progress_value, remaining_time=remaining_time, ) + progress = Progress( + value=progress_value, + remaining_time=remaining_time, + ) self.signals.progress.emit(progress) self.signals.result.emit(result) - except Exception: + except Exception: # noqa: BLE001 traceback.print_exc() exc_type, value = sys.exc_info()[:2] self.signals.error.emit((exc_type, value, traceback.format_exc())) diff --git a/src/qml/components/ConvertPage.qml b/src/qml/components/ConvertPage.qml index 0bd2540..a84813b 100644 --- a/src/qml/components/ConvertPage.qml +++ b/src/qml/components/ConvertPage.qml @@ -8,7 +8,7 @@ import "convert" Rectangle { id: root //audioUrls must be in jsonstring format - signal convertRequested(list urls) + signal convertRequested(list urls) color: theme.background @@ -39,7 +39,6 @@ Rectangle { topMargin: 64 rightMargin: 75 } - } Image { @@ -54,7 +53,6 @@ Rectangle { topMargin: title.anchors.topMargin + 48 rightMargin: 60 } - } Text { @@ -74,7 +72,6 @@ Rectangle { topMargin: 6 rightMargin: 10 } - } Text { @@ -92,14 +89,15 @@ Rectangle { right: title.right topMargin: 16 } - } AcceptTasks { id: acceptTasks - onAddedNewAudio: (audio) => { - audioFilesModel.append({'file': audio}); + onAddedNewAudio: audio => { + audioFilesModel.append({ + "file": audio + }); } anchors { @@ -109,7 +107,6 @@ Rectangle { rightMargin: 72 leftMargin: 72 } - } Rectangle { @@ -144,9 +141,7 @@ Rectangle { audioFilesModel.remove(index); // Remove the audio file from the model } } - } - } RowLayout { @@ -166,31 +161,29 @@ Rectangle { text: qsTr("البــــدء") Layout.fillWidth: true onClicked: { - var listData = [] + var listData = []; for (var i = 0; i < audioFilesModel.count; i++) { - var item = audioFilesModel.get(i) - listData.push(item.file) + var item = audioFilesModel.get(i); + listData.push(item.file); } - - root.convertRequested(listData) + root.convertRequested(listData); } } CustomButton { - text: qsTr("الغــاء") + text: qsTr("إلغــاء") backColor: theme.card Layout.fillWidth: true onClicked: audioFilesModel.clear() } } - Connections { target: backend enabled: root.visible function onFinish() { - audioFilesModel.clear() + audioFilesModel.clear(); } } } diff --git a/src/qml/components/ProcessPage.qml b/src/qml/components/ProcessPage.qml index 6f83108..2448cdb 100644 --- a/src/qml/components/ProcessPage.qml +++ b/src/qml/components/ProcessPage.qml @@ -106,7 +106,7 @@ Rectangle { } function onFinish() { - progressBar.value = 0 + progressBar.value = 0; } } } diff --git a/src/qml/components/SideBar.qml b/src/qml/components/SideBar.qml index ff92df1..806f27b 100644 --- a/src/qml/components/SideBar.qml +++ b/src/qml/components/SideBar.qml @@ -6,7 +6,7 @@ Rectangle { property int index: 0 signal sidebarButtonClicked(int index) - signal folderClick() + signal folderClick color: theme.primary width: 80 @@ -80,7 +80,6 @@ Rectangle { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: folderClick() - } } diff --git a/src/qml/components/convert/CircularProgressBar.qml b/src/qml/components/convert/CircularProgressBar.qml index 78283be..2a3f208 100644 --- a/src/qml/components/convert/CircularProgressBar.qml +++ b/src/qml/components/convert/CircularProgressBar.qml @@ -1,7 +1,6 @@ import QtQuick 6.4 import QtQuick.Controls 6.4 - Item { id: progressBar property int value: 0 @@ -15,7 +14,7 @@ Item { onValueChanged: { canvas.degree = value; - labelValue = value + labelValue = value; } Canvas { @@ -34,7 +33,6 @@ Item { const radius = Math.min(width, height) * 0.4; const startAngle = -90; // Start angle at the top (-90 degrees) const endAngle = startAngle + (degree / 100) * 360; // Calculate the end angle based on progress - const ctx = getContext("2d"); ctx.clearRect(0, 0, width, height); // Clear the canvas @@ -52,14 +50,14 @@ Item { ctx.stroke(); } - Behavior on degree { + Behavior on degree { NumberAnimation { duration: progressBar.animationDuration } } } - Row { + Row { anchors.centerIn: parent Text { text: progressBar.labelValue @@ -76,7 +74,7 @@ Item { } } - Behavior on labelValue { + Behavior on labelValue { NumberAnimation { duration: progressBar.animationDuration } diff --git a/src/qml/main.qml b/src/qml/main.qml index 85c1778..268fc46 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -30,18 +30,16 @@ ApplicationWindow { anchors.fill: parent hoverEnabled: true - onPressed: (mouse) => { + onPressed: mouse => { clickPos = Qt.point(mouse.x, mouse.y); } - onMouseXChanged: (mouse) => { + onMouseXChanged: mouse => { if (mouse.buttons === Qt.LeftButton) mainWindow.x += (mouse.x - clickPos.x); - } - onMouseYChanged: (mouse) => { + onMouseYChanged: mouse => { if (mouse.buttons === Qt.LeftButton) mainWindow.y += (mouse.y - clickPos.y); - } } @@ -52,9 +50,9 @@ ApplicationWindow { anchors.bottom: parent.bottom anchors.left: parent.left onFolderClick: { - backend.open_folder(settingsPage.saveLocation) + backend.open_folder(settingsPage.saveLocation); } - onSidebarButtonClicked: (index) => { + onSidebarButtonClicked: index => { stackLayout.currentIndex = index; } } @@ -70,17 +68,17 @@ ApplicationWindow { } ConvertPage { - onConvertRequested: (urls) => { - backend.urls = urls - backend.start() - stackLayout.currentIndex = 2 + onConvertRequested: urls => { + backend.urls = urls; + backend.start(); + stackLayout.currentIndex = 2; } } SettingsPage { id: settingsPage - onThemeChanged: (state) => { + onThemeChanged: state => { isLightTheme = !state; } } @@ -103,7 +101,7 @@ ApplicationWindow { enabled: mainWindow.visible function onFinish() { - timer.start() + timer.start(); } } @@ -115,7 +113,7 @@ ApplicationWindow { repeat: false onTriggered: { timer.stop(); - stackLayout.currentIndex = 0 + stackLayout.currentIndex = 0; } } } From 2fbe488f508c49a42dec4dfa3a6018461dc423bc Mon Sep 17 00:00:00 2001 From: Al-Taie Date: Mon, 31 Jul 2023 00:14:41 +0300 Subject: [PATCH 6/7] fix formatting issues --- src/domain/backend.py | 4 +--- src/qml/components/ConvertPage.qml | 2 +- src/qml/components/SideBar.qml | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/domain/backend.py b/src/domain/backend.py index e639d4f..903da42 100644 --- a/src/domain/backend.py +++ b/src/domain/backend.py @@ -63,7 +63,6 @@ def run(self, *args: Any, **kwargs: Any) -> Any: app_config: AppConfig = CaseSensitiveConfigParser.read_config() config = Config( - *args, urls_or_paths=self.urls, playlist_items="", verbose=False, @@ -85,13 +84,12 @@ def run(self, *args: Any, **kwargs: Any) -> Any: output_sample=0, output_formats=app_config.get_output_formats(), output_dir=app_config.save_location.replace("file:///", ""), - **kwargs, ) return farrigh(config) - @staticmethod @Slot(str) + @staticmethod def open_folder(path: str) -> None: if system() == "Windows": from os import startfile # type: ignore[attr-defined] diff --git a/src/qml/components/ConvertPage.qml b/src/qml/components/ConvertPage.qml index a84813b..88cf802 100644 --- a/src/qml/components/ConvertPage.qml +++ b/src/qml/components/ConvertPage.qml @@ -8,7 +8,7 @@ import "convert" Rectangle { id: root //audioUrls must be in jsonstring format - signal convertRequested(list urls) + signal convertRequested(list urls) color: theme.background diff --git a/src/qml/components/SideBar.qml b/src/qml/components/SideBar.qml index 806f27b..6e0b247 100644 --- a/src/qml/components/SideBar.qml +++ b/src/qml/components/SideBar.qml @@ -6,7 +6,7 @@ Rectangle { property int index: 0 signal sidebarButtonClicked(int index) - signal folderClick + signal folderClick() color: theme.primary width: 80 From 8140632ead5470408c190f7aee4b02f84acd9fa0 Mon Sep 17 00:00:00 2001 From: yshalsager Date: Mon, 31 Jul 2023 11:37:49 +0300 Subject: [PATCH 7/7] chore(dev): Disable qmlformat and qmllint pre-commit hooks Signed-off-by: yshalsager --- .pre-commit-config.yaml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 69669dd..83e9aa9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ # https://pre-commit.com -ci: - skip: [qmlformat, qmllint] +#ci: +# skip: [qmlformat, qmllint] default_install_hook_types: [ commit-msg, pre-commit ] default_stages: [ commit, manual ] fail_fast: true @@ -67,21 +67,21 @@ repos: rev: 'v1.4.1' # Use the sha / tag you want to point at hooks: - id: mypy - - repo: local - hooks: - - id: qmlformat - name: qmlformat - entry: qmlformat -i - pass_filenames: true - require_serial: true - language: system - types: [ text ] - files: ^.*\.qml$ - - id: qmllint - name: qmllint - entry: qmllint - pass_filenames: true - require_serial: true - language: system - types: [ text ] - files: ^.*\.qml$ \ No newline at end of file +# - repo: local +# hooks: +# - id: qmlformat +# name: qmlformat +# entry: qmlformat -i +# pass_filenames: true +# require_serial: true +# language: system +# types: [ text ] +# files: ^.*\.qml$ +# - id: qmllint +# name: qmllint +# entry: qmllint +# pass_filenames: true +# require_serial: true +# language: system +# types: [ text ] +# files: ^.*\.qml$ \ No newline at end of file