Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: working on support for autosave with multiple instances #355

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
7 changes: 6 additions & 1 deletion YUViewApp/src/yuviewapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include "common/typedef.h"
#include "ui/YUViewApplication.h"
#include "common/YUViewInstanceInfo.h"

int main(int argc, char *argv[])
{
Expand All @@ -45,7 +46,11 @@ int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents,false);

qRegisterMetaType<recacheIndicator>("recacheIndicator");

qRegisterMetaType<YUViewInstanceInfo>("YUViewInstanceInfo");
qRegisterMetaType<YUViewInstanceInfoList>("YUViewInstanceInfoList");
qRegisterMetaTypeStreamOperators<YUViewInstanceInfo>("YUViewInstanceInfo");
qRegisterMetaTypeStreamOperators<YUViewInstanceInfoList>("YUViewInstanceInfoList");

YUViewApplication app(argc, argv);

return app.returnCode;
Expand Down
1 change: 1 addition & 0 deletions YUViewLib/src/common/EnumMapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <optional>
#include <string>
#include <vector>
#include <stdexcept>

/* This class implement mapping of "enum class" values to and from names (string).
*/
Expand Down
294 changes: 294 additions & 0 deletions YUViewLib/src/common/YUViewInstanceInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
#include "YUViewInstanceInfo.h"
#include <QCoreApplication>
#include <QProcess>
#include <QSettings>


const QString YUViewInstanceInfo::instanceInfoKey = QString("YUViewInstances");


QDataStream& operator<<(QDataStream &ds, const YUViewInstanceInfo &inObj)
{
ds << inObj.uuid;
// conversion to string should not be necessary.
// but without I get ambiguous overload for operator
ds << QString::number(inObj.pid);
ds << inObj.compressedPlaylist;
return ds;
}

QDataStream& operator>>(QDataStream &ds, YUViewInstanceInfo &outObj)
{
ds >> outObj.uuid;
QString tmp;
ds >> tmp;
outObj.pid = tmp.toLongLong();
ds >> outObj.compressedPlaylist;
return ds;
}

QDataStream& operator<<(QDataStream &ds, const YUViewInstanceInfoList &inObj)
{
const qint64 list_length = inObj.length();
// conversion to string should not be necessary.
// but without I get ambiguous overload for operator
ds << QString::number(list_length);
for( auto instance : inObj )
{
ds << instance;
}
return ds;
}

QDataStream& operator>>(QDataStream &ds, YUViewInstanceInfoList &outObj)
{
outObj.clear();
QString tmp;
ds >> tmp;
const qint64 list_length = tmp.toLongLong();
for(unsigned i = 0; i<list_length; i++)
{
YUViewInstanceInfo tmp;
ds >> tmp;
outObj.push_back(tmp);
}
return ds;
}

void YUViewInstanceInfo::initializeAsNewInstance()
{
QSettings settings;
uuid = QUuid::createUuid();
pid = QCoreApplication::applicationPid();
// append instance to recorded ones
YUViewInstanceInfoList listOfQSettingInstances = YUViewInstanceInfo::getYUViewInstancesInQSettings();
listOfQSettingInstances.append(*this);
settings.setValue(instanceInfoKey, QVariant::fromValue(listOfQSettingInstances));
}

void YUViewInstanceInfo::autoSavePlaylist(const QByteArray &newCompressedPlaylist)
{
QSettings settings;
// set new playlist
this->compressedPlaylist = newCompressedPlaylist;

// change the one stored
YUViewInstanceInfoList listOfQSettingInstances = YUViewInstanceInfo::getYUViewInstancesInQSettings();
QMutableListIterator<YUViewInstanceInfo> it(listOfQSettingInstances);
while (it.hasNext())
{
YUViewInstanceInfo otherInstance = it.next();
if(this->uuid == otherInstance.uuid &&
this->pid == otherInstance.pid )
{
// already had a instance, replace its playlist
it.setValue(*this);
settings.setValue(instanceInfoKey, QVariant::fromValue(listOfQSettingInstances));
return;
}
}

// no instance yet, save new one
// should never be reached, as we create the instance in initializeAsNewInstance,
// thus there always should be one
listOfQSettingInstances.append(*this);
settings.setValue(instanceInfoKey, QVariant::fromValue(listOfQSettingInstances));
return;
}

void YUViewInstanceInfo::dropAutosavedPlaylist()
{
// replace saved on with emtpy playlist
this->autoSavePlaylist(QByteArray());
}

void YUViewInstanceInfo::removeInstanceFromQSettings()
{
QSettings settings;

// find and remove current instance from stored ones
YUViewInstanceInfoList listOfQSettingInstances = YUViewInstanceInfo::getYUViewInstancesInQSettings();
QMutableListIterator<YUViewInstanceInfo> it(listOfQSettingInstances);
while (it.hasNext())
{
YUViewInstanceInfo otherInstance = it.next();
if(this->uuid == otherInstance.uuid &&
this->pid == otherInstance.pid )
{
// found it, now remove it
it.remove();
settings.setValue(instanceInfoKey, QVariant::fromValue(listOfQSettingInstances));
return;
}
}
}

YUViewInstanceInfo YUViewInstanceInfo::getAutosavedPlaylist()
{
QSettings settings;
QList<qint64> listOfPids = YUViewInstanceInfo::getRunningYUViewInstances();
YUViewInstanceInfoList listOfQSettingInstances = YUViewInstanceInfo::getYUViewInstancesInQSettings();

YUViewInstanceInfo candidateForRestore;
QMutableListIterator<YUViewInstanceInfo> it(listOfQSettingInstances);
while (it.hasNext())
{
YUViewInstanceInfo instanceInSettings = it.next();
bool still_running = false;
// get known instances, if instance no longer runs, remove it from QSettings
for( auto pid_running : listOfPids )
{
if( pid_running == instanceInSettings.pid ) still_running = true;
}
if(!still_running)
{
// found an instance which is no longer running: candidate for restore
candidateForRestore = instanceInSettings;
it.remove();
settings.setValue(instanceInfoKey, QVariant::fromValue(listOfQSettingInstances));
break; // keep playlist from potential other crashed instances for other new instances
}
}

return candidateForRestore;
}

void YUViewInstanceInfo::cleanupRecordedInstances()
{
QSettings settings;
QList<qint64> listOfPids = YUViewInstanceInfo::getRunningYUViewInstances();
YUViewInstanceInfoList listOfQSettingInstances = YUViewInstanceInfo::getYUViewInstancesInQSettings();

QMutableListIterator<YUViewInstanceInfo> it(listOfQSettingInstances);
while (it.hasNext())
{
YUViewInstanceInfo instanceInSettings = it.next();
bool still_running = false;
// get known instances, if instance no longer runs, remove it from QSettings
for( auto pid_running : listOfPids )
{
if( pid_running == instanceInSettings.pid ) still_running = true;
}
if(!still_running)
{
// can remove this one
it.remove();
}
}

settings.setValue(instanceInfoKey, QVariant::fromValue(listOfQSettingInstances));
}

bool YUViewInstanceInfo::isValid()
{
return !uuid.isNull();
}

QList<qint64> YUViewInstanceInfo::getRunningYUViewInstances()
{

QList<qint64> listOfPids;

// code for getting process ids adopted from https://www.qtcentre.org/threads/44489-Get-Process-ID-for-a-running-application
// also mentions checking /proc. However there is no proc on macos, while pgrep should be available.

#if defined(Q_OS_WIN)
// Get the list of process identifiers.
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;

if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
{
return 0;
}

// Calculate how many process identifiers were returned.
cProcesses = cbNeeded / sizeof(DWORD);

// Search for a matching name for each process
for (i = 0; i < cProcesses; i++)
{
if (aProcesses[i] != 0)
{
char szProcessName[MAX_PATH] = {0};

DWORD processID = aProcesses[i];

// Get a handle to the process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID);

// Get the process name
if (NULL != hProcess)
{
HMODULE hMod;
DWORD cbNeeded;

if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
{
GetModuleBaseNameA(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(char));
}

// Release the handle to the process.
CloseHandle(hProcess);

if (*szProcessName != 0 && strcmp(processName, szProcessName) == 0)
{
listOfPids.append(QString::number(processID));
}
}
}
}

return listOfPids.count();

#else

// Run pgrep, which looks through the currently running processses and lists the process IDs
// which match the selection criteria to stdout.
QProcess process;
process.start("pgrep", QStringList() << "YUView");
process.waitForReadyRead();

QByteArray bytes = process.readAllStandardOutput();

process.terminate();
process.waitForFinished();
process.kill();

// Output is something like "2472\n2323" for multiple instances
if (bytes.isEmpty())
return QList<qint64>();

// Remove trailing CR
if (bytes.endsWith("\n"))
bytes.resize(bytes.size() - 1);

QStringList strListOfPids = QString(bytes).split("\n");
for( auto strPid : strListOfPids )
{
listOfPids.append(strPid.toLongLong());
}
#endif

return listOfPids;
}

YUViewInstanceInfoList YUViewInstanceInfo::getYUViewInstancesInQSettings()
{
QSettings settings;
YUViewInstanceInfoList instancesInQSettings;
if (settings.contains(instanceInfoKey))
{
instancesInQSettings = settings.value(instanceInfoKey).value<YUViewInstanceInfoList>();
}
return instancesInQSettings;
}

QByteArray YUViewInstanceInfo::getCompressedPlaylist() const
{
return compressedPlaylist;
}


52 changes: 52 additions & 0 deletions YUViewLib/src/common/YUViewInstanceInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef YUVIEWINSTANCEINFO_H
#define YUVIEWINSTANCEINFO_H

#include <QMetaType>
#include <QStringList>
#include <QUuid>
#include <QList>

class YUViewInstanceInfo;
typedef QList<YUViewInstanceInfo> YUViewInstanceInfoList;

class YUViewInstanceInfo
{
public:
YUViewInstanceInfo() = default;
~YUViewInstanceInfo() = default;
YUViewInstanceInfo(const YUViewInstanceInfo &) = default;
YUViewInstanceInfo &operator=(const YUViewInstanceInfo &) = default;

void initializeAsNewInstance();
void autoSavePlaylist(const QByteArray &newCompressedPlaylist);
void dropAutosavedPlaylist();
void removeInstanceFromQSettings();
YUViewInstanceInfo getAutosavedPlaylist();
void cleanupRecordedInstances();
bool isValid();

QByteArray getCompressedPlaylist() const;

friend QDataStream& (operator<<) (QDataStream &ds, const YUViewInstanceInfo &inObj);
friend QDataStream& (operator>>) (QDataStream &ds, YUViewInstanceInfo &outObj);
private:
static QList<qint64> getRunningYUViewInstances();
static YUViewInstanceInfoList getYUViewInstancesInQSettings();

qint64 pid;
QUuid uuid;
QByteArray compressedPlaylist;

static const QString instanceInfoKey; // key used for QSettings
};

QDataStream& operator<<(QDataStream &ds, const YUViewInstanceInfo &inObj);
QDataStream& operator>>(QDataStream &ds, YUViewInstanceInfo &outObj);
QDataStream& operator<<(QDataStream &ds, const YUViewInstanceInfoList &inObj);
QDataStream& operator>>(QDataStream &ds, YUViewInstanceInfoList &outObj);

Q_DECLARE_METATYPE(YUViewInstanceInfo);
Q_DECLARE_METATYPE(YUViewInstanceInfoList);


#endif // YUVIEWINSTANCEINFO_H
3 changes: 2 additions & 1 deletion YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ playlistItemCompressedVideo::newPlaylistItemCompressedVideo(const YUViewDomEleme
const QString &playlistFilePath)
{
// Parse the DOM element. It should have all values of a playlistItemRawCodedVideo
auto absolutePath = root.findChildValue("absolutePath");
QUrl absoluteUrl = root.findChildValue("absolutePath");
auto absolutePath = absoluteUrl.toLocalFile();
auto relativePath = root.findChildValue("relativePath");
int displaySignal = root.findChildValue("displayComponent").toInt();

Expand Down
3 changes: 2 additions & 1 deletion YUViewLib/src/playlistitem/playlistItemImageFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ playlistItemImageFile::newplaylistItemImageFile(const YUViewDomElement &root,
const QString & playlistFilePath)
{
// Parse the DOM element. It should have all values of a playlistItemImageFile
QString absolutePath = root.findChildValue("absolutePath");
QUrl absoluteUrl = root.findChildValue("absolutePath");
auto absolutePath = absoluteUrl.toLocalFile();
QString relativePath = root.findChildValue("relativePath");

// check if file with absolute path exists, otherwise check relative path
Expand Down