File indexing completed on 2024-04-21 04:49:07
0001 /* 0002 * SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "application.h" 0008 0009 #include "actionsmodel.h" 0010 #include "audiosettings.h" 0011 #include "customcommandsmodel.h" 0012 #include "generalsettings.h" 0013 #include "global.h" 0014 #include "haruna-version.h" 0015 #include "informationsettings.h" 0016 #include "mousesettings.h" 0017 #include "mpvitem.h" 0018 #include "mpvpreview.h" 0019 #include "mpvproperties.h" 0020 #include "playbacksettings.h" 0021 #include "playlistmodel.h" 0022 #include "playlistsettings.h" 0023 #include "recentfilesmodel.h" 0024 #include "subtitlesfoldersmodel.h" 0025 #include "subtitlessettings.h" 0026 #include "tracksmodel.h" 0027 #include "videosettings.h" 0028 #include "worker.h" 0029 0030 #include <QApplication> 0031 #include <QCommandLineParser> 0032 #include <QDesktopServices> 0033 #include <QDir> 0034 #include <QFileInfo> 0035 #include <QFontDatabase> 0036 #include <QGuiApplication> 0037 #include <QMimeDatabase> 0038 #include <QQuickView> 0039 #include <QStandardPaths> 0040 #include <QStyle> 0041 #include <QStyleFactory> 0042 #include <QThread> 0043 0044 #include <KAboutData> 0045 #include <KColorSchemeManager> 0046 #include <KConfig> 0047 #include <KConfigGroup> 0048 #include <KFileItem> 0049 #include <KFileMetaData/Properties> 0050 #include <KLocalizedString> 0051 #include <KWindowConfig> 0052 #include <KWindowSystem> 0053 0054 Application *Application::instance() 0055 { 0056 static Application app; 0057 return &app; 0058 } 0059 0060 Application::Application() 0061 : m_app(qApp) 0062 , m_config(KSharedConfig::openConfig(Global::instance()->appConfigFilePath())) 0063 , m_schemes(new KColorSchemeManager(this)) 0064 , m_systemDefaultStyle(m_app->style()->objectName()) 0065 { 0066 // used to hide playlist when mouse leaves the application 0067 // while moving between monitors while in fullscreen 0068 auto *appEventFilter = new ApplicationEventFilter(); 0069 m_app->installEventFilter(appEventFilter); 0070 QObject::connect(appEventFilter, &ApplicationEventFilter::applicationMouseLeave, this, &Application::qmlApplicationMouseLeave); 0071 QObject::connect(appEventFilter, &ApplicationEventFilter::applicationMouseEnter, this, &Application::qmlApplicationMouseEnter); 0072 0073 if (GeneralSettings::guiStyle() != QStringLiteral("System")) { 0074 QApplication::setStyle(GeneralSettings::guiStyle()); 0075 } 0076 0077 setupWorkerThread(); 0078 setupAboutData(); 0079 setupCommandLineParser(); 0080 setupQmlSettingsTypes(); 0081 0082 connect(Global::instance(), &Global::error, this, &Application::error); 0083 connect(this, &Application::saveWindowGeometryAsync, Worker::instance(), &Worker::saveWindowGeometry, Qt::QueuedConnection); 0084 } 0085 0086 void Application::setupWorkerThread() 0087 { 0088 auto worker = Worker::instance(); 0089 auto thread = new QThread(); 0090 worker->moveToThread(thread); 0091 QObject::connect(thread, &QThread::finished, worker, &Worker::deleteLater); 0092 QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); 0093 thread->start(); 0094 } 0095 0096 void Application::setupAboutData() 0097 { 0098 KAboutData m_aboutData; 0099 m_aboutData.setComponentName(QStringLiteral("haruna")); 0100 m_aboutData.setDisplayName(i18nc("application title/display name", "Haruna Media Player")); 0101 m_aboutData.setVersion(Application::version().toUtf8()); 0102 m_aboutData.setShortDescription(i18nc("@title", "A configurable media player.")); 0103 m_aboutData.setLicense(KAboutLicense::GPL_V3); 0104 m_aboutData.setCopyrightStatement(i18nc("copyright statement", "(c) 2019-2023")); 0105 m_aboutData.setHomepage(QStringLiteral("https://haruna.kde.org")); 0106 m_aboutData.setBugAddress(QStringLiteral("https://bugs.kde.org/enter_bug.cgi?product=Haruna").toUtf8()); 0107 m_aboutData.setDesktopFileName(QStringLiteral("org.kde.haruna")); 0108 0109 m_aboutData.addAuthor(i18nc("@info:credit", "George Florea Bănuș"), 0110 i18nc("@info:credit", "Developer"), 0111 QStringLiteral("georgefb899@gmail.com"), 0112 QStringLiteral("https://georgefb.com")); 0113 0114 KAboutData::setApplicationData(m_aboutData); 0115 } 0116 0117 void Application::setupCommandLineParser() 0118 { 0119 m_parser = new QCommandLineParser(); 0120 m_aboutData.setupCommandLine(m_parser); 0121 m_parser->addPositionalArgument(QStringLiteral("file"), i18nc("@info:shell", "File to open")); 0122 0123 QCommandLineOption ytdlFormatSelectionOption(QStringList() << QStringLiteral("ytdl-format-selection") << QStringLiteral("ytdlfs"), 0124 i18nc("@info:shell", 0125 "Allows to temporarily override the ytdl format selection setting. " 0126 "Will be overwritten if the setting is changed through the GUI"), 0127 i18nc("@info:shell", "bestvideo+bestaudio/best"), 0128 QString()); 0129 m_parser->addOption(ytdlFormatSelectionOption); 0130 0131 m_parser->process(*m_app); 0132 m_aboutData.processCommandLine(m_parser); 0133 0134 for (auto i = 0; i < m_parser->positionalArguments().size(); ++i) { 0135 addUrl(i, m_parser->positionalArguments().at(i)); 0136 } 0137 } 0138 0139 void Application::setupQmlSettingsTypes() 0140 { 0141 qmlRegisterSingletonInstance("org.kde.haruna.mpvproperties", 1, 0, "MpvProperties", MpvProperties::self()); 0142 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "AudioSettings", AudioSettings::self()); 0143 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "GeneralSettings", GeneralSettings::self()); 0144 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "InformationSettings", InformationSettings::self()); 0145 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "MouseSettings", MouseSettings::self()); 0146 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "PlaybackSettings", PlaybackSettings::self()); 0147 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "PlaylistSettings", PlaylistSettings::self()); 0148 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "SubtitlesSettings", SubtitlesSettings::self()); 0149 qmlRegisterSingletonInstance("org.kde.haruna.settings", 1, 0, "VideoSettings", VideoSettings::self()); 0150 } 0151 0152 void Application::restoreWindowGeometry(QQuickWindow *window) const 0153 { 0154 if (!GeneralSettings::rememberWindowGeometry()) { 0155 return; 0156 } 0157 KConfig dataResource(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation); 0158 KConfigGroup windowGroup(&dataResource, QStringLiteral("Window")); 0159 KWindowConfig::restoreWindowSize(window, windowGroup); 0160 KWindowConfig::restoreWindowPosition(window, windowGroup); 0161 } 0162 0163 void Application::saveWindowGeometry(QQuickWindow *window) 0164 { 0165 Q_EMIT saveWindowGeometryAsync(window); 0166 } 0167 0168 bool Application::urlExists(const QUrl &url) 0169 { 0170 return pathExists(url.toLocalFile()); 0171 } 0172 0173 bool Application::pathExists(const QString &path) 0174 { 0175 QFileInfo fileInfo(path); 0176 if (fileInfo.exists()) { 0177 return true; 0178 } 0179 return false; 0180 } 0181 0182 QUrl Application::configFilePath() 0183 { 0184 auto file = Global::instance()->appConfigFilePath(); 0185 if (pathExists(file)) { 0186 return pathToUrl(file); 0187 } 0188 return QUrl(); 0189 } 0190 0191 QUrl Application::ccConfigFilePath() 0192 { 0193 auto file = Global::instance()->appConfigFilePath(Global::ConfigFile::CustomCommands); 0194 if (pathExists(file)) { 0195 return pathToUrl(file); 0196 } 0197 return QUrl(); 0198 } 0199 0200 QUrl Application::configFolderPath() 0201 { 0202 auto folder = Global::instance()->appConfigDirPath(); 0203 if (pathExists(folder)) { 0204 return pathToUrl(folder); 0205 } 0206 return QUrl(); 0207 } 0208 0209 QUrl Application::pathToUrl(const QString &path) 0210 { 0211 auto url = QUrl::fromUserInput(path); 0212 if (!url.isValid()) { 0213 return QUrl(); 0214 } 0215 return url; 0216 } 0217 0218 bool Application::configFolderExists() 0219 { 0220 QFileInfo fi(Global::instance()->appConfigDirPath()); 0221 return fi.exists(); 0222 } 0223 0224 QString Application::version() 0225 { 0226 return QStringLiteral(HARUNA_VERSION_STRING); 0227 } 0228 0229 bool Application::hasYoutubeDl() 0230 { 0231 return !youtubeDlExecutable().isEmpty(); 0232 } 0233 0234 QString Application::youtubeDlExecutable() 0235 { 0236 auto ytDlp = QStandardPaths::findExecutable(QStringLiteral("yt-dlp")); 0237 if (!ytDlp.isEmpty()) { 0238 return ytDlp; 0239 } 0240 0241 auto youtubeDl = QStandardPaths::findExecutable(QStringLiteral("youtube-dl")); 0242 if (!youtubeDl.isEmpty()) { 0243 return youtubeDl; 0244 } 0245 0246 return QString(); 0247 } 0248 0249 QUrl Application::parentUrl(const QString &path) 0250 { 0251 QUrl url(path); 0252 if (!url.isValid()) { 0253 return QUrl(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)); 0254 } 0255 QFileInfo fileInfo; 0256 if (url.isLocalFile()) { 0257 fileInfo.setFile(url.toLocalFile()); 0258 } else { 0259 fileInfo.setFile(url.toString()); 0260 } 0261 QUrl parentFolderUrl(fileInfo.absolutePath()); 0262 parentFolderUrl.setScheme(QStringLiteral("file")); 0263 0264 return parentFolderUrl; 0265 } 0266 0267 bool Application::isYoutubePlaylist(const QString &path) 0268 { 0269 return path.contains(QStringLiteral("youtube.com/playlist?list")); 0270 } 0271 0272 QString Application::formatTime(const double time) 0273 { 0274 int totalNumberOfSeconds = static_cast<int>(time); 0275 int seconds = totalNumberOfSeconds % 60; 0276 int minutes = (totalNumberOfSeconds / 60) % 60; 0277 int hours = (totalNumberOfSeconds / 60 / 60); 0278 0279 QString hoursString = QStringLiteral("%1").arg(hours, 2, 10, QLatin1Char('0')); 0280 QString minutesString = QStringLiteral("%1").arg(minutes, 2, 10, QLatin1Char('0')); 0281 QString secondsString = QStringLiteral("%1").arg(seconds, 2, 10, QLatin1Char('0')); 0282 QString timeString = QStringLiteral("%1:%2:%3").arg(hoursString, minutesString, secondsString); 0283 0284 return timeString; 0285 } 0286 0287 QUrl Application::url(int key) 0288 { 0289 return m_urls[key]; 0290 } 0291 0292 void Application::addUrl(int key, const QString &value) 0293 { 0294 m_urls.insert(key, QUrl::fromUserInput(value, QDir::currentPath())); 0295 } 0296 0297 QString Application::getFileContent(const QString &file) 0298 { 0299 QFile f(file); 0300 f.open(QIODevice::ReadOnly); 0301 QString content = QString::fromUtf8(f.readAll()); 0302 f.close(); 0303 return content; 0304 } 0305 0306 QString Application::mimeType(QUrl url) 0307 { 0308 KFileItem fileItem(url, KFileItem::NormalMimeTypeDetermination); 0309 return fileItem.mimetype(); 0310 } 0311 0312 void Application::handleSecondayInstanceMessage(const QByteArray &message) 0313 { 0314 auto msgString = QString::fromStdString(message.data()); 0315 QFileInfo fileInfo{msgString}; 0316 if (fileInfo.exists() && fileInfo.isFile()) { 0317 Q_EMIT openUrl(QUrl::fromUserInput(msgString)); 0318 raiseWindow(); 0319 } 0320 } 0321 0322 QStringList Application::availableGuiStyles() 0323 { 0324 return QStyleFactory::keys(); 0325 } 0326 0327 void Application::setGuiStyle(const QString &style) 0328 { 0329 if (style == QStringLiteral("Default")) { 0330 QApplication::setStyle(m_systemDefaultStyle); 0331 return; 0332 } 0333 QApplication::setStyle(style); 0334 } 0335 0336 QAbstractItemModel *Application::colorSchemesModel() 0337 { 0338 return m_schemes->model(); 0339 } 0340 0341 QQmlApplicationEngine *Application::qmlEngine() const 0342 { 0343 return m_qmlEngine; 0344 } 0345 0346 void Application::setQmlEngine(QQmlApplicationEngine *_qmlEngine) 0347 { 0348 m_qmlEngine = _qmlEngine; 0349 } 0350 0351 QCommandLineParser *Application::parser() const 0352 { 0353 return m_parser; 0354 } 0355 0356 void Application::activateColorScheme(const QString &name) 0357 { 0358 m_schemes->activateScheme(m_schemes->indexForScheme(name)); 0359 } 0360 0361 void Application::openDocs(const QString &page) 0362 { 0363 QDesktopServices::openUrl(QUrl(page)); 0364 } 0365 0366 QStringList Application::getFonts() 0367 { 0368 static QFontDatabase *fontDB = new QFontDatabase(); 0369 return fontDB->families(); 0370 } 0371 0372 int Application::frameworksVersionMinor() 0373 { 0374 return KCONFIG_VERSION_MINOR; 0375 } 0376 0377 int Application::qtMajorVersion() 0378 { 0379 return QT_VERSION_MAJOR; 0380 } 0381 0382 QString Application::platformName() 0383 { 0384 return QGuiApplication::platformName(); 0385 } 0386 0387 void Application::raiseWindow() 0388 { 0389 QObject *m_rootObject = m_qmlEngine->rootObjects().constFirst(); 0390 if (!m_rootObject) { 0391 return; 0392 } 0393 0394 // todo: replace deprecated methods 0395 QWindow *window = qobject_cast<QWindow *>(m_rootObject); 0396 if (window) { 0397 KWindowSystem::activateWindow(window); 0398 } 0399 } 0400 0401 #include "moc_application.cpp"