File indexing completed on 2024-04-14 04:46:51

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "profilerepository.hpp"
0007 #include "kdenlive_debug.h"
0008 #include "kdenlivesettings.h"
0009 #include "profilemodel.hpp"
0010 #include <KLocalizedString>
0011 #include <KMessageBox>
0012 #include <QDir>
0013 #include <QStandardPaths>
0014 #include <algorithm>
0015 #include <mlt++/MltProfile.h>
0016 
0017 std::unique_ptr<ProfileRepository> ProfileRepository::instance;
0018 std::once_flag ProfileRepository::m_onceFlag;
0019 std::vector<std::pair<int, QString>> ProfileRepository::colorProfiles{{601, QStringLiteral("ITU-R BT.601")},
0020                                                                       {709, QStringLiteral("ITU-R BT.709")},
0021                                                                       {240, QStringLiteral("SMPTE ST240")},
0022                                                                       {9, QStringLiteral("ITU-R BT.2020")},
0023                                                                       {10, QStringLiteral("ITU-R BT.2020")}};
0024 
0025 ProfileRepository::ProfileRepository()
0026 {
0027     refresh();
0028 }
0029 
0030 std::unique_ptr<ProfileRepository> &ProfileRepository::get()
0031 {
0032     std::call_once(m_onceFlag, [] { instance.reset(new ProfileRepository()); });
0033     return instance;
0034 }
0035 
0036 void ProfileRepository::refresh()
0037 {
0038     QWriteLocker locker(&m_mutex);
0039 
0040     // Helper function to check a profile and print debug info
0041     auto check_profile = [&](std::unique_ptr<ProfileModel> &profile, const QString &file) {
0042         if (m_profiles.count(file) > 0) {
0043             return false;
0044         }
0045         if (!profile->is_valid()) {
0046             qCWarning(KDENLIVE_LOG) << "//// WARNING: invalid profile found: " << file << ". Ignoring.";
0047             return false;
0048         }
0049         return true;
0050     };
0051 
0052     // list MLT profiles.
0053     QDir mltDir(KdenliveSettings::mltpath());
0054     QStringList profilesFiles = mltDir.entryList(QDir::Files);
0055 
0056     // list Custom Profiles
0057     QStringList customProfilesDir = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("profiles/"), QStandardPaths::LocateDirectory);
0058     for (const auto &dir : qAsConst(customProfilesDir)) {
0059         QStringList files = QDir(dir).entryList(QDir::Files);
0060         for (const auto &file : qAsConst(files)) {
0061             profilesFiles << QDir(dir).absoluteFilePath(file);
0062         }
0063     }
0064 
0065     // Iterate through files
0066     for (const auto &file : qAsConst(profilesFiles)) {
0067         std::unique_ptr<ProfileModel> profile(new ProfileModel(file));
0068         if (check_profile(profile, file)) {
0069             m_profiles.insert(std::make_pair(file, std::move(profile)));
0070         }
0071     }
0072 }
0073 
0074 QVector<QPair<QString, QString>> ProfileRepository::getAllProfiles() const
0075 {
0076     QReadLocker locker(&m_mutex);
0077 
0078     QVector<QPair<QString, QString>> list;
0079     for (const auto &profile : m_profiles) {
0080         list.push_back({profile.second->description(), profile.first});
0081     }
0082     std::sort(list.begin(), list.end());
0083     return list;
0084 }
0085 
0086 std::unique_ptr<ProfileModel> &ProfileRepository::getProfile(const QString &path)
0087 {
0088     QReadLocker locker(&m_mutex);
0089     if (m_profiles.count(path) == 0) {
0090         // qCWarning(KDENLIVE_LOG) << "//// WARNING: profile not found: " << path << ". Returning default profile instead.";
0091         QString default_profile = KdenliveSettings::default_profile();
0092         if (default_profile.isEmpty()) {
0093             default_profile = QStringLiteral("dv_pal");
0094         }
0095         if (m_profiles.count(default_profile) == 0) {
0096             qCWarning(KDENLIVE_LOG) << "//// WARNING: default profile not found: " << default_profile << ". Returning random profile instead.";
0097             return (*(m_profiles.begin())).second;
0098         }
0099         return m_profiles.at(default_profile);
0100     }
0101     return m_profiles.at(path);
0102 }
0103 
0104 bool ProfileRepository::profileExists(const QString &path) const
0105 {
0106     QReadLocker locker(&m_mutex);
0107     return m_profiles.count(path) != 0;
0108 }
0109 
0110 // static
0111 QString ProfileRepository::getColorspaceDescription(int colorspace)
0112 {
0113     // TODO: should the descriptions be translated?
0114     for (const auto &cs : colorProfiles) {
0115         if (cs.first == colorspace) return cs.second;
0116     }
0117     return i18n("Unknown");
0118 }
0119 
0120 // static
0121 int ProfileRepository::getColorspaceFromDescription(const QString &description)
0122 {
0123     for (const auto &cs : colorProfiles) {
0124         if (cs.second == description) return cs.first;
0125     }
0126     return 0;
0127 }
0128 
0129 QVector<double> ProfileRepository::getAllFps() const
0130 {
0131     QReadLocker locker(&m_mutex);
0132     QVector<double> res;
0133     for (const auto &ptr : m_profiles) {
0134         res.push_back(ptr.second->fps());
0135     }
0136     std::sort(res.begin(), res.end());
0137     res.erase(std::unique(res.begin(), res.end()), res.end());
0138     return res;
0139 }
0140 
0141 QString ProfileRepository::findMatchingProfile(ProfileInfo *profile) const
0142 {
0143     QReadLocker locker(&m_mutex);
0144     for (const auto &ptr : m_profiles) {
0145         if (*ptr.second.get() == *profile) {
0146             return ptr.first;
0147         }
0148     }
0149     return QString();
0150 }
0151 
0152 const QString ProfileRepository::saveProfile(ProfileInfo *profile, QString profilePath)
0153 {
0154     bool newProfile = false;
0155     if (profilePath.isEmpty()) {
0156         int i = 0;
0157         QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
0158         if (!dir.exists()) {
0159             dir.mkpath(QStringLiteral("."));
0160         }
0161         QString customName = QStringLiteral("customprofile");
0162         profilePath = dir.absoluteFilePath(customName + QString::number(i));
0163         while (QFile::exists(profilePath)) {
0164             ++i;
0165             profilePath = dir.absoluteFilePath(customName + QString::number(i));
0166         }
0167     }
0168     QFile file(profilePath);
0169     if (file.exists()) {
0170         newProfile = false;
0171     }
0172     if (!file.open(QIODevice::WriteOnly)) {
0173         KMessageBox::error(nullptr, i18n("Cannot open file %1", profilePath));
0174         return QString();
0175     }
0176     QTextStream out(&file);
0177     out << "description=" << profile->description() << '\n'
0178         << "frame_rate_num=" << profile->frame_rate_num() << '\n'
0179         << "frame_rate_den=" << profile->frame_rate_den() << '\n'
0180         << "width=" << profile->width() << '\n'
0181         << "height=" << profile->height() << '\n'
0182         << "progressive=" << static_cast<int>(profile->progressive()) << '\n'
0183         << "bottom_field_first=" << static_cast<int>(profile->bottom_field_first()) << '\n'
0184         << "sample_aspect_num=" << profile->sample_aspect_num() << '\n'
0185         << "sample_aspect_den=" << profile->sample_aspect_den() << '\n'
0186         << "display_aspect_num=" << profile->display_aspect_num() << '\n'
0187         << "display_aspect_den=" << profile->display_aspect_den() << '\n'
0188         << "colorspace=" << profile->colorspace() << '\n';
0189     if (file.error() != QFile::NoError) {
0190         KMessageBox::error(nullptr, i18n("Cannot write to file %1", profilePath));
0191         profilePath.clear();
0192     }
0193     file.close();
0194     if (!newProfile) {
0195         // We edited an existing profile, remove it to trigger a reload
0196         if (m_profiles.count(profilePath) > 0) {
0197             m_profiles.erase(profilePath);
0198         }
0199     }
0200     refresh();
0201     return profilePath;
0202 }
0203 
0204 bool ProfileRepository::deleteProfile(const QString &path)
0205 {
0206     bool success = false;
0207     if (path.contains(QLatin1Char('/'))) {
0208         success = QFile::remove(path);
0209     }
0210     if (!success) {
0211         qCDebug(KDENLIVE_LOG) << "//// Cannot delete profile " << path << ", does not seem to be custom one";
0212     } else {
0213         if (m_profiles.count(path) > 0) {
0214             // remove the stored profile
0215             m_profiles.erase(path);
0216         }
0217         refresh();
0218     }
0219     return success;
0220 }