#include "gentmpthread.h" #include "cfg.h" #include "globaldefine.h" #include "tools.h" #include "program/eaclock.h" #include "program/eaudio.h" #include "program/edclock.h" #include "program/eenviron.h" #include "program/egif.h" #include "program/etext.h" #include "program/etimer.h" #include "program/evideo.h" #include #include #include #include GenTmpThread::GenTmpThread(ProgItem *progItem, const QString &prog_name, const QString &zip_file, const QString &password, QObject *parent) : QThread(parent), mProgItem(progItem), prog_name(prog_name), zip_file(zip_file), password(password) { connect(this, &QThread::finished, this, &QThread::deleteLater); connect(this, &GenTmpThread::onErr, this, [](QString err) { QMessageBox::warning(gMainWin, "GenTmpThread Error", err); }); } void GenTmpThread::run() { auto srcDir = programsDir() + "/" + prog_name; dstDir = srcDir + "_tmp"; //清空目录 emit sProgress(tr("Preparing ..."), 0); QDir progsDir(programsDir()); progsDir.remove(prog_name + "_tmp.zip"); QDir dstQDir(dstDir); if(! dstQDir.exists() || dstQDir.removeRecursively()) { int iReTryCount = 0; while(!progsDir.mkdir(prog_name + "_tmp")) { QThread::msleep(250); iReTryCount++; if(iReTryCount > 4) break; } } QFile jsonFile(srcDir+"/pro.json"); if(! jsonFile.open(QIODevice::ReadOnly)) { onErr("Can't open "+srcDir+"/pro.json"); return; } auto data = jsonFile.readAll(); jsonFile.close(); QJsonParseError error; auto proJson = QJsonDocument::fromJson(data, &error); if(error.error != QJsonParseError::NoError) { onErr("Parse "+srcDir+"/pro.json Error: "+error.errorString()); return; } //扫描节目, 返回多个节目数组 emit sProgress(tr("Scan program ..."), 30); QStringList pageNames = QDir(srcDir).entryList(QDir::Dirs | QDir::NoDotAndDotDot); //查询 order 属性, 将最上层的放在转换后 layers 的最前面 //一个 page.json 对应节目任务中的一个 items 里的 program QList pageJsons; foreach(QString pageName, pageNames) { QFile jsonFile(srcDir+"/"+pageName+"/page.json"); if(jsonFile.open(QIODevice::ReadOnly)) { auto data = jsonFile.readAll(); jsonFile.close(); auto pageJson = QJsonDocument::fromJson(data, &error); if(error.error == QJsonParseError::NoError) pageJsons.append(pageJson); } } std::sort(pageJsons.begin(), pageJsons.end(), [](const QJsonDocument &a, const QJsonDocument &b) { return a["order"].toInt() < b["order"].toInt(); }); QJsonArray items; foreach(QJsonDocument pageJson, pageJsons) { srcPageDir = srcDir + "/" + pageJson["name"].toString(); items.append(cvtPage(pageJson, proJson)); } emit sProgress(tr("Create json ..."), 60); QJsonObject json; json["_type"] = "PlayXixunTask"; json["id"] = QUuid::createUuid().toString(QUuid::WithoutBraces); json["preDownloadURL"] = "http://192.168.8.202:23412/file/download?id="; json["notificationURL"] = "http://192.168.8.202:23412/test"; json["task"] = QJsonObject{ {"_id", QUuid::createUuid().toString(QUuid::WithoutBraces)}, {"name", prog_name}, {"cmdId", QUuid::createUuid().toString(QUuid::WithoutBraces)}, {"items", items} }; QFile program(dstDir + "/program"); if(program.open(QFile::WriteOnly)) { program.write(QJsonDocument(json).toJson()); program.close(); } //如果是usb更新则生成压缩包,网络发送则不需要 if(! zip_file.isEmpty()) { QStringList args{"a", zip_file, dstDir+"/*"}; if(! password.isEmpty()) args << "-p"+password; QProcess::execute("7z.exe", args); } } //此处需要把幻灯片中的元素按层顺序排序,再放入layers中,每个元素对一个layer。ewindow中的多个顺序元素为一个层上的时间轴上的素材 QJsonObject GenTmpThread::cvtPage(const QJsonDocument &pageJson, const QJsonDocument &proJson) { auto audios = pageJson["audios"].toArray(); auto sourceRepeat = pageJson["loop"].toBool(); QJsonArray sources; int start = 0; foreach(QJsonValue audio, audios) { auto dur = audio["dur"].toInt(); if(dur==0) continue; auto name = audio["name"].toString(); if(name.isEmpty()) continue; QString file = audio["dir"].toString()+"/"+name; QFileInfo srcInfo(file); if(! srcInfo.isFile()) continue; QString id = Tools::fileMd5(file); if(id.isEmpty()) continue; QFile::copy(file, dstDir+"/"+id); QJsonObject source; source.insert("_type", "Audio"); source["id"] = id; source["md5"] = id; source["name"] = name; source["size"] = srcInfo.size(); source["fileExt"] = srcInfo.suffix().toLower(); source["timeSpan"] = dur; source["playTime"] = start; source["vol"] = audio["vol"].toInt(); source["enabled"] = true; source["left"] = -1; source["top"] = -1; source["width"] = 1; source["height"] = 1; sources.append(source); start += dur; } QJsonArray layers; if(! sources.isEmpty()) layers.append(QJsonObject{{"repeat", sourceRepeat}, {"sources", sources}}); auto elements = pageJson["elements"].toArray(); foreach(auto ele, elements) { QString type = ele["elementType"].toString(); auto geometry = ele["geometry"]; QJsonArray sources; if(type=="Window") { auto elements = ele["elements"].toArray(); auto left = geometry["x"]; auto top = geometry["y"]; auto width = geometry["w"]; auto height = geometry["h"]; QList eles; foreach(auto value, elements) eles.append(value.toObject()); std::sort(eles.begin(), eles.end(), [](const QJsonObject &a, const QJsonObject &b) { return a["geometry"]["order"].toInt() < b["geometry"]["order"].toInt(); }); int playTime = 0; foreach(const auto ele, eles) { QJsonObject source = cvtEle(ele["elementType"].toString(), ele); if(source.isEmpty()) continue; source["left"] = left; source["top"] = top; source["width"] = width; source["height"] = height; source["playTime"] = playTime; playTime += source["timeSpan"].toInt(); sources.append(source); } } else { QJsonObject source = cvtEle(type, ele.toObject()); if(! source.isEmpty()) { if(mProgItem->mMaxWidth && (type=="Photo" || type=="Movie")) { source["left"] = 0; source["top"] = 0; source["width"] = mProgItem->mMaxWidth; source["height"] = mProgItem->mHeight * mProgItem->mSplitWidths.size(); } else { source["left"] = geometry["x"]; source["top"] = geometry["y"]; source["width"] = geometry["w"]; source["height"] = geometry["h"]; } source["playTime"] = 0; sources.append(source); } } if(! sources.isEmpty()) { QJsonObject layer{{"repeat", sourceRepeat}, {"sources", sources}}; auto border = ele["border"].toString(); if(! border.isEmpty()) { QString bdSrc = ":res/borders/"+border; QString id = Tools::fileMd5(bdSrc); QFile::copy(bdSrc, dstDir+"/"+id); auto borderSize = ele["borderSize"]; if(borderSize.isUndefined() || borderSize.isNull()){ QImage img(bdSrc); borderSize = QJsonArray{img.width(), img.height()}; } layer.insert("border", QJsonObject{ {"img", id}, {"eff", ele["borderEff"]}, {"speed", ele["borderSpeed"]}, {"img_size", borderSize}, {"geometry", QJsonArray{geometry["x"], geometry["y"], geometry["w"], geometry["h"]}} }); } layers.append(layer); } } QJsonArray schedules, plans = pageJson["plans"].toArray(); auto validDate = pageJson["validDate"]; bool isValid = validDate["isValid"].toBool(); if(plans.isEmpty()) { if(isValid) { QJsonObject schedule; schedule["dateType"] = "Range"; schedule["startDate"] = validDate["start"]; schedule["endDate"] = validDate["end"]; schedule["timeType"] = "All"; schedule["filterType"] = "None"; schedule["monthFilter"] = QJsonArray(); schedule["weekFilter"] = QJsonArray(); schedules.append(schedule); } } else { foreach(QJsonValue plan, plans) { QJsonObject schedule; if(isValid) { schedule["dateType"] = "Range"; schedule["startDate"] = validDate["start"]; schedule["endDate"] = validDate["end"]; } else schedule["dateType"] = "All"; schedule["timeType"] = "Range"; schedule["startTime"] = plan["start"]; schedule["endTime"] = plan["end"]; auto weekly = plan["weekly"]; schedule["weekFilter"] = weekly; schedule["filterType"] = weekly.toArray().isEmpty() ? "None" : "Week"; schedule["monthFilter"] = QJsonArray(); schedules.append(schedule); } } return QJsonObject{ {"_id", QUuid::createUuid().toString(QUuid::WithoutBraces)}, {"priority", 0}, {"version", 0}, {"schedules", schedules}, {"_program", QJsonObject{ {"_id", QUuid::createUuid().toString(QUuid::WithoutBraces)}, {"totalSize", 0}, {"name", pageJson["name"].toString()}, {"width", proJson["resolution"]["w"]}, {"height", proJson["resolution"]["h"]}, {"splitWidths", proJson["splitWidths"]}, {"_company", "alahover"}, {"_department", "alahover"}, {"_role", "alahover"}, {"_user", "alahover"}, {"__v", 0}, {"dateCreated", pageJson["last_edit"]}, {"layers", layers} }}, {"repeatTimes", pageJson["repeat"]} }; } QJsonObject GenTmpThread::cvtEle(const QString &type, const QJsonObject &ele) { if(type=="Text") return convertText(ele); else if(type=="Photo") return convertPhoto(ele); else if(type=="Movie") return EVideo::genProg(ele, dstDir, mProgItem); else if(type=="Gif") return convertGif(ele); else if(type=="DClock") return convertDClock(ele); else if(type=="AClock") return convertAClock(ele); else if(type=="Temp") return EEnviron::genProg(ele, dstDir, srcPageDir); else if(type=="Web") return convertWeb(ele); else if(type=="Timer") return convertTimer(ele); else if(type=="Audio") return EAudio::genProg(ele, dstDir); return QJsonObject(); } QJsonObject GenTmpThread::convertText(const QJsonObject &json) { EText::Data attr; EText::setElement(json, attr); QJsonObject source; auto type = mProgItem->mMaxWidth ? "SplitText" : "MultiPng"; source["_type"] = type; source["name"] = type; source["id"] = res_id++; auto widget = json["widget"]; QJsonArray srcFiles = widget["files"].toArray(); source["iPicCount"] = srcFiles.size(); if(attr.playMode==EText::Flip) { source["playMode"] = "Flip"; source["timeSpan"] = attr.flip.pageDuration * srcFiles.size(); if(attr.flip.effectDuration >= attr.flip.pageDuration) attr.flip.effectDuration = attr.flip.pageDuration / 2; } else if(attr.playMode==EText::Scroll) { source["playMode"] = "Scroll"; source["timeSpan"] = attr.scroll.duration; } else if(attr.playMode==EText::Static) { source["playMode"] = "Static"; source["timeSpan"] = attr.duration; } QJsonArray arrayPics; QString filePrefix = srcPageDir+"/"+widget["idDir"].toString()+"/"; for(int i=0; imMaxWidth) { srcFile += "-square.png"; auto md5 = Tools::fileMd5(srcFile); if(md5.isEmpty()) return source; QFile::copy(srcFile, dstDir+"/"+md5); source["id"] = md5; source["md5"] = md5; source["size"] = srcInfo.size(); source["name"] = name; source["fileExt"] = "png"; source["mime"] = "image/png"; } else { QImage img(srcFile); auto geometry = ele["geometry"]; int width = geometry["w"].toInt(); int height = geometry["h"].toInt(); if(img.width() > width*2 && img.height() > height*2) { QBuffer buf; img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).save(&buf, "PNG"); QCryptographicHash cryptoHash(QCryptographicHash::Md5); cryptoHash.addData(buf.data()); auto md5 = QString::fromLatin1(cryptoHash.result().toHex()); QFile file(dstDir+"/"+md5); if(! file.open(QFile::WriteOnly)) return source; file.write(buf.data()); file.close(); source["id"] = md5; source["md5"] = md5; source["size"] = buf.size(); source["name"] = srcInfo.baseName()+".png"; source["fileExt"] = "png"; source["mime"] = "image/png"; } else { auto md5 = Tools::fileMd5(srcFile); if(md5.isEmpty()) return source; QFile::copy(srcFile, dstDir+"/"+md5); source["id"] = md5; source["md5"] = md5; source["size"] = srcInfo.size(); source["name"] = name; QString sufLower = srcInfo.suffix().toLower(); source["fileExt"] = sufLower; source["mime"] = "image/"+(sufLower=="jpg" ? "jpeg" : sufLower); } } source["_type"] = "Image"; auto play = ele["play"]; source["timeSpan"] = play["playDuration"]; source["enabled"] = true; source["enabledBy"] = ""; source["oldFilePath"] = ""; static const int effSize = 13; static const int effs[effSize] = {0,1,3,4,5,6,7,9,10,11,12,17,18}; int enterStyle = play["enterStyle"].toInt(); source["entryEffect"] = enterStyle < effSize ? effs[enterStyle] : 0; source["exitEffect"] = 0; source["entryEffectTimeSpan"] = play["enterDuration"]; source["exitEffectTimeSpan"] = 0; return source; } //转换图片 QJsonObject GenTmpThread::convertGif(const QJsonObject &json) { auto widget = json["widget"]; auto path = widget["path"].toString(); auto name = widget["file"].toString(); QString srcFile = path + "/" + name; QFileInfo srcInfo(srcFile); if(! srcInfo.isFile()) return QJsonObject(); QString id = Tools::fileMd5(srcFile); if(id.isEmpty()) return QJsonObject(); QFile::copy(srcFile, dstDir+"/"+id); QJsonObject oRes; oRes["_type"] = "Image"; oRes["id"] = id; oRes["md5"] = id; oRes["name"] = name; oRes["size"] = srcInfo.size(); QString suffix = srcInfo.suffix().toLower(); oRes["fileExt"] = suffix; oRes["mime"] = "image/gif"; auto play = json["play"]; oRes["timeSpan"] = play["playDuration"].toInt() * play["playTimes"].toInt(); oRes["enabled"] = true; oRes["entryEffect"] = "None"; oRes["exitEffect"] = "None"; oRes["entryEffectTimeSpan"] = 0; oRes["exitEffectTimeSpan"] = 0; return oRes; } QJsonObject GenTmpThread::convertDClock(const QJsonObject &json){ QJsonObject oRes; oRes["_type"] = "DigitalClockNew"; oRes["name"] = "DigitalClockNew"; oRes["id"] = ""; oRes["timeSpan"] = json["play"]["duration"]; oRes["timezone"] = 8; auto widget = json["widget"]; oRes["year"] = widget["year"]; oRes["month"] = widget["month"]; oRes["day"] = widget["day"]; oRes["hour"] = widget["hour"]; oRes["min"] = widget["min"]; oRes["sec"] = widget["sec"]; oRes["weekly"] = widget["weekly"]; oRes["fullYear"] = widget["fullYear"]; oRes["hour12"] = widget["12Hour"]; oRes["AmPm"] = widget["AmPm"]; oRes["dateStyle"] = widget["dateStyle"]; oRes["timeStyle"] = widget["timeStyle"]; oRes["multiline"] = widget["multiline"]; oRes["fontSize"] = widget["font"]["size"]; oRes["entryEffect"] = "None"; oRes["exitEffect"] = "None"; oRes["entryEffectTimeSpan"] = 0; oRes["exitEffectTimeSpan"] = 0; auto fontVal = widget["font"]; auto textColor = Tools::int2Color(fontVal["color"].toInt()); QFont font(fontVal["family"].toString(), fontVal["size"].toInt()); font.setBold(fontVal["bold"].toBool()); font.setItalic(fontVal["italics"].toBool()); font.setUnderline(fontVal["underline"].toBool()); font.setStyleStrategy(gTextAntialiasing ? QFont::PreferAntialias : QFont::NoAntialias); QFontMetricsF metricF(font); oRes["spaceWidth"] = metricF.horizontalAdvance(" "); QFontMetrics metric(font); QColor color(textColor); QJsonArray imgs; for(auto str : str0_9) Tools::saveImg2(dstDir, metric, font, color, imgs, str, str); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("MON"), "MON"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("TUE"), "TUE"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("WED"), "WED"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("THU"), "THU"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("FRI"), "FRI"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("SAT"), "SAT"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("SUN"), "SUN"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("AM"), "AM"); Tools::saveImg2(dstDir, metric, font, color, imgs, tr("PM"), "PM"); Tools::saveImg2(dstDir, metric, font, color, imgs, "年", "YEAR"); Tools::saveImg2(dstDir, metric, font, color, imgs, "月", "MONTH"); Tools::saveImg2(dstDir, metric, font, color, imgs, "日", "DAY"); Tools::saveImg2(dstDir, metric, font, color, imgs, ":", "maohao"); Tools::saveImg2(dstDir, metric, font, color, imgs, "/", "xiegang"); Tools::saveImg2(dstDir, metric, font, color, imgs, "-", "hengxian"); oRes["iPicCount"] = imgs.size(); oRes["arrayPics"] = imgs; return oRes; } QJsonObject GenTmpThread::convertAClock(const QJsonObject &json) { auto widget = json["widget"]; QString srcFile = srcPageDir + "/" + widget["selfCreateDialName"].toString(); QFile srcQFile(srcFile); if(! srcQFile.exists()) return QJsonObject(); QString id = Tools::fileMd5(srcFile); srcQFile.copy(dstDir+"/"+id); QJsonObject oRes; oRes["_type"] = "AnalogClock"; oRes["id"] = id; oRes["md5"] = id; oRes["mime"] = "image/png"; oRes["name"] = "001"; oRes["timeSpan"] = json["play"]["duration"]; oRes["shade"] = 0;//表盘形状 oRes["opacity"] = 1;//透明度 oRes["showBg"] = false;//是否显示背景色 oRes["bgColor"] = 0; oRes["showHourScale"] = false;//是否显示时针 auto color = widget["hourMarkColor"]; oRes["scaleHourColor"] = color.isString() ? color : Tools::int2Color(color.toInt()).name(); color = widget["minMarkColor"]; oRes["scaleMinColor"] = color.isString() ? color : Tools::int2Color(color.toInt()).name(); color = widget["hourHandColor"]; oRes["pinHourColor"] = color.isString() ? color : Tools::int2Color(color.toInt()).name(); color = widget["minHandColor"]; oRes["pinMinColor"] = color.isString() ? color : Tools::int2Color(color.toInt()).name(); color = widget["secHandColor"]; oRes["pinSecColor"] = color.isString() ? color : Tools::int2Color(color.toInt()).name(); oRes["pinHourLen"] = widget["hhLen"].toInt(); oRes["pinMinLen"] = widget["mhLen"].toInt(); oRes["pinSecLen"] = widget["shLen"].toInt(); oRes["pinHourWidth"] = widget["hhWidth"].toInt(); oRes["pinMinWidth"] = widget["mhWidth"].toInt(); oRes["pinSecWidth"] = widget["shWidth"].toInt(); oRes["showMinScale"] = false; oRes["scaleStyle"] = 0; oRes["showScaleNum"] = false; oRes["pinStyle"] = 1; oRes["showSecond"] = widget["showSecHand"]; //下同Video oRes["entryEffect"] = "None"; oRes["exitEffect"] = "None"; oRes["entryEffectTimeSpan"] = 0; oRes["exitEffectTimeSpan"] = 0 ; return oRes; } QJsonObject GenTmpThread::convertWeb(const QJsonObject &res) { QJsonObject dst; dst["_type"] = "WebURL"; dst["id"] = ""; dst["name"] = "WebURL"; dst["url"] = res["url"]; dst["timeSpan"] = res["duration"]; return dst; } QJsonObject GenTmpThread::convertTimer(const QJsonObject &json) { QJsonObject oRes; oRes["_type"] = "Timer"; oRes["name"] = "Timer"; oRes["id"] = ""; oRes["targetTime"] = json["targetTime"]; oRes["isDown"] = json["isDown"]; oRes["hasDay"] = json["hasDay"]; oRes["hasHour"] = json["hasHour"]; oRes["hasMin"] = json["hasMin"]; oRes["hasSec"] = json["hasSec"]; oRes["isMultiline"] = json["isMultiline"]; auto text = json["text"].toString(); oRes["text"] = text; QFont font(json["font"].toString(), json["fontSize"].toInt()); font.setBold(json["fontBold"].toBool()); font.setItalic(json["fontItalic"].toBool()); font.setUnderline(json["fontUnderline"].toBool()); oRes["font"] = font.family(); oRes["fontSize"] = font.pixelSize(); oRes["fontBold"] = font.bold(); oRes["fontItalic"] = font.italic(); oRes["fontUnderline"] = font.underline(); auto textColor = json["textColor"].toString(); oRes["textColor"] = textColor; oRes["backColor"] = json["backColor"]; oRes["timeSpan"] = json["duration"]; font.setStyleStrategy(gTextAntialiasing ? QFont::PreferAntialias : QFont::NoAntialias); QFontMetricsF metricF(font); oRes["spaceWidth"] = metricF.horizontalAdvance(" "); QFontMetrics metric(font); QColor color(textColor); QJsonObject imgs; for(auto str : str0_9) Tools::saveImg(dstDir, metric, font, color, imgs, str, str); Tools::saveImg(dstDir, metric, font, color, imgs, tr("day"), "day"); Tools::saveImg(dstDir, metric, font, color, imgs, tr("hour"), "hour"); Tools::saveImg(dstDir, metric, font, color, imgs, tr("min"), "min"); Tools::saveImg(dstDir, metric, font, color, imgs, tr("sec"), "sec"); if(! text.isEmpty()) { auto innerW = json["innerW"].toInt(); auto innerH = json["innerH"].toInt(); auto rect = metric.boundingRect(0, 0, innerW, innerH, Qt::AlignCenter | Qt::TextWordWrap, text); QImage img(qMin(rect.width(), innerW), qMin(rect.height(), innerH), QImage::Format_ARGB32); img.fill(Qt::transparent); { QPainter painter(&img); painter.setFont(font); painter.setPen(color); painter.drawText(QRectF(0, 0, img.width(), img.height()), text, QTextOption(Qt::AlignCenter)); } QByteArray data; QBuffer buffer(&data); buffer.open(QIODevice::WriteOnly); if(img.save(&buffer, "PNG")) { QCryptographicHash cryptoHash(QCryptographicHash::Md5); cryptoHash.addData(data); auto md5 = QString::fromLatin1(cryptoHash.result().toHex()); QFile file(dstDir+"/"+md5); if(file.open(QFile::WriteOnly)) { file.write(data); file.close(); imgs.insert("text", md5); } else emit onErr("convertTimer file.open false"); } else emit onErr("convertTimer img.save false"); } oRes["imgs"] = imgs; return oRes; }