Skip to content
Extraits de code Groupes Projets

Comparer les révisions

Les modifications sont affichées comme si la révision source était fusionnée avec la révision cible. En savoir plus sur la comparaison des révisions.

Source

Sélectionner le projet cible
No results found
Sélectionner une révision Git
  • devel
  • fix-mpv
  • master
  • new-devel
  • script
  • timingView-edit
6 résultats

Cible

Sélectionner le projet cible
  • Elliu/Vivy
1 résultat
Sélectionner une révision Git
  • devel
  • fix-mpv
  • master
  • new-devel
  • script
  • timingView-edit
6 résultats
Afficher les modifications
Validations sur la source (29)
Affichage de
avec 516 ajouts et 244 suppressions
......@@ -10,10 +10,6 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
# C++20, at least we try
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Pthread ftw
set(THREADS_PREFER_PTHREAD_FLAG ON)
......@@ -59,20 +55,38 @@ target_link_libraries(Vivy PRIVATE lua)
# Headers related things
target_include_directories(Vivy PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc)
target_precompile_headers(Vivy
PRIVATE
target_precompile_headers(Vivy PRIVATE
# Private Vivy headers
${Vivy_INC}
# STL headers
<memory>
<vector>
# Qt headers
<QString>
<QList>
<QVector>
<QMap>
<QWidget>
<QIcon>
)
# STL headers
<memory>
# Set Vivy's needed C++ features
target_compile_features(Vivy PRIVATE
cxx_std_20
cxx_auto_type
cxx_deleted_functions
cxx_explicit_conversions
cxx_final
cxx_inline_namespaces
cxx_lambdas
cxx_noexcept
cxx_nonstatic_member_init
cxx_nullptr
cxx_override
cxx_range_for
cxx_strong_enums
)
# More options and warnings
......@@ -90,7 +104,9 @@ if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
-Wno-unused-private-field # Skip the unused private fields for now
-fopenmp # We do OpenMP here
)
target_link_libraries(Vivy PRIVATE -fopenmp)
target_link_libraries(Vivy PRIVATE
-fopenmp
)
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
target_compile_options(Vivy PRIVATE -fopenmp)
target_link_libraries(Vivy PRIVATE -fopenmp)
......
#ifndef VIVY_ASS_ASS_H
#define VIVY_ASS_ASS_H
#include "Char.hh"
#include "Line.hh"
#include "Syl.hh"
#include "Style.hh"
#include "AssPrivate.hh"
#include "AssFactory.hh"
#include "StyleProperties.hh"
#include <memory.h>
namespace Vivy::Ass
{
using StylePtr = AssFactory::StylePtr;
using LinePtr = AssFactory::LinePtr;
using StyleWeakPtr = AssFactory::StyleWeakPtr;
using LineWeakPtr = AssFactory::LineWeakPtr;
}
#endif // VIVY_ASS_ASS_H
#include "AssFactory.hh"
#include <algorithm>
#include <stdexcept>
using namespace Vivy::Ass;
......@@ -10,17 +11,19 @@ AssFactory::initFromStorage() noexcept
QTextStream in(&diskStorage);
QString currentSection{};
quint64 lineIndex = 0;
QStringList stylesContent{};
QStringList eventsContent{};
while (!in.atEnd()) {
const QString line = in.readLine().trimmed();
/* Dectect comment */
// Dectect comment
if (line.startsWith(";") || line.isEmpty()) {
lineIndex++;
continue;
}
/* Dectect sections */
// Dectect sections
else if (line.startsWith("[") && line.endsWith("]")) {
currentSection = line.mid(1, line.size() - 2);
qDebug() << "Parsing section" << currentSection;
......@@ -30,31 +33,45 @@ AssFactory::initFromStorage() noexcept
}
}
/* Other lines */
// Other lines
else if (!currentSection.isEmpty()) {
const int separatorIndex = line.indexOf(": ");
const int baseValueIndex = separatorIndex + 2;
if (separatorIndex < 0) {
// Easy way to see if the line was invalid
if (separatorIndex < 0)
qWarning() << "Invalid line #" << lineIndex << ":" << line;
} else {
assContent[currentSection].insert(line.mid(0, separatorIndex),
line.mid(baseValueIndex));
// Script's info
else if (currentSection == sectionScriptInfo) {
assInfo.insert(line.mid(0, separatorIndex), line.mid(baseValueIndex));
qDebug() << "Got line #" << lineIndex << ":" << line;
}
// Skip the headers and the comment lines
else if ((currentSection == sectionStyles) && line.startsWith("Style: ")) {
stylesContent.append(line);
} else if ((currentSection == sectionEvents) && line.startsWith("Dialogue: ")) {
eventsContent.append(line);
} else if ((currentSection == sectionEvents) && line.startsWith("Comment: ")) {
eventsContent.append(line);
}
}
lineIndex++;
}
/* Construct the styles and the lines */
for (const auto &styleLine : assContent[sectionStyles])
assStyles.push_back(std::make_shared<Style>(styleLine.toString()));
for (const auto &assLine : assContent[sectionEvents])
assLines.push_back(std::make_shared<Line>(assLine.toString()));
// Construct the styles and the lines
try {
for (const auto &styleLine : stylesContent)
assStyles.push_back(std::make_shared<Style>(styleLine));
for (const auto &assLine : eventsContent)
assLines.push_back(std::make_shared<Line>(this, assLine));
} catch (const std::runtime_error &e) {
qCritical() << "Failed to create ASS style or events with error:" << e.what();
}
/* Get back some memory */
assContent.remove(sectionStyles);
assContent.remove(sectionEvents);
qDebug() << "Got" << assLines.size() << "ASS dialog lines";
return true;
}
......@@ -62,28 +79,32 @@ AssFactory::initFromStorage() noexcept
bool
AssFactory::checkValidity() const noexcept
{
if (assContent.isEmpty()) {
if (assInfo.isEmpty()) {
qCritical() << "Empty info section";
return false;
}
const SectionContent &scriptInfo = assContent[sectionScriptInfo];
/* Check for fields that must be integers */
bool ok = false;
SectionContent::const_iterator it = scriptInfo.begin();
const SectionContent::const_iterator end = scriptInfo.end();
// Check for fields that must be integers
SectionContent::const_iterator it = assInfo.begin();
const SectionContent::const_iterator end = assInfo.end();
while (it != end) {
if (intTypeFields.contains(it.key()) && (it.value().toInt(&ok), !ok))
bool ok = false;
if (intTypeFields.contains(it.key()) && (it.value().toInt(&ok), !ok)) {
qCritical() << it.key() << "is not an integer:" << it.value();
return false;
}
++it;
}
/* Check for fixed values fields */
// Check for fixed values fields
for (const auto &fixedValues : checkedValues) {
if (const auto &first = fixedValues.first;
scriptInfo.contains(first) && scriptInfo[first] != fixedValues.second)
assInfo.contains(first) && assInfo[first] != fixedValues.second) {
qCritical() << "Invalid" << first << "as it should be equal to" << fixedValues.second
<< "but was" << assInfo[first];
return false;
}
}
return true;
}
......@@ -102,7 +123,7 @@ AssFactory::AssFactory(const QString &fileName)
}
void
AssFactory::getStyles(QVector<AssFactory::StylePtr> &ret) const noexcept
AssFactory::getStyles(QVector<StylePtr> &ret) const noexcept
{
ret.clear();
for (const auto &style : assStyles)
......@@ -110,7 +131,7 @@ AssFactory::getStyles(QVector<AssFactory::StylePtr> &ret) const noexcept
}
void
AssFactory::getLines(QVector<AssFactory::LinePtr> &ret) const noexcept
AssFactory::getLines(QVector<LinePtr> &ret) const noexcept
{
ret.clear();
for (const auto &line : assLines)
......@@ -120,5 +141,30 @@ AssFactory::getLines(QVector<AssFactory::LinePtr> &ret) const noexcept
AssFactory::SectionContent
AssFactory::getInfoSection() const noexcept
{
return assContent[sectionScriptInfo];
return assInfo;
}
StyleWeakPtr
AssFactory::getStyle(const QString &name) const noexcept
{
auto findByName = [&name](const StylePtr &style) noexcept -> bool {
return style->getElementName() == name;
};
auto styleIt = std::find_if(std::begin(assStyles), std::end(assStyles), findByName);
if (styleIt != std::end(assStyles))
return StyleWeakPtr(*styleIt);
// Will be unable to lock
return StyleWeakPtr(spareNullStylePtr);
}
bool
AssFactory::hasStyle(const QString &name) const noexcept
{
for (const auto &stylePtr : assStyles) {
if (stylePtr->getElementName() == name)
return true;
}
return false;
}
......@@ -4,16 +4,16 @@
#include "../Utils.hh"
#include "Style.hh"
#include "Line.hh"
#include "AssPrivate.hh"
#include <memory.h>
#include <memory>
#include <QFile>
#include <QMap>
#include <QVariant>
#include <QString>
/* An ASS file is basically an INI file, but some keys are present multiple
* times (V4+ Styles/Style, Events/Dialogue, Events/Comment).
*/
// An ASS file is basically an INI file, but some keys are present multiple
// times (V4+ Styles/Style, Events/Dialogue, Events/Comment).
namespace Vivy::Ass
{
......@@ -27,29 +27,27 @@ public:
Events = 3,
};
using LinePtr = std::shared_ptr<Line>;
using LineWeakPtr = std::weak_ptr<Line>;
using StylePtr = std::shared_ptr<Style>;
using StyleWeakPtr = std::weak_ptr<Style>;
using SectionContent = QMap<QString, QVariant>;
private:
QFile diskStorage;
QMap<QString, SectionContent> assContent{};
SectionContent assInfo{};
QVector<LinePtr> assLines{};
QVector<StylePtr> assStyles{};
StylePtr spareNullStylePtr{ nullptr };
static inline const QString sectionScriptInfo = "Script Info";
static inline const QString sectionStyles = "V4+ Styles";
static inline const QString sectionEvents = "Events";
static inline const QStringList validSections = { sectionEvents, sectionScriptInfo,
sectionStyles };
static inline const QStringList intTypeFields = { "PlayResX", "PlayResY", "WrapStyle" };
static inline const QList<QPair<QString, QString>> checkedValues = {
static inline const QStringList intTypeFields{ "PlayResX", "PlayResY", "WrapStyle" };
static inline const QVector<QPair<QString, QString>> checkedValues{
{ "ScriptType", "v4.00+" },
{ "WrapStyle", "0" },
{ "YCbCr Matrix", "TV.601" },
// { "YCbCr Matrix", "TV.601" },
{ "ScaledBorderAndShadow", "yes" }
};
......@@ -60,6 +58,9 @@ public:
explicit AssFactory(const QString &);
~AssFactory() noexcept = default;
bool hasStyle(const QString &) const noexcept;
StyleWeakPtr getStyle(const QString &) const noexcept;
SectionContent getInfoSection() const noexcept;
void getStyles(QVector<StylePtr> &) const noexcept;
void getLines(QVector<LinePtr> &) const noexcept;
......
#pragma once
#include <memory>
namespace Vivy::Ass
{
class Style;
class Line;
using StylePtr = std::shared_ptr<Style>;
using LinePtr = std::shared_ptr<Line>;
using StyleWeakPtr = std::weak_ptr<Style>;
using LineWeakPtr = std::weak_ptr<Line>;
}
#include "Char.hh"
#include "Syl.hh"
using namespace Vivy::Ass;
Char::Char(Syl *const syl, const QChar /*unused*/)
: parentLine(syl->parentLine)
, parentSyl(syl)
{
}
#ifndef VIVY_ASS_CHAR_H
#define VIVY_ASS_CHAR_H
#include <QChar>
#include <QtGlobal>
#include "StyleProperties.hh"
namespace Vivy::Ass
{
class Line;
class Syl;
class Char {
private:
QChar content{};
StyleProperties styleProperties;
quint64 dur{ 0 };
public:
Line *const parentLine;
Syl *const parentSyl;
public:
// Copy constructor
explicit Char(const Char &) = default;
explicit Char(Syl *const, const QChar);
Char &operator=(const Char &) = delete;
~Char() noexcept = default;
};
}
#endif // VIVY_ASS_CHAR_H
#include "Line.hh"
#include "AssFactory.hh"
#include <QRegularExpression>
using namespace Vivy::Ass;
Line::Line(const QString & /*unused*/)
Line::Line(AssFactory *const factory, const QString &lineString)
: assFactory(factory)
{
enum LineIndex : int {
Layer = 0, // int
Start = 1, // time
End = 2, // time
Style = 3, // text
Name = 4, // text
MarginL = 5, // int
MarginR = 6, // int
MarginV = 7, // int
Effect = 8, // text
Text = 9, // text
PastLastCode
// NOTE: time is of the form: `h:mm:ss.cc`
};
static const QString lineHeader = "Dialogue: ";
isComment = !lineString.startsWith(lineHeader);
const QString lineContent = lineString.mid(lineString.indexOf(": ") + 2 /* strlen ": " */);
QStringList contentList = lineContent.split(",", Qt::KeepEmptyParts, Qt::CaseInsensitive);
if (contentList.size() < LineIndex::PastLastCode)
throw std::runtime_error(("invalid number of items " + QString::number(contentList.size()) +
" instead of something superiror or equal to " +
QString::number(PastLastCode) + " for line: " + lineContent)
.toStdString());
layer = Utils::decodeLineToInteger(contentList[LineIndex::Layer], "Layer is not an integer");
effect = contentList[LineIndex::Effect];
nameOrActor = contentList[LineIndex::Name];
start = Utils::Time::fromString(contentList[LineIndex::Start]).toUInt();
end = Utils::Time::fromString(contentList[LineIndex::End]).toUInt();
styleProperties.marginL =
Utils::decodeLineToInteger(contentList[LineIndex::MarginL], "MarginL is not an integer");
styleProperties.marginR =
Utils::decodeLineToInteger(contentList[LineIndex::MarginR], "MarginR is not an integer");
styleProperties.marginV =
Utils::decodeLineToInteger(contentList[LineIndex::MarginV], "MarginV is not an integer");
const QString style = contentList[LineIndex::Style];
if (!assFactory->hasStyle(style))
throw std::runtime_error(("Invalid or not declared style name: " + style).toStdString());
lineStyle = assFactory->getStyle(style);
// Pop all but the text, it may contains `,` characters
for (int i = 0; i < LineIndex::Text; ++i)
contentList.removeFirst();
___contentAsText = contentList.join("");
initSylFromString(___contentAsText);
}
void
Line::initSylFromString(const QString &line) noexcept
{
// Matches syllabes like: `{\toto}{\alpha&HFF}content`
QRegularExpression re("((?:{[^}]*})+[^{]*)");
if (!re.isValid())
qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
bool once = false;
try {
QRegularExpressionMatchIterator it = re.globalMatch(line);
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
content.append(Syl(this, match.captured(1)));
once |= true;
}
} catch (const std::runtime_error &e) {
qCritical() << "Failed to init syllabes with line:" << line;
qCritical() << "Fallback to all line is one syllabe";
once = false;
}
if (!once) {
content.clear();
content.append(Syl(this, line, Syl::ConstructMode::Raw));
}
}
void
......@@ -21,3 +105,33 @@ Line::setEnd(quint64 s) noexcept
if (start > s)
start = s;
}
quint64
Line::getDuration() const noexcept
{
return end - start;
}
StyleProperties
Line::getStyleProperties() const noexcept
{
return styleProperties;
}
StyleWeakPtr
Line::getStyle() const noexcept
{
return lineStyle;
}
const QVector<Syl> &
Line::getContent() const noexcept
{
return content;
}
bool
Line::getIsComment() const noexcept
{
return isComment;
}
......@@ -5,27 +5,47 @@
#include <QtGlobal>
#include "Syl.hh"
#include "StyleProperties.hh"
#include "Style.hh"
namespace Vivy::Ass
{
class Line {
class AssFactory;
class Line final {
private:
quint64 start{ 0 };
quint64 end{ 0 };
int layer{ 0 };
bool isComment{ true };
QVector<Syl> content{};
StyleProperties styleProperties;
StyleProperties styleProperties{};
QString effect{};
QString nameOrActor{};
StyleWeakPtr lineStyle;
QString ___contentAsText;
AssFactory *const assFactory;
public:
explicit Line() = default;
explicit Line(const Line &) = default;
explicit Line(const QString &);
explicit Line(AssFactory *const, const QString &);
Line &operator=(const Line &) = delete;
~Line() noexcept = default;
void setStart(quint64 s) noexcept;
void setEnd(quint64 s) noexcept;
quint64 getDuration() const noexcept;
bool getIsComment() const noexcept;
StyleProperties getStyleProperties() const noexcept;
StyleWeakPtr getStyle() const noexcept;
const QVector<Syl> &getContent() const noexcept;
private:
void initSylFromString(const QString &) noexcept;
};
}
......
#include "Style.hh"
#include <stdexcept>
#include <QJsonDocument>
#include <QJsonObject>
using namespace Vivy::Ass;
Style::Style(const QString &styleString)
{
/* Some usefull defines only needed in that function */
// Some usefull defines only needed in that function
enum StyleIndex : int {
Name = 0,
......@@ -34,111 +36,104 @@ Style::Style(const QString &styleString)
Shadow = 17,
Alignement = 18,
MarginL = 18,
MarginR = 19,
MarginV = 20,
MarginL = 19,
MarginR = 20,
MarginV = 21,
Encoding = 21,
Encoding = 22,
PastLastCode
};
static const QString lineHeader = "Style: ";
static const QString headerSeparator = ": ";
static constexpr int styleContentListSize = StyleIndex::PastLastCode;
/* Check line header and content items number */
// Check line header and content items number
if (!styleString.startsWith(lineHeader))
throw std::runtime_error("invalid style line header");
throw std::runtime_error(("invalid style line header: " + styleString).toStdString());
const QString lineContent = styleString.section(headerSeparator, 2, 2);
const QString lineContent = styleString.mid(styleString.indexOf(": ") + 2 /* strlen ": " */);
const QStringList content = lineContent.split(",", Qt::KeepEmptyParts, Qt::CaseInsensitive);
if (lineContent.size() != styleContentListSize)
throw std::runtime_error("invalid line content: invalid number of intems");
if (content.size() != StyleIndex::PastLastCode)
throw std::runtime_error(("invalid number of items " + QString::number(content.size()) +
" instead of " + QString::number(PastLastCode) +
" for line: " + lineContent)
.toStdString());
/* Unpack data from the line */
// Unpack data from the line
styleName = content[StyleIndex::Name];
fontName = content[StyleIndex::FontName];
fontSize = decodeLineToInteger(content[StyleIndex::FontSize], "FontSize is not an integer");
fontSize =
Utils::decodeLineToInteger(content[StyleIndex::FontSize], "FontSize is not an integer");
primaryColor = Color::fromString(content[StyleIndex::ColorPrimary]);
secondaryColor = Color::fromString(content[StyleIndex::ColorSecondary]);
outlineColor = Color::fromString(content[StyleIndex::ColorOutline]);
backColor = Color::fromString(content[StyleIndex::ColorBack]);
bold = decodeLineToBoolean(content[StyleIndex::Bold], "Bold is not a boolean");
italic = decodeLineToBoolean(content[StyleIndex::Italic], "Italic is not a boolean");
underline = decodeLineToBoolean(content[StyleIndex::Underline], "Underline is not a boolean");
strikeOut = decodeLineToBoolean(content[StyleIndex::StrikeOut], "StrikeOut is not a boolean");
scaleX = decodeLineToFloating(content[StyleIndex::ScaleX], "ScaleX is not a floating");
scaleY = decodeLineToFloating(content[StyleIndex::ScaleY], "ScaleY is not a floating");
spacing = decodeLineToFloating(content[StyleIndex::ScaleY], "Spacing is not a floating");
angle = decodeLineToFloating(content[StyleIndex::Angle], "Angle is not a floating");
borderStyle =
decodeLineToFloating(content[StyleIndex::BorderStyle], "BorderStyle is not a floating");
outline = decodeLineToFloating(content[StyleIndex::Outline], "Outline is not a floating");
shadow = decodeLineToFloating(content[StyleIndex::Shadow], "Shadow is not a floating");
alignment = decodeLineToInteger(content[StyleIndex::Shadow], "Alignement is not an integer");
bold = Utils::decodeLineToBoolean(content[StyleIndex::Bold], "Bold is not a boolean");
italic = Utils::decodeLineToBoolean(content[StyleIndex::Italic], "Italic is not a boolean");
underline =
Utils::decodeLineToBoolean(content[StyleIndex::Underline], "Underline is not a boolean");
strikeOut =
Utils::decodeLineToBoolean(content[StyleIndex::StrikeOut], "StrikeOut is not a boolean");
scaleX = Utils::decodeLineToFloating(content[StyleIndex::ScaleX], "ScaleX is not a floating");
scaleY = Utils::decodeLineToFloating(content[StyleIndex::ScaleY], "ScaleY is not a floating");
spacing = Utils::decodeLineToFloating(content[StyleIndex::ScaleY], "Spacing is not a floating");
angle = Utils::decodeLineToFloating(content[StyleIndex::Angle], "Angle is not a floating");
borderStyle = Utils::decodeLineToFloating(content[StyleIndex::BorderStyle],
"BorderStyle is not a floating");
outline =
Utils::decodeLineToFloating(content[StyleIndex::Outline], "Outline is not a floating");
shadow = Utils::decodeLineToFloating(content[StyleIndex::Shadow], "Shadow is not a floating");
alignment =
Utils::decodeLineToInteger(content[StyleIndex::Alignement], "Alignement is not an integer");
if (alignment < 1 || alignment > 9)
throw std::runtime_error(
"invalid line content: Alignement must be between 1 and 9 included");
("invalid line content: Alignement must be between 1 and 9 included, it was " +
QString::number(alignment))
.toStdString());
marginL = decodeLineToInteger(content[StyleIndex::MarginL], "MarginL is not an integer");
marginR = decodeLineToInteger(content[StyleIndex::MarginR], "MarginR is not an integer");
marginV = decodeLineToInteger(content[StyleIndex::MarginV], "MarginV is not an integer");
encoding = decodeLineToInteger(content[StyleIndex::Encoding], "Encoding is not an integer");
marginL = Utils::decodeLineToInteger(content[StyleIndex::MarginL], "MarginL is not an integer");
marginR = Utils::decodeLineToInteger(content[StyleIndex::MarginR], "MarginR is not an integer");
marginV = Utils::decodeLineToInteger(content[StyleIndex::MarginV], "MarginV is not an integer");
encoding =
Utils::decodeLineToInteger(content[StyleIndex::Encoding], "Encoding is not an integer");
if (encoding != 1)
qWarning() << "Encoding is not '1' in the ASS Style";
}
bool
Style::decodeLineToBoolean(const QString &item, const QString &error)
QString
Color::toString(const QColor &c) noexcept
{
return decodeLineToInteger(item, error) >= 1;
}
int
Style::decodeLineToInteger(const QString &item, const QString &error)
{
bool conversion = false;
int ret = item.toInt(&conversion);
if (!conversion)
throw std::runtime_error(("invalid line content: " + error).toStdString());
return ret;
}
float
Style::decodeLineToFloating(const QString &item, const QString &error)
{
bool conversion = false;
float ret = item.toFloat(&conversion);
if (!conversion)
throw std::runtime_error(("invalid line content: " + error).toStdString());
return ret;
return "&H" + QString::number(c.blue(), 16).toUpper() +
QString::number(c.green(), 16).toUpper() + QString::number(c.red(), 16).toUpper();
}
QColor
Color::fromString(const QString &colorString) noexcept
{
/* Ignore the '&H' at the begeing of the color if needed */
// Ignore the '&H' at the begeing of the color if needed
int startIndex = 0;
if (colorString.startsWith("&"))
if (colorString[startIndex] == '&')
startIndex++;
if (colorString.startsWith("H") || colorString.startsWith("h"))
if (colorString[startIndex] == 'H' || colorString[startIndex] == 'h')
startIndex++;
/* In some cases, the color can begin by a '#' */
if (colorString.startsWith("#"))
// In some cases, the color can begin by a '#'
if (colorString[startIndex] == '#')
startIndex++;
/* A valid string color is like 'AARRGGBB' for now (skipped 'aH') */
// A valid string color is like 'AARRGGBB' for now (skipped 'aH')
if (colorString.size() - startIndex != 8) {
qCritical()
<< "Invalid color string: size - index_start =" << (colorString.size() - startIndex)
<< "| string =" << colorString.mid(startIndex);
qCritical() << "Found an invalid color string:" << colorString;
return Color::defaultValue;
}
......@@ -159,3 +154,54 @@ Color::fromString(const QString &colorString) noexcept
return QColor(red, green, blue, alpha);
}
QString
Style::getElementName() const noexcept
{
return styleName;
}
QJsonDocument
Style::getProperties() const noexcept
{
QJsonDocument ret;
QJsonObject styleFont{
{ "Font name", fontName }, { "Font size", fontSize }, { "Bold", bold },
{ "Italic", italic }, { "Underline", underline }, { "Strike Out", strikeOut },
};
QJsonObject styleColors{
{ "Primary", Color::toString(primaryColor) },
{ "Secondary", Color::toString(secondaryColor) },
{ "Outline", Color::toString(outlineColor) },
{ "Back", Color::toString(backColor) },
};
QJsonObject styleFontProperties{
{ "Scale X", static_cast<const double>(scaleX) },
{ "Scale Y", static_cast<const double>(scaleY) },
{ "Spacing", static_cast<const double>(spacing) },
{ "Angle", static_cast<const double>(angle) },
{ "Border Style", static_cast<const double>(borderStyle) },
{ "Outline", static_cast<const double>(outline) },
{ "Shadow", static_cast<const double>(shadow) },
};
QJsonObject styleMargins{
{ "Left", marginL },
{ "Right", marginR },
{ "Vertical", marginV },
};
QJsonObject object{ { "Name", styleName },
{ "Font", styleFont },
{ "Font properties", styleFontProperties },
{ "Colors", styleColors },
{ "Margin", styleMargins },
{ "Alignement", alignment },
{ "Encoding", encoding } };
ret.setObject(object);
return ret;
}
......@@ -5,12 +5,15 @@
#include <QVector>
#include <QtGlobal>
#include <QColor>
#include <QObject>
#include "AssPrivate.hh"
namespace Vivy::Ass
{
namespace Color
{
QColor fromString(const QString &) noexcept;
QString toString(const QColor &) noexcept;
static inline const QColor defaultValue = QColor(0, 0, 0, 0);
};
......@@ -41,10 +44,8 @@ public:
Style &operator=(const Style &) = delete;
~Style() noexcept = default;
private:
static bool decodeLineToBoolean(const QString &item, const QString &error);
static int decodeLineToInteger(const QString &item, const QString &error);
static float decodeLineToFloating(const QString &item, const QString &error);
QString getElementName() const noexcept;
QJsonDocument getProperties() const noexcept;
};
}
......
......@@ -7,15 +7,14 @@
namespace Vivy::Ass
{
/* Overrides some properties of the Style of an Ass::Line, Ass::Char, Ass::Syl
*/
// Overrides some properties of the Style of an Ass::Line, Ass::Char, Ass::Syl
struct StyleProperties final {
// Colors
QColor primaryColor{ Color::defaultValue }, secondaryColor{ Color::defaultValue },
outlineColor{ Color::defaultValue }, backColor{ Color::defaultValue };
QString fontName; // Can override the font's name
QString fontName{}; // Can override the font's name
int fontSize{}; // The font size != the font scaling
bool bold{ false }, italic{ false }, underline{ false }, strikeOut{ false };
......
#include "Syl.hh"
#include "Line.hh"
#include <QRegularExpression>
using namespace Vivy::Ass;
Syl::Syl(Line *const line, const QString & /*unused*/)
: parentLine(line)
Syl::Syl(Line *const line, const QString &lineString, ConstructMode mode) noexcept
: content(lineString)
, styleProperties(line->getStyleProperties())
, duration(line->getDuration())
, parentLine(line)
{
// Will override the `content`, but will be heavy anyway
if (mode == ConstructMode::ReadAssTags) {
const int textBegin = lineString.lastIndexOf('}') + 1;
content = (textBegin >= lineString.size()) ? "" : lineString.mid(textBegin);
duration = getDurationFromString(lineString);
}
}
quint64
Syl::getDurationFromString(const QString &line) noexcept
{
QRegularExpression re("\\\\(?:k|K|ko|kf)(\\d+)");
if (!re.isValid())
qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
quint64 duration = 0;
QRegularExpressionMatchIterator it = re.globalMatch(line);
while (it.hasNext())
duration += it.next().captured(1).toUInt();
return duration;
}
QString
Syl::getContent() const noexcept
{
return content;
}
#ifndef VIVY_SYL_H
#define VIVY_SYL_H
#include "Char.hh"
#include "StyleProperties.hh"
#include <QString>
#include <QVector>
......@@ -11,21 +10,32 @@ namespace Vivy::Ass
{
class Line;
class Syl {
class Syl final {
private:
QVector<Char> content;
QString content;
StyleProperties styleProperties;
quint64 dur{ 0 };
quint64 duration{ 0 };
public:
Line *const parentLine;
public:
explicit Syl(const Syl &) = default;
explicit Syl(Line *const, const QString &);
enum class ConstructMode {
Raw, // Don't read ASS tags
ReadAssTags, // Read ass tags
};
explicit Syl(const Syl &) noexcept = default;
explicit Syl(Line *const, const QString &,
ConstructMode mode = ConstructMode::ReadAssTags) noexcept;
Syl &operator=(const Syl &) = delete;
~Syl() noexcept = default;
QString getContent() const noexcept;
private:
static quint64 getDurationFromString(const QString &) noexcept;
};
}
......
......@@ -34,7 +34,6 @@ AudioContext::AudioContext(const QString &path)
AVCodecParameters *params = itFormat->codecpar;
AVCodec *streamCodec = avcodec_find_decoder(params->codec_id);
if (streamCodec && streamCodec->type == AVMEDIA_TYPE_AUDIO) {
audioStreamIndexes.push_back(i);
audioStreams.insert(i, std::make_shared<Stream>(streamCodec, formatPtr, itFormat, i));
}
}
......@@ -44,15 +43,12 @@ AudioContext::AudioContext(const QString &path)
-1, // Let AV find one stream
-1, // We don't want related streams
nullptr, 0);
qDebug() << "Opened audio context for" << path << "with duration" << formatPtr->duration;
if (defaultStreamIndex < 0) {
qCritical() << "Could not find the best audio stream";
}
// Get the number of audio streams in the audio context
int
AudioContext::getStreamCount() const noexcept
{
return audioStreamIndexes.size();
qDebug() << "Opened audio context for" << path << "with duration" << formatPtr->duration
<< "and default stream index" << defaultStreamIndex;
}
// Get a specific audio stream, try to lock the weak pointer. if the index was
......@@ -60,15 +56,14 @@ AudioContext::getStreamCount() const noexcept
AudioContext::StreamWeakPtr
AudioContext::getStream(int index) const noexcept
{
if (index >= audioStreamIndexes.size())
if (index < 0)
return StreamWeakPtr{ spareNullSreamPtr };
const uint streamIndex = audioStreamIndexes[index];
const auto found = audioStreams.find(streamIndex);
uint unsignedIndex = static_cast<uint>(index);
const auto found = audioStreams.find(unsignedIndex);
if (found != audioStreams.end()) {
if (found != audioStreams.end())
return StreamWeakPtr{ *found };
}
return StreamWeakPtr{ spareNullSreamPtr };
}
......@@ -94,7 +89,6 @@ QJsonDocument
AudioContext::getProperties() const noexcept
{
QJsonDocument ret;
QJsonObject self;
QJsonArray streams;
QFileInfo file(filePath);
......@@ -103,9 +97,7 @@ AudioContext::getProperties() const noexcept
streams.append(audioStreamPtr->getProperties());
}
self.insert("Streams", streams);
self.insert("File path", filePath);
self.insert("Base name", file.baseName());
QJsonObject self{ { "Streams", streams }, { "Base name", file.baseName() } };
ret.setObject(self);
return ret;
......@@ -157,9 +149,12 @@ AudioContext::Stream::Stream(AVCodec *streamCodec, AVFormatContext *format, AVSt
AudioContext::Stream::~Stream() noexcept
{
if (dataPtr)
if (dataPtr) {
qDebug() << "Free data ptr";
free(dataPtr);
}
qDebug() << "Delete stream object";
}
QJsonObject
AudioContext::Stream::getProperties() const noexcept
......
......@@ -127,7 +127,6 @@ public:
// The stream non-owning view pointer
using StreamWeakPtr = std::weak_ptr<Stream>;
int getStreamCount() const noexcept;
StreamWeakPtr getStream(int) const noexcept;
StreamWeakPtr getDefaultStream() const noexcept;
......@@ -145,7 +144,6 @@ private:
AVFormatContextPtr format{ avformat_alloc_context(), avFormatContextDeleter };
QString filePath; // Usefull information
QVector<uint> audioStreamIndexes{}; // Index all the audio streams of the file
QMap<uint, StreamPtr> audioStreams{}; // THe audio streams of the file
int defaultStreamIndex{ -1 };
......
......@@ -5,9 +5,14 @@
#error "This is a C++ header"
#endif
#include "../Utils.hh"
namespace Vivy
{
class AbstractDocument {
class AbstractDocument : public QObject {
Q_OBJECT
VIVY_UNMOVABLE_OBJECT(AbstractDocument)
protected:
AbstractDocument() = default;
virtual ~AbstractDocument() noexcept = default;
......@@ -15,6 +20,9 @@ protected:
public:
virtual bool rename(const QString &) noexcept = 0;
virtual QString getName() const noexcept = 0;
signals:
void documentChanged();
};
}
......
......@@ -25,17 +25,11 @@ AudioSubDocument::getDefaultStream() const noexcept
if (auto ptr = contextPtr->getDefaultStream().lock()) {
return ptr;
} else {
qCritical() << "Document deleted!";
return nullptr;
}
}
// Get the stream count, may be 0
int
AudioSubDocument::getStreamCount() const noexcept
{
return contextPtr->getStreamCount();
}
// Get the stream asked for, nullptr if no stream or if the index is invalid
AudioContext::StreamPtr
AudioSubDocument::getStream(int index) const noexcept
......@@ -68,9 +62,10 @@ QJsonDocument
AudioSubDocument::getProperties() const noexcept
{
QJsonDocument ret;
QJsonObject object;
const QJsonDocument contextDocument = contextPtr->getProperties();
object.insert("Audio context", contextDocument.object());
QJsonObject object{
{ "Audio context", contextPtr->getProperties().object() },
{ "File", filePath },
};
ret.setObject(object);
return ret;
}
......@@ -108,17 +103,39 @@ AssSubDocument::initFromPath(const QString &path)
factory.getStyles(styles);
factory.getLines(lines);
}
QString
AssSubDocument::getElementName() const noexcept
{
return "AssSubDocument";
}
const QVector<Ass::LinePtr> &
AssSubDocument::getLines() const noexcept
{
return lines;
}
const QVector<Ass::StylePtr> &
AssSubDocument::getStyles() const noexcept
{
return styles;
}
QJsonDocument
AssSubDocument::getProperties() const noexcept
{
QJsonDocument ret;
QJsonObject object;
QJsonObject styleObject;
for (const Ass::StylePtr &style : styles) {
styleObject.insert(style->getElementName(), style->getProperties().object());
}
QJsonObject object{
{ "Styles", styleObject },
{ "File", filePath },
};
ret.setObject(object);
return ret;
}
......@@ -41,7 +41,7 @@ public:
try {
ret->initFromPath(path); // May throw
} catch (const std::runtime_error &e) {
qDebug() << "Failed to init document from file" << path;
qDebug().nospace() << "Failed to init document from file " << path << ": " << e.what();
ret.reset();
}
......@@ -75,7 +75,6 @@ private:
public:
int getDefaultStreamIndex() const noexcept;
AudioContext::StreamPtr getDefaultStream() const noexcept;
int getStreamCount() const noexcept;
AudioContext::StreamPtr getStream(int index) const noexcept;
QString getElementName() const noexcept;
......@@ -110,6 +109,9 @@ public:
QString getElementName() const noexcept;
QJsonDocument getProperties() const noexcept;
const QVector<Ass::LinePtr> &getLines() const noexcept;
const QVector<Ass::StylePtr> &getStyles() const noexcept;
private:
QVector<Ass::StylePtr> styles;
QVector<Ass::LinePtr> lines;
......
......@@ -199,10 +199,8 @@ VivyDocument::setAudioSubDocument(const QString filename) noexcept
}
audioDocument = AudioSubDocument::fromFile(filename);
if (audioDocument) {
documentType |= Capabilities::AudioAble;
documentOptions &= ~Options::UntouchedByDefault;
}
if (audioDocument)
addDocumentType(AudioAble);
}
void
......@@ -218,10 +216,8 @@ VivyDocument::setVideoSubDocument(const QString filename) noexcept
}
videoDocument = VideoSubDocument::fromFile(filename);
if (videoDocument) {
documentType |= Capabilities::VideoAble;
documentOptions &= ~Options::UntouchedByDefault;
}
if (videoDocument)
addDocumentType(VideoAble);
}
void
......@@ -237,10 +233,16 @@ VivyDocument::setAssSubDocument(const QString filename) noexcept
}
assDocument = AssSubDocument::fromFile(filename);
if (assDocument) {
documentType |= Capabilities::AssAble;
documentOptions &= ~Options::UntouchedByDefault;
if (assDocument)
addDocumentType(AssAble);
}
void
VivyDocument::addDocumentType(Capabilities newCap) noexcept
{
documentType |= newCap;
documentOptions &= ~Options::UntouchedByDefault;
emit documentChanged();
}
QString
......