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 };