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

Ensure timeline clips are reloaded on change.

Should fix #814
parent 1683e9ad
Pipeline #145734 passed with stage
in 6 minutes and 42 seconds
......@@ -72,6 +72,7 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, const std::share
, ClipController(id, producer)
, m_resetTimelineOccurences(false)
, m_audioCount(0)
, m_uuid(QUuid::createUuid())
{
m_markerModel = std::make_shared<MarkerListModel>(id, pCore->projectManager()->undoStack());
if (producer->get_int("_placeholder") == 1) {
......@@ -142,6 +143,7 @@ ProjectClip::ProjectClip(const QString &id, const QDomElement &description, cons
, ClipController(id)
, m_resetTimelineOccurences(false)
, m_audioCount(0)
, m_uuid(QUuid::createUuid())
{
m_clipStatus = FileStatus::StatusWaiting;
m_thumbnail = thumb;
......@@ -377,10 +379,13 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudio
// In that case, we only want a new thumbnail.
// We thus set up a thumb job. We must make sure that there is no pending LOADJOB
// Clear cache first
ThumbnailCache::get()->invalidateThumbsForClip(clipId());
ThumbnailCache::get()->invalidateThumbsForClip(m_binId);
pCore->taskManager.discardJobs({ObjectType::BinClip, m_binId.toInt()}, AbstractTask::LOADJOB, true);
pCore->taskManager.discardJobs({ObjectType::BinClip, m_binId.toInt()}, AbstractTask::CACHEJOB);
m_thumbsProducer.reset();
// Reset uuid to enforce reloading thumbnails from qml cache
m_uuid = QUuid::createUuid();
updateTimelineClips({TimelineModel::ClipThumbRole});
ClipLoadTask::start({ObjectType::BinClip,m_binId.toInt()}, QDomElement(), true, -1, -1, this);
} else {
// If another load job is running?
......@@ -419,6 +424,9 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudio
}
m_audioThumbCreated = false;
ThumbnailCache::get()->invalidateThumbsForClip(clipId());
// Reset uuid to enforce reloading thumbnails from qml cache
m_uuid = QUuid::createUuid();
updateTimelineClips({TimelineModel::ClipThumbRole});
if (forceAudioReload || (!isProxy && hashChanged)) {
discardAudioThumb();
}
......@@ -475,7 +483,7 @@ void ProjectClip::setThumbnail(const QImage &img, int in, int out, bool inCache)
}
if (!inCache && (m_clipType == ClipType::Text || m_clipType == ClipType::TextTemplate)) {
// Title clips always use the same thumb as bin, refresh
updateTimelineClips({TimelineModel::ReloadThumbRole});
updateTimelineClips({TimelineModel::ClipThumbRole});
}
}
......@@ -2234,3 +2242,8 @@ const QStringList ProjectClip::enforcedParams() const
}
return params;
}
const QString ProjectClip::baseThumbPath()
{
return QString("%1/%2/#").arg(m_binId).arg(m_uuid.toString());
}
......@@ -17,6 +17,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QFuture>
#include <QMutex>
#include <QTimer>
#include <QUuid>
#include <memory>
class ClipPropertiesController;
......@@ -237,6 +238,8 @@ public:
/** @brief Retuns a list of important enforces parameters in MLT format, for example to disable autorotate. */
const QStringList enforcedParams() const;
const QString baseThumbPath();
protected:
friend class ClipModel;
/** @brief This is a call-back called by a ClipModel when it is created
......@@ -318,6 +321,7 @@ private:
std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_videoProducers;
std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_timewarpProducers;
std::shared_ptr<Mlt::Producer> m_disabledProducer;
QUuid m_uuid;
signals:
void producerChanged(const QString &, const std::shared_ptr<Mlt::Producer> &);
......
......@@ -1474,3 +1474,12 @@ QString ClipModel::clipHash() const
container.appendChild(m_effectStack->toXml(document));
return document.toString();
}
const QString ClipModel::clipThumbPath()
{
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(m_binClipId);
if (binClip) {
return binClip->baseThumbPath();
}
return QString();
}
......@@ -147,6 +147,7 @@ public:
/** @brief Tracks have two sub playlists to enable same track transitions. This returns the index of the sub-playlist containing this clip */
int getSubPlaylistIndex() const;
void setSubPlaylistIndex(int index, int trackId);
const QString clipThumbPath();
friend class TrackModel;
friend class TimelineModel;
......
......@@ -228,7 +228,7 @@ QHash<int, QByteArray> TimelineItemModel::roleNames() const
roles[HasAudio] = "hasAudio";
roles[CanBeAudioRole] = "canBeAudio";
roles[CanBeVideoRole] = "canBeVideo";
roles[ReloadThumbRole] = "reloadThumb";
roles[ClipThumbRole] = "clipThumbId";
roles[ReloadAudioThumbRole] = "reloadAudioThumb";
roles[PositionOffsetRole] = "positionOffset";
roles[ThumbsFormatRole] = "thumbsFormat";
......@@ -340,8 +340,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
return clip->getMixDuration();
case MixCutRole:
return clip->getMixCutPosition();
case ReloadThumbRole:
return clip->forceThumbReload;
case ClipThumbRole:
return clip->clipThumbPath();
case ReloadAudioThumbRole:
return clip->forceThumbReload;
case PositionOffsetRole:
......
......@@ -5499,7 +5499,7 @@ void TimelineModel::replugClip(int clipId)
void TimelineModel::requestClipUpdate(int clipId, const QVector<int> &roles)
{
QModelIndex modelIndex = makeClipIndexFromID(clipId);
if (roles.contains(TimelineModel::ReloadThumbRole) || roles.contains(TimelineModel::ReloadAudioThumbRole)) {
if (roles.contains(TimelineModel::ReloadAudioThumbRole)) {
m_allClips[clipId]->forceThumbReload = !m_allClips[clipId]->forceThumbReload;
}
notifyChange(modelIndex, modelIndex, roles);
......
......@@ -145,7 +145,7 @@ public:
FadeOutRole, /// clip only
FileHashRole, /// clip only
SpeedRole, /// clip only
ReloadThumbRole, /// clip only
ClipThumbRole, /// clip only
ReloadAudioThumbRole, /// clip only
PositionOffsetRole, /// clip only
TimeRemapRole, /// clip only
......
......@@ -66,7 +66,7 @@ Rectangle {
property bool canBeVideo
property double speed: 1.0
property color borderColor: "#000000"
property bool forceReloadThumb
property string clipThumbId
property bool forceReloadAudioThumb
property bool isComposition: false
property bool hideClipViews: scrollStart > (clipDuration * timeline.scaleFactor) || scrollStart + scrollView.width < 0 || clipRoot.width < root.minClipWidthForViews
......@@ -163,16 +163,6 @@ Rectangle {
}
}
onForceReloadThumbChanged: {
// TODO: find a way to force reload of clip thumbs
if (isAudio) {
return;
}
if (thumbsLoader.item) {
thumbsLoader.item.reload(0)
}
}
onForceReloadAudioThumbChanged: {
// TODO: find a way to force reload of clip thumbs
if (!isAudio) {
......@@ -260,7 +250,7 @@ Rectangle {
}
*/
property bool noThumbs: (isAudio || itemType === ProducerType.Color || mltService === '')
property string baseThumbPath: noThumbs ? '' : 'image://thumbnail/' + binId + '/' + documentId + '/#'
property string baseThumbPath: noThumbs ? '' : 'image://thumbnail/' + clipThumbId
DropArea { //Drop area for clips
anchors.fill: clipRoot
......
......@@ -19,10 +19,6 @@ Row {
property bool fixedThumbs: clipRoot.itemType === ProducerType.Image || clipRoot.itemType === ProducerType.Text || clipRoot.itemType === ProducerType.TextTemplate
property int thumbWidth: container.height * root.dar
property bool enableCache: clipRoot.itemType === ProducerType.Video || clipRoot.itemType === ProducerType.AV
function reload(reset) {
//console.log('+++++\n\ntriggered ML thumb reload\n\n++++++++++++++')
clipRoot.baseThumbPath = clipRoot.variableThumbs ? '' : 'image://thumbnail/' + clipRoot.binId + '/' + Math.random() + '/#'
}
Repeater {
id: thumbRepeater
......
......@@ -236,8 +236,8 @@ Item{
}
Binding {
target: loader.item
property: "forceReloadThumb"
value: model.reloadThumb
property: "clipThumbId"
value: model.clipThumbId
when: loader.status == Loader.Ready && clipItem
}
Binding {
......
Supports Markdown
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