tagparser/abstractattachment.cpp

208 lines
5.8 KiB
C++
Raw Normal View History

2015-09-06 19:57:33 +02:00
#include "./abstractattachment.h"
2015-04-22 19:22:01 +02:00
2015-09-06 19:57:33 +02:00
#include "./exceptions.h"
2018-03-07 01:17:50 +01:00
#include "./mediafileinfo.h"
#include "./progressfeedback.h"
2015-04-22 19:22:01 +02:00
2016-08-04 00:16:19 +02:00
#include <c++utilities/io/copy.h>
2015-10-06 22:30:05 +02:00
2017-02-05 21:02:40 +01:00
#include <memory>
2018-03-07 01:17:50 +01:00
#include <sstream>
2015-04-22 19:22:01 +02:00
using namespace std;
2019-06-10 22:49:11 +02:00
using namespace CppUtilities;
2015-04-22 19:22:01 +02:00
namespace TagParser {
2015-04-22 19:22:01 +02:00
/// \brief The AbstractAttachmentPrivate struct contains private fields of the AbstractAttachment class.
struct AbstractAttachmentPrivate {};
2015-04-22 19:22:01 +02:00
/*!
* \class TagParser::StreamDataBlock
2015-04-22 19:22:01 +02:00
* \brief The StreamDataBlock class is a reference to a certain data block of a stream.
*/
/*!
* \brief Constructs a new StreamDataBlock.
*
* The derived is responsible for the prober initialization of the object.
*/
2018-03-07 01:17:50 +01:00
StreamDataBlock::StreamDataBlock()
: m_stream(nullptr)
, m_startOffset(0)
, m_endOffset(0)
{
}
2015-04-22 19:22:01 +02:00
/*!
* \brief Constructs a new StreamDataBlock with the specified \a stream and offsets.
*
* The \a stream must be provided as function returning a reference the associated stream. This way of passing the stream
* allows the caller to change the stream without the need to update all StreamDataBlock objects
* referring to the stream. This is required when rewriting a file because during rewriting the original file
* gets renamed and then reopend with another stream object.
*
* The object does NOT take ownership over the stream returned by the specified function.
*/
2021-03-20 21:26:25 +01:00
StreamDataBlock::StreamDataBlock(const std::function<std::istream &()> &stream, std::uint64_t startOffset, std::ios_base::seekdir startDir,
std::uint64_t endOffset, std::ios_base::seekdir endDir)
2018-03-07 01:17:50 +01:00
: m_stream(stream)
2015-04-22 19:22:01 +02:00
{
auto &s = stream();
auto currentPos = s.tellg();
2021-03-20 21:26:25 +01:00
s.seekg(static_cast<std::istream::off_type>(startOffset), startDir);
m_startOffset = static_cast<std::uint64_t>(s.tellg());
s.seekg(static_cast<std::istream::off_type>(endOffset), endDir);
m_endOffset = static_cast<std::uint64_t>(s.tellg());
2015-04-22 19:22:01 +02:00
s.seekg(currentPos);
2018-03-07 01:17:50 +01:00
if (m_endOffset < m_startOffset) {
2019-03-13 19:06:42 +01:00
throw std::ios_base::failure("End offset is less than start offset.");
2015-04-22 19:22:01 +02:00
}
}
/*!
* \brief Discards buffered data.
*/
StreamDataBlock::~StreamDataBlock()
{
}
2015-11-26 14:22:44 +01:00
/*!
* \brief Buffers the data block. Buffered data can be accessed via buffer().
*/
void StreamDataBlock::makeBuffer() const
{
m_buffer = make_unique<char[]>(size());
2021-03-20 21:26:25 +01:00
stream().seekg(static_cast<std::istream::off_type>(startOffset()));
stream().read(m_buffer.get(), static_cast<std::streamsize>(size()));
2015-11-26 14:22:44 +01:00
}
2016-08-04 00:16:19 +02:00
/*!
* \brief Copies the data to the specified \a stream.
* \remarks Makes use of the buffer allocated with makeBuffer() if this method has been called before.
*/
void StreamDataBlock::copyTo(ostream &stream) const
{
2018-03-07 01:17:50 +01:00
if (buffer()) {
2021-03-20 21:26:25 +01:00
stream.write(buffer().get(), static_cast<std::streamsize>(size()));
2016-08-04 00:16:19 +02:00
} else {
CopyHelper<0x2000> copyHelper;
2021-03-20 21:26:25 +01:00
m_stream().seekg(static_cast<std::streamsize>(startOffset()));
2016-08-04 00:16:19 +02:00
copyHelper.copy(m_stream(), stream, size());
}
}
2015-04-22 19:22:01 +02:00
/*!
* \class TagParser::FileDataBlock
2015-04-22 19:22:01 +02:00
* \brief The FileDataBlock class is a reference to a certain data block of a file stream.
*/
/*!
* \brief Constructs a new FileDataBlock with the specified \a path.
*
* Opens a file stream with the specified \a path.
*
* \throws Throws ios_base::failure when an IO error occurs.
*/
FileDataBlock::FileDataBlock(std::string_view path, Diagnostics &diag, AbortableProgressFeedback &progress)
: m_fileInfo(make_unique<MediaFileInfo>())
2015-04-22 19:22:01 +02:00
{
m_fileInfo->setPath(path);
m_fileInfo->open(true);
m_fileInfo->parseContainerFormat(diag, progress);
2015-04-22 19:22:01 +02:00
m_startOffset = 0;
m_endOffset = m_fileInfo->size();
2018-03-07 01:17:50 +01:00
m_stream = [this]() -> std::istream & { return this->m_fileInfo->stream(); };
2015-04-22 19:22:01 +02:00
}
/*!
* \brief Destroys the FileDataBlock.
* \remarks This method is needed although it is empty. Otherwise the default d'tor would be
* inlined where FileDataBlock is used creating a dependency to MediaFileInfo which
* therefore couldn't be opaque anymore.
*/
FileDataBlock::~FileDataBlock()
{
}
2015-04-22 19:22:01 +02:00
/*!
* \class TagParser::AbstractAttachment
2015-04-22 19:22:01 +02:00
* \brief The AbstractAttachment class parses and stores attachment information.
*/
/*!
* \brief Constructs a new attachment.
*/
AbstractAttachment::AbstractAttachment()
: m_id(0)
, m_isDataFromFile(false)
, m_ignored(false)
{
}
/*!
* \brief Destroys the attachment.
*/
AbstractAttachment::~AbstractAttachment()
{
}
2015-04-22 19:22:01 +02:00
/*!
* \brief Returns a label for the track.
*/
string AbstractAttachment::label() const
{
stringstream ss;
ss << "ID: " << id();
2018-03-07 01:17:50 +01:00
if (!name().empty()) {
2015-04-22 19:22:01 +02:00
ss << ", name: \"" << name() << "\"";
}
2018-03-07 01:17:50 +01:00
if (!mimeType().empty()) {
2015-04-22 19:22:01 +02:00
ss << ", mime-type: \"" << mimeType() << "\"";
}
return ss.str();
}
/*!
* \brief Resets the object to its initial state.
*/
void AbstractAttachment::clear()
{
m_description.clear();
m_name.clear();
m_mimeType.clear();
m_id = 0;
m_data.reset();
}
/*!
* \brief Sets the data, name and MIME-type for the specified \a path.
*
* A stream for the file with the specified \a path is opened (read-only).
* This stream will be freed by the attachment if the other data is assigned
* or the attachment gets destroyed.
*
* \throws Throws std::ios_base::failure when an IO error occurs.
* \throws Throws TagParser::Failure or a derived class when a parsing
2015-04-22 19:22:01 +02:00
* error occurs.
*
* When such an exception is thrown, the attachment remains unchanged.
*/
void AbstractAttachment::setFile(string_view path, Diagnostics &diag, AbortableProgressFeedback &progress)
2015-04-22 19:22:01 +02:00
{
2015-10-06 22:30:05 +02:00
m_data.reset();
auto file = make_unique<FileDataBlock>(path, diag, progress);
2015-10-06 22:30:05 +02:00
const auto fileName = file->fileInfo()->fileName();
2018-03-07 01:17:50 +01:00
if (!fileName.empty()) {
2015-04-22 19:22:01 +02:00
m_name = fileName;
}
const auto mimeType = file->fileInfo()->mimeType();
if (!mimeType.empty()) {
2015-04-22 19:22:01 +02:00
m_mimeType = mimeType;
}
2023-02-20 19:54:42 +01:00
m_data = std::move(file);
2015-11-26 14:22:44 +01:00
m_isDataFromFile = true;
2015-04-22 19:22:01 +02:00
}
2018-03-07 01:17:50 +01:00
} // namespace TagParser