When copy/paste between 2 kdenlive windows, correctly load bin clip effects

This commit is contained in:
Jean-Baptiste Mardelle
2025-12-05 12:36:07 +01:00
parent d3f024c256
commit ddd31bbef7
5 changed files with 78 additions and 45 deletions

View File

@@ -386,6 +386,28 @@ QDomElement EffectStackModel::rowToXml(int row, QDomDocument &document)
return container; return container;
} }
bool EffectStackModel::fromMltXml(const QDomElement &effectsXml)
{
QDomNodeList nodeList = effectsXml.elementsByTagName(QStringLiteral("filter"));
for (int i = 0; i < nodeList.count(); ++i) {
QDomElement node = nodeList.item(i).toElement();
if (!Xml::hasXmlProperty(node, QStringLiteral("kdenlive_id")) || Xml::hasXmlProperty(node, QStringLiteral("internal_added"))) {
// Internal effect, ignore
continue;
}
if (Xml::hasXmlProperty(node, QStringLiteral("kdenlive:builtin")) && Xml::getXmlProperty(node, QStringLiteral("disable")) == QLatin1String("1")) {
// Disabled built-in effect, ignore
continue;
}
const QString effectId = Xml::getXmlProperty(node, QStringLiteral("kdenlive_id"));
Fun undo = []() { return true; };
Fun redo = []() { return true; };
stringMap params = Xml::getXmlPropertyByWildcard(node, QString());
doAppendEffect(effectId, false, params, undo, redo);
}
return true;
}
bool EffectStackModel::fromXml(const QDomElement &effectsXml, Fun &undo, Fun &redo) bool EffectStackModel::fromXml(const QDomElement &effectsXml, Fun &undo, Fun &redo)
{ {
QDomNodeList nodeList = effectsXml.elementsByTagName(QStringLiteral("effect")); QDomNodeList nodeList = effectsXml.elementsByTagName(QStringLiteral("effect"));

View File

@@ -137,6 +137,8 @@ public:
QDomElement rowToXml(int row, QDomDocument &document); QDomElement rowToXml(int row, QDomDocument &document);
/** @brief Load an effect stack from an XML representation */ /** @brief Load an effect stack from an XML representation */
bool fromXml(const QDomElement &effectsXml, Fun &undo, Fun &redo); bool fromXml(const QDomElement &effectsXml, Fun &undo, Fun &redo);
/** @brief Load an effect stack from an MLT XML representation */
bool fromMltXml(const QDomElement &effectsXml);
/** @brief Delete active effect from stack */ /** @brief Delete active effect from stack */
void removeCurrentEffect(); void removeCurrentEffect();

View File

@@ -69,6 +69,9 @@ ClipController::ClipController(const QString &clipId, const std::shared_ptr<Mlt:
} else { } else {
m_controlUuid = QUuid::createUuid(); m_controlUuid = QUuid::createUuid();
} }
if (description.elementsByTagName(QStringLiteral("filter")).count() > 0) {
m_effectsToLoad = description;
}
} }
} }
@@ -123,56 +126,61 @@ void ClipController::addMasterProducer(const std::shared_ptr<Mlt::Producer> &pro
if (!m_masterProducer->is_valid()) { if (!m_masterProducer->is_valid()) {
m_masterProducer = std::shared_ptr<Mlt::Producer>(pCore->mediaUnavailable->cut()); m_masterProducer = std::shared_ptr<Mlt::Producer>(pCore->mediaUnavailable->cut());
qCDebug(KDENLIVE_LOG) << "// WARNING, USING INVALID PRODUCER"; qCDebug(KDENLIVE_LOG) << "// WARNING, USING INVALID PRODUCER";
} else { connectEffectStack();
setProducerProperty(QStringLiteral("kdenlive:id"), m_controllerBinId); return;
if (!m_properties->property_exists("kdenlive:control_uuid")) { }
m_properties->set("kdenlive:control_uuid", m_controlUuid.toString().toUtf8().constData()); setProducerProperty(QStringLiteral("kdenlive:id"), m_controllerBinId);
} if (!m_properties->property_exists("kdenlive:control_uuid")) {
getInfoForProducer(); m_properties->set("kdenlive:control_uuid", m_controlUuid.toString().toUtf8().constData());
checkAudioVideo(); }
if (!m_hasMultipleVideoStreams && m_service.startsWith(QLatin1String("avformat")) && (m_clipType == ClipType::AV || m_clipType == ClipType::Video)) { getInfoForProducer();
// Check if clip has multiple video streams checkAudioVideo();
QList<int> videoStreams; if (!m_effectsToLoad.isNull()) {
QList<int> audioStreams; m_effectStack->fromMltXml(m_effectsToLoad);
int aStreams = m_properties->get_int("meta.media.nb_streams"); m_effectsToLoad.clear();
for (int ix = 0; ix < aStreams; ++ix) { }
char property[200]; if (!m_hasMultipleVideoStreams && m_service.startsWith(QLatin1String("avformat")) && (m_clipType == ClipType::AV || m_clipType == ClipType::Video)) {
snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix); // Check if clip has multiple video streams
QString type = m_properties->get(property); QList<int> videoStreams;
if (type == QLatin1String("video")) { QList<int> audioStreams;
QString key = QStringLiteral("meta.media.%1.codec.name").arg(ix); int aStreams = m_properties->get_int("meta.media.nb_streams");
QString codec_name = m_properties->get(key.toLatin1().constData()); for (int ix = 0; ix < aStreams; ++ix) {
if (codec_name == QLatin1String("png")) { char property[200];
snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix);
QString type = m_properties->get(property);
if (type == QLatin1String("video")) {
QString key = QStringLiteral("meta.media.%1.codec.name").arg(ix);
QString codec_name = m_properties->get(key.toLatin1().constData());
if (codec_name == QLatin1String("png")) {
// This is a cover image, skip
qDebug() << "=== FOUND PNG COVER ART STREAM: " << ix;
setProducerProperty(QStringLiteral("kdenlive:coverartstream"), ix);
continue;
}
if (codec_name == QLatin1String("mjpeg")) {
key = QStringLiteral("meta.media.%1.stream.frame_rate").arg(ix);
QString fps = m_properties->get(key.toLatin1().constData());
if (fps.isEmpty()) {
key = QStringLiteral("meta.media.%1.codec.frame_rate").arg(ix);
fps = m_properties->get(key.toLatin1().constData());
}
if (fps == QLatin1String("90000")) {
// This is a cover image, skip // This is a cover image, skip
qDebug() << "=== FOUND PNG COVER ART STREAM: " << ix; qDebug() << "=== FOUND MJPEG COVER ART STREAM: " << ix;
setProducerProperty(QStringLiteral("kdenlive:coverartstream"), ix); setProducerProperty(QStringLiteral("kdenlive:coverartstream"), ix);
continue; continue;
} }
if (codec_name == QLatin1String("mjpeg")) {
key = QStringLiteral("meta.media.%1.stream.frame_rate").arg(ix);
QString fps = m_properties->get(key.toLatin1().constData());
if (fps.isEmpty()) {
key = QStringLiteral("meta.media.%1.codec.frame_rate").arg(ix);
fps = m_properties->get(key.toLatin1().constData());
}
if (fps == QLatin1String("90000")) {
// This is a cover image, skip
qDebug() << "=== FOUND MJPEG COVER ART STREAM: " << ix;
setProducerProperty(QStringLiteral("kdenlive:coverartstream"), ix);
continue;
}
}
videoStreams << ix;
} else if (type == QLatin1String("audio")) {
audioStreams << ix;
} }
videoStreams << ix;
} else if (type == QLatin1String("audio")) {
audioStreams << ix;
} }
if (videoStreams.count() > 1) { }
setProducerProperty(QStringLiteral("kdenlive:multistreams"), 1); if (videoStreams.count() > 1) {
m_hasMultipleVideoStreams = true; setProducerProperty(QStringLiteral("kdenlive:multistreams"), 1);
QMetaObject::invokeMethod(pCore->bin(), "processMultiStream", Qt::QueuedConnection, Q_ARG(QString, m_controllerBinId), m_hasMultipleVideoStreams = true;
Q_ARG(QList<int>, videoStreams), Q_ARG(QList<int>, audioStreams)); QMetaObject::invokeMethod(pCore->bin(), "processMultiStream", Qt::QueuedConnection, Q_ARG(QString, m_controllerBinId),
} Q_ARG(QList<int>, videoStreams), Q_ARG(QList<int>, audioStreams));
} }
} }
connectEffectStack(); connectEffectStack();

View File

@@ -251,6 +251,7 @@ private:
/** @brief Temporarily store clip properties until producer is available */ /** @brief Temporarily store clip properties until producer is available */
QMap <QString, QVariant> m_tempProps; QMap <QString, QVariant> m_tempProps;
QString m_controllerBinId; QString m_controllerBinId;
QDomElement m_effectsToLoad;
/** @brief Build the audio info object */ /** @brief Build the audio info object */
void buildAudioInfo(int audioIndex); void buildAudioInfo(int audioIndex);
}; };

View File

@@ -218,7 +218,7 @@ QMap<QString, QString> Xml::getXmlPropertyByWildcard(const QDomElement &element,
QDomNodeList params = element.elementsByTagName(QStringLiteral("property")); QDomNodeList params = element.elementsByTagName(QStringLiteral("property"));
for (int i = 0; i < params.count(); ++i) { for (int i = 0; i < params.count(); ++i) {
QDomElement e = params.item(i).toElement(); QDomElement e = params.item(i).toElement();
if (e.attribute(QStringLiteral("name")).startsWith(propertyName)) { if (propertyName.isEmpty() || e.attribute(QStringLiteral("name")).startsWith(propertyName)) {
props.insert(e.attribute(QStringLiteral("name")), e.text()); props.insert(e.attribute(QStringLiteral("name")), e.text());
} }
} }