Skip to content

Commit

Permalink
Merge pull request #4356 from uklotzde/soundsource-mimetype
Browse files Browse the repository at this point in the history
lp1445885: Detect MIME type and deduce expected file extension and type
  • Loading branch information
Holzhaus authored Oct 5, 2021
2 parents e086cdd + 7b70a59 commit b237b50
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 21 deletions.
49 changes: 45 additions & 4 deletions src/sources/soundsource.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "sources/soundsource.h"

#include <QMimeDatabase>
#include <QMimeType>

#include "util/logger.h"

namespace mixxx {
Expand All @@ -8,7 +11,7 @@ namespace {

const Logger kLogger("SoundSource");

inline QUrl validateUrl(QUrl url) {
inline QUrl validateLocalFileUrl(QUrl url) {
DEBUG_ASSERT(url.isValid());
VERIFY_OR_DEBUG_ASSERT(url.isLocalFile()) {
kLogger.warning()
Expand All @@ -20,12 +23,50 @@ inline QUrl validateUrl(QUrl url) {

} // anonymous namespace

/*static*/ QString SoundSource::getFileExtensionFromUrl(const QUrl& url) {
return validateUrl(url).toString().section(".", -1).toLower().trimmed();
//static
QString SoundSource::getTypeFromUrl(const QUrl& url) {
const QString filePath = validateLocalFileUrl(url).toLocalFile();
return getTypeFromFile(filePath);
}

//static
QString SoundSource::getTypeFromFile(const QFileInfo& fileInfo) {
const QString fileSuffix = fileInfo.suffix();
DEBUG_ASSERT(!fileSuffix.isEmpty() || fileSuffix == QString{});
const QMimeType mimeType = QMimeDatabase().mimeTypeForFile(fileInfo);
if (!mimeType.isValid()) {
qWarning()
<< "Unknown MIME type for file" << fileInfo.filePath();
return fileSuffix;
}
const QString preferredSuffix = mimeType.preferredSuffix();
if (preferredSuffix.isEmpty()) {
DEBUG_ASSERT(mimeType.suffixes().isEmpty());
qInfo()
<< "MIME type" << mimeType
<< "has no preferred suffix";
return fileSuffix;
}
if (fileSuffix == preferredSuffix || mimeType.suffixes().contains(fileSuffix)) {
return fileSuffix;
}
if (fileSuffix.isEmpty()) {
qWarning()
<< "Using type" << preferredSuffix
<< "according to the detected MIME type" << mimeType
<< "of file" << fileInfo.filePath();
} else {
qWarning()
<< "Using type" << preferredSuffix
<< "instead of" << fileSuffix
<< "according to the detected MIME type" << mimeType
<< "of file" << fileInfo.filePath();
}
return preferredSuffix;
}

SoundSource::SoundSource(const QUrl& url, const QString& type)
: AudioSource(validateUrl(url)),
: AudioSource(validateLocalFileUrl(url)),
MetadataSourceTagLib(getLocalFileName()),
m_type(type) {
}
Expand Down
16 changes: 12 additions & 4 deletions src/sources/soundsource.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#pragma once

#include <QDebug>
#include <QFileInfo>

#include "sources/audiosource.h"
#include "sources/metadatasourcetaglib.h"

#include "util/assert.h"

namespace mixxx {
Expand All @@ -15,8 +14,17 @@ class SoundSource
: public AudioSource,
public MetadataSourceTagLib {
public:
static QString getFileExtensionFromUrl(const QUrl& url);
/// Determine the type from an URL.
///
/// Only local file URLs are supported.
static QString getTypeFromUrl(const QUrl& url);

/// Determine the type from a (local) file.
static QString getTypeFromFile(const QFileInfo& fileInfo);

/// The type of the source.
///
/// The type equals the preferred suffix of the content's MIME type.
QString getType() const {
return m_type;
}
Expand All @@ -25,7 +33,7 @@ class SoundSource
// If no type is provided the file extension of the file referred
// by the URL will be used as the type of the SoundSource.
explicit SoundSource(const QUrl& url)
: SoundSource(url, getFileExtensionFromUrl(url)) {
: SoundSource(url, getTypeFromUrl(url)) {
}
SoundSource(const QUrl& url, const QString& type);

Expand Down
16 changes: 8 additions & 8 deletions src/sources/soundsourcemodplug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ const QStringList kSupportedFileExtensions = {
constexpr SINT kChunkSizeInBytes = SINT(1) << 19;

QString getModPlugTypeFromUrl(const QUrl& url) {
const QString fileExtension(SoundSource::getFileExtensionFromUrl(url));
if (fileExtension == "mod") {
const QString fileType = SoundSource::getTypeFromUrl(url);
if (fileType == "mod") {
return "Protracker";
} else if (fileExtension == "med") {
} else if (fileType == "med") {
return "OctaMed";
} else if (fileExtension == "okt") {
} else if (fileType == "okt") {
return "Oktalyzer";
} else if (fileExtension == "s3m") {
} else if (fileType == "s3m") {
return "Scream Tracker 3";
} else if (fileExtension == "stm") {
} else if (fileType == "stm") {
return "Scream Tracker";
} else if (fileExtension == "xm") {
} else if (fileType == "xm") {
return "FastTracker2";
} else if (fileExtension == "it") {
} else if (fileType == "it") {
return "Impulse Tracker";
} else {
return "Module";
Expand Down
8 changes: 4 additions & 4 deletions src/sources/soundsourceproxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,17 +288,17 @@ SoundSourceProxy::allProviderRegistrationsForUrl(
// silently ignore empty URLs
return {};
}
const QString fileExtension =
mixxx::SoundSource::getFileExtensionFromUrl(url);
if (fileExtension.isEmpty()) {
const QString fileType =
mixxx::SoundSource::getTypeFromUrl(url);
if (fileType.isEmpty()) {
kLogger.warning()
<< "Unknown file type:"
<< url.toString();
return {};
}
const auto providerRegistrations =
allProviderRegistrationsForFileExtension(
fileExtension);
fileType);
if (providerRegistrations.isEmpty()) {
kLogger.warning()
<< "Unsupported file type:"
Expand Down
27 changes: 27 additions & 0 deletions src/test/soundproxy_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,3 +708,30 @@ TEST_F(SoundSourceProxyTest, regressionTestCachingReaderChunkJumpForward) {
}
}
}

TEST_F(SoundSourceProxyTest, getTypeFromFile) {
// Generate file names for the temporary file
const QString filePathWithoutSuffix =
mixxxtest::generateTemporaryFileName("file_with_no_file_suffix");
const QString filePathWithUnknownSuffix =
mixxxtest::generateTemporaryFileName("file_with.unknown_suffix");
const QString filePathWithWrongSuffix =
mixxxtest::generateTemporaryFileName("file_with_wrong_suffix.wav");

// Create the temporary files by copying an existing file
const QString validFilePath = kTestDir.absoluteFilePath(QStringLiteral("empty.mp3"));
mixxxtest::copyFile(validFilePath, filePathWithoutSuffix);
mixxxtest::copyFile(validFilePath, filePathWithUnknownSuffix);
mixxxtest::copyFile(validFilePath, filePathWithWrongSuffix);

ASSERT_STREQ(qPrintable("mp3"), qPrintable(mixxx::SoundSource::getTypeFromFile(validFilePath)));
EXPECT_STREQ(qPrintable("mp3"),
qPrintable(mixxx::SoundSource::getTypeFromFile(
filePathWithoutSuffix)));
EXPECT_STREQ(qPrintable("mp3"),
qPrintable(mixxx::SoundSource::getTypeFromFile(
filePathWithUnknownSuffix)));
EXPECT_STREQ(qPrintable("mp3"),
qPrintable(mixxx::SoundSource::getTypeFromFile(
filePathWithWrongSuffix)));
}
2 changes: 1 addition & 1 deletion src/track/serato/tags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace {

QString getPrimaryDecoderNameForFilePath(const QString& filePath) {
const QString fileExtension =
mixxx::SoundSource::getFileExtensionFromUrl(QUrl::fromLocalFile(filePath));
mixxx::SoundSource::getTypeFromFile(filePath);
const mixxx::SoundSourceProviderPointer pPrimaryProvider =
SoundSourceProxy::getPrimaryProviderForFileExtension(fileExtension);
if (pPrimaryProvider) {
Expand Down

0 comments on commit b237b50

Please sign in to comment.