File indexing completed on 2024-04-28 04:49:19
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 "mpvitem.h" 0008 0009 #include <QCommandLineParser> 0010 #include <QCryptographicHash> 0011 #include <QDir> 0012 #include <QTimer> 0013 0014 #include <KFileMetaData/ExtractorCollection> 0015 #include <KFileMetaData/SimpleExtractionResult> 0016 #include <KLocalizedString> 0017 #include <KShell> 0018 #include <MpvController> 0019 0020 #include "application.h" 0021 #include "audiosettings.h" 0022 #include "chaptersmodel.h" 0023 #include "generalsettings.h" 0024 #include "global.h" 0025 #include "informationsettings.h" 0026 #include "mpvproperties.h" 0027 #include "playbacksettings.h" 0028 #include "playlistmodel.h" 0029 #include "playlistsettings.h" 0030 #include "subtitlessettings.h" 0031 #include "tracksmodel.h" 0032 #include "videosettings.h" 0033 #include "worker.h" 0034 0035 #if defined(Q_OS_UNIX) 0036 #include "lockmanager.h" 0037 #include "mediaplayer2.h" 0038 #include "mediaplayer2player.h" 0039 #include <QDBusConnection> 0040 #endif 0041 0042 MpvItem::MpvItem(QQuickItem *parent) 0043 : MpvAbstractItem(parent) 0044 , m_audioTracksModel{new TracksModel} 0045 , m_subtitleTracksModel{new TracksModel} 0046 , m_playlistModel{new PlaylistModel} 0047 , m_playlistProxyModel{new PlaylistProxyModel} 0048 , m_chaptersModel{new ChaptersModel} 0049 , m_watchLaterPath{QString(Global::instance()->appConfigDirPath()).append(QStringLiteral("/watch-later/"))} 0050 { 0051 m_playlistProxyModel->setSourceModel(m_playlistModel); 0052 observeProperty(MpvProperties::self()->MediaTitle, MPV_FORMAT_STRING); 0053 observeProperty(MpvProperties::self()->Position, MPV_FORMAT_DOUBLE); 0054 observeProperty(MpvProperties::self()->Remaining, MPV_FORMAT_DOUBLE); 0055 observeProperty(MpvProperties::self()->Duration, MPV_FORMAT_DOUBLE); 0056 observeProperty(MpvProperties::self()->Pause, MPV_FORMAT_FLAG); 0057 observeProperty(MpvProperties::self()->Volume, MPV_FORMAT_INT64); 0058 observeProperty(MpvProperties::self()->VolumeMax, MPV_FORMAT_INT64); 0059 observeProperty(MpvProperties::self()->Mute, MPV_FORMAT_FLAG); 0060 observeProperty(MpvProperties::self()->AudioId, MPV_FORMAT_INT64); 0061 observeProperty(MpvProperties::self()->SubtitleId, MPV_FORMAT_INT64); 0062 observeProperty(MpvProperties::self()->Width, MPV_FORMAT_NODE); 0063 observeProperty(MpvProperties::self()->Height, MPV_FORMAT_NODE); 0064 observeProperty(MpvProperties::self()->SecondarySubtitleId, MPV_FORMAT_INT64); 0065 observeProperty(MpvProperties::self()->Chapter, MPV_FORMAT_INT64); 0066 observeProperty(MpvProperties::self()->ChapterList, MPV_FORMAT_NODE); 0067 0068 initProperties(); 0069 setupConnections(); 0070 0071 auto *timer = new QTimer(); 0072 timer->setInterval(PlaybackSettings::savePositionInterval() * 1000); 0073 timer->start(); 0074 0075 connect(timer, &QTimer::timeout, this, [=]() { 0076 if (finishedLoading() && duration() > 0 && !pause()) { 0077 if (position() < duration() - 10) { 0078 saveTimePosition(); 0079 } else { 0080 resetTimePosition(); 0081 } 0082 } 0083 }); 0084 0085 connect(QApplication::instance(), &QApplication::aboutToQuit, this, [=]() { 0086 if (position() < duration() - 10) { 0087 saveTimePosition(); 0088 } else { 0089 resetTimePosition(); 0090 } 0091 }); 0092 0093 connect(this, &MpvAbstractItem::ready, this, &MpvItem::onReady); 0094 0095 // run user commands 0096 KSharedConfig::Ptr m_customPropsConfig; 0097 QString ccConfig = Global::instance()->appConfigFilePath(Global::ConfigFile::CustomCommands); 0098 m_customPropsConfig = KSharedConfig::openConfig(ccConfig, KConfig::SimpleConfig); 0099 QStringList groups = m_customPropsConfig->groupList(); 0100 for (const QString &_group : std::as_const(groups)) { 0101 auto configGroup = m_customPropsConfig->group(_group); 0102 QString type = configGroup.readEntry("Type", QString()); 0103 bool setOnStartup = configGroup.readEntry("SetOnStartup", true); 0104 if (type == QStringLiteral("startup") && setOnStartup) { 0105 userCommand(configGroup.readEntry("Command", QString())); 0106 } 0107 } 0108 } 0109 0110 void MpvItem::initProperties() 0111 { 0112 // setProperty(QStringLiteral("terminal"), InformationSettings::mpvLogging()); 0113 // setProperty(QStringLiteral("msg-level"), QStringLiteral("all=v")); 0114 0115 QString hwdec = PlaybackSettings::useHWDecoding() ? PlaybackSettings::hWDecoding() : QStringLiteral("no"); 0116 setProperty(MpvProperties::self()->HardwareDecoding, hwdec); 0117 setProperty(MpvProperties::self()->Volume, AudioSettings::volume()); 0118 setProperty(MpvProperties::self()->VolumeMax, 100); 0119 0120 // set ytdl_path to yt-dlp or fallback to youtube-dl 0121 setProperty(MpvProperties::self()->ScriptOpts, QStringLiteral("ytdl_hook-ytdl_path=%1").arg(Application::youtubeDlExecutable())); 0122 QCommandLineParser *cmdParser = Application::instance()->parser(); 0123 QString ytdlFormat = PlaybackSettings::ytdlFormat(); 0124 if (cmdParser->isSet(QStringLiteral("ytdl-format-selection"))) { 0125 ytdlFormat = cmdParser->value(QStringLiteral("ytdl-format-selection")); 0126 } 0127 setProperty(MpvProperties::self()->YtdlFormat, ytdlFormat); 0128 0129 setProperty(MpvProperties::self()->SubtitleAuto, QStringLiteral("exact")); 0130 setProperty(MpvProperties::self()->SubtitleUseMargins, SubtitlesSettings::allowOnBlackBorders()); 0131 setProperty(MpvProperties::self()->SubtitleAssForceMargins, SubtitlesSettings::allowOnBlackBorders()); 0132 setProperty(MpvProperties::self()->SubtitleFont, SubtitlesSettings::fontFamily()); 0133 setProperty(MpvProperties::self()->SubtitleFontSize, SubtitlesSettings::fontSize()); 0134 setProperty(MpvProperties::self()->SubtitleColor, SubtitlesSettings::fontColor()); 0135 setProperty(MpvProperties::self()->SubtitleShadowColor, SubtitlesSettings::shadowColor()); 0136 setProperty(MpvProperties::self()->SubtitleShadowOffset, SubtitlesSettings::shadowOffset()); 0137 setProperty(MpvProperties::self()->SubtitleBorderColor, SubtitlesSettings::borderColor()); 0138 setProperty(MpvProperties::self()->SubtitleBorderSize, SubtitlesSettings::borderSize()); 0139 setProperty(MpvProperties::self()->SubtitleBold, SubtitlesSettings::isBold()); 0140 setProperty(MpvProperties::self()->SubtitleItalic, SubtitlesSettings::isItalic()); 0141 0142 setProperty(MpvProperties::self()->ScreenshotTemplate, VideoSettings::screenshotTemplate()); 0143 setProperty(MpvProperties::self()->ScreenshotFormat, VideoSettings::screenshotFormat()); 0144 0145 setProperty(MpvProperties::self()->AudioClientName, QStringLiteral("haruna")); 0146 const QVariant preferredAudioTrack = AudioSettings::preferredTrack(); 0147 setProperty(MpvProperties::self()->AudioId, preferredAudioTrack == 0 ? QStringLiteral("auto") : preferredAudioTrack); 0148 setProperty(MpvProperties::self()->AudioLanguage, AudioSettings::preferredLanguage().remove(QStringLiteral(" "))); 0149 0150 const QVariant preferredSubTrack = SubtitlesSettings::preferredTrack(); 0151 setProperty(MpvProperties::self()->SubtitleId, preferredSubTrack == 0 ? QStringLiteral("auto") : preferredSubTrack); 0152 setProperty(MpvProperties::self()->SubtitleLanguage, SubtitlesSettings::preferredLanguage().remove(QStringLiteral(" "))); 0153 setProperty(MpvProperties::self()->SubtitleFilePaths, SubtitlesSettings::subtitlesFolders().join(QStringLiteral(":"))); 0154 } 0155 0156 void MpvItem::setupConnections() 0157 { 0158 // clang-format off 0159 connect(mpvController(), &MpvController::propertyChanged, 0160 this, &MpvItem::onPropertyChanged, Qt::QueuedConnection); 0161 0162 connect(mpvController(), &MpvController::fileStarted, 0163 this, &MpvItem::fileStarted, Qt::QueuedConnection); 0164 0165 connect(mpvController(), &MpvController::endFile, 0166 this, &MpvItem::onEndFile, Qt::QueuedConnection); 0167 0168 connect(mpvController(), &MpvController::videoReconfig, 0169 this, &MpvItem::videoReconfig, Qt::QueuedConnection); 0170 0171 connect(mpvController(), &MpvController::asyncReply, 0172 this, &MpvItem::onAsyncReply, Qt::QueuedConnection); 0173 0174 connect(this, &MpvItem::currentUrlChanged, this, [=]() { 0175 setFinishedLoading(false); 0176 }); 0177 0178 connect(mpvController(), &MpvController::fileLoaded, this, [=]() { 0179 getPropertyAsync(MpvProperties::self()->ChapterList, static_cast<int>(AsyncIds::ChapterList)); 0180 getPropertyAsync(MpvProperties::self()->VideoId, static_cast<int>(AsyncIds::VideoId)); 0181 getPropertyAsync(MpvProperties::self()->TrackList, static_cast<int>(AsyncIds::TrackList)); 0182 0183 if (m_playlistModel->rowCount() <= 1 && PlaylistSettings::repeat()) { 0184 setProperty(MpvProperties::self()->LoopFile, QStringLiteral("inf")); 0185 } 0186 0187 setProperty(MpvProperties::self()->ABLoopA, QStringLiteral("no")); 0188 setProperty(MpvProperties::self()->ABLoopB, QStringLiteral("no")); 0189 0190 // this is only run when reloading the last file in the playlist 0191 // due to the playlist repeat setting being turned off 0192 if (isFileReloaded()) { 0193 setPause(true); 0194 setPosition(0); 0195 setIsFileReloaded(false); 0196 setFinishedLoading(true); 0197 return; 0198 } 0199 0200 setFinishedLoading(true); 0201 Q_EMIT fileLoaded(); 0202 0203 }, Qt::QueuedConnection); 0204 0205 connect(this, &MpvItem::positionChanged, this, [this]() { 0206 auto pos = static_cast<int>(position()); 0207 if (!m_secondsWatched.contains(pos)) { 0208 m_secondsWatched << pos; 0209 if (m_duration != 0) { 0210 setWatchPercentage(m_secondsWatched.count() * 100 / m_duration); 0211 } 0212 } 0213 }); 0214 0215 connect(this, &MpvItem::chapterChanged, 0216 this, &MpvItem::onChapterChanged); 0217 0218 connect(m_playlistModel, &PlaylistModel::playingItemChanged, this, [=]() { 0219 loadFile(m_playlistModel->m_playlist[m_playlistModel->m_playingItem].url.toString()); 0220 }); 0221 0222 0223 #if defined(Q_OS_UNIX) 0224 // register mpris dbus service 0225 QString mspris2Name(QStringLiteral("org.mpris.MediaPlayer2.haruna")); 0226 QDBusConnection::sessionBus().registerService(mspris2Name); 0227 QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/mpris/MediaPlayer2"), this, QDBusConnection::ExportAdaptors); 0228 // org.mpris.MediaPlayer2 mpris2 interface 0229 auto mp2 = new MediaPlayer2(this); 0230 connect(mp2, &MediaPlayer2::raise, this, &MpvItem::raise); 0231 auto mp2Player = new MediaPlayer2Player(this); 0232 connect(mp2Player, &MediaPlayer2Player::playpause, this, [=]() { 0233 setPause(!pause()); 0234 }); 0235 connect(mp2Player, &MediaPlayer2Player::play, this, [=]() { 0236 setPause(false); 0237 }); 0238 connect(mp2Player, &MediaPlayer2Player::pause, this, [=]() { 0239 setPause(true); 0240 }); 0241 connect(mp2Player, &MediaPlayer2Player::stop, this, [=]() { 0242 setPosition(0); 0243 setPause(true); 0244 }); 0245 connect(mp2Player, &MediaPlayer2Player::next, this, [=]() { 0246 Q_EMIT playNext(); 0247 }); 0248 connect(mp2Player, &MediaPlayer2Player::previous, this, [=]() { 0249 Q_EMIT playPrevious(); 0250 }); 0251 connect(mp2Player, &MediaPlayer2Player::seek, this, [=](int offset) { 0252 command(QStringList() << QStringLiteral("add") << QStringLiteral("time-pos") << QString::number(offset)); 0253 }); 0254 connect(mp2Player, &MediaPlayer2Player::openUri, this, [=](const QString &uri) { 0255 Q_EMIT openUri(uri); 0256 }); 0257 #endif 0258 0259 #if defined(Q_OS_UNIX) 0260 auto lockManager = new LockManager(this); 0261 connect(this, &MpvItem::pauseChanged, this, [=]() { 0262 if (pause()) { 0263 lockManager->setInhibitionOff(); 0264 } else { 0265 lockManager->setInhibitionOn(); 0266 } 0267 }); 0268 #endif 0269 0270 connect(this, &MpvItem::syncConfigValue, Worker::instance(), &Worker::syncConfigValue, Qt::QueuedConnection); 0271 // clang-format on 0272 } 0273 0274 void MpvItem::onEndFile(const QString &reason) 0275 { 0276 if (reason == u"error"_qs) { 0277 if (playlistModel()->rowCount() == 0) { 0278 return; 0279 } 0280 0281 const auto index = playlistProxyModel()->index(playlistProxyModel()->getPlayingItem(), 0); 0282 const auto title = playlistModel()->data(playlistProxyModel()->mapToSource(index), PlaylistModel::TitleRole); 0283 0284 Q_EMIT osdMessage(i18nc("@info:tooltip", "Could not play: %1", title.toString())); 0285 } 0286 0287 if (playlistProxyModel()->getPlayingItem() + 1 < playlistModel()->rowCount()) { 0288 playlistProxyModel()->playNext(); 0289 } else { 0290 // Last file in playlist 0291 if (PlaylistSettings::repeat()) { 0292 playlistProxyModel()->setPlayingItem(0); 0293 } else { 0294 setIsFileReloaded(true); 0295 playlistProxyModel()->setPlayingItem(playlistProxyModel()->getPlayingItem()); 0296 } 0297 } 0298 } 0299 0300 void MpvItem::onReady() 0301 { 0302 setIsReady(true); 0303 0304 QUrl url{Application::instance()->url(0)}; 0305 if (!url.isEmpty() && url.isValid()) { 0306 playlistModel()->addItem(Application::instance()->url(0), PlaylistModel::Clear); 0307 Q_EMIT addToRecentFiles(url); 0308 } else { 0309 if (PlaybackSettings::openLastPlayedFile()) { 0310 // if both lastPlaylist and lastPlayedFile are set the playlist is loaded 0311 // and the lastPlayedFile is searched in the playlist 0312 if (!GeneralSettings::lastPlaylist().isEmpty()) { 0313 playlistModel()->addItem(GeneralSettings::lastPlaylist(), PlaylistModel::Clear); 0314 } else { 0315 playlistModel()->addItem(GeneralSettings::lastPlayedFile(), PlaylistModel::Clear); 0316 } 0317 } 0318 } 0319 } 0320 0321 void MpvItem::onPropertyChanged(const QString &property, const QVariant &value) 0322 { 0323 if (property == MpvProperties::self()->MediaTitle) { 0324 m_mediaTitle = value.toString(); 0325 Q_EMIT mediaTitleChanged(); 0326 0327 } else if (property == MpvProperties::self()->Position) { 0328 m_position = value.toDouble(); 0329 m_formattedPosition = Application::formatTime(m_position); 0330 Q_EMIT positionChanged(); 0331 0332 } else if (property == MpvProperties::self()->Remaining) { 0333 m_remaining = value.toDouble(); 0334 m_formattedRemaining = Application::formatTime(m_remaining); 0335 Q_EMIT remainingChanged(); 0336 0337 } else if (property == MpvProperties::self()->Duration) { 0338 m_duration = value.toDouble(); 0339 m_formattedDuration = Application::formatTime(m_duration); 0340 Q_EMIT durationChanged(); 0341 0342 } else if (property == MpvProperties::self()->Pause) { 0343 m_pause = value.toBool(); 0344 Q_EMIT pauseChanged(); 0345 0346 } else if (property == MpvProperties::self()->Volume) { 0347 m_volume = value.toInt(); 0348 Q_EMIT volumeChanged(); 0349 0350 AudioSettings::setVolume(m_volume); 0351 AudioSettings::self()->save(); 0352 0353 } else if (property == MpvProperties::self()->VolumeMax) { 0354 m_volumeMax = value.toInt(); 0355 Q_EMIT volumeMaxChanged(); 0356 0357 } else if (property == MpvProperties::self()->Mute) { 0358 m_mute = value.toBool(); 0359 Q_EMIT muteChanged(); 0360 0361 } else if (property == MpvProperties::self()->Chapter) { 0362 m_chapter = value.toInt(); 0363 Q_EMIT chapterChanged(); 0364 0365 } else if (property == MpvProperties::self()->ChapterList) { 0366 Q_EMIT chapterListChanged(); 0367 0368 } else if (property == MpvProperties::self()->AudioId) { 0369 m_audioId = value.toInt(); 0370 Q_EMIT audioIdChanged(); 0371 0372 } else if (property == MpvProperties::self()->SubtitleId) { 0373 m_subtitleId = value.toInt(); 0374 Q_EMIT subtitleIdChanged(); 0375 0376 } else if (property == MpvProperties::self()->SecondarySubtitleId) { 0377 m_secondarySubtitleId = value.toInt(); 0378 Q_EMIT secondarySubtitleIdChanged(); 0379 0380 } else if (property == MpvProperties::self()->Width) { 0381 m_videoWidth = value.toInt(); 0382 Q_EMIT videoWidthChanged(); 0383 0384 } else if (property == MpvProperties::self()->Height) { 0385 m_videoHeight = value.toInt(); 0386 Q_EMIT videoHeightChanged(); 0387 } 0388 } 0389 0390 void MpvItem::loadFile(const QString &file) 0391 { 0392 auto url = QUrl::fromUserInput(file); 0393 if (m_currentUrl != url) { 0394 m_currentUrl = url; 0395 Q_EMIT currentUrlChanged(); 0396 } 0397 0398 setPause(false); 0399 setWatchLaterPosition(loadTimePosition()); 0400 if (PlaybackSettings::seekToLastPosition()) { 0401 setPause(!PlaybackSettings::playOnResume() && watchLaterPosition() > 0); 0402 setProperty(u"start"_qs, QVariant(u"+"_qs + QString::number(m_watchLaterPosition))); 0403 } 0404 command(QStringList() << QStringLiteral("loadfile") << m_currentUrl.toString()); 0405 0406 GeneralSettings::setLastPlayedFile(m_currentUrl.toString()); 0407 GeneralSettings::self()->save(); 0408 } 0409 0410 void MpvItem::loadTracks(QList<QVariant> tracks) 0411 { 0412 m_subtitleTracks.clear(); 0413 m_audioTracks.clear(); 0414 0415 QMap<QString, QVariant> none = { 0416 {QStringLiteral("id"), QVariant(0)}, 0417 {QStringLiteral("title"), QVariant(i18nc("@action The \"None\" subtitle track is used to clear/unset selected track", "None"))}, 0418 }; 0419 m_subtitleTracks.append(none); 0420 0421 for (const auto &track : tracks) { 0422 const auto trackMap = track.toMap(); 0423 if (trackMap[QStringLiteral("type")] == QStringLiteral("sub")) { 0424 m_subtitleTracks.append(track); 0425 } 0426 if (trackMap[QStringLiteral("type")] == QStringLiteral("audio")) { 0427 m_audioTracks.append(track); 0428 } 0429 } 0430 m_subtitleTracksModel->setTracks(std::move(m_subtitleTracks)); 0431 m_audioTracksModel->setTracks(std::move(m_audioTracks)); 0432 0433 Q_EMIT audioTracksModelChanged(); 0434 Q_EMIT subtitleTracksModelChanged(); 0435 } 0436 0437 void MpvItem::onAsyncReply(const QVariant &data, mpv_event event) 0438 { 0439 switch (static_cast<AsyncIds>(event.reply_userdata)) { 0440 case AsyncIds::None: { 0441 break; 0442 } 0443 case AsyncIds::SavePosition: { 0444 auto hash = md5(currentUrl().toString()); 0445 auto watchLaterConfig = QString(m_watchLaterPath).append(hash); 0446 Q_EMIT syncConfigValue(watchLaterConfig, QString(), QStringLiteral("TimePosition"), data.toString()); 0447 break; 0448 } 0449 case AsyncIds::Screenshot: { 0450 if (event.error < 0) { 0451 osdMessage(i18nc("@info:tooltip osd", "Screenshot failed")); 0452 break; 0453 } 0454 auto filename = data.toMap().value(QStringLiteral("filename")).toString(); 0455 if (filename.isEmpty()) { 0456 osdMessage(i18nc("@info:tooltip osd", "Screenshot taken")); 0457 } else { 0458 osdMessage(i18nc("@info:tooltip osd", "Screenshot: %1", filename)); 0459 } 0460 break; 0461 } 0462 case AsyncIds::TrackList: { 0463 loadTracks(data.toList()); 0464 break; 0465 } 0466 case AsyncIds::ChapterList: { 0467 m_chaptersList = data.toList(); 0468 QList<Chapter> chaptersList; 0469 for (const auto &chapter : m_chaptersList) { 0470 Chapter c; 0471 c.title = chapter.toMap()[QStringLiteral("title")].toString(); 0472 c.startTime = chapter.toMap()[QStringLiteral("time")].toDouble(); 0473 chaptersList.append(c); 0474 } 0475 m_chaptersModel->setChapters(chaptersList); 0476 break; 0477 } 0478 case AsyncIds::VideoId: { 0479 if (!data.toBool()) { 0480 command(QStringList{QStringLiteral("video-add"), VideoSettings::defaultCover()}); 0481 } 0482 break; 0483 } 0484 } 0485 } 0486 0487 void MpvItem::onChapterChanged() 0488 { 0489 if (!finishedLoading() || !PlaybackSettings::skipChapters()) { 0490 return; 0491 } 0492 0493 const QString chaptersToSkip = PlaybackSettings::chaptersToSkip(); 0494 if (m_chaptersList.count() == 0 || chaptersToSkip == QString()) { 0495 return; 0496 } 0497 0498 const QStringList words = chaptersToSkip.split(QStringLiteral(",")); 0499 auto ch = m_chaptersList.value(chapter()).toMap(); 0500 auto title = ch.value(QStringLiteral("title")).toString(); 0501 for (int i = 0; i < words.count(); ++i) { 0502 QString word = words.at(i).toLower().simplified(); 0503 if (!ch.isEmpty() && title.toLower().contains(word)) { 0504 command({QStringLiteral("add"), QStringLiteral("chapter"), QStringLiteral("1")}); 0505 if (PlaybackSettings::showOsdOnSkipChapters()) { 0506 Q_EMIT osdMessage(i18nc("@info:tooltip osd", "Skipped chapter: %1", title)); 0507 } 0508 return; 0509 } 0510 } 0511 } 0512 0513 void MpvItem::saveTimePosition() 0514 { 0515 // saving position is disabled 0516 if (PlaybackSettings::minDurationToSavePosition() == -1) { 0517 return; 0518 } 0519 // position is saved only for files longer than PlaybackSettings::minDurationToSavePosition() 0520 if (getProperty(MpvProperties::self()->Duration).toInt() < PlaybackSettings::minDurationToSavePosition() * 60) { 0521 return; 0522 } 0523 0524 getPropertyAsync(MpvProperties::self()->Position, static_cast<int>(AsyncIds::SavePosition)); 0525 } 0526 0527 double MpvItem::loadTimePosition() 0528 { 0529 // saving position is disabled 0530 if (PlaybackSettings::minDurationToSavePosition() == -1) { 0531 return 0; 0532 } 0533 double duration{0}; 0534 0535 // KFileMetaData is faster than getProperty 0536 QString mimeType = Application::mimeType(m_currentUrl); 0537 KFileMetaData::ExtractorCollection exCol; 0538 QList<KFileMetaData::Extractor *> extractors = exCol.fetchExtractors(mimeType); 0539 KFileMetaData::SimpleExtractionResult result(m_currentUrl.toLocalFile(), mimeType, KFileMetaData::ExtractionResult::ExtractMetaData); 0540 if (extractors.size() > 0) { 0541 KFileMetaData::Extractor *ex = extractors.first(); 0542 ex->extract(&result); 0543 auto properties = result.properties(); 0544 duration = properties.value(KFileMetaData::Property::Duration).toDouble(); 0545 } else { 0546 duration = getProperty(MpvProperties::self()->Duration).toInt(); 0547 } 0548 0549 // position for files with a duration lower than 0550 // PlaybackSettings::minDurationToSavePosition() is not restored 0551 if (duration < PlaybackSettings::minDurationToSavePosition() * 60) { 0552 return 0; 0553 } 0554 0555 auto hash = md5(currentUrl().toString()); 0556 auto watchLaterConfig = QString(m_watchLaterPath).append(hash); 0557 KConfig config(watchLaterConfig); 0558 auto pos = config.group(QString()).readEntry("TimePosition", QString::number(0)).toDouble(); 0559 0560 return pos; 0561 } 0562 0563 void MpvItem::resetTimePosition() 0564 { 0565 auto hash = md5(currentUrl().toString()); 0566 auto watchLaterConfig = QString(m_watchLaterPath).append(hash); 0567 QFile f(watchLaterConfig); 0568 0569 if (f.exists()) { 0570 f.remove(); 0571 } 0572 f.close(); 0573 } 0574 0575 void MpvItem::userCommand(const QString &commandString) 0576 { 0577 QStringList args = KShell::splitArgs(commandString.simplified()); 0578 command(args); 0579 } 0580 0581 QString MpvItem::md5(const QString &str) 0582 { 0583 auto md5 = QCryptographicHash::hash((str.toUtf8()), QCryptographicHash::Md5); 0584 0585 return QString::fromUtf8(md5.toHex()); 0586 } 0587 0588 PlaylistModel *MpvItem::playlistModel() 0589 { 0590 return m_playlistModel; 0591 } 0592 0593 void MpvItem::setPlaylistModel(PlaylistModel *model) 0594 { 0595 m_playlistModel = model; 0596 } 0597 0598 PlaylistProxyModel *MpvItem::playlistProxyModel() 0599 { 0600 return m_playlistProxyModel; 0601 } 0602 0603 void MpvItem::setPlaylistProxyModel(PlaylistProxyModel *model) 0604 { 0605 m_playlistProxyModel = model; 0606 } 0607 0608 ChaptersModel *MpvItem::chaptersModel() const 0609 { 0610 return m_chaptersModel; 0611 } 0612 0613 void MpvItem::setChaptersModel(ChaptersModel *_chaptersModel) 0614 { 0615 if (m_chaptersModel == _chaptersModel) { 0616 return; 0617 } 0618 m_chaptersModel = _chaptersModel; 0619 Q_EMIT chaptersModelChanged(); 0620 } 0621 0622 TracksModel *MpvItem::subtitleTracksModel() const 0623 { 0624 return m_subtitleTracksModel; 0625 } 0626 0627 TracksModel *MpvItem::audioTracksModel() const 0628 { 0629 return m_audioTracksModel; 0630 } 0631 0632 bool MpvItem::isFileReloaded() const 0633 { 0634 return m_isFileReloaded; 0635 } 0636 0637 void MpvItem::setIsFileReloaded(bool _isFileReloaded) 0638 { 0639 if (m_isFileReloaded == _isFileReloaded) { 0640 return; 0641 } 0642 m_isFileReloaded = _isFileReloaded; 0643 Q_EMIT isFileReloadedChanged(); 0644 } 0645 0646 QString MpvItem::mediaTitle() 0647 { 0648 return m_mediaTitle; 0649 } 0650 0651 double MpvItem::position() 0652 { 0653 return m_position; 0654 } 0655 0656 void MpvItem::setPosition(double value) 0657 { 0658 if (qFuzzyCompare(value, position())) { 0659 return; 0660 } 0661 setProperty(MpvProperties::self()->Position, value); 0662 } 0663 0664 double MpvItem::remaining() 0665 { 0666 return m_remaining; 0667 } 0668 0669 double MpvItem::duration() 0670 { 0671 return m_duration; 0672 } 0673 0674 bool MpvItem::pause() 0675 { 0676 return m_pause; 0677 } 0678 0679 void MpvItem::setPause(bool value) 0680 { 0681 if (value == pause()) { 0682 return; 0683 } 0684 setProperty(MpvProperties::self()->Pause, value); 0685 } 0686 0687 int MpvItem::volume() 0688 { 0689 return m_volume; 0690 } 0691 0692 void MpvItem::setVolume(int value) 0693 { 0694 if (value == volume()) { 0695 return; 0696 } 0697 setProperty(MpvProperties::self()->Volume, value); 0698 } 0699 0700 int MpvItem::volumeMax() 0701 { 0702 return m_volumeMax; 0703 } 0704 0705 void MpvItem::setVolumeMax(int value) 0706 { 0707 if (volumeMax() == value) { 0708 return; 0709 } 0710 0711 setProperty(MpvProperties::self()->VolumeMax, value); 0712 } 0713 0714 bool MpvItem::mute() 0715 { 0716 return m_mute; 0717 } 0718 0719 void MpvItem::setMute(bool value) 0720 { 0721 if (value == mute()) { 0722 return; 0723 } 0724 setProperty(MpvProperties::self()->Mute, value); 0725 } 0726 0727 int MpvItem::chapter() 0728 { 0729 return m_chapter; 0730 } 0731 0732 void MpvItem::setChapter(int value) 0733 { 0734 if (value == chapter()) { 0735 return; 0736 } 0737 setProperty(MpvProperties::self()->Chapter, value); 0738 } 0739 0740 int MpvItem::audioId() 0741 { 0742 return m_audioId; 0743 } 0744 0745 void MpvItem::setAudioId(int value) 0746 { 0747 if (value == audioId()) { 0748 return; 0749 } 0750 setProperty(MpvProperties::self()->AudioId, value); 0751 } 0752 0753 int MpvItem::subtitleId() 0754 { 0755 return m_subtitleId; 0756 } 0757 0758 void MpvItem::setSubtitleId(int value) 0759 { 0760 if (value == subtitleId()) { 0761 return; 0762 } 0763 setProperty(MpvProperties::self()->SubtitleId, value); 0764 } 0765 0766 int MpvItem::secondarySubtitleId() 0767 { 0768 return m_secondarySubtitleId; 0769 } 0770 0771 void MpvItem::setSecondarySubtitleId(int value) 0772 { 0773 if (value == secondarySubtitleId()) { 0774 return; 0775 } 0776 setProperty(MpvProperties::self()->SecondarySubtitleId, value); 0777 } 0778 0779 QString MpvItem::formattedDuration() const 0780 { 0781 return m_formattedDuration; 0782 } 0783 0784 QString MpvItem::formattedRemaining() const 0785 { 0786 return m_formattedRemaining; 0787 } 0788 0789 QString MpvItem::formattedPosition() const 0790 { 0791 return m_formattedPosition; 0792 } 0793 0794 QUrl MpvItem::currentUrl() const 0795 { 0796 return m_currentUrl; 0797 } 0798 0799 bool MpvItem::isReady() const 0800 { 0801 return m_isReady; 0802 } 0803 0804 inline void MpvItem::setIsReady(bool _isReady) 0805 { 0806 if (m_isReady == _isReady) { 0807 return; 0808 } 0809 m_isReady = _isReady; 0810 Q_EMIT isReadyChanged(); 0811 } 0812 0813 double MpvItem::watchLaterPosition() const 0814 { 0815 return m_watchLaterPosition; 0816 } 0817 0818 void MpvItem::setWatchLaterPosition(double _watchLaterPosition) 0819 { 0820 if (qFuzzyCompare(m_watchLaterPosition + 1, _watchLaterPosition + 1)) { 0821 return; 0822 } 0823 m_watchLaterPosition = _watchLaterPosition; 0824 Q_EMIT watchLaterPositionChanged(); 0825 } 0826 0827 bool MpvItem::finishedLoading() const 0828 { 0829 return m_finishedLoading; 0830 } 0831 0832 void MpvItem::setFinishedLoading(bool _finishedLoading) 0833 { 0834 if (m_finishedLoading == _finishedLoading) { 0835 return; 0836 } 0837 m_finishedLoading = _finishedLoading; 0838 Q_EMIT finishedLoadingChanged(); 0839 } 0840 0841 double MpvItem::watchPercentage() 0842 { 0843 return m_watchPercentage; 0844 } 0845 0846 void MpvItem::setWatchPercentage(double value) 0847 { 0848 if (qFuzzyCompare(m_watchPercentage, value)) { 0849 return; 0850 } 0851 m_watchPercentage = value; 0852 Q_EMIT watchPercentageChanged(); 0853 } 0854 0855 int MpvItem::videoWidth() 0856 { 0857 return m_videoWidth; 0858 } 0859 0860 int MpvItem::videoHeight() 0861 { 0862 return m_videoHeight; 0863 } 0864 0865 #include "moc_mpvitem.cpp"