1 /** 2 * \file taggedfile.h 3 * Base class for tagged files. 4 * 5 * \b Project: Kid3 6 * \author Urs Fleisch 7 * \date 25 Sep 2005 8 * 9 * Copyright (C) 2005-2018 Urs Fleisch 10 * 11 * This file is part of Kid3. 12 * 13 * Kid3 is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * Kid3 is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program. If not, see <http://www.gnu.org/licenses/>. 25 */ 26 27 #pragma once 28 29 #include <QString> 30 #include <QStringList> 31 #include <QList> 32 #include <QPersistentModelIndex> 33 #include "frame.h" 34 35 class FileProxyModel; 36 37 /** Base class for tagged files. */ 38 class KID3_CORE_EXPORT TaggedFile { 39 public: 40 /** 41 * Special features and formats supported. 42 * Additional information which cannot be deduced from the file format 43 * supported. 44 */ 45 enum Feature { 46 TF_ID3v11 = 1 << 0, /**< Supports ID3v1.1 tags */ 47 TF_ID3v22 = 1 << 1, /**< Supports ID3v2.2 tags */ 48 TF_ID3v23 = 1 << 2, /**< Supports ID3v2.3 tags */ 49 TF_ID3v24 = 1 << 3, /**< Supports ID3v2.4 tags */ 50 TF_OggPictures = 1 << 4, /**< Supports pictures in Ogg files */ 51 TF_OggFlac = 1 << 5 /**< Supports Ogg FLAC files */ 52 }; 53 54 /** Information about file. */ 55 struct KID3_CORE_EXPORT DetailInfo { 56 /** Channel mode. */ 57 enum ChannelMode { CM_None, CM_Stereo, CM_JointStereo }; 58 59 /** Constructor. */ 60 DetailInfo(); 61 62 QString format; /**< format description */ 63 ChannelMode channelMode; /**< channel mode */ 64 unsigned channels; /**< number of channels > 0 */ 65 unsigned sampleRate; /**< sample rate in Hz > 0 */ 66 unsigned bitrate; /**< 0 < bitrate in kbps < 16384 */ 67 unsigned long duration; /**< duration in seconds > 0 */ 68 bool valid; /**< true if information valid */ 69 bool vbr; /**< true if variable bitrate */ 70 71 /** 72 * Get string representation of detail information. 73 * @return information summary as string. 74 */ 75 QString toString() const; 76 }; 77 78 /** 79 * Constructor. 80 * 81 * @param idx index in file proxy model 82 */ 83 explicit TaggedFile(const QPersistentModelIndex& idx); 84 85 /** 86 * Destructor. 87 */ 88 virtual ~TaggedFile() = default; 89 90 /** 91 * Set file name. 92 * 93 * @param fn file name 94 */ 95 void setFilename(const QString& fn); 96 97 /** 98 * Set file name and format it if format while editing is switched on. 99 * 100 * @param fn file name 101 */ 102 void setFilenameFormattedIfEnabled(QString fn); 103 104 /** 105 * Get file name. 106 * 107 * @return file name 108 */ getFilename()109 const QString& getFilename() const { return m_newFilename; } 110 111 /** 112 * Get directory name. 113 * 114 * @return directory name 115 */ 116 QString getDirname() const; 117 118 /** 119 * Get key of tagged file format. 120 * @return key. 121 */ 122 virtual QString taggedFileKey() const = 0; 123 124 /** 125 * Get features supported. 126 * @return bit mask with Feature flags set. 127 */ 128 virtual int taggedFileFeatures() const; 129 130 /** 131 * Get currently active tagged file features. 132 * @return active tagged file features. 133 * @see setActiveTaggedFileFeatures() 134 */ 135 virtual int activeTaggedFileFeatures() const; 136 137 /** 138 * Activate some features provided by the tagged file. 139 * For example, if the TF_ID3v24 feature is provided, it can be set, so that 140 * writeTags() will write ID3v2.4.0 tags. If the feature is deactivated by 141 * passing 0, tags in the default format will be written again. 142 * 143 * @param features bit mask with some of the Feature flags which are 144 * provided by this file, as returned by taggedFileFeatures(), 0 to disable 145 * special features. 146 */ 147 virtual void setActiveTaggedFileFeatures(int features); 148 149 /** 150 * Read tags from file. 151 * Implementations should call notifyModelDataChanged(). 152 * 153 * @param force true to force reading even if tags were already read. 154 */ 155 virtual void readTags(bool force) = 0; 156 157 /** 158 * Write tags to file and rename it if necessary. 159 * 160 * @param force true to force writing even if file was not changed. 161 * @param renamed will be set to true if the file was renamed, 162 * i.e. the file name is no longer valid, else *renamed 163 * is left unchanged 164 * @param preserve true to preserve file time stamps 165 * 166 * @return true if ok, false if the file could not be written or renamed. 167 */ 168 virtual bool writeTags(bool force, bool* renamed, bool preserve) = 0; 169 170 /** 171 * Free resources allocated when calling readTags(). 172 * Implementations should call notifyModelDataChanged(). 173 * 174 * @param force true to force clearing even if the tags are modified 175 */ 176 virtual void clearTags(bool force) = 0; 177 178 /** 179 * Remove frames. 180 * 181 * @param tagNr tag number 182 * @param flt filter specifying which frames to remove 183 */ 184 virtual void deleteFrames(Frame::TagNumber tagNr, const FrameFilter& flt); 185 186 /** 187 * Check if file has a tag. 188 * 189 * @return true if a tag is available. 190 * @see isTagInformationRead() 191 */ 192 virtual bool hasTag(Frame::TagNumber tagNr) const; 193 194 /** 195 * Check if tags are supported by the format of this file. 196 * 197 * @param tagNr tag number 198 * @return true if tags are supported. 199 */ 200 virtual bool isTagSupported(Frame::TagNumber tagNr) const; 201 202 /** 203 * Check if tag information has already been read. 204 * 205 * @return true if information is available, 206 * false if the tags have not been read yet, in which case 207 * hasTag() does not return meaningful information. 208 */ 209 virtual bool isTagInformationRead() const = 0; 210 211 /** 212 * Get technical detail information. 213 * 214 * @param info the detail information is returned here 215 */ 216 virtual void getDetailInfo(DetailInfo& info) const = 0; 217 218 /** 219 * Get duration of file. 220 * 221 * @return duration in seconds, 222 * 0 if unknown. 223 */ 224 virtual unsigned getDuration() const = 0; 225 226 /** 227 * Get file extension including the dot. 228 * 229 * @return file extension, e.g. ".mp3". 230 */ 231 virtual QString getFileExtension() const = 0; 232 233 /** 234 * Get the format of tag. 235 * 236 * @param tagNr tag number 237 * @return string describing format of tag, 238 * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", 239 * QString::null if unknown. 240 */ 241 virtual QString getTagFormat(Frame::TagNumber tagNr) const; 242 243 /** 244 * Get a specific frame from the tags. 245 * 246 * @param tagNr tag number 247 * @param type frame type 248 * @param frame the frame is returned here 249 * 250 * @return true if ok. 251 */ 252 virtual bool getFrame(Frame::TagNumber tagNr, Frame::Type type, Frame& frame) const = 0; 253 254 /** 255 * Set a frame in the tags. 256 * 257 * @param tagNr tag number 258 * @param frame frame to set. 259 * 260 * @return true if ok. 261 */ 262 virtual bool setFrame(Frame::TagNumber tagNr, const Frame& frame) = 0; 263 264 /** 265 * Add a frame in the tags. 266 * 267 * @param tagNr tag number 268 * @param frame frame to add, a field list may be added by this method 269 * 270 * @return true if ok. 271 */ 272 virtual bool addFrame(Frame::TagNumber tagNr, Frame& frame); 273 274 /** 275 * Delete a frame from the tags. 276 * 277 * @param tagNr tag number 278 * @param frame frame to delete 279 * 280 * @return true if ok. 281 */ 282 virtual bool deleteFrame(Frame::TagNumber tagNr, const Frame& frame); 283 284 /** 285 * Get a list of frame IDs which can be added. 286 * @param tagNr tag number 287 * @return list with frame IDs. 288 */ 289 virtual QStringList getFrameIds(Frame::TagNumber tagNr) const = 0; 290 291 /** 292 * Get all frames in tag. 293 * 294 * @param tagNr tag number 295 * @param frames frame collection to set. 296 */ 297 virtual void getAllFrames(Frame::TagNumber tagNr, FrameCollection& frames); 298 299 /** 300 * Close any file handles which are held open by the tagged file object. 301 * The default implementation does nothing. If a concrete subclass holds 302 * any file handles open, it has to close them in this method. This method 303 * can be used before operations which require that a file is not open, 304 * e.g. file renaming on Windows. 305 */ 306 virtual void closeFileHandle(); 307 308 /** 309 * Add a suitable field list for the frame if missing. 310 * If a frame is created, its field list is empty. This method will create 311 * a field list appropriate for the frame type and tagged file type if no 312 * field list exists. The default implementation does nothing. 313 * @param tagNr tag number 314 * @param frame frame where field list is added 315 */ 316 virtual void addFieldList(Frame::TagNumber tagNr, Frame& frame) const; 317 318 /** 319 * Set frames in tag. 320 * 321 * @param tagNr tag number 322 * @param frames frame collection 323 * @param onlyChanged only frames with value marked as changed are set 324 */ 325 void setFrames(Frame::TagNumber tagNr, const FrameCollection& frames, bool onlyChanged = true); 326 327 /** 328 * Get tags from filename. 329 * Supported formats: 330 * album/track - artist - song 331 * artist - album/track song 332 * /artist - album - track - song 333 * album/artist - track - song 334 * artist/album/track song 335 * album/artist - song 336 * 337 * @param frames frames to put result 338 * @param fmt format string containing the following codes: 339 * %s title (song) 340 * %l album 341 * %a artist 342 * %c comment 343 * %y year 344 * %t track 345 */ 346 void getTagsFromFilename(FrameCollection& frames, const QString& fmt); 347 348 /** 349 * Check if file is changed. 350 * 351 * @return true if file was changed. 352 */ isChanged()353 bool isChanged() const { return m_modified; } 354 355 /** 356 * Check if filename is changed. 357 * 358 * @return true if filename was changed. 359 */ isFilenameChanged()360 bool isFilenameChanged() const { return m_newFilename != m_filename; } 361 362 /** 363 * Get absolute filename. 364 * 365 * @return absolute file path. 366 */ 367 QString getAbsFilename() const; 368 369 /** 370 * Undo reverted modification of filename. 371 * When writeTags() fails because the file is not writable, the filename is 372 * reverted using revertChangedFilename() so that the file permissions can be 373 * changed using the real filename. After changing the permissions, this 374 * function can be used to change the filename back before saving the file. 375 */ 376 void undoRevertChangedFilename(); 377 378 /** 379 * Update the current filename after the file was renamed. 380 */ 381 void updateCurrentFilename(); 382 383 /** 384 * Check if tag was changed. 385 * @param tagNr tag number 386 * @return true if tag 1 was changed. 387 */ isTagChanged(Frame::TagNumber tagNr)388 bool isTagChanged(Frame::TagNumber tagNr) const { 389 return tagNr < Frame::Tag_NumValues ? m_changed[tagNr] : false; 390 } 391 392 /** 393 * Mark tag as changed. 394 * 395 * @param tagNr tag number 396 * @param type type of changed frame 397 */ 398 void markTagChanged(Frame::TagNumber tagNr, Frame::Type type); 399 400 /** 401 * Mark tag as unchanged. 402 * @param tagNr tag number 403 */ 404 void markTagUnchanged(Frame::TagNumber tagNr); 405 406 /** 407 * Get the mask of the frame types changed in tag. 408 * @param tagNr tag number 409 * @return mask of frame types. 410 */ getChangedFrames(Frame::TagNumber tagNr)411 quint64 getChangedFrames(Frame::TagNumber tagNr) const { 412 return tagNr < Frame::Tag_NumValues ? m_changedFrames[tagNr] : 0; 413 } 414 415 /** 416 * Set the mask of the frame types changed in tag. 417 * @param tagNr tag number 418 * @param mask mask of frame types 419 */ 420 void setChangedFrames(Frame::TagNumber tagNr, quint64 mask); 421 422 /** 423 * Get the truncation flags. 424 * @param tagNr tag number 425 * @return truncation flags. 426 */ getTruncationFlags(Frame::TagNumber tagNr)427 quint64 getTruncationFlags(Frame::TagNumber tagNr) const { 428 return tagNr == Frame::Tag_Id3v1 ? m_truncation : 0; 429 } 430 431 /** 432 * Format track number/total number of tracks with configured digits. 433 * 434 * @param num track number, <= 0 if empty 435 * @param numTracks total number of tracks, <= 0 to disable 436 * 437 * @return formatted "track/total" string. 438 */ 439 QString trackNumberString(int num, int numTracks) const; 440 441 /** 442 * Format the track number (digits, total number of tracks) if enabled. 443 * 444 * @param value string containing track number, will be modified 445 * @param addTotal true to add total number of tracks if enabled 446 * "/t" with t = total number of tracks will be appended 447 * if enabled and value contains a number 448 */ 449 void formatTrackNumberIfEnabled(QString& value, bool addTotal) const; 450 451 /** 452 * Get the total number of tracks in the directory. 453 * 454 * @return total number of tracks, -1 if unavailable. 455 */ 456 int getTotalNumberOfTracksInDir() const; 457 458 /** 459 * Get index of tagged file in model. 460 * @return index 461 */ getIndex()462 const QPersistentModelIndex& getIndex() const { return m_index; } 463 464 /** 465 * Check if the file is marked. 466 */ isMarked()467 bool isMarked() const { return m_marked; } 468 469 /** 470 * Format a time string "h:mm:ss". 471 * If the time is less than an hour, the hour is not put into the 472 * string and the minute is not padded with zeroes. 473 * 474 * @param seconds time in seconds 475 * 476 * @return string with the time in hours, minutes and seconds. 477 */ 478 static QString formatTime(unsigned seconds); 479 480 /** 481 * Split a track string into number and total. 482 * 483 * @param str track 484 * @param total the total is returned here if found, else 0 485 * 486 * @return number, 0 if parsing failed, -1 if str is null 487 */ 488 static int splitNumberAndTotal(const QString& str, int* total=nullptr); 489 490 /** 491 * Get access and modification time of file. 492 * @param path file path 493 * @param actime the last access time is returned here 494 * @param modtime the last modification time is returned here 495 * @return true if ok. 496 */ 497 static bool getFileTimeStamps(const QString& path, 498 quint64& actime, quint64& modtime); 499 500 /** 501 * Set access and modification time of file. 502 * @param path file path 503 * @param actime last access time 504 * @param modtime last modification time 505 * @return true if ok. 506 */ 507 static bool setFileTimeStamps(const QString& path, 508 quint64 actime, quint64 modtime); 509 510 /** 511 * Free static resources. 512 */ 513 static void staticCleanup(); 514 515 protected: 516 /** 517 * Rename a file. 518 * This methods takes care of case insensitive filesystems. 519 * @return true if ok. 520 */ 521 bool renameFile() const; 522 523 /** 524 * Get field name for comment from configuration. 525 * 526 * @return field name. 527 */ 528 QString getCommentFieldName() const; 529 530 /** 531 * Get the total number of tracks if it is enabled. 532 * 533 * @return total number of tracks, 534 * -1 if disabled or unavailable. 535 */ 536 int getTotalNumberOfTracksIfEnabled() const; 537 538 /** 539 * Get the number of track number digits configured. 540 * 541 * @return track number digits, 542 * 1 if invalid or unavailable. 543 */ 544 int getTrackNumberDigits() const; 545 546 /** 547 * Get current filename. 548 * @return existing name. 549 */ currentFilename()550 QString currentFilename() const { return m_filename; } 551 552 /** 553 * Get current path to file. 554 * @return absolute path. 555 */ 556 QString currentFilePath() const; 557 558 /** 559 * Mark filename as unchanged. 560 */ 561 void markFilenameUnchanged(); 562 563 /** 564 * Revert modification of filename. 565 */ 566 void revertChangedFilename(); 567 568 /** 569 * Check if a string has to be truncated. 570 * 571 * @param tagNr tag number 572 * @param str string to be checked 573 * @param flag flag to be set if string has to be truncated 574 * @param len maximum length of string 575 * 576 * @return str truncated to len characters if necessary, else QString::null. 577 */ 578 QString checkTruncation(Frame::TagNumber tagNr, const QString& str, 579 quint64 flag, int len = 30); 580 581 /** 582 * Check if a number has to be truncated. 583 * 584 * @param tagNr tag number 585 * @param val value to be checked 586 * @param flag flag to be set if number has to be truncated 587 * @param max maximum value 588 * 589 * @return val truncated to max if necessary, else -1. 590 */ 591 int checkTruncation(Frame::TagNumber tagNr, int val, quint64 flag, 592 int max = 255); 593 594 /** 595 * Clear all truncation flags. 596 * @param tagNr tag number 597 */ clearTrunctionFlags(Frame::TagNumber tagNr)598 void clearTrunctionFlags(Frame::TagNumber tagNr) { 599 if (tagNr == Frame::Tag_Id3v1) m_truncation = 0; 600 } 601 602 /** 603 * Get file proxy model. 604 * @return file proxy model. 605 */ 606 const FileProxyModel* getFileProxyModel() const; 607 608 /** 609 * Notify model about changes in extra model data, e.g. the information on 610 * which the CoreTaggedFileIconProvider depends. 611 * 612 * This method shall be called when such data changes, e.g. at the end of 613 * readTags() implementations. 614 * 615 * @param priorIsTagInformationRead prior value returned by 616 * isTagInformationRead() 617 */ 618 void notifyModelDataChanged(bool priorIsTagInformationRead) const; 619 620 /** 621 * Notify model about changes in the truncation state. 622 * 623 * This method shall be called when truncation is checked. 624 * 625 * @param priorTruncation prior value of m_truncation != 0 626 */ 627 void notifyTruncationChanged(bool priorTruncation) const; 628 629 /** 630 * Update marked property of frames. 631 * Mark frames which violate configured rules. This method should be called 632 * in reimplementations of getAllFrames(). 633 * 634 * @param tagNr tag number 635 * @param frames frames to check 636 */ 637 void updateMarkedState(Frame::TagNumber tagNr, FrameCollection& frames); 638 639 private: 640 TaggedFile(const TaggedFile&); 641 TaggedFile& operator=(const TaggedFile&); 642 643 void updateModifiedState(); 644 645 /** Index of file in model */ 646 QPersistentModelIndex m_index; 647 /** File name */ 648 QString m_filename; 649 /** New file name */ 650 QString m_newFilename; 651 /** File name reverted because file was not writable */ 652 QString m_revertedFilename; 653 /** changed tag frame types */ 654 quint64 m_changedFrames[Frame::Tag_NumValues]; 655 /** Truncation flags. */ 656 quint64 m_truncation; 657 /** true if tags were changed */ 658 bool m_changed[Frame::Tag_NumValues]; 659 /** true if tagged file is modified */ 660 bool m_modified; 661 /** true if tagged file is marked */ 662 bool m_marked; 663 }; 664