1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-11-05 10:32:28 +01:00

Add .screenplay project export import via QArchive

This commit is contained in:
Elias Steurer 2022-05-06 14:51:56 +02:00
parent 12e3aa91ac
commit a366fcc56a
12 changed files with 450 additions and 8 deletions

View File

@ -4,6 +4,17 @@ include(FetchContent)
set(THIRD_PARTY_PATH "${CMAKE_SOURCE_DIR}/ThirdParty/")
FetchContent_Populate(
QArchive
GIT_REPOSITORY https://github.com/antony-jr/QArchive.git
GIT_TAG 2d05e652ad9a2bff8c87962d5525e2b3c4d7351b
# Workaround because:
# 1. QtCreator cannot handle QML_ELEMENT stuff when it is in bin folder
# https://bugreports.qt.io/browse/QTCREATORBUG-27083
SOURCE_DIR ${THIRD_PARTY_PATH}/QArchive
)
FetchContent_Populate(
qml-plausible
GIT_REPOSITORY https://gitlab.com/kelteseth/qml-plausible.git

View File

@ -72,6 +72,10 @@ execute_process(
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE)
include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FetchContentThirdParty.cmake)
add_subdirectory(CMake)
add_subdirectory(ScreenPlay)
add_subdirectory(ScreenPlaySDK)
add_subdirectory(ScreenPlayShader)
@ -79,14 +83,14 @@ add_subdirectory(ScreenPlayWallpaper)
add_subdirectory(ScreenPlayWidget)
add_subdirectory(ScreenPlayUtil)
add_subdirectory(ScreenPlayWeather)
add_subdirectory(CMake)
add_subdirectory(ThirdParty/QArchive)
add_subdirectory(Tools)
if(${SCREENPLAY_TESTS})
enable_testing()
endif()
include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/FetchContentThirdParty.cmake)
# Only add target SteamSDKQtEnums
add_subdirectory(ScreenPlayWorkshop/SteamSDK)

View File

@ -299,6 +299,7 @@ target_link_libraries(
Qt6::Svg
ScreenPlayUtil
ScreenPlayUtilplugin
QArchive
SteamSDKQtEnums)
if(${SCREENPLAY_STEAM})

View File

@ -56,6 +56,10 @@
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlayUtil/util.h"
#include "qarchive_enums.hpp"
#include "qarchivediskcompressor.hpp"
#include "qarchivediskextractor.hpp"
#include <fstream>
#include <iostream>
#include <optional>
@ -106,7 +110,8 @@ public slots:
void copyToClipboard(const QString& text) const;
void openFolderInExplorer(const QString& url) const;
QString toLocal(const QString& url);
bool exportProject(QString& contentPath, QString& exportPath);
bool importProject(QString& archivePath, QString& extractionPath);
void requestAllLicenses();
void requestDataProtection();
@ -148,6 +153,8 @@ private:
QString m_debugMessages {};
QFuture<void> m_requestAllLicensesFuture;
std::unique_ptr<QArchive::DiskCompressor> m_compressor;
std::unique_ptr<QArchive::DiskExtractor> m_extractor;
};
// Used for redirect content from static logToGui to setDebugMessages

View File

@ -1,13 +1,16 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import QtQuick.Controls.Material
import Qt5Compat.GraphicalEffects
import QtQuick.Controls.Material.impl
import Qt.labs.platform 1.1 as Labs
import ScreenPlayApp
import ScreenPlay
import ScreenPlay.Enums.InstalledType
import ScreenPlay.Enums.SearchType
import ScreenPlayUtil
import ScreenPlayUtil as Util
Item {
id: root
@ -245,6 +248,7 @@ Item {
// Set the menu to the current item informations
contextMenu.publishedFileID = delegate.publishedFileID
contextMenu.absoluteStoragePath = delegate.absoluteStoragePath
contextMenu.fileName = delegate.customTitle
const pos = delegate.mapToItem(root, position.x, position.y)
// Disable duplicate opening. The can happen if we
// call popup when we are in the closing animtion.
@ -266,14 +270,29 @@ Item {
property var publishedFileID: 0
property url absoluteStoragePath
property string fileName
MenuItem {
text: qsTr("Open containing folder")
objectName: "openFolder"
icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_folder_open.svg"
onClicked: {
App.util.openFolderInExplorer(
contextMenu.absoluteStoragePath)
App.util.openFolderInExplorer(contextMenu.absoluteStoragePath)
}
}
MenuItem {
text: qsTr("Export")
objectName: enabled ? "removeItem" : "removeWorkshopItem"
icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_download.svg"
onClicked: {
exportFileDialog.absoluteStoragePath = contextMenu.absoluteStoragePath
let urlFileName = Labs.StandardPaths.writableLocation(
Labs.StandardPaths.DesktopLocation) + "/"
+ contextMenu.fileName + ".screenplay"
exportFileDialog.currentFile = urlFileName
exportFileDialog.open()
}
}
@ -297,6 +316,29 @@ Item {
}
}
}
Labs.FileDialog {
id: exportFileDialog
fileMode: FileDialog.SaveFile
property string absoluteStoragePath
onAccepted: {
const success = App.util.exportProject(
exportFileDialog.absoluteStoragePath,
exportFileDialog.currentFile)
}
Dialog {
id: exportFileProgressDialog
modal: true
anchors.centerIn: Overlay.overlay
standardButtons: Dialog.Ok
onAccepted: errorDialog.close()
ProgressBar {
id: exportFileProgressBar
anchors.centerIn: parent
}
}
}
Dialog {
id: deleteDialog
@ -321,4 +363,83 @@ Item {
left: parent.left
}
}
DropArea {
id: dropArea
anchors.fill: parent
property string filePath
onEntered: function (drag) {
dropPopup.open()
}
onDropped: function (drop) {
dropPopup.close()
dropArea.enabled = false
if (drop.urls.length > 1) {
errorDialog.title = qsTr(
"We only support adding one item at once.")
errorDialog.open()
return
}
var file = "" // Convert url to string
file = "" + drop.urls[0]
if (!file.endsWith('.screenplay')) {
errorDialog.title = qsTr(
"File type not supported. We only support '.screenplay' files.")
errorDialog.open()
return
}
importDialog.open()
dropArea.filePath = file
}
onExited: {
dropPopup.close()
}
Dialog {
id: errorDialog
modal: true
anchors.centerIn: Overlay.overlay
standardButtons: Dialog.Ok
onAccepted: errorDialog.close()
}
Dialog {
id: importDialog
modal: true
anchors.centerIn: Overlay.overlay
standardButtons: Dialog.Ok
RowLayout {
Text {
text: qsTr("Import Content...")
}
}
onOpened: {
App.util.importProject(dropArea.filePath,
App.globalVariables.localStoragePath)
dropArea.filePath = ""
}
onAccepted: {
importDialog.close()
}
}
}
Popup {
id: dropPopup
anchors.centerIn: Overlay.overlay
width: root.width * .95
height: root.height * .95
dim: true
modal: true
onOpened: fileDropAnimation.state = "fileDrop"
onClosed: {
fileDropAnimation.state = ""
dropArea.enabled = true
}
Util.FileDropAnimation {
id: fileDropAnimation
anchors.centerIn: parent
}
}
}

View File

@ -97,11 +97,121 @@ void Util::openFolderInExplorer(const QString& url) const
explorer.startDetached();
}
/*!
\brief Removes file///: or file:// from the url/string
*/
QString Util::toLocal(const QString& url)
{
return ScreenPlayUtil::toLocal(url);
}
/*!
\brief Exports a given project into a .screenplay 7Zip file.
*/
bool Util::exportProject(QString& contentPath, QString& exportPath)
{
contentPath = ScreenPlayUtil::toLocal(contentPath);
exportPath = ScreenPlayUtil::toLocal(exportPath);
QDir dir(contentPath);
bool success = true;
if (!dir.exists()) {
qWarning() << "Directory does not exist!" << dir;
return false;
}
QStringList files;
for (auto& item : dir.entryInfoList(QDir::Files)) {
files.append(item.absoluteFilePath());
}
m_compressor = std::make_unique<QArchive::DiskCompressor>(exportPath);
m_compressor->setArchiveFormat(QArchive::SevenZipFormat);
m_compressor->addFiles(files);
/* Connect Signals with Slots (in this case lambda functions). */
QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::started, [&]() {
qInfo() << "[+] Starting Compressor... ";
});
QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::finished, [&]() {
qInfo() << "[+] Compressed File(s) Successfully!";
return;
});
QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::error, [&](short code, QString file) {
qInfo() << "[-] An error has occured :: " << QArchive::errorCodeToString(code) << " :: " << file;
return;
});
QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::progress, [&](QString file, int proc, int total, qint64 br, qint64 bt) {
qInfo() << "Progress::" << file << ":: Done ( " << proc << " / " << total << ") " << (br * 100 / bt) << "%.";
return;
});
m_compressor->start();
return true;
}
/*!
\brief Imports a given project from a .screenplay zip file.
*/
bool Util::importProject(QString& archivePath, QString& extractionPath)
{
archivePath = ScreenPlayUtil::toLocal(archivePath);
extractionPath = ScreenPlayUtil::toLocal(extractionPath);
QFileInfo fileInfo(archivePath);
if (!fileInfo.fileName().endsWith(".screenplay")) {
qWarning() << "Unsupported file type: " << fileInfo.fileName() << ". We only support '.screenplay' files.";
return false;
}
const QString name = fileInfo.fileName().remove(".screenplay");
const auto timestamp = QDateTime::currentDateTime().toString("ddMMyyyyhhmmss-");
extractionPath = extractionPath + "/" + timestamp + name + "/";
QDir dir(extractionPath);
if (dir.exists()) {
qWarning() << "Directory does already exist!" << dir;
return false;
}
if (!dir.mkdir(extractionPath)) {
qWarning() << "Unable to create directory:" << dir;
return false;
}
m_extractor = std::make_unique<QArchive::DiskExtractor>(archivePath, extractionPath);
QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::started, [&]() {
qInfo() << "[+] Starting Extractor... ";
});
QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::finished, [&]() {
qInfo() << "[+] Extracted File(s) Successfully!";
return;
});
QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::error, [&](short code) {
if (code == QArchive::ArchivePasswordNeeded || code == QArchive::ArchivePasswordIncorrect) {
return;
}
qInfo() << "[-] An error has occured :: " << QArchive::errorCodeToString(code);
return;
});
QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::info, [&](QJsonObject info) {
qInfo() << "ARCHIVE CONTENTS:: " << info;
return;
});
QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::progress,
[&](QString file, int proc, int total, qint64 br, qint64 bt) {
qInfo() << "Progress(" << proc << "/" << total << "): "
<< file << " : " << (br * 100 / bt) << "% done.";
});
m_extractor->setCalculateProgress(true);
m_extractor->getInfo();
m_extractor->start();
return true;
}
/*!
\brief Loads all content of the legal folder in the qrc into a property string of this class.
allLicenseLoaded is emited when loading is finished.

View File

@ -29,6 +29,7 @@ set(QML
qml/RippleEffect.qml
qml/Search.qml
qml/Shake.qml
qml/FileDropAnimation.qml
qml/Slider.qml
qml/Tag.qml
qml/TagSelector.qml
@ -52,6 +53,11 @@ set(HEADER
inc/public/ScreenPlayUtil/SingletonHelper.h
inc/public/ScreenPlayUtil/util.h)
set(RESOURCES
assets/icons/folder.svg
assets/icons/description.svg
assets/icons/attach_file.svg)
qt_add_library(
${PROJECT_NAME}
STATIC
@ -68,7 +74,9 @@ qt_add_qml_module(
VERSION
1.0
QML_FILES
${QML})
${QML}
RESOURCES
${RESOURCES})
find_path(CPP_HTTPLIB_INCLUDE_DIRS "httplib.h")
target_include_directories(${PROJECT_NAME} PUBLIC ${CPP_HTTPLIB_INCLUDE_DIRS})

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M33 12v23c0 4.42-3.58 8-8 8s-8-3.58-8-8V10c0-2.76 2.24-5 5-5s5 2.24 5 5v21c0 1.1-.89 2-2 2-1.11 0-2-.9-2-2V12h-3v19c0 2.76 2.24 5 5 5s5-2.24 5-5V10c0-4.42-3.58-8-8-8s-8 3.58-8 8v25c0 6.08 4.93 11 11 11s11-4.92 11-11V12h-3z"/></svg>

After

Width:  |  Height:  |  Size: 323 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M28 4H12C9.79 4 8.02 5.79 8.02 8L8 40c0 2.21 1.77 4 3.98 4H36c2.21 0 4-1.79 4-4V16L28 4zm4 32H16v-4h16v4zm0-8H16v-4h16v4zm-6-10V7l11 11H26z"/></svg>

After

Width:  |  Height:  |  Size: 240 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M20 8H8c-2.21 0-3.98 1.79-3.98 4L4 36c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V16c0-2.21-1.79-4-4-4H24l-4-4z"/></svg>

After

Width:  |  Height:  |  Size: 207 B

View File

@ -0,0 +1,176 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Material
import Qt5Compat.GraphicalEffects
Item {
id: root
height: 400
width: 400
property int secondaryIconSize: 180
property int centerIconSize: 210
property int growSize: 40
Image {
id: fileRight
opacity: 0
width: root.secondaryIconSize
height: root.secondaryIconSize
rotation: -5
smooth: true
antialiasing: true
mipmap: true
source: "qrc:/qml/ScreenPlayUtil/assets/icons/attach_file.svg"
sourceSize: Qt.size(width, width)
layer {
enabled: true
smooth: true
effect: ColorOverlay {
color: Material.iconColor
}
}
anchors {
top: parent.top
topMargin: 150
left: fileCenter.horizontalCenter
leftMargin: -root.secondaryIconSize
}
}
Image {
id: fileLeft
opacity: 0
width: root.secondaryIconSize
height: root.secondaryIconSize
source: "qrc:/qml/ScreenPlayUtil/assets/icons/description.svg"
sourceSize: Qt.size(width, width)
layer {
enabled: true
smooth: true
effect: ColorOverlay {
color: Material.iconColor
}
}
rotation: 5
smooth: true
antialiasing: true
mipmap: true
anchors {
top: parent.top
topMargin: 150
right: fileCenter.horizontalCenter
rightMargin: -root.secondaryIconSize
}
}
Image {
id: fileCenter
opacity: 0
width: root.centerIconSize
height: root.centerIconSize
source: "qrc:/qml/ScreenPlayUtil/assets/icons/folder.svg"
sourceSize: Qt.size(width, width)
layer {
enabled: true
smooth: true
effect: ColorOverlay {
color: Material.color(Material.Orange)
}
}
smooth: true
antialiasing: true
mipmap: true
anchors {
top: parent.top
topMargin: 150
horizontalCenter: parent.horizontalCenter
}
Material.elevation: 6
}
Text {
id: txt
color: Material.primaryTextColor
opacity: 0
anchors.top: fileCenter.bottom
anchors.topMargin: 100
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 16
text: qsTr("Drop your '.screenplay' file here to add it to your Installed content.")
horizontalAlignment: Text.AlignHCenter
}
states: [
State {
name: "fileDrop"
PropertyChanges {
target: fileCenter
opacity: 1
anchors.topMargin: 100
width: root.centerIconSize + root.growSize
height: root.centerIconSize + root.growSize
}
PropertyChanges {
target: txt
anchors.topMargin: 20
opacity: 1
}
PropertyChanges {
target: fileRight
opacity: 1
anchors.topMargin: 100
anchors.leftMargin: -170 - root.growSize
width: root.secondaryIconSize + root.growSize
height: root.secondaryIconSize + root.growSize
rotation: -10
}
PropertyChanges {
target: fileLeft
opacity: 1
anchors.topMargin: 100
anchors.rightMargin: -170 - root.growSize
width: root.secondaryIconSize + root.growSize
height: root.secondaryIconSize + root.growSize
rotation: 10
}
}
]
transitions: Transition {
AnchorAnimation {
duration: 400
}
PropertyAnimation {
target: fileCenter
properties: "anchors.topMargin, width, height, opacity"
duration: 400
easing.type: Easing.OutCirc
}
PropertyAnimation {
target: txt
properties: "anchors.topMargin, opacity"
duration: 400
easing.type: Easing.OutCirc
}
PropertyAnimation {
target: fileRight
properties: "anchors.topMargin,anchors.leftMargin, width, height, rotation, opacity"
duration: 400
easing.type: Easing.OutCirc
}
PropertyAnimation {
target: fileLeft
properties: "anchors.topMargin, anchors.rightMargin, width, height, rotation, opacity"
duration: 400
easing.type: Easing.OutCirc
}
RotationAnimation {
target: fileLeft
duration: 800
}
RotationAnimation {
target: fileRight
duration: 800
}
}
}

View File

@ -12,7 +12,8 @@ vcpkg_version = "2ac61f8" # Master 23.04.2022
vcpkg_packages_list = [
"openssl",
"curl",
"cpp-httplib"
"cpp-httplib",
"libarchive"
]
class commands_list():