Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Fix clipcontroller created twice on document opening, load & save clip markers...

Fix clipcontroller created twice on document opening, load & save clip markers (switched to frame based saving)
parent f2032c17
......@@ -1948,7 +1948,7 @@ QStringList Bin::getBinFolderClipIds(const QString &id) const
std::shared_ptr<ProjectClip> Bin::getBinClip(const QString &id)
{
std::shared_ptr<ProjectClip> clip;
std::shared_ptr<ProjectClip> clip = nullptr;
if (id.contains(QLatin1Char('_'))) {
clip = m_itemModel->getClipByBinID(id.section(QLatin1Char('_'), 0, 0));
} else if (!id.isEmpty()) {
......@@ -2040,10 +2040,10 @@ void Bin::slotProducerReady(const requestClipInfo &info, std::shared_ptr<Mlt::Pr
} else {
parentFolder = m_itemModel->getRootFolder();
}
// TODO at this point, we shouldn't have a controller, but rather a bare producer
std::shared_ptr<ProjectClip> newClip = ProjectClip::construct(info.clipId, m_blankThumb, m_itemModel, producer);
parentFolder->appendChild(newClip);
emit producerReady(info.clipId);
ClipType t = newClip->clipType();
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist) {
m_doc->watchFile(newClip->url());
......
......@@ -350,6 +350,41 @@ void MarkerListModel::registerSnapModel(std::weak_ptr<SnapModel> snapModel)
}
}
bool MarkerListModel::loadFromJson(const QString &data)
{
QWriteLocker locker(&m_lock);
auto json = QJsonDocument::fromJson(data.toUtf8());
if (!json.isArray()) {
qDebug() << "Error : Json file should be an array";
return false;
}
auto list = json.array();
beginInsertRows(QModelIndex(), 0, list.size() - 1);
for (const auto &entry : list) {
if (!entry.isObject()) {
qDebug() << "Warning : Skipping invalid marker data";
continue;
}
auto entryObj = entry.toObject();
if (!entryObj.contains(QLatin1String("pos"))) {
qDebug() << "Warning : Skipping invalid marker data (does not contain position)";
continue;
}
GenTime pos(entryObj[QLatin1String("pos")].toInt(), pCore->getCurrentFps());
QString comment = entryObj[QLatin1String("comment")].toString(i18n("Marker"));
int type = entryObj[QLatin1String("type")].toInt(0);
if (type < 0 || type >= (int)markerTypes.size()) {
qDebug() << "Warning : invalid type found:" << type << " Defaulting to 0";
type = 0;
}
m_markerList[pos] = {comment, type};
addSnapPoint(pos);
}
endInsertRows();
return true;
}
bool MarkerListModel::importFromJson(const QString &data, bool ignoreConflicts)
{
QWriteLocker locker(&m_lock);
......@@ -372,7 +407,7 @@ bool MarkerListModel::importFromJson(const QString &data, bool ignoreConflicts)
qDebug() << "Warning : Skipping invalid marker data (does not contain position)";
continue;
}
double pos = entryObj[QLatin1String("pos")].toDouble();
int pos = entryObj[QLatin1String("pos")].toInt();
QString comment = entryObj[QLatin1String("comment")].toString(i18n("Marker"));
int type = entryObj[QLatin1String("type")].toInt(0);
if (type < 0 || type >= (int)markerTypes.size()) {
......@@ -380,13 +415,13 @@ bool MarkerListModel::importFromJson(const QString &data, bool ignoreConflicts)
type = 0;
}
bool res = true;
if (!ignoreConflicts && m_markerList.count(GenTime(pos)) > 0) {
if (!ignoreConflicts && m_markerList.count(GenTime(pos, pCore->getCurrentFps())) > 0) {
// potential conflict found, checking
QString oldComment = m_markerList[GenTime(pos)].first;
int oldType = m_markerList[GenTime(pos)].second;
QString oldComment = m_markerList[GenTime(pos, pCore->getCurrentFps())].first;
int oldType = m_markerList[GenTime(pos, pCore->getCurrentFps())].second;
res = (oldComment == comment) && (type == oldType);
}
res = res && addMarker(GenTime(pos), comment, type, undo, redo);
res = res && addMarker(GenTime(pos, pCore->getCurrentFps()), comment, type, undo, redo);
if (!res) {
bool undone = undo();
Q_ASSERT(undone);
......@@ -404,7 +439,7 @@ QString MarkerListModel::toJson() const
QJsonArray list;
for (const auto &marker : m_markerList) {
QJsonObject currentMarker;
currentMarker.insert(QLatin1String("pos"), QJsonValue(marker.first.seconds()));
currentMarker.insert(QLatin1String("pos"), QJsonValue(marker.first.frames(pCore->getCurrentFps())));
currentMarker.insert(QLatin1String("comment"), QJsonValue(marker.second.first));
currentMarker.insert(QLatin1String("type"), QJsonValue(marker.second.second));
list.push_back(currentMarker);
......
......@@ -115,6 +115,7 @@ public:
such markers are overridden silently
*/
bool importFromJson(const QString &data, bool ignoreConflicts);
bool loadFromJson(const QString &data);
/* @brief Exports the model to json using format above */
QString toJson() const;
......
......@@ -75,6 +75,10 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, std::shared_ptr<
hash();
connect(this, &ProjectClip::updateJobStatus, this, &ProjectClip::setJobStatus);
connect(this, &ProjectClip::updateThumbProgress, model.get(), &ProjectItemModel::updateThumbProgress);
QString markers = getProducerProperty(QStringLiteral("kdenlive:markers"));
if (!markers.isEmpty()) {
m_markerModel->loadFromJson(markers);
}
}
// static
......@@ -85,6 +89,7 @@ std::shared_ptr<ProjectClip> ProjectClip::construct(const QString &id, const QIc
baseFinishConstruct(self);
model->loadSubClips(id, self->getPropertiesFromPrefix(QStringLiteral("kdenlive:clipzone.")));
self->createAudioThumbs();
pCore->binController()->addClipToBin(id, self);
return self;
}
......@@ -95,9 +100,9 @@ ProjectClip::ProjectClip(const QDomElement &description, const QIcon &thumb, std
, m_thumbsProducer(nullptr)
{
Q_ASSERT(description.hasAttribute(QStringLiteral("id")));
m_markerModel = std::make_shared<MarkerListModel>(description.attribute(QStringLiteral("id")), pCore->projectManager()->current()->commandStack());
m_clipStatus = StatusWaiting;
m_thumbnail = thumb;
m_markerModel = std::make_shared<MarkerListModel>(description.attribute(QStringLiteral("id")), pCore->projectManager()->current()->commandStack());
if (description.hasAttribute(QStringLiteral("type"))) {
m_clipType = (ClipType)description.attribute(QStringLiteral("type")).toInt();
if (m_clipType == Audio) {
......@@ -115,7 +120,6 @@ ProjectClip::ProjectClip(const QDomElement &description, const QIcon &thumb, std
}
connect(this, &ProjectClip::updateJobStatus, this, &ProjectClip::setJobStatus);
connect(this, &ProjectClip::updateThumbProgress, model.get(), &ProjectItemModel::updateThumbProgress);
m_markerModel = std::make_shared<MarkerListModel>(m_binId, pCore->projectManager()->current()->commandStack());
}
std::shared_ptr<ProjectClip> ProjectClip::construct(const QDomElement &description, const QIcon &thumb, std::shared_ptr<ProjectItemModel> model)
......@@ -326,6 +330,7 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool repl
Q_UNUSED(replaceProducer);
updateProducer(std::move(producer));
// Update info
if (m_name.isEmpty()) {
m_name = clipName();
......@@ -692,6 +697,7 @@ void ProjectClip::addMarkers(QList<CommentedTime> &markers)
// refresh markers in clip monitor
//if (auto ptr = m_model.lock()) std::static_pointer_cast<ProjectItemModel>(ptr)->bin()->refreshClipMarkers(m_binId);
// refresh markers in timeline clips
setProducerProperty(QStringLiteral("kdenlive:markers"), m_markerModel->toJson());
emit refreshClipDisplay();
}
......
......@@ -160,6 +160,7 @@ void Core::initGUI(const QUrl &Url)
connect(m_binWidget, SIGNAL(storeFolder(QString, QString, QString, QString)), m_binController.get(),
SLOT(slotStoreFolder(QString, QString, QString, QString)));
connect(m_binController.get(), SIGNAL(loadFolders(QMap<QString, QString>)), m_binWidget, SLOT(slotLoadFolders(QMap<QString, QString>)));
connect(m_binController.get(), &BinController::slotProducerReady, m_binWidget, &Bin::slotProducerReady, Qt::DirectConnection);
connect(m_binController.get(), &BinController::prepareTimelineReplacement, m_binWidget, &Bin::prepareTimelineReplacement, Qt::DirectConnection);
connect(m_binController.get(), &BinController::requestAudioThumb, m_binWidget, &Bin::slotCreateAudioThumb);
......
......@@ -131,9 +131,12 @@ void BinController::initializeBin(Mlt::Playlist playlist)
if (m_clipList.contains(mainId)) {
// The controller for this track producer already exists
} else {
// Create empty controller for this track
qDebug() << "creating empty clipcontroller";
ClipController::construct(shared_from_this(), ClipController::mediaUnavailable);
// Create empty controller for this clip
requestClipInfo info;
info.imageHeight = 0;
info.clipId = id;
info.replaceProducer = true;
emit slotProducerReady(info, ClipController::mediaUnavailable);
}
} else {
// Controller was already added by a track producer, add master now
......@@ -149,25 +152,15 @@ void BinController::initializeBin(Mlt::Playlist playlist)
producer->set("resource", color.toUtf8().constData());
}
}
qDebug() << "creating clipcontroller";
std::shared_ptr<ClipController> controller = ClipController::construct(shared_from_this(), producer, true);
m_clipList.insert(id, controller);
requestClipInfo info;
info.imageHeight = 0;
info.clipId = id;
info.replaceProducer = true;
emit slotProducerReady(info, producer);
}
}
emit loadingBin(i + 1);
}
// Load markers
Mlt::Properties markerProperties;
markerProperties.pass_values(playlistProps, "kdenlive:marker.");
for (int i = 0; i < markerProperties.count(); i++) {
QString markerId = markerProperties.get_name(i);
QString controllerId = markerId.section(QLatin1Char(':'), 0, 0);
if (!m_clipList.contains(controllerId)) {
qDebug() << "Warning: receiving marker info for unavailable clip";
continue;
}
m_clipList[controllerId]->loadSnapMarker(markerId.section(QLatin1Char(':'), 1), markerProperties.get(i));
}
}
QMap<double, QString> BinController::takeGuidesData()
......@@ -284,7 +277,7 @@ void BinController::replaceProducer(const requestClipInfo &info, const std::shar
emit prepareTimelineReplacement(info);
}
void BinController::addClipToBin(const QString &id, const std::shared_ptr<ClipController> &controller) // Mlt::Producer &producer)
void BinController::addClipToBin(const QString &id, const std::shared_ptr<ClipController> &controller)
{
/** Test: we can use filters on clips in the bin this way
Mlt::Filter f(*m_mltProfile, "sepia");
......
......@@ -58,6 +58,7 @@ public:
mlt_service service();
friend class ClipController;
friend class ProjectClip;
protected:
/** @brief Add a new clip producer to the project.
......@@ -210,6 +211,7 @@ signals:
void prepareTimelineReplacement(const requestClipInfo &);
/** @brief Indicate which clip we are loading */
void loadingBin(int);
void slotProducerReady(const requestClipInfo &info, std::shared_ptr<Mlt::Producer> producer);
};
#endif
......@@ -95,13 +95,9 @@ ClipController::ClipController(std::shared_ptr<BinController> bincontroller)
}
// static
std::shared_ptr<ClipController> ClipController::construct(const std::shared_ptr<BinController> &binController, std::shared_ptr<Mlt::Producer> producer,
bool loadingFromBinPlaylist)
std::shared_ptr<ClipController> ClipController::construct(const std::shared_ptr<BinController> &binController, std::shared_ptr<Mlt::Producer> producer)
{
std::shared_ptr<ClipController> ptr(new ClipController(binController, producer));
if (!loadingFromBinPlaylist) {
binController->addClipToBin(ptr->clipId(), ptr);
}
return ptr;
}
......
......@@ -58,8 +58,7 @@ public:
* @param producer producer to create reference to
* @param loadingFromBinPlaylist if true, we are loading the clip from bin playlist, so no need to insert it here
*/
static std::shared_ptr<ClipController> construct(const std::shared_ptr<BinController> &bincontroller, std::shared_ptr<Mlt::Producer> producer,
bool loadingFromBinPlaylist = false);
static std::shared_ptr<ClipController> construct(const std::shared_ptr<BinController> &bincontroller, std::shared_ptr<Mlt::Producer> producer);
protected:
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment