File indexing completed on 2024-05-12 16:18:06

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2005 Max Howell <max.howell@methylblue.com>        *
0003  *             (C) 2003-2010 Mark Kretschmann <kretschmann@kde.org>        *
0004  *             (C) 2005-2007 Alexandre Oliveira <aleprj@gmail.com>         *
0005  *             (C) 2008 Dan Meltzer <parallelgrapefruit@gmail.com>         *
0006  *             (C) 2008-2009 Jeff Mitchell <mitchell@kde.org>              *
0007  *                                                                         *
0008  *   This program is free software; you can redistribute it and/or modify  *
0009  *   it under the terms of the GNU General Public License as published by  *
0010  *   the Free Software Foundation; either version 2 of the License, or     *
0011  *   (at your option) any later version.                                   *
0012  *                                                                         *
0013  *   This program is distributed in the hope that it will be useful,       *
0014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0016  *   GNU General Public License for more details.                          *
0017  *                                                                         *
0018  *   You should have received a copy of the GNU General Public License     *
0019  *   along with this program; if not, write to the                         *
0020  *   Free Software Foundation, Inc.,                                       *
0021  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0022  ***************************************************************************/
0023 
0024 #include "Directory.h"
0025 
0026 #include "collectionscanner/ScanningState.h"
0027 #include "collectionscanner/Track.h"
0028 #include "collectionscanner/utils.h"
0029 
0030 #include <QDebug>
0031 #include <QString>
0032 #include <QStringList>
0033 #include <QSettings>
0034 #include <QDir>
0035 #include <QFile>
0036 #include <QDateTime>
0037 #include <QFileInfo>
0038 
0039 #include <QXmlStreamReader>
0040 #include <QXmlStreamWriter>
0041 
0042 CollectionScanner::Directory::Directory( const QString &path,
0043                                          CollectionScanner::ScanningState *state,
0044                                          bool skip )
0045     : m_ignored( false )
0046 {
0047     m_path = path;
0048     m_rpath = QDir::current().relativeFilePath( path );
0049     m_mtime = QFileInfo( path ).lastModified().toSecsSinceEpoch();
0050     m_skipped = skip;
0051 
0052     if( m_skipped )
0053         return;
0054 
0055     QDir dir( path );
0056     if( dir.exists( QStringLiteral("fmps_ignore") ) )
0057     {
0058         m_ignored = true;
0059         return;
0060     }
0061 
0062     QStringList validImages;
0063     validImages << QStringLiteral("jpg") << QStringLiteral("png") << QStringLiteral("gif") << QStringLiteral("jpeg") << QStringLiteral("bmp") << QStringLiteral("svg") << QStringLiteral("xpm");
0064     QStringList validPlaylists;
0065     validPlaylists << QStringLiteral("m3u") << QStringLiteral("pls") << QStringLiteral("xspf");
0066 
0067     // --- check if we were restarted and failed at a file
0068     QStringList badFiles;
0069 
0070     if( state->lastDirectory() == path )
0071     {
0072         badFiles << state->badFiles();
0073         QString lastFile = state->lastFile();
0074         if( !lastFile.isEmpty() )
0075         {
0076             badFiles << state->lastFile();
0077             state->setBadFiles( badFiles );
0078         }
0079     }
0080     else
0081         state->setLastDirectory( path );
0082     state->setLastFile( QString() ); // reset so we don't add a leftover file
0083 
0084     dir.setFilter( QDir::NoDotAndDotDot | QDir::Files );
0085     QFileInfoList fileInfos = dir.entryInfoList();
0086 
0087     foreach( const QFileInfo &fi, fileInfos )
0088     {
0089         if( !fi.exists() )
0090             continue;
0091 
0092         const QFileInfo &f = fi.isSymLink() ? QFileInfo( fi.symLinkTarget() ) : fi;
0093 
0094         if( badFiles.contains( f.absoluteFilePath() ) )
0095             continue;
0096 
0097         const QString suffix  = fi.suffix().toLower();
0098         const QString filePath = f.absoluteFilePath();
0099 
0100         // -- cover image ?
0101         if( validImages.contains( suffix ) )
0102             m_covers.append( filePath );
0103 
0104         // -- playlist ?
0105         else if( validPlaylists.contains( suffix ) )
0106             m_playlists.append( CollectionScanner::Playlist( filePath ) );
0107 
0108         // -- audio track ?
0109         else
0110         {
0111             // remember the last file before it get's dangerous. Before starting taglib
0112             state->setLastFile( f.absoluteFilePath() );
0113 
0114             CollectionScanner::Track *newTrack = new CollectionScanner::Track( filePath, this );
0115             if( newTrack->isValid() )
0116                 m_tracks.append( newTrack );
0117             else
0118                 delete newTrack;
0119         }
0120     }
0121 }
0122 
0123 CollectionScanner::Directory::Directory( QXmlStreamReader *reader )
0124     : m_mtime( 0 )
0125     , m_skipped( false )
0126     , m_ignored( false )
0127 {
0128     // improve scanner with skipCurrentElement as soon as Amarok requires Qt 4.6
0129     while (!reader->atEnd()) {
0130         reader->readNext();
0131 
0132         if( reader->isStartElement() )
0133         {
0134             QStringRef name = reader->name();
0135             if( name == QLatin1String("path") )
0136                 m_path = reader->readElementText(QXmlStreamReader::SkipChildElements);
0137             else if( name == QLatin1String("rpath") )
0138                 m_rpath = reader->readElementText(QXmlStreamReader::SkipChildElements);
0139             else if( name == QLatin1String("mtime") )
0140                 m_mtime = reader->readElementText(QXmlStreamReader::SkipChildElements).toUInt();
0141             else if( name == QLatin1String("cover") )
0142                 m_covers.append(reader->readElementText(QXmlStreamReader::SkipChildElements));
0143             else if( name == QLatin1String("skipped") )
0144             {
0145                 m_skipped = true;
0146                 reader->skipCurrentElement();
0147             }
0148             else if( name == QLatin1String("ignored") )
0149             {
0150                 m_ignored = true;
0151                 reader->skipCurrentElement();
0152             }
0153             else if( name == QLatin1String("track") )
0154                 m_tracks.append( new CollectionScanner::Track( reader, this ) );
0155             else if( name == QLatin1String("playlist") )
0156                 m_playlists.append( CollectionScanner::Playlist( reader ) );
0157             else
0158             {
0159                 qDebug() << "Unexpected xml start element"<<name<<"in input";
0160                 reader->skipCurrentElement();
0161             }
0162         }
0163 
0164         else if( reader->isEndElement() )
0165         {
0166             break;
0167         }
0168     }
0169 }
0170 
0171 CollectionScanner::Directory::~Directory()
0172 {
0173     foreach( CollectionScanner::Track *track, m_tracks )
0174         delete track;
0175 }
0176 
0177 QString
0178 CollectionScanner::Directory::path() const
0179 {
0180     return m_path;
0181 }
0182 
0183 QString
0184 CollectionScanner::Directory::rpath() const
0185 {
0186     return m_rpath;
0187 }
0188 
0189 uint
0190 CollectionScanner::Directory::mtime() const
0191 {
0192     return m_mtime;
0193 }
0194 
0195 bool
0196 CollectionScanner::Directory::isSkipped() const
0197 {
0198     return m_skipped;
0199 }
0200 
0201 const QStringList&
0202 CollectionScanner::Directory::covers() const
0203 {
0204     return m_covers;
0205 }
0206 
0207 const QList<CollectionScanner::Track *>&
0208 CollectionScanner::Directory::tracks() const
0209 {
0210     return m_tracks;
0211 }
0212 
0213 const QList<CollectionScanner::Playlist>&
0214 CollectionScanner::Directory::playlists() const
0215 {
0216     return m_playlists;
0217 }
0218 
0219 void
0220 CollectionScanner::Directory::toXml( QXmlStreamWriter *writer ) const
0221 {
0222     writer->writeTextElement( QStringLiteral("path"), escapeXml10(m_path) );
0223     writer->writeTextElement( QStringLiteral("rpath"), escapeXml10(m_rpath) );
0224     writer->writeTextElement( QStringLiteral("mtime"), QString::number( m_mtime ) );
0225     if( m_skipped )
0226         writer->writeEmptyElement( QStringLiteral("skipped") );
0227     if( m_ignored )
0228         writer->writeEmptyElement( QStringLiteral("ignored") );
0229 
0230     foreach( const QString &cover, m_covers )
0231     {
0232         writer->writeTextElement( QStringLiteral("cover"), escapeXml10(cover) );
0233     }
0234     foreach( CollectionScanner::Track *track, m_tracks )
0235     {
0236         writer->writeStartElement( QStringLiteral("track") );
0237         track->toXml( writer );
0238         writer->writeEndElement();
0239     }
0240 
0241     foreach( const CollectionScanner::Playlist &playlist, m_playlists )
0242     {
0243         writer->writeStartElement( QStringLiteral("playlist") );
0244         playlist.toXml( writer );
0245         writer->writeEndElement();
0246     }
0247 }