File indexing completed on 2024-05-12 04:51:35
0001 /* 0002 SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org> 0003 SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl> 0004 SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "k3baudiometainforenamerplugin.h" 0010 0011 #include <config-k3b.h> 0012 0013 // the k3b stuff we need 0014 #include "k3bcore.h" 0015 #include "k3bdatadoc.h" 0016 #include "k3bdiritem.h" 0017 #include "k3bfileitem.h" 0018 #include "k3bmixeddoc.h" 0019 #include "k3bplugin_i18n.h" 0020 0021 #include <KConfig> 0022 #include <KMessageBox> 0023 0024 #include <QDebug> 0025 #include <QFile> 0026 #include <QMimeDatabase> 0027 #include <QMimeType> 0028 #include <QPair> 0029 #include <QLatin1String> 0030 #include <QHash> 0031 #include <QString> 0032 #include <QIcon> 0033 #include <QCheckBox> 0034 #include <QComboBox> 0035 #include <QGroupBox> 0036 #include <QLabel> 0037 #include <QLayout> 0038 #include <QPushButton> 0039 #include <QRadioButton> 0040 #include <QToolTip> 0041 #include <QTreeWidget> 0042 #include <QVBoxLayout> 0043 0044 // Taglib includes 0045 #include <tag.h> 0046 #include <fileref.h> 0047 #include <audioproperties.h> 0048 #include <mpegfile.h> 0049 #include <vorbisfile.h> 0050 #include <oggflacfile.h> 0051 0052 0053 K_PLUGIN_CLASS_WITH_JSON(K3bAudioMetainfoRenamerPlugin , "k3baudiometainforenamerplugin.json") 0054 0055 0056 namespace { 0057 class K3bMimeTypeResolver : public TagLib::FileRef::FileTypeResolver 0058 { 0059 public: 0060 // to make gcc shut up 0061 virtual ~K3bMimeTypeResolver() {} 0062 0063 TagLib::File* createFile( TagLib::FileName fileName, bool, TagLib::AudioProperties::ReadStyle ) const override 0064 { 0065 QMimeType mimetype = m_mimeDataBase.mimeTypeForFile( QFile::decodeName( fileName ) ); 0066 if ( mimetype.isValid() ) { 0067 if ( mimetype.name() == QLatin1String( "audio/mpeg" ) ) 0068 return new TagLib::MPEG::File(fileName); 0069 else if ( mimetype.name() == QLatin1String( "application/ogg" ) ) 0070 return new TagLib::Ogg::Vorbis::File(fileName); 0071 else if ( mimetype.name() == QLatin1String( "application/x-flac" ) ) 0072 return new TagLib::Ogg::FLAC::File(fileName); 0073 } 0074 0075 return 0; 0076 } 0077 0078 private: 0079 QMimeDatabase m_mimeDataBase; 0080 }; 0081 } 0082 0083 0084 class K3bAudioMetainfoRenamerPluginWidget::Private 0085 { 0086 public: 0087 K3b::DataDoc* doc; 0088 QString pattern; 0089 0090 QComboBox* comboPattern; 0091 QTreeWidget* viewFiles; 0092 // KProgressDialog* progressDialog; 0093 QPushButton* scanButton; 0094 0095 QList< QPair<K3b::FileItem*, QTreeWidgetItem*> > renamableItems; 0096 QHash<K3b::DirItem*, QTreeWidgetItem*> dirItemHash; 0097 0098 // long long scannedSize; 0099 // int progressCounter; 0100 }; 0101 0102 0103 K3bAudioMetainfoRenamerPluginWidget::K3bAudioMetainfoRenamerPluginWidget( K3b::DataDoc* doc, 0104 QWidget* parent ) 0105 : QWidget( parent ) 0106 { 0107 d = new Private(); 0108 d->doc = doc; 0109 // d->progressDialog = 0; 0110 0111 // pattern group 0112 QGroupBox* patternGroup = new QGroupBox( i18n("Rename Pattern"), this ); 0113 QHBoxLayout* patternGroupLayout = new QHBoxLayout( patternGroup ); 0114 0115 d->comboPattern = new QComboBox( patternGroup ); 0116 d->comboPattern->setEditable( true ); 0117 0118 d->scanButton = new QPushButton( i18n("Scan"), patternGroup ); 0119 patternGroupLayout->addWidget( d->comboPattern ); 0120 patternGroupLayout->addWidget( d->scanButton ); 0121 0122 // the files view 0123 QGroupBox* filesGroup = new QGroupBox( i18n("Found Files"), this ); 0124 QHBoxLayout* filesGroupLayout = new QHBoxLayout( filesGroup ); 0125 0126 d->viewFiles = new QTreeWidget( filesGroup ); 0127 d->viewFiles->setHeaderLabels( QStringList() << i18n("New Name") << i18n("Old Name") ); 0128 // FIXME: how about a class that installs an event filter to paint the no item text? 0129 // d->viewFiles->setNoItemText( i18n("Please click the Scan button to search for renameable files.") ); 0130 0131 filesGroupLayout->addWidget( d->viewFiles ); 0132 0133 // layout 0134 QVBoxLayout* box = new QVBoxLayout( this ); 0135 box->setContentsMargins( 0, 0, 0, 0 ); 0136 0137 box->addWidget( patternGroup ); 0138 box->addWidget( filesGroup ); 0139 0140 connect( d->scanButton, SIGNAL(clicked()), this, SLOT(slotScanClicked()) ); 0141 0142 d->scanButton->setToolTip( i18n("Scan for renamable files") ); 0143 d->comboPattern->setWhatsThis( i18n("<qt>This specifies how the files should be renamed. " 0144 "Currently only the special strings <em>%a</em> (Artist), " 0145 "<em>%n</em> (Track number), and <em>%t</em> (Title) " 0146 "are supported.") ); 0147 0148 TagLib::FileRef::addFileTypeResolver( new K3bMimeTypeResolver() ); 0149 } 0150 0151 0152 K3bAudioMetainfoRenamerPluginWidget::~K3bAudioMetainfoRenamerPluginWidget() 0153 { 0154 delete d; 0155 } 0156 0157 0158 QString K3bAudioMetainfoRenamerPluginWidget::title() const 0159 { 0160 return i18n("Rename Audio Files"); 0161 } 0162 0163 0164 QString K3bAudioMetainfoRenamerPluginWidget::subTitle() const 0165 { 0166 return i18n("Based on meta info"); 0167 } 0168 0169 0170 void K3bAudioMetainfoRenamerPluginWidget::readSettings( const KConfigGroup& grp ) 0171 { 0172 d->comboPattern->setEditText( grp.readEntry( "rename pattern", "%a - %t" ) ); 0173 } 0174 0175 0176 void K3bAudioMetainfoRenamerPluginWidget::saveSettings( KConfigGroup grp ) 0177 { 0178 grp.writeEntry( "rename pattern", d->comboPattern->currentText() ); 0179 } 0180 0181 0182 void K3bAudioMetainfoRenamerPluginWidget::slotScanClicked() 0183 { 0184 d->pattern = d->comboPattern->currentText(); 0185 if( d->pattern.isEmpty() ) { 0186 KMessageBox::error( this, i18n("Please specify a valid pattern.") ); 0187 } 0188 else { 0189 // if( d->progressDialog == 0 ) { 0190 // d->progressDialog = new KProgressDialog( this, "scanning_progress", 0191 // i18n("Scanning..."), 0192 // i18n("Scanning for renameable files."), 0193 // true ); 0194 // d->progressDialog->setAllowCancel(false); 0195 // } 0196 0197 K3b::DirItem* dir = d->doc->root(); 0198 0199 // clear old searches 0200 d->viewFiles->clear(); 0201 d->renamableItems.clear(); 0202 d->dirItemHash.clear(); 0203 // d->scannedSize = 0; 0204 // d->progressCounter = 0; 0205 0206 // create root item 0207 QTreeWidgetItem* rootItem = new QTreeWidgetItem( d->viewFiles, QStringList() << QLatin1String( "/" ) ); 0208 rootItem->setIcon( 0, QIcon::fromTheme( "folder" ) ); 0209 0210 // d->progressDialog->show(); 0211 scanDir( dir, rootItem ); 0212 // d->progressDialog->close(); 0213 0214 rootItem->setExpanded(true); 0215 0216 if( d->renamableItems.isEmpty() ) 0217 KMessageBox::error( this, i18n("No renameable files found.") ); 0218 } 0219 } 0220 0221 0222 void K3bAudioMetainfoRenamerPluginWidget::scanDir( K3b::DirItem* dir, QTreeWidgetItem* viewRoot ) 0223 { 0224 qDebug() << "(K3bAudioMetainfoRenamerPluginWidget) scanning dir " << dir->k3bName(); 0225 0226 d->dirItemHash.insert( dir, viewRoot ); 0227 0228 foreach( K3b::DataItem* item, dir->children() ) { 0229 if( item->isFile() ) { 0230 if( item->isRenameable() ) { 0231 QString newName = createNewName( (K3b::FileItem*)item ); 0232 if( !newName.isEmpty() ) { 0233 QTreeWidgetItem* fileViewItem = new QTreeWidgetItem( viewRoot, QStringList() << newName << item->k3bName() ); 0234 fileViewItem->setCheckState( 0, Qt::Checked ); 0235 fileViewItem->setIcon( 0, QIcon::fromTheme( item->mimeType().iconName() ) ); 0236 d->renamableItems.append( qMakePair( (K3b::FileItem*)item, fileViewItem ) ); 0237 } 0238 } 0239 0240 // d->scannedSize += item->k3bSize(); 0241 // d->progressCounter++; 0242 // if( d->progressCounter > 50 ) { 0243 // d->progressCounter = 0; 0244 // d->progressDialog->progressBar()->setProgress( 100*d->scannedSize/d->doc->root()->k3bSize() ); 0245 // qApp->processEvents(); 0246 // } 0247 } 0248 else if( item->isDir() ) { 0249 // create dir item 0250 K3b::DirItem* dirItem = static_cast<K3b::DirItem*>( item ); 0251 if ( !dirItem->children().isEmpty() ) { 0252 QTreeWidgetItem* dirViewItem = new QTreeWidgetItem( viewRoot, QStringList() << item->k3bName() ); 0253 dirViewItem->setIcon( 0, QIcon::fromTheme( "folder" ) ); 0254 scanDir( dirItem, dirViewItem ); 0255 dirViewItem->setExpanded(true); 0256 } 0257 } 0258 } 0259 } 0260 0261 0262 void K3bAudioMetainfoRenamerPluginWidget::activate() 0263 { 0264 if( d->renamableItems.isEmpty() ) { 0265 KMessageBox::error( this, i18n("Please click the Scan button to search for renameable files.") ); 0266 } 0267 else { 0268 for( QList< QPair<K3b::FileItem*, QTreeWidgetItem*> >::iterator it = d->renamableItems.begin(); 0269 it != d->renamableItems.end(); ++it ) { 0270 QPair<K3b::FileItem*, QTreeWidgetItem*>& item = *it; 0271 0272 if( item.second->checkState(0) == Qt::Checked ) 0273 item.first->setK3bName( item.second->text(0) ); 0274 } 0275 0276 d->viewFiles->clear(); 0277 d->renamableItems.clear(); 0278 0279 KMessageBox::information( this, i18n("Done.") ); 0280 } 0281 } 0282 0283 0284 QString K3bAudioMetainfoRenamerPluginWidget::createNewName( K3b::FileItem* item ) 0285 { 0286 TagLib::FileRef file( QFile::encodeName( item->localPath() ).data() ); 0287 0288 if ( !file.isNull() && file.tag() ) { 0289 QString artist, title, track; 0290 artist = TStringToQString( file.tag()->artist() ); 0291 title = TStringToQString( file.tag()->title() ); 0292 if ( file.tag()->track() > 0 ) 0293 track = QString::number( file.tag()->track() ); 0294 0295 QString newName; 0296 for( int i = 0; i < d->pattern.length(); ++i ) { 0297 0298 if( d->pattern[i] == '%' ) { 0299 ++i; 0300 0301 if( i < d->pattern.length() ) { 0302 if( d->pattern[i] == 'a' ) { 0303 if( artist.isEmpty() ) 0304 return QString(); 0305 newName.append(artist); 0306 } 0307 else if( d->pattern[i] == 'n' ) { 0308 if (track.isEmpty()) 0309 return QString(); 0310 newName.append(track); 0311 } 0312 else if( d->pattern[i] == 't' ) { 0313 if( title.isEmpty() ) 0314 return QString(); 0315 newName.append(title); 0316 } 0317 else { 0318 newName.append( "%" ); 0319 newName.append( d->pattern[i] ); 0320 } 0321 } 0322 else { // end of pattern 0323 newName.append( "%" ); 0324 } 0325 } 0326 else { 0327 newName.append( d->pattern[i] ); 0328 } 0329 } 0330 0331 // remove white spaces from end and beginning 0332 newName = newName.trimmed(); 0333 0334 QString extension = item->k3bName().mid( item->k3bName().lastIndexOf('.') ); 0335 0336 if( !newName.isEmpty() ) { 0337 // 0338 // Check if files with that name exists and if so append number 0339 // 0340 if( existsOtherItemWithSameName( item, newName + extension ) ) { 0341 qDebug() << "(K3bAudioMetainfoRenamerPluginWidget) file with name " 0342 << newName << extension << " already exists" << Qt::endl; 0343 int i = 1; 0344 while( existsOtherItemWithSameName( item, newName + QString( " (%1)").arg(i) + extension ) ) 0345 i++; 0346 newName.append( QString( " (%1)").arg(i) ); 0347 } 0348 0349 // append extension 0350 newName.append( extension ); 0351 } 0352 0353 return newName; 0354 } 0355 else 0356 return QString(); 0357 } 0358 0359 0360 bool K3bAudioMetainfoRenamerPluginWidget::existsOtherItemWithSameName( K3b::FileItem* item, const QString& name ) 0361 { 0362 K3b::DirItem* dir = item->parent(); 0363 K3b::DataItem* otherItem = dir->find( name ); 0364 if( otherItem && otherItem != item ) 0365 return true; 0366 0367 QTreeWidgetItem* dirViewItem = d->dirItemHash[dir]; 0368 for ( int i = 0; i < dirViewItem->childCount(); ++i ) { 0369 QTreeWidgetItem* current = dirViewItem->child( i ); 0370 if( current->text(0) == name ) 0371 return true; 0372 } 0373 0374 return false; 0375 } 0376 0377 0378 0379 K3bAudioMetainfoRenamerPlugin::K3bAudioMetainfoRenamerPlugin( QObject* parent, const QVariantList& ) 0380 : K3b::ProjectPlugin( K3b::Doc::DataProject, true, parent ) 0381 { 0382 setText( i18n("Rename Audio Files") ); 0383 setToolTip( i18n("Rename audio files based on their meta info.") ); 0384 setIcon( QIcon::fromTheme( "edit-rename" ) ); 0385 } 0386 0387 0388 K3bAudioMetainfoRenamerPlugin::~K3bAudioMetainfoRenamerPlugin() 0389 { 0390 } 0391 0392 0393 K3b::ProjectPluginGUIBase* K3bAudioMetainfoRenamerPlugin::createGUI( K3b::Doc* doc, QWidget* parent ) 0394 { 0395 if( K3b::DataDoc* dataDoc = dynamic_cast<K3b::DataDoc*>( doc ) ) 0396 return new K3bAudioMetainfoRenamerPluginWidget( dataDoc, parent ); 0397 else if( K3b::MixedDoc* mixedDoc = dynamic_cast<K3b::MixedDoc*>( doc ) ) 0398 return new K3bAudioMetainfoRenamerPluginWidget( mixedDoc->dataDoc(), parent ); 0399 else 0400 return 0; 0401 } 0402 0403 #include "k3baudiometainforenamerplugin.moc" 0404 0405 #include "moc_k3baudiometainforenamerplugin.cpp"