mirror of
https://gitlab.com/kelteseth/ScreenPlay.git
synced 2024-09-18 16:32:33 +02:00
Rewrote create to be more modular
This commit is contained in:
parent
3c38d0593b
commit
ba4e9d2607
@ -122,5 +122,6 @@
|
|||||||
<file>qml/Create/Wizards/CreateWallpaper/Page_0.qml</file>
|
<file>qml/Create/Wizards/CreateWallpaper/Page_0.qml</file>
|
||||||
<file>qml/Create/Wizards/CreateWallpaper/Page_1.qml</file>
|
<file>qml/Create/Wizards/CreateWallpaper/Page_1.qml</file>
|
||||||
<file>qml/Create/Wizards/CreateWallpaper/Page_2.qml</file>
|
<file>qml/Create/Wizards/CreateWallpaper/Page_2.qml</file>
|
||||||
|
<file>qml/Create/Wizards/CreateWallpaper/NextButton.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -4,11 +4,11 @@ import QtQuick.Controls 2.2
|
|||||||
import QtQuick.Controls.Material 2.2
|
import QtQuick.Controls.Material 2.2
|
||||||
import Qt.labs.platform 1.0
|
import Qt.labs.platform 1.0
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
import net.aimber.create 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: createNew
|
id: createNew
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Component.onCompleted: state = "in"
|
|
||||||
state: "out"
|
state: "out"
|
||||||
|
|
||||||
property string filePath
|
property string filePath
|
||||||
@ -18,6 +18,20 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
triggeredOnStart: false
|
||||||
|
running: true
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
screenPlayCreate.createWallpaperStart(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
state = "in"
|
||||||
|
}
|
||||||
|
|
||||||
RectangularGlow {
|
RectangularGlow {
|
||||||
id: effect
|
id: effect
|
||||||
anchors {
|
anchors {
|
||||||
@ -70,6 +84,7 @@ Item {
|
|||||||
id: view
|
id: view
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: 0
|
currentIndex: 0
|
||||||
|
interactive: false
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -84,10 +99,24 @@ Item {
|
|||||||
topMargin: 0
|
topMargin: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
interactive: false
|
|
||||||
|
|
||||||
Page_0 {
|
Page_0 {
|
||||||
id: firstPage
|
id: page_0
|
||||||
|
onCanNextChanged: {
|
||||||
|
if (canNext) {
|
||||||
|
btnNext.state = "enabledPage0"
|
||||||
|
} else {
|
||||||
|
if (gifCreated) {
|
||||||
|
btnNext.state = "diabledPage0NoTitle"
|
||||||
|
} else {
|
||||||
|
btnNext.state = "diabledPage0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onGifCreatedChanged: {
|
||||||
|
if (gifCreated) {
|
||||||
|
btnNext.state = "diabledPage0NoTitle"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Page_1 {
|
Page_1 {
|
||||||
id: secondPage
|
id: secondPage
|
||||||
@ -148,7 +177,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
width: 160
|
width: childrenRect.width
|
||||||
height: 50
|
height: 50
|
||||||
spacing: 20
|
spacing: 20
|
||||||
anchors {
|
anchors {
|
||||||
@ -171,16 +200,14 @@ Item {
|
|||||||
view.setCurrentIndex(view.currentIndex - 1)
|
view.setCurrentIndex(view.currentIndex - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button {
|
NextButton {
|
||||||
text: qsTr("Next")
|
id: btnNext
|
||||||
Material.background: Material.Orange
|
state: "diabledPage0"
|
||||||
Material.foreground: "white"
|
|
||||||
|
|
||||||
icon.source: "qrc:/assets/icons/icon_arrow_right.svg"
|
|
||||||
icon.color: "white"
|
|
||||||
icon.width: 16
|
|
||||||
icon.height: 16
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
||||||
|
if (!page_0.canNext && !page_0.gifCreated )
|
||||||
|
return
|
||||||
|
|
||||||
if (view.currentIndex < view.count - 1)
|
if (view.currentIndex < view.count - 1)
|
||||||
view.setCurrentIndex(view.currentIndex + 1)
|
view.setCurrentIndex(view.currentIndex + 1)
|
||||||
}
|
}
|
||||||
|
62
ScreenPlay/qml/Create/Wizards/CreateWallpaper/NextButton.qml
Normal file
62
ScreenPlay/qml/Create/Wizards/CreateWallpaper/NextButton.qml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Controls.Material 2.2
|
||||||
|
import Qt.labs.platform 1.0
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: root
|
||||||
|
text: qsTr("Next")
|
||||||
|
Material.background: Material.Orange
|
||||||
|
Material.foreground: "white"
|
||||||
|
|
||||||
|
icon.source: "qrc:/assets/icons/icon_arrow_right.svg"
|
||||||
|
icon.color: "white"
|
||||||
|
icon.width: 16
|
||||||
|
icon.height: 16
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "enabled"
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "diabledPage0"
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
text: qsTr("Creating preview")
|
||||||
|
Material.background: Material.Grey
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "diabledPage0NoTitle"
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
text: qsTr("No title set!")
|
||||||
|
Material.background: Material.Grey
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "enabledPage0"
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
text: qsTr("Next")
|
||||||
|
Material.background: Material.Orange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: "*"
|
||||||
|
to: "*"
|
||||||
|
NumberAnimation {
|
||||||
|
property: "width"
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
duration: 2000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,12 +1,15 @@
|
|||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
import net.aimber.create 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
property bool allNecessaryConfigsSet: false
|
|
||||||
|
|
||||||
Rectangle {
|
property bool canNext: false
|
||||||
|
property bool gifCreated: false
|
||||||
|
|
||||||
|
Item {
|
||||||
id: rectangle1
|
id: rectangle1
|
||||||
width: parent.width * .5
|
width: parent.width * .5
|
||||||
anchors {
|
anchors {
|
||||||
@ -33,21 +36,70 @@ Rectangle {
|
|||||||
cornerRadius: 15
|
cornerRadius: 15
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: imgWrapper
|
id: imgWrapper
|
||||||
width: parent.width * .9
|
width: parent.width * .9
|
||||||
anchors {
|
anchors {
|
||||||
top:parent.top
|
top: parent.top
|
||||||
margins: parent.width * .05
|
margins: parent.width * .05
|
||||||
right:parent.right
|
right: parent.right
|
||||||
left:parent.left
|
left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
height: 200
|
height: 200
|
||||||
color: "gray"
|
color: "gray"
|
||||||
Image {
|
|
||||||
|
BusyIndicator {
|
||||||
|
id: busyIndicator
|
||||||
|
anchors.centerIn: parent
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: text1
|
||||||
|
color: "white"
|
||||||
|
text: qsTr("Generating preview...")
|
||||||
|
font.pixelSize: 14
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: screenPlayCreate
|
||||||
|
|
||||||
|
onCreateWallpaperStateChanged: {
|
||||||
|
if (state === Create.State.ConvertingPreviewGifFinished) {
|
||||||
|
imgPreview.source = "file:///"
|
||||||
|
+ screenPlayCreate.workingDir + "/preview.gif"
|
||||||
|
imgPreview.visible = true
|
||||||
|
gifCreated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedImage {
|
||||||
id: imgPreview
|
id: imgPreview
|
||||||
|
asynchronous: true
|
||||||
|
playing: true
|
||||||
|
visible: false
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
anchors {
|
||||||
|
top: imgWrapper.bottom
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
margins: 20
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: txtOut
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +124,13 @@ Rectangle {
|
|||||||
placeholderText: qsTr("Name")
|
placeholderText: qsTr("Name")
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
onTextChanged: {
|
||||||
|
if (textField.text.length >= 3 && gifCreated) {
|
||||||
|
canNext = true
|
||||||
|
} else {
|
||||||
|
canNext = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
@ -98,9 +156,3 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*##^## Designer {
|
|
||||||
D{i:0;autoSize:true;height:768;width:1366}D{i:4;anchors_height:200;anchors_x:200;anchors_y:0}
|
|
||||||
D{i:7;anchors_height:400;anchors_width:200}D{i:3;anchors_height:200;anchors_width:683}
|
|
||||||
}
|
|
||||||
##^##*/
|
|
||||||
|
@ -6,17 +6,11 @@ Create::Create(Settings* st, QMLUtilities* util, QObject* parent)
|
|||||||
|
|
||||||
m_settings = st;
|
m_settings = st;
|
||||||
m_utils = util;
|
m_utils = util;
|
||||||
|
|
||||||
|
qRegisterMetaType<Create::State>();
|
||||||
qmlRegisterType<Create>("net.aimber.create", 1, 0, "Create");
|
qmlRegisterType<Create>("net.aimber.create", 1, 0, "Create");
|
||||||
}
|
}
|
||||||
|
|
||||||
Create::Create()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Create::~Create()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Create::copyProject(QString relativeProjectPath, QString toPath)
|
void Create::copyProject(QString relativeProjectPath, QString toPath)
|
||||||
{
|
{
|
||||||
if (toPath.contains("file:///")) {
|
if (toPath.contains("file:///")) {
|
||||||
@ -53,321 +47,222 @@ bool Create::copyRecursively(const QString& srcFilePath, const QString& tgtFileP
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Create::importVideo(QString title, QUrl videoPath, QUrl previewPath, int videoDuration)
|
void Create::createWallpaperStart(QString videoPath)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (m_importState != Create::State::Idle) {
|
videoPath.remove("file:///");
|
||||||
return;
|
|
||||||
}
|
|
||||||
QtConcurrent::run([=]() {
|
QtConcurrent::run([=]() {
|
||||||
emit localWorkshopCreationStatusChanged(State::Started);
|
|
||||||
|
|
||||||
QString fromVideoPath = QString(videoPath.toString()).replace("file:///", "");
|
QDir dir;
|
||||||
QString fromImagePath = QString(previewPath.toString()).replace("file:///", "");
|
dir.cd(m_settings->localStoragePath().toString());
|
||||||
QString toPath = m_settings->localStoragePath().toString() + "/" + title;
|
|
||||||
QString toPathWithVideoFile = toPath + "/" + videoPath.fileName();
|
|
||||||
QString toPathWithImageFile = toPath + "/" + previewPath.fileName();
|
|
||||||
|
|
||||||
if (QDir(toPath).exists()) {
|
CreateWallpaperData createWallpaperData;
|
||||||
if (!QDir(toPath).isEmpty()) {
|
createWallpaperData.videoPath = videoPath;
|
||||||
emit localWorkshopCreationStatusChanged(State::ErrorFolder);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
//if(!QDir(toPath + ))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Create a temp dir so we can later alter it to the workshop id
|
||||||
|
createWallpaperData.exportPath = QString(dir.path() + "/" + "_tmp_" + QTime::currentTime().toString()).replace(":", "");
|
||||||
|
|
||||||
|
|
||||||
|
if (dir.mkdir(createWallpaperData.exportPath)) {
|
||||||
|
// TODO
|
||||||
} else {
|
} else {
|
||||||
//TODO: Display Error
|
|
||||||
if (!QDir().mkdir(toPath)) {
|
|
||||||
emit localWorkshopCreationStatusChanged(State::ErrorFolderCreation);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Copy Video File
|
|
||||||
if (QFile::copy(fromVideoPath, toPathWithVideoFile)) {
|
|
||||||
emit localWorkshopCreationStatusChanged(State::CopyVideoFinished);
|
|
||||||
} else {
|
|
||||||
emit localWorkshopCreationStatusChanged(State::ErrorCopyVideo);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Copy Image File
|
|
||||||
if (QFile::copy(fromImagePath, toPathWithImageFile)) {
|
|
||||||
emit localWorkshopCreationStatusChanged(State::CopyImageFinished);
|
|
||||||
} else {
|
|
||||||
emit localWorkshopCreationStatusChanged(State::ErrorCopyImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Copy Project File
|
|
||||||
QFile configFile(toPath + "/" + "project.json");
|
|
||||||
|
|
||||||
if (!configFile.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
|
||||||
emit localWorkshopCreationStatusChanged(State::ErrorCopyConfig);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextStream out(&configFile);
|
// If we return early/false this means the creation
|
||||||
QJsonObject configObj;
|
// process did not work
|
||||||
|
// Todo: cleanup!
|
||||||
|
|
||||||
configObj.insert("file", videoPath.fileName());
|
if (!this->createWallpaperInfo(createWallpaperData))
|
||||||
configObj.insert("preview", previewPath.fileName());
|
return;
|
||||||
//TODO
|
|
||||||
configObj.insert("description", "");
|
|
||||||
configObj.insert("title", title);
|
|
||||||
|
|
||||||
QJsonDocument configJsonDocument(configObj);
|
if (!this->createWallpaperVideoPreview(createWallpaperData))
|
||||||
out << configJsonDocument.toJson();
|
return;
|
||||||
configFile.close();
|
|
||||||
|
|
||||||
emit localWorkshopCreationStatusChanged(State::Finished);
|
if (!this->createWallpaperVideo(createWallpaperData))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this->createWallpaperProjectFile(createWallpaperData))
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Create::importVideo(QString title, QUrl videoPath, int timeStamp, int videoDuration, int videoFrameRate)
|
bool Create::createWallpaperInfo(CreateWallpaperData& createWallpaperData)
|
||||||
{
|
{
|
||||||
|
// Get video info
|
||||||
|
QStringList args;
|
||||||
|
args.append("-print_format");
|
||||||
|
args.append("json");
|
||||||
|
args.append("-show_format");
|
||||||
|
args.append("-show_streams");
|
||||||
|
args.append(createWallpaperData.videoPath);
|
||||||
|
QScopedPointer<QProcess> pro(new QProcess());
|
||||||
|
pro.data()->setArguments(args);
|
||||||
|
|
||||||
if (m_importState != Create::State::Idle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qDebug() << title << videoPath << timeStamp;
|
|
||||||
|
|
||||||
QString tmpPath = videoPath.toString();
|
|
||||||
tmpPath.remove("file:///");
|
|
||||||
|
|
||||||
QString tmpTimeStamp = QString::number(timeStamp);
|
|
||||||
QString ffmpegTimeStamp;
|
|
||||||
|
|
||||||
QString sHours;
|
|
||||||
QString sMinutes;
|
|
||||||
QString sSeconds;
|
|
||||||
int intTmpTimeStamp;
|
|
||||||
|
|
||||||
if (tmpTimeStamp.length() > 3) {
|
|
||||||
tmpTimeStamp.remove(tmpTimeStamp.length() - 3, 3);
|
|
||||||
intTmpTimeStamp = tmpTimeStamp.toInt();
|
|
||||||
|
|
||||||
const int cseconds_in_day = 86400;
|
|
||||||
const int cseconds_in_hour = 3600;
|
|
||||||
const int cseconds_in_minute = 60;
|
|
||||||
const int cseconds = 1;
|
|
||||||
int32 hours = (intTmpTimeStamp % cseconds_in_day) / cseconds_in_hour;
|
|
||||||
int32 minutes = ((intTmpTimeStamp % cseconds_in_day) % cseconds_in_hour) / cseconds_in_minute;
|
|
||||||
int32 seconds = (((intTmpTimeStamp % cseconds_in_day) % cseconds_in_hour) % cseconds_in_minute) / cseconds;
|
|
||||||
|
|
||||||
if (hours < 10) {
|
|
||||||
sHours = "0" + QString::number(hours);
|
|
||||||
} else {
|
|
||||||
sHours = QString::number(hours);
|
|
||||||
}
|
|
||||||
if (minutes < 10) {
|
|
||||||
sMinutes = "0" + QString::number(minutes);
|
|
||||||
} else {
|
|
||||||
sMinutes = QString::number(minutes);
|
|
||||||
}
|
|
||||||
if (seconds < 10) {
|
|
||||||
sSeconds = "0" + QString::number(seconds);
|
|
||||||
} else {
|
|
||||||
sSeconds = QString::number(seconds);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ffmpegTimeStamp = "00:00:00";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString tmpVideoDuration = QString::number(videoDuration);
|
|
||||||
|
|
||||||
if (tmpVideoDuration.length() > 3)
|
|
||||||
tmpVideoDuration.remove(tmpVideoDuration.length() - 3, 3);
|
|
||||||
|
|
||||||
int videoDurationInSeconds = tmpVideoDuration.toInt();
|
|
||||||
|
|
||||||
ffmpegTimeStamp = sHours + ":" + sMinutes + ":" + sSeconds;
|
|
||||||
|
|
||||||
if (intTmpTimeStamp == 0 || videoFrameRate == 0)
|
|
||||||
qWarning() << "Cannot be zero!";
|
|
||||||
|
|
||||||
QtConcurrent::run([=]() {
|
|
||||||
QStringList args;
|
|
||||||
// Seek first because otherwise it will load
|
|
||||||
// the complete video first and this will make
|
|
||||||
// the conversion 50x slower!
|
|
||||||
args.append("-y");
|
|
||||||
args.append("-ss");
|
|
||||||
args.append(ffmpegTimeStamp);
|
|
||||||
args.append("-i");
|
|
||||||
args.append(tmpPath);
|
|
||||||
args.append("-speed");
|
|
||||||
args.append("ultrafast");
|
|
||||||
args.append("-vframes");
|
|
||||||
args.append("1");
|
|
||||||
args.append("preview.png");
|
|
||||||
QScopedPointer<QProcess> pro(new QProcess());
|
|
||||||
|
|
||||||
pro.data()->setArguments(args);
|
|
||||||
qDebug() << "Start converting video to thumbnail";
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
pro.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe");
|
pro.data()->setProgram(QApplication::applicationDirPath() + "/ffprobe.exe");
|
||||||
#endif
|
#endif
|
||||||
emit localWorkshopCreationStatusChanged(State::ConvertingPreviewImage);
|
|
||||||
pro.data()->start();
|
pro.data()->start();
|
||||||
pro.data()->waitForFinished(-1);
|
pro.data()->waitForFinished(-1);
|
||||||
qDebug() << "Done converting video to thumbnail" << pro.data()->readAllStandardOutput();
|
QJsonObject obj;
|
||||||
emit localWorkshopCreationStatusChanged(State::ConvertingPreviewImageFinished);
|
QJsonParseError err;
|
||||||
pro.data()->close();
|
QJsonDocument doc = QJsonDocument::fromJson(pro.data()->readAll(), &err);
|
||||||
});
|
if (err.error == QJsonParseError::NoError) {
|
||||||
|
obj = doc.object();
|
||||||
|
}
|
||||||
|
|
||||||
|
pro.data()->close();
|
||||||
|
|
||||||
|
// Get video length
|
||||||
|
QJsonObject objFormat = obj.value("format").toObject();
|
||||||
|
bool okParseDuration = false;
|
||||||
|
auto tmpLength = objFormat.value("duration").toVariant().toFloat(&okParseDuration);
|
||||||
|
|
||||||
|
if (!okParseDuration) {
|
||||||
|
qDebug() << "Error parsing video length";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = 0;
|
||||||
|
length = static_cast<int>(tmpLength);
|
||||||
|
createWallpaperData.length = length;
|
||||||
|
|
||||||
|
// Get framerate
|
||||||
|
QJsonArray arrSteams = obj.value("streams").toArray();
|
||||||
|
if (arrSteams.size() < 1) {
|
||||||
|
qDebug() << "Error container does not have any video streams";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject tmpObjStreams = arrSteams.at(0).toObject();
|
||||||
|
|
||||||
|
// The paramter gets us the exact framerate
|
||||||
|
// "avg_frame_rate":"47850000/797509"
|
||||||
|
// so we need no calc the value by dividing the two numbers
|
||||||
|
QString avgFrameRate = tmpObjStreams.value("avg_frame_rate").toVariant().toString();
|
||||||
|
|
||||||
|
QStringList avgFrameRateList = avgFrameRate.split('/', QString::SkipEmptyParts);
|
||||||
|
if (avgFrameRateList.length() != 2) {
|
||||||
|
qDebug() << "Error could not parse streams with length: " << avgFrameRateList.length();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int framerate = 0;
|
||||||
|
float value1 = static_cast<float>(avgFrameRateList.at(0).toInt());
|
||||||
|
float value2 = static_cast<float>(avgFrameRateList.at(1).toInt());
|
||||||
|
|
||||||
|
framerate = qCeil(value1 / value2);
|
||||||
|
createWallpaperData.framerate = framerate;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Create::createVideoPreview(QString path, int frames, int frameRate)
|
bool Create::createWallpaperVideoPreview(CreateWallpaperData& createWallpaperData)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (m_importState != Create::State::Idle) {
|
qDebug() << createWallpaperData.length << createWallpaperData.framerate;
|
||||||
return;
|
QStringList args;
|
||||||
}
|
args.append("-y");
|
||||||
qDebug() << frames << frameRate;
|
args.append("-stats");
|
||||||
QtConcurrent::run([=]() {
|
args.append("-i");
|
||||||
QStringList args;
|
args.append(createWallpaperData.videoPath);
|
||||||
args.append("-y");
|
args.append("-speed");
|
||||||
args.append("-stats");
|
args.append("ultrafast");
|
||||||
args.append("-i");
|
args.append("-vf");
|
||||||
args.append(path);
|
// We allways want to have a 5 second clip via 24fps -> 120 frames
|
||||||
args.append("-speed");
|
// Divided by the number of frames we can skip (timeInSeconds * Framrate)
|
||||||
args.append("ultrafast");
|
// scale & crop parameter: https://unix.stackexchange.com/a/284731
|
||||||
args.append("-vf");
|
args.append("select='not(mod(n," + QString::number((createWallpaperData.length / 5)) + "))',setpts=N/FRAME_RATE/TB,crop=in_h*16/9:in_h,scale=-2:400");
|
||||||
// We allways want to have a 5 second clip via 24fps -> 120 frames
|
// Disable audio
|
||||||
// Divided by the number of frames we can skip (timeInSeconds * Framrate)
|
args.append("-an");
|
||||||
// scale & crop parameter: https://unix.stackexchange.com/a/284731
|
args.append(createWallpaperData.exportPath + "/preview.mp4");
|
||||||
args.append("select='not(mod(n," + QString::number((frames / 5)) + "))',setpts=N/FRAME_RATE/TB,crop=in_h*16/9:in_h,scale=-2:400");
|
QScopedPointer<QProcess> proConvertPreviewMP4(new QProcess());
|
||||||
// Disable audio
|
|
||||||
args.append("-an");
|
|
||||||
args.append(m_workingDir + "/preview.mp4");
|
|
||||||
QScopedPointer<QProcess> proConvertPreviewMP4(new QProcess());
|
|
||||||
|
|
||||||
proConvertPreviewMP4.data()->setArguments(args);
|
proConvertPreviewMP4.data()->setArguments(args);
|
||||||
qDebug() << "Start converting video to preview mp4";
|
qDebug() << "Start converting video to preview mp4";
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
proConvertPreviewMP4.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe");
|
proConvertPreviewMP4.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe");
|
||||||
#endif
|
#endif
|
||||||
proConvertPreviewMP4.data()->start();
|
emit createWallpaperStateChanged(Create::State::ConvertingPreviewVideo);
|
||||||
proConvertPreviewMP4.data()->waitForFinished(-1);
|
|
||||||
qDebug() << proConvertPreviewMP4.data()->program() << proConvertPreviewMP4.data()->arguments();
|
|
||||||
qDebug() << "Done converting video to preview" << proConvertPreviewMP4.data()->readAll() << "\n"
|
|
||||||
<< proConvertPreviewMP4.data()->readAllStandardError();
|
|
||||||
proConvertPreviewMP4.data()->close();
|
|
||||||
|
|
||||||
/*
|
proConvertPreviewMP4.data()->start();
|
||||||
|
|
||||||
|
QScopedPointer<QTimer> processOutputTimer(new QTimer());
|
||||||
|
|
||||||
|
connect(processOutputTimer.data(), &QTimer::timeout, [&]() {
|
||||||
|
qDebug() << "### " << proConvertPreviewMP4.data()->readAll();
|
||||||
|
this->processOutput(proConvertPreviewMP4.data()->readAll());
|
||||||
|
});
|
||||||
|
processOutputTimer.data()->start(100);
|
||||||
|
proConvertPreviewMP4.data()->waitForFinished(-1);
|
||||||
|
// qDebug() << proConvertPreviewMP4.data()->program() << proConvertPreviewMP4.data()->arguments();
|
||||||
|
// qDebug() << "Done converting video to preview" << proConvertPreviewMP4.data()->readAll() << "\n"
|
||||||
|
// << proConvertPreviewMP4.data()->readAllStandardError();
|
||||||
|
proConvertPreviewMP4.data()->close();
|
||||||
|
processOutputTimer.data()->stop();
|
||||||
|
emit createWallpaperStateChanged(Create::State::ConvertingPreviewVideoFinished);
|
||||||
|
|
||||||
|
/*
|
||||||
*
|
*
|
||||||
* Create gif
|
* Create gif
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
args.clear();
|
emit createWallpaperStateChanged(Create::State::ConvertingPreviewGif);
|
||||||
args.append("-y");
|
args.clear();
|
||||||
args.append("-stats");
|
args.append("-y");
|
||||||
args.append("-i");
|
args.append("-stats");
|
||||||
args.append(m_workingDir + "/preview.mp4");
|
args.append("-i");
|
||||||
args.append("-filter_complex");
|
args.append(createWallpaperData.exportPath + "/preview.mp4");
|
||||||
args.append("[0:v] fps=12,scale=w=480:h=-1,split [a][b];[a] palettegen=stats_mode=single [p];[b][p] paletteuse=new=1");
|
args.append("-filter_complex");
|
||||||
args.append(m_workingDir + "/preview.gif");
|
args.append("[0:v] fps=12,scale=w=480:h=-1,split [a][b];[a] palettegen=stats_mode=single [p];[b][p] paletteuse=new=1");
|
||||||
|
args.append(createWallpaperData.exportPath + "/preview.gif");
|
||||||
|
|
||||||
QScopedPointer<QProcess> proConvertGif(new QProcess());
|
QScopedPointer<QProcess> proConvertGif(new QProcess());
|
||||||
proConvertGif.data()->setArguments(args);
|
proConvertGif.data()->setArguments(args);
|
||||||
qDebug() << "Start converting video to preview gif";
|
qDebug() << "Start converting video to preview gif";
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
proConvertGif.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe");
|
proConvertGif.data()->setProgram(QApplication::applicationDirPath() + "/ffmpeg.exe");
|
||||||
#endif
|
#endif
|
||||||
proConvertGif.data()->start();
|
proConvertGif.data()->start();
|
||||||
proConvertGif.data()->waitForFinished(-1);
|
proConvertGif.data()->waitForFinished(-1);
|
||||||
qDebug() << proConvertGif.data()->program() << proConvertGif.data()->arguments();
|
// qDebug() << proConvertGif.data()->program() << proConvertGif.data()->arguments();
|
||||||
qDebug() << "Done converting video to preview" << proConvertGif.data()->readAll() << "\n"
|
// qDebug() << "Done converting video to preview" << proConvertGif.data()->readAll() << "\n"
|
||||||
<< proConvertGif.data()->readAllStandardError();
|
// << proConvertGif.data()->readAllStandardError();
|
||||||
proConvertGif.data()->close();
|
proConvertGif.data()->close();
|
||||||
});
|
emit createWallpaperStateChanged(Create::State::ConvertingPreviewGifFinished);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Create::createWallpaper(QString videoPath)
|
bool Create::createWallpaperVideo(CreateWallpaperData& createWallpaperData)
|
||||||
{
|
{
|
||||||
|
return true;
|
||||||
if (m_importState != Create::State::Idle) {
|
}
|
||||||
return;
|
|
||||||
}
|
bool Create::createWallpaperProjectFile(CreateWallpaperData& createWallpaperData)
|
||||||
videoPath.remove("file:///");
|
{
|
||||||
|
//Copy Project File
|
||||||
QDir dir;
|
QFile configFile(createWallpaperData.exportPath + "/project.json");
|
||||||
dir.cd(m_settings->localStoragePath().toString());
|
|
||||||
m_workingDir = QString("_tmp_" + QTime::currentTime().toString()).replace(":", "");
|
if (!configFile.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||||
|
|
||||||
if (dir.mkdir(m_workingDir)) {
|
return false;
|
||||||
|
}
|
||||||
} else {
|
|
||||||
return;
|
// QTextStream out(&configFile);
|
||||||
}
|
// QJsonObject configObj;
|
||||||
|
|
||||||
m_workingDir = dir.path() + "/" + m_workingDir;
|
// configObj.insert("file", videoPath.fileName());
|
||||||
|
// configObj.insert("preview", previewPath.fileName());
|
||||||
QtConcurrent::run([=]() {
|
// //TODO
|
||||||
// Get video info
|
// configObj.insert("description", "");
|
||||||
QStringList args;
|
// configObj.insert("title", title);
|
||||||
args.append("-print_format");
|
|
||||||
args.append("json");
|
// QJsonDocument configJsonDocument(configObj);
|
||||||
args.append("-show_format");
|
// out << configJsonDocument.toJson();
|
||||||
args.append("-show_streams");
|
configFile.close();
|
||||||
args.append(videoPath);
|
return true;
|
||||||
QScopedPointer<QProcess> pro(new QProcess());
|
|
||||||
pro.data()->setArguments(args);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
pro.data()->setProgram(QApplication::applicationDirPath() + "/ffprobe.exe");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pro.data()->start();
|
|
||||||
pro.data()->waitForFinished(-1);
|
|
||||||
QJsonObject obj;
|
|
||||||
QJsonParseError err;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(pro.data()->readAll(), &err);
|
|
||||||
if (err.error == QJsonParseError::NoError) {
|
|
||||||
obj = doc.object();
|
|
||||||
}
|
|
||||||
|
|
||||||
pro.data()->close();
|
|
||||||
|
|
||||||
// Get video length
|
|
||||||
QJsonObject objFormat = obj.value("format").toObject();
|
|
||||||
bool okParseDuration = false;
|
|
||||||
auto tmpLength = objFormat.value("duration").toVariant().toFloat(&okParseDuration);
|
|
||||||
|
|
||||||
if (!okParseDuration) {
|
|
||||||
qDebug() << "Error parsing video length";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = 0;
|
|
||||||
length = static_cast<int>(tmpLength);
|
|
||||||
|
|
||||||
// Get framerate
|
|
||||||
QJsonArray arrSteams = obj.value("streams").toArray();
|
|
||||||
if (arrSteams.size() < 1) {
|
|
||||||
qDebug() << "Error container does not have any video streams";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject tmpObjStreams = arrSteams.at(0).toObject();
|
|
||||||
|
|
||||||
// The paramter gets us the exact framerate
|
|
||||||
// "avg_frame_rate":"47850000/797509"
|
|
||||||
// so we need no calc the value by dividing the two numbers
|
|
||||||
QString avgFrameRate = tmpObjStreams.value("avg_frame_rate").toVariant().toString();
|
|
||||||
|
|
||||||
QStringList avgFrameRateList = avgFrameRate.split('/', QString::SkipEmptyParts);
|
|
||||||
if (avgFrameRateList.length() != 2) {
|
|
||||||
qDebug() << "Error could not parse streams with length: " << avgFrameRateList.length();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int framerate = 0;
|
|
||||||
float value1 = static_cast<float>(avgFrameRateList.at(0).toInt());
|
|
||||||
float value2 = static_cast<float>(avgFrameRateList.at(1).toInt());
|
|
||||||
|
|
||||||
framerate = qCeil(value1 / value2);
|
|
||||||
|
|
||||||
this->createVideoPreview(videoPath, length, framerate);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -3,43 +3,49 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QObject>
|
#include <QJsonArray>
|
||||||
#include <QProcess>
|
|
||||||
#include <QQmlEngine>
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QTime>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonParseError>
|
#include <QJsonParseError>
|
||||||
#include <QJsonArray>
|
#include <QObject>
|
||||||
#include <QtMath>
|
#include <QProcess>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
#include "qmlutilities.h"
|
#include "qmlutilities.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
struct CreateWallpaperData {
|
||||||
|
CreateWallpaperData() {}
|
||||||
|
|
||||||
|
QString videoPath = "";
|
||||||
|
QString exportPath = "";
|
||||||
|
int length = 0;
|
||||||
|
int framerate = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class Create : public QObject {
|
class Create : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit Create(Settings* st, QMLUtilities* util,QObject *parent = nullptr);
|
explicit Create(Settings* st, QMLUtilities* util, QObject* parent = nullptr);
|
||||||
Create();
|
Create() {}
|
||||||
~Create();
|
~Create() {}
|
||||||
|
|
||||||
Q_PROPERTY(State importState READ importState WRITE setImportState NOTIFY importStateChanged)
|
|
||||||
|
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
Idle,
|
Idle,
|
||||||
Started,
|
Started,
|
||||||
CopyVideoFinished,
|
|
||||||
CopyImageFinished,
|
|
||||||
CopyConfigFinished,
|
|
||||||
ConvertingPreviewImage,
|
ConvertingPreviewImage,
|
||||||
ConvertingPreviewImageFinished,
|
ConvertingPreviewImageFinished,
|
||||||
ConvertingPreviewVideo,
|
ConvertingPreviewVideo,
|
||||||
ConvertingPreviewVideoFinished,
|
ConvertingPreviewVideoFinished,
|
||||||
|
ConvertingPreviewGif,
|
||||||
|
ConvertingPreviewGifFinished,
|
||||||
Finished,
|
Finished,
|
||||||
ErrorFolder,
|
ErrorFolder,
|
||||||
ErrorFolderCreation,
|
ErrorFolderCreation,
|
||||||
@ -52,37 +58,25 @@ public:
|
|||||||
Q_ENUM(State)
|
Q_ENUM(State)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
State importState() const
|
|
||||||
{
|
|
||||||
return m_importState;
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void localWorkshopCreationStatusChanged(State status);
|
void createWallpaperStateChanged(Create::State state);
|
||||||
void importStateChanged(State importState);
|
void processOutput(QString text);
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void copyProject(QString relativeProjectPath, QString toPath);
|
void copyProject(QString relativeProjectPath, QString toPath);
|
||||||
bool copyRecursively(const QString& srcFilePath, const QString& tgtFilePath);
|
bool copyRecursively(const QString& srcFilePath, const QString& tgtFilePath);
|
||||||
|
|
||||||
void importVideo(QString title, QUrl videoPath, QUrl previewPath, int videoDuration);
|
// Steps to convert any video to a wallpaper broken down into
|
||||||
void importVideo(QString title, QUrl videoPath, int timeStamp, int videoDuration, int videoFrameRate);
|
// several methods in this order:
|
||||||
void createVideoPreview(QString path, int frames, int length);
|
void createWallpaperStart(QString videoPath);
|
||||||
void createWallpaper(QString videoPath);
|
bool createWallpaperInfo(CreateWallpaperData& createWallpaperData);
|
||||||
void setImportState(State importState)
|
bool createWallpaperVideoPreview(CreateWallpaperData& createWallpaperData);
|
||||||
{
|
bool createWallpaperVideo(CreateWallpaperData& createWallpaperData);
|
||||||
if (m_importState == importState)
|
bool createWallpaperProjectFile(CreateWallpaperData& createWallpaperData);
|
||||||
return;
|
|
||||||
|
|
||||||
m_importState = importState;
|
|
||||||
emit importStateChanged(m_importState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Settings* m_settings;
|
Settings* m_settings;
|
||||||
QMLUtilities* m_utils;
|
QMLUtilities* m_utils;
|
||||||
QString m_workingDir;
|
|
||||||
|
|
||||||
State m_importState = State::Idle;
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user