File indexing completed on 2024-05-19 04:56:07
0001 /** 0002 * \file scriptinterface.cpp 0003 * D-Bus script adaptor. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 20 Dec 2007 0008 * 0009 * Copyright (C) 2007-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 #include "scriptinterface.h" 0028 #ifdef HAVE_QTDBUS 0029 #include <QDBusMessage> 0030 #include <QDBusConnection> 0031 #include <QFileInfo> 0032 #include <QCoreApplication> 0033 #include <QItemSelectionModel> 0034 #include "kid3application.h" 0035 #include "taggedfile.h" 0036 #include "frametablemodel.h" 0037 #include "filefilter.h" 0038 #include "pictureframe.h" 0039 #include "fileproxymodel.h" 0040 #include "modeliterator.h" 0041 #include "batchimportconfig.h" 0042 #include "batchimportprofile.h" 0043 #include "fileconfig.h" 0044 0045 /** 0046 * Constructor. 0047 * 0048 * @param app parent application 0049 */ 0050 ScriptInterface::ScriptInterface(Kid3Application* app) 0051 : QDBusAbstractAdaptor(app), m_app(app) 0052 { 0053 setObjectName(QLatin1String("ScriptInterface")); 0054 setAutoRelaySignals(true); 0055 } 0056 0057 /** 0058 * Open file or directory. 0059 * 0060 * @param path path to file or directory 0061 * 0062 * @return true if ok. 0063 */ 0064 bool ScriptInterface::openDirectory(const QString& path) 0065 { 0066 return m_app->openDirectory({path}, true); 0067 } 0068 0069 /** 0070 * Unload all tags. 0071 * The tags of all files which are not modified or selected are freed to 0072 * reclaim their memory. 0073 */ 0074 void ScriptInterface::unloadAllTags() 0075 { 0076 m_app->unloadAllTags(); 0077 } 0078 0079 /** 0080 * Save all modified files. 0081 * 0082 * @return true if ok, 0083 * else the error message is available using getErrorMessage(). 0084 */ 0085 bool ScriptInterface::save() 0086 { 0087 if (QStringList errorFiles = m_app->saveDirectory(); errorFiles.isEmpty()) { 0088 m_errorMsg.clear(); 0089 return true; 0090 } else { 0091 m_errorMsg = QLatin1String("Error while writing file:\n") + 0092 errorFiles.join(QLatin1String("\n")); 0093 return false; 0094 } 0095 } 0096 0097 /** 0098 * Get a detailed error message provided by some methods. 0099 * 0100 * @return detailed error message. 0101 */ 0102 QString ScriptInterface::getErrorMessage() const 0103 { 0104 return m_errorMsg; 0105 } 0106 0107 /** 0108 * Revert changes in the selected files. 0109 */ 0110 void ScriptInterface::revert() 0111 { 0112 m_app->revertFileModifications(); 0113 } 0114 0115 /** 0116 * Import tags from a file. 0117 * 0118 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0119 * @param path path of file, "clipboard" for import from clipboard 0120 * @param fmtIdx index of format 0121 * 0122 * @return true if ok. 0123 */ 0124 bool ScriptInterface::importFromFile(int tagMask, const QString& path, 0125 int fmtIdx) 0126 { 0127 return m_app->importTags(Frame::tagVersionCast(tagMask), path, fmtIdx); 0128 } 0129 0130 /** 0131 * Import from tags. 0132 * 0133 * @param tagMask tag mask 0134 * @param source format to get source text from tags 0135 * @param extraction regular expression with frame names and captures to 0136 * extract from source text 0137 */ 0138 void ScriptInterface::importFromTags(int tagMask, 0139 const QString& source, 0140 const QString& extraction) 0141 { 0142 m_app->importFromTags(Frame::tagVersionCast(tagMask), source, extraction); 0143 } 0144 0145 /** 0146 * Import from tags on selected files. 0147 * 0148 * @param tagMask tag mask 0149 * @param source format to get source text from tags 0150 * @param extraction regular expression with frame names and captures to 0151 * extract from source text 0152 * 0153 * @return extracted values for "%{__return}(.+)", empty if not used. 0154 */ 0155 QStringList ScriptInterface::importFromTagsToSelection(int tagMask, 0156 const QString& source, 0157 const QString& extraction) 0158 { 0159 return m_app->importFromTagsToSelection(Frame::tagVersionCast(tagMask), 0160 source, extraction); 0161 } 0162 0163 /** 0164 * Start an automatic batch import. 0165 * 0166 * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) 0167 * @param profileName name of batch import profile to use 0168 * 0169 * @return true if profile found. 0170 */ 0171 bool ScriptInterface::batchImport(int tagMask, const QString& profileName) 0172 { 0173 return m_app->batchImport(profileName, Frame::tagVersionCast(tagMask)); 0174 } 0175 0176 /** 0177 * Download album cover art into the picture frame of the selected files. 0178 * 0179 * @param url URL of picture file or album art resource 0180 * @param allFilesInDir true to add the image to all files in the directory 0181 */ 0182 void ScriptInterface::downloadAlbumArt(const QString& url, bool allFilesInDir) 0183 { 0184 m_app->downloadImage(url, allFilesInDir); 0185 } 0186 0187 /** 0188 * Export tags to a file. 0189 * 0190 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0191 * @param path path of file, "clipboard" for export to clipboard 0192 * @param fmtIdx index of format 0193 * 0194 * @return true if ok. 0195 */ 0196 bool ScriptInterface::exportToFile(int tagMask, const QString& path, int fmtIdx) 0197 { 0198 return m_app->exportTags(Frame::tagVersionCast(tagMask), path, fmtIdx); 0199 } 0200 0201 /** 0202 * Create a playlist. 0203 * 0204 * @return true if ok. 0205 */ 0206 bool ScriptInterface::createPlaylist() 0207 { 0208 return m_app->writePlaylist(); 0209 } 0210 0211 /** 0212 * Get items of a playlist. 0213 * @param path path to playlist file 0214 * @return list of absolute paths to playlist items. 0215 */ 0216 QStringList ScriptInterface::getPlaylistItems(const QString& path) 0217 { 0218 return m_app->getPlaylistItems(path); 0219 } 0220 0221 /** 0222 * Set items of a playlist. 0223 * @param path path to playlist file 0224 * @param items list of absolute paths to playlist items 0225 * @return true if ok, false if not all @a items were found and added or 0226 * saving failed. 0227 */ 0228 bool ScriptInterface::setPlaylistItems(const QString& path, 0229 const QStringList& items) 0230 { 0231 return m_app->setPlaylistItems(path, items); 0232 } 0233 0234 /** 0235 * Quit the application. 0236 */ 0237 void ScriptInterface::quit() 0238 { 0239 selectAll(); 0240 revert(); 0241 QCoreApplication::quit(); 0242 } 0243 0244 /** 0245 * Select all files. 0246 */ 0247 void ScriptInterface::selectAll() 0248 { 0249 m_app->selectAllFiles(); 0250 } 0251 0252 /** 0253 * Deselect all files. 0254 */ 0255 void ScriptInterface::deselectAll() 0256 { 0257 m_app->deselectAllFiles(); 0258 } 0259 0260 /** 0261 * Set the first file as the current file. 0262 * 0263 * @return true if there is a first file. 0264 */ 0265 bool ScriptInterface::firstFile() 0266 { 0267 return m_app->firstFile(false); 0268 } 0269 0270 /** 0271 * Set the previous file as the current file. 0272 * 0273 * @return true if there is a previous file. 0274 */ 0275 bool ScriptInterface::previousFile() 0276 { 0277 return m_app->previousFile(false); 0278 } 0279 0280 /** 0281 * Set the next file as the current file. 0282 * 0283 * @return true if there is a next file. 0284 */ 0285 bool ScriptInterface::nextFile() 0286 { 0287 return m_app->nextFile(false); 0288 } 0289 0290 /** 0291 * Select the first file. 0292 * 0293 * @return true if there is a first file. 0294 */ 0295 bool ScriptInterface::selectFirstFile() 0296 { 0297 return m_app->firstFile(true); 0298 } 0299 0300 /** 0301 * Select the previous file. 0302 * 0303 * @return true if there is a previous file. 0304 */ 0305 bool ScriptInterface::selectPreviousFile() 0306 { 0307 return m_app->previousFile(true); 0308 } 0309 0310 /** 0311 * Select the next file. 0312 * 0313 * @return true if there is a next file. 0314 */ 0315 bool ScriptInterface::selectNextFile() 0316 { 0317 return m_app->nextFile(true); 0318 } 0319 0320 /** 0321 * Select the current file. 0322 * 0323 * @return true if there is a current file. 0324 */ 0325 bool ScriptInterface::selectCurrentFile() 0326 { 0327 return m_app->selectCurrentFile(true); 0328 } 0329 0330 /** 0331 * Expand the current file item if it is a directory. 0332 * A file list item is a directory if getFileName() returns a name with 0333 * '/' as the last character. 0334 * The directory is fetched but not expanded in the GUI. To expand it in the 0335 * GUI, call nextFile() or selectNextFile() after expandDirectory(). 0336 * 0337 * @return true if current file item is a directory. 0338 */ 0339 bool ScriptInterface::expandDirectory() 0340 { 0341 if (QModelIndex index(m_app->getFileSelectionModel()->currentIndex()); 0342 !FileProxyModel::getPathIfIndexOfDir(index).isNull()) { 0343 m_app->expandDirectory(index); 0344 return true; 0345 } 0346 return false; 0347 } 0348 0349 /** 0350 * Expand the file list. 0351 */ 0352 void ScriptInterface::expandFileList() 0353 { 0354 m_app->requestExpandFileList(); 0355 } 0356 0357 /** 0358 * Apply the file name format. 0359 */ 0360 void ScriptInterface::applyFilenameFormat() 0361 { 0362 m_app->applyFilenameFormat(); 0363 } 0364 0365 /** 0366 * Apply the tag format. 0367 */ 0368 void ScriptInterface::applyTagFormat() 0369 { 0370 m_app->applyTagFormat(); 0371 } 0372 0373 /** 0374 * Apply text encoding. 0375 */ 0376 void ScriptInterface::applyTextEncoding() 0377 { 0378 m_app->applyTextEncoding(); 0379 } 0380 0381 /** 0382 * Set the directory name from the tags. 0383 * 0384 * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) 0385 * @param format directory name format 0386 * @param create true to create, false to rename 0387 * 0388 * @return true if ok, 0389 * else the error message is available using getErrorMessage(). 0390 */ 0391 bool ScriptInterface::setDirNameFromTag(int tagMask, const QString& format, 0392 bool create) 0393 { 0394 connect(m_app, &Kid3Application::renameActionsScheduled, 0395 this, &ScriptInterface::onRenameActionsScheduled); 0396 if (m_app->renameDirectory(Frame::tagVersionCast(tagMask), format, 0397 create)) { 0398 return true; 0399 } 0400 disconnect(m_app, &Kid3Application::renameActionsScheduled, 0401 this, &ScriptInterface::onRenameActionsScheduled); 0402 return false; 0403 } 0404 0405 void ScriptInterface::onRenameActionsScheduled() 0406 { 0407 disconnect(m_app, &Kid3Application::renameActionsScheduled, 0408 this, &ScriptInterface::onRenameActionsScheduled); 0409 m_errorMsg = m_app->performRenameActions(); 0410 if (!m_errorMsg.isEmpty()) { 0411 m_errorMsg = QLatin1String("Error while renaming:\n") + m_errorMsg; 0412 } 0413 } 0414 0415 /** 0416 * Set subsequent track numbers in the selected files. 0417 * 0418 * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) 0419 * @param firstTrackNr number to use for first file 0420 */ 0421 void ScriptInterface::numberTracks(int tagMask, int firstTrackNr) 0422 { 0423 m_app->numberTracks(firstTrackNr, 0, Frame::tagVersionCast(tagMask)); 0424 } 0425 0426 /** 0427 * Filter the files. 0428 * 0429 * @param expression filter expression 0430 */ 0431 void ScriptInterface::filter(const QString& expression) 0432 { 0433 m_app->applyFilter(expression); 0434 } 0435 0436 /** 0437 * Convert ID3v2.3 tags to ID3v2.4. 0438 */ 0439 void ScriptInterface::convertToId3v24() 0440 { 0441 m_app->convertToId3v24(); 0442 } 0443 0444 /** 0445 * Convert ID3v2.4 tags to ID3v2.3. 0446 */ 0447 void ScriptInterface::convertToId3v23() 0448 { 0449 m_app->convertToId3v23(); 0450 } 0451 0452 /** 0453 * Get path of directory. 0454 * 0455 * @return absolute path of directory. 0456 */ 0457 QString ScriptInterface::getDirectoryName() 0458 { 0459 return m_app->getDirPath(); 0460 } 0461 0462 /** 0463 * Get name of current file. 0464 * 0465 * @return absolute file name, ends with "/" if it is a directory. 0466 */ 0467 QString ScriptInterface::getFileName() 0468 { 0469 return m_app->getFileNameOfSelectedFile(); 0470 } 0471 0472 /** 0473 * Set name of selected file. 0474 * The file will be renamed when the directory is saved. 0475 * 0476 * @param name file name. 0477 */ 0478 void ScriptInterface::setFileName(const QString& name) 0479 { 0480 m_app->setFileNameOfSelectedFile(name); 0481 } 0482 0483 /** 0484 * Set format to use when setting the filename from the tags. 0485 * 0486 * @param format file name format 0487 * @see setFileNameFromTag() 0488 */ 0489 void ScriptInterface::setFileNameFormat(const QString& format) 0490 { 0491 FileConfig::instance().setToFilenameFormat(format); 0492 } 0493 0494 /** 0495 * Set the file names of the selected files from the tags. 0496 * 0497 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0498 * @see setFileNameFormat() 0499 */ 0500 void ScriptInterface::setFileNameFromTag(int tagMask) 0501 { 0502 m_app->getFilenameFromTags(Frame::tagVersionCast(tagMask)); 0503 } 0504 0505 /** 0506 * Get value of frame. 0507 * To get binary data like a picture, the name of a file to write can be 0508 * added after the @a name, e.g. "Picture:/path/to/file". 0509 * 0510 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0511 * @param name name of frame (e.g. "Artist") 0512 */ 0513 QString ScriptInterface::getFrame(int tagMask, const QString& name) 0514 { 0515 return m_app->getFrame(Frame::tagVersionCast(tagMask), name); 0516 } 0517 0518 /** 0519 * Set value of frame. 0520 * For tag 2 (@a tagMask 2), if no frame with @a name exists, a new frame 0521 * is added, if @a value is empty, the frame is deleted. 0522 * To add binary data like a picture, a file can be added after the 0523 * @a name, e.g. "Picture:/path/to/file". 0524 * 0525 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0526 * @param name name of frame (e.g. "Artist") 0527 * @param value value of frame 0528 */ 0529 bool ScriptInterface::setFrame(int tagMask, const QString& name, 0530 const QString& value) 0531 { 0532 return m_app->setFrame(Frame::tagVersionCast(tagMask), name, value); 0533 } 0534 0535 /** 0536 * Get all frames of a tag. 0537 * 0538 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0539 * 0540 * @return list with alternating frame names and values. 0541 */ 0542 QStringList ScriptInterface::getTag(int tagMask) 0543 { 0544 Frame::TagNumber tagNr = 0545 Frame::tagNumberFromMask(Frame::tagVersionCast(tagMask)); 0546 if (tagNr >= Frame::Tag_NumValues) 0547 return QStringList(); 0548 0549 QStringList lst; 0550 FrameTableModel* ft = m_app->frameModel(tagNr); 0551 for (auto it = ft->frames().cbegin(); it != ft->frames().cend(); ++it) { 0552 lst << it->getName(); 0553 lst << it->getValue(); 0554 } 0555 return lst; 0556 } 0557 0558 /** 0559 * Get technical information about file. 0560 * Properties are Format, Bitrate, Samplerate, Channels, Duration, 0561 * Channel Mode, VBR, Tag 1, Tag 2. 0562 * Properties which are not available are omitted. 0563 * 0564 * @return list with alternating property names and values. 0565 */ 0566 QStringList ScriptInterface::getInformation() 0567 { 0568 QStringList lst; 0569 QModelIndex index = m_app->getFileSelectionModel()->currentIndex(); 0570 if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { 0571 TaggedFile::DetailInfo info; 0572 taggedFile->getDetailInfo(info); 0573 if (info.valid) { 0574 lst << QLatin1String("Format") << info.format; 0575 if (info.bitrate > 0 && info.bitrate < 16384) { 0576 lst << QLatin1String("Bitrate") << QString::number(info.bitrate); 0577 } 0578 if (info.sampleRate > 0) { 0579 lst << QLatin1String("Samplerate") << QString::number(info.sampleRate); 0580 } 0581 if (info.channels > 0) { 0582 lst << QLatin1String("Channels") << QString::number(info.channels); 0583 } 0584 if (info.duration > 0) { 0585 lst << QLatin1String("Duration") << QString::number(info.duration); 0586 } 0587 if (info.channelMode == TaggedFile::DetailInfo::CM_Stereo || 0588 info.channelMode == TaggedFile::DetailInfo::CM_JointStereo) { 0589 lst << QLatin1String("Channel Mode") << 0590 (info.channelMode == TaggedFile::DetailInfo::CM_Stereo ? 0591 QLatin1String("Stereo") : QLatin1String("Joint Stereo")); 0592 } 0593 if (info.vbr) { 0594 lst << QLatin1String("VBR") << QLatin1String("1"); 0595 } 0596 } 0597 FOR_ALL_TAGS(tagNr) { 0598 if (QString tag = taggedFile->getTagFormat(tagNr); !tag.isEmpty()) { 0599 lst << QLatin1String("Tag ") + Frame::tagNumberToString(tagNr) << tag; // clazy:exclude=reserve-candidates 0600 } 0601 } 0602 } 0603 return lst; 0604 } 0605 0606 /** 0607 * Set tag from file name. 0608 * 0609 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0610 */ 0611 void ScriptInterface::setTagFromFileName(int tagMask) 0612 { 0613 m_app->getTagsFromFilename(Frame::tagVersionCast(tagMask)); 0614 } 0615 0616 /** 0617 * Set tag from other tag. 0618 * 0619 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0620 */ 0621 void ScriptInterface::setTagFromOtherTag(int tagMask) 0622 { 0623 m_app->copyToOtherTag(Frame::tagVersionCast(tagMask)); 0624 } 0625 0626 /** 0627 * Copy tag. 0628 * 0629 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0630 */ 0631 void ScriptInterface::copyTag(int tagMask) 0632 { 0633 m_app->copyTags(Frame::tagVersionCast(tagMask)); 0634 } 0635 0636 /** 0637 * Paste tag. 0638 * 0639 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0640 */ 0641 void ScriptInterface::pasteTag(int tagMask) 0642 { 0643 m_app->pasteTags(Frame::tagVersionCast(tagMask)); 0644 } 0645 0646 /** 0647 * Remove tag. 0648 * 0649 * @param tagMask tag bit (1 for tag 1, 2 for tag 2) 0650 */ 0651 void ScriptInterface::removeTag(int tagMask) 0652 { 0653 m_app->removeTags(Frame::tagVersionCast(tagMask)); 0654 } 0655 0656 /** 0657 * Reparse the configuration. 0658 * Automated configuration changes are possible by modifying 0659 * the configuration file and then reparsing the configuration. 0660 */ 0661 void ScriptInterface::reparseConfiguration() 0662 { 0663 m_app->readConfig(); 0664 } 0665 0666 /** 0667 * Play selected audio files. 0668 */ 0669 void ScriptInterface::playAudio() 0670 { 0671 m_app->playAudio(); 0672 } 0673 0674 #endif // HAVE_QTDBUS