#include "PresetManager.h" #include "WarpGraphModel.h" #include #include #include #include #include #include bool PresetManager::savePreset(const QString &path, warppipe::Client *client, const WarpGraphModel *model) { if (!client || !model) return false; QJsonArray devicesArray; auto nodesResult = client->ListNodes(); if (nodesResult.ok()) { for (const auto &node : nodesResult.value) { if (!node.is_virtual) continue; QJsonObject dev; dev["name"] = QString::fromStdString(node.name); dev["description"] = QString::fromStdString(node.description); dev["media_class"] = QString::fromStdString(node.media_class); auto portsResult = client->ListPorts(node.id); int channels = 0; if (portsResult.ok()) { for (const auto &port : portsResult.value) { if (port.is_input) ++channels; } if (channels == 0) { for (const auto &port : portsResult.value) { if (!port.is_input) ++channels; } } } dev["channels"] = channels > 0 ? channels : 2; devicesArray.append(dev); } } QJsonArray routingArray; auto linksResult = client->ListLinks(); if (linksResult.ok() && nodesResult.ok()) { std::unordered_map> portMap; for (const auto &node : nodesResult.value) { auto portsResult = client->ListPorts(node.id); if (!portsResult.ok()) continue; for (const auto &port : portsResult.value) { portMap[port.id.value] = {node.name, port.name}; } } for (const auto &link : linksResult.value) { auto outIt = portMap.find(link.output_port.value); auto inIt = portMap.find(link.input_port.value); if (outIt == portMap.end() || inIt == portMap.end()) continue; QJsonObject route; route["out_node"] = QString::fromStdString(outIt->second.first); route["out_port"] = QString::fromStdString(outIt->second.second); route["in_node"] = QString::fromStdString(inIt->second.first); route["in_port"] = QString::fromStdString(inIt->second.second); routingArray.append(route); } } QJsonArray layoutArray; for (auto qtId : model->allNodeIds()) { const WarpNodeData *data = model->warpNodeData(qtId); if (!data) continue; QPointF pos = model->nodeData(qtId, QtNodes::NodeRole::Position).toPointF(); QJsonObject nodeLayout; nodeLayout["name"] = QString::fromStdString(data->info.name); nodeLayout["x"] = pos.x(); nodeLayout["y"] = pos.y(); layoutArray.append(nodeLayout); } QJsonArray volumesArray; for (auto qtId : model->allNodeIds()) { const WarpNodeData *data = model->warpNodeData(qtId); if (!data) continue; auto vs = model->nodeVolumeState(qtId); if (vs.volume != 1.0f || vs.mute) { QJsonObject volObj; volObj["name"] = QString::fromStdString(data->info.name); volObj["volume"] = static_cast(vs.volume); volObj["mute"] = vs.mute; volumesArray.append(volObj); } } QJsonObject root; root["version"] = 1; root["virtual_devices"] = devicesArray; root["routing"] = routingArray; root["layout"] = layoutArray; if (!volumesArray.isEmpty()) root["volumes"] = volumesArray; QFileInfo fi(path); QDir dir = fi.absoluteDir(); if (!dir.exists()) dir.mkpath("."); QFile file(path); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; file.write(QJsonDocument(root).toJson(QJsonDocument::Indented)); return true; } bool PresetManager::loadPreset(const QString &path, warppipe::Client *client, WarpGraphModel *model) { if (!client || !model) return false; QFile file(path); if (!file.open(QIODevice::ReadOnly)) return false; QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); if (!doc.isObject()) return false; QJsonObject root = doc.object(); int version = root["version"].toInt(); if (version < 1 || version > 1) return false; auto existingNodes = client->ListNodes(); std::unordered_set existingNames; if (existingNodes.ok()) { for (const auto &node : existingNodes.value) { existingNames.insert(node.name); } } QJsonArray devicesArray = root["virtual_devices"].toArray(); for (const auto &val : devicesArray) { QJsonObject dev = val.toObject(); std::string name = dev["name"].toString().toStdString(); std::string mediaClass = dev["media_class"].toString().toStdString(); int channels = dev["channels"].toInt(2); if (existingNames.count(name)) continue; warppipe::VirtualNodeOptions opts; opts.format.channels = static_cast(channels); bool isSink = mediaClass == "Audio/Sink" || mediaClass == "Audio/Duplex"; if (isSink) { client->CreateVirtualSink(name, opts); } else { client->CreateVirtualSource(name, opts); } } QJsonArray layoutArray = root["layout"].toArray(); for (const auto &val : layoutArray) { QJsonObject obj = val.toObject(); std::string name = obj["name"].toString().toStdString(); double x = obj["x"].toDouble(); double y = obj["y"].toDouble(); model->setPendingPosition(name, QPointF(x, y)); } model->refreshFromClient(); QJsonArray routingArray = root["routing"].toArray(); for (const auto &val : routingArray) { QJsonObject route = val.toObject(); std::string outNode = route["out_node"].toString().toStdString(); std::string outPort = route["out_port"].toString().toStdString(); std::string inNode = route["in_node"].toString().toStdString(); std::string inPort = route["in_port"].toString().toStdString(); client->CreateLinkByName(outNode, outPort, inNode, inPort, warppipe::LinkOptions{.linger = true}); } model->refreshFromClient(); if (root.contains("volumes")) { QJsonArray volumesArray = root["volumes"].toArray(); for (const auto &val : volumesArray) { QJsonObject obj = val.toObject(); std::string name = obj["name"].toString().toStdString(); float volume = static_cast(obj["volume"].toDouble(1.0)); bool mute = obj["mute"].toBool(false); for (auto qtId : model->allNodeIds()) { const WarpNodeData *data = model->warpNodeData(qtId); if (data && data->info.name == name) { WarpGraphModel::NodeVolumeState vs; vs.volume = volume; vs.mute = mute; model->setNodeVolumeState(qtId, vs); break; } } } } return true; }