File indexing completed on 2024-05-19 04:56:12

0001 /**
0002  * \file taggedfile.h
0003  * Base class for tagged files.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 25 Sep 2005
0008  *
0009  * Copyright (C) 2005-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #pragma once
0028 
0029 #include <QString>
0030 #include <QStringList>
0031 #include <QList>
0032 #include <QSet>
0033 #include <QPersistentModelIndex>
0034 #include "frame.h"
0035 
0036 class TaggedFileSystemModel;
0037 
0038 /** Base class for tagged files. */
0039 class KID3_CORE_EXPORT TaggedFile {
0040 public:
0041   /**
0042    * Special features and formats supported.
0043    * Additional information which cannot be deduced from the file format
0044    * supported.
0045    */
0046   enum Feature {
0047     TF_ID3v11      = 1 << 0, /**< Supports ID3v1.1 tags */
0048     TF_ID3v22      = 1 << 1, /**< Supports ID3v2.2 tags */
0049     TF_ID3v23      = 1 << 2, /**< Supports ID3v2.3 tags */
0050     TF_ID3v24      = 1 << 3, /**< Supports ID3v2.4 tags */
0051     TF_OggPictures = 1 << 4, /**< Supports pictures in Ogg files */
0052     TF_OggFlac     = 1 << 5  /**< Supports Ogg FLAC files */
0053   };
0054 
0055   /** Tag type. */
0056   enum TagType {
0057     TT_Unknown,
0058     TT_Id3v1,
0059     TT_Id3v2,
0060     TT_Vorbis,
0061     TT_Ape,
0062     TT_Mp4,
0063     TT_Asf,
0064     TT_Info
0065   };
0066 
0067   /** Information about file. */
0068   struct KID3_CORE_EXPORT DetailInfo {
0069     /** Channel mode. */
0070     enum ChannelMode { CM_None, CM_Stereo, CM_JointStereo };
0071 
0072     /** Constructor. */
0073     DetailInfo();
0074 
0075     QString format;          /**< format description */
0076     ChannelMode channelMode; /**< channel mode */
0077     unsigned channels;       /**< number of channels > 0 */
0078     unsigned sampleRate;     /**< sample rate in Hz > 0 */
0079     unsigned bitrate;        /**< 0 < bitrate in kbps < 16384 */
0080     unsigned long duration;  /**< duration in seconds > 0 */
0081     bool valid;              /**< true if information valid */
0082     bool vbr;                /**< true if variable bitrate */
0083 
0084     /**
0085      * Get string representation of detail information.
0086      * @return information summary as string.
0087      */
0088     QString toString() const;
0089   };
0090 
0091   /**
0092    * Constructor.
0093    *
0094    * @param idx index in tagged file system model
0095    */
0096   explicit TaggedFile(const QPersistentModelIndex& idx);
0097 
0098   /**
0099    * Destructor.
0100    */
0101   virtual ~TaggedFile() = default;
0102 
0103   /**
0104    * Set file name.
0105    *
0106    * @param fn file name
0107    */
0108   void setFilename(const QString& fn);
0109 
0110   /**
0111    * Set file name and format it if format while editing is switched on.
0112    *
0113    * @param fn file name
0114    */
0115   void setFilenameFormattedIfEnabled(QString fn);
0116 
0117   /**
0118    * Get file name.
0119    *
0120    * @return file name
0121    */
0122   const QString& getFilename() const { return m_newFilename; }
0123 
0124   /**
0125    * Get directory name.
0126    *
0127    * @return directory name
0128    */
0129   QString getDirname() const;
0130 
0131   /**
0132    * Get key of tagged file format.
0133    * @return key.
0134    */
0135   virtual QString taggedFileKey() const = 0;
0136 
0137   /**
0138    * Get features supported.
0139    * @return bit mask with Feature flags set.
0140    */
0141   virtual int taggedFileFeatures() const;
0142 
0143   /**
0144    * Get currently active tagged file features.
0145    * @return active tagged file features.
0146    * @see setActiveTaggedFileFeatures()
0147    */
0148   virtual int activeTaggedFileFeatures() const;
0149 
0150   /**
0151    * Activate some features provided by the tagged file.
0152    * For example, if the TF_ID3v24 feature is provided, it can be set, so that
0153    * writeTags() will write ID3v2.4.0 tags. If the feature is deactivated by
0154    * passing 0, tags in the default format will be written again.
0155    *
0156    * @param features bit mask with some of the Feature flags which are
0157    * provided by this file, as returned by taggedFileFeatures(), 0 to disable
0158    * special features.
0159    */
0160   virtual void setActiveTaggedFileFeatures(int features);
0161 
0162   /**
0163    * Read tags from file.
0164    * Implementations should call notifyModelDataChanged().
0165    *
0166    * @param force true to force reading even if tags were already read.
0167    */
0168   virtual void readTags(bool force) = 0;
0169 
0170   /**
0171    * Write tags to file and rename it if necessary.
0172    *
0173    * @param force   true to force writing even if file was not changed.
0174    * @param renamed will be set to true if the file was renamed,
0175    *                i.e. the file name is no longer valid, else *renamed
0176    *                is left unchanged
0177    * @param preserve true to preserve file time stamps
0178    *
0179    * @return true if ok, false if the file could not be written or renamed.
0180    */
0181   virtual bool writeTags(bool force, bool* renamed, bool preserve) = 0;
0182 
0183   /**
0184    * Free resources allocated when calling readTags().
0185    * Implementations should call notifyModelDataChanged().
0186    *
0187    * @param force true to force clearing even if the tags are modified
0188    */
0189   virtual void clearTags(bool force) = 0;
0190 
0191   /**
0192    * Remove frames.
0193    *
0194    * @param tagNr tag number
0195    * @param flt filter specifying which frames to remove
0196    */
0197   virtual void deleteFrames(Frame::TagNumber tagNr, const FrameFilter& flt);
0198 
0199   /**
0200    * Check if file has a tag.
0201    *
0202    * @return true if a tag is available.
0203    * @see isTagInformationRead()
0204    */
0205   virtual bool hasTag(Frame::TagNumber tagNr) const;
0206 
0207   /**
0208    * Check if tags are supported by the format of this file.
0209    *
0210    * @param tagNr tag number
0211    * @return true if tags are supported.
0212    */
0213   virtual bool isTagSupported(Frame::TagNumber tagNr) const;
0214 
0215   /**
0216    * Check if tag information has already been read.
0217    *
0218    * @return true if information is available,
0219    *         false if the tags have not been read yet, in which case
0220    *         hasTag() does not return meaningful information.
0221    */
0222   virtual bool isTagInformationRead() const = 0;
0223 
0224   /**
0225    * Get technical detail information.
0226    *
0227    * @param info the detail information is returned here
0228    */
0229   virtual void getDetailInfo(DetailInfo& info) const = 0;
0230 
0231   /**
0232    * Get duration of file.
0233    *
0234    * @return duration in seconds,
0235    *         0 if unknown.
0236    */
0237   virtual unsigned getDuration() const = 0;
0238 
0239   /**
0240    * Get file extension including the dot.
0241    *
0242    * @return file extension, e.g. ".mp3".
0243    */
0244   virtual QString getFileExtension() const = 0;
0245 
0246   /**
0247    * Get the format of tag.
0248    *
0249    * @param tagNr tag number
0250    * @return string describing format of tag,
0251    *         e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE",
0252    *         QString::null if unknown.
0253    */
0254   virtual QString getTagFormat(Frame::TagNumber tagNr) const;
0255 
0256   /**
0257    * Get a specific frame from the tags.
0258    *
0259    * @param tagNr tag number
0260    * @param type  frame type
0261    * @param frame the frame is returned here
0262    *
0263    * @return true if ok.
0264    */
0265   virtual bool getFrame(Frame::TagNumber tagNr, Frame::Type type, Frame& frame) const = 0;
0266 
0267   /**
0268    * Set a frame in the tags.
0269    *
0270    * @param tagNr tag number
0271    * @param frame frame to set.
0272    *
0273    * @return true if ok.
0274    */
0275   virtual bool setFrame(Frame::TagNumber tagNr, const Frame& frame) = 0;
0276 
0277   /**
0278    * Add a frame in the tags.
0279    *
0280    * @param tagNr tag number
0281    * @param frame frame to add, a field list may be added by this method
0282    *
0283    * @return true if ok.
0284    */
0285   virtual bool addFrame(Frame::TagNumber tagNr, Frame& frame);
0286 
0287   /**
0288    * Delete a frame from the tags.
0289    *
0290    * @param tagNr tag number
0291    * @param frame frame to delete
0292    *
0293    * @return true if ok.
0294    */
0295   virtual bool deleteFrame(Frame::TagNumber tagNr, const Frame& frame);
0296 
0297   /**
0298    * Get a list of frame IDs which can be added.
0299    * @param tagNr tag number
0300    * @return list with frame IDs.
0301    */
0302   virtual QStringList getFrameIds(Frame::TagNumber tagNr) const = 0;
0303 
0304   /**
0305    * Get all frames in tag.
0306    *
0307    * @param tagNr tag number
0308    * @param frames frame collection to set.
0309    */
0310   virtual void getAllFrames(Frame::TagNumber tagNr, FrameCollection& frames);
0311 
0312   /**
0313    * Close any file handles which are held open by the tagged file object.
0314    * The default implementation does nothing. If a concrete subclass holds
0315    * any file handles open, it has to close them in this method. This method
0316    * can be used before operations which require that a file is not open,
0317    * e.g. file renaming on Windows.
0318    */
0319   virtual void closeFileHandle();
0320 
0321   /**
0322    * Add a suitable field list for the frame if missing.
0323    * If a frame is created, its field list is empty. This method will create
0324    * a field list appropriate for the frame type and tagged file type if no
0325    * field list exists. The default implementation does nothing.
0326    * @param tagNr tag number
0327    * @param frame frame where field list is added
0328    */
0329   virtual void addFieldList(Frame::TagNumber tagNr, Frame& frame) const;
0330 
0331   /**
0332    * Set frames in tag.
0333    *
0334    * @param tagNr tag number
0335    * @param frames      frame collection
0336    * @param onlyChanged only frames with value marked as changed are set
0337    */
0338   void setFrames(Frame::TagNumber tagNr, const FrameCollection& frames, bool onlyChanged = true);
0339 
0340   /**
0341    * Get tags from filename.
0342    * Supported formats:
0343    * album/track - artist - song
0344    * artist - album/track song
0345    * /artist - album - track - song
0346    * album/artist - track - song
0347    * artist/album/track song
0348    * album/artist - song
0349    *
0350    * @param frames frames to put result
0351    * @param fmt format string containing the following codes:
0352    *            %s title (song)
0353    *            %l album
0354    *            %a artist
0355    *            %c comment
0356    *            %y year
0357    *            %t track
0358    */
0359   void getTagsFromFilename(FrameCollection& frames, const QString& fmt) const;
0360 
0361   /**
0362    * Check if file is changed.
0363    *
0364    * @return true if file was changed.
0365    */
0366   bool isChanged() const { return m_modified; }
0367 
0368   /**
0369    * Check if filename is changed.
0370    *
0371    * @return true if filename was changed.
0372    */
0373   bool isFilenameChanged() const { return m_newFilename != m_filename; }
0374 
0375   /**
0376    * Get absolute filename.
0377    *
0378    * @return absolute file path.
0379    */
0380   QString getAbsFilename() const;
0381 
0382   /**
0383    * Undo reverted modification of filename.
0384    * When writeTags() fails because the file is not writable, the filename is
0385    * reverted using revertChangedFilename() so that the file permissions can be
0386    * changed using the real filename. After changing the permissions, this
0387    * function can be used to change the filename back before saving the file.
0388    */
0389   void undoRevertChangedFilename();
0390 
0391   /**
0392    * Update the current filename after the file was renamed.
0393    */
0394   void updateCurrentFilename();
0395 
0396   /**
0397    * Check if tag was changed.
0398    * @param tagNr tag number
0399    * @return true if tag 1 was changed.
0400    */
0401   bool isTagChanged(Frame::TagNumber tagNr) const {
0402     return tagNr < Frame::Tag_NumValues ? m_changed[tagNr] : false;
0403   }
0404 
0405   /**
0406    * Mark tag as changed.
0407    *
0408    * @param tagNr tag number
0409    * @param extendedType type of changed frame
0410    */
0411   void markTagChanged(Frame::TagNumber tagNr,
0412                       const Frame::ExtendedType& extendedType);
0413 
0414   /**
0415    * Mark tag as unchanged.
0416    * @param tagNr tag number
0417    */
0418   void markTagUnchanged(Frame::TagNumber tagNr);
0419 
0420   /**
0421    * Get the types of the changed frames in a tag.
0422    * @param tagNr tag number
0423    * @return types of changed frames.
0424    */
0425   QList<Frame::ExtendedType> getChangedFrames(Frame::TagNumber tagNr) const;
0426 
0427   /**
0428    * Set the types of the changed frames in a tag.
0429    * @param tagNr tag number
0430    * @param types types of changed frames
0431    */
0432   void setChangedFrames(Frame::TagNumber tagNr,
0433                         const QList<Frame::ExtendedType>& types);
0434 
0435   /**
0436    * Get the truncation flags.
0437    * @param tagNr tag number
0438    * @return truncation flags.
0439    */
0440   quint64 getTruncationFlags(Frame::TagNumber tagNr) const {
0441     return tagNr == Frame::Tag_Id3v1 ? m_truncation : 0;
0442   }
0443 
0444   /**
0445    * Format track number/total number of tracks with configured digits.
0446    *
0447    * @param num track number, <= 0 if empty
0448    * @param numTracks total number of tracks, <= 0 to disable
0449    *
0450    * @return formatted "track/total" string.
0451    */
0452   QString trackNumberString(int num, int numTracks) const;
0453 
0454   /**
0455    * Format the track number (digits, total number of tracks) if enabled.
0456    *
0457    * @param value    string containing track number, will be modified
0458    * @param addTotal true to add total number of tracks if enabled
0459    *                 "/t" with t = total number of tracks will be appended
0460    *                 if enabled and value contains a number
0461    */
0462   void formatTrackNumberIfEnabled(QString& value, bool addTotal) const;
0463 
0464   /**
0465    * Get the total number of tracks in the directory.
0466    *
0467    * @return total number of tracks, -1 if unavailable.
0468    */
0469   int getTotalNumberOfTracksInDir() const;
0470 
0471   /**
0472    * Get index of tagged file in model.
0473    * @return index
0474    */
0475   const QPersistentModelIndex& getIndex() const { return m_index; }
0476 
0477   /**
0478    * Check if the file is marked.
0479    */
0480   bool isMarked() const { return m_marked; }
0481 
0482   /**
0483    * Format a time string "h:mm:ss".
0484    * If the time is less than an hour, the hour is not put into the
0485    * string and the minute is not padded with zeroes.
0486    *
0487    * @param seconds time in seconds
0488    *
0489    * @return string with the time in hours, minutes and seconds.
0490    */
0491   static QString formatTime(unsigned seconds);
0492 
0493   /**
0494    * Split a track string into number and total.
0495    *
0496    * @param str track
0497    * @param total the total is returned here if found, else 0
0498    *
0499    * @return number, 0 if parsing failed, -1 if str is null
0500    */
0501   static int splitNumberAndTotal(const QString& str, int* total=nullptr);
0502 
0503   /**
0504    * Fix up a key to be valid.
0505    * If the key contains new line characters because it is coming from an ID3
0506    * frame (e.g. "COMM - COMMENTS\nDescription"), the description part is taken.
0507    * Illegal characters depending on @a tagType are removed.
0508    *
0509    * @param key key which might have invalid characters.
0510    * @param tagType tag type
0511    * @return key which can be used for tag type.
0512    */
0513   static QString fixUpTagKey(const QString& key, TagType tagType);
0514 
0515   /**
0516    * Get access and modification time of file.
0517    * @param path file path
0518    * @param actime the last access time is returned here
0519    * @param modtime the last modification time is returned here
0520    * @return true if ok.
0521    */
0522   static bool getFileTimeStamps(const QString& path,
0523                                 quint64& actime, quint64& modtime);
0524 
0525   /**
0526    * Set access and modification time of file.
0527    * @param path file path
0528    * @param actime last access time
0529    * @param modtime last modification time
0530    * @return true if ok.
0531    */
0532   static bool setFileTimeStamps(const QString& path,
0533                                 quint64 actime, quint64 modtime);
0534 
0535   /**
0536    * Free static resources.
0537    */
0538   static void staticCleanup();
0539 
0540 protected:
0541   /**
0542    * Rename a file.
0543    * This methods takes care of case insensitive filesystems.
0544    * @return true if ok.
0545    */
0546   bool renameFile() const;
0547 
0548   /**
0549    * Get field name for comment from configuration.
0550    *
0551    * @return field name.
0552    */
0553   QString getCommentFieldName() const;
0554 
0555     /**
0556    * Get the total number of tracks if it is enabled.
0557    *
0558    * @return total number of tracks,
0559    *         -1 if disabled or unavailable.
0560    */
0561   int getTotalNumberOfTracksIfEnabled() const;
0562 
0563   /**
0564    * Get the number of track number digits configured.
0565    *
0566    * @return track number digits,
0567    *         1 if invalid or unavailable.
0568    */
0569   int getTrackNumberDigits() const;
0570 
0571   /**
0572    * Get current filename.
0573    * @return existing name.
0574    */
0575   QString currentFilename() const { return m_filename; }
0576 
0577   /**
0578    * Get current path to file.
0579    * @return absolute path.
0580    */
0581   QString currentFilePath() const;
0582 
0583   /**
0584    * Mark filename as unchanged.
0585    */
0586   void markFilenameUnchanged();
0587 
0588   /**
0589    * Revert modification of filename.
0590    */
0591   void revertChangedFilename();
0592 
0593   /**
0594    * Check if a string has to be truncated.
0595    *
0596    * @param tagNr tag number
0597    * @param str  string to be checked
0598    * @param flag flag to be set if string has to be truncated
0599    * @param len  maximum length of string
0600    *
0601    * @return str truncated to len characters if necessary, else QString::null.
0602    */
0603   QString checkTruncation(Frame::TagNumber tagNr, const QString& str,
0604                           quint64 flag, int len = 30);
0605 
0606   /**
0607    * Check if a number has to be truncated.
0608    *
0609    * @param tagNr tag number
0610    * @param val  value to be checked
0611    * @param flag flag to be set if number has to be truncated
0612    * @param max  maximum value
0613    *
0614    * @return val truncated to max if necessary, else -1.
0615    */
0616   int checkTruncation(Frame::TagNumber tagNr, int val, quint64 flag,
0617                       int max = 255);
0618 
0619   /**
0620    * Clear all truncation flags.
0621    * @param tagNr tag number
0622    */
0623   void clearTrunctionFlags(Frame::TagNumber tagNr) {
0624     if (tagNr == Frame::Tag_Id3v1) m_truncation = 0;
0625   }
0626 
0627   /**
0628    * Get tagged file model.
0629    * @return tagged file model.
0630    */
0631   const TaggedFileSystemModel* getTaggedFileSystemModel() const;
0632 
0633   /**
0634    * Notify model about changes in extra model data, e.g. the information on
0635    * which the CoreTaggedFileIconProvider depends.
0636    *
0637    * This method shall be called when such data changes, e.g. at the end of
0638    * readTags() implementations.
0639    *
0640    * @param priorIsTagInformationRead prior value returned by
0641    * isTagInformationRead()
0642    */
0643   void notifyModelDataChanged(bool priorIsTagInformationRead) const;
0644 
0645   /**
0646    * Notify model about changes in the truncation state.
0647    *
0648    * This method shall be called when truncation is checked.
0649    *
0650    * @param priorTruncation prior value of m_truncation != 0
0651    */
0652   void notifyTruncationChanged(bool priorTruncation) const;
0653 
0654   /**
0655    * Update marked property of frames.
0656    * Mark frames which violate configured rules. This method should be called
0657    * in reimplementations of getAllFrames().
0658    *
0659    * @param tagNr tag number
0660    * @param frames frames to check
0661    */
0662   void updateMarkedState(Frame::TagNumber tagNr, FrameCollection& frames);
0663 
0664 private:
0665   TaggedFile(const TaggedFile&);
0666   TaggedFile& operator=(const TaggedFile&);
0667 
0668   void updateModifiedState();
0669 
0670   /** Index of file in model */
0671   QPersistentModelIndex m_index;
0672   /** File name */
0673   QString m_filename;
0674   /** New file name */
0675   QString m_newFilename;
0676   /** File name reverted because file was not writable */
0677   QString m_revertedFilename;
0678   /** The names of changed tag frames of type Frame::FT_Other */
0679   QSet<QString> m_changedOtherFrameNames[Frame::Tag_NumValues];
0680   /** changed tag frame types */
0681   quint64 m_changedFrames[Frame::Tag_NumValues];
0682   /** Truncation flags. */
0683   quint64 m_truncation;
0684   /** true if tags were changed */
0685   bool m_changed[Frame::Tag_NumValues];
0686   /** true if tagged file is modified */
0687   bool m_modified;
0688   /** true if tagged file is marked */
0689   bool m_marked;
0690 };