File indexing completed on 2024-04-21 04:51:33
0001 /* 0002 SPDX-FileCopyrightText: 2007 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 /** @class KdenliveDoc 0008 * @brief Represents a kdenlive project file 0009 * 0010 * Instances of KdenliveDoc classes are created by void MainWindow::newFile(bool showProjectSettings, bool force) 0011 */ 0012 0013 #pragma once 0014 0015 #include <KJob> 0016 #include <QAction> 0017 #include <QDir> 0018 #include <QList> 0019 #include <QMap> 0020 #include <QObject> 0021 #include <QUuid> 0022 #include <memory> 0023 #include <qdom.h> 0024 0025 #include <kautosavefile.h> 0026 #include "../bin/model/subtitlemodel.hpp" 0027 0028 #include "definitions.h" 0029 #include "utils/gentime.h" 0030 #include "utils/timecode.h" 0031 0032 class MainWindow; 0033 class TrackInfo; 0034 class ProjectClip; 0035 class MarkerListModel; 0036 class Render; 0037 class ProfileParam; 0038 class SubtitleModel; 0039 class MarkerSortModel; 0040 0041 class QUndoGroup; 0042 class QUndoCommand; 0043 class DocUndoStack; 0044 0045 namespace Mlt { 0046 class Profile; 0047 } 0048 0049 /** Object returned by KdenliveDoc::Open(), containing a pointer to a KdenliveDoc 0050 * (if successful) and also additional information about whether the doc was 0051 * modified or upgraded, and any error message. If the doc is nullptr, then 0052 * errorMessage() will return an error string that can be shown to the user. 0053 */ 0054 class DocOpenResult { 0055 public: 0056 bool isSuccessful() const { return m_succeeded; } 0057 bool isAborted() const { return m_aborted; } 0058 /** @returns a unique_ptr to the KdenliveDoc, or an empty unique_ptr */ 0059 std::unique_ptr<KdenliveDoc> getDocument() { return std::move(m_doc); } 0060 /** @returns an error message if the doc could not be opened. */ 0061 QString getError() const { return m_errorMessage; } 0062 /** @return true if the doc was upgraded from an older version */ 0063 bool wasUpgraded() const { return m_upgraded; } 0064 /** @return true if the doc was modified by the validator */ 0065 bool wasModified() const { return m_modified; } 0066 void setDocument(std::unique_ptr<KdenliveDoc> doc) { 0067 m_doc = std::move(doc); 0068 m_succeeded = true; 0069 } 0070 void setError(const QString &error) { m_errorMessage = error; } 0071 void setAborted() { m_aborted = true; } 0072 void setUpgraded(bool upgraded) { m_upgraded = upgraded; } 0073 void setModified(bool modified) { m_modified = modified; } 0074 0075 private: 0076 std::unique_ptr<KdenliveDoc> m_doc; 0077 QString m_errorMessage = QString(); 0078 QString m_notification = QString(); 0079 bool m_upgraded = false; 0080 bool m_modified = false; 0081 bool m_succeeded = false; 0082 bool m_aborted = false; 0083 }; 0084 0085 class KdenliveDoc : public QObject 0086 { 0087 Q_OBJECT 0088 0089 public: 0090 friend class LoadJob; 0091 friend class TimelineModel; 0092 /** @brief Create a new empty Kdenlive project with the specified profile and requested number of tracks. 0093 * 0094 * @param tracks The number of <video, audio> tracks to create in the project. 0095 */ 0096 KdenliveDoc(QString projectFolder, QUndoGroup *undoGroup, const QString &profileName, const QMap<QString, QString> &properties, 0097 const QMap<QString, QString> &metadata, const std::pair<int, int> &tracks, int audioChannels, MainWindow *parent = nullptr); 0098 /** @brief Open an existing Kdenlive project, returning nothing if the project cannot be opened. */ 0099 static DocOpenResult Open(const QUrl &url, const QString &projectFolder, QUndoGroup *undoGroup, 0100 bool recoverCorruption, MainWindow *parent = nullptr); 0101 /** @brief Create a dummy project, used for testing. */ 0102 KdenliveDoc(std::shared_ptr<DocUndoStack> undoStack, std::pair<int, int> tracks = {2, 2}, MainWindow *parent = nullptr); 0103 ~KdenliveDoc() override; 0104 QUuid activeUuid; 0105 /** @brief True until all project timelines are loaded. */ 0106 bool loading{true}; 0107 /** @brief True if we are currently closing the project. */ 0108 bool closing{false}; 0109 /** @brief Get current document's producer. */ 0110 const QByteArray getAndClearProjectXml(); 0111 double fps() const; 0112 int width() const; 0113 int height() const; 0114 QUrl url() const; 0115 KAutoSaveFile *m_autosave; 0116 /** @brief Whether the project folder should be in the same folder as the project file (var is only used for new projects)*/ 0117 bool m_sameProjectFolder; 0118 Timecode timecode() const; 0119 std::shared_ptr<DocUndoStack> commandStack(); 0120 0121 int getFramePos(const QString &duration); 0122 /** @brief Get a list of all clip ids that are inside a folder. */ 0123 QStringList getBinFolderClipIds(const QString &folderId) const; 0124 0125 const QString description(const QString suffix = QString()) const; 0126 void setUrl(const QUrl &url); 0127 /** @brief Update path of subtitle url and timewarp sequence playlists. */ 0128 void updateWorkFilesBeforeSave(const QString &newUrl = QString(), bool onRender = false); 0129 /** @brief Restore tmp work path for subtitle filters after saving. */ 0130 void updateWorkFilesAfterSave(); 0131 0132 void prepareRenderAssets(const QDir &destFolder); 0133 void restoreRenderAssets(); 0134 0135 /** @brief Defines whether the document needs to be saved. */ 0136 bool isModified() const; 0137 /** @brief Adds a "modified" attribute to the document root so that a backup 0138 * will be created the next time the document is saved. 0139 */ 0140 void requestBackup(); 0141 0142 /** @brief Returns the project folder, used to store project temporary files. */ 0143 QString projectTempFolder() const; 0144 /** @brief Returns the folder used to store project data files (titles, etc). */ 0145 QString projectDataFolder(const QString &newPath = QString(), bool folderForAudio = false) const; 0146 void setZoom(const QUuid &uuid, int horizontal, int vertical = -1); 0147 QPoint zoom(const QUuid &uuid) const; 0148 double dar() const; 0149 /** @brief Returns the project file xml. */ 0150 QDomDocument xmlSceneList(const QString &scene); 0151 /** @brief Saves the project file xml to a file. */ 0152 bool saveSceneList(const QString &path, const QString &scene, bool saveOverExistingFile = true); 0153 void cacheImage(const QString &fileId, const QImage &img) const; 0154 void setProjectFolder(const QUrl &url); 0155 void setZone(const QUuid &uuid, int start, int end); 0156 QPoint zone(const QUuid &uuid) const; 0157 /** @brief Returns target tracks (video, audio). */ 0158 QPair<int, int> targetTracks(const QUuid &uuid) const; 0159 /** @brief Load document guides from properties. */ 0160 void loadDocumentGuides(const QUuid &uuid, std::shared_ptr<TimelineItemModel> model); 0161 /** @brief Load sequence properties from the MLT tractor. */ 0162 void loadSequenceProperties(const QUuid &uuid, Mlt::Properties sequenceProps); 0163 /** @brief Set a document property. */ 0164 void setDocumentProperty(const QString &name, const QString &value); 0165 virtual const QString getDocumentProperty(const QString &name, const QString &defaultValue = QString()) const; 0166 bool hasDocumentProperty(const QString &name) const; 0167 /** @brief Set a timeline sequence property. */ 0168 void setSequenceProperty(const QUuid &uuid, const QString &name, const QString &value); 0169 void setSequenceProperty(const QUuid &uuid, const QString &name, int value); 0170 /** @brief Get a timeline sequence property. */ 0171 const QString getSequenceProperty(const QUuid &uuid, const QString &name, const QString defaultValue = QString()) const; 0172 /** @brief Returns true if a sequence property exists. */ 0173 bool hasSequenceProperty(const QUuid &uuid, const QString &name) const; 0174 /** @brief Delete the sequence property after it has been used. */ 0175 void clearSequenceProperty(const QUuid &uuid, const QString &name); 0176 const QMap<QString, QString> getSequenceProperties(const QUuid &uuid) const; 0177 /** @brief Move document properties into sequence properties (mostly useful to convert older KdenliveDoc formats.*/ 0178 void importSequenceProperties(const QUuid uuid, const QStringList properties); 0179 /** @brief Get the list of subtitles in a timeline. */ 0180 QMap<std::pair<int, QString>, QString> multiSubtitlePath(const QUuid &uuid); 0181 0182 /** @brief Gets the list of renderer properties saved into the document. */ 0183 QMap<QString, QString> getRenderProperties() const; 0184 /** @brief Read the display ratio from an xml project file. */ 0185 static double getDisplayRatio(const QString &path); 0186 /** @brief Backup the project file */ 0187 void backupLastSavedVersion(const QString &path); 0188 /** @brief Returns the document metadata (author, copyright, ...) */ 0189 const QMap<QString, QString> metadata() const; 0190 /** @brief Set the document metadata (author, copyright, ...) */ 0191 void setMetadata(const QMap<QString, QString> &meta); 0192 /** @brief Get all document properties that need to be saved */ 0193 QMap<QString, QString> documentProperties(bool saveHash = false); 0194 bool useProxy() const; 0195 bool useExternalProxy() const; 0196 /** @brief Returns true if a proxy clip should be automatically generated for this width. 0197 * if width < 0, returns true if the document's automatic proxy generation is enabled 0198 */ 0199 bool autoGenerateProxy(int width) const; 0200 /** @brief Returns true if a proxy clip should be automatically generated for this width. 0201 * if width < 0, returns true if the document's automatic proxy generation is enabled 0202 */ 0203 bool autoGenerateImageProxy(int width) const; 0204 /** @brief Saves effects embedded in project file. */ 0205 void saveCustomEffects(const QDomNodeList &customeffects); 0206 void resetProfile(bool reloadThumbs); 0207 /** @brief Returns true if the profile file has changed. */ 0208 bool profileChanged(const QString &profile) const; 0209 /** @brief Get an action from main actioncollection. */ 0210 QAction *getAction(const QString &name); 0211 /** @brief Add an action to main actioncollection. */ 0212 void doAddAction(const QString &name, QAction *a, const QKeySequence &shortcut); 0213 void invalidatePreviews(QList<int> chunks); 0214 void previewProgress(int p); 0215 /** @brief Select most appropriate rendering profile for timeline preview based on fps / size. */ 0216 void selectPreviewProfile(); 0217 void displayMessage(const QString &text, MessageType type = DefaultMessage, int timeOut = 0); 0218 /** @brief Get a cache directory for this project. virtual to allow mocking */ 0219 virtual const QDir getCacheDir(CacheType type, bool *ok, const QUuid uuid = QUuid()) const; 0220 /** @brief Create standard cache dirs for the project */ 0221 void initCacheDirs(); 0222 /** @brief Get a list of all proxy hash used in this project */ 0223 QStringList getProxyHashList(); 0224 /** @brief Move project data files to new url */ 0225 const QList<QUrl> getProjectData(bool *ok); 0226 0227 /** @brief Returns a pointer to the guide model of timeline uuid */ 0228 std::shared_ptr<MarkerListModel> getGuideModel(const QUuid uuid) const; 0229 std::shared_ptr<MarkerSortModel> getFilteredGuideModel(const QUuid uuid); 0230 0231 // TODO REFAC: delete */ 0232 Render *renderer(); 0233 /** @brief Returns MLT's root (base path) */ 0234 const QString documentRoot() const; 0235 /** @brief Returns true if timeline preview settings changed*/ 0236 bool updatePreviewSettings(const QString &profile); 0237 /** @brief Returns the recommended proxy profile parameters */ 0238 QString getAutoProxyProfile(); 0239 /** @brief Returns the number of clips in this project (useful to show loading progress) */ 0240 int clipsCount() const; 0241 int updateClipsCount(); 0242 /** @brief Returns a list of project tags (color / description) */ 0243 QMap <int, QStringList> getProjectTags() const; 0244 /** @brief Returns the number of audio channels for this project */ 0245 int audioChannels() const; 0246 /** @brief Ensure we don't have leftover preview chunks (created after last save */ 0247 void cleanupTimelinePreview(const QDateTime &documentDate); 0248 /** @brief Returns the guides categories for the project in format {name:index:#color} */ 0249 const QStringList guidesCategories(); 0250 /** @brief Set the guides categories for the project in format {name:index:#color} */ 0251 void updateGuideCategories(const QStringList &categories, const QMap<int, int> remapCategories = {}); 0252 0253 /** 0254 * If the document used a decimal point different than “.”, it is stored in this property. 0255 * @return Original decimal point, or an empty string if it was “.” already 0256 */ 0257 QString &modifiedDecimalPoint(); 0258 void setModifiedDecimalPoint(const QString &decimalPoint) { m_modifiedDecimalPoint = decimalPoint; } 0259 /** @brief Get the list of secondary timelines uuid */ 0260 const QStringList getSecondaryTimelines() const; 0261 0262 /** @brief Returns a path for current document's subtitle file. 0263 * uuid is appended to the path if this is not the primary timeline 0264 * ix is the index of the subtitle, appended to the path if > 0 0265 * If final is true, this will be the project filename with ".srt" appended. Otherwise a file in /tmp */ 0266 const QString subTitlePath(const QUuid &uuid, int ix, bool final); 0267 /** @brief Returns the list of all used subtitles paths. */ 0268 QStringList getAllSubtitlesPath(bool final); 0269 /** @brief Creates a new project. */ 0270 QDomDocument createEmptyDocument(int videotracks, int audiotracks, bool disableProfile = true); 0271 /** @brief Return the document version. */ 0272 double getDocumentVersion() const; 0273 0274 /** @brief Returns true if this project has subtitles. */ 0275 bool hasSubtitles() const; 0276 /** @brief Generate a temporary subtitle file for a zone. */ 0277 void generateRenderSubtitleFile(const QUuid &uuid, int in, int out, const QString &subtitleFile); 0278 /** @brief Returns the default definition for guide categories.*/ 0279 static const QStringList getDefaultGuideCategories(); 0280 void addTimeline(const QUuid &uuid, std::shared_ptr<TimelineItemModel> model, bool force = false); 0281 /** @brief Load the guides into the model for a sequence.*/ 0282 void loadSequenceGroupsAndGuides(const QUuid &uuid); 0283 /** @brief Get a timeline by its uuid.*/ 0284 std::shared_ptr<TimelineItemModel> getTimeline(const QUuid &uuid, bool allowEmpty = false); 0285 /** @brief Before closing a timeline, store its groups and other properties.*/ 0286 void closeTimeline(const QUuid uuid, bool onDeletion = true); 0287 /** @brief store groups in our properties.*/ 0288 void storeGroups(const QUuid &uuid); 0289 void checkUsage(const QUuid &uuid); 0290 /** @brief Return all timelines UUIDs.*/ 0291 QList<QUuid> getTimelinesUuids() const; 0292 /** @brief Return all timelines MLT ids.*/ 0293 QStringList getTimelinesIds(); 0294 /** @brief Returns the number of timelines in this project.*/ 0295 int openedTimelineCount() const; 0296 /** @brief Get the currently active project name.*/ 0297 const QString projectName() const; 0298 /** @brief Returns the project's main uuid.*/ 0299 const QUuid uuid() const; 0300 /** @brief Returns true if a sequence thumbnail needs an update.*/ 0301 bool sequenceThumbRequiresRefresh(const QUuid &uuid) const; 0302 void setSequenceThumbRequiresUpdate(const QUuid &uuid); 0303 /** @brief Thumbnail for a sequence was updated, remove it from the update list.*/ 0304 void sequenceThumbUpdated(const QUuid &uuid); 0305 0306 /** @brief Replace proxy clips with originals for rendering. */ 0307 static void useOriginals(QDomDocument &doc); 0308 static void processProxyNodes(QDomNodeList producers, const QString &root, const QMap<QString, QString> &proxies); 0309 /** @brief Disable all subtitle filters of @param doc */ 0310 static void disableSubtitles(QDomDocument &doc); 0311 /** @brief Sets the color of the first producer in @param doc with id "black_track" to transparent */ 0312 static void makeBackgroundTrackTransparent(QDomDocument &doc); 0313 /** @brief Set the autoclose attribute to all playlists in @param doc. 0314 * This is eg. needed for rendering, as the process would not stop at the end of the playlist if it was not closed */ 0315 static void setAutoclosePlaylists(QDomDocument &doc, const QString &mainSequenceUuid); 0316 /** @brief Check that the timelines hash have not changed between saved version and current status */ 0317 bool checkConsistency(); 0318 0319 protected: 0320 static int next_id; /// next valid id to assign 0321 0322 private: 0323 /** @brief Create a new KdenliveDoc using the provided QDomDocument (an 0324 * existing project file), used by the Open() named constructor. */ 0325 KdenliveDoc(const QUrl &url, QDomDocument& newDom, QString projectFolder, QUndoGroup *undoGroup, 0326 MainWindow *parent = nullptr); 0327 /** @brief Set document default properties using hard-coded values and KdenliveSettings. 0328 * @param newDocument true if we are creating a new document, false when opening an existing one 0329 */ 0330 void initializeProperties(bool newDocument = true, std::pair<int, int> tracks = {}, int audioChannels = 2); 0331 QUuid m_uuid; 0332 QDomDocument m_document; 0333 int m_clipsCount; 0334 /** @brief MLT's root (base path) that is stripped from urls in saved xml */ 0335 QString m_documentRoot; 0336 Timecode m_timecode; 0337 std::shared_ptr<DocUndoStack> m_commandStack; 0338 QString m_searchFolder; 0339 0340 /** @brief Tells whether the current document has been changed after being saved. */ 0341 bool m_modified; 0342 0343 /** @brief The default recommended proxy extension */ 0344 QString m_proxyExtension; 0345 0346 /** @brief The default recommended proxy params */ 0347 QString m_proxyParams; 0348 0349 /** @brief Tells whether the current document was modified by Kdenlive on opening, and a backup should be created on save. */ 0350 enum DOCSTATUS { CleanProject, ModifiedProject, UpgradedProject }; 0351 DOCSTATUS m_documentOpenStatus; 0352 0353 QUrl m_url; 0354 0355 /** @brief The project folder, used to store project files (titles, effects...). */ 0356 QString m_projectFolder; 0357 QList<int> m_undoChunks; 0358 QMap<QString, QString> m_documentProperties; 0359 QMap<QString, QString> m_documentMetadata; 0360 QMap<QUuid, QMap<QString, QString>> m_sequenceProperties; 0361 QUuid m_filteredTimelineUuid; 0362 QSet<QUuid> m_sequenceThumbsNeedsRefresh; 0363 0364 QString m_modifiedDecimalPoint; 0365 /** @brief A list of guide models for this project (one for each timeline). */ 0366 QMap<QUuid, std::shared_ptr<TimelineItemModel>> m_timelines; 0367 QString searchFileRecursively(const QDir &dir, const QString &matchSize, const QString &matchHash) const; 0368 0369 /** @brief Creates a new project. */ 0370 QDomDocument createEmptyDocument(const QList<TrackInfo> &tracks, bool disableProfile); 0371 0372 /** @brief Updates the project folder location entry in the kdenlive file dialogs to point to the current project folder. */ 0373 void updateProjectFolderPlacesEntry(); 0374 /** @brief Only keep some backup files, delete some */ 0375 void cleanupBackupFiles(); 0376 /** @brief Load document properties from the xml file */ 0377 void loadDocumentProperties(); 0378 /** @brief update document properties to reflect a change in the current profile */ 0379 void updateProjectProfile(bool reloadProducers = false, bool reloadThumbs = false); 0380 /** @brief initialize proxy settings based on hw status */ 0381 void initProxySettings(); 0382 0383 public Q_SLOTS: 0384 void slotCreateTextTemplateClip(const QString &group, const QString &groupId, QUrl path); 0385 0386 /** @brief Sets the document as modified or up to date. 0387 * 0388 * If crash recovery is turned on, a timer calls KdenliveDoc::slotAutoSave() \n 0389 * Emits docModified connected to MainWindow::slotUpdateDocumentState \n 0390 * 0391 * @param mod (optional) true if the document has to be saved */ 0392 void setModified(bool mod = true); 0393 QMap<QString, QString> proxyClipsById(const QStringList &ids, bool proxy, const QMap<QString, QString> &proxyPath = QMap<QString, QString>()); 0394 void slotProxyCurrentItem(bool doProxy, QList<std::shared_ptr<ProjectClip>> clipList = QList<std::shared_ptr<ProjectClip>>(), bool force = false, 0395 QUndoCommand *masterCommand = nullptr); 0396 /** @brief Saves the current project at the autosave location. 0397 * 0398 * The autosave files are in ~/.kde/data/stalefiles/kdenlive/ */ 0399 void slotAutoSave(const QString &scene); 0400 void switchProfile(ProfileParam* pf, const QString &clipName); 0401 0402 private Q_SLOTS: 0403 void slotModified(); 0404 void slotSwitchProfile(const QString &profile_path, bool reloadThumbs); 0405 /** @brief Check if we did a new action invalidating more recent undo items. */ 0406 void checkPreviewStack(int ix); 0407 /** @brief Display error message on failed move. */ 0408 void slotMoveFinished(KJob *job); 0409 /** @brief Save the project guide categories in the document properties. */ 0410 void saveGuideCategories(); 0411 0412 Q_SIGNALS: 0413 void resetProjectList(); 0414 0415 /** @brief Informs that the document status has been changed. 0416 * 0417 * If the document has been modified, it's called with true as an argument. */ 0418 void docModified(bool); 0419 void selectLastAddedClip(const QString &); 0420 /** @brief When creating a backup file, also save a thumbnail of current timeline */ 0421 void saveTimelinePreview(const QString &path); 0422 /** @brief Trigger the autosave timer start */ 0423 void startAutoSave(); 0424 /** @brief Current doc created effects, reload list */ 0425 void reloadEffects(const QStringList &paths); 0426 /** @brief Fps was changed, update timeline (changed = 1 means no change) */ 0427 void updateFps(double changed); 0428 /** @brief If a command is pushed when we are in the middle of undo stack, invalidate further undo history */ 0429 void removeInvalidUndo(int ix); 0430 /** @brief Update compositing info */ 0431 void updateCompositionMode(bool); 0432 };