Skip to content
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
Loading