Skip to content

Commit

Permalink
feat(ui): add scene management struct !
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanCarollo committed Mar 16, 2024
1 parent a8fe0b8 commit 44030fd
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 67 deletions.
1 change: 1 addition & 0 deletions downloader/Downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def __init__(self):
super().__init__()

def startDownload(self, transcriberContext):
# TODO: add an args that automatically overwrite the file
command = ["youtube-dl", "-o", self.downloadPath, transcriberContext.url]
DLog.goodlog("Starting Youtube Download")
subprocess.run(command)
Expand Down
Binary file modified export/video.mp4
Binary file not shown.
3 changes: 1 addition & 2 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

if __name__ == "__main__":
app = QApplication(sys.argv)
window = SceneManager.getInstance()
window.show()
SceneManager.getInstance().show()
sys.exit(app.exec_())


6 changes: 3 additions & 3 deletions tests/TranscriberTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ def myEndedFunctionDamour(result):
contextTranscriber = IATranscriberContext("small")
myManager = TranscriberManager()
myManager.setCurrentAI(ListAI.WHISPER, contextTranscriber)
myManager.transcribe(tsContext.audioPath, myEndedFunctionDamour)
return myManager.transcribe(tsContext.audioPath)

class TranscriberTest(unittest.TestCase):
def test_transcriber(self):
tsContext = TranscriberContext.getContextWithVideoAndAudio()
runTranscribtion(tsContext)
self.assertIsNotNone(tsContext.transcribeText)
result = runTranscribtion(tsContext)
self.assertIsNotNone(result)

if __name__ == '__main__':
unittest.main()
7 changes: 3 additions & 4 deletions transcriber/TranscriberManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@ def setCurrentAI(self, aiName, aiContext):
else:
DLog.error("Error : AI not in list")


def transcribe(self, filename, callback):

def transcribe(self, filename):
if self.currentAI != None:
self.state = ListState.TRANSCRIBING
self.currentAI.transcribe(filename, callback)
result = self.currentAI.transcribe(filename)
self.state = ListState.IDLE
return result


9 changes: 2 additions & 7 deletions transcriber/WhisperTranscriber.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,5 @@ def changeModel(self, model):
self.model = model
self.loadModel()

# transcribe
def transcribe(self, filename, callback):
try:
result = self.model.transcribe(filename)
callback(result)
except:
DLog.error(f"Error : When module transcribe")
def transcribe(self, filename):
return self.model.transcribe(filename)
5 changes: 2 additions & 3 deletions transcription/TranscriptionController.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self):
# We may need to thread that in the future
# The callback of this function take a value in parameter so, in this callback, you will need
# to put something like trContext.text = result["text"]
def startTranscription(self, input: str, callback):
def startTranscription(self, input: str):
transcriberContext = TranscriberContext(input)

downloaderManager = DownloaderManager()
Expand All @@ -39,8 +39,7 @@ def startTranscription(self, input: str, callback):
converterCoordinator = ConversionCoordinator.getConversionCoordinator()
conversionResult = converterCoordinator.convert(transcriberContext)

self.trManager.transcribe(transcriberContext.audioPath, callback)
pass
return self.trManager.transcribe(transcriberContext.audioPath)

# For some reason, this function worked as a static even without the @staticmethod... so i searched and lol
# https://stackoverflow.com/questions/52831534/why-is-a-method-of-a-python-class-declared-without-self-and-without-decorators
Expand Down
36 changes: 18 additions & 18 deletions window/SceneManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
from window.scene.MainScene import MainScene
from window.scene.VideoTranscribedScene import VideoTranscribedScene

# To be clearer, i consider in this object, every QWidget who looks like a scene are a scene
# Here, we do a system like UnityScene for example, every scene have a lifecycle but cannot be instanciated
# with different params

# Here, the "SceneManager", is just the QMainWindow, but with capabilities to manage scenes, the name
# isn't good imo, i don't have other idea for now
class SceneManager(QMainWindow):
instance = None

Expand All @@ -21,21 +21,21 @@ def __init__(self):
central_widget.setLayout(layout)
self.stacked_widget = QStackedWidget()
layout.addWidget(self.stacked_widget)

# Setup every scene
self.scenes = {
# https://www.youtube.com/watch?v=z9w6tO4d90U
"mainScene": MainScene(),
"videoTranscribedScene": VideoTranscribedScene(),
}

for scene in self.scenes.values():
self.stacked_widget.addWidget(scene)

def goToScene(self, sceneName: str):
self.stacked_widget.currentWidget().endScene()
self.stacked_widget.setCurrentWidget(self.scenes[sceneName])
self.stacked_widget.currentWidget().startScene()
self.stacked_widget.addWidget(MainScene())

def newScene(self, scene):
self.removeScene()
self.stacked_widget.addWidget(scene)
self.stacked_widget.setCurrentWidget(scene)

def removeScene(self):
# Remove other scenes from memory but keep 1 scene in the stacked widget, in that manner, we can
# keep the thread from the widget before the first one !
if self.stacked_widget.count() > 1:
removed_widget = self.stacked_widget.currentWidget()
self.stacked_widget.removeWidget(removed_widget)
removed_widget.deleteLater()

@staticmethod
def getInstance():
Expand Down
21 changes: 21 additions & 0 deletions window/scene/LoadingScene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QDesktopWidget
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QDragEnterEvent, QDropEvent
from Utils import Utils
import threading


# This is the loading scene called after the MainScene, this will need some improvements
class LoadingScene(QWidget):
def __init__(self):
super().__init__()
# Create a QLabel widget
label = QLabel("Loading scene")
# Create a layout and add the label to it
layout = QVBoxLayout()
layout.addWidget(label)
# Set the layout for the widget
self.setLayout(layout)



4 changes: 1 addition & 3 deletions window/scene/MainScene.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import threading


# ---------------------------------------------------------------------------- #
# Window #
# ---------------------------------------------------------------------------- #
# The MainScene (the first one !)
class MainScene(QWidget):
def __init__(self):
super().__init__()
Expand Down
24 changes: 13 additions & 11 deletions window/scene/VideoTranscribedScene.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,31 @@
from PyQt5.QtGui import QFont
from window.scene.videoTranscribedScene.ParentWidget import ParentWidget


# The Video Transcribed Scene
class VideoTranscribedScene(QWidget):
# TODO : retire this stupid None
def __init__(self, result=None, video_path=None):
def __init__(self, result, video_path):
super().__init__()
print(video_path)
# TODO : RETIRE THIS
#self.result = result
self.result = [{'id': 0, 'seek': 0, 'start': 0.0, 'end': 5.6000000000000005, 'text': " We're introducing three revolutionary products of this class.", 'tokens': [50364, 492, 434, 15424, 1045, 22687, 3383, 295, 341, 1508, 13, 50644], 'temperature': 0.0, 'avg_logprob': -0.40571454366048176, 'compression_ratio': 1.17, 'no_speech_prob': 0.002564801834523678}, {'id': 1, 'seek': 0, 'start': 5.6000000000000005, 'end': 15.1, 'text': ' The first one is a widescreen iPod with touch controls.', 'tokens': [50644, 440, 700, 472, 307, 257, 21516, 66, 1492, 5180, 378, 365, 2557, 9003, 13, 51119], 'temperature': 0.0, 'avg_logprob': -0.40571454366048176, 'compression_ratio': 1.17, 'no_speech_prob': 0.002564801834523678}, {'id': 2, 'seek': 1510, 'start': 15.1, 'end': 40.1, 'text': ' The second is a revolutionary mobile camera.', 'tokens': [50364, 440, 1150, 307, 257, 22687, 6013, 2799, 13, 51614], 'temperature': 0.0, 'avg_logprob': -0.5153783957163492, 'compression_ratio': 0.8461538461538461, 'no_speech_prob': 0.009670082479715347}, {'id': 3, 'seek': 4010, 'start': 40.1, 'end': 50.1, 'text': ' And the third is a breakthrough Internet communications device.', 'tokens': [50364, 400, 264, 2636, 307, 257, 22397, 7703, 15163, 4302, 13, 50864], 'temperature': 0.0, 'avg_logprob': -0.22980055937895905, 'compression_ratio': 1.3130434782608695, 'no_speech_prob': 0.09138037264347076}, {'id': 4, 'seek': 4010, 'start': 50.1, 'end': 60.1, 'text': ' These are not three separate devices. This is one device.', 'tokens': [50864, 1981, 366, 406, 1045, 4994, 5759, 13, 639, 307, 472, 4302, 13, 51364], 'temperature': 0.0, 'avg_logprob': -0.22980055937895905, 'compression_ratio': 1.3130434782608695, 'no_speech_prob': 0.09138037264347076}, {'id': 5, 'seek': 4010, 'start': 60.1, 'end': 66.1, 'text': ' And we are calling it iPhone.', 'tokens': [51364, 400, 321, 366, 5141, 309, 7252, 13, 51664], 'temperature': 0.0, 'avg_logprob': -0.22980055937895905, 'compression_ratio': 1.3130434782608695, 'no_speech_prob': 0.09138037264347076}, {'id': 6, 'seek': 6610, 'start': 66.1, 'end': 74.1, 'text': ' Today Apple is going to reinvent the phone.', 'tokens': [50364, 2692, 6373, 307, 516, 281, 33477, 264, 2593, 13, 50764], 'temperature': 0.0, 'avg_logprob': -0.22366726398468018, 'compression_ratio': 0.8431372549019608, 'no_speech_prob': 0.20343056321144104}]

self.result = result
self.video_path = video_path

# Créer et afficher le widget parent
self.parentWidget = ParentWidget(self.result, self.video_path)
self.button = QPushButton("RETURN MAIN MENU")
self.button.clicked.connect(self.goToMainScene)
self.button.setStyleSheet(f"background-color: #FFA1A1; border-radius: 20px; padding: 10px 20px;")


# Mise en page
layout = QVBoxLayout()
layout.addWidget(self.parentWidget)
layout.addWidget(self.button)
self.setLayout(layout)


def startScene(self):
def goToMainScene(self):
print("go to main scene")
from window.SceneManager import SceneManager
from window.scene.MainScene import MainScene
SceneManager.getInstance().newScene(MainScene())
pass

def endScene(self):
pass
45 changes: 29 additions & 16 deletions window/scene/mainScene/DragAndDrop.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QDesktopWidget
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtCore import Qt, QUrl, QThread, pyqtSignal
from PyQt5.QtGui import QDragEnterEvent, QDropEvent
from Utils import Utils
from transcription.TranscriptionController import *
from window.scene.VideoTranscribedScene import *
from window.scene.LoadingScene import *
import threading

class TranscribeVideoThread(QThread):
# Here, we signal that the pyqtSignal take 1 arg and it is a list
finished_signal = pyqtSignal(list)

def __init__(self, url):
super().__init__()
self.url = url

def run(self):
result = TranscriptionController.getInstance().startTranscription(self.url);
self.finished_signal.emit(result["segments"])

class DragAndDrop(QWidget):
def __init__(self):
Expand Down Expand Up @@ -39,7 +51,7 @@ def __init__(self):
self.inputText.setPlaceholderText("https://www.youtube.com/watch?v=dQw4w9WgXcQ")

self.button = QPushButton("GO")
self.button.clicked.connect(lambda: self.buttonClicked(self.inputText.text()))
self.button.clicked.connect(lambda: self.launchTranscription(self.inputText.text()))
self.button.setStyleSheet(f"background-color: #FFA1A1; border-radius: 20px; padding: 10px 20px; color: {self.textColor};")

self.inputHLayout.addWidget(self.inputText)
Expand Down Expand Up @@ -84,20 +96,21 @@ def dropEvent(self, event: QDropEvent):
# ---------------------------------------------------------------------------- #
# Event #
# ---------------------------------------------------------------------------- #
def buttonClicked(self, url):
self.url = url
self.nextPage()

def nextPage(self):
# Cause of circular import, i can import SceneManager at the module level (and it's normal lol)
# So i do this x)
print("go to the next page")
# This is the function called when we click on the button
def launchTranscription(self, url):
self.transcribe_thread = TranscribeVideoThread(url)
# Connect the callback to the finished signal !
self.transcribe_thread.finished_signal.connect(self.nextPage)
self.transcribe_thread.start()

# To avoid circular import here lol
from window.SceneManager import SceneManager
SceneManager.getInstance().newScene(LoadingScene())


def setResultCallback(result):
segmentTranscription = result["segments"]
SceneManager.getInstance().goToScene("videoTranscribedScene")

thread = threading.Thread(target=TranscriptionController.getInstance().startTranscription, args=(self.url, setResultCallback))
thread.start()
def nextPage(self, result: list):
# Cause of circular import, i can't import SceneManager at the module level (and it's normal lol)
# So i do this x)
from window.SceneManager import SceneManager
SceneManager.getInstance().newScene(VideoTranscribedScene(result, Utils.getDirectoryAbsolutePath() + "/export/video.mp4"))

0 comments on commit 44030fd

Please sign in to comment.