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"