mirror of
https://github.com/taglib/taglib.git
synced 2026-03-30 10:42:47 -04:00
Support for Matroska chapters
This commit is contained in:
@ -4,6 +4,8 @@
|
||||
#include "matroskasimpletag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "tstring.h"
|
||||
#include "tutils.h"
|
||||
#include "tbytevector.h"
|
||||
@ -66,16 +68,48 @@ int main(int argc, char *argv[])
|
||||
const TagLib::String &mediaType = attachedFile.mediaType();
|
||||
PRINT_PRETTY("Media Type", !mediaType.isEmpty() ? mediaType.toCString(false) : "None");
|
||||
PRINT_PRETTY("Data Size",
|
||||
TagLib::Utils::formatString("%u byte(s)",attachedFile.data().size()).toCString(false)
|
||||
TagLib::Utils::formatString("%u byte(s)", attachedFile.data().size()).toCString(false)
|
||||
);
|
||||
PRINT_PRETTY("UID",
|
||||
TagLib::Utils::formatString("%llu",attachedFile.uid()).toCString(false)
|
||||
TagLib::Utils::formatString("%llu", attachedFile.uid()).toCString(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("File has no attachments\n");
|
||||
|
||||
TagLib::Matroska::Chapters *chapters = file.chapters();
|
||||
if(chapters) {
|
||||
printf("Chapters:\n");
|
||||
const TagLib::Matroska::Chapters::ChapterEditionList &editions = chapters->chapterEditionList();
|
||||
for(const auto &edition : editions) {
|
||||
if(edition.uid()) {
|
||||
PRINT_PRETTY("Edition UID", TagLib::Utils::formatString("%llu", edition.uid())
|
||||
.toCString(false));
|
||||
}
|
||||
PRINT_PRETTY("Edition Flags", TagLib::Utils::formatString("default=%d, ordered=%d",
|
||||
edition.isDefault(), edition.isOrdered())
|
||||
.toCString(false));
|
||||
printf("\n");
|
||||
for(const auto &chapter : edition.chapterList()) {
|
||||
PRINT_PRETTY("Chapter UID", TagLib::Utils::formatString("%llu", chapter.uid())
|
||||
.toCString(false));
|
||||
PRINT_PRETTY("Chapter flags", TagLib::Utils::formatString("hidden=%d", chapter.isHidden())
|
||||
.toCString(false));
|
||||
PRINT_PRETTY("Start-End", TagLib::Utils::formatString("%llu-%llu",
|
||||
chapter.timeStart(), chapter.timeEnd()).toCString(false));
|
||||
for(const auto &display : chapter.displayList()) {
|
||||
PRINT_PRETTY("Display", display.string().toCString(false));
|
||||
PRINT_PRETTY("Language", !display.language().isEmpty()
|
||||
? display.language().toCString(false) : "Not set");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("File has no chapters\n");
|
||||
|
||||
if(auto properties = dynamic_cast<const TagLib::Matroska::Properties *>(file.audioProperties())) {
|
||||
printf("Properties:\n");
|
||||
PRINT_PRETTY("Doc Type", properties->docType().toCString(false));
|
||||
|
||||
@ -230,6 +230,9 @@ if(WITH_MATROSKA)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
matroska/matroskaattachedfile.h
|
||||
matroska/matroskaattachments.h
|
||||
matroska/matroskachapter.h
|
||||
matroska/matroskachapteredition.h
|
||||
matroska/matroskachapters.h
|
||||
matroska/matroskacues.h
|
||||
matroska/matroskaelement.h
|
||||
matroska/matroskafile.h
|
||||
@ -242,6 +245,7 @@ if(WITH_MATROSKA)
|
||||
matroska/ebml/ebmlelement.h
|
||||
matroska/ebml/ebmlmasterelement.h
|
||||
matroska/ebml/ebmlmkattachments.h
|
||||
matroska/ebml/ebmlmkchapters.h
|
||||
matroska/ebml/ebmlmkcues.h
|
||||
matroska/ebml/ebmlmkseekhead.h
|
||||
matroska/ebml/ebmlmksegment.h
|
||||
@ -449,6 +453,9 @@ if(WITH_MATROSKA)
|
||||
set(matroska_SRCS
|
||||
matroska/matroskaattachedfile.cpp
|
||||
matroska/matroskaattachments.cpp
|
||||
matroska/matroskachapter.cpp
|
||||
matroska/matroskachapteredition.cpp
|
||||
matroska/matroskachapters.cpp
|
||||
matroska/matroskacues.cpp
|
||||
matroska/matroskaelement.cpp
|
||||
matroska/matroskafile.cpp
|
||||
@ -464,6 +471,7 @@ if(WITH_MATROSKA)
|
||||
matroska/ebml/ebmlelement.cpp
|
||||
matroska/ebml/ebmlmasterelement.cpp
|
||||
matroska/ebml/ebmlmkattachments.cpp
|
||||
matroska/ebml/ebmlmkchapters.cpp
|
||||
matroska/ebml/ebmlmkcues.cpp
|
||||
matroska/ebml/ebmlmkseekhead.cpp
|
||||
matroska/ebml/ebmlmksegment.cpp
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "ebmlmksegment.h"
|
||||
#include "ebmlmktags.h"
|
||||
#include "ebmlmkattachments.h"
|
||||
#include "ebmlmkchapters.h"
|
||||
#include "ebmlmktracks.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
@ -113,6 +114,19 @@ std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueCodecState);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueReference);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueRefTime);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapters);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionEntry);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagDefault);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagOrdered);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterAtom);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeStart);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeEnd);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterFlagHidden);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterDisplay);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapString);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapLanguage);
|
||||
}
|
||||
return std::make_unique<Element>(id, sizeLength, dataSize);
|
||||
}
|
||||
|
||||
@ -86,6 +86,19 @@ namespace TagLib::EBML {
|
||||
MkSamplingFrequency = 0xB5,
|
||||
MkBitDepth = 0x6264,
|
||||
MkChannels = 0x9F,
|
||||
MkChapters = 0x1043A770,
|
||||
MkEditionEntry = 0x45B9,
|
||||
MkEditionUID = 0x45BC,
|
||||
MkEditionFlagDefault = 0x45DB,
|
||||
MkEditionFlagOrdered = 0x45DD,
|
||||
MkChapterAtom = 0xB6,
|
||||
MkChapterUID = 0x73C4,
|
||||
MkChapterTimeStart = 0x91,
|
||||
MkChapterTimeEnd = 0x92,
|
||||
MkChapterFlagHidden = 0x98,
|
||||
MkChapterDisplay = 0x80,
|
||||
MkChapString = 0x85,
|
||||
MkChapLanguage = 0x437C,
|
||||
};
|
||||
|
||||
Element(Id id, int sizeLength, offset_t dataSize) :
|
||||
@ -134,6 +147,7 @@ namespace TagLib::EBML {
|
||||
class MkTags;
|
||||
class MkAttachments;
|
||||
class MkSeekHead;
|
||||
class MkChapters;
|
||||
class MkCues;
|
||||
class VoidElement;
|
||||
|
||||
@ -196,6 +210,19 @@ namespace TagLib::EBML {
|
||||
template <> struct GetElementTypeById<Element::Id::MkTitle> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSamplingFrequency> { using type = FloatElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeekHead> { using type = MkSeekHead; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapters> { using type = MkChapters; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionEntry> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionFlagDefault> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionFlagOrdered> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterAtom> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterTimeStart> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterTimeEnd> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterFlagHidden> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterDisplay> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapString> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapLanguage> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::VoidElement> { using type = VoidElement; };
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
|
||||
102
taglib/matroska/ebml/ebmlmkchapters.cpp
Normal file
102
taglib/matroska/ebml/ebmlmkchapters.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlmkchapters.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskachapteredition.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
std::unique_ptr<Matroska::Chapters> EBML::MkChapters::parse()
|
||||
{
|
||||
auto chapters = std::make_unique<Matroska::Chapters>();
|
||||
chapters->setOffset(offset);
|
||||
chapters->setSize(getSize());
|
||||
|
||||
for(const auto &element : elements) {
|
||||
if(element->getId() != Id::MkEditionEntry)
|
||||
continue;
|
||||
|
||||
List<Matroska::Chapter> editionChapters;
|
||||
Matroska::ChapterEdition::UID editionUid = 0;
|
||||
bool editionIsDefault = false;
|
||||
bool editionIsOrdered = false;
|
||||
auto edition = element_cast<Id::MkEditionEntry>(element);
|
||||
for(const auto &editionChild : *edition) {
|
||||
Id id = editionChild->getId();
|
||||
if(id == Id::MkEditionUID)
|
||||
editionUid = element_cast<Id::MkEditionUID>(editionChild)->getValue();
|
||||
else if(id == Id::MkEditionFlagDefault)
|
||||
editionIsDefault = element_cast<Id::MkEditionFlagDefault>(editionChild)->getValue() != 0;
|
||||
else if(id == Id::MkEditionFlagOrdered)
|
||||
editionIsOrdered = element_cast<Id::MkEditionFlagOrdered>(editionChild)->getValue() != 0;
|
||||
else if(id == Id::MkChapterAtom) {
|
||||
Matroska::Chapter::UID chapterUid = 0;
|
||||
Matroska::Chapter::Time chapterTimeStart = 0;
|
||||
Matroska::Chapter::Time chapterTimeEnd = 0;
|
||||
List<Matroska::Chapter::Display> chapterDisplays;
|
||||
bool chapterHidden = false;
|
||||
auto chapterAtom = element_cast<Id::MkChapterAtom>(editionChild);
|
||||
for(const auto &chapterChild : *chapterAtom) {
|
||||
Id cid = chapterChild->getId();
|
||||
if(cid == Id::MkChapterUID)
|
||||
chapterUid = element_cast<Id::MkChapterUID>(chapterChild)->getValue();
|
||||
else if(cid == Id::MkChapterTimeStart)
|
||||
chapterTimeStart = element_cast<Id::MkChapterTimeStart>(chapterChild)->getValue();
|
||||
else if(cid == Id::MkChapterTimeEnd)
|
||||
chapterTimeEnd = element_cast<Id::MkChapterTimeEnd>(chapterChild)->getValue();
|
||||
else if(cid == Id::MkChapterFlagHidden)
|
||||
chapterHidden = element_cast<Id::MkChapterFlagHidden>(chapterChild)->getValue() != 0;
|
||||
else if(cid == Id::MkChapterDisplay) {
|
||||
auto display = element_cast<Id::MkChapterDisplay>(chapterChild);
|
||||
String displayString;
|
||||
String displayLanguage;
|
||||
for(const auto &displayChild : *display) {
|
||||
Id did = displayChild->getId();
|
||||
if(did == Id::MkChapString)
|
||||
displayString = element_cast<Id::MkChapString>(displayChild)->getValue();
|
||||
else if(did == Id::MkChapLanguage)
|
||||
displayLanguage = element_cast<Id::MkChapLanguage>(displayChild)->getValue();
|
||||
}
|
||||
if(!displayString.isEmpty()) {
|
||||
chapterDisplays.append(Matroska::Chapter::Display(displayString, displayLanguage));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(chapterUid) {
|
||||
editionChapters.append(Matroska::Chapter(
|
||||
chapterTimeStart, chapterTimeEnd, chapterDisplays, chapterUid, chapterHidden));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!editionChapters.isEmpty()) {
|
||||
chapters->addChapterEdition(Matroska::ChapterEdition(
|
||||
editionChapters, editionIsDefault, editionIsOrdered, editionUid));
|
||||
}
|
||||
}
|
||||
return chapters;
|
||||
}
|
||||
59
taglib/matroska/ebml/ebmlmkchapters.h
Normal file
59
taglib/matroska/ebml/ebmlmkchapters.h
Normal file
@ -0,0 +1,59 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLMKCHAPTERS_H
|
||||
#define TAGLIB_EBMLMKCHAPTERS_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Chapters;
|
||||
}
|
||||
namespace EBML {
|
||||
class MkChapters : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkChapters(int sizeLength, offset_t dataSize, offset_t offset) :
|
||||
MasterElement(Id::MkChapters, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
MkChapters(Id, int sizeLength, offset_t dataSize, offset_t offset) :
|
||||
MasterElement(Id::MkChapters, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
MkChapters() :
|
||||
MasterElement(Id::MkChapters, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
std::unique_ptr<Matroska::Chapters> parse();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@ -19,15 +19,11 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlmksegment.h"
|
||||
#include "ebmlmktags.h"
|
||||
#include "ebmlmkattachments.h"
|
||||
#include "ebmlmkseekhead.h"
|
||||
#include "ebmlmkinfo.h"
|
||||
#include "ebmlmktracks.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "matroskafile.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskacues.h"
|
||||
#include "matroskaseekhead.h"
|
||||
#include "matroskasegment.h"
|
||||
@ -80,6 +76,11 @@ bool EBML::MkSegment::read(File &file)
|
||||
if(!attachments->read(file))
|
||||
return false;
|
||||
}
|
||||
else if(id == Id::MkChapters) {
|
||||
chapters = element_cast<Id::MkChapters>(std::move(element));
|
||||
if(!chapters->read(file))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if(id == Id::VoidElement
|
||||
&& seekHead
|
||||
@ -103,6 +104,11 @@ std::unique_ptr<Matroska::Attachments> EBML::MkSegment::parseAttachments()
|
||||
return attachments ? attachments->parse() : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Chapters> EBML::MkSegment::parseChapters()
|
||||
{
|
||||
return chapters ? chapters->parse() : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::SeekHead> EBML::MkSegment::parseSeekHead()
|
||||
{
|
||||
return seekHead ? seekHead->parse(segmentDataOffset()) : nullptr;
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "ebmlmktags.h"
|
||||
#include "ebmlmkattachments.h"
|
||||
#include "ebmlmkchapters.h"
|
||||
#include "ebmlmkseekhead.h"
|
||||
#include "ebmlmkcues.h"
|
||||
#include "ebmlmkinfo.h"
|
||||
@ -35,6 +36,7 @@ namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Tag;
|
||||
class Attachments;
|
||||
class Chapters;
|
||||
class SeekHead;
|
||||
class Segment;
|
||||
}
|
||||
@ -55,6 +57,7 @@ namespace TagLib {
|
||||
bool read(File &file) override;
|
||||
std::unique_ptr<Matroska::Tag> parseTag();
|
||||
std::unique_ptr<Matroska::Attachments> parseAttachments();
|
||||
std::unique_ptr<Matroska::Chapters> parseChapters();
|
||||
std::unique_ptr<Matroska::SeekHead> parseSeekHead();
|
||||
std::unique_ptr<Matroska::Cues> parseCues();
|
||||
std::unique_ptr<Matroska::Segment> parseSegment();
|
||||
@ -64,6 +67,7 @@ namespace TagLib {
|
||||
private:
|
||||
std::unique_ptr<MkTags> tags;
|
||||
std::unique_ptr<MkAttachments> attachments;
|
||||
std::unique_ptr<MkChapters> chapters;
|
||||
std::unique_ptr<MkSeekHead> seekHead;
|
||||
std::unique_ptr<MkCues> cues;
|
||||
std::unique_ptr<MkInfo> info;
|
||||
|
||||
156
taglib/matroska/matroskachapter.cpp
Normal file
156
taglib/matroska/matroskachapter.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "matroskachapter.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::Chapter::Display::DisplayPrivate
|
||||
{
|
||||
public:
|
||||
DisplayPrivate() = default;
|
||||
~DisplayPrivate() = default;
|
||||
String string;
|
||||
String language;
|
||||
};
|
||||
|
||||
class Matroska::Chapter::ChapterPrivate
|
||||
{
|
||||
public:
|
||||
ChapterPrivate() = default;
|
||||
~ChapterPrivate() = default;
|
||||
UID uid = 0;
|
||||
Time timeStart = 0;
|
||||
Time timeEnd = 0;
|
||||
List<Display> displayList;
|
||||
bool hidden = false;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::Chapter::Chapter(Time timeStart, Time timeEnd,
|
||||
const List<Display> &displayList, UID uid, bool hidden) :
|
||||
d(std::make_unique<ChapterPrivate>())
|
||||
{
|
||||
d->uid = uid;
|
||||
d->timeStart = timeStart;
|
||||
d->timeEnd = timeEnd;
|
||||
d->displayList = displayList;
|
||||
d->hidden = hidden;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Chapter(const Chapter &other) :
|
||||
d(std::make_unique<ChapterPrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Chapter::Chapter(Chapter &&other) noexcept = default;
|
||||
|
||||
Matroska::Chapter::~Chapter() = default;
|
||||
|
||||
Matroska::Chapter &Matroska::Chapter::operator=(Chapter &&other) noexcept = default;
|
||||
|
||||
Matroska::Chapter &Matroska::Chapter::operator=(const Chapter &other)
|
||||
{
|
||||
Chapter(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Matroska::Chapter::swap(Chapter &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
Matroska::Chapter::UID Matroska::Chapter::uid() const
|
||||
{
|
||||
return d->uid;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Time Matroska::Chapter::timeStart() const
|
||||
{
|
||||
return d->timeStart;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Time Matroska::Chapter::timeEnd() const
|
||||
{
|
||||
return d->timeEnd;
|
||||
}
|
||||
|
||||
bool Matroska::Chapter::isHidden() const
|
||||
{
|
||||
return d->hidden;
|
||||
}
|
||||
|
||||
const List<Matroska::Chapter::Display>& Matroska::Chapter::displayList() const
|
||||
{
|
||||
return d->displayList;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display::Display(const String &string, const String &language) :
|
||||
d(std::make_unique<DisplayPrivate>())
|
||||
{
|
||||
d->string = string;
|
||||
d->language = language;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display::Display(const Display &other) :
|
||||
d(std::make_unique<DisplayPrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display::Display(Display &&other) noexcept = default;
|
||||
|
||||
Matroska::Chapter::Display::~Display() = default;
|
||||
|
||||
Matroska::Chapter::Display& Matroska::Chapter::Display::operator=(const Display &other)
|
||||
{
|
||||
Display(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display& Matroska::Chapter::Display::operator=(Display &&other) noexcept = default;
|
||||
|
||||
void Matroska::Chapter::Display::swap(Display &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
const String& Matroska::Chapter::Display::string() const
|
||||
{
|
||||
return d->string;
|
||||
}
|
||||
|
||||
const String& Matroska::Chapter::Display::language() const
|
||||
{
|
||||
return d->language;
|
||||
}
|
||||
174
taglib/matroska/matroskachapter.h
Normal file
174
taglib/matroska/matroskachapter.h
Normal file
@ -0,0 +1,174 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MATROSKACHAPTER_H
|
||||
#define TAGLIB_MATROSKACHAPTER_H
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
#include "tlist.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace EBML {
|
||||
class MkChapters;
|
||||
}
|
||||
|
||||
class String;
|
||||
class ByteVector;
|
||||
namespace Matroska {
|
||||
//! Matroska chapter.
|
||||
class TAGLIB_EXPORT Chapter
|
||||
{
|
||||
public:
|
||||
using UID = unsigned long long;
|
||||
using Time = unsigned long long;
|
||||
|
||||
/*!
|
||||
* Contains all possible strings to use for the chapter display.
|
||||
*/
|
||||
class TAGLIB_EXPORT Display
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Construct a chapter display.
|
||||
*/
|
||||
Display(const String &string, const String &language);
|
||||
|
||||
/*!
|
||||
* Construct a chapter display as a copy of \a other.
|
||||
*/
|
||||
Display(const Display &other);
|
||||
|
||||
/*!
|
||||
* Construct a chapter display moving from \a other.
|
||||
*/
|
||||
Display(Display &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this chapter display.
|
||||
*/
|
||||
~Display();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this object.
|
||||
*/
|
||||
Display &operator=(const Display &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this object.
|
||||
*/
|
||||
Display &operator=(Display &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the object with the content of \a other.
|
||||
*/
|
||||
void swap(Display &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns string representing the chapter.
|
||||
*/
|
||||
const String &string() const;
|
||||
|
||||
/*!
|
||||
* Returns language corresponding to the string.
|
||||
*/
|
||||
const String &language() const;
|
||||
|
||||
private:
|
||||
class DisplayPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<DisplayPrivate> d;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Construct a chapter.
|
||||
*/
|
||||
Chapter(Time timeStart, Time timeEnd, const List<Display> &displayList,
|
||||
UID uid, bool hidden = false);
|
||||
|
||||
/*!
|
||||
* Construct a chapter as a copy of \a other.
|
||||
*/
|
||||
Chapter(const Chapter &other);
|
||||
|
||||
/*!
|
||||
* Construct a chapter moving from \a other.
|
||||
*/
|
||||
Chapter(Chapter &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this chapter.
|
||||
*/
|
||||
~Chapter();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this object.
|
||||
*/
|
||||
Chapter &operator=(const Chapter &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this object.
|
||||
*/
|
||||
Chapter &operator=(Chapter &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the object with the content of \a other.
|
||||
*/
|
||||
void swap(Chapter &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the UID of the chapter.
|
||||
*/
|
||||
UID uid() const;
|
||||
|
||||
/*!
|
||||
* Returns the timestamp of the start of the chapter in nanoseconds.
|
||||
*/
|
||||
Time timeStart() const;
|
||||
|
||||
/*!
|
||||
* Returns the timestamp of the start of the chapter in nanoseconds.
|
||||
*/
|
||||
Time timeEnd() const;
|
||||
|
||||
/*!
|
||||
* Check if chapter is hidden.
|
||||
*/
|
||||
bool isHidden() const;
|
||||
|
||||
/*!
|
||||
* Returns strings with language.
|
||||
*/
|
||||
const List<Display> &displayList() const;
|
||||
|
||||
private:
|
||||
class ChapterPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ChapterPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
101
taglib/matroska/matroskachapteredition.cpp
Normal file
101
taglib/matroska/matroskachapteredition.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "matroskachapter.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tlist.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::ChapterEdition::ChapterEditionPrivate
|
||||
{
|
||||
public:
|
||||
ChapterEditionPrivate() = default;
|
||||
~ChapterEditionPrivate() = default;
|
||||
List<Chapter> chapters;
|
||||
UID uid = 0;
|
||||
bool flagDefault = false;
|
||||
bool flagOrdered = false;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::ChapterEdition::ChapterEdition(const List<Chapter> &chapterList,
|
||||
bool isDefault, bool isOrdered, UID uid) :
|
||||
d(std::make_unique<ChapterEditionPrivate>())
|
||||
{
|
||||
d->chapters = chapterList;
|
||||
d->uid = uid;
|
||||
d->flagDefault = isDefault;
|
||||
d->flagOrdered = isOrdered;
|
||||
}
|
||||
|
||||
Matroska::ChapterEdition::ChapterEdition(const ChapterEdition &other) :
|
||||
d(std::make_unique<ChapterEditionPrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::ChapterEdition::ChapterEdition(ChapterEdition&& other) noexcept = default;
|
||||
|
||||
Matroska::ChapterEdition::~ChapterEdition() = default;
|
||||
|
||||
Matroska::ChapterEdition &Matroska::ChapterEdition::operator=(ChapterEdition &&other) = default;
|
||||
|
||||
Matroska::ChapterEdition &Matroska::ChapterEdition::operator=(const ChapterEdition &other)
|
||||
{
|
||||
ChapterEdition(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Matroska::ChapterEdition::swap(ChapterEdition &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
Matroska::ChapterEdition::UID Matroska::ChapterEdition::uid() const
|
||||
{
|
||||
return d->uid;
|
||||
}
|
||||
|
||||
bool Matroska::ChapterEdition::isDefault() const
|
||||
{
|
||||
return d->flagDefault;
|
||||
}
|
||||
|
||||
bool Matroska::ChapterEdition::isOrdered() const
|
||||
{
|
||||
return d->flagOrdered;
|
||||
}
|
||||
|
||||
const List<Matroska::Chapter> &Matroska::ChapterEdition::chapterList() const
|
||||
{
|
||||
return d->chapters;
|
||||
}
|
||||
106
taglib/matroska/matroskachapteredition.h
Normal file
106
taglib/matroska/matroskachapteredition.h
Normal file
@ -0,0 +1,106 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MATROSKACHAPTEREDITION_H
|
||||
#define TAGLIB_MATROSKACHAPTEREDITION_H
|
||||
|
||||
#include "matroskachapter.h"
|
||||
|
||||
namespace TagLib {
|
||||
class String;
|
||||
class ByteVector;
|
||||
namespace Matroska {
|
||||
//! Edition of chapters.
|
||||
class TAGLIB_EXPORT ChapterEdition
|
||||
{
|
||||
public:
|
||||
using UID = unsigned long long;
|
||||
|
||||
/*!
|
||||
* Construct an edition.
|
||||
*/
|
||||
ChapterEdition(const List<Chapter> &chapterList,
|
||||
bool isDefault, bool isOrdered = false, UID uid = 0);
|
||||
|
||||
/*!
|
||||
* Construct an edition as a copy of \a other.
|
||||
*/
|
||||
ChapterEdition(const ChapterEdition &other);
|
||||
|
||||
/*!
|
||||
* Construct an edition moving from \a other.
|
||||
*/
|
||||
ChapterEdition(ChapterEdition &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this edition.
|
||||
*/
|
||||
~ChapterEdition();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this object.
|
||||
*/
|
||||
ChapterEdition &operator=(const ChapterEdition &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this object.
|
||||
*/
|
||||
ChapterEdition &operator=(ChapterEdition &&other);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the object with the content of \a other.
|
||||
*/
|
||||
void swap(ChapterEdition &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the UID of the edition.
|
||||
*/
|
||||
UID uid() const;
|
||||
|
||||
/*!
|
||||
* Check if this edition should be used as the default one.
|
||||
*/
|
||||
bool isDefault() const;
|
||||
|
||||
/*!
|
||||
* Check if the chapters can be defined multiple times and the order to
|
||||
* play them is enforced.
|
||||
*/
|
||||
bool isOrdered() const;
|
||||
|
||||
/*!
|
||||
* Get the list of all chapters.
|
||||
*/
|
||||
const List<Chapter> &chapterList() const;
|
||||
|
||||
private:
|
||||
class ChapterEditionPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ChapterEditionPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
152
taglib/matroska/matroskachapters.cpp
Normal file
152
taglib/matroska/matroskachapters.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "matroskachapters.h"
|
||||
#include <memory>
|
||||
#include "matroskachapteredition.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "ebmlmkchapters.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::Chapters::ChaptersPrivate
|
||||
{
|
||||
public:
|
||||
ChaptersPrivate() = default;
|
||||
~ChaptersPrivate() = default;
|
||||
ChaptersPrivate(const ChaptersPrivate &) = delete;
|
||||
ChaptersPrivate &operator=(const ChaptersPrivate &) = delete;
|
||||
ChapterEditionList editions;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::Chapters::Chapters() :
|
||||
Element(static_cast<ID>(EBML::Element::Id::MkChapters)),
|
||||
d(std::make_unique<ChaptersPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Chapters::~Chapters() = default;
|
||||
|
||||
void Matroska::Chapters::addChapterEdition(const ChapterEdition& edition)
|
||||
{
|
||||
d->editions.append(edition);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
void Matroska::Chapters::removeChapterEdition(unsigned long long uid)
|
||||
{
|
||||
auto it = std::find_if(d->editions.begin(), d->editions.end(),
|
||||
[uid](const ChapterEdition& file) {
|
||||
return file.uid() == uid;
|
||||
});
|
||||
if(it != d->editions.end()) {
|
||||
d->editions.erase(it);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Matroska::Chapters::clear()
|
||||
{
|
||||
d->editions.clear();
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
const Matroska::Chapters::ChapterEditionList &Matroska::Chapters::chapterEditionList() const
|
||||
{
|
||||
return d->editions;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
ByteVector Matroska::Chapters::renderInternal()
|
||||
{
|
||||
if(d->editions.isEmpty()) {
|
||||
// Avoid writing a Chapters element without ChapterEdition element.
|
||||
return {};
|
||||
}
|
||||
|
||||
EBML::MkChapters chapters;
|
||||
for(const auto &chapterEdition : std::as_const(d->editions)) {
|
||||
auto chapterEditionElement = EBML::make_unique_element<EBML::Element::Id::MkEditionEntry>();
|
||||
|
||||
if(auto uid = chapterEdition.uid()) {
|
||||
auto uidElement = EBML::make_unique_element<EBML::Element::Id::MkEditionUID>();
|
||||
uidElement->setValue(uid);
|
||||
chapterEditionElement->appendElement(std::move(uidElement));
|
||||
}
|
||||
auto defaultElement = EBML::make_unique_element<EBML::Element::Id::MkEditionFlagDefault>();
|
||||
defaultElement->setValue(chapterEdition.isDefault());
|
||||
chapterEditionElement->appendElement(std::move(defaultElement));
|
||||
auto orderedElement = EBML::make_unique_element<EBML::Element::Id::MkEditionFlagOrdered>();
|
||||
orderedElement->setValue(chapterEdition.isOrdered());
|
||||
chapterEditionElement->appendElement(std::move(orderedElement));
|
||||
|
||||
for(const auto &chapter : chapterEdition.chapterList()) {
|
||||
auto chapterElement = EBML::make_unique_element<EBML::Element::Id::MkChapterAtom>();
|
||||
|
||||
auto cuidElement = EBML::make_unique_element<EBML::Element::Id::MkChapterUID>();
|
||||
auto cuid = chapter.uid();
|
||||
cuidElement->setValue(cuid ? cuid : EBML::randomUID());
|
||||
chapterElement->appendElement(std::move(cuidElement));
|
||||
auto timeStartElement = EBML::make_unique_element<EBML::Element::Id::MkChapterTimeStart>();
|
||||
timeStartElement->setValue(chapter.timeStart());
|
||||
chapterElement->appendElement(std::move(timeStartElement));
|
||||
auto timeEndElement = EBML::make_unique_element<EBML::Element::Id::MkChapterTimeEnd>();
|
||||
timeEndElement->setValue(chapter.timeEnd());
|
||||
chapterElement->appendElement(std::move(timeEndElement));
|
||||
auto hiddenElement = EBML::make_unique_element<EBML::Element::Id::MkChapterFlagHidden>();
|
||||
hiddenElement->setValue(chapter.isHidden());
|
||||
chapterElement->appendElement(std::move(hiddenElement));
|
||||
|
||||
for(const auto& display : chapter.displayList()) {
|
||||
auto displayElement = EBML::make_unique_element<EBML::Element::Id::MkChapterDisplay>();
|
||||
|
||||
auto stringElement = EBML::make_unique_element<EBML::Element::Id::MkChapString>();
|
||||
stringElement->setValue(display.string());
|
||||
displayElement->appendElement(std::move(stringElement));
|
||||
auto languageElement = EBML::make_unique_element<EBML::Element::Id::MkChapLanguage>();
|
||||
languageElement->setValue(display.language());
|
||||
displayElement->appendElement(std::move(languageElement));
|
||||
|
||||
chapterElement->appendElement(std::move(displayElement));
|
||||
}
|
||||
|
||||
chapterEditionElement->appendElement(std::move(chapterElement));
|
||||
}
|
||||
|
||||
chapters.appendElement(std::move(chapterEditionElement));
|
||||
}
|
||||
return chapters.render();
|
||||
}
|
||||
83
taglib/matroska/matroskachapters.h
Normal file
83
taglib/matroska/matroskachapters.h
Normal file
@ -0,0 +1,83 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MATROSKACHAPTERS_H
|
||||
#define TAGLIB_MATROSKACHAPTERS_H
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
#include "tlist.h"
|
||||
#include "matroskaelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
namespace EBML {
|
||||
class MkChapters;
|
||||
}
|
||||
namespace Matroska {
|
||||
class ChapterEdition;
|
||||
class File;
|
||||
|
||||
//! Collection of chapter editions.
|
||||
class TAGLIB_EXPORT Chapters
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
: private Element
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
using ChapterEditionList = List<ChapterEdition>;
|
||||
//! Construct chapters.
|
||||
Chapters();
|
||||
|
||||
//! Destroy chapters.
|
||||
virtual ~Chapters();
|
||||
|
||||
//! Add a chapter edition.
|
||||
void addChapterEdition(const ChapterEdition &edition);
|
||||
|
||||
//! Remove a chapter edition.
|
||||
void removeChapterEdition(unsigned long long uid);
|
||||
|
||||
//! Remove all chapter editions.
|
||||
void clear();
|
||||
|
||||
//! Get list of all chapter editions.
|
||||
const ChapterEditionList &chapterEditionList() const;
|
||||
|
||||
private:
|
||||
friend class EBML::MkChapters;
|
||||
friend class File;
|
||||
class ChaptersPrivate;
|
||||
|
||||
// private Element implementation
|
||||
ByteVector renderInternal() override;
|
||||
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ChaptersPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -22,6 +22,9 @@
|
||||
#include "matroskatag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskachapter.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskaseekhead.h"
|
||||
#include "matroskacues.h"
|
||||
#include "matroskasegment.h"
|
||||
@ -50,6 +53,7 @@ public:
|
||||
|
||||
std::unique_ptr<Tag> tag;
|
||||
std::unique_ptr<Attachments> attachments;
|
||||
std::unique_ptr<Chapters> chapters;
|
||||
std::unique_ptr<SeekHead> seekHead;
|
||||
std::unique_ptr<Cues> cues;
|
||||
std::unique_ptr<Segment> segment;
|
||||
@ -179,12 +183,64 @@ StringList Matroska::File::complexPropertyKeys() const
|
||||
}
|
||||
}
|
||||
}
|
||||
if(d->chapters && !d->chapters->chapterEditionList().isEmpty()) {
|
||||
keys.append("CHAPTERS");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> Matroska::File::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props = TagLib::File::complexProperties(key);
|
||||
if(key.upper() == "CHAPTERS") {
|
||||
if(d->chapters) {
|
||||
for(const auto &edition : d->chapters->chapterEditionList()) {
|
||||
VariantMap property;
|
||||
if(auto uid = edition.uid()) {
|
||||
property.insert("uid", uid);
|
||||
}
|
||||
if(auto isDefault = edition.isDefault()) {
|
||||
property.insert("isDefault", isDefault);
|
||||
}
|
||||
if(auto isOrdered = edition.isOrdered()) {
|
||||
property.insert("isOrdered", isOrdered);
|
||||
}
|
||||
if(auto chapters = edition.chapterList(); !chapters.isEmpty()) {
|
||||
VariantList chaps;
|
||||
for(const auto &chapter : chapters) {
|
||||
VariantMap chap;
|
||||
if(auto uid = chapter.uid()) {
|
||||
chap.insert("uid", uid);
|
||||
}
|
||||
if(auto isHidden = chapter.isHidden()) {
|
||||
chap.insert("isHidden", isHidden);
|
||||
}
|
||||
chap.insert("timeStart", chapter.timeStart());
|
||||
if(auto timeEnd = chapter.timeEnd()) {
|
||||
chap.insert("timeEnd", timeEnd);
|
||||
}
|
||||
if(auto displays = chapter.displayList(); !displays.isEmpty()) {
|
||||
VariantList disps;
|
||||
for(const auto &display : displays) {
|
||||
VariantMap disp;
|
||||
if(auto str = display.string(); !str.isEmpty()) {
|
||||
disp.insert("string", str);
|
||||
}
|
||||
if(auto language = display.language(); !language.isEmpty()) {
|
||||
disp.insert("language", language);
|
||||
}
|
||||
disps.append(disp);
|
||||
}
|
||||
chap.insert("displays", disps);
|
||||
}
|
||||
chaps.append(chap);
|
||||
}
|
||||
property.insert("chapters", chaps);
|
||||
}
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(d->attachments) {
|
||||
const auto &attachedFiles = d->attachments->attachedFileList();
|
||||
for(const auto &attachedFile : attachedFiles) {
|
||||
@ -208,6 +264,37 @@ bool Matroska::File::setComplexProperties(const String &key, const List<VariantM
|
||||
return true;
|
||||
}
|
||||
|
||||
if(key.upper() == "CHAPTERS") {
|
||||
chapters(true)->clear();
|
||||
for(const auto &ed : value) {
|
||||
List<Chapter> editionChapters;
|
||||
const auto chaps = ed.value("chapters").toList();
|
||||
for(const auto &chapVar : chaps) {
|
||||
auto chap = chapVar.toMap();
|
||||
const auto disps = chap.value("displays").toList();
|
||||
List<Chapter::Display> chapterDisplays;
|
||||
for(const auto &dispVar : disps) {
|
||||
auto disp = dispVar.toMap();
|
||||
chapterDisplays.append(Chapter::Display(
|
||||
disp.value("string").toString(),
|
||||
disp.value("language").toString()));
|
||||
}
|
||||
editionChapters.append(Chapter(
|
||||
chap.value("timeStart").toULongLong(),
|
||||
chap.value("timeEnd").toULongLong(),
|
||||
chapterDisplays,
|
||||
chap.value("uid", 0ULL).toULongLong(),
|
||||
chap.value("isHidden", false).toBool()));
|
||||
}
|
||||
d->chapters->addChapterEdition(ChapterEdition(
|
||||
editionChapters,
|
||||
ed.value("isDefault", false).toBool(),
|
||||
ed.value("isOrdered", false).toBool(),
|
||||
ed.value("uid", 0ULL).toULongLong()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
List<AttachedFile> &files = attachments(true)->attachedFiles();
|
||||
for(auto it = files.begin(); it != files.end();) {
|
||||
if(keyMatchesAttachedFile(key, *it)) {
|
||||
@ -268,6 +355,13 @@ Matroska::Attachments *Matroska::File::attachments(bool create) const
|
||||
return d->attachments.get();
|
||||
}
|
||||
|
||||
Matroska::Chapters *Matroska::File::chapters(bool create) const
|
||||
{
|
||||
if(!d->chapters && create)
|
||||
d->chapters = std::make_unique<Chapters>();
|
||||
return d->chapters.get();
|
||||
}
|
||||
|
||||
void Matroska::File::read(bool readProperties, Properties::ReadStyle readStyle)
|
||||
{
|
||||
offset_t fileLength = length();
|
||||
@ -312,6 +406,7 @@ void Matroska::File::read(bool readProperties, Properties::ReadStyle readStyle)
|
||||
d->cues = segment->parseCues();
|
||||
d->tag = segment->parseTag();
|
||||
d->attachments = segment->parseAttachments();
|
||||
d->chapters = segment->parseChapters();
|
||||
|
||||
if(readProperties) {
|
||||
d->properties = std::make_unique<Properties>(this);
|
||||
@ -355,8 +450,13 @@ bool Matroska::File::save()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do not create new attachments or tags and corresponding seek head entries
|
||||
// if only empty objects were created.
|
||||
// Do not create new attachments, chapters or tags and corresponding
|
||||
// seek head entries if only empty objects were created.
|
||||
if(d->chapters && d->chapters->chapterEditionList().isEmpty() &&
|
||||
d->chapters->size() == 0 && d->chapters->offset() == 0 &&
|
||||
d->chapters->data().isEmpty()) {
|
||||
d->chapters.reset();
|
||||
}
|
||||
if(d->attachments && d->attachments->attachedFileList().isEmpty() &&
|
||||
d->attachments->size() == 0 && d->attachments->offset() == 0 &&
|
||||
d->attachments->data().isEmpty()) {
|
||||
@ -373,6 +473,7 @@ bool Matroska::File::save()
|
||||
|
||||
// List of all possible elements we can write
|
||||
List<Element *> elements {
|
||||
d->chapters.get(),
|
||||
d->attachments.get(),
|
||||
d->tag.get()
|
||||
};
|
||||
@ -381,7 +482,7 @@ bool Matroska::File::save()
|
||||
* to the end of the file. For new elements,
|
||||
* the order is from least likely to change,
|
||||
* to most likely to change:
|
||||
* 1. Bookmarks (todo)
|
||||
* 1. Chapters
|
||||
* 2. Attachments
|
||||
* 3. Tags
|
||||
*/
|
||||
|
||||
@ -30,6 +30,7 @@ namespace TagLib::Matroska {
|
||||
class Properties;
|
||||
class Tag;
|
||||
class Attachments;
|
||||
class Chapters;
|
||||
|
||||
/*!
|
||||
* Implementation of TagLib::File for Matroska.
|
||||
@ -143,8 +144,26 @@ namespace TagLib::Matroska {
|
||||
*/
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the attachments of the file.
|
||||
*
|
||||
* If \a create is \c false this may return a null pointer if there are no
|
||||
* attachments.
|
||||
* If \a create is \c true it will create attachments if none exist and
|
||||
* returns a valid pointer.
|
||||
*/
|
||||
Attachments *attachments(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the chapters of the file.
|
||||
*
|
||||
* If \a create is \c false this may return a null pointer if there are no
|
||||
* chapters.
|
||||
* If \a create is \c true it will create chapters if none exist and
|
||||
* returns a valid pointer.
|
||||
*/
|
||||
Chapters *chapters(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a Matroska
|
||||
* file.
|
||||
|
||||
@ -130,6 +130,9 @@
|
||||
#include "matroskatag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskachapter.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskasimpletag.h"
|
||||
#include "plainfile.h"
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
@ -152,6 +155,7 @@ class TestMatroska : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testComplexProperties);
|
||||
CPPUNIT_TEST(testOpenInvalid);
|
||||
CPPUNIT_TEST(testSegmentSizeChange);
|
||||
CPPUNIT_TEST(testChapters);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -995,6 +999,208 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testChapters()
|
||||
{
|
||||
const Matroska::ChapterEdition edition1(
|
||||
List{
|
||||
Matroska::Chapter(
|
||||
0, 40000,
|
||||
List{
|
||||
Matroska::Chapter::Display("Chapter 1", "eng")},
|
||||
1, false),
|
||||
Matroska::Chapter(
|
||||
40000, 80000,
|
||||
List{
|
||||
Matroska::Chapter::Display("Chapter 2", "eng"),
|
||||
Matroska::Chapter::Display("Kapitel 2", "deu"),
|
||||
},
|
||||
2),
|
||||
Matroska::Chapter(
|
||||
80000, 120000,
|
||||
List{
|
||||
Matroska::Chapter::Display("Chapter 3", "und")},
|
||||
3, true)
|
||||
},
|
||||
true, false);
|
||||
const VariantMap chapterEdition1 {
|
||||
{"chapters",
|
||||
VariantList{
|
||||
VariantMap{
|
||||
{"displays", VariantList{
|
||||
VariantMap{{"language", "eng"}, {"string", "Chapter 1"}}}},
|
||||
{"timeEnd", 40000ULL},
|
||||
{"timeStart", 0ULL},
|
||||
{"uid", 1ULL}
|
||||
},
|
||||
VariantMap{
|
||||
{"displays", VariantList{
|
||||
VariantMap{{"language", "eng"}, {"string", "Chapter 2"}},
|
||||
VariantMap{{"language", "deu"}, {"string", "Kapitel 2"}}}},
|
||||
{"timeEnd", 80000ULL},
|
||||
{"timeStart", 40000ULL},
|
||||
{"uid", 2ULL}
|
||||
},
|
||||
VariantMap{
|
||||
{
|
||||
"displays", VariantList{
|
||||
VariantMap{{"language", "und"}, {"string", "Chapter 3"}}}
|
||||
},
|
||||
{"isHidden", true},
|
||||
{"timeEnd", 120000ULL},
|
||||
{"timeStart", 80000ULL},
|
||||
{"uid", 3ULL}
|
||||
}
|
||||
}
|
||||
},
|
||||
{"isDefault", true}
|
||||
};
|
||||
const VariantMap chapterEdition2 {
|
||||
{"chapters",
|
||||
VariantList{
|
||||
VariantMap{
|
||||
{"displays", VariantList{
|
||||
VariantMap{{"string", "Chapter A"}}}},
|
||||
{"timeStart", 10000ULL},
|
||||
{"uid", 1234567890ULL}
|
||||
},
|
||||
}
|
||||
},
|
||||
{"isOrdered", true},
|
||||
{"uid", 321ULL}
|
||||
};
|
||||
|
||||
ScopedFileCopy copy("tags-before-cues", ".mkv");
|
||||
string newname = copy.fileName();
|
||||
{
|
||||
Matroska::File f(newname.c_str());
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT(f.tag(false));
|
||||
CPPUNIT_ASSERT(!f.attachments(false));
|
||||
CPPUNIT_ASSERT(!f.chapters(false));
|
||||
CPPUNIT_ASSERT_EQUAL(StringList({"DURATION"}), f.complexPropertyKeys());
|
||||
CPPUNIT_ASSERT(f.complexProperties("CHAPTERS").isEmpty());
|
||||
|
||||
f.chapters(true)->addChapterEdition(edition1);
|
||||
CPPUNIT_ASSERT(f.save());
|
||||
}
|
||||
{
|
||||
Matroska::File f(newname.c_str());
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT(f.tag(false));
|
||||
CPPUNIT_ASSERT(!f.attachments(false));
|
||||
auto chapters = f.chapters(false);
|
||||
CPPUNIT_ASSERT(chapters);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList({"DURATION", "CHAPTERS"}), f.complexPropertyKeys());
|
||||
auto chaptersProperties = f.complexProperties("CHAPTERS");
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chaptersProperties.size());
|
||||
CPPUNIT_ASSERT_EQUAL(chapterEdition1, chaptersProperties.front());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chapters->chapterEditionList().size());
|
||||
const auto &edition = chapters->chapterEditionList().front();
|
||||
CPPUNIT_ASSERT_EQUAL(true, edition.isDefault());
|
||||
CPPUNIT_ASSERT_EQUAL(false, edition.isOrdered());
|
||||
CPPUNIT_ASSERT_EQUAL(0ULL, edition.uid());
|
||||
const auto &chapterAtoms = edition.chapterList();
|
||||
CPPUNIT_ASSERT_EQUAL(3U, chapterAtoms.size());
|
||||
CPPUNIT_ASSERT_EQUAL(1ULL, chapterAtoms[0].uid());
|
||||
CPPUNIT_ASSERT_EQUAL(false, chapterAtoms[0].isHidden());
|
||||
CPPUNIT_ASSERT_EQUAL(0ULL, chapterAtoms[0].timeStart());
|
||||
CPPUNIT_ASSERT_EQUAL(40000ULL, chapterAtoms[0].timeEnd());
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chapterAtoms[0].displayList().size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Chapter 1"), chapterAtoms[0].displayList()[0].string());
|
||||
CPPUNIT_ASSERT_EQUAL(String("eng"), chapterAtoms[0].displayList()[0].language());
|
||||
CPPUNIT_ASSERT_EQUAL(2ULL, chapterAtoms[1].uid());
|
||||
CPPUNIT_ASSERT_EQUAL(false, chapterAtoms[1].isHidden());
|
||||
CPPUNIT_ASSERT_EQUAL(40000ULL, chapterAtoms[1].timeStart());
|
||||
CPPUNIT_ASSERT_EQUAL(80000ULL, chapterAtoms[1].timeEnd());
|
||||
CPPUNIT_ASSERT_EQUAL(2U, chapterAtoms[1].displayList().size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Chapter 2"), chapterAtoms[1].displayList()[0].string());
|
||||
CPPUNIT_ASSERT_EQUAL(String("eng"), chapterAtoms[1].displayList()[0].language());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Kapitel 2"), chapterAtoms[1].displayList()[1].string());
|
||||
CPPUNIT_ASSERT_EQUAL(String("deu"), chapterAtoms[1].displayList()[1].language());
|
||||
CPPUNIT_ASSERT_EQUAL(3ULL, chapterAtoms[2].uid());
|
||||
CPPUNIT_ASSERT_EQUAL(true, chapterAtoms[2].isHidden());
|
||||
CPPUNIT_ASSERT_EQUAL(80000ULL, chapterAtoms[2].timeStart());
|
||||
CPPUNIT_ASSERT_EQUAL(120000ULL, chapterAtoms[2].timeEnd());
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chapterAtoms[2].displayList().size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Chapter 3"), chapterAtoms[2].displayList()[0].string());
|
||||
CPPUNIT_ASSERT_EQUAL(String("und"), chapterAtoms[2].displayList()[0].language());
|
||||
|
||||
CPPUNIT_ASSERT(f.setComplexProperties("CHAPTERS", {chapterEdition2}));
|
||||
CPPUNIT_ASSERT(f.save());
|
||||
}
|
||||
{
|
||||
Matroska::File f(newname.c_str(), true, AudioProperties::Accurate);
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT(f.tag(false));
|
||||
CPPUNIT_ASSERT(!f.attachments(false));
|
||||
auto chapters = f.chapters(false);
|
||||
CPPUNIT_ASSERT(chapters);
|
||||
CPPUNIT_ASSERT_EQUAL(StringList({"DURATION", "CHAPTERS"}), f.complexPropertyKeys());
|
||||
auto chaptersProperties = f.complexProperties("CHAPTERS");
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chaptersProperties.size());
|
||||
CPPUNIT_ASSERT_EQUAL(chapterEdition2, chaptersProperties.front());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chapters->chapterEditionList().size());
|
||||
const auto &edition = chapters->chapterEditionList().front();
|
||||
CPPUNIT_ASSERT_EQUAL(false, edition.isDefault());
|
||||
CPPUNIT_ASSERT_EQUAL(true, edition.isOrdered());
|
||||
CPPUNIT_ASSERT_EQUAL(321ULL, edition.uid());
|
||||
const auto &chapterAtoms = edition.chapterList();
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chapterAtoms.size());
|
||||
CPPUNIT_ASSERT_EQUAL(1234567890ULL, chapterAtoms[0].uid());
|
||||
CPPUNIT_ASSERT_EQUAL(false, chapterAtoms[0].isHidden());
|
||||
CPPUNIT_ASSERT_EQUAL(10000ULL, chapterAtoms[0].timeStart());
|
||||
CPPUNIT_ASSERT_EQUAL(0ULL, chapterAtoms[0].timeEnd());
|
||||
CPPUNIT_ASSERT_EQUAL(1U, chapterAtoms[0].displayList().size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Chapter A"), chapterAtoms[0].displayList()[0].string());
|
||||
CPPUNIT_ASSERT_EQUAL(String(), chapterAtoms[0].displayList()[0].language());
|
||||
|
||||
const Matroska::ChapterEdition edition2 = chapters->chapterEditionList().front();
|
||||
chapters->removeChapterEdition(321ULL);
|
||||
chapters->addChapterEdition(edition1);
|
||||
chapters->addChapterEdition(edition2);
|
||||
|
||||
Matroska::AttachedFile attachedFile;
|
||||
attachedFile.setFileName("folder.png");
|
||||
attachedFile.setMediaType("image/png");
|
||||
attachedFile.setDescription("Cover");
|
||||
attachedFile.setData(ByteVector("PNG data"));
|
||||
attachedFile.setUID(1763187649ULL);
|
||||
f.attachments(true)->addAttachedFile(attachedFile);
|
||||
CPPUNIT_ASSERT(f.save());
|
||||
}
|
||||
{
|
||||
Matroska::File f(newname.c_str(), true, AudioProperties::Accurate);
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT(f.tag(false));
|
||||
CPPUNIT_ASSERT(f.attachments(false));
|
||||
CPPUNIT_ASSERT(f.chapters(false));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(StringList({"DURATION", "PICTURE", "CHAPTERS"}),
|
||||
f.complexPropertyKeys());
|
||||
auto chaptersProperties = f.complexProperties("CHAPTERS");
|
||||
CPPUNIT_ASSERT_EQUAL(2U, chaptersProperties.size());
|
||||
CPPUNIT_ASSERT_EQUAL(chapterEdition1, chaptersProperties.front());
|
||||
CPPUNIT_ASSERT_EQUAL(chapterEdition2, chaptersProperties.back());
|
||||
|
||||
f.attachments()->clear();
|
||||
f.chapters()->clear();
|
||||
CPPUNIT_ASSERT(f.save());
|
||||
}
|
||||
{
|
||||
Matroska::File f(newname.c_str(), true, AudioProperties::Accurate);
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT(f.tag(false));
|
||||
CPPUNIT_ASSERT(!f.attachments(false));
|
||||
}
|
||||
|
||||
// Check if file with initial tags is same as original file
|
||||
const ByteVector origData = PlainFile(TEST_FILE_PATH_C("tags-before-cues.mkv")).readAll();
|
||||
const ByteVector fileData = PlainFile(newname.c_str()).readAll();
|
||||
CPPUNIT_ASSERT(origData == fileData);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestMatroska);
|
||||
|
||||
@ -158,6 +158,7 @@
|
||||
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskachapters.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
@ -312,6 +313,7 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(classSize(3, true), sizeof(TagLib::Matroska::Tag));
|
||||
CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::Matroska::Element));
|
||||
CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Matroska::Attachments));
|
||||
CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Matroska::Chapters));
|
||||
CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::Matroska::SimpleTag));
|
||||
CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::Matroska::AttachedFile));
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user