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