aboutsummaryrefslogblamecommitdiff
path: root/src/plugins/General/fileops/fileops.cpp
blob: 365ead53fa1870b0451cb3005f5e1fb92c35ae8a (plain) (tree)
1
2
3
                                                                            
                                                                            
                                                                            













                                                                            
                                                                            


                                                                             
                    
                       


                          
               
                   
                           
                            
                                 
                                   

                                
                                     

                    

                              
                                                   
 

                                            
                                   
                                            
                                   
                   

                                                                 
                                          
                                                                             


               

                                                              
     









                                                                                              
         
                                                      
                                  
                                                                                        
                                                                             
                                                                             
         

            

                        
                                                                         




                   
                          
 








                                                                     
 
                                                                                          
                                                                 




                 
                                
                                        




                                                                              
                                              


                
     
                                  
                                          
              
     
                
     
                                  
                                                                            
                                                                                              
                                                        


                                                                                            
                                                                    

                  
                                                    
         
                                                                        

                      
                                                                                              
                                          
         
              
     


                                
                                        




                                                                              
                                                                          





                                                                                            


                                                     





                                           

     
 
                                                                                                                 





                                                    
                                 
               
                                                
     
                                                            
                     
 

                                                                         
                                                                              
                                                                          







                                               
             
                                                                

                         
         
                                 
                     
 
                   
                                
                        
                                         



                                                                   
                                           



                                                                    
 
                                                              






                                                                                              
                                                                 







                                   
                                                                                                                    
 
                                                
     
                                                                          

                     
                                                                    

                  
                                                                         
 
                                                                              
                                                                          

                                               

                                                                
                                                                                  
         
                                                  
                                    





                                             
 
                                                                                                                                       


                                                    
                                          



                                             
                                                
     
                                                            
                     
 
                                                                    

                  
                                                                         
 
                                                                              
                                                                          



                                                
                                 















                                                                                             





                                                                                       
                                  
                                              

                                   
                                 
                                             


                     
                                
                        
                                         



                                                                   
                                           




                                                                    
                                                              

                             
 



                                                
                                                                 
                                  
         


                   


                                                                                       

                                                                                       
 
                             



                                         
     
                     
 
 










                                                                                                                     
                                                                 









                                                                       

                                                 
                                                                                                           

                                  
/***************************************************************************
 *   Copyright (C) 2009-2021 by Ilya Kotov                                 *
 *   forkotov02@ya.ru                                                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include <QAction>
#include <QSettings>
#include <QApplication>
#include <QProgressDialog>
#include <QMessageBox>
#include <QFile>
#include <QDir>
#include <QProcess>
#include <qmmp/soundcore.h>
#include <qmmpui/uihelper.h>
#include <qmmpui/playlistmodel.h>
#include <qmmpui/playlistmanager.h>
#include <qmmpui/playlistitem.h>
#include <qmmpui/mediaplayer.h>
#include <qmmpui/metadataformatter.h>
#include "fileops.h"

#define COPY_BLOCK_SIZE 102400

FileOps::FileOps(QObject *parent) : QObject(parent)
{
    //separators
    QAction *separator1 = new QAction(this);
    separator1->setSeparator(true);
    QAction *separator2 = new QAction(this);
    separator2->setSeparator(true);
    //load settings
    QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
    settings.beginGroup("FileOps");
    if(!settings.value("name_0").isNull())
        UiHelper::instance()->addAction(separator1, UiHelper::PLAYLIST_MENU);
    else
        return;

    int i = 0;
    while(!settings.value(QString("name_%1").arg(i)).isNull())
    {
        QString name = settings.value(QString("name_%1").arg(i)).toString();
        QVariantMap data = {
            { "action",  settings.value(QString("action_%1").arg(i), FileOps::COPY).toInt() },
            { "pattern", settings.value(QString("pattern_%1").arg(i)).toString() },
            { "destination", settings.value(QString("destination_%1").arg(i)).toString() },
            { "command", settings.value(QString("command_%1").arg(i)).toString() },

        };

        if(settings.value(QString("enabled_%1").arg(i), true).toBool())
        {
            QAction *action = new QAction(name, this);
            action->setData(data);
            action->setShortcut(settings.value(QString("hotkey_%1").arg(i)).toString());
            connect(action, &QAction::triggered, this, &FileOps::execAction);
            UiHelper::instance()->addAction(action, UiHelper::PLAYLIST_MENU);
        }

        ++i;
    }
    settings.endGroup();
    UiHelper::instance()->addAction(separator2, UiHelper::PLAYLIST_MENU);
}

FileOps::~FileOps()
{}

void FileOps::execAction()
{
    QAction *action = qobject_cast<QAction *>(sender());
    QVariantMap data = action->data().toMap();

    int type = data["action"].toInt();
    QString pattern = data["pattern"].toString();
    QString destination = data["destination"].toString();
    QString command = data["command"].toString();

    MetaDataFormatter formatter(type == EXECUTE ? command : pattern);

    PlayListModel *model = MediaPlayer::instance()->playListManager()->selectedPlayList();
    const QList<PlayListTrack*> tracks = model->selectedTracks();

    switch (type)
    {
    case COPY:
    {
        qDebug("FileOps: copy");
        if(!QDir(destination).exists ())
        {
            QMessageBox::critical (qApp->activeWindow (), tr("Error"),
                                   tr("Destination directory doesn't exist"));
            break;
        }
        copy(tracks, destination, &formatter);
        break;
    }
    case RENAME:
    {
        qDebug("FileOps: rename");
        rename(tracks, &formatter, model);
        break;
    }
    case REMOVE:
    {
        qDebug("FileOps: remove");
        if(QMessageBox::question (qApp->activeWindow (), tr("Remove Files"),
                                   tr("Are you sure you want to remove %n file(s) from disk?",
                                      "",tracks.size()),
                                   QMessageBox::Yes | QMessageBox::No) !=  QMessageBox::Yes)
            break;

        if(PlayListManager::instance()->selectedPlayList() != model)
            break;

        for(PlayListTrack *track : qAsConst(tracks))
        {
            if(PlayListManager::instance()->selectedPlayList() != model)
                break;

            if(isValid(track) && QFile::exists(track->path()) && QFile::remove(track->path()))
                model->removeTrack(track);
        }
        break;
    }
    case MOVE:
    {
        qDebug("FileOps: move");
        if(!QDir(destination).exists ())
        {
            QMessageBox::critical (qApp->activeWindow (), tr("Error"),
                                   tr("Destination directory doesn't exist"));
            break;
        }
        if(QMessageBox::question (qApp->activeWindow (), tr("Move Files"),
                                   tr("Are you sure you want to move %n file(s)?",
                                      "",tracks.size()),
                                   QMessageBox::Yes | QMessageBox::No) !=  QMessageBox::Yes)
        {
            break;
        }
        move(tracks, destination, &formatter, model);
        break;
    }
    case EXECUTE:
    {
        qDebug("FileOps: execute");
        execute(tracks, &formatter, model);
        break;
    }
    }
}

void FileOps::copy(const QList<PlayListTrack *> &tracks, const QString &dest, const MetaDataFormatter *formatter)
{
    QProgressDialog progress(qApp->activeWindow ());
    progress.setWindowModality(Qt::WindowModal);
    progress.setWindowTitle(tr("Copying"));
    progress.setCancelButtonText(tr("Stop"));
    progress.show();
    progress.setAutoClose(false);
    int i  = 0;
    for(PlayListTrack *track : qAsConst(tracks))
    {
        if(!isValid(track) || !QFile::exists(track->path()))
            continue;

        QString fileName = formatter->format(track); //generate file name

        QString ext = QString(".") + track->path().section(".", -1).toLower();
        if(!ext.isEmpty() && !fileName.endsWith(ext, Qt::CaseInsensitive))
            fileName += ext; //append extension

        //create destination path
        QString path = dest + "/" + fileName;
        QDir dir = QFileInfo(path).dir();
        if(!dir.exists())
        {
            if(!dir.mkpath(dir.absolutePath()))
            {
                qWarning("FileOps: unable to create directory");
                continue;
            }
        }
        if(track->path() == path)
            continue;

        //copy file
        QFile in(track->path());
        QFile out(path);
        if(!in.open(QIODevice::ReadOnly))
        {
            qWarning("FileOps: %s", qPrintable(in.errorString ()));
            continue;
        }
        if(!out.open(QIODevice::WriteOnly))
        {
            qWarning("FileOps: %s", qPrintable(out.errorString ()));
            continue;
        }

        progress.setMaximum(int(in.size() / COPY_BLOCK_SIZE));
        progress.setValue(0);
        progress.setLabelText (QString(tr("Copying file %1/%2")).arg(++i).arg(tracks.size()));
        progress.update();

        while (!in.atEnd ())
        {
            out.write(in.read(COPY_BLOCK_SIZE));
            progress.setValue(int(out.size() / COPY_BLOCK_SIZE));
            qApp->processEvents();
        }
        if(progress.wasCanceled ())
            break;
    }
    progress.close();
}

void FileOps::rename(const QList<PlayListTrack *> &tracks, const MetaDataFormatter *formatter, PlayListModel *model)
{
    for(PlayListTrack *track : qAsConst(tracks))
    {
        if(!isValid(track) || !QFile::exists(track->path())) //is it file?
            continue;

        if(PlayListManager::instance()->selectedPlayList() != model)
            break;

        QString fileName = formatter->format(track); //generate file name

        QString ext = QString(".") + track->path().section(".", -1).toLower();
        if(!ext.isEmpty() && !fileName.endsWith(ext, Qt::CaseInsensitive))
            fileName += ext; //append extension
        //rename file
        QFile file(track->path());
        QString dest = QFileInfo(track->path()).absolutePath ();
        if(isValid(track) && file.rename(dest + "/" + fileName) && isValid(track))
        {
            track->setPath(dest + "/" + fileName);
            track->updateMetaData();
            model->doCurrentVisibleRequest();
        }
        else
            continue;
    }
}

void FileOps::move(const QList<PlayListTrack *> &tracks, const QString &dest, const MetaDataFormatter *formatter, PlayListModel *model)
{
    QProgressDialog progress(qApp->activeWindow ());
    progress.setWindowModality(Qt::WindowModal);
    progress.setWindowTitle(tr("Moving"));
    progress.setCancelButtonText(tr("Stop"));
    progress.show();
    progress.setAutoClose (false);
    int i  = 0;
    for(PlayListTrack *track : qAsConst(tracks))
    {
        if(!isValid(track) || !QFile::exists(track->path()))
            continue;

        if(PlayListManager::instance()->selectedPlayList() != model)
            break;

        QString fileName = formatter->format(track); //generate file name

        QString ext = QString(".") + track->path().section(".", -1).toLower();
        if(!ext.isEmpty() && !fileName.endsWith(ext, Qt::CaseInsensitive))
            fileName += ext;  //append extension
        //create destination path
        QString path = dest + "/" + fileName;
        //skip moved files
        if(path == track->path())
            continue;

        QDir dir = QFileInfo(path).dir();
        if(!dir.exists())
        {
            if(!dir.mkpath(dir.absolutePath()))
            {
                qWarning("FileOps: unable to create directory");
                continue;
            }
        }

        progress.setRange(0, 100);
        progress.setValue(0);
        progress.setLabelText (QString(tr("Moving file %1/%2")).arg(++i).arg(tracks.size()));
        progress.update();
        if(!isValid(track) || PlayListManager::instance()->selectedPlayList() != model)
        {
            progress.setValue(100);
            continue;
        }

        //try to rename file first
        if(QFile::rename(track->path(), path))
        {
            progress.setValue(100);
            track->setPath(path);
            model->doCurrentVisibleRequest();
            continue;
        }
        //copy file
        QFile in(track->path());
        QFile out(path);
        if(!in.open(QIODevice::ReadOnly))
        {
            qWarning("FileOps: %s", qPrintable(in.errorString ()));
            continue;
        }
        if(!out.open(QIODevice::WriteOnly))
        {
            qWarning("FileOps: %s", qPrintable(out.errorString ()));
            continue;
        }

        progress.setMaximum(int(in.size() / COPY_BLOCK_SIZE));
        progress.setValue(0);
        progress.update();

        while (!in.atEnd ())
        {
            progress.wasCanceled ();
            out.write(in.read(COPY_BLOCK_SIZE));
            progress.setValue(int(out.size() / COPY_BLOCK_SIZE));
            qApp->processEvents();
        }

        in.close();

        if(!isValid(track) || PlayListManager::instance()->selectedPlayList() != model)
            continue;

        if(!QFile::remove(track->path()))
            qWarning("FileOps: unable to remove file '%s'", qPrintable(track->path()));

        track->setPath(path);
        model->doCurrentVisibleRequest();

        if(progress.wasCanceled())
            break;
    }
    progress.close();
}

void FileOps::execute(const QList<PlayListTrack *> &tracks, const MetaDataFormatter *formatter, PlayListModel *model)
{
    for(PlayListTrack *track : qAsConst(tracks))
    {
        if(!isValid(track) || !QFile::exists(track->path())) //is it file?
            continue;

        if(PlayListManager::instance()->selectedPlayList() != model)
            break;

        QString command = formatter->format(track); //generate file name
        qDebug("FileOps: exec command: %s", qPrintable(command));

#ifdef Q_OS_WIN
        QProcess::startDetached(QString("cmd.exe /C %1").arg(command));
#else
        QStringList args = { "-c", command };
        QProcess::startDetached("sh", args);
#endif
    }
}

bool FileOps::isValid(PlayListTrack *track) const
{
    const QList<PlayListTrack*> tracks = PlayListManager::instance()->selectedPlayList()->selectedTracks();
    return tracks.contains(track);
}