File indexing completed on 2023-05-30 11:30:48

0001 /**
0002  * Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org>
0003  *
0004  * This program is free software; you can redistribute it and/or modify it under
0005  * the terms of the GNU General Public License as published by the Free Software
0006  * Foundation; either version 2 of the License, or (at your option) any later
0007  * version.
0008  *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.
0012  *
0013  * You should have received a copy of the GNU General Public License along with
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.
0015  */
0016 
0017 #include "mediafiles.h"
0018 
0019 #include <KLocalizedString>
0020 #include <KIO/StatJob>
0021 #include <KJobWidgets>
0022 
0023 #include <QWidget>
0024 #include <QFile>
0025 #include <QUrl>
0026 #include <QFileDialog>
0027 #include <QStandardPaths>
0028 #include <QMimeType>
0029 #include <QMimeDatabase>
0030 
0031 #include <taglib.h>
0032 #include <taglib_config.h>
0033 #include <tag.h>
0034 #include <mpegfile.h>
0035 #include <vorbisfile.h>
0036 #include <flacfile.h>
0037 #include <xiphcomment.h>
0038 #include <oggflacfile.h>
0039 #include <mpcfile.h>
0040 #include <opusfile.h>
0041 #include <asffile.h>
0042 #include <mp4file.h>
0043 
0044 #include "juk_debug.h"
0045 
0046 namespace MediaFiles {
0047     static QStringList savedMimeTypes;
0048 
0049     static const char mp3Type[]  = "audio/mpeg";
0050     static const char oggType[]  = "audio/ogg";
0051     static const char flacType[] = "audio/x-flac";
0052     static const char mpcType[]  = "audio/x-musepack";
0053     static const char m3uType[]  = "audio/x-mpegurl";
0054 
0055     static const char vorbisType[]  = "audio/x-vorbis+ogg";
0056     static const char oggflacType[] = "audio/x-flac+ogg";
0057     static const char oggopusType[] = "audio/x-opus+ogg";
0058 
0059     static const char asfType[] = "video/x-ms-asf";
0060 
0061     static const char mp4Type[] = "audio/mp4";
0062     static const char mp4AudiobookType[] = "audio/x-m4b";
0063 
0064     static const char *const mediaTypes[] = {
0065         mp3Type, oggType, flacType, mpcType, vorbisType, oggflacType
0066         ,asfType
0067         ,mp4Type
0068         ,mp4AudiobookType
0069         ,oggopusType
0070     };
0071 
0072     static const char playlistExtension[] = ".m3u";
0073 }
0074 
0075 #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
0076 
0077 static QString getMusicDir()
0078 {
0079     const auto musicLocation =
0080         QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
0081 
0082     QDir musicDir(musicLocation);
0083     if (Q_UNLIKELY(
0084         !musicDir.exists() &&
0085         musicDir.isAbsolute() && // safety precaution here
0086         !musicDir.mkpath(musicLocation)))
0087     {
0088         qCWarning(JUK_LOG) << "Failed to create music dir:" << musicLocation;
0089     }
0090 
0091     return musicLocation;
0092 }
0093 
0094 QStringList MediaFiles::openDialog(QWidget *parent)
0095 {
0096     QFileDialog dialog(parent);
0097 
0098     dialog.setFileMode(QFileDialog::ExistingFiles);
0099     dialog.setMimeTypeFilters(mimeTypes());
0100     // limit to only file:// for now
0101     dialog.setSupportedSchemes(QStringList() << QStringLiteral("file"));
0102     dialog.setDirectory(getMusicDir());
0103     dialog.setWindowTitle(i18nc("open audio file", "Open"));
0104 
0105     if(dialog.exec()) {
0106         return dialog.selectedFiles();
0107     }
0108 
0109     return QStringList();
0110 }
0111 
0112 QString MediaFiles::savePlaylistDialog(const QString &playlistName, QWidget *parent)
0113 {
0114     QString fileName = QFileDialog::getSaveFileName(
0115         parent,
0116         i18n("Save Playlist") + QStringLiteral(" ") + playlistName,
0117         getMusicDir(),
0118         i18nc("For save dialog, %1 is always .m3u", "Playlists (*%1)", playlistExtension)
0119     );
0120     return fileName;
0121 }
0122 
0123 TagLib::File *MediaFiles::fileFactoryByType(const QString &fileName)
0124 {
0125     QMimeDatabase db;
0126     QMimeType result = db.mimeTypeForFile(fileName);
0127     if(!result.isValid())
0128         return nullptr;
0129 
0130     TagLib::File *file(nullptr);
0131     QByteArray encodedFileName(QFile::encodeName(fileName));
0132 
0133     if(result.inherits(QLatin1String(mp3Type)))
0134         file = new TagLib::MPEG::File(encodedFileName.constData());
0135     else if(result.inherits(QLatin1String(flacType)))
0136         file = new TagLib::FLAC::File(encodedFileName.constData());
0137     else if(result.inherits(QLatin1String(vorbisType)))
0138         file = new TagLib::Vorbis::File(encodedFileName.constData());
0139     else if(result.inherits(QLatin1String(asfType)))
0140         file = new TagLib::ASF::File(encodedFileName.constData());
0141     else if(result.inherits(QLatin1String(mp4Type)) || result.inherits(QLatin1String(mp4AudiobookType)))
0142         file = new TagLib::MP4::File(encodedFileName.constData());
0143     else if(result.inherits(QLatin1String(mpcType)))
0144         file = new TagLib::MPC::File(encodedFileName.constData());
0145     else if(result.inherits(QLatin1String(oggflacType)))
0146         file = new TagLib::Ogg::FLAC::File(encodedFileName.constData());
0147     else if(result.inherits(QLatin1String(oggopusType)) ||
0148             (result.inherits(QLatin1String(oggType)) && fileName.endsWith(QLatin1String(".opus")))
0149             )
0150     {
0151         file = new TagLib::Ogg::Opus::File(encodedFileName.constData());
0152     }
0153 
0154     return file;
0155 }
0156 
0157 bool MediaFiles::isMediaFile(const QString &fileName)
0158 {
0159     QMimeDatabase db;
0160     QMimeType result = db.mimeTypeForFile(fileName);
0161     if(!result.isValid())
0162         return false;
0163 
0164     // Search through our table of media types for a match
0165     const auto validMimeTypes(mimeTypes());
0166     for(const auto &mimeType : validMimeTypes) {
0167         if(result.inherits(mimeType))
0168             return true;
0169     }
0170 
0171     return false;
0172 }
0173 
0174 static bool isFileOfMimeType(const QString &fileName, const QString &mimeType)
0175 {
0176     QMimeDatabase db;
0177     QMimeType result = db.mimeTypeForFile(fileName);
0178     return result.isValid() && result.inherits(mimeType);
0179 }
0180 
0181 bool MediaFiles::isPlaylistFile(const QString &fileName)
0182 {
0183     return isFileOfMimeType(fileName, m3uType);
0184 }
0185 
0186 bool MediaFiles::isMP3(const QString &fileName)
0187 {
0188     return isFileOfMimeType(fileName, mp3Type);
0189 }
0190 
0191 bool MediaFiles::isOgg(const QString &fileName)
0192 {
0193     return isFileOfMimeType(fileName, oggType);
0194 }
0195 
0196 bool MediaFiles::isFLAC(const QString &fileName)
0197 {
0198     return isFileOfMimeType(fileName, flacType);
0199 }
0200 
0201 bool MediaFiles::isMPC(const QString &fileName)
0202 {
0203     return isFileOfMimeType(fileName, mpcType);
0204 }
0205 
0206 bool MediaFiles::isVorbis(const QString &fileName)
0207 {
0208     return isFileOfMimeType(fileName, vorbisType);
0209 }
0210 
0211 bool MediaFiles::isASF(const QString &fileName)
0212 {
0213     return isFileOfMimeType(fileName, asfType);
0214 }
0215 
0216 bool MediaFiles::isMP4(const QString &fileName)
0217 {
0218     return isFileOfMimeType(fileName, mp4Type) || isFileOfMimeType(fileName, mp4AudiobookType);
0219 }
0220 
0221 bool MediaFiles::isOggFLAC(const QString &fileName)
0222 {
0223     return isFileOfMimeType(fileName, oggflacType);
0224 }
0225 
0226 QStringList MediaFiles::mimeTypes()
0227 {
0228     if(!savedMimeTypes.isEmpty())
0229         return savedMimeTypes;
0230 
0231     for(unsigned i = 0; i < ARRAY_SIZE(mediaTypes); ++i) {
0232         savedMimeTypes << QLatin1String(mediaTypes[i]);
0233     }
0234 
0235     return savedMimeTypes;
0236 }
0237 
0238 QStringList MediaFiles::convertURLsToLocal(const QList<QUrl> &urlList, QWidget *w)
0239 {
0240     QStringList result;
0241     QUrl localUrl;
0242 
0243     for(const auto &url : urlList) {
0244         auto localizerJob = KIO::mostLocalUrl(url);
0245         KJobWidgets::setWindow(localizerJob, w);
0246 
0247         if(localizerJob->exec() && (localUrl = localizerJob->mostLocalUrl()).isLocalFile())
0248             result.append(localUrl.path());
0249         else
0250             qCDebug(JUK_LOG) << url << " is not a local file, skipping.";
0251     }
0252 
0253     return result;
0254 }
0255 
0256 // vim: set et sw=4 tw=0 sta: