1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-10-06 17:27:07 +02:00

Refactor video wizard into a single wizard

This commit is contained in:
Elias Steurer 2024-01-05 14:10:18 +01:00
parent b41242a938
commit bd5516fb41
19 changed files with 231 additions and 1285 deletions

View File

@ -64,16 +64,11 @@ set(QML
qml/Create/Wizards/GodotWallpaper.qml qml/Create/Wizards/GodotWallpaper.qml
qml/Create/Wizards/HTMLWallpaper.qml qml/Create/Wizards/HTMLWallpaper.qml
qml/Create/Wizards/HTMLWidget.qml qml/Create/Wizards/HTMLWidget.qml
qml/Create/Wizards/Importh264/Importh264.qml
qml/Create/Wizards/Importh264/Importh264Convert.qml
qml/Create/Wizards/Importh264/Importh264Init.qml
qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaper.qml qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaper.qml
qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaperInit.qml qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaperFileSelect.qml
qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaperSettings.qml
qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaperResult.qml qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaperResult.qml
qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaperVideoImportConvert.qml qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaperVideoImportConvert.qml
qml/Create/Wizards/ImportWebm/ImportWebm.qml
qml/Create/Wizards/ImportWebm/ImportWebmConvert.qml
qml/Create/Wizards/ImportWebm/ImportWebmInit.qml
qml/Create/Wizards/QMLWallpaper.qml qml/Create/Wizards/QMLWallpaper.qml
qml/Create/Wizards/QMLWidget.qml qml/Create/Wizards/QMLWidget.qml
qml/Create/Wizards/WebsiteWallpaper.qml qml/Create/Wizards/WebsiteWallpaper.qml

View File

@ -34,7 +34,6 @@ class Create : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
QML_UNCREATABLE("") QML_UNCREATABLE("")
Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
Q_PROPERTY(QString workingDir READ workingDir WRITE setWorkingDir NOTIFY workingDirChanged) Q_PROPERTY(QString workingDir READ workingDir WRITE setWorkingDir NOTIFY workingDirChanged)
Q_PROPERTY(float progress READ progress WRITE setProgress NOTIFY progressChanged) Q_PROPERTY(float progress READ progress WRITE setProgress NOTIFY progressChanged)
@ -45,13 +44,22 @@ public:
Create(); Create();
enum class VideoCodec { Q_INVOKABLE void cancel();
VP8,
VP9, Q_INVOKABLE void createWallpaperStart(
AV1, QString videoPath,
H264 ScreenPlay::Video::VideoCodec codec,
}; const int quality = 50);
Q_ENUM(VideoCodec)
Q_INVOKABLE void saveWallpaper(const QString title,
const QString description,
QString filePath,
QString previewImagePath,
const QString youtube,
const ScreenPlay::Video::VideoCodec codec,
const QVector<QString> tags);
Q_INVOKABLE void abortAndCleanup();
float progress() const { return m_progress; } float progress() const { return m_progress; }
QString workingDir() const { return m_workingDir; } QString workingDir() const { return m_workingDir; }
@ -68,22 +76,6 @@ signals:
void finished(); void finished();
public slots: public slots:
void cancel();
void createWallpaperStart(QString videoPath, Create::VideoCodec codec = Create::VideoCodec::VP9, const int quality = 50);
void importH264(QString videoPath);
void saveWallpaper(const QString title,
const QString description,
QString filePath,
QString previewImagePath,
const QString youtube,
const ScreenPlay::Create::VideoCodec codec,
const QVector<QString> tags);
void abortAndCleanup();
void setProgress(float progress) void setProgress(float progress)
{ {
if (qFuzzyCompare(m_progress, progress)) if (qFuzzyCompare(m_progress, progress))

View File

@ -19,6 +19,7 @@
#include <QtMath> #include <QtMath>
#include "ScreenPlay/createimportstates.h" #include "ScreenPlay/createimportstates.h"
#include "ScreenPlayUtil/contenttypes.h"
namespace ScreenPlay { namespace ScreenPlay {
@ -30,8 +31,12 @@ class CreateImportVideo : public QObject {
Q_PROPERTY(float progress READ progress WRITE setProgress NOTIFY progressChanged) Q_PROPERTY(float progress READ progress WRITE setProgress NOTIFY progressChanged)
public: public:
explicit CreateImportVideo(const QString& videoPath, const QString& exportPath, const QString& codec, const int quality, std::atomic<bool>& interrupt); explicit CreateImportVideo(
explicit CreateImportVideo(const QString& videoPath, const QString& exportPath, std::atomic<bool>& interrupt); const QString& videoPath,
const QString& exportPath,
const ScreenPlay::Video::VideoCodec targetCodec,
const int quality,
std::atomic<bool>& interrupt);
enum class Executable { enum class Executable {
FFMPEG, FFMPEG,
@ -50,7 +55,8 @@ public:
QString m_videoPath; QString m_videoPath;
QString m_exportPath; QString m_exportPath;
QString m_format; QString m_format;
QString m_codec; Video::VideoCodec m_targetCodec;
Video::VideoCodec m_sourceCodec;
const int m_quality = 50; const int m_quality = 50;
int m_numberOfFrames { 0 }; int m_numberOfFrames { 0 };

View File

@ -23,10 +23,8 @@
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include "ScreenPlay/globalvariables.h" #include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/profilelistmodel.h"
#include "ScreenPlay/settings.h" #include "ScreenPlay/settings.h"
#include "ScreenPlayUtil/projectfile.h" #include "ScreenPlayUtil/projectfile.h"
#include "ScreenPlayUtil/util.h"
#include <memory> #include <memory>

View File

@ -17,7 +17,7 @@ Rectangle {
property alias model: listView.model property alias model: listView.model
property StackView stackView property StackView stackView
width: 380 width: 340
state: expanded ? "" : "inactive" state: expanded ? "" : "inactive"
layer.enabled: true layer.enabled: true
Component.onCompleted: expanded = true Component.onCompleted: expanded = true
@ -56,19 +56,7 @@ Rectangle {
} }
ListElement { ListElement {
headline: qsTr("Video Import h264 (.mp4)") headline: qsTr("Import Video")
source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/Importh264/Importh264.qml"
category: "Video Wallpaper"
}
ListElement {
headline: qsTr("Video Import VP8 & VP9 (.webm)")
source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/ImportWebm/ImportWebm.qml"
category: "Video Wallpaper"
}
ListElement {
headline: qsTr("Video import (all types)")
source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaper.qml" source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/ImportVideoAndConvert/CreateWallpaper.qml"
category: "Video Wallpaper" category: "Video Wallpaper"
objectName: "videoImportConvert" objectName: "videoImportConvert"
@ -83,25 +71,25 @@ Rectangle {
ListElement { ListElement {
headline: qsTr("3D Engine Wallpaper (Godot 4.2)") headline: qsTr("3D Engine Wallpaper (Godot 4.2)")
source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/GodotWallpaper.qml" source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/GodotWallpaper.qml"
category: "3D Engine & Code Wallpaper" category: "3D Engine & \nCode Wallpaper"
} }
ListElement { ListElement {
headline: qsTr("QML Wallpaper") headline: qsTr("QML Wallpaper")
source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/QMLWallpaper.qml" source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/QMLWallpaper.qml"
category: "3D Engine & Code Wallpaper" category: "3D Engine & \nCode Wallpaper"
} }
ListElement { ListElement {
headline: qsTr("HTML5 Wallpaper") headline: qsTr("HTML5 Wallpaper")
source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/HTMLWallpaper.qml" source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/HTMLWallpaper.qml"
category: "3D Engine & Code Wallpaper" category: "3D Engine & \nCode Wallpaper"
} }
ListElement { ListElement {
headline: qsTr("Website Wallpaper") headline: qsTr("Website Wallpaper")
source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/WebsiteWallpaper.qml" source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/WebsiteWallpaper.qml"
category: "3D Engine & Code Wallpaper" category: "3D Engine & \nCode Wallpaper"
} }
ListElement { ListElement {
@ -123,9 +111,10 @@ Rectangle {
} }
section.delegate: Item { section.delegate: Item {
height: 60 height: headline.contentHeight + 20
Text { Text {
id:headline
font.pointSize: 18 font.pointSize: 18
color: Material.primaryTextColor color: Material.primaryTextColor
text: section text: section

View File

@ -19,18 +19,20 @@ Item {
anchors.fill: parent anchors.fill: parent
interactive: false interactive: false
clip: true clip: true
CreateWallpaperFileSelect {
CreateWallpaperInit { onNext: function (filePath) {
onNext: function (filePath, codec) { createWallpaperVideoImportConvert.filePath = filePath
startConvert(filePath, codec); swipeView.currentIndex = 1
} }
}
function startConvert(filePath, codec) { CreateWallpaperSettings {
root.wizardStarted(); id: createWallpaperSettings
swipeView.currentIndex = 1; onNext: function (codec,quality) {
createWallpaperVideoImportConvert.codec = codec; root.wizardStarted()
createWallpaperVideoImportConvert.filePath = filePath; swipeView.currentIndex = 2
App.create.createWallpaperStart(filePath, codec, quality); createWallpaperVideoImportConvert.codec = codec
App.create.createWallpaperStart(createWallpaperVideoImportConvert.filePath, codec, quality)
} }
} }
@ -40,7 +42,6 @@ Item {
onAbort: root.wizardExited() onAbort: root.wizardExited()
} }
CreateWallpaperResult { CreateWallpaperResult {}
}
} }
} }

View File

@ -11,6 +11,7 @@ import "../../"
Item { Item {
id: root id: root
property var allowedVideoFileEndings: ["*.webm", "*.mkv", "*.mp4", "*.mpg", "*.mp2", "*.mpeg", "*.ogv", "*.avi", "*.wmv", "*.m4v", "*.3gp"]
signal next(var filePath) signal next(var filePath)
@ -29,7 +30,7 @@ Item {
Util.Headline { Util.Headline {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Import a .mp4 video") text: qsTr("Import Video Wallpaper")
} }
RowLayout { RowLayout {
@ -42,37 +43,37 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
spacing: 40 spacing: 40
Text {
id: txtDescription
text: qsTr("ScreenPlay v0.15 and up can play *.mp4 (also more known as h264). This can improove performance on older systems.")
color: Material.primaryTextColor
Layout.fillWidth: true
font.pointSize: 13
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
font.family: App.settings.font
}
DropArea { DropArea {
id: dropArea id: dropArea
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
onExited: { onExited: {
bg.color = Qt.darker(Material.backgroundColor); bg.color = Qt.darker(Material.backgroundColor)
}
onEntered: {
bg.color = Qt.darker(Qt.darker(Material.backgroundColor));
drag.accept(Qt.LinkAction);
}
onDropped: {
let file = App.util.toLocal(drop.urls[0]);
bg.color = Qt.darker(Qt.darker(Material.backgroundColor));
if (file.endsWith(".mp4"))
root.next(drop.urls[0]);
else
txtFile.text = qsTr("Invalid file type. Must be valid h264 (*.mp4)!");
} }
onEntered: drag => {
bg.color = Qt.darker(
Qt.darker(Material.backgroundColor))
drag.accept(Qt.LinkAction)
}
onDropped: drop => {
let file = App.util.toLocal(drop.urls[0])
bg.color = Qt.darker(
Qt.darker(Material.backgroundColor))
let found = false
for (let ending in root.allowedVideoFileEndings) {
if (file.endsWith(ending)) {
found = true
break
}
}
if (found) {
root.next(drop.urls[0])
} else {
txtFile.text = qsTr(
"Invalid file type. Must be valid video!")
}
}
Rectangle { Rectangle {
id: bg id: bg
@ -94,7 +95,8 @@ Item {
Text { Text {
id: txtFile id: txtFile
text: qsTr("Drop a *.mp4 file here or use 'Select file' below.") text: qsTr("Drag and drop your video here. Supported video formats are:\n\n%1").arg(
root.allowedVideoFileEndings.join(" "))
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: Material.primaryTextColor color: Material.primaryTextColor
font.pointSize: 13 font.pointSize: 13
@ -123,7 +125,8 @@ Item {
icon.width: 16 icon.width: 16
icon.height: 16 icon.height: 16
font.family: App.settings.font font.family: App.settings.font
onClicked: Qt.openUrlExternally("https://kelteseth.gitlab.io/ScreenPlayDocs/wallpaper/wallpaper/#performance") onClicked: Qt.openUrlExternally(
"https://kelteseth.gitlab.io/ScreenPlayDocs/wallpaper/wallpaper/#performance")
anchors { anchors {
bottom: parent.bottom bottom: parent.bottom
@ -137,7 +140,7 @@ Item {
highlighted: true highlighted: true
font.family: App.settings.font font.family: App.settings.font
onClicked: { onClicked: {
fileDialogImportVideo.open(); fileDialogImportVideo.open()
} }
FileDialog { FileDialog {
@ -145,7 +148,7 @@ Item {
nameFilters: ["Video files (*.mp4)"] nameFilters: ["Video files (*.mp4)"]
onAccepted: { onAccepted: {
root.next(fileDialogImportVideo.currentFile); root.next(fileDialogImportVideo.currentFile)
} }
} }

View File

@ -14,7 +14,7 @@ Item {
property int quality: sliderQuality.slider.value property int quality: sliderQuality.slider.value
signal next(var filePath, var codec) signal next(var codec, var quality)
ColumnLayout { ColumnLayout {
spacing: 40 spacing: 40
@ -29,14 +29,13 @@ Item {
Util.Headline { Util.Headline {
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Import any video type") text: qsTr("Import Video Wallpaper - Select Codec")
} }
Text { Text {
id: txtDescription id: txtDescription
text: qsTr("Depending on your PC configuration it is better to convert your wallpaper to a specific video codec. If both have bad performance you can also try a QML wallpaper! Supported video formats are: \n text: qsTr("Depending on your PC configuration it is better to convert your wallpaper to a specific video codec. We skip encoding if the input format matches the ouput format.")
*.mp4 *.mpg *.mp2 *.mpeg *.ogv *.avi *.wmv *.m4v *.3gp *.flv")
color: Material.primaryTextColor color: Material.primaryTextColor
Layout.fillWidth: true Layout.fillWidth: true
font.pointSize: 13 font.pointSize: 13
@ -63,11 +62,15 @@ Item {
Layout.preferredWidth: 400 Layout.preferredWidth: 400
textRole: "text" textRole: "text"
valueRole: "value" valueRole: "value"
currentIndex: 1 currentIndex: 0
font.family: App.settings.font font.family: App.settings.font
model: ListModel { model: ListModel {
id: model id: model
ListElement {
text: "✨h.264 (Better for all hardware)"
value: Util.Video.VideoCodec.H264
}
ListElement { ListElement {
text: "VP8 (Better for older hardware)" text: "VP8 (Better for older hardware)"
@ -86,7 +89,7 @@ Item {
id: sliderQuality id: sliderQuality
iconSource: "qrc:/qml/ScreenPlayApp/assets/icons/icon_settings.svg" iconSource: "qrc:/qml/ScreenPlayApp/assets/icons/icon_settings.svg"
headline: qsTr("Quality slider. Lower value means better quality.") headline: qsTr("Set video quality. Lower value means better quality.")
Layout.preferredWidth: 400 Layout.preferredWidth: 400
slider { slider {
@ -107,7 +110,8 @@ Item {
icon.width: 16 icon.width: 16
icon.height: 16 icon.height: 16
font.family: App.settings.font font.family: App.settings.font
onClicked: Qt.openUrlExternally("https://kelteseth.gitlab.io/ScreenPlayDocs/wallpaper/wallpaper/#performance") onClicked: Qt.openUrlExternally(
"https://kelteseth.gitlab.io/ScreenPlayDocs/wallpaper/wallpaper/#performance")
anchors { anchors {
bottom: parent.bottom bottom: parent.bottom
@ -116,23 +120,15 @@ Item {
} }
Button { Button {
objectName: "createWallpaperInitFileSelectButton" objectName: "createWallpaperStartImportButton"
text: qsTr("Select file") text: qsTr("Start import")
highlighted: true highlighted: true
font.family: App.settings.font font.family: App.settings.font
onClicked: { onClicked: {
fileDialogImportVideo.open(); let a = Util.Video.VideoCodec.H264
let targetCodec = comboBoxCodec.currentValue
root.next(a, sliderQuality.slider.value)
} }
FileDialog {
id: fileDialogImportVideo
nameFilters: ["Video files (*.mp4 *.mpg *.mp2 *.mpeg *.ogv *.avi *.wmv *.m4v *.3gp *.flv)"]
onAccepted: {
root.next(fileDialogImportVideo.currentFile, model.get(comboBoxCodec.currentIndex).value);
}
}
anchors { anchors {
right: parent.right right: parent.right
bottom: parent.bottom bottom: parent.bottom

View File

@ -12,7 +12,7 @@ Item {
property bool conversionFinishedSuccessful: false property bool conversionFinishedSuccessful: false
property bool canSave: false property bool canSave: false
property var codec: Create.VP8 property var codec: Util.Video.VideoCodec.H264
property string filePath property string filePath
signal abort signal abort
@ -335,7 +335,14 @@ Item {
onClicked: { onClicked: {
if (conversionFinishedSuccessful) { if (conversionFinishedSuccessful) {
btnSave.enabled = false; btnSave.enabled = false;
App.create.saveWallpaper(textFieldName.text, textFieldDescription.text, root.filePath, previewSelector.imageSource, textFieldYoutubeURL.text, codec, textFieldTags.getTags()); App.create.saveWallpaper(
textFieldName.text,
textFieldDescription.text,
root.filePath,
previewSelector.imageSource,
textFieldYoutubeURL.text,
root.codec,
textFieldTags.getTags());
savePopup.open(); savePopup.open();
} }
} }

View File

@ -1,39 +0,0 @@
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Layouts
import ScreenPlayApp
import ScreenPlay
Item {
id: root
signal wizardStarted
signal wizardExited
signal next
SwipeView {
id: swipeView
anchors.fill: parent
interactive: false
clip: true
ImportWebmInit {
onNext: function (filePath) {
root.wizardStarted();
swipeView.currentIndex = 1;
createWallpaperVideoImportConvert.filePath = filePath;
App.util.setNavigationActive(false);
App.create.createWallpaperStart(filePath);
}
}
ImportWebmConvert {
id: createWallpaperVideoImportConvert
onExit: root.wizardExited()
}
}
}

View File

@ -1,373 +0,0 @@
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Layouts
import ScreenPlayApp
import ScreenPlay
import ScreenPlayUtil as Util
Item {
id: root
property bool conversionFinishedSuccessful: false
property bool canSave: false
property string filePath
signal exit
signal save
function basename(str) {
let filenameWithExtentions = (str.slice(str.lastIndexOf("/") + 1));
let filename = filenameWithExtentions.split('.').slice(0, -1).join('.');
return filename;
}
function checkCanSave() {
if (canSave && conversionFinishedSuccessful)
btnSave.enabled = true;
else
btnSave.enabled = false;
}
onCanSaveChanged: root.checkCanSave()
onFilePathChanged: {
textFieldName.text = basename(filePath);
}
Connections {
function onCreateWallpaperStateChanged(state) {
switch (state) {
case Import.State.AnalyseVideo:
txtConvert.text = qsTr("AnalyseVideo...");
break;
case Import.State.ConvertingPreviewImage:
txtConvert.text = qsTr("Generating preview image...");
break;
case Import.State.ConvertingPreviewThumbnailImage:
txtConvert.text = qsTr("Generating preview thumbnail image...");
break;
case Import.State.ConvertingPreviewImageFinished:
imgPreview.source = "file:///" + App.create.workingDir + "/preview.jpg";
imgPreview.visible = true;
break;
case Import.State.ConvertingPreviewVideo:
txtConvert.text = qsTr("Generating 5 second preview video...");
break;
case Import.State.ConvertingPreviewGif:
txtConvert.text = qsTr("Generating preview gif...");
break;
case Import.State.ConvertingPreviewGifFinished:
gifPreview.source = "file:///" + App.create.workingDir + "/preview.gif";
imgPreview.visible = false;
gifPreview.visible = true;
gifPreview.playing = true;
break;
case Import.State.ConvertingAudio:
txtConvert.text = qsTr("Converting Audio...");
break;
case Import.State.ConvertingVideo:
txtConvert.text = qsTr("Converting Video... This can take some time!");
break;
case Import.State.ConvertingVideoError:
txtConvert.text = qsTr("Converting Video ERROR!");
break;
case Import.State.AnalyseVideoError:
txtConvert.text = qsTr("Analyse Video ERROR!");
break;
case Import.State.Finished:
txtConvert.text = "";
conversionFinishedSuccessful = true;
busyIndicator.running = false;
btnExit.enabled = false;
root.checkCanSave();
break;
}
}
function onProgressChanged(progress) {
var percentage = Math.floor(progress * 100);
if (percentage > 100 || progress > 0.95)
percentage = 100;
if (percentage === NaN)
print(progress, percentage);
txtConvertNumber.text = percentage + "%";
}
target: App.create
}
Util.Headline {
id: txtHeadline
text: qsTr("Import a video to a wallpaper")
anchors {
top: parent.top
left: parent.left
margins: 40
bottomMargin: 0
}
}
Item {
id: wrapperLeft
width: parent.width * 0.66
anchors {
left: parent.left
top: txtHeadline.bottom
margins: 30
bottom: parent.bottom
}
Rectangle {
id: imgWrapper
color: Material.color(Material.Grey)
anchors {
top: parent.top
right: parent.right
rightMargin: 20
bottom: previewSelector.top
bottomMargin: 20
left: parent.left
}
Image {
id: imgPreview
fillMode: Image.PreserveAspectCrop
asynchronous: true
visible: false
anchors.fill: parent
}
AnimatedImage {
id: gifPreview
fillMode: Image.PreserveAspectCrop
asynchronous: true
playing: true
visible: false
anchors.fill: parent
}
Rectangle {
id: shadow
anchors.fill: parent
gradient: Gradient {
GradientStop {
id: gradientStop0
position: 1
color: "#DD000000"
}
GradientStop {
id: gradientStop1
position: 0
color: "#00000000"
}
}
}
BusyIndicator {
id: busyIndicator
anchors.centerIn: parent
running: true
}
Text {
id: txtConvertNumber
color: "white"
font.pointSize: 21
font.family: App.settings.font
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 40
}
}
Text {
id: txtConvert
color: Material.secondaryTextColor
text: qsTr("Generating preview video...")
font.pointSize: 14
font.family: App.settings.font
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 20
}
}
}
Util.ImageSelector {
id: previewSelector
height: 80
anchors {
right: parent.right
rightMargin: 20
left: parent.left
bottom: parent.bottom
}
}
}
Item {
id: wrapperRight
width: parent.width * 0.33
anchors {
top: txtHeadline.bottom
topMargin: 30
bottom: parent.bottom
right: parent.right
}
ColumnLayout {
id: column
spacing: 0
anchors {
right: parent.right
left: parent.left
margins: 30
top: parent.top
topMargin: 0
bottom: column1.top
bottomMargin: 50
}
Util.TextField {
id: textFieldName
placeholderText: qsTr("Name (required!)")
width: parent.width
Layout.fillWidth: true
onTextChanged: {
if (textFieldName.text.length >= 3)
canSave = true;
else
canSave = false;
}
}
Util.TextField {
id: textFieldDescription
placeholderText: qsTr("Description")
width: parent.width
Layout.fillWidth: true
}
Util.TextField {
id: textFieldYoutubeURL
placeholderText: qsTr("Youtube URL")
width: parent.width
Layout.fillWidth: true
}
Util.TagSelector {
id: textFieldTags
width: parent.width
Layout.fillWidth: true
}
}
Row {
id: column1
height: 80
width: childrenRect.width
spacing: 10
anchors {
right: parent.right
rightMargin: 30
bottomMargin: -10
bottom: parent.bottom
}
Button {
id: btnExit
text: qsTr("Abort")
Material.accent: Material.color(Material.Red)
highlighted: true
font.family: App.settings.font
onClicked: {
root.exit();
App.create.cancel();
}
}
Button {
id: btnSave
objectName: "btnSave"
text: qsTr("Save")
enabled: false
Material.background: Material.accent
Material.foreground: "white"
font.family: App.settings.font
onClicked: {
if (conversionFinishedSuccessful) {
btnSave.enabled = false;
App.create.saveWallpaper(textFieldName.text, textFieldDescription.text, root.filePath, previewSelector.imageSource, textFieldYoutubeURL.text, Util.Video.VideoCodec.VP9, textFieldTags.getTags());
savePopup.open();
}
}
}
}
}
Popup {
id: savePopup
modal: true
focus: true
width: 250
anchors.centerIn: parent
height: 200
onOpened: timerSave.start()
BusyIndicator {
anchors.centerIn: parent
running: true
}
Text {
text: qsTr("Save Wallpaper...")
color: Material.primaryTextColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 30
font.family: App.settings.font
}
Timer {
id: timerSave
interval: 1000 + Math.random() * 1000
onTriggered: {
savePopup.close();
App.util.setNavigationActive(true);
root.exit();
}
}
}
}

View File

@ -1,158 +0,0 @@
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Layouts
import QtQuick.Dialogs
import ScreenPlayApp
import ScreenPlay
import ScreenPlayUtil as Util
import "../../"
Item {
id: root
signal next(var filePath)
ColumnLayout {
id: wrapper
spacing: 40
anchors {
top: parent.top
bottom: btnOpenDocs.top
left: parent.left
right: parent.right
margins: 20
}
Util.Headline {
Layout.fillWidth: true
text: qsTr("Import a .webm video")
}
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 40
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 40
Text {
id: txtDescription
text: qsTr("When importing webm we can skip the long conversion. When you get unsatisfying results with the ScreenPlay importer from 'ideo import and convert (all types)' you can also convert via the free and open source HandBrake!")
color: Material.primaryTextColor
Layout.fillWidth: true
font.pointSize: 13
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
font.family: App.settings.font
}
DropArea {
id: dropArea
Layout.fillHeight: true
Layout.fillWidth: true
onExited: {
bg.color = Qt.darker(Material.backgroundColor);
}
onEntered: {
bg.color = Qt.darker(Qt.darker(Material.backgroundColor));
drag.accept(Qt.LinkAction);
}
onDropped: {
let file = App.util.toLocal(drop.urls[0]);
bg.color = Qt.darker(Qt.darker(Material.backgroundColor));
if (file.endsWith(".webm"))
root.next(drop.urls[0]);
else
txtFile.text = qsTr("Invalid file type. Must be valid VP8 or VP9 (*.webm)!");
}
Rectangle {
id: bg
anchors.fill: parent
radius: 3
color: Qt.darker(Material.backgroundColor)
}
Image {
id: bgPattern
anchors.fill: parent
fillMode: Image.Tile
opacity: 0.2
source: "qrc:/qml/ScreenPlayApp/assets/images/noisy-texture-3.png"
}
Text {
id: txtFile
text: qsTr("Drop a *.webm file here or use 'Select file' below.")
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: Material.primaryTextColor
font.pointSize: 13
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
font.family: App.settings.font
anchors {
fill: parent
margins: 40
}
}
}
}
}
}
Button {
id: btnOpenDocs
text: qsTr("Open Documentation")
Material.accent: Material.color(Material.LightGreen)
highlighted: true
icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_document.svg"
icon.color: "white"
icon.width: 16
icon.height: 16
font.family: App.settings.font
onClicked: Qt.openUrlExternally("https://kelteseth.gitlab.io/ScreenPlayDocs/wallpaper/wallpaper/#performance")
anchors {
bottom: parent.bottom
left: parent.left
margins: 20
}
}
Button {
text: qsTr("Select file")
highlighted: true
font.family: App.settings.font
onClicked: {
fileDialogImportVideo.open();
}
FileDialog {
id: fileDialogImportVideo
nameFilters: ["Video files (*.webm)"]
onAccepted: {
root.next(fileDialogImportVideo.currentFile);
}
}
anchors {
right: parent.right
bottom: parent.bottom
margins: 20
}
}
}

View File

@ -1,39 +0,0 @@
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Layouts
import ScreenPlayApp
import ScreenPlay
Item {
id: root
signal wizardStarted
signal wizardExited
signal next
SwipeView {
id: swipeView
anchors.fill: parent
interactive: false
clip: true
Importh264Init {
onNext: function (filePath) {
root.wizardStarted();
swipeView.currentIndex = 1;
createWallpaperVideoImportConvert.filePath = filePath;
App.util.setNavigationActive(false);
App.create.importH264(filePath);
}
}
Importh264Convert {
id: createWallpaperVideoImportConvert
onExit: root.wizardExited()
}
}
}

View File

@ -1,374 +0,0 @@
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Layouts
import ScreenPlayApp
import ScreenPlay
import ScreenPlayUtil as Util
Item {
id: root
property bool conversionFinishedSuccessful: false
property bool canSave: false
property string filePath
signal exit
signal save
function basename(str) {
let filenameWithExtentions = (str.slice(str.lastIndexOf("/") + 1));
let filename = filenameWithExtentions.split('.').slice(0, -1).join('.');
return filename;
}
function checkCanSave() {
if (canSave && conversionFinishedSuccessful)
btnSave.enabled = true;
else
btnSave.enabled = false;
}
onCanSaveChanged: root.checkCanSave()
onFilePathChanged: {
textFieldName.text = basename(filePath);
}
Connections {
function onCreateWallpaperStateChanged(state) {
switch (state) {
case Import.State.AnalyseVideo:
txtConvert.text = qsTr("AnalyseVideo...");
break;
case Import.State.ConvertingPreviewImage:
txtConvert.text = qsTr("Generating preview image...");
break;
case Import.State.ConvertingPreviewThumbnailImage:
txtConvert.text = qsTr("Generating preview thumbnail image...");
break;
case Import.State.ConvertingPreviewImageFinished:
imgPreview.source = "file:///" + App.create.workingDir + "/preview.jpg";
imgPreview.visible = true;
break;
case Import.State.ConvertingPreviewVideo:
txtConvert.text = qsTr("Generating 5 second preview video...");
break;
case Import.State.ConvertingPreviewGif:
txtConvert.text = qsTr("Generating preview gif...");
break;
case Import.State.ConvertingPreviewGifFinished:
gifPreview.source = "file:///" + App.create.workingDir + "/preview.gif";
imgPreview.visible = false;
gifPreview.visible = true;
gifPreview.playing = true;
break;
case Import.State.ConvertingAudio:
txtConvert.text = qsTr("Converting Audio...");
break;
case Import.State.ConvertingVideo:
txtConvert.text = qsTr("Converting Video... This can take some time!");
break;
case Import.State.ConvertingVideoError:
txtConvert.text = qsTr("Converting Video ERROR!");
break;
case Import.State.AnalyseVideoError:
txtConvert.text = qsTr("Analyse Video ERROR!");
break;
case Import.State.Finished:
txtConvert.text = "";
conversionFinishedSuccessful = true;
busyIndicator.running = false;
btnExit.enabled = false;
root.checkCanSave();
break;
}
}
function onProgressChanged(progress) {
var percentage = Math.floor(progress * 100);
if (percentage > 100 || progress > 0.95)
percentage = 100;
if (percentage === NaN)
print(progress, percentage);
txtConvertNumber.text = percentage + "%";
}
target: App.create
}
Util.Headline {
id: txtHeadline
text: qsTr("Import a video to a wallpaper")
anchors {
top: parent.top
left: parent.left
margins: 40
bottomMargin: 0
}
}
Item {
id: wrapperLeft
width: parent.width * 0.66
anchors {
left: parent.left
top: txtHeadline.bottom
margins: 30
bottom: parent.bottom
}
Rectangle {
id: imgWrapper
color: Material.color(Material.Grey)
anchors {
top: parent.top
right: parent.right
rightMargin: 20
bottom: previewSelector.top
bottomMargin: 20
left: parent.left
}
Image {
id: imgPreview
fillMode: Image.PreserveAspectCrop
asynchronous: true
visible: false
anchors.fill: parent
}
AnimatedImage {
id: gifPreview
fillMode: Image.PreserveAspectCrop
asynchronous: true
playing: true
visible: false
anchors.fill: parent
}
Rectangle {
id: shadow
anchors.fill: parent
gradient: Gradient {
GradientStop {
id: gradientStop0
position: 1
color: "#DD000000"
}
GradientStop {
id: gradientStop1
position: 0
color: "#00000000"
}
}
}
BusyIndicator {
id: busyIndicator
anchors.centerIn: parent
running: true
}
Text {
id: txtConvertNumber
color: "white"
font.pointSize: 21
font.family: App.settings.font
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 40
}
}
Text {
id: txtConvert
color: Material.secondaryTextColor
text: qsTr("Generating preview video...")
font.pointSize: 14
font.family: App.settings.font
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 20
}
}
}
Util.ImageSelector {
id: previewSelector
height: 80
anchors {
right: parent.right
rightMargin: 20
left: parent.left
bottom: parent.bottom
}
}
}
Item {
id: wrapperRight
width: parent.width * 0.33
anchors {
top: txtHeadline.bottom
topMargin: 30
bottom: parent.bottom
right: parent.right
}
ColumnLayout {
id: column
spacing: 0
anchors {
right: parent.right
left: parent.left
margins: 30
top: parent.top
topMargin: 0
bottom: column1.top
bottomMargin: 50
}
Util.TextField {
id: textFieldName
placeholderText: qsTr("Name (required!)")
width: parent.width
Layout.fillWidth: true
onTextChanged: {
if (textFieldName.text.length >= 3)
canSave = true;
else
canSave = false;
}
}
Util.TextField {
id: textFieldDescription
placeholderText: qsTr("Description")
width: parent.width
Layout.fillWidth: true
}
Util.TextField {
id: textFieldYoutubeURL
placeholderText: qsTr("Youtube URL")
width: parent.width
Layout.fillWidth: true
}
Util.TagSelector {
id: textFieldTags
width: parent.width
Layout.fillWidth: true
}
}
Row {
id: column1
height: 80
width: childrenRect.width
spacing: 10
anchors {
right: parent.right
rightMargin: 30
bottomMargin: -10
bottom: parent.bottom
}
Button {
id: btnExit
text: qsTr("Abort")
Material.background: Material.Red
Material.foreground: "white"
font.family: App.settings.font
onClicked: {
root.exit();
App.create.cancel();
}
}
Button {
id: btnSave
objectName: "btnSave"
text: qsTr("Save")
enabled: false
Material.background: Material.accent
Material.foreground: "white"
font.family: App.settings.font
onClicked: {
if (conversionFinishedSuccessful) {
btnSave.enabled = false;
App.create.saveWallpaper(textFieldName.text, textFieldDescription.text, root.filePath, previewSelector.imageSource, textFieldYoutubeURL.text, Create.H264, textFieldTags.getTags());
savePopup.open();
}
}
}
}
}
Popup {
id: savePopup
modal: true
focus: true
width: 250
anchors.centerIn: parent
height: 200
onOpened: timerSave.start()
BusyIndicator {
anchors.centerIn: parent
running: true
}
Text {
text: qsTr("Save Wallpaper...")
color: Material.primaryTextColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 30
font.family: App.settings.font
}
Timer {
id: timerSave
interval: 1000 + Math.random() * 1000
onTriggered: {
savePopup.close();
App.util.setNavigationActive(true);
root.exit();
}
}
}
}

View File

@ -340,9 +340,15 @@ Item {
} }
} }
} }
MessageDialog {
id: errorDialog Dialog {
buttons: MessageDialog.Ok id: dialog
standardButtons: Dialog.Ok
title: qsTr("Export Godot project")
property alias message: messageText.text
Text {
id: messageText
}
} }
Button { Button {
@ -375,21 +381,22 @@ Item {
absoluteStoragePath, absoluteStoragePath,
App.globalVariables.godotEditorExecutablePath).then( App.globalVariables.godotEditorExecutablePath).then(
result => { result => {
if(!result.success){ if (!result.success) {
errorDialog.text = ("Error exporting Godot") dialog.title = ("Error exporting Godot")
errorDialog.informativeText = result.messag dialog.message = result.message
errorDialog.open() dialog.open()
return } else {
const screenFile = item.m_file
let success = App.screenPlayManager.createWallpaper(
root.type,
cbVideoFillMode.currentValue,
absoluteStoragePath,
previewImage, screenFile,
activeMonitors, volume,
1, {}, true)
} }
const screenFile = item.m_file
let success = App.screenPlayManager.createWallpaper(
root.type,
cbVideoFillMode.currentValue,
absoluteStoragePath,
previewImage, screenFile,
activeMonitors, volume,
1, {}, true)
}) })
root.state = "inactive"
return return
} }

View File

@ -2,6 +2,7 @@
#include "ScreenPlay/create.h" #include "ScreenPlay/create.h"
#include "ScreenPlayUtil/util.h" #include "ScreenPlayUtil/util.h"
#include "qguiapplication.h"
namespace ScreenPlay { namespace ScreenPlay {
@ -43,7 +44,7 @@ void Create::reset()
/*! /*!
\brief Starts the process. \brief Starts the process.
*/ */
void Create::createWallpaperStart(QString videoPath, Create::VideoCodec codec, const int quality) void Create::createWallpaperStart(QString videoPath, ScreenPlay::Video::VideoCodec target_codec, const int quality)
{ {
reset(); reset();
ScreenPlay::Util util; ScreenPlay::Util util;
@ -63,19 +64,6 @@ void Create::createWallpaperStart(QString videoPath, Create::VideoCodec codec, c
return; return;
} }
QString target_codec;
switch (codec) {
case Create::VideoCodec::VP8:
target_codec = "vp8";
break;
case Create::VideoCodec::VP9:
target_codec = "vp9";
break;
case Create::VideoCodec::AV1:
target_codec = "av1";
break;
}
m_createImportFuture = QtConcurrent::run(QThreadPool::globalInstance(), [videoPath, target_codec, quality, this]() { m_createImportFuture = QtConcurrent::run(QThreadPool::globalInstance(), [videoPath, target_codec, quality, this]() {
CreateImportVideo import(videoPath, workingDir(), target_codec, quality, m_interrupt); CreateImportVideo import(videoPath, workingDir(), target_codec, quality, m_interrupt);
QObject::connect(&import, &CreateImportVideo::createWallpaperStateChanged, this, &Create::createWallpaperStateChanged, Qt::ConnectionType::QueuedConnection); QObject::connect(&import, &CreateImportVideo::createWallpaperStateChanged, this, &Create::createWallpaperStateChanged, Qt::ConnectionType::QueuedConnection);
@ -134,13 +122,7 @@ void Create::createWallpaperStart(QString videoPath, Create::VideoCodec codec, c
} }
} }
// Skip convert for webm qInfo() << "createWallpaperVideo";
if (import.m_isWebm) {
emit createWallpaperStateChanged(Import::State::Finished);
return;
}
qInfo() << "createWallpaperVideo()";
if (!import.createWallpaperVideo() || m_interrupt) { if (!import.createWallpaperVideo() || m_interrupt) {
emit createWallpaperStateChanged(Import::State::Failed); emit createWallpaperStateChanged(Import::State::Failed);
emit import.abortAndCleanup(); emit import.abortAndCleanup();
@ -157,91 +139,6 @@ void Create::createWallpaperStart(QString videoPath, Create::VideoCodec codec, c
m_createImportFutureWatcher.setFuture(m_createImportFuture); m_createImportFutureWatcher.setFuture(m_createImportFuture);
} }
void Create::importH264(QString videoPath)
{
reset();
ScreenPlay::Util util;
videoPath = util.toLocal(videoPath);
const QDir installedDir = util.toLocal(m_globalVariables->localStoragePath().toString());
// Create a temp dir so we can later alter it to the workshop id
const QDateTime date = QDateTime::currentDateTime();
const auto folderName = date.toString("ddMMyyyyhhmmss");
setWorkingDir(installedDir.path() + "/" + folderName);
if (!installedDir.mkdir(folderName)) {
qInfo() << "Unable to create folder with name: " << folderName << " at: " << installedDir;
emit createWallpaperStateChanged(Import::State::CreateTmpFolderError);
emit abortCreateWallpaper();
return;
}
m_createImportFuture = QtConcurrent::run(QThreadPool::globalInstance(), [videoPath, this]() {
CreateImportVideo import(videoPath, workingDir(), m_interrupt);
QObject::connect(&import, &CreateImportVideo::createWallpaperStateChanged, this, &Create::createWallpaperStateChanged, Qt::ConnectionType::QueuedConnection);
QObject::connect(&import, &CreateImportVideo::abortAndCleanup, this, &Create::abortAndCleanup, Qt::ConnectionType::QueuedConnection);
QObject::connect(
&import, &CreateImportVideo::processOutput, this, [this](const QString text) {
appendFfmpegOutput(text + "\n");
},
Qt::ConnectionType::QueuedConnection);
if (!import.createWallpaperInfo() || m_interrupt) {
emit createWallpaperStateChanged(Import::State::Failed);
emit import.abortAndCleanup();
return;
}
qInfo() << "createWallpaperImageThumbnailPreview()";
if (!import.createWallpaperImageThumbnailPreview() || m_interrupt) {
emit createWallpaperStateChanged(Import::State::Failed);
emit import.abortAndCleanup();
return;
}
qInfo() << "createWallpaperImagePreview()";
if (!import.createWallpaperImagePreview() || m_interrupt) {
emit createWallpaperStateChanged(Import::State::Failed);
emit import.abortAndCleanup();
return;
}
// Skip preview convert for webm
if (!import.createWallpaperVideoPreview() || m_interrupt) {
emit createWallpaperStateChanged(Import::State::Failed);
emit import.abortAndCleanup();
return;
}
qInfo() << "createWallpaperGifPreview()";
if (!import.createWallpaperGifPreview() || m_interrupt) {
emit createWallpaperStateChanged(Import::State::Failed);
emit import.abortAndCleanup();
return;
}
// If the video has no audio we can skip the extraction
if (!import.m_skipAudio) {
qInfo() << "extractWallpaperAudio()";
if (!import.extractWallpaperAudio() || m_interrupt) {
emit createWallpaperStateChanged(Import::State::Failed);
emit import.abortAndCleanup();
return;
}
}
emit createWallpaperStateChanged(Import::State::Finished);
return;
});
QObject::connect(&m_createImportFutureWatcher, &QFutureWatcherBase::finished, this, [this]() {
if (m_interrupt)
abortAndCleanup();
});
m_createImportFutureWatcher.setFuture(m_createImportFuture);
}
/*! /*!
\brief When converting of the wallpaper steps where successful. \brief When converting of the wallpaper steps where successful.
@ -252,7 +149,7 @@ void Create::saveWallpaper(
QString filePath, QString filePath,
QString previewImagePath, QString previewImagePath,
const QString youtube, const QString youtube,
const Create::VideoCodec codec, const ScreenPlay::Video::VideoCodec codec,
const QVector<QString> tags) const QVector<QString> tags)
{ {
ScreenPlay::Util util; ScreenPlay::Util util;
@ -282,13 +179,13 @@ void Create::saveWallpaper(
} }
QFileInfo filePathFile(filePath); QFileInfo filePathFile(filePath);
if (filePath.endsWith(".webm") || filePath.endsWith(".mp4")) { // if (filePath.endsWith(".webm") || filePath.endsWith(".mp4")) {
if (!QFile::copy(filePath, m_workingDir + "/" + filePathFile.fileName())) { // if (!QFile::copy(filePath, m_workingDir + "/" + filePathFile.fileName())) {
qDebug() << "Could not copy" << filePath << " to " << m_workingDir + "/" + filePathFile.fileName(); // qDebug() << "Could not copy" << filePath << " to " << m_workingDir + "/" + filePathFile.fileName();
emit createWallpaperStateChanged(Import::State::CopyFilesError); // emit createWallpaperStateChanged(Import::State::CopyFilesError);
return; // return;
} // }
} // }
emit createWallpaperStateChanged(Import::State::CopyFilesFinished); emit createWallpaperStateChanged(Import::State::CopyFilesFinished);
emit createWallpaperStateChanged(Import::State::CreateProjectFile); emit createWallpaperStateChanged(Import::State::CreateProjectFile);
@ -296,8 +193,17 @@ void Create::saveWallpaper(
obj.insert("description", description); obj.insert("description", description);
obj.insert("title", title); obj.insert("title", title);
obj.insert("youtube", youtube); obj.insert("youtube", youtube);
obj.insert("videoCodec", QVariant::fromValue<VideoCodec>(codec).toString()); obj.insert("videoCodec", QVariant::fromValue<Video::VideoCodec>(codec).toString());
obj.insert("file", filePathFile.completeBaseName() + (codec == VideoCodec::H264 ? ".mp4" : ".webm"));
QString fileEnding;
if (codec == Video::VideoCodec::H264)
fileEnding = ".mp4";
if (codec == Video::VideoCodec::AV1)
fileEnding = ".mkv";
if (codec == Video::VideoCodec::VP8 || codec == Video::VideoCodec::VP9)
fileEnding = ".webm";
obj.insert("file", filePathFile.completeBaseName() + fileEnding);
obj.insert("previewGIF", "preview.gif"); obj.insert("previewGIF", "preview.gif");
obj.insert("previewWEBM", "preview.webm"); obj.insert("previewWEBM", "preview.webm");
obj.insert("preview", previewImageFile.exists() ? previewImageFile.fileName() : "preview.jpg"); obj.insert("preview", previewImageFile.exists() ? previewImageFile.fileName() : "preview.jpg");

View File

@ -24,7 +24,7 @@ namespace ScreenPlay {
CreateImportVideo::CreateImportVideo( CreateImportVideo::CreateImportVideo(
const QString& videoPath, const QString& videoPath,
const QString& exportPath, const QString& exportPath,
const QString& codec, const ScreenPlay::Video::VideoCodec targetCodec,
const int quality, const int quality,
std::atomic<bool>& interrupt) std::atomic<bool>& interrupt)
: QObject(nullptr) : QObject(nullptr)
@ -33,17 +33,7 @@ CreateImportVideo::CreateImportVideo(
{ {
m_videoPath = videoPath; m_videoPath = videoPath;
m_exportPath = exportPath; m_exportPath = exportPath;
m_codec = codec; m_targetCodec = targetCodec;
setupFFMPEG();
}
CreateImportVideo::CreateImportVideo(const QString& videoPath, const QString& exportPath, std::atomic<bool>& interrupt)
: QObject(nullptr)
, m_quality(0)
, m_interrupt(interrupt)
{
m_videoPath = videoPath;
m_exportPath = exportPath;
setupFFMPEG(); setupFFMPEG();
} }
@ -86,10 +76,6 @@ void CreateImportVideo::setupFFMPEG()
*/ */
bool CreateImportVideo::createWallpaperInfo() bool CreateImportVideo::createWallpaperInfo()
{ {
if (m_videoPath.endsWith(".webm") || m_videoPath.endsWith(".mkv")) {
m_isWebm = true;
}
// Get video info // Get video info
QStringList args; QStringList args;
args.append("-print_format"); args.append("-print_format");
@ -138,12 +124,7 @@ bool CreateImportVideo::createWallpaperInfo()
emit createWallpaperStateChanged(Import::State::AnalyseVideoError); emit createWallpaperStateChanged(Import::State::AnalyseVideoError);
return false; return false;
} }
return analyzeVideo(obj.value());
if (m_isWebm) {
return analyzeWebmReadFrames(obj.value());
} else {
return analyzeVideo(obj.value());
}
} }
/*! /*!
@ -165,6 +146,15 @@ bool CreateImportVideo::analyzeWebmReadFrames(const QJsonObject& obj)
const QJsonObject firstStream = streams.first().toObject(); const QJsonObject firstStream = streams.first().toObject();
qInfo() << "streams:" << streams; qInfo() << "streams:" << streams;
for (const auto& stream : streams) {
QString codec_type = stream.toObject().value("codec_type").toString();
if (codec_type == "audio") {
m_skipAudio = false;
} else {
m_skipAudio = true;
}
}
bool okParseNumberOfFrames { false }; bool okParseNumberOfFrames { false };
int numberOfFrames = firstStream.value("nb_read_frames").toString().toInt(&okParseNumberOfFrames); int numberOfFrames = firstStream.value("nb_read_frames").toString().toInt(&okParseNumberOfFrames);
if (!okParseNumberOfFrames) { if (!okParseNumberOfFrames) {
@ -202,6 +192,37 @@ bool CreateImportVideo::analyzeVideo(const QJsonObject& obj)
{ {
// Check for audio and video streams // Check for audio and video streams
const QJsonArray arrayStream = obj.value("streams").toArray(); const QJsonArray arrayStream = obj.value("streams").toArray();
// Get framerate
const QJsonArray streams = obj.value("streams").toArray();
if (streams.empty()) {
qDebug() << "Error container does not have any video streams";
emit processOutput("Error container does not have any video streams");
return false;
}
const QJsonObject firstStream = streams.first().toObject();
const QString codecName = firstStream.value("codec_name").toVariant().toString();
// It is not that important to check for all codecs,
// we just need a check for the important once to skip
// import convertion if it is the same codec.
if (codecName == "vp8") {
m_sourceCodec = Video::VideoCodec::VP8;
} else if (codecName == "vp9") {
m_sourceCodec = Video::VideoCodec::VP9;
} else if (codecName == "av1") {
m_sourceCodec = Video::VideoCodec::AV1;
} else if (codecName == "h264") {
m_sourceCodec = Video::VideoCodec::H264;
} else if (codecName == "hevc") {
m_sourceCodec = Video::VideoCodec::H265; // HEVC is H.265
} else {
m_sourceCodec = Video::VideoCodec::Unknown;
}
if (m_sourceCodec == Video::VideoCodec::VP8 || m_sourceCodec == Video::VideoCodec::VP9) {
return analyzeWebmReadFrames(obj);
}
bool hasAudioStream { false }; bool hasAudioStream { false };
bool hasVideoStream { false }; bool hasVideoStream { false };
@ -257,16 +278,6 @@ bool CreateImportVideo::analyzeVideo(const QJsonObject& obj)
m_length = static_cast<int>(tmpLength); m_length = static_cast<int>(tmpLength);
// Get framerate
const QJsonArray streams = obj.value("streams").toArray();
if (streams.empty()) {
qDebug() << "Error container does not have any video streams";
emit processOutput("Error container does not have any video streams");
return false;
}
const QJsonObject firstStream = streams.first().toObject();
// The paramter gets us the exact framerate // The paramter gets us the exact framerate
// "avg_frame_rate":"47850000/797509" // "avg_frame_rate":"47850000/797509"
// so we need no calc the value by dividing the two numbers // so we need no calc the value by dividing the two numbers
@ -519,6 +530,18 @@ bool CreateImportVideo::createWallpaperImagePreview()
*/ */
bool CreateImportVideo::createWallpaperVideo() bool CreateImportVideo::createWallpaperVideo()
{ {
const QFileInfo sourceFile(m_videoPath);
if (m_sourceCodec == m_targetCodec) {
qInfo() << "Skip video convert because they are the same";
if (!QFile::copy(sourceFile.absoluteFilePath(), m_exportPath + "/" + sourceFile.fileName())) {
qDebug() << "Could not copy" << sourceFile.absoluteFilePath() << " to " << m_exportPath;
return false;
}
emit createWallpaperStateChanged(Import::State::Finished);
return true;
}
emit createWallpaperStateChanged(Import::State::ConvertingVideo); emit createWallpaperStateChanged(Import::State::ConvertingVideo);
connect(m_process.get(), &QProcess::readyReadStandardOutput, this, [&]() { connect(m_process.get(), &QProcess::readyReadStandardOutput, this, [&]() {
@ -543,6 +566,26 @@ bool CreateImportVideo::createWallpaperVideo()
emit processOutput(tmpOut); emit processOutput(tmpOut);
}); });
QString targetCodec;
QString targetFileEnding;
if (m_targetCodec == Video::VideoCodec::VP8)
targetCodec = "libvpx";
targetFileEnding = ".webm";
if (m_targetCodec == Video::VideoCodec::VP8)
targetCodec = "libvpx";
targetFileEnding = ".webm";
if (m_targetCodec == Video::VideoCodec::AV1)
targetCodec = "libaom-av1";
targetFileEnding = ".mkv";
if (m_targetCodec == Video::VideoCodec::H264) {
targetFileEnding = ".mp4";
if (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows) {
targetCodec = "h264_mf";
} else {
targetCodec = "libx264";
}
}
QStringList args; QStringList args;
args.append("-hide_banner"); args.append("-hide_banner");
args.append("-y"); args.append("-y");
@ -550,12 +593,7 @@ bool CreateImportVideo::createWallpaperVideo()
args.append("-i"); args.append("-i");
args.append(m_videoPath); args.append(m_videoPath);
args.append("-c:v"); args.append("-c:v");
if (m_codec == "vp8") args.append(targetCodec);
args.append("libvpx");
if (m_codec == "vp9")
args.append("libvpx-vp9");
if (m_codec == "av1")
args.append("libaom-av1");
args.append("-b:v"); args.append("-b:v");
args.append("13000k"); args.append("13000k");
args.append("-threads"); args.append("-threads");
@ -593,12 +631,7 @@ bool CreateImportVideo::createWallpaperVideo()
args.append("-i"); args.append("-i");
args.append(m_videoPath); args.append(m_videoPath);
args.append("-c:v"); args.append("-c:v");
if (m_codec == "vp8") args.append(targetCodec);
args.append("libvpx");
if (m_codec == "vp9")
args.append("libvpx-vp9");
if (m_codec == "av1")
args.append("libaom-av1");
args.append("-b:v"); args.append("-b:v");
args.append("13000k"); args.append("13000k");
args.append("-threads"); args.append("-threads");
@ -617,8 +650,7 @@ bool CreateImportVideo::createWallpaperVideo()
args.append(QString::number(m_quality)); args.append(QString::number(m_quality));
args.append("-pass"); args.append("-pass");
args.append("2"); args.append("2");
const QFileInfo file(m_videoPath); const QString convertedFileAbsolutePath { m_exportPath + "/" + sourceFile.completeBaseName() + targetFileEnding };
const QString convertedFileAbsolutePath { m_exportPath + "/" + file.completeBaseName() + ".webm" };
args.append(convertedFileAbsolutePath); args.append(convertedFileAbsolutePath);
const QString ffmpegOutput = waitForFinished(args); const QString ffmpegOutput = waitForFinished(args);
@ -696,7 +728,7 @@ QString CreateImportVideo::waitForFinished(
{ {
m_process = std::make_unique<QProcess>(); m_process = std::make_unique<QProcess>();
QObject::connect(m_process.get(), &QProcess::errorOccurred, [=, this](QProcess::ProcessError error) { QObject::connect(m_process.get(), &QProcess::errorOccurred, this, [=, this](QProcess::ProcessError error) {
qDebug() << "error enum val = " << error << m_process->errorString(); qDebug() << "error enum val = " << error << m_process->errorString();
emit createWallpaperStateChanged(Import::State::AnalyseVideoError); emit createWallpaperStateChanged(Import::State::AnalyseVideoError);
m_process->terminate(); m_process->terminate();

View File

@ -5,9 +5,6 @@
#include <QQmlEngine> #include <QQmlEngine>
#include <QtCore/qmetatype.h> #include <QtCore/qmetatype.h>
// We must package everything into a class for
// qml to be able to have typed enums. Making
// qml enums unscoped as default was a mistake.
namespace ScreenPlay { namespace ScreenPlay {
class ContentTypes : public QObject { class ContentTypes : public QObject {

View File

@ -12,7 +12,7 @@ Item {
property string iconSource: "qrc:/qml/ScreenPlayApp/assets/icons/icon_volume.svg" property string iconSource: "qrc:/qml/ScreenPlayApp/assets/icons/icon_volume.svg"
property alias slider: slider property alias slider: slider
height: 70 height: 80
Text { Text {
id: txtHeadline id: txtHeadline
@ -40,7 +40,7 @@ Item {
left: parent.left left: parent.left
} }
Image { ColorImage {
id: imgIcon id: imgIcon
width: 20 width: 20