-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmainwindow.cpp
More file actions
981 lines (775 loc) · 31.5 KB
/
mainwindow.cpp
File metadata and controls
981 lines (775 loc) · 31.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "QFileDialog"
#include "QProcess"
#include "QStandardPaths"
#include "QJsonDocument"
#include "QJsonObject"
#include "QJsonArray"
#include "QToolTip"
#include "QHelpEvent"
#include "QSettings"
#include "QMessageBox"
#include <QDesktopServices>
#include <QUrl>
#include <QStyleFactory>
//QSettings default valuess
double defaultSizeLimit = 50;
int defaultIndexSizeType = 5;
QString defaultVideoFolder = QDir::homePath();
QString defaultOutputFolder = "";
int defaultIntIndex = 0;
//Default window values
int windowHeight;
int windowWidth;
bool isExtended = false;
//Dynamic dependencies pathing
QString ffmpegPath = "ffmpeg";
QString ffprobePath = "ffprobe";
// Used to save data between the different compression jobs (retrieving data, pass 1, etc)
struct LogInfo {
QString overrideMessage = ""; // if set, will override
QString l1 = "--Status--";
QString fileName = "temp.mp4";
QString fileIndex = "";
QString fileCount = "";
QString step = ""; //Retrieving video data | Pass 1 | Pass 2
QString targetSize = "";
QString encoder = "lib264";
QString ffmpegOutput = "";
};
//Stores all of the jobs required to do ( each compression has 3)
QList<VideoJob> videoJobs;
//Current used LogInfo
LogInfo currentLog;
//Number of videos that needs to be compressed
int totalVideosCompressing;
//To prevent false positive with how ffmpeg outputs updates for the progress bar
// if the new value is smaller than the old one, it skips
int oldProgress;
//When the Abort button is pressed, this is changed and the current jobs spots it and aborts
bool abortPressed = false;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("GUIVideoCompressor by Math");
this->setWindowIcon(QIcon(":/icons/resources/icons/GUIVideoCompressorLogo.png"));
windowHeight = this->height();
windowWidth = this->width();
//Prevents the window from being resized by the user, only works on windows
this->setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, true);
this->setWindowFlag(Qt::WindowMaximizeButtonHint, false);
this->statusBar()->setSizeGripEnabled(false);
//Bibux ok
this->setFixedSize(this->size());
//Sets the different themes from the user's system
ui->comboBox_theme->addItems(QStyleFactory::keys());
//Enable the custom events for the thumbnails preview
ui->videoList->viewport()->installEventFilter(this);
QCoreApplication::setOrganizationName("MathMoth"); // me :)
QCoreApplication::setApplicationName("GUIVideoCompressor");
//Settings setup
QSettings settings;
if (settings.contains("inputFolder")){
defaultVideoFolder = settings.value("inputFolder").toString();
}
if (settings.contains("outputFolder")){
defaultOutputFolder = settings.value("outputFolder").toString();
}
if (settings.contains("sizeLimit")){
QString t = settings.value("sizeLimit").toString();
if (t.toDouble()){
defaultSizeLimit = t.toDouble();
}
}
if (settings.contains("sizeLimitType")){
QString t = settings.value("sizeLimitType").toString();
if (t.toInt()){
defaultIndexSizeType = t.toInt();
}
}
if (settings.contains("themeIndex")){
QString t = settings.value("themeIndex").toString();
if (t.toInt()){
defaultIntIndex = t.toInt();
}
}else{
//Sets the default style/theme to Fusion
int storedIndex = 0;
for (int i = 0 ; i < ui->comboBox_theme->count() ; i ++){
if (ui->comboBox_theme->itemText(i) == "Fusion"){
storedIndex = i;
break;
}
}
defaultIntIndex = storedIndex;
}
//Saves the default settings values
ui->lineEdit_outputFolder->setText(defaultOutputFolder);
ui->comboBox_finalSizeType->setCurrentIndex(defaultIndexSizeType);
ui->doubleSpinBox_finalSize->setValue(defaultSizeLimit);
ui->comboBox_theme->setCurrentIndex(defaultIntIndex);
//Sets the theme
changeAppStyle(ui->comboBox_theme->currentText());
//Creates if needed the folder for ffmpeg
QDir parentFolder(QCoreApplication::applicationDirPath());
parentFolder.cdUp();
if (!parentFolder.exists("deps")){
parentFolder.mkdir("deps");
}
//Check if the dependencies are ok before starting up
refreshDependencies();
}
MainWindow::~MainWindow()
{
delete ui;
}
//Adding clips to the selection
void MainWindow::on_button_AddVideos_pressed()
{
QString videoFolder = defaultVideoFolder;
QSettings settings;
//Checking if the video path is set, otherwise the chosen path will be the home folder
if (!QDir(defaultVideoFolder).exists()){
videoFolder = QDir::homePath();
}
//Open the dialog and select all of the files
QStringList files = QFileDialog::getOpenFileNames(
this,
"Select mp4 files",
videoFolder,
"*.mp4"
);
if (files.isEmpty())return;
//Save into the settings the path of the folder
QString folderPath = QFileInfo(files.at(0)).absolutePath();
settings.setValue("inputFolder",folderPath);
//Creates if needed and store the cache folder for the thumbnails
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
QString thumbnailDir = cachePath + "/thumbnails";
QDir().mkpath(thumbnailDir);
//Also creates the cache path for the pass encoding files of ffmpeg
QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/tempffmpeg/");
//Add every file into the list, without any thumbnail
for (const QString &filePath : files.toVector()){
QFileInfo fileName(filePath);
// Check if the list doesn't already contain the element
bool alreadyExisting = false;
for (int i = 0 ; i < ui->videoList->count(); i ++){
QListWidgetItem *item = ui->videoList->item(i);
if (item->data(Qt::UserRole) == filePath){
alreadyExisting = true;
break;
}
}
if (alreadyExisting)break;
//Creating/Adding the item to the list
QListWidgetItem *item = new QListWidgetItem();
item->setText(fileName.fileName());
item->setToolTip(filePath);
item->setData(Qt::UserRole,filePath);
ui->videoList->addItem(item);
//Setting the icon to the generated thumbnail
QString tempThumbnailPath = thumbnailDir + "/" + fileName.fileName() + ".jpg";
//Creates the process to get the thumbnail
QProcess *process = new QProcess(this);
//Called when the process is finished, to add the generated thumbnail to the item as an icon
connect(process, &QProcess::finished, this, [=](int exitCode, QProcess::ExitStatus status){
if (exitCode == 0) {
QPixmap pixmap(tempThumbnailPath);
pixmap = pixmap.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation);
item->setIcon(QIcon(pixmap));
//Add the image to the tooltip using html thingie
QString tooltip =
"<html>"
"<b>"+item->toolTip()+"<b>" +
"<img src=\"file:///" + tempThumbnailPath + "\" width=\"400\"/><br/>"+
"</html>";
item->setToolTip(tooltip);
// QFile thumbnail(tempThumbnailPath);
// if (thumbnail.exists())
// thumbnail.moveToTrash();
} else {
currentLog.overrideMessage = "FFMPEG error while retrieving the thumbnail of the videos !";
}
process->deleteLater();
});
//FFMPEG command to generate the thumbnail
QStringList args = {
"-y",
"-i", filePath,
"-ss", "00:00:01",
"-vframes", "1",
tempThumbnailPath
};
process->start(ffmpegPath, args);
}
}
//Removes the selected items of the list
void MainWindow::on_button_removeSelectedVideo_pressed()
{
for(const QListWidgetItem *item : ui->videoList->selectedItems()){
delete ui->videoList->takeItem(ui->videoList->row(item));
}
}
//select the output folder
void MainWindow::on_toolButton_choseOutputFolder_pressed()
{
QString outputFolder = defaultOutputFolder;
QSettings settings;
if (!QDir(defaultOutputFolder).exists()){
outputFolder = QDir::homePath();
}
QString chosenDir = QFileDialog::getExistingDirectory(
this,
tr("$Choose the output folder of the files"),
outputFolder, // Default folder, TODO from config
QFileDialog::ShowDirsOnly
);
//Saves the value to the settings
settings.setValue("outputFolder",chosenDir);
//Changes the ui display of the output folder
ui->lineEdit_outputFolder->setText(chosenDir);
}
// to enable/disable the change output fps spot
void MainWindow::on_checkBox_outputFPS_pressed()
{
ui->spinBox_outputFPS->setEnabled(!ui->checkBox_outputFPS->isChecked());
}
//Handles the information display + progressBar
void MainWindow::updateInfo(){
//currentLog is the current LogInfo containing all of the current compression information
LogInfo c = currentLog;
QString txt = "";
if (abortPressed){
c.overrideMessage = "ABORTED";
}
//if there is no override message, default info display stuct
if (c.overrideMessage == ""){
txt = c.l1
+ "\nFile Name : "+c.fileName
+"\n File "+c.fileIndex + "/"+c.fileCount
+ "\nCurrent Step : "+c.step
+ "\nTarget Size : "+c.targetSize;
}else{
txt = c.overrideMessage;
}
ui->label_log->setText(txt);
//ui->label_log_ffmpeg->setText( (c.overrideMessage == "" ? "FFMPEG Output :\n"+c.ffmpegOutput : ""));
//Updating progress bar, god help me
//This gets the progress, dividing each actions
int passNumber = (c.step == "Pass 2" ? 2 : 1);
int totalFiles = c.fileCount.toInt();
int fileIndex0 = c.fileIndex.toInt() - 1;
double stepValue = 100.0 / (totalFiles * 2);
double progress = (fileIndex0 * 2 + (passNumber - 1)) * stepValue;
//Adding the local progress to the progress
if (videoJobs.isEmpty())return;
//Then adds to the progress the relative progress of the current task
VideoJob currentJob = CurrentVJ();
float totalVideoDuration = currentJob.videoInfo.duration; // in seconds
QString ffmpegOutput = c.ffmpegOutput;
// frame= 123 fps= 60 q=28.0 size= 1024kB time=00:00:05.12 bitrate=1638.4kbits/s speed=1.00x
//Checks if it succesfully retrieved the time from the ffmpeg output
bool gotTime = false;
float relativeProgress = 0;
QStringList indexes = ffmpegOutput.split("time=");
//Gets the time value from the output, ex time=00:00:05.12
if (ffmpegOutput.startsWith("frame") && indexes.count() >= 2){
ffmpegOutput = indexes[1];
QStringList spaceIndexes = ffmpegOutput.split(" ");
if (spaceIndexes.count() >= 1){
ffmpegOutput = spaceIndexes[0];
gotTime = true;
}
}
//Splits every values from time=00:00:05.12 and converts all to seconds
if (gotTime){
QStringList parts = ffmpegOutput.split(':');
if (parts.size() == 3){
int hour = parts[0].toInt();
int minutes = parts[1].toInt();
double seconds = parts[2].toDouble();
seconds += minutes* 60 + hour * 3600;
if (seconds > 2){ // to prevent false positives
//Calculates the relative progress, from the current time of the video and the total duration
float mult = 100/totalVideoDuration;
double diff = (seconds*mult) / (totalVideoDuration*mult);
//Adds the calculated relative progress
relativeProgress = (diff * 100) / (c.fileCount.toInt() * 2);
}
}
}
//Adds the relative progress
int totalProgress = progress + relativeProgress;
//Prevents progress from going backwards, ok maybe because the calculation is weird and i got fed up from this
totalProgress = (totalProgress < oldProgress ? oldProgress : totalProgress);
oldProgress = totalProgress;
if (abortPressed) totalProgress = 0;
ui->progressBar->setValue(static_cast<int>(totalProgress));
}
//Changes the theme of the app + sets the background of the progress bar
void MainWindow::changeAppStyle(QString style){
qApp->setStyle(style);
//Sets the progress bar style depending on the theme
QString progressBarDark = R"(
QProgressBar {
height: 22px;
border: 1px solid #444;
border-radius: 6px;
background-color: #1e1e1e;
color: white;
text-align: center;
}
QProgressBar::chunk {
background-color: qlineargradient(
x1:0, y1:0, x2:1, y2:0,
stop:0 #4fc3f7, stop:1 #0288d1
);
border-radius: 6px;
}
)";
QString progressBarLight = R"(
QProgressBar {
height: 22px;
border: 1px solid #bbb;
border-radius: 6px;
background-color: #ffffff;
color: black;
text-align: center;
}
QProgressBar::chunk {
background-color: qlineargradient(
x1:0, y1:0, x2:1, y2:0,
stop:0 #64b5f6, stop:1 #1976d2
);
border-radius: 6px;
}
)";
bool isDarkTheme =
qApp->palette().color(QPalette::Window).lightness() < 128;
ui->progressBar->setStyleSheet(isDarkTheme ? progressBarDark : progressBarLight);
}
//Starting the compression of all of the selected videos if any
void MainWindow::on_pushButton_compress_pressed()
{
//Saving the settings of the size thingie
QSettings settings;
settings.setValue("sizeLimit",ui->doubleSpinBox_finalSize->value());
settings.setValue("sizeLimitType",ui->comboBox_finalSizeType->currentIndex());
//Resets the overriden message of the log
currentLog.overrideMessage = "";
//Before compressing, checking if there is any value in the list + if the output folder exists + checks
if (ui->videoList->count() == 0){
currentLog.overrideMessage = "No video selected !";
updateInfo();
return;
}
QDir outputFolder(ui->lineEdit_outputFolder->text());
if (ui->lineEdit_outputFolder->text() == "" || !outputFolder.exists()){
currentLog.overrideMessage = "The output folder is invalid !";
updateInfo();
return;
}
if (ui->doubleSpinBox_finalSize->value() <= 0){
currentLog.overrideMessage = "You must set a valid fps limit !";
updateInfo();
return;
}
if (ffmpegPath == "" || ffprobePath == ""){
currentLog.overrideMessage = "Can't detect a valid ffmpeg or ffprobe instance, check the settings to set them !";
updateInfo();
return;
}
//Resets dynamic variables
oldProgress = 0;
abortPressed = false;
videoJobs.clear();
currentLog.overrideMessage = "Preparing videos...";
updateInfo();
// Starts by getting every videoinformation using ffprobe
for (int i = 0 ; i < ui->videoList->count(); i ++){
QListWidgetItem *item = ui->videoList->item(i);
QString filePath = item->data(Qt::ItemDataRole::UserRole).toString();
QString fileName = QFileInfo(filePath).fileName();
//Check if no output path is the same
bool isTheSame = false;
for(int j = 0; j < videoJobs.count() ; j++){
if (videoJobs.at(j).outputPath.endsWith(fileName)){
isTheSame = true;
}
}
VideoJob videoJob;
videoJob.inputPath = filePath;
//If outputPath is the same, add i at the end of the name of the file
// ex : clip.mp4 => clip1.mp4, or clip2.mp4
QString outputPath;
if (isTheSame){
QString temp = QFileInfo(videoJob.inputPath).fileName();
temp.chop(4);
temp += QString::number(i) + ".mp4";
outputPath = outputFolder.absolutePath() + "/" + temp;
}else{
outputPath = outputFolder.absolutePath() + "/" + QFileInfo(videoJob.inputPath).fileName();
}
videoJob.outputPath = outputPath;
videoJobs.append(videoJob);
}
totalVideosCompressing = videoJobs.count();
currentLog.fileCount = QString::number(videoJobs.count());
//Starts the Pass1
job1StartLoop();
}
// Starts or continue the loop of compression for all videos
void MainWindow::job1StartLoop(){
//This function is called in a loop until no video is left
if (videoJobs.isEmpty()){
currentLog.overrideMessage = "Succesfully compressed all of the files !";
updateInfo();
ui->progressBar->setValue(100);
return;
}
job2GetVideoData();
}
//Starts by retrieving all video data using ffprobe, such as framerate, duration, width + height, etc
void MainWindow::job2GetVideoData(){
QProcess *ffprobe = new QProcess(this);
//this part is called after the original command has been executed
connect(ffprobe, &QProcess::finished, this, [=](int exitCode, QProcess::ExitStatus){
qDebug() << "job2 - finished ffprobe with exit code ";
qDebug() << exitCode;
if (exitCode != 0) {
qWarning() << "Error FFMPEG";
qWarning () << exitCode;
currentLog.overrideMessage = "Error FFMPEG : "+ QString::number(exitCode) + "\n"+ffprobe->readAllStandardError();
updateInfo();
ffprobe->deleteLater();
return;
}
//Gets the output of the command
QByteArray output = ffprobe->readAllStandardOutput();
QJsonDocument doc = QJsonDocument::fromJson(output);
//Tries to retrieve the data from the generated json
if (doc.isObject()){
QJsonObject root = doc.object();
// Duration
if (root.contains("format")) {
QJsonObject format = root["format"].toObject();
if (format.contains("duration"))
CurrentVJ().videoInfo.duration = format["duration"].toString().toDouble();
}
// Video stream
if (root.contains("streams")) {
QJsonArray streams = root["streams"].toArray();
for (const QJsonValue& val : streams) {
QJsonObject stream = val.toObject();
QString codecType = stream["codec_type"].toString();
if (codecType == "video") {
// FPS
QString fpsStr = stream["avg_frame_rate"].toString(); //ex: "30000/1001"
QStringList parts = fpsStr.split('/');
if (parts.size() == 2)
CurrentVJ().videoInfo.fps = parts[0].toDouble() / parts[1].toDouble();
CurrentVJ().videoInfo.width = stream["width"].toInt();
CurrentVJ().videoInfo.height = stream["height"].toInt();
break;
}
}
// Audio stream
for (const QJsonValue& val : streams) {
QJsonObject stream = val.toObject();
QString codecType = stream["codec_type"].toString();
if (codecType == "audio") {
CurrentVJ().videoInfo.audioBitrateKbps = stream["bit_rate"].toString().toInt() / 1000; //kbps
break;
}
}
}}
//here we got the full videoInfo set, we start the job3 - Pass1
job3Pass1();
});
//Resets the log
currentLog.overrideMessage = "";
currentLog.fileName = QFileInfo(CurrentVJ().inputPath).fileName();
currentLog.fileIndex = QString::number(totalVideosCompressing - videoJobs.count() + 1);
currentLog.step = "Retrieving video data";
currentLog.targetSize = QString::number(ui->doubleSpinBox_finalSize->value() ) + ui->comboBox_finalSizeType->currentText();
updateInfo();
//Sets up the ffprobe command and executes it
QStringList args;
args << "-v" << "error"
<< "-show_entries"
<< "format=duration:stream=index,codec_type,avg_frame_rate,width,height,bit_rate,sample_rate,channels"
<< "-of" << "json"
<< CurrentVJ().inputPath;
ffprobe->start(ffprobePath, args);
}
//Starts the Pass1 of the compression, after getting all of the video data
//Pass 1 = Scanning the file and making a file with all of the data
void MainWindow::job3Pass1(){
currentLog.step = "Pass 1";
updateInfo();
QProcess *ffmpegPass1 = new QProcess(this);
//Called after the original command has been executed and when an update is sent by ffmpeg
//in order to update the progress bar
connect(ffmpegPass1, &QProcess::readyReadStandardError, this, [=](){
if (abortPressed){
ffmpegPass1->deleteLater();
updateInfo();
return;
}
currentLog.ffmpegOutput = ffmpegPass1->readAllStandardError();
updateInfo();
});
//Called once the command has been executed and completed, so that it starts the job4 - Pass2
connect(ffmpegPass1, &QProcess::finished, this, [=](int exitCode, QProcess::ExitStatus){
qDebug() << "finished ffprobe with exit code";
qDebug() << exitCode;
if (exitCode != 0) {
qWarning() << "Error FFMPEG";
qWarning() << exitCode;
currentLog.overrideMessage = "Error FFMPEG : "+ QString::number(exitCode) + "\n"+ffmpegPass1->readAllStandardError();
updateInfo();
ffmpegPass1->deleteLater();
return;
}
job3Pass2();
//Pass2 !!
});
//QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/tempffmpeg/"
//Making the Pass1
//Maths to get the final video bitrate + other stuff kms
//Sets the path for the pass1 log, in the cache folder of cutie
QString passlogPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/tempffmpeg/ffmpeg_pass-0.log";
QDir().mkpath(passlogPath);
//Pretty sure it can handle any type of number, even for realle huge sizes lol
long long unsigned targetBitSize;
long long unsigned videoBitratebps;
double size = ui->doubleSpinBox_finalSize->value();
QString type = ui->comboBox_finalSizeType->currentText();
//Sorry abaienst but not switch for QString ? pretty sure it doesn't change anything once compiled
//converts the target size to bits
if (type == "b"){
targetBitSize = size;
}else if (type == "B"){
targetBitSize = size * 8;
}else if (type == "Kb"){
targetBitSize = size * 1000;
}else if (type == "KB"){
targetBitSize = size * 1000 * 8;
}else if (type == "Mb"){
targetBitSize = size * 1000000;
}else if (type == "MB"){
targetBitSize = size * 1000000 * 8;
}else if (type == "Gb"){
targetBitSize = size * 1000000000;
}else if (type == "GB"){
targetBitSize = size * 1000000000 * 8;
}
//Calculating the video bitratebps in order to re encode the video with this limit of bits for the video
//While keeping the audio untouched
long long unsigned video_bits = targetBitSize - (CurrentVJ().videoInfo.audioBitrateKbps * 1000 * CurrentVJ().videoInfo.duration);
videoBitratebps = video_bits/CurrentVJ().videoInfo.duration;
long long unsigned video_bitrate_kbps = std::roundl(videoBitratebps / 1000);
CurrentVJ().videoInfo.videoBitrateKbps = video_bitrate_kbps;
double targetFPS = CurrentVJ().videoInfo.fps;
//if we needs to reencode with custom output fps
if (ui->spinBox_outputFPS->isEnabled() && ui->spinBox_outputFPS->value() < targetFPS){
targetFPS = ui->spinBox_outputFPS->value();
CurrentVJ().videoInfo.fps = targetFPS;
}
//Sets up the ffmpeg command and executes it
QStringList args;
args << "-y" << "-i" << CurrentVJ().inputPath
<< "-r" << QString::number(targetFPS) << "-c:v" << "libx264"
<< "-b:v" << QString::number(video_bitrate_kbps)+"k"
<< "-pass"<<"1"<< "-passlogfile" <<passlogPath << "-an"
<< "-f"<<"null"<< "NUL";
ffmpegPass1->start(ffmpegPath, args);
}
//job3 - Pass2 !!
//Reencode the video with the calculated bitrate for the video, custom fps if any, and data from the
//generated passlog of ffmpeg
void MainWindow::job3Pass2(){
currentLog.step = "Pass 2";
QProcess *ffmpegPass2 = new QProcess(this);
connect(ffmpegPass2, &QProcess::readyReadStandardError, this, [=](){
if (abortPressed){
ffmpegPass2->deleteLater();
updateInfo();
return;
}
currentLog.ffmpegOutput = ffmpegPass2->readAllStandardError();
updateInfo();
});
connect(ffmpegPass2, &QProcess::finished, this, [=](int exitCode, QProcess::ExitStatus){
qDebug() << "finished ffmpegPass2 with exit code";
qDebug() << exitCode;
if (exitCode != 0) {
qWarning() << "Error FFMPEG";
qWarning() << exitCode;
currentLog.overrideMessage = "Error FFMPEG : "+ QString::number(exitCode) + "\n"+ffmpegPass2->readAllStandardError();
updateInfo();
ffmpegPass2->deleteLater();
return;
}
//Because the compression is done, removes the video from the "queue"
//just a list lol kill me pardon
videoJobs.takeFirst();
//Restarts the loop
job1StartLoop();
});
//Retrieves the generated passlog from ffmpeg to use it in the args
QString passlogPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/tempffmpeg/ffmpeg_pass-0.log";
//Generates the ffmpeg args with the data
//it took way too much times to do and debug, worst thing ever 10/10
QStringList args;
args << "-y" << "-i" << CurrentVJ().inputPath
<< "-r" << QString::number(CurrentVJ().videoInfo.fps)
<<"-c:v" <<"libx264" <<"-b:v"
<< QString::number(CurrentVJ().videoInfo.videoBitrateKbps)+"k"
<< "-pass" << "2" << "-passlogfile" << passlogPath<< "-c:a"
<<"aac"<<"-b:a" << QString::number(CurrentVJ().videoInfo.audioBitrateKbps) + "k"
<< "-preset" <<"slow"<<"-profile:v"<<"high"
<<"-level"<<"4.2" << CurrentVJ().outputPath;
ffmpegPass2->start(ffmpegPath, args);
}
//To get the current VideoJob, couldn't correctly gives the same stuct through
//the different functions while changing its data due to skill issue
//TEMP
VideoJob& MainWindow::CurrentVJ(){
return videoJobs[0];
}
//to abort the whole thing
void MainWindow::on_pushButton_abort_pressed()
{
abortPressed = true;
oldProgress = 0;
ui->progressBar->setValue(0);
}
//To clear the output folder when new files are being compressed
void MainWindow::on_radioButton_clearOutputFolder_pressed()
{
if (ui->radioButton_clearOutputFolder->isChecked())return;
QMessageBox msgBox;
msgBox.setText("By checking this, every mp4 files in the selected directory will be deleted when compressing.");
msgBox.exec();
ui->radioButton_clearOutputFolder->setChecked(true);
}
//Opens the output folder if set
void MainWindow::on_pushButton_open_output_folder_pressed()
{
QSettings settings;
QDesktopServices::openUrl(
QUrl::fromLocalFile(settings.value("outputFolder").toString()));
}
//Extends the window to display the dependencies settings, pretty neat i know i know i'm the best
void MainWindow::on_pushButton_open_settings_pressed()
{
int extendValue = (isExtended ? -230 : 230);
isExtended = !isExtended;
this->setFixedSize(windowWidth, windowHeight + extendValue);
resize(windowWidth, windowHeight + extendValue);
windowHeight += extendValue;
this->ui->pushButton_open_settings->setText(isExtended ? "Close Dependencies Settings":"Open Dependencies Settings");
}
//To open the dependencies folder
void MainWindow::on_pushButton_open_deps_folder_pressed()
{
QDir parentFolder(QCoreApplication::applicationDirPath());
parentFolder.cdUp();
QDesktopServices::openUrl(
QUrl::fromLocalFile(parentFolder.absolutePath() + "/deps/"));
}
//Tests if ffmpeg & ffprobe are set, either locally in the deps folder
// or installed globally on the computer via the env var
void MainWindow::refreshDependencies(){
//Default name for linux of ffmpeg & ffprobe
QString ffmpegFileName = "ffmpeg";
QString ffprobeFileName = "ffprobe";
//if win user, adds the extension of the
#ifdef Q_OS_WIN
ffmpegFileName += ".exe";
ffprobeFileName += ".exe";
#endif
//Checking if ffmpeg & ffprobe are detected on the deps folder, if so, try to start them
QDir parentFolder(QCoreApplication::applicationDirPath());
parentFolder.cdUp();
parentFolder.cd("deps");
bool ffmpegLocal = testProcess(parentFolder.absolutePath() + "/"+ffmpegFileName);
bool ffprobeLocal = testProcess(parentFolder.absolutePath() + "/"+ffprobeFileName);
//sets the path for the deps if they are detected
ffmpegPath = ffmpegLocal ? parentFolder.absolutePath() + "/"+ffmpegFileName : "";
ffprobePath = ffprobeLocal ? parentFolder.absolutePath() + "/"+ffprobeFileName : "";
bool ffmpegGlobal = false;
bool ffprobeGlobal = false;
// if not detected locally, try to load the globally
if (!ffmpegLocal){
if (testProcess("ffmpeg")){
ffmpegGlobal = true;
ffmpegPath = "ffmpeg";
}
}
if (!ffprobeLocal){
if (testProcess("ffprobe")){
ffprobeGlobal = true;
ffprobePath = "ffprobe";
}
}
//Sets the info value
QString ffmpegButtonLabel = "FFMPEG is not detected !";
QString ffprobeButtonLabel = "FFPROBE is not detected !";
if (ffmpegLocal){
ffmpegButtonLabel = "FFMPEG is detected !\n(locally)";
}else if (ffmpegGlobal){
ffmpegButtonLabel = "FFMPEG is detected !\n(globally)";
}
if (ffprobeLocal){
ffprobeButtonLabel = "FFPROBE is detected !\n(locally)";
}else if (ffprobeGlobal){
ffprobeButtonLabel = "FFPROBE is detected !\n(globally)";
}
this->ui->label_ffmpeg_detection->setText(ffmpegButtonLabel);
this->ui->label_ffprobe_detection->setText(ffprobeButtonLabel);
//All deps detected
if (ffmpegPath != "" && ffprobePath != ""){
this->ui->label_dependencies_check->setText("All dependencies loaded !");
this->ui->label_dependencies_check->setStyleSheet("color: rgb(126, 255, 169);");
}else{
//Not all of the deps detected
this->ui->label_dependencies_check->setText("Couldn't load dependencies\ncheck the settings for more info");
this->ui->label_dependencies_check->setStyleSheet("color: rgb(255, 120, 120);");
}
}
//test the given process, can only be ffmpeg & ffprobe
//Does it sync, even tho it freezes the app, we're sure that it works well
bool MainWindow::testProcess(QString processName){
QProcess process;
process.start(processName, { "-version" });
if (!process.waitForStarted(2000)) {
//qDebug() << "it doesn't exists ";
return false;
}
process.waitForFinished(2000);
if (process.exitStatus() == QProcess::NormalExit &&
process.exitCode() == 0) {
//qDebug() << "it exists !";
return true;
}
//qDebug() << "it doesn't exists ";
return false;
}
//Refresh dependencies button
void MainWindow::on_pushButton_3_pressed()
{
refreshDependencies();
}
//To change the theme because sexy
void MainWindow::on_comboBox_theme_currentIndexChanged(int index)
{
QSettings settings;
changeAppStyle(ui->comboBox_theme->currentText());
settings.setValue("themeIndex",index);
}