Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lp1445885: Detect MIME type and deduce expected file extension and type #4356

Merged
merged 1 commit into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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