From af325db8ab3f76dfcb74244c242ad2575697764f Mon Sep 17 00:00:00 2001 From: Martchus Date: Mon, 20 Dec 2021 23:22:57 +0100 Subject: [PATCH] Improve "Usage" documentation * Move example code to a different file and build it as part of the tests to make sure it actually compiles * Add notes about the CMake find module and pkg-config module --- CMakeLists.txt | 3 +- README.md | 83 ++++------------------------------------------ doc/example.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 78 deletions(-) create mode 100644 doc/example.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 618c17e..b0ff513 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,7 +174,8 @@ set(TEST_SRC_FILES tests/overallogg.cpp tests/tagvalue.cpp tests/testfilecheck.cpp - tests/utils.cpp) + tests/utils.cpp + doc/example.cpp) set(DOC_FILES README.md doc/adding-new-fields.md) set(LANGUAGE_HEADER_ISO_639_2 "${CMAKE_CURRENT_BINARY_DIR}/resources/iso_language_codes.h") set(RES_FILES "${LANGUAGE_HEADER_ISO_639_2}") diff --git a/README.md b/README.md index 81df22c..e6cfc51 100644 --- a/README.md +++ b/README.md @@ -40,83 +40,12 @@ It also allows to inspect and validate the element structure of MP4 and Matroska The library is aware of different text encodings and can convert between different encodings using iconv. ## Usage -This example shows how to read and write tag fields in a format-independent way: - -``` -#include -#include -#include - -// create a MediaFileInfo for high-level access to overall functionality of the library -auto fileInfo = MediaFileInfo(); - -// create container for errors, warnings, etc. -auto diag = Diagnostics(); - -// create handle to abort gracefully and get feedback during during long operations -auto progress = AbortableProgressFeedback([callback for status update], [callback for percentage-only updates]); - -// open file (might throw ios_base::failure) -fileInfo.setPath("/path/to/some/file"); -fileInfo.open(); - -// parse container format, tags, attachments and/or chapters as needed -// notes: -// - These functions might throw exceptions derived from ios_base::failure for IO errors and -// populate diag with possibly critical parsing messages you definitely want to check in production -// code. -// - Parsing a file can be expensive if the file is big or the disk IO is slow. You might want to -// run it in a separate thread. -// - At this point the parser does not make much use of the progress object. -fileInfo.parseContainerFormat(diag, progress); -fileInfo.parseTags(diag, progress); -fileInfo.parseAttachments(diag, progress); -fileInfo.parseChapters(diag, progress); -fileInfo.parseEverything(diag, progress); // just use that one if you want all over the above - -// get tag as an object derived from the Tag class -// notes: -// - In real code you might want to check how many tags are assigned or use -// fileInfo.createAppropriateTags(…) to create tags as needed. -auto tag = fileInfo.tags().at(0); - -// extract a field value and convert it to UTF-8 std::string (toString() might throw ConversionException) -#include -#include -auto title = tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8); - -// change a field value using an encoding suitable for the tag format -tag->setValue(KnownField::Album, TagValue("some UTF-8 string", TagTextEncoding::Utf8, tag->proposedTextEncoding())); - -// get/remove/create attachments -#include -if (auto *const container = fileInfo.container()) { - for (auto i = 0, count = container->attachmentCount(); i != count; ++i) { - auto attachment = container->attachment(i); - if (attachment->mimeType() == "image/jpeg") { - attachment->setIgnored(true); // remove existing attachment - } - } - // create new attachment - auto attachment = container->createAttachment(); - attachment->setName("cover.jpg"); - attachment->setFile(cover, diag); -} - -// apply changes to the file on disk -// notes: -// - Might throw exception derived from TagParser::Failure for fatal processing error or ios_base::failure -// for IO errors. -// - Applying changes can be expensive if the file is big or the disk IO is slow. You might want to -// run it in a separate thread. -// - Use progress.tryToAbort() from another thread or an interrupt handler to abort gracefully without leaving -// the file in an inconsistent state. -// - Be sure everything has been parsed before as the library needs to be aware of the whole file structure. -fileInfo.parseEverything(diag, progress); -fileInfo.applyChanges(diag, progress); -``` - -### Summary +* For building/installing the library, checkout the build instructions below. +* To use the library with CMake, use its find module (e.g. `find_package(tagparser 10.1.0 REQUIRED)`) which + provides the imported target `tagparser` you can link against. Otherwise, use the `pkg-config` module + `tagparser` to query the required compiler/linker flags. +* For a code example that shows how to read and write tag fields in a format-independent way, have + a look at [`example.cpp`](doc/example.cpp). * The most important class is `TagParser::MediaFileInfo` providing access to everything else. * IO errors are propagated via standard `std::ios_base::failure`. * Fatal processing errors are propagated by throwing a class derived from `TagParser::Failure`. diff --git a/doc/example.cpp b/doc/example.cpp new file mode 100644 index 0000000..2effe6e --- /dev/null +++ b/doc/example.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include + +/*! + * \brief An example for reading and writing tags in a format-independent way. + * \sa Checkout the README's "Usage" section for further explanations. + */ +void example() +{ + using namespace std::literals; + using namespace TagParser; + + // create a MediaFileInfo for high-level access to overall functionality of the library + auto fileInfo = MediaFileInfo(); + + // create container for errors, warnings, etc. + auto diag = Diagnostics(); + + // create handle to abort gracefully and get feedback during during long operations + auto progress = AbortableProgressFeedback([] (AbortableProgressFeedback &feedback) { + // callback for status update + std::clog << "At step: " << feedback.step() << '\n'; + }, [] (AbortableProgressFeedback &feedback) { + // callback for percentage-only updates + std::clog << "Step percentage: " << feedback.stepPercentage() << '\n'; + }); + + // open file (might throw ios_base::failure) + fileInfo.setPath("/path/to/some/file"sv); + fileInfo.open(); + + // parse container format, tags, attachments and/or chapters as needed + // notes: + // - These functions might throw exceptions derived from ios_base::failure for IO errors and + // populate diag with possibly critical parsing messages you definitely want to check in production + // code. + // - Parsing a file can be expensive if the file is big or the disk IO is slow. You might want to + // run it in a separate thread. + // - At this point the parser does not make much use of the progress object. + fileInfo.parseContainerFormat(diag, progress); + fileInfo.parseTags(diag, progress); + fileInfo.parseAttachments(diag, progress); + fileInfo.parseChapters(diag, progress); + fileInfo.parseEverything(diag, progress); // just use that one if you want all over the above + + // get tag as an object derived from the Tag class + // notes: + // - In real code you might want to check how many tags are assigned or use + // fileInfo.createAppropriateTags(…) to create tags as needed. + auto tag = fileInfo.tags().at(0); + + // extract a field value and convert it to UTF-8 std::string (toString() might throw ConversionException) + auto title = tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8); + + // change a field value using an encoding suitable for the tag format + tag->setValue(KnownField::Album, TagValue("some UTF-8 string", TagTextEncoding::Utf8, tag->proposedTextEncoding())); + + // get/remove/create attachments + if (auto *const container = fileInfo.container()) { + for (std::size_t i = 0, count = container->attachmentCount(); i != count; ++i) { + auto attachment = container->attachment(i); + if (attachment->mimeType() == "image/jpeg") { + attachment->setIgnored(true); // remove existing attachment + } + } + // create new attachment + auto attachment = container->createAttachment(); + attachment->setName("The cover"); + attachment->setFile("cover.jpg", diag, progress); + } + + // apply changes to the file on disk + // notes: + // - Might throw exception derived from TagParser::Failure for fatal processing error or ios_base::failure + // for IO errors. + // - Applying changes can be expensive if the file is big or the disk IO is slow. You might want to + // run it in a separate thread. + // - Use progress.tryToAbort() from another thread or an interrupt handler to abort gracefully without leaving + // the file in an inconsistent state. + // - Be sure everything has been parsed before as the library needs to be aware of the whole file structure. + fileInfo.parseEverything(diag, progress); + fileInfo.applyChanges(diag, progress); +} +