Made some audio info fields now mandatory, otherwise consider parsing failed
This commit is contained in:
@@ -37,18 +37,30 @@ namespace lms::audio::ffmpeg
|
|||||||
const auto containerInfo{ audioFile.getContainerInfo() };
|
const auto containerInfo{ audioFile.getContainerInfo() };
|
||||||
const auto bestStreamInfo{ audioFile.getBestStreamInfo() };
|
const auto bestStreamInfo{ audioFile.getBestStreamInfo() };
|
||||||
if (!bestStreamInfo)
|
if (!bestStreamInfo)
|
||||||
throw AudioFileParsingException{ audioFile.getPath(), "Cannot find best audio stream" };
|
throw AudioFileParsingException{ "Cannot find best audio stream" };
|
||||||
|
|
||||||
if (!containerInfo.container)
|
if (!containerInfo.container)
|
||||||
throw AudioFileParsingException{ audioFile.getPath(), "Unhandled container type '" + containerInfo.containerName + "'" };
|
throw AudioFileParsingException{ "Unhandled container type '" + containerInfo.containerName + "'" };
|
||||||
|
|
||||||
|
if (!bestStreamInfo->codec)
|
||||||
|
throw AudioFileParsingException{ "Unhandled codec type '" + bestStreamInfo->codecName + "'" };
|
||||||
|
|
||||||
|
if (!bestStreamInfo->bitrate || *bestStreamInfo->bitrate == 0)
|
||||||
|
throw AudioFileParsingException{ "Cannot determine bitrate" };
|
||||||
|
|
||||||
|
if (!bestStreamInfo->channelCount || *bestStreamInfo->channelCount == 0)
|
||||||
|
throw AudioFileParsingException{ "Cannot determine channel count" };
|
||||||
|
|
||||||
|
if (!bestStreamInfo->sampleRate || *bestStreamInfo->sampleRate == 0)
|
||||||
|
throw AudioFileParsingException{ "Cannot determine sample rate" };
|
||||||
|
|
||||||
audioProperties.container = *containerInfo.container;
|
audioProperties.container = *containerInfo.container;
|
||||||
audioProperties.duration = containerInfo.duration;
|
audioProperties.duration = containerInfo.duration;
|
||||||
audioProperties.codec = bestStreamInfo->codec;
|
audioProperties.codec = *bestStreamInfo->codec;
|
||||||
audioProperties.bitrate = bestStreamInfo->bitrate;
|
audioProperties.bitrate = *bestStreamInfo->bitrate;
|
||||||
|
audioProperties.channelCount = *bestStreamInfo->channelCount;
|
||||||
|
audioProperties.sampleRate = *bestStreamInfo->sampleRate;
|
||||||
audioProperties.bitsPerSample = bestStreamInfo->bitsPerSample;
|
audioProperties.bitsPerSample = bestStreamInfo->bitsPerSample;
|
||||||
audioProperties.channelCount = bestStreamInfo->channelCount;
|
|
||||||
audioProperties.sampleRate = bestStreamInfo->sampleRate;
|
|
||||||
|
|
||||||
return audioProperties;
|
return audioProperties;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,9 +60,20 @@ namespace lms::audio::taglib
|
|||||||
|
|
||||||
// Common properties
|
// Common properties
|
||||||
audioProperties.bitrate = static_cast<std::size_t>(properties.bitrate() * 1000);
|
audioProperties.bitrate = static_cast<std::size_t>(properties.bitrate() * 1000);
|
||||||
|
if (audioProperties.bitrate == 0)
|
||||||
|
throw AudioFileParsingException{ "Cannot determine bitrate" };
|
||||||
|
|
||||||
audioProperties.channelCount = static_cast<std::size_t>(properties.channels());
|
audioProperties.channelCount = static_cast<std::size_t>(properties.channels());
|
||||||
|
if (audioProperties.channelCount == 0)
|
||||||
|
throw AudioFileParsingException{ "Cannot determine channel count" };
|
||||||
|
|
||||||
audioProperties.duration = std::chrono::milliseconds{ properties.lengthInMilliseconds() };
|
audioProperties.duration = std::chrono::milliseconds{ properties.lengthInMilliseconds() };
|
||||||
|
if (audioProperties.duration == decltype(audioProperties.duration)::zero())
|
||||||
|
throw AudioFileParsingException{ "Cannot determine duration" };
|
||||||
|
|
||||||
audioProperties.sampleRate = static_cast<std::size_t>(properties.sampleRate());
|
audioProperties.sampleRate = static_cast<std::size_t>(properties.sampleRate());
|
||||||
|
if (audioProperties.sampleRate == 0)
|
||||||
|
throw AudioFileParsingException{ "Cannot determine sample rate" };
|
||||||
|
|
||||||
// Guess container from the file type
|
// Guess container from the file type
|
||||||
if (const auto* apeFile{ dynamic_cast<const ::TagLib::APE::File*>(&file) })
|
if (const auto* apeFile{ dynamic_cast<const ::TagLib::APE::File*>(&file) })
|
||||||
@@ -90,8 +101,7 @@ namespace lms::audio::taglib
|
|||||||
audioProperties.codec = CodecType::WMA9Pro;
|
audioProperties.codec = CodecType::WMA9Pro;
|
||||||
break;
|
break;
|
||||||
case ::TagLib::ASF::Properties::Codec::Unknown:
|
case ::TagLib::ASF::Properties::Codec::Unknown:
|
||||||
audioProperties.codec = std::nullopt;
|
throw AudioFileParsingException{ "Unhandled ASF codec type" };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
audioProperties.bitsPerSample = asfFile->audioProperties()->bitsPerSample();
|
audioProperties.bitsPerSample = asfFile->audioProperties()->bitsPerSample();
|
||||||
@@ -122,8 +132,7 @@ namespace lms::audio::taglib
|
|||||||
audioProperties.codec = CodecType::ALAC;
|
audioProperties.codec = CodecType::ALAC;
|
||||||
break;
|
break;
|
||||||
case ::TagLib::MP4::Properties::Codec::Unknown:
|
case ::TagLib::MP4::Properties::Codec::Unknown:
|
||||||
audioProperties.codec = std::nullopt;
|
throw AudioFileParsingException{ "Unhandled MP4 codec type" };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
audioProperties.bitsPerSample = mp4File->audioProperties()->bitsPerSample();
|
audioProperties.bitsPerSample = mp4File->audioProperties()->bitsPerSample();
|
||||||
@@ -195,6 +204,13 @@ namespace lms::audio::taglib
|
|||||||
audioProperties.codec = CodecType::WavPack;
|
audioProperties.codec = CodecType::WavPack;
|
||||||
audioProperties.bitsPerSample = wavPackFile->audioProperties()->bitsPerSample();
|
audioProperties.bitsPerSample = wavPackFile->audioProperties()->bitsPerSample();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw AudioFileParsingException{ "Unhandled file type" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioProperties.bitsPerSample && *audioProperties.bitsPerSample == 0)
|
||||||
|
audioProperties.bitsPerSample.reset();
|
||||||
|
|
||||||
return audioProperties;
|
return audioProperties;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,16 +229,10 @@ namespace lms::audio::taglib::utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
throw AudioFileParsingException{ "Parsing failed" };
|
||||||
LMS_LOG(METADATA, ERROR, "File " << p << ": parsing failed");
|
|
||||||
throw AudioFileParsingException{ p, "Parsing failed" };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file->audioProperties())
|
if (!file->audioProperties())
|
||||||
{
|
throw AudioFileParsingException{ "No audio properties" };
|
||||||
LMS_LOG(METADATA, ERROR, "File " << p << ": no audio properties");
|
|
||||||
throw AudioFileNoAudioPropertiesException{ p };
|
|
||||||
}
|
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,12 +74,12 @@ namespace lms::audio
|
|||||||
|
|
||||||
struct AudioProperties
|
struct AudioProperties
|
||||||
{
|
{
|
||||||
std::optional<ContainerType> container;
|
ContainerType container;
|
||||||
std::optional<CodecType> codec;
|
CodecType codec;
|
||||||
std::chrono::milliseconds duration{};
|
std::chrono::milliseconds duration;
|
||||||
std::optional<unsigned> bitrate;
|
unsigned bitrate;
|
||||||
|
unsigned channelCount;
|
||||||
|
unsigned sampleRate;
|
||||||
std::optional<unsigned> bitsPerSample;
|
std::optional<unsigned> bitsPerSample;
|
||||||
std::optional<unsigned> channelCount;
|
|
||||||
std::optional<unsigned> sampleRate;
|
|
||||||
};
|
};
|
||||||
} // namespace lms::audio
|
} // namespace lms::audio
|
||||||
@@ -43,19 +43,7 @@ namespace lms::audio
|
|||||||
class AudioFileParsingException : public Exception
|
class AudioFileParsingException : public Exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioFileParsingException(const std::filesystem::path& path, std::string_view error = "")
|
using Exception::Exception;
|
||||||
: Exception{ error }, _path{ path } {}
|
|
||||||
|
|
||||||
const std::filesystem::path& getPath() const { return _path; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::filesystem::path _path;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AudioFileNoAudioPropertiesException : public AudioFileParsingException
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using AudioFileParsingException::AudioFileParsingException;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class IOException : public Exception
|
class IOException : public Exception
|
||||||
|
|||||||
@@ -539,10 +539,6 @@ namespace lms::scanner
|
|||||||
index++;
|
index++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (const audio::AudioFileNoAudioPropertiesException&)
|
|
||||||
{
|
|
||||||
addError<NoAudioTrackFoundError>(getFilePath());
|
|
||||||
}
|
|
||||||
catch (const audio::IOException& e)
|
catch (const audio::IOException& e)
|
||||||
{
|
{
|
||||||
addError<IOScanError>(getFilePath(), e.getErrorCode());
|
addError<IOScanError>(getFilePath(), e.getErrorCode());
|
||||||
@@ -672,11 +668,11 @@ namespace lms::scanner
|
|||||||
track.modify()->setScanVersion(getScannerSettings().audioScanVersion);
|
track.modify()->setScanVersion(getScannerSettings().audioScanVersion);
|
||||||
|
|
||||||
// Audio properties
|
// Audio properties
|
||||||
track.modify()->setBitrate(_file->audioProperties.bitrate ? *_file->audioProperties.bitrate : 0);
|
track.modify()->setBitrate(_file->audioProperties.bitrate);
|
||||||
track.modify()->setBitsPerSample(_file->audioProperties.bitsPerSample ? *_file->audioProperties.bitsPerSample : 0);
|
track.modify()->setBitsPerSample(_file->audioProperties.bitsPerSample ? *_file->audioProperties.bitsPerSample : 0);
|
||||||
track.modify()->setChannelCount(_file->audioProperties.channelCount ? *_file->audioProperties.channelCount : 0);
|
track.modify()->setChannelCount(_file->audioProperties.channelCount);
|
||||||
track.modify()->setDuration(_file->audioProperties.duration);
|
track.modify()->setDuration(_file->audioProperties.duration);
|
||||||
track.modify()->setSampleRate(_file->audioProperties.sampleRate ? *_file->audioProperties.sampleRate : 0);
|
track.modify()->setSampleRate(_file->audioProperties.sampleRate);
|
||||||
|
|
||||||
track.modify()->setFileSize(getFileSize());
|
track.modify()->setFileSize(getFileSize());
|
||||||
track.modify()->setLastWriteTime(getLastWriteTime());
|
track.modify()->setLastWriteTime(getLastWriteTime());
|
||||||
|
|||||||
@@ -120,10 +120,7 @@ namespace lms::api::subsonic
|
|||||||
{
|
{
|
||||||
const auto audioFile{ audio::parseAudioFile(trackPath) };
|
const auto audioFile{ audio::parseAudioFile(trackPath) };
|
||||||
|
|
||||||
if (!audioFile->getAudioProperties().codec)
|
return isCodecCompatibleWithOutputFormat(audioFile->getAudioProperties().codec, outputFormat);
|
||||||
throw RequestedDataNotFoundError{}; // TODO 404?
|
|
||||||
|
|
||||||
return isCodecCompatibleWithOutputFormat(*audioFile->getAudioProperties().codec, outputFormat);
|
|
||||||
}
|
}
|
||||||
catch (const audio::Exception& e)
|
catch (const audio::Exception& e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -147,12 +147,9 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
if (const auto audioFile{ audio::parseAudioFile(track->getAbsoluteFilePath()) })
|
if (const auto audioFile{ audio::parseAudioFile(track->getAbsoluteFilePath()) })
|
||||||
{
|
{
|
||||||
if (audioFile->getAudioProperties().codec)
|
releaseInfo->setCondition("if-has-codec", true);
|
||||||
{
|
releaseInfo->bindString("codec", audio::codecTypeToString(audioFile->getAudioProperties().codec).c_str(), Wt::TextFormat::Plain);
|
||||||
releaseInfo->setCondition("if-has-codec", true);
|
break;
|
||||||
releaseInfo->bindString("codec", audio::codecTypeToString(*audioFile->getAudioProperties().codec).c_str(), Wt::TextFormat::Plain);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const audio::Exception& e)
|
catch (const audio::Exception& e)
|
||||||
|
|||||||
@@ -133,11 +133,8 @@ namespace lms::ui::TrackListHelpers
|
|||||||
{
|
{
|
||||||
if (const auto audioFile{ audio::parseAudioFile(track->getAbsoluteFilePath()) })
|
if (const auto audioFile{ audio::parseAudioFile(track->getAbsoluteFilePath()) })
|
||||||
{
|
{
|
||||||
if (audioFile->getAudioProperties().codec)
|
trackInfo->setCondition("if-has-codec", true);
|
||||||
{
|
trackInfo->bindString("codec", audio::codecTypeToString(audioFile->getAudioProperties().codec).c_str(), Wt::TextFormat::Plain);
|
||||||
trackInfo->setCondition("if-has-codec", true);
|
|
||||||
trackInfo->bindString("codec", audio::codecTypeToString(*audioFile->getAudioProperties().codec).c_str(), Wt::TextFormat::Plain);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const audio::Exception& e)
|
catch (const audio::Exception& e)
|
||||||
|
|||||||
@@ -39,19 +39,13 @@ namespace lms::audio
|
|||||||
std::ostream& operator<<(std::ostream& os, const AudioProperties& audioProperties)
|
std::ostream& operator<<(std::ostream& os, const AudioProperties& audioProperties)
|
||||||
{
|
{
|
||||||
os << "\tDuration: " << std::fixed << std::setprecision(2) << std::chrono::duration_cast<std::chrono::duration<float>>(audioProperties.duration) << std::endl;
|
os << "\tDuration: " << std::fixed << std::setprecision(2) << std::chrono::duration_cast<std::chrono::duration<float>>(audioProperties.duration) << std::endl;
|
||||||
|
os << "\tContainer: " << containerTypeToString(audioProperties.container) << std::endl;
|
||||||
if (audioProperties.container)
|
os << "\tCodec: " << codecTypeToString(audioProperties.codec) << std::endl;
|
||||||
os << "\tContainer: " << containerTypeToString(*audioProperties.container) << std::endl;
|
os << "\tBitrate: " << audioProperties.bitrate << " bps" << std::endl;
|
||||||
if (audioProperties.codec)
|
|
||||||
os << "\tCodec: " << codecTypeToString(*audioProperties.codec) << std::endl;
|
|
||||||
if (audioProperties.bitrate)
|
|
||||||
os << "\tBitrate: " << *audioProperties.bitrate << " bps" << std::endl;
|
|
||||||
if (audioProperties.bitsPerSample)
|
if (audioProperties.bitsPerSample)
|
||||||
os << "\tBitsPerSample: " << *audioProperties.bitsPerSample << std::endl;
|
os << "\tBitsPerSample: " << *audioProperties.bitsPerSample << std::endl;
|
||||||
if (audioProperties.channelCount)
|
os << "\tChannelCount: " << audioProperties.channelCount << std::endl;
|
||||||
os << "\tChannelCount: " << *audioProperties.channelCount << std::endl;
|
os << "\tSampleRate: " << audioProperties.sampleRate << std::endl;
|
||||||
if (audioProperties.sampleRate)
|
|
||||||
os << "\tSampleRate: " << *audioProperties.sampleRate << std::endl;
|
|
||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user