mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Systemd changes | root script | URL support for gif effects (#1319)
* Systemd changes and URL option for Gif Effects * Add grayscale to gif effect * WebUI adjustments * Rename version to .version * Copy runHyperionAsRoot.sh to rpi packages * Pack script into all unix packages * Start hyperion only after network is available * Snap builds removed due to poor server connection * Flexible updateHyperionUser.sh * updateHyperionUser script entered in the package * Print help on none sudo execute * Corrected embedded Python location * Replacement for the QWindowsScreen grabWindow function * Updated to latest 2.x mbedtls version 2.27 Co-authored-by: LordGrey <lordgrey.emmel@gmail.com>
This commit is contained in:
@@ -70,9 +70,9 @@ QString EffectFileHandler::deleteEffect(const QString& effectName)
|
||||
{
|
||||
if (effectConfigurationFile.exists())
|
||||
{
|
||||
if ((it->script == ":/effects/gif.py") && !it->args.value("image").toString("").isEmpty())
|
||||
if ((it->script == ":/effects/gif.py") && !it->args.value("file").toString("").isEmpty())
|
||||
{
|
||||
QFileInfo effectImageFile(it->args.value("image").toString());
|
||||
QFileInfo effectImageFile(it->args.value("file").toString());
|
||||
if (effectImageFile.exists())
|
||||
{
|
||||
QFile::remove(effectImageFile.absoluteFilePath());
|
||||
@@ -159,19 +159,26 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message)
|
||||
newFileName.setFile(f);
|
||||
}
|
||||
|
||||
if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("image").toString("").isEmpty())
|
||||
if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("file").toString("").isEmpty())
|
||||
{
|
||||
QJsonObject args = message["args"].toObject();
|
||||
QString imageFilePath = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + args.value("image").toString();
|
||||
QString imageFilePath = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + args.value("file").toString();
|
||||
|
||||
QFileInfo imageFileName(imageFilePath);
|
||||
if (!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log))
|
||||
{
|
||||
return "Error while saving image file '" + message["args"].toObject().value("image").toString() + ", please check the Hyperion Log";
|
||||
return "Error while saving image file '" + message["args"].toObject().value("file").toString() + ", please check the Hyperion Log";
|
||||
}
|
||||
|
||||
//Update json with image file location
|
||||
args["image"] = imageFilePath;
|
||||
args["file"] = imageFilePath;
|
||||
effectJson["args"] = args;
|
||||
}
|
||||
|
||||
if (message["args"].toObject().value("imageSource").toString("") == "url" || message["args"].toObject().value("imageSource").toString("") == "file")
|
||||
{
|
||||
QJsonObject args = message["args"].toObject();
|
||||
args.remove(args.value("imageSource").toString("") == "url" ? "file" : "url");
|
||||
effectJson["args"] = args;
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,10 @@
|
||||
#include <QDateTime>
|
||||
#include <QImageReader>
|
||||
#include <QBuffer>
|
||||
#include <QUrl>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QEventLoop>
|
||||
|
||||
// Get the effect from the capsule
|
||||
#define getEffect() static_cast<Effect*>((Effect*)PyCapsule_Import("hyperion.__effectObj", 0))
|
||||
@@ -219,31 +223,57 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
{
|
||||
QString file;
|
||||
QBuffer buffer;
|
||||
QImageReader reader;
|
||||
char *source;
|
||||
int cropLeft = 0, cropTop = 0, cropRight = 0, cropBottom = 0;
|
||||
bool grayscale = false;
|
||||
|
||||
if (getEffect()->_imageData.isEmpty())
|
||||
{
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
|
||||
char *source;
|
||||
if(!PyArg_ParseTuple(args, "s", &source))
|
||||
if(!PyArg_ParseTuple(args, "s|iiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "String required");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file = QString::fromUtf8(source);
|
||||
const QUrl url = QUrl(source);
|
||||
if (url.isValid())
|
||||
{
|
||||
QNetworkAccessManager *networkManager = new QNetworkAccessManager();
|
||||
QNetworkReply * networkReply = networkManager->get(QNetworkRequest(url));
|
||||
|
||||
if (file.mid(0, 1) == ":")
|
||||
file = ":/effects/"+file.mid(1);
|
||||
QEventLoop eventLoop;
|
||||
connect(networkReply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
|
||||
eventLoop.exec();
|
||||
|
||||
reader.setDecideFormatFromContent(true);
|
||||
reader.setFileName(file);
|
||||
if (networkReply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
buffer.setData(networkReply->readAll());
|
||||
buffer.open(QBuffer::ReadOnly);
|
||||
reader.setDecideFormatFromContent(true);
|
||||
reader.setDevice(&buffer);
|
||||
}
|
||||
|
||||
delete networkReply;
|
||||
delete networkManager;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString file = QString::fromUtf8(source);
|
||||
|
||||
if (file.mid(0, 1) == ":")
|
||||
file = ":/effects/"+file.mid(1);
|
||||
|
||||
reader.setDecideFormatFromContent(true);
|
||||
reader.setFileName(file);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PyArg_ParseTuple(args, "|siiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale);
|
||||
buffer.setData(QByteArray::fromBase64(getEffect()->_imageData.toUtf8()));
|
||||
buffer.open(QBuffer::ReadOnly);
|
||||
reader.setDecideFormatFromContent(true);
|
||||
@@ -260,19 +290,33 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
if (reader.canRead())
|
||||
{
|
||||
QImage qimage = reader.read();
|
||||
|
||||
int width = qimage.width();
|
||||
int height = qimage.height();
|
||||
|
||||
if (cropLeft > 0 || cropTop > 0 || cropRight > 0 || cropBottom > 0)
|
||||
{
|
||||
if (cropLeft + cropRight >= width || cropTop + cropBottom >= height)
|
||||
{
|
||||
QString errorStr = QString("Rejecting invalid crop values: left: %1, right: %2, top: %3, bottom: %4, higher than height/width %5/%6").arg(cropLeft).arg(cropRight).arg(cropTop).arg(cropBottom).arg(height).arg(width);
|
||||
PyErr_SetString(PyExc_RuntimeError, qPrintable(errorStr));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
qimage = qimage.copy(cropLeft, cropTop, width - cropLeft - cropRight, height - cropTop - cropBottom);
|
||||
width = qimage.width();
|
||||
height = qimage.height();
|
||||
}
|
||||
|
||||
QByteArray binaryImage;
|
||||
for (int i = 0; i<height; ++i)
|
||||
for (int i = 0; i<height; i++)
|
||||
{
|
||||
const QRgb *scanline = reinterpret_cast<const QRgb *>(qimage.scanLine(i));
|
||||
for (int j = 0; j< width; ++j)
|
||||
const QRgb *end = scanline + qimage.width();
|
||||
for (; scanline != end; scanline++)
|
||||
{
|
||||
binaryImage.append((char) qRed(scanline[j]));
|
||||
binaryImage.append((char) qGreen(scanline[j]));
|
||||
binaryImage.append((char) qBlue(scanline[j]));
|
||||
binaryImage.append(!grayscale ? (char) qRed(scanline[0]) : (char) qGray(scanline[0]));
|
||||
binaryImage.append(!grayscale ? (char) qGreen(scanline[1]) : (char) qGray(scanline[1]));
|
||||
binaryImage.append(!grayscale ? (char) qBlue(scanline[2]) : (char) qGray(scanline[2]));
|
||||
}
|
||||
}
|
||||
PyList_SET_ITEM(result, i, Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", PyByteArray_FromStringAndSize(binaryImage.constData(),binaryImage.size())));
|
||||
@@ -283,6 +327,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
|
@@ -11,6 +11,10 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
@@ -156,6 +160,58 @@ void QtGrabber::geometryChanged(const QRect &geo)
|
||||
updateScreenDimensions(true);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
extern QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int format = 0);
|
||||
|
||||
QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int height) const
|
||||
{
|
||||
QSize windowSize;
|
||||
int x = xIn;
|
||||
int y = yIn;
|
||||
HWND hwnd = reinterpret_cast<HWND>(window);
|
||||
if (hwnd)
|
||||
{
|
||||
RECT r;
|
||||
GetClientRect(hwnd, &r);
|
||||
windowSize = QSize(r.right - r.left, r.bottom - r.top);
|
||||
}
|
||||
else
|
||||
{
|
||||
hwnd = GetDesktopWindow();
|
||||
const QRect screenGeometry = _screen->geometry();
|
||||
windowSize = screenGeometry.size();
|
||||
x += screenGeometry.x();
|
||||
y += screenGeometry.y();
|
||||
}
|
||||
|
||||
if (width < 0)
|
||||
width = windowSize.width() - x;
|
||||
|
||||
if (height < 0)
|
||||
height = windowSize.height() - y;
|
||||
|
||||
// Create and setup bitmap
|
||||
HDC display_dc = GetDC(nullptr);
|
||||
HDC bitmap_dc = CreateCompatibleDC(display_dc);
|
||||
HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
|
||||
HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
|
||||
|
||||
// copy data
|
||||
HDC window_dc = GetDC(hwnd);
|
||||
BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY);
|
||||
|
||||
// clean up all but bitmap
|
||||
ReleaseDC(hwnd, window_dc);
|
||||
SelectObject(bitmap_dc, null_bitmap);
|
||||
DeleteDC(bitmap_dc);
|
||||
const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
|
||||
DeleteObject(bitmap);
|
||||
ReleaseDC(nullptr, display_dc);
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
#endif
|
||||
|
||||
int QtGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -170,7 +226,11 @@ int QtGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
|
||||
if (_isEnabled)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
QPixmap originalPixmap = grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
|
||||
#else
|
||||
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
|
||||
#endif
|
||||
if (originalPixmap.isNull())
|
||||
{
|
||||
rc = -1;
|
||||
|
@@ -23,17 +23,26 @@ PythonInit::PythonInit()
|
||||
// register modules
|
||||
EffectModule::registerHyperionExtensionModule();
|
||||
|
||||
// Set Program name
|
||||
Py_SetProgramName(L"Hyperion");
|
||||
|
||||
// set Python module path when exists
|
||||
QString py_patch = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python");
|
||||
QString py_patch = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR_MINOR));
|
||||
QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR_MINOR) + ".zip");
|
||||
|
||||
if (QFile(py_file).exists() || QDir(py_patch).exists())
|
||||
{
|
||||
Py_NoSiteFlag++;
|
||||
if (QFile(py_file).exists())
|
||||
{
|
||||
Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
|
||||
Py_SetPath(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
|
||||
}
|
||||
else if (QDir(py_patch).exists())
|
||||
{
|
||||
Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
|
||||
Py_SetPath(Py_DecodeLocale(py_patch.toLatin1().data(), nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
// init Python
|
||||
|
Reference in New Issue
Block a user