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 }