RaidenVideoRipper
/
editorwindow.cpp
1027 строк · 28.8 Кб
1#include "editorwindow.h"
2#include <QApplication>
3#include <QMainWindow>
4#include <QMediaPlayer>
5#include <QVideoWidget>
6#include <QFileDialog>
7#include <QAction>
8#include <QSlider>
9#include <QToolBar>
10#include <QLabel>
11#include <QMenuBar>
12#include <QMessageBox>
13#include <QSize>
14#include <QStandardPaths>
15#include <QStyle>
16#include <QStatusBar>
17#include <QScreen>
18#include <QShortcut>
19#include <QPushButton>
20#include <QDial>
21#include <QString>
22#include <QLibraryInfo>
23#include <videoprocessor.h>
24#include "constants.h"
25#include "utils.h"
26
27EditorWindow::EditorWindow()
28{
29qRegisterMetaType<OutputFormat>("OutputFormat");
30auto style = this->style();
31playIcon = QIcon::fromTheme(
32"media-playback-start.png",
33style->standardIcon(QStyle::SP_MediaPlay)
34);
35pauseIcon = QIcon::fromTheme(
36"media-playback-pause.png",
37style->standardIcon(QStyle::SP_MediaPause)
38);
39stopIcon = QIcon::fromTheme(
40"media-playback-stop.png",
41style->standardIcon(QStyle::SP_MediaStop)
42);
43playbackState = std::make_tuple(QMediaPlayer::StoppedState, 0);
44userForcedStop = false;
45state = IDLE;
46audioOutput = nullptr;
47
48auto mp4 = OutputFormat(
49outputFormatMp4,
50tr("Video (mp4)"),
51"mp4",
52settings.value(outputFormatIsSelectedKey(QString(outputFormatMp4)), true).value<bool>()
53);
54
55auto gif = OutputFormat(
56outputFormatGif,
57"Gif",
58"gif",
59settings.value(outputFormatIsSelectedKey(QString(outputFormatGif)), true).value<bool>()
60);
61
62auto webm = OutputFormat(
63outputFormatWebm,
64"WebM",
65"webm",
66settings.value(outputFormatIsSelectedKey(QString(outputFormatWebm)), false).value<bool>()
67);
68
69auto mp3 = OutputFormat(
70outputFormatMp3,
71tr("Audio (mp3)"),
72"mp3",
73settings.value(outputFormatIsSelectedKey(QString(outputFormatMp3)), false).value<bool>()
74);
75
76successfulRunsCount = settings.value(successfulRunsCountKey).value<int>();
77
78outputFormats.push_back(mp4);
79outputFormats.push_back(gif);
80outputFormats.push_back(webm);
81outputFormats.push_back(mp3);
82
83createLayout();
84initializePlayer();
85setupActions();
86updateWindowTitle();
87openArgumentsFileIfNeeded();
88updateDurationLabel();
89progressBarWindow = nullptr;
90restoreWindowSize();
91}
92
93void EditorWindow::showDonateWindowIfNeeded()
94{
95successfulRunsCount += 1;
96auto outputText = tr("Success!");
97if (successfulRunsCount >= donateSuccessfulRunsCount) {
98outputText = tr("<b>Success!</b><br>If you like this application, please consider a donation:<br><a href=\"https://www.donationalerts.com/r/demensdeum\">https://www.donationalerts.com/r/demensdeum</a>");
99successfulRunsCount = 0;
100}
101showAlert(
102tr("Wow!"),
103outputText
104);
105settings.setValue(successfulRunsCountKey, successfulRunsCount);
106}
107
108QString EditorWindow::outputFormatIsSelectedKey(QString identifier)
109{
110return identifier + isSelectedKeyExtension;
111}
112
113void EditorWindow::restoreWindowSize()
114{
115restoreGeometry(settings.value(mainWindowGeometryKey).toByteArray());
116restoreState(settings.value(mainWindowStateKey).toByteArray());
117}
118
119void EditorWindow::openArgumentsFileIfNeeded()
120{
121auto application = QCoreApplication::instance();
122auto arguments = application->arguments();
123if (arguments.length() == 2) {
124auto filepath = arguments.at(1);
125auto url = QUrl::fromLocalFile(filepath);
126handleOpenFile(url);
127}
128}
129
130void EditorWindow::setupActions()
131{
132openAction = new QAction(tr("Open..."), this);
133openAction->setShortcut(QKeySequence::Open);
134connect(openAction, &QAction::triggered, this, &EditorWindow::open);
135
136auto exitAction = new QAction(tr("Exit"), this);
137connect(exitAction, &QAction::triggered, this, &QMainWindow::close);
138
139auto fileMenu = menuBar()->addMenu(tr("&File"));
140fileMenu->addAction(openAction);
141fileMenu->addAction(exitAction);
142
143auto optionsMenu = menuBar()->addMenu(tr("&Options"));
144
145auto isPreviewChecked = settings.value(previewCheckboxStateKey, true).value<bool>();
146previewCheckboxAction = new QAction(tr("Preview"), this);
147previewCheckboxAction->setCheckable(true);
148previewCheckboxAction->setChecked(isPreviewChecked);
149connect(
150previewCheckboxAction,
151&QAction::triggered,
152this,
153&EditorWindow::previewCheckboxStateChange
154);
155optionsMenu->addAction(previewCheckboxAction);
156
157for (auto&& outputFormat : outputFormats) {
158QAction *outputFormatCheckboxAction = new QAction(outputFormat.title, this);
159outputFormatCheckboxAction->setCheckable(true);
160outputFormatCheckboxAction->setChecked(outputFormat.isSelected);
161outputFormatCheckboxAction->setData(QVariant::fromValue(&outputFormat));
162connect(
163outputFormatCheckboxAction,
164&QAction::triggered,
165this,
166&EditorWindow::outputFormatCheckboxStateChanged
167);
168optionsMenu->addAction(outputFormatCheckboxAction);
169}
170
171auto aboutMenu = menuBar()->addMenu(tr("&About"));
172
173auto aboutApplicationAction = new QAction(tr("About Raiden Video Ripper"), this);
174connect(aboutApplicationAction, &QAction::triggered, qApp, [this] { showAboutApplication(); });
175aboutMenu->addAction(aboutApplicationAction);
176
177auto aboutQtAction = new QAction(tr("About &Qt"), this);
178connect(aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
179aboutMenu->addAction(aboutQtAction);
180}
181
182void EditorWindow::updateDurationLabel()
183{
184auto format = QString("hh:mm:ss.zzz");
185auto start = timelineIndicator->getStartValue();
186auto playback = timelineIndicator->getPlaybackValue();
187auto end = timelineIndicator->getEndValue();
188
189auto startTime = QTime(0, 0);
190startTime = startTime.addMSecs(start);
191auto startFormattedTime = startTime.toString(format);
192
193auto endTime = QTime(0, 0);
194endTime = endTime.addMSecs(end);
195auto endFormattedTime = endTime.toString(format);
196
197auto playbackTime = QTime(0, 0);
198playbackTime = playbackTime.addMSecs(playback);
199auto durationFormattedTime = playbackTime.toString(format);
200
201auto text = QString("%1 - %2 - %3")
202.arg(
203startFormattedTime,
204durationFormattedTime,
205endFormattedTime
206);
207
208durationLabel->setText(text);
209}
210
211void EditorWindow::cancelInProgress()
212{
213qDebug() << "cancel in progress";
214state = CANCELLED;
215progressBarWindow.value()->close();
216videoProcessor.value()->cancel();
217videoProcessorProgressPoller.value()->stop();
218RaidenVideoRipper::Utils::clearQueue(currentOutputFormats);
219}
220
221void EditorWindow::cleanupBeforeExit()
222{
223settings.setValue(mainWindowGeometryKey, saveGeometry());
224settings.setValue(mainWindowStateKey, saveState());
225
226switch (state) {
227case IDLE:
228return;
229case FILE_PROCESSING:
230cancelInProgress();
231break;
232case CANCELLED:
233qDebug() << "Cleanup in cancelled state, wut?";
234return;
235case EnumCount:
236qDebug() << "Cleanup in Enum Count state, u nuts????";
237return;
238}
239}
240
241void EditorWindow::closeEvent(QCloseEvent *event)
242{
243cleanupBeforeExit();
244QMainWindow::closeEvent(event);
245}
246
247void EditorWindow::createLayout()
248{
249auto bottomPrimaryHorizontalPanel = new QWidget();
250bottomPrimaryHorizontalPanel->setFixedHeight(primaryPanelHeight);
251
252playbackButton = new QPushButton();
253playbackButton->setFixedWidth(40);
254playbackButton->setIcon(playIcon);
255connect(playbackButton, &QPushButton::clicked, this, &EditorWindow::playToggleButtonClicked);
256
257stopButton = new QPushButton();
258stopButton->setFixedWidth(40);
259stopButton->setIcon(stopIcon);
260connect(stopButton, &QPushButton::clicked, this, &EditorWindow::stopButtonClicked);
261
262auto bottomPrimaryHorizontalPanelLayout = new QHBoxLayout(bottomPrimaryHorizontalPanel);
263bottomPrimaryHorizontalPanelLayout->setContentsMargins(0, 0, 0, 0);
264bottomPrimaryHorizontalPanelLayout->addWidget(playbackButton);
265bottomPrimaryHorizontalPanelLayout->addWidget(stopButton);
266
267volumeSlider = new QSlider(Qt::Horizontal, this);
268volumeSlider->setMinimum(0);
269volumeSlider->setMaximum(100);
270volumeSlider->setToolTip("Volume");
271volumeSlider->setFixedWidth(80);
272auto savedVolume = settings.value(volumeSettingsKey, volumeSlider->maximum()).value<qint64>();
273volumeSlider->setValue(savedVolume);
274connect(volumeSlider, &QSlider::valueChanged, this, &EditorWindow::volumeChanged);
275bottomPrimaryHorizontalPanelLayout->addWidget(volumeSlider);
276this->volumeChanged(savedVolume);
277
278auto bottomSecondaryPanel = new QWidget();
279bottomSecondaryPanel->setFixedHeight(secondaryPanelHeight);
280
281auto startButton = new QPushButton(tr("START"));
282startButton->setFixedWidth(160);
283startButton->setFixedHeight(30);
284startButton->setStyleSheet(startButtonStyleSheet);
285connect(startButton, &QPushButton::clicked, this, &EditorWindow::startButtonClicked);
286
287durationLabel = new QLabel("00:00:00.00 - 00:00:00.00 - 00:00:00.00");
288durationLabel->setAlignment(Qt::AlignCenter);
289
290auto emptySpace = new QWidget();
291emptySpace->setFixedWidth(1);
292
293auto leftEmptySpace = new QWidget();
294leftEmptySpace->setFixedWidth(1);
295
296auto rightEmptySpace = new QWidget();
297rightEmptySpace->setFixedWidth(1);
298
299auto bottomSecondaryHorizontalPanelLayout = new QHBoxLayout(bottomSecondaryPanel);
300bottomSecondaryHorizontalPanelLayout->setContentsMargins(0, 0, 0, 0);
301bottomSecondaryHorizontalPanelLayout->addWidget(leftEmptySpace);
302bottomSecondaryHorizontalPanelLayout->addWidget(playbackButton);
303bottomSecondaryHorizontalPanelLayout->addWidget(stopButton);
304bottomSecondaryHorizontalPanelLayout->addWidget(emptySpace);
305bottomSecondaryHorizontalPanelLayout->addWidget(volumeSlider);
306bottomSecondaryHorizontalPanelLayout->addWidget(durationLabel);
307bottomSecondaryHorizontalPanelLayout->addWidget(startButton);
308bottomSecondaryHorizontalPanelLayout->addWidget(rightEmptySpace);
309
310videoWidget = new VideoWidget(this);
311videoWidget->setAspectRatioMode(Qt::KeepAspectRatio);
312videoWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
313connect(
314videoWidget,
315&VideoWidget::dragDidDropUrl,
316this,
317&EditorWindow::handleDropUrl
318);
319
320auto layout = new QVBoxLayout();
321layout->setContentsMargins(0, 0, 0, 0);
322layout->addWidget(videoWidget);
323
324timelineIndicator = new TimelineWidget(this, 100);
325
326connect(
327timelineIndicator,
328&TimelineWidget::startSliderDraggingStarted,
329this,
330&EditorWindow::startSliderDraggingStarted
331);
332
333connect(
334timelineIndicator,
335&TimelineWidget::startSliderDraggingFinished,
336this,
337&EditorWindow::startSliderDraggingFinished
338);
339
340connect(
341timelineIndicator,
342&TimelineWidget::playbackSliderDraggingStarted,
343this,
344&EditorWindow::playbackSliderDraggingStarted
345);
346
347connect(
348timelineIndicator,
349&TimelineWidget::playbackSliderDraggingFinished,
350this,
351&EditorWindow::playbackSliderDraggingFinished
352);
353
354connect(
355timelineIndicator,
356&TimelineWidget::endSliderDraggingStarted,
357this,
358&EditorWindow::endSliderDraggingStarted
359);
360
361connect(
362timelineIndicator,
363&TimelineWidget::endSliderDraggingFinished,
364this,
365&EditorWindow::endSliderDraggingFinished
366);
367
368connect(
369timelineIndicator,
370&TimelineWidget::startValueChanged,
371this,
372&EditorWindow::startPositionSliderMoved
373);
374
375connect(
376timelineIndicator,
377&TimelineWidget::playbackValueChanged,
378this,
379&EditorWindow::playbackSliderMoved
380);
381
382connect(
383timelineIndicator,
384&TimelineWidget::endValueChanged,
385this,
386&EditorWindow::endPositionSliderMoved
387);
388
389auto isPreviewChecked = settings.value(previewCheckboxStateKey, true).value<bool>();
390timelineIndicator->setFreeplayMode(!isPreviewChecked);
391
392auto horizontalIndicators = new QWidget();
393auto horizontalIndicatorsLayout = new QHBoxLayout(horizontalIndicators);
394horizontalIndicatorsLayout->setContentsMargins(0, 0, 0, 0);
395horizontalIndicatorsLayout->addWidget(timelineIndicator);
396layout->addWidget(horizontalIndicators);
397layout->addWidget(bottomSecondaryPanel);
398
399auto widget = new QWidget();
400widget->setLayout(layout);
401setCentralWidget(widget);
402
403QRect availableGeometry = QApplication::primaryScreen()->availableGeometry();
404auto width = availableGeometry.width() * 0.7038043478260869;
405auto height = width * 0.5625;
406
407resize(width, height);
408}
409
410void EditorWindow::playbackSliderDraggingStarted()
411{
412auto state = player->playbackState();
413
414switch (state) {
415case QMediaPlayer::PlayingState:
416case QMediaPlayer::PausedState:
417playbackState = std::make_tuple(player->playbackState(), player->position());
418player->pause();
419return;
420case QMediaPlayer::StoppedState:
421return;
422}
423}
424
425void EditorWindow::playbackSliderDraggingFinished()
426{
427auto state = std::get<0>(playbackState);
428
429switch (state) {
430case QMediaPlayer::PlayingState:
431player->play();
432break;
433case QMediaPlayer::PausedState:
434player->pause();
435break;
436default:
437break;
438}
439
440playbackState = std::make_tuple(QMediaPlayer::StoppedState, 0);
441}
442
443void EditorWindow::handleDropUrl(QUrl url)
444{
445handleOpenFile(url);
446}
447
448void EditorWindow::startPositionSliderMoved(qint64 position) {
449if (!previewCheckboxAction->isChecked()) return;
450player->setPosition(position);
451this->updateDurationLabel();
452}
453
454void EditorWindow::endPositionSliderMoved(qint64 position) {
455if (!previewCheckboxAction->isChecked()) return;
456player->setPosition(position);
457this->updateDurationLabel();
458}
459
460void EditorWindow::initializePlayer()
461{
462audioOutput = new QAudioOutput();
463player = new QMediaPlayer();
464player->setAudioOutput(audioOutput);
465
466connect(
467player,
468&QMediaPlayer::positionChanged,
469this,
470&EditorWindow::playbackChanged
471);
472connect(
473player,
474&QMediaPlayer::playbackStateChanged,
475this,
476&EditorWindow::playbackStateChanged
477);
478connect(
479player,
480&QMediaPlayer::errorOccurred,
481this,
482&EditorWindow::handlePlayerError
483);
484connect(
485player,
486&QMediaPlayer::durationChanged,
487this,
488&EditorWindow::durationLoaded
489);
490player->setVideoOutput(videoWidget);
491}
492
493void EditorWindow::durationLoaded(qint64 duration)
494{
495fileDuration = duration;
496player->play();
497volumeChanged(volumeSlider->value());
498timelineIndicator->setMaximumValue(duration);
499timelineIndicator->setStartValue(0);
500timelineIndicator->setPlaybackValue(0);
501timelineIndicator->setEndValue(duration);
502updateWindowTitle();
503}
504
505void EditorWindow::playbackStateChanged(QMediaPlayer::PlaybackState state)
506{
507updateButtons(state);
508
509if (previewCheckboxAction->isChecked()) {
510if (state == QMediaPlayer::StoppedState && userForcedStop == false) {
511player->play();
512}
513}
514}
515
516void EditorWindow::handleLeftKeyPress()
517{
518timelineIndicator->moveLeft();
519}
520
521void EditorWindow::handleRightKeyPress()
522{
523timelineIndicator->moveRight();
524}
525
526void EditorWindow::outputFormatCheckboxStateChanged(bool isChecked)
527{
528auto sender = static_cast<QAction *>(QObject::sender());
529OutputFormat *outputFormat = qvariant_cast<OutputFormat *>(sender->data());
530outputFormat->isSelected = isChecked;
531auto key = outputFormatIsSelectedKey(outputFormat->identifier);
532settings.setValue(key, isChecked);
533}
534
535void EditorWindow::previewCheckboxStateChange(bool isChecked)
536{
537timelineIndicator->setFreeplayMode(!isChecked);
538settings.setValue(previewCheckboxStateKey, isChecked);
539update();
540}
541
542void EditorWindow::playToggleButtonClicked() {
543userForcedStop = false;
544togglePlayback();
545}
546
547void EditorWindow::stopButtonClicked() {
548userForcedStop = true;
549player->stop();
550}
551
552void EditorWindow::togglePlayback() {
553userForcedStop = false;
554if (player->playbackState() == QMediaPlayer::PlayingState) {
555player->pause();
556}
557else {
558player->play();
559}
560}
561
562void EditorWindow::showAboutApplication()
563{
564const auto copyright =
565tr("Copyright © 2023 <a href=\"https://www.demensdeum.com/\">Ilia Prokhorov</a>")
566.arg(applicationName);
567const auto license =
568QStringLiteral("<a href=\"https://opensource.org/license/mit/\">MIT License</a>");
569const auto sourceCode =
570QStringLiteral("<a href=\"https://github.com/demensdeum/RaidenVideoRipper/\">https://github.com/demensdeum/RaidenVideoRipper</a>");
571
572QMessageBox::about(
573this,
574tr("About %1").arg(qApp->applicationName()),
575tr(
576"<h1>Version %1 %2</h1>"
577"<p><a href=\"%3\">%1</a> is an open-source project designed for video editing and format conversion. It's built using Qt 6 (Qt Creator) and allows you to trim and convert videos to MP4 or GIF formats.</p>"
578"<small><p>%4</p>"
579"<p>Licensed under the %5</p>"
580"<p>This program proudly uses the following projects:<ul>"
581"<li><a href=\"https://www.qt.io/\">Qt</a> application and UI framework</li>"
582"<li><a href=\"https://www.ffmpeg.org/\">FFmpeg</a> multimedia format and codec libraries</li>"
583"<li><a href=\"https://github.com/demensdeum/Dullahan-FFmpeg/\">Dullahan-FFmpeg</a> FFmpeg CLI as shared library </li>"
584"</ul></p>"
585"<p>The source code used to build this program can be downloaded from "
586"%6</p>"
587"This program is distributed in the hope that it will be useful, "
588"but WITHOUT ANY WARRANTY; without even the implied warranty of "
589"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.</small>"
590)
591.arg(
592qApp->applicationName(),
593qApp->applicationVersion(),
594"https://github.com/demensdeum/RaidenVideoRipper",
595copyright,
596license,
597sourceCode
598)
599);
600}
601
602void EditorWindow::savePlaybackState()
603{
604auto state = player->playbackState();
605switch (state) {
606case QMediaPlayer::PlayingState:
607case QMediaPlayer::PausedState:
608playbackState = std::make_tuple(player->playbackState(), player->position());
609player->pause();
610return;
611default:
612return;
613}
614}
615
616void EditorWindow::restorePlaybackState()
617{
618auto state = std::get<0>(playbackState);
619auto position = std::get<1>(playbackState);
620
621if (
622position >= timelineIndicator->getStartValue()
623&&
624position <= timelineIndicator->getEndValue()
625)
626{
627player->setPosition(position);
628}
629else {
630player->setPosition(timelineIndicator->getStartValue());
631}
632switch (state) {
633case QMediaPlayer::PlayingState:
634player->play();
635break;
636case QMediaPlayer::PausedState:
637player->pause();
638break;
639default:
640break;
641}
642
643playbackState = std::make_tuple(QMediaPlayer::StoppedState, 0);
644}
645
646void EditorWindow::startSliderDraggingStarted()
647{
648savePlaybackState();
649}
650
651void EditorWindow::startSliderDraggingFinished()
652{
653restorePlaybackState();
654}
655
656void EditorWindow::endSliderDraggingStarted()
657{
658savePlaybackState();
659}
660
661void EditorWindow::endSliderDraggingFinished()
662{
663restorePlaybackState();
664}
665
666void EditorWindow::open()
667{
668QFileDialog fileDialog(this);
669
670QString moviesLocation = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
671auto path = settings.value(previousWorkingPathKey, moviesLocation).value<QString>();
672
673fileDialog.setDirectory(path);
674
675if (fileDialog.exec() == QDialog::Accepted)
676{
677QUrl url = fileDialog.selectedUrls().at(0);
678handleOpenFile(url);
679}
680}
681
682void EditorWindow::handleOpenFile(QUrl url)
683{
684switch (state) {
685case IDLE:
686break;
687case FILE_PROCESSING:
688raiseProgressWindowToUser();
689qDebug() << "Open file while in processing state, user nuts?";
690return;
691case CANCELLED:
692case EnumCount:
693qDebug() << "Wtf? Trying to use wrong state for file open!!!";
694return;
695}
696
697auto incomingFilepath = QDir::toNativeSeparators(url.toLocalFile());
698
699if (incomingFilepath == filePath) {
700qDebug() << "User trying to open same file, are they nuts?";
701return;
702}
703filePath = incomingFilepath;
704
705auto filePathDirectory = QFileInfo(filePath).absolutePath();
706settings.setValue(previousWorkingPathKey, filePathDirectory);
707fileDuration.reset();
708player->setSource(url);
709}
710
711void EditorWindow::updateWindowTitle()
712{
713auto applicationTitle = QString(applicationName) + " " + QString(applicationVersion);
714auto title = applicationTitle;
715if (!filePath.isEmpty()) {
716title = QFileInfo(filePath).fileName() + " - " + applicationTitle;
717}
718this->setWindowTitle(title);
719}
720
721std::vector<OutputFormat> EditorWindow::getSelectedOutputFormats()
722{
723std::vector<OutputFormat> selectedFormats;
724
725std::copy_if(outputFormats.begin(), outputFormats.end(), std::back_inserter(selectedFormats),
726[](const OutputFormat& format) {
727return format.isSelected;
728});
729
730return selectedFormats;
731}
732
733void EditorWindow::raiseProgressWindowToUser()
734{
735progressBarWindow.value()->raise();
736progressBarWindow.value()->activateWindow();
737}
738
739void EditorWindow::startButtonClicked()
740{
741auto selectedOutputFormats = getSelectedOutputFormats();
742switch (state) {
743case IDLE:
744if (filePath.isEmpty()) {
745showAlert(tr("WUT!"), tr("Open file first!"));
746return;
747}
748else if (selectedOutputFormats.empty())
749{
750showAlert(tr("WUT!"), tr("Select output formats checkboxes first!"));
751return;
752}
753break;
754
755case FILE_PROCESSING:
756raiseProgressWindowToUser();
757qDebug() << "Start pressed in file processing state, user nuts?";
758return;
759
760case CANCELLED:
761qDebug() << "Start pressed in cancelled state, wtf?";
762return;
763
764case EnumCount:
765break;
766}
767
768for (auto&& outputFormat : selectedOutputFormats) {
769currentOutputFormats.push(outputFormat);
770}
771
772state = FILE_PROCESSING;
773
774cut();
775}
776
777void EditorWindow::volumeChanged(qint64 position)
778{
779auto volume = static_cast<float>(position) / static_cast<float>(volumeSlider->maximum());
780if (audioOutput) {
781audioOutput->setVolume(volume);
782}
783settings.setValue(volumeSettingsKey, position);
784}
785
786void EditorWindow::showProgressbarWindow(QString text)
787{
788if (!fileDuration.has_value()) {
789qDebug() << "showProgressbar - no duration!";
790return;
791}
792progressBarWindow = new ProgressBarWindow(text);
793connect(
794progressBarWindow.value(),
795&ProgressBarWindow::cancelButtonPressed,
796this,
797&EditorWindow::cancelInProgress
798);
799progressBarWindow.value()->show();
800
801if (videoProcessorProgressPoller.has_value()) {
802videoProcessorProgressPoller.value()->stop();
803}
804videoProcessorProgressPoller = new VideoProcessorProgressPoller(fileDuration.value());
805connect(
806videoProcessorProgressPoller.value(),
807&VideoProcessorProgressPoller::didPollProgress,
808this,
809&EditorWindow::didPollProgress
810);
811}
812
813void EditorWindow::cut()
814{
815if (!fileDuration.has_value()) {
816qDebug() << "No file duration";
817return;
818}
819
820if (!currentOutputFormats.empty()) {
821auto front = currentOutputFormats.front();
822currentOutputFormat = new OutputFormat(
823front.identifier,
824front.title,
825front.extension,
826front.isSelected
827);
828currentOutputFormats.pop();
829}
830else {
831qDebug() << "Cut called with empty currentOutputFormats, wtf?";
832return;
833}
834
835int startPosition = timelineIndicator->getStartValue();
836int endPosition = timelineIndicator->getEndValue();
837
838if (endPosition < startPosition)
839{
840state = IDLE;
841return;
842}
843
844QString outputVideoPath;
845switch (state)
846{
847case IDLE:
848qDebug() << "Internal error, IDLE state!";
849return;
850
851case FILE_PROCESSING:
852outputVideoPath = filePath + outputFileSuffix + "." + currentOutputFormat.value()->extension;
853break;
854
855case CANCELLED:
856qDebug() << "Cut started in cancelled mode, wtf?";
857return;
858
859case EnumCount:
860qDebug() << "State is EnumCount, the hell?";
861return;
862}
863
864auto stateString = RaidenVideoRipper::Utils::capitalized(
865currentOutputFormat.value()->title
866);
867QString text = tr("Cutting ") + stateString + "...";
868showProgressbarWindow(text);
869
870videoProcessor = new VideoProcessor(
871startPosition,
872endPosition,
873filePath,
874outputVideoPath
875);
876videoProcessor.value()->setAutoDelete(true);
877
878connect(
879videoProcessor.value(),
880&VideoProcessor::videoProcessingDidFinish,
881this,
882&EditorWindow::convertingDidFinish
883);
884threadPool.start(videoProcessor.value());
885}
886
887void EditorWindow::didPollProgress(int progress)
888{
889progressBarWindow.value()->setProgress(progress);
890}
891
892void EditorWindow::showAlert(const QString &title, const QString &message)
893{
894QMessageBox messageBox;
895messageBox.setWindowTitle(title);
896messageBox.setText(message);
897messageBox.setIcon(QMessageBox::Information);
898messageBox.exec();
899}
900
901void EditorWindow::processNextOutputFormatOrFinish()
902{
903if (!currentOutputFormats.empty()) {
904cut();
905}
906else {
907switch (state) {
908case IDLE:
909return;
910case FILE_PROCESSING:
911state = IDLE;
912showDonateWindowIfNeeded();
913return;
914case CANCELLED:
915state = IDLE;
916return;
917case EnumCount:
918return;
919}
920}
921}
922
923void EditorWindow::convertingDidFinish(bool result)
924{
925videoProcessorProgressPoller.value()->stop();
926progressBarWindow.value()->close();
927
928qDebug("Process Finished");
929
930auto isSuccess = result == 0;
931
932switch (state) {
933case IDLE:
934break;
935case FILE_PROCESSING:
936if (isSuccess) {
937processNextOutputFormatOrFinish();
938}
939else {
940state = IDLE;
941showAlert(
942tr("Uhh!"),
943tr("Error while cutting! Result code: %1")
944.arg(
945QString::number(result)
946)
947);
948}
949break;
950
951case CANCELLED:
952processNextOutputFormatOrFinish();
953break;
954
955case EnumCount:
956break;
957}
958}
959
960void EditorWindow::playbackSliderMoved(qint64 position)
961{
962player->setPosition(position);
963this->updateDurationLabel();
964}
965
966void EditorWindow::playbackChanged(qint64 position)
967{
968auto sliderUpdate = [this] (int position) {
969timelineIndicator->blockSignals(true);
970timelineIndicator->setPlaybackValue(position);
971timelineIndicator->blockSignals(false);
972};
973
974if (player->isPlaying() && this->previewCheckboxAction->isChecked()) {
975auto startPosition = timelineIndicator->getStartValue();
976auto endPosition = timelineIndicator->getEndValue();
977if (position > endPosition) {
978player->setPosition(startPosition);
979}
980else if (position < startPosition) {
981player->setPosition(startPosition);
982}
983}
984sliderUpdate(player->position());
985updateDurationLabel();
986}
987
988void EditorWindow::ensureStopped()
989{
990if (player->playbackState() != QMediaPlayer::StoppedState)
991{
992player->stop();
993}
994}
995
996void EditorWindow::updateButtons(QMediaPlayer::PlaybackState state)
997{
998if (player->isPlaying()) {
999playbackButton->setIcon(pauseIcon);
1000}
1001else {
1002playbackButton->setIcon(playIcon);
1003}
1004stopButton->setEnabled(state != QMediaPlayer::StoppedState);
1005}
1006
1007void EditorWindow::showErrorMessage([[maybe_unused]]const QString &message)
1008{
1009auto previousErrorFilePath = std::get<0>(previousFileAndErrorState);
1010auto previousErrorText = std::get<1>(previousFileAndErrorState);
1011if (
1012previousErrorFilePath == filePath
1013&&
1014previousErrorText == message
1015)
1016{
1017return;
1018}
1019previousFileAndErrorState = std::make_tuple(filePath, message);
1020showAlert(tr("Uhh!"), message);
1021}
1022
1023void EditorWindow::handlePlayerError(QMediaPlayer::Error error, const QString &errorString)
1024{
1025Q_UNUSED(error);
1026showErrorMessage(errorString);
1027}
1028