Commit 57152173 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Fix thumbnail cache bug causing incorrect thumbs to sometimes display after saving project

parent 2933fe30
Pipeline #154822 passed with stage
in 11 minutes and 51 seconds
......@@ -426,7 +426,6 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudio
m_audioThumbCreated = false;
// Reset uuid to enforce reloading thumbnails from qml cache
m_uuid = QUuid::createUuid();
updateTimelineClips({TimelineModel::ClipThumbRole});
if (forceAudioReload || (!isProxy && hashChanged)) {
discardAudioThumb();
}
......@@ -516,6 +515,8 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool gene
qDebug() << "################### ProjectClip::setproducer #################";
QMutexLocker locker(&m_producerMutex);
FileStatus::ClipStatus currentStatus = m_clipStatus;
// Make sure we have a hash for this clip
getFileHash();
updateProducer(producer);
emit producerChanged(m_binId, producer);
m_thumbsProducer.reset();
......@@ -541,7 +542,7 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool gene
QVector<int>updateRoles;
if (m_clipStatus != currentStatus) {
updateRoles = {AbstractProjectItem::ClipStatus, AbstractProjectItem::IconOverlay};
updateTimelineClips({TimelineModel::StatusRole});
updateTimelineClips({TimelineModel::StatusRole,TimelineModel::ClipThumbRole});
}
setTags(getProducerProperty(QStringLiteral("kdenlive:tags")));
AbstractProjectItem::setRating(uint(getProducerIntProperty(QStringLiteral("kdenlive:rating"))));
......@@ -551,8 +552,6 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool gene
updateRoles);
std::static_pointer_cast<ProjectItemModel>(ptr)->updateWatcher(std::static_pointer_cast<ProjectClip>(shared_from_this()));
}
// Make sure we have a hash for this clip
getFileHash();
// set parent again (some info need to be stored in producer)
updateParent(parentItem().lock());
if (generateThumb && m_clipType != ClipType::Audio) {
......
......@@ -360,8 +360,7 @@ bool ProjectManager::saveFileAs(const QString &outputFileName, bool saveACopy)
}
QUrl url = QUrl::fromLocalFile(outputFileName);
// Save timeline thumbnails
QStringList thumbKeys = pCore->window()->getMainTimeline()->controller()->getThumbKeys();
ThumbnailCache::get()->saveCachedThumbs(thumbKeys);
ThumbnailCache::get()->saveCachedThumbs(pCore->window()->getMainTimeline()->controller()->getThumbKeys());
if (!saveACopy) {
m_project->setUrl(url);
// setting up autosave file in ~/.kde/data/stalefiles/kdenlive/
......
......@@ -4184,17 +4184,24 @@ bool TimelineController::endFakeGroupMove(int clipId, int groupId, int delta_tra
return true;
}
QStringList TimelineController::getThumbKeys()
const std::unordered_map<QString, std::vector<int>> TimelineController::getThumbKeys()
{
QStringList result;
std::unordered_map<QString, std::vector<int>> framesToStore;
for (const auto &clp : m_model->m_allClips) {
if (clp.second->isAudioOnly()) {
//Don't process audio clips
continue;
}
const QString binId = getClipBinId(clp.first);
std::shared_ptr<ProjectClip> binClip = pCore->bin()->getBinClip(binId);
result << binClip->hash() + QLatin1Char('#') + QString::number(clp.second->getIn()) + QStringLiteral(".jpg");
result << binClip->hash() + QLatin1Char('#') + QString::number(clp.second->getOut()) + QStringLiteral(".jpg");
framesToStore[binId].push_back(clp.second->getIn());
framesToStore[binId].push_back(clp.second->getOut());
}
result.removeDuplicates();
return result;
for (auto p : framesToStore) {
std::sort(p.second.begin(), p.second.end());
auto last = std::unique(p.second.begin(), p.second.end());
p.second.erase(last, p.second.end());
}
return framesToStore;
}
bool TimelineController::isInSelection(int itemId)
......
......@@ -601,7 +601,7 @@ public:
/** @brief Set keyboard grabbing on current selection */
Q_INVOKABLE void grabCurrent();
/** @brief Returns keys for all used thumbnails */
QStringList getThumbKeys();
const std::unordered_map<QString, std::vector<int>> getThumbKeys();
/** @brief Returns true if a drag operation is currently running in timeline */
bool dragOperationRunning();
/** @brief Returns true if the timeline is in trimming mode (slip, slide, ripple, rolle) */
......
......@@ -155,7 +155,9 @@ QImage ThumbnailCache::getThumbnail(const QString &binId, int pos, bool volatile
}
QDir thumbFolder = getDir(false, &ok);
if (ok && thumbFolder.exists(key)) {
m_storedOnDisk[binId].push_back(pos);
if(m_storedOnDisk.find(binId) == m_storedOnDisk.end() || std::find(m_storedOnDisk[binId].begin(), m_storedOnDisk[binId].end(), pos) == m_storedOnDisk[binId].end()) {
m_storedOnDisk[binId].push_back(pos);
}
return QImage(thumbFolder.absoluteFilePath(key));
}
return QImage();
......@@ -175,7 +177,9 @@ void ThumbnailCache::storeThumbnail(const QString &binId, int pos, const QImage
if (!img.save(thumbFolder.absoluteFilePath(key))) {
qDebug() << ".............\n!!!!!!!! ERROR SAVING THUMB in: "<<thumbFolder.absoluteFilePath(key);
}
m_storedOnDisk[binId].push_back(pos);
if (m_storedOnDisk.find(binId) == m_storedOnDisk.end() || std::find(m_storedOnDisk[binId].begin(), m_storedOnDisk[binId].end(), pos) == m_storedOnDisk[binId].end()) {
m_storedOnDisk[binId].push_back(pos);
}
// if volatile cache also contains this entry, update it
if (m_volatileCache->contains(key)) {
m_volatileCache->remove(key);
......@@ -190,19 +194,30 @@ void ThumbnailCache::storeThumbnail(const QString &binId, int pos, const QImage
}
}
void ThumbnailCache::saveCachedThumbs(const QStringList &keys)
void ThumbnailCache::saveCachedThumbs(const std::unordered_map<QString, std::vector<int>> &keys)
{
bool ok;
QDir thumbFolder = getDir(false, &ok);
if (!ok) {
return;
}
for (const QString &key : keys) {
if (!thumbFolder.exists(key) && m_volatileCache->contains(key)) {
QImage img = m_volatileCache->get(key);
if (!img.save(thumbFolder.absoluteFilePath(key))) {
qDebug() << "// Error writing thumbnails to " << thumbFolder.absolutePath();
break;
for (auto &key : keys) {
bool ok;
for(const auto& pos: key.second) {
if (m_storedOnDisk.find(key.first) == m_storedOnDisk.end() || std::find(m_storedOnDisk[key.first].begin(), m_storedOnDisk[key.first].end(), pos) == m_storedOnDisk[key.first].end()) {
const QString thumbKey = getKey(key.first, pos, &ok);
if (!ok) {
continue;
}
if (!thumbFolder.exists(thumbKey) && m_volatileCache->contains(thumbKey)) {
QImage img = m_volatileCache->get(thumbKey);
if (!img.save(thumbFolder.absoluteFilePath(thumbKey))) {
qDebug() << "// Error writing thumbnails to " << thumbFolder.absolutePath();
break;
} else {
m_storedOnDisk[key.first].push_back(pos);
}
}
}
}
}
......@@ -226,7 +241,7 @@ void ThumbnailCache::invalidateThumbsForClip(const QString &binId)
QDir thumbFolder = getDir(false, &ok);
if (ok && m_storedOnDisk.find(binId) != m_storedOnDisk.end()) {
// Remove persistent cache
for (int pos : m_storedOnDisk.at(binId)) {
for (const auto &pos : m_storedOnDisk.at(binId)) {
if (pos >= 0) {
auto key = getKey(binId, pos, &ok);
if (ok) {
......
......@@ -58,7 +58,7 @@ public:
void invalidateThumbsForClip(const QString &binId);
/** @brief Save all cached thumbs to disk */
void saveCachedThumbs(const QStringList &keys);
void saveCachedThumbs(const std::unordered_map<QString, std::vector<int>> &keys);
/** @brief Reset cache (discarding all thumbs stored in memory) */
void clearCache();
......
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