Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package shotcut for openSUSE:Factory checked in at 2021-09-20 23:32:49 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/shotcut (Old) and /work/SRC/openSUSE:Factory/.shotcut.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "shotcut" Mon Sep 20 23:32:49 2021 rev:47 rq:920032 version:21.09.13 Changes: -------- --- /work/SRC/openSUSE:Factory/shotcut/shotcut.changes 2021-09-01 21:37:51.520925823 +0200 +++ /work/SRC/openSUSE:Factory/.shotcut.new.1899/shotcut.changes 2021-09-20 23:34:22.115263668 +0200 @@ -1,0 +2,15 @@ +Fri Sep 17 12:32:10 UTC 2021 - Michael Vetter <mvet...@suse.com> + +- Update to 21.09.13: + * Fixed dragging to Timeline broken if there is MLT XML on theclipboard (broke v21.08.29). + * Fixed seeking backwards after seeking to the end (broke in v21.08.29). + * Fixed File > Save (Ctrl-S) immediately after File > Close (Ctrl-W) can corrupt a saved project. + * Fixed Add Custom Video Mode > Frames/sec was not accepting some legitimate values. + * Fixed encoding HEVC without hardware encoder (x265) would always output 10-bit instead of 8 (broke in v20.11.28). + * Changed Copy the filters to only copy enabled filters. + * Changed fading on the Timeline to automatically fade the opacity when it is not the bottom video track. + * Expire old QML cache items at startup to reduce app data footprint. + * Improved performance of 360: video filters. + * Added 360: Equirectangular to Stereographic video filter. This can be used to create so-called Tiny Planet videos. + +------------------------------------------------------------------- Old: ---- shotcut-21.08.29.tar.gz New: ---- shotcut-21.09.13.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ shotcut.spec ++++++ --- /var/tmp/diff_new_pack.v9chNr/_old 2021-09-20 23:34:22.707264399 +0200 +++ /var/tmp/diff_new_pack.v9chNr/_new 2021-09-20 23:34:22.707264399 +0200 @@ -25,7 +25,7 @@ %global __requires_exclude qmlimport\\((Shotcut\\.Controls|org\\.shotcut\\.qml).* Name: shotcut -Version: 21.08.29 +Version: 21.09.13 Release: 0 # This package creates a build time version from the current date and uses it to check # for updates. See patch1 and prep/build section. For reproducible builds. ++++++ shotcut-21.08.29.tar.gz -> shotcut-21.09.13.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/scripts/build-shotcut-msys2.sh new/shotcut-21.09.13/scripts/build-shotcut-msys2.sh --- old/shotcut-21.08.29/scripts/build-shotcut-msys2.sh 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/scripts/build-shotcut-msys2.sh 2021-09-13 07:05:06.000000000 +0200 @@ -42,13 +42,13 @@ SHOTCUT_REVISION= SHOTCUT_VERSION=$(date '+%y.%m.%d') ENABLE_BIGSH0T=1 -BIGSH0T_HEAD=0 -BIGSH0T_REVISION="5fad6d3b5963ce69141e9debcc3b733b84a0842d" +BIGSH0T_HEAD=1 +BIGSH0T_REVISION= ENABLE_ZIMG=1 ZIMG_HEAD=1 ZIMG_REVISION= -DAV1D_HEAD=1 -DAV1D_REVISION= +DAV1D_HEAD=0 +DAV1D_REVISION="0.9.2" AOM_HEAD=0 AOM_REVISION="v2.0.2" ENABLE_VMAF=1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/scripts/build-shotcut.sh new/shotcut-21.09.13/scripts/build-shotcut.sh --- old/shotcut-21.08.29/scripts/build-shotcut.sh 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/scripts/build-shotcut.sh 2021-09-13 07:05:06.000000000 +0200 @@ -73,13 +73,13 @@ RUBBERBAND_HEAD=0 RUBBERBAND_REVISION="v1.9.1" ENABLE_BIGSH0T=1 -BIGSH0T_HEAD=0 -BIGSH0T_REVISION="5fad6d3b5963ce69141e9debcc3b733b84a0842d" +BIGSH0T_HEAD=1 +BIGSH0T_REVISION= ENABLE_ZIMG=1 ZIMG_HEAD=1 ZIMG_REVISION= -DAV1D_HEAD=1 -DAV1D_REVISION= +DAV1D_HEAD=0 +DAV1D_REVISION="0.9.2" AOM_HEAD=0 AOM_REVISION="v2.0.2" VMAF_HEAD=0 @@ -858,11 +858,7 @@ ###### # x265 CFLAGS_[13]=$CFLAGS - if test "$TARGET_OS" = "Win32" -o "$TARGET_OS" = "Win64" ; then - CONFIG[13]="cmake -DCMAKE_INSTALL_PREFIX=$FINAL_INSTALL_DIR -DCMAKE_TOOLCHAIN_FILE=my.cmake -DENABLE_CLI=OFF -DHIGH_BIT_DEPTH=ON $CMAKE_DEBUG_FLAG" - else - CONFIG[13]="cmake -DCMAKE_INSTALL_PREFIX=$FINAL_INSTALL_DIR -DENABLE_CLI=OFF -DHIGH_BIT_DEPTH=ON $CMAKE_DEBUG_FLAG" - fi + CONFIG[13]="cmake -G Ninja -D CMAKE_INSTALL_PREFIX=$FINAL_INSTALL_DIR -DENABLE_CLI=OFF -D ENABLE_SHARED=ON -D EXTRA_LIB='x265_main10.a' -D LINKED_10BIT=ON -D EXTRA_LINK_FLAGS='-L.' $CMAKE_DEBUG_FLAG" LDFLAGS_[13]=$LDFLAGS ####### @@ -1482,7 +1478,12 @@ # Special hack for x265 if test "x265" = "$1"; then - cd source + [ ! -d "10bit" ] && mkdir 10bit + cd 10bit + cmd cmake -G Ninja -D ENABLE_CLI=OFF -D ENABLE_SHARED=OFF -D EXPORT_C_API=OFF -D HIGH_BIT_DEPTH=ON $CMAKE_DEBUG_FLAG ../source + cmd ninja + cd ../source + cmd ln -s ../10bit/libx265.a libx265_main10.a fi # Special hack for AMF @@ -1544,6 +1545,16 @@ cmd ninja -C builddir -j $MAKEJ || die "Unable to build $1" elif test "aom" = "$1" -o "mlt" = "$1"; then cmd ninja -j $MAKEJ || die "Unable to build $1" + elif test "x265" = "$1" ; then + cmd ninja -j $MAKEJ || die "Unable to build $1" + cmd mv libx265.a libx265_main.a + ar -M <<EOF +CREATE libx265.a +ADDLIB libx265_main.a +ADDLIB libx265_main10.a +SAVE +END +EOF elif test "vmaf" = "$1" ; then cmd ninja -C libvmaf/build -j $MAKEJ || die "Unable to build $1" elif test "$MYCONFIG" != ""; then @@ -1593,7 +1604,7 @@ fi elif test "dav1d" = "$1" -o "rubberband" = "$1" ; then cmd meson install -C builddir || die "Unable to install $1" - elif test "aom" = "$1" -o "mlt" = "$1" ; then + elif test "aom" = "$1" -o "mlt" = "$1" -o "x265" = "$1" ; then cmd ninja install || die "Unable to install $1" elif test "vmaf" = "$1" ; then cmd ninja install -C libvmaf/build || die "Unable to install $1" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/dialogs/customprofiledialog.cpp new/shotcut-21.09.13/src/dialogs/customprofiledialog.cpp --- old/shotcut-21.08.29/src/dialogs/customprofiledialog.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/dialogs/customprofiledialog.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -57,23 +57,9 @@ QSize sar(ui->aspectNumSpinner->value() * ui->heightSpinner->value(), ui->aspectDenSpinner->value() * ui->widthSpinner->value()); auto gcd = Util::greatestCommonDivisor(sar.width(), sar.height()); MLT.profile().set_sample_aspect(sar.width() / gcd, sar.height() / gcd); - switch (int(ui->fpsSpinner->value() * 10)) { - case 239: - MLT.profile().set_frame_rate(24000, 1001); - break; - case 299: - MLT.profile().set_frame_rate(30000, 1001); - break; - case 479: - MLT.profile().set_frame_rate(48000, 1001); - break; - case 599: - MLT.profile().set_frame_rate(60000, 1001); - break; - default: - MLT.profile().set_frame_rate(ui->fpsSpinner->value() * 1000, 1000); - break; - } + int numerator, denominator; + Util::normalizeFrameRate(ui->fpsSpinner->value(), numerator, denominator); + MLT.profile().set_frame_rate(numerator, denominator); MLT.profile().set_progressive(ui->scanModeCombo->currentIndex()); MLT.profile().set_colorspace((ui->colorspaceCombo->currentIndex() == 1)? 709 : 601); MLT.updatePreviewProfile(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/docks/encodedock.cpp new/shotcut-21.09.13/src/docks/encodedock.cpp --- old/shotcut-21.08.29/src/docks/encodedock.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/docks/encodedock.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -845,24 +845,10 @@ // Only if the frame rate spinner does not match the profile. && (includeProfile || qRound(ui->fpsSpinner->value() * 1000000.0) != qRound(MLT.profile().fps() * 1000000.0))) { - // Convert some common non-integer frame rates to fractions. - if (qRound(ui->fpsSpinner->value() * 1000000.0) == 23976024) { - p->set("frame_rate_num", 24000); - p->set("frame_rate_den", 1001); - } else if (qRound(ui->fpsSpinner->value() * 100000.0) == 2997003) { - p->set("frame_rate_num", 30000); - p->set("frame_rate_den", 1001); - } else if (qRound(ui->fpsSpinner->value() * 1000000.0) == 47952048) { - p->set("frame_rate_num", 48000); - p->set("frame_rate_den", 1001); - } else if (qRound(ui->fpsSpinner->value() * 100000.0) == 5994006) { - p->set("frame_rate_num", 60000); - p->set("frame_rate_den", 1001); - } else { - // Workaround storing QDoubleSpinBox::value() loses precision. - p->set("frame_rate_num", qRound(ui->fpsSpinner->value() * 1000000.0)); - p->set("frame_rate_den", 1000000); - } + int numerator, denominator; + Util::normalizeFrameRate(ui->fpsSpinner->value(), numerator, denominator); + p->set("frame_rate_num", numerator); + p->set("frame_rate_den", denominator); } if (ui->formatCombo->currentText() == "image2") setIfNotSet(p, "threads", 1); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/docks/timelinedock.cpp new/shotcut-21.09.13/src/docks/timelinedock.cpp --- old/shotcut-21.08.29/src/docks/timelinedock.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/docks/timelinedock.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -1233,7 +1233,7 @@ Mlt::Producer producer(MLT.isClip()? MLT.producer() : MLT.savedProducer()); ProxyManager::generateIfNotExists(producer); xmlToUse = MLT.XML(&producer); - } else if (xmlToUse.isEmpty()) { + } else if (!xml.isEmpty()) { xmlToUse = convertUrlsToXML(xml); if (xml.startsWith(kFileUrlProtocol) && xml.split(kFilesUrlDelimiter).size() > 1) { selectBlocker.reset(new TimelineSelectionBlocker(*this)); @@ -1313,7 +1313,7 @@ Mlt::Producer producer(MLT.isClip()? MLT.producer() : MLT.savedProducer()); ProxyManager::generateIfNotExists(producer); xmlToUse = MLT.XML(&producer); - } else if (xmlToUse.isEmpty()) { + } else if (!xml.isEmpty()) { xmlToUse = convertUrlsToXML(xml); if (xml.startsWith(kFileUrlProtocol) && xml.split(kFilesUrlDelimiter).size() > 1) { selectBlocker.reset(new TimelineSelectionBlocker(*this)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/main.cpp new/shotcut-21.09.13/src/main.cpp --- old/shotcut-21.08.29/src/main.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/main.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -45,6 +45,7 @@ } #endif +static const int kMaxCacheCount = 5000; #ifdef Q_OS_WIN static const char* kDefaultScaleRoundPolicy = "RoundPreferFloor"; #else @@ -370,6 +371,26 @@ splash.show(); a.processEvents(); + // Expire old items from the qmlcache + splash.showMessage(QCoreApplication::translate("main", "Expiring cache..."), Qt::AlignRight | Qt::AlignVCenter); + a.processEvents(); + auto dir = QDir(QStandardPaths::standardLocations(QStandardPaths::CacheLocation).constFirst()); + if (dir.exists() && dir.cd("qmlcache")) { + auto ls = dir.entryList(QDir::Files | QDir::Readable | QDir::NoDotAndDotDot, QDir::Time); + if (qMax(0, ls.size() - kMaxCacheCount) > 0) { + LOG_INFO() << "removing" << qMax(0, ls.size() - kMaxCacheCount) << "from" << dir.path(); + } + for (int i = kMaxCacheCount; i < ls.size(); i++) { + QString filePath = dir.filePath(ls[i]); + if (!QFile::remove(filePath)) { + LOG_WARNING() << "failed to delete" << filePath; + } + if (i % 1000 == 0) { + a.processEvents(); + } + } + } + QQuickStyle::setStyle("Fusion"); a.setProperty("system-style", a.style()->objectName()); MainWindow::changeTheme(Settings.theme()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/mainwindow.cpp new/shotcut-21.09.13/src/mainwindow.cpp --- old/shotcut-21.08.29/src/mainwindow.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/mainwindow.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -188,6 +188,7 @@ connect(ui->actionAbout_Qt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); connect(this, &MainWindow::producerOpened, this, &MainWindow::onProducerOpened); connect(ui->mainToolBar, SIGNAL(visibilityChanged(bool)), SLOT(onToolbarVisibilityChanged(bool))); + ui->actionSave->setEnabled(false); // Accept drag-n-drop of files. this->setAcceptDrops(true); @@ -2687,6 +2688,7 @@ Util::getHash(*MLT.producer()); ui->actionPaste->setEnabled(true); } + ui->actionSave->setEnabled(true); QMutexLocker locker(&m_autosaveMutex); if (m_autosaveFile) setCurrentFile(m_autosaveFile->managedFileName()); @@ -3960,7 +3962,13 @@ { if (continueModified()) { LOG_DEBUG() << ""; + QMutexLocker locker(&m_autosaveMutex); + m_autosaveFile.reset(); + locker.unlock(); + setCurrentFile(""); + MLT.resetURL(); MLT.setProjectFolder(QString()); + ui->actionSave->setEnabled(false); MLT.stop(); if (multitrack()) m_timelineDock->model()->close(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/mltcontroller.cpp new/shotcut-21.09.13/src/mltcontroller.cpp --- old/shotcut-21.08.29/src/mltcontroller.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/mltcontroller.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -970,7 +970,7 @@ return newUid; } -void Controller::copyFilters(Producer& fromProducer, Producer& toProducer, bool fromClipboard) +void Controller::copyFilters(Producer& fromProducer, Producer& toProducer, bool fromClipboard, bool includeDisabled) { int in = fromProducer.get(kFilterInProperty)? fromProducer.get_int(kFilterInProperty) : fromProducer.get_in(); int out = fromProducer.get(kFilterOutProperty)? fromProducer.get_int(kFilterOutProperty) : fromProducer.get_out(); @@ -978,7 +978,8 @@ for (int i = 0; i < count; i++) { QScopedPointer<Mlt::Filter> fromFilter(fromProducer.filter(i)); - if (fromFilter && fromFilter->is_valid() && !fromFilter->get_int("_loader") && fromFilter->get("mlt_service")) { + if (fromFilter && fromFilter->is_valid() && !fromFilter->get_int("_loader") && fromFilter->get("mlt_service") + && (includeDisabled || !fromFilter->get_int("disable"))) { Mlt::Filter toFilter(MLT.profile(), fromFilter->get("mlt_service")); if (toFilter.is_valid()) { toFilter.inherit(*fromFilter); @@ -1015,10 +1016,10 @@ { if (producer && producer->is_valid()) { initFiltersClipboard(); - copyFilters(*producer, *m_filtersClipboard); + copyFilters(*producer, *m_filtersClipboard, false, false); } else if (m_producer && m_producer->is_valid()) { initFiltersClipboard(); - copyFilters(*m_producer, *m_filtersClipboard); + copyFilters(*m_producer, *m_filtersClipboard, false, false); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/mltcontroller.h new/shotcut-21.09.13/src/mltcontroller.h --- old/shotcut-21.08.29/src/mltcontroller.h 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/mltcontroller.h 2021-09-13 07:05:06.000000000 +0200 @@ -123,7 +123,7 @@ QUuid uuid(Mlt::Properties &properties) const; void setUuid(Mlt::Properties &properties, QUuid uid) const; QUuid ensureHasUuid(Mlt::Properties& properties) const; - static void copyFilters(Mlt::Producer& fromProducer, Mlt::Producer& toProducer, bool fromClipboard = false); + static void copyFilters(Mlt::Producer& fromProducer, Mlt::Producer& toProducer, bool fromClipboard = false, bool includeDisabled = true); void copyFilters(Mlt::Producer* producer = nullptr); void pasteFilters(Mlt::Producer* producer = nullptr, Mlt::Producer* fromProducer = nullptr); static void adjustFilters(Mlt::Producer& producer, int startIndex = 0); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/models/multitrackmodel.cpp new/shotcut-21.09.13/src/models/multitrackmodel.cpp --- old/shotcut-21.08.29/src/models/multitrackmodel.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/models/multitrackmodel.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -1365,13 +1365,18 @@ if (Settings.playerGPU()) { Mlt::Filter f(MLT.profile(), "movit.opacity"); f.set(kShotcutFilterProperty, "fadeInMovit"); - f.set("alpha", 1); + f.set("alpha", i == bottomVideoTrackMltIndex()? 1 : -1); info->producer->attach(f); filter.reset(new Mlt::Filter(f)); } else { Mlt::Filter f(MLT.profile(), "brightness"); f.set(kShotcutFilterProperty, "fadeInBrightness"); - f.set("alpha", 1); + if (i == bottomVideoTrackMltIndex()) { + f.set("alpha", 1); + } else { + f.set("alpha", 0); + f.set("level", 1); + } info->producer->attach(f); filter.reset(new Mlt::Filter(f)); } @@ -1473,13 +1478,18 @@ if (Settings.playerGPU()) { Mlt::Filter f(MLT.profile(), "movit.opacity"); f.set(kShotcutFilterProperty, "fadeOutMovit"); - f.set("alpha", 1); + f.set("alpha", i == bottomVideoTrackMltIndex()? 1 : -1); info->producer->attach(f); filter.reset(new Mlt::Filter(f)); } else { Mlt::Filter f(MLT.profile(), "brightness"); f.set(kShotcutFilterProperty, "fadeOutBrightness"); - f.set("alpha", 1); + if (i == bottomVideoTrackMltIndex()) { + f.set("alpha", 1); + } else { + f.set("alpha", 0); + f.set("level", 1); + } info->producer->attach(f); filter.reset(new Mlt::Filter(f)); } @@ -2455,14 +2465,20 @@ return transition; } -void MultitrackModel::refreshVideoBlendTransitions() +int MultitrackModel::bottomVideoTrackMltIndex() const { // Get the MLT index of the bottom-most video track - int a_track = 0; + int track = 0; for (auto& t : m_trackList) { if (t.type == VideoTrackType) - a_track = t.mlt_index; + track = t.mlt_index; } + return track; +} + +void MultitrackModel::refreshVideoBlendTransitions() +{ + int a_track = bottomVideoTrackMltIndex(); // For each video track for (auto& t : m_trackList) { if (t.type == VideoTrackType) { @@ -3319,11 +3335,7 @@ } // Change a_track of composite transitions to bottom video track. - int a_track = 0; - foreach (Track t, m_trackList) { - if (t.type == VideoTrackType) - a_track = t.mlt_index; - } + int a_track = bottomVideoTrackMltIndex(); foreach (Track t, m_trackList) { if (t.type == VideoTrackType) { QScopedPointer<Mlt::Transition> transition(getVideoBlendTransition(t.mlt_index)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/models/multitrackmodel.h new/shotcut-21.09.13/src/models/multitrackmodel.h --- old/shotcut-21.08.29/src/models/multitrackmodel.h 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/models/multitrackmodel.h 2021-09-13 07:05:06.000000000 +0200 @@ -194,6 +194,7 @@ bool warnIfInvalid(Mlt::Service& service); Mlt::Transition* getVideoBlendTransition(int trackIndex) const; void refreshVideoBlendTransitions(); + int bottomVideoTrackMltIndex() const; friend class UndoHelper; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/player.cpp new/shotcut-21.09.13/src/player.cpp --- old/shotcut-21.08.29/src/player.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/player.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -690,7 +690,7 @@ onProducerOpened(false); } int position = frame.get_position(); - if (position < m_duration) { + if (position <= m_duration) { m_position = position; m_positionSpinner->blockSignals(true); m_positionSpinner->setValue(position); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml --- old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml 1970-01-01 01:00:00.000000000 +0100 +++ new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/meta.qml 2021-09-13 07:05:06.000000000 +0200 @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +import QtQuick 2.0 +import org.shotcut.qml 1.0 + +Metadata { + type: Metadata.Filter + name: qsTr("360: Equirectangular to Stereographic") + mlt_service: "frei0r.bigsh0t_eq_to_stereo" + objectName: "bigsh0t_eq_to_stereo" + qml: "ui.qml" + vui: "vui.qml" + keyframes { + allowAnimateIn: true + allowAnimateOut: true + simpleProperties: ['yaw', "pitch", "roll", "fov", "amount"] + parameters: [ + Parameter { + name: qsTr('Yaw') + property: 'yaw' + isSimple: true + isCurve: true + minimum: -360 + maximum: 360 + }, + Parameter { + name: qsTr('Pitch') + property: 'pitch' + isSimple: true + isCurve: true + minimum: -180 + maximum: 180 + }, + Parameter { + name: qsTr('Roll') + property: 'roll' + isSimple: true + isCurve: true + minimum: -180 + maximum: 180 + }, + Parameter { + name: qsTr('FOV') + property: 'fov' + isSimple: true + isCurve: true + minimum: 0 + maximum: 180 + }, + Parameter { + name: qsTr('Amount') + property: 'amount' + isSimple: true + isCurve: true + minimum: 0 + maximum: 100 + } + ] + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml --- old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml 1970-01-01 01:00:00.000000000 +0100 +++ new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/ui.qml 2021-09-13 07:05:06.000000000 +0200 @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import Shotcut.Controls 1.0 as Shotcut + + +Item { + width: 350 + height: 200 + property bool blockUpdate: true + + property double yawStart : 0.0; property double yawMiddle : 0.0; property double yawEnd : 0.0; + property double pitchStart : 0.0; property double pitchMiddle : 0.0; property double pitchEnd : 0.0; + property double rollStart : 0.0; property double rollMiddle : 0.0; property double rollEnd : 0.0; + property double fovStart : 0.0; property double fovMiddle : 0.0; property double fovEnd : 0.0; + property double amountStart : 0.0; property double amountMiddle : 0.0; property double amountEnd : 0.0; + property int interpolationValue : 0; + + function updateSimpleKeyframes() { + if (filter.animateIn <= 0 && filter.animateOut <= 0) { + // When disabling simple keyframes. Clear out the keyframes before proceeding. + filter.blockSignals = true + if (!yawKeyframesButton.checked && filter.keyframeCount("yaw") > 0) { + filter.resetProperty("yaw") + } + if (!pitchKeyframesButton.checked && filter.keyframeCount("pitch") > 0) { + filter.resetProperty("pitch") + } + if (!rollKeyframesButton.checked && filter.keyframeCount("roll") > 0) { + filter.resetProperty("roll") + } + if (!fovKeyframesButton.checked && filter.keyframeCount("fov") > 0) { + filter.resetProperty("fov") + } + if (!amountKeyframesButton.checked && filter.keyframeCount("amount") > 0) { + filter.resetProperty("amount") + } + filter.blockSignals = false + } else if (filter.animateIn > 0 || filter.animateOut > 0) { + // When enabling simple keyframes, initialize the keyframes with the current value + if (filter.keyframeCount("yaw") <= 0) { + yawStart = yawMiddle = yawEnd = filter.getDouble("yaw") + } + if (filter.keyframeCount("pitch") <= 0) { + pitchStart = pitchMiddle = pitchEnd = filter.getDouble("pitch") + } + if (filter.keyframeCount("roll") <= 0) { + rollStart = rollMiddle = rollEnd = filter.getDouble("roll") + } + if (filter.keyframeCount("fov") <= 0) { + fovStart = fovMiddle = fovEnd = filter.getDouble("fov") + } + if (filter.keyframeCount("amount") <= 0) { + amountStart = amountMiddle = amountEnd = filter.getDouble("amount") + } + } + updateProperty_yaw (null) + updateProperty_pitch (null) + updateProperty_roll (null) + updateProperty_fov (null) + updateProperty_amount (null) + updateProperty_interpolation () + } + + Connections { + target: filter + onChanged: setControls() + onInChanged: updateSimpleKeyframes() + onOutChanged: updateSimpleKeyframes() + onAnimateInChanged: updateSimpleKeyframes() + onAnimateOutChanged: updateSimpleKeyframes() + } + + Component.onCompleted: { + if (filter.isNew) { filter.set("yaw", 0); } else { yawMiddle = filter.getDouble("yaw", filter.animateIn); if (filter.animateIn > 0) { yawStart = filter.getDouble("yaw", 0); } if (filter.animateOut > 0) { yawEnd = filter.getDouble("yaw", filter.duration - 1); } } + if (filter.isNew) { filter.set("pitch", -90); } else { pitchMiddle = filter.getDouble("pitch", filter.animateIn); if (filter.animateIn > 0) { pitchStart = filter.getDouble("pitch", 0); } if (filter.animateOut > 0) { pitchEnd = filter.getDouble("pitch", filter.duration - 1); } } + if (filter.isNew) { filter.set("roll", 0); } else { rollMiddle = filter.getDouble("roll", filter.animateIn); if (filter.animateIn > 0) { rollStart = filter.getDouble("roll", 0); } if (filter.animateOut > 0) { rollEnd = filter.getDouble("roll", filter.duration - 1); } } + if (filter.isNew) { filter.set("fov", 160); } else { fovMiddle = filter.getDouble("fov", filter.animateIn); if (filter.animateIn > 0) { fovStart = filter.getDouble("fov", 0); } if (filter.animateOut > 0) { fovEnd = filter.getDouble("fov", filter.duration - 1); } } + if (filter.isNew) { filter.set("amount", 100); } else { amountMiddle = filter.getDouble("amount", filter.animateIn); if (filter.animateIn > 0) { amountStart = filter.getDouble("amount", 0); } if (filter.animateOut > 0) { amountEnd = filter.getDouble("amount", filter.duration - 1); } } + if (filter.isNew) { filter.set("interpolation", 1); } else { interpolationValue = filter.get("interpolation"); } + + if (filter.isNew) { + filter.savePreset(preset.parameters) + } + setControls() + } + + function setControls() { + var position = getPosition() + blockUpdate = true + yawSlider.value = filter.getDouble("yaw", position) + yawKeyframesButton.checked = filter.animateIn <= 0 && filter.animateOut <= 0 && filter.keyframeCount("yaw") > 0 + pitchSlider.value = filter.getDouble("pitch", position) + pitchKeyframesButton.checked = filter.animateIn <= 0 && filter.animateOut <= 0 && filter.keyframeCount("pitch") > 0 + rollSlider.value = filter.getDouble("roll", position) + rollKeyframesButton.checked = filter.animateIn <= 0 && filter.animateOut <= 0 && filter.keyframeCount("roll") > 0 + fovSlider.value = filter.getDouble("fov", position) + fovKeyframesButton.checked = filter.animateIn <= 0 && filter.animateOut <= 0 && filter.keyframeCount("fov") > 0 + amountSlider.value = filter.getDouble("amount", position) + interpolationComboBox.currentIndex = filter.get("interpolation") + blockUpdate = false + } + + function updateProperty_yaw (position) { if (blockUpdate) return; var value = yawSlider.value; if (position !== null) { if (position <= 0 && filter.animateIn > 0) { yawStart = value; } else if (position >= filter.duration - 1 && filter.animateOut > 0) { yawEnd = value; } else { yawMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("yaw"); yawKeyframesButton.checked = false; if (filter.animateIn > 0) { filter.set("yaw", yawStart, 0); filter.set("yaw", yawMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { filter.set("yaw", yawMiddle, filter.duration - filter.animateOut); filter.set("yaw", yawEnd, filter.duration - 1); } } else if (!yawKeyframesButton.checked) { filter.resetProperty("yaw"); filter.set("yaw", yawMiddle); } else if (position !== null) { filter.set("yaw", value, position); } } + function updateProperty_pitch (position) { if (blockUpdate) return; var value = pitchSlider.value; if (position !== null) { if (position <= 0 && filter.animateIn > 0) { pitchStart = value; } else if (position >= filter.duration - 1 && filter.animateOut > 0) { pitchEnd = value; } else { pitchMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("pitch"); pitchKeyframesButton.checked = false; if (filter.animateIn > 0) { filter.set("pitch", pitchStart, 0); filter.set("pitch", pitchMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { filter.set("pitch", pitchMiddle, filter.duration - filter.animateOut); filter.set("pitch", pitchEnd, filter.duration - 1); } } else if (!pitchKeyframesButton.checked) { filter.resetProperty("pitch"); filter.set("pitch", pitchMiddle); } else if (position !== null) { filter.set("pitch", value, position); } } + function updateProperty_roll (position) { if (blockUpdate) return; var value = rollSlider.value; if (position !== null) { if (position <= 0 && filter.animateIn > 0) { rollStart = value; } else if (position >= filter.duration - 1 && filter.animateOut > 0) { rollEnd = value; } else { rollMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("roll"); rollKeyframesButton.checked = false; if (filter.animateIn > 0) { filter.set("roll", rollStart, 0); filter.set("roll", rollMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { filter.set("roll", rollMiddle, filter.duration - filter.animateOut); filter.set("roll", rollEnd, filter.duration - 1); } } else if (!rollKeyframesButton.checked) { filter.resetProperty("roll"); filter.set("roll", rollMiddle); } else if (position !== null) { filter.set("roll", value, position); } } + function updateProperty_fov (position) { if (blockUpdate) return; var value = fovSlider.value; if (position !== null) { if (position <= 0 && filter.animateIn > 0) { fovStart = value; } else if (position >= filter.duration - 1 && filter.animateOut > 0) { fovEnd = value; } else { fovMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("fov"); fovKeyframesButton.checked = false; if (filter.animateIn > 0) { filter.set("fov", fovStart, 0); filter.set("fov", fovMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { filter.set("fov", fovMiddle, filter.duration - filter.animateOut); filter.set("fov", fovEnd, filter.duration - 1); } } else if (!fovKeyframesButton.checked) { filter.resetProperty("fov"); filter.set("fov", fovMiddle); } else if (position !== null) { filter.set("fov", value, position); } } + function updateProperty_amount (position) { if (blockUpdate) return; var value = amountSlider.value; if (position !== null) { if (position <= 0 && filter.animateIn > 0) { amountStart = value; } else if (position >= filter.duration - 1 && filter.animateOut > 0) { amountEnd = value; } else { amountMiddle = value; } } if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("amount"); amountKeyframesButton.checked = false; if (filter.animateIn > 0) { filter.set("amount", amountStart, 0); filter.set("amount", amountMiddle, filter.animateIn - 1); } if (filter.animateOut > 0) { filter.set("amount", amountMiddle, filter.duration - filter.animateOut); filter.set("amount", amountEnd, filter.duration - 1); } } else if (!amountKeyframesButton.checked) { filter.resetProperty("amount"); filter.set("amount", amountMiddle); } else if (position !== null) { filter.set("amount", value, position); } } + function updateProperty_interpolation () { if (blockUpdate) return; var value = interpolationComboBox.currentIndex; filter.set("interpolation", value); } + + function getPosition() { + return Math.max(producer.position - (filter.in - producer.in), 0) + } + + GridLayout { + columns: 4 + anchors.fill: parent + anchors.margins: 8 + + Label { + text: qsTr('Preset') + Layout.alignment: Qt.AlignRight + } + Shotcut.Preset { + id: preset + parameters: ["yaw", "pitch", "roll", "fov", "interpolation", "amount"] + Layout.columnSpan: 3 + onBeforePresetLoaded: { + filter.resetProperty('yaw') + filter.resetProperty('pitch') + filter.resetProperty('roll') + filter.resetProperty('fov') + filter.resetProperty('amount') + filter.resetProperty('interpolation') + } + onPresetSelected: { + yawMiddle = filter.getDouble("yaw", filter.animateIn); if (filter.animateIn > 0) { yawStart = filter.getDouble("yaw", 0); } if (filter.animateOut > 0) { yawEnd = filter.getDouble("yaw", filter.duration - 1); } + pitchMiddle = filter.getDouble("pitch", filter.animateIn); if (filter.animateIn > 0) { pitchStart = filter.getDouble("pitch", 0); } if (filter.animateOut > 0) { pitchEnd = filter.getDouble("pitch", filter.duration - 1); } + rollMiddle = filter.getDouble("roll", filter.animateIn); if (filter.animateIn > 0) { rollStart = filter.getDouble("roll", 0); } if (filter.animateOut > 0) { rollEnd = filter.getDouble("roll", filter.duration - 1); } + fovMiddle = filter.getDouble("fov", filter.animateIn); if (filter.animateIn > 0) { fovStart = filter.getDouble("fov", 0); } if (filter.animateOut > 0) { fovEnd = filter.getDouble("fov", filter.duration - 1); } + amountMiddle = filter.getDouble("amount", filter.animateIn); if (filter.animateIn > 0) { amountStart = filter.getDouble("amount", 0); } if (filter.animateOut > 0) { amountEnd = filter.getDouble("amount", filter.duration - 1); } + interpolationValue = filter.get("interpolation"); + setControls(null); + } + } + + Label { + text: qsTr('Interpolation') + Layout.alignment: Qt.AlignRight + } + Shotcut.ComboBox { + currentIndex: 0 + implicitWidth: 180 + model: ["Nearest-neighbor", "Bilinear"] + id: interpolationComboBox + Layout.columnSpan: 2 + onCurrentIndexChanged: updateProperty_interpolation() + } + Shotcut.UndoButton { + id: interpolationUndo + onClicked: interpolationComboBox.currentIndex = 0 + } + + Label { + text: qsTr('Yaw') + Layout.alignment: Qt.AlignRight + } + Shotcut.SliderSpinner { + id: yawSlider + minimumValue: -360 + maximumValue: 360 + spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1; + onValueChanged: updateProperty_yaw(getPosition()) + } + Shotcut.KeyframesButton { id: yawKeyframesButton; onToggled: { var value = yawSlider.value; if (checked) { blockUpdate = true; if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("yaw"); yawSlider.enabled = true; } filter.clearSimpleAnimation("yaw"); blockUpdate = false; filter.set("yaw", value, getPosition()); } else { filter.resetProperty("yaw"); filter.set("yaw", value); } } } + Shotcut.UndoButton { + id: yawUndo + onClicked: yawSlider.value = 0 + } + + Label { + text: qsTr('Pitch') + Layout.alignment: Qt.AlignRight + } + Shotcut.SliderSpinner { + id: pitchSlider + minimumValue: -180 + maximumValue: 180 + spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1; + onValueChanged: updateProperty_pitch(getPosition()) + } + Shotcut.KeyframesButton { id: pitchKeyframesButton; onToggled: { var value = pitchSlider.value; if (checked) { blockUpdate = true; if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("pitch"); pitchSlider.enabled = true; } filter.clearSimpleAnimation("pitch"); blockUpdate = false; filter.set("pitch", value, getPosition()); } else { filter.resetProperty("pitch"); filter.set("pitch", value); } } } + Shotcut.UndoButton { + id: pitchUndo + onClicked: pitchSlider.value = -90 + } + + Label { + text: qsTr('Roll') + Layout.alignment: Qt.AlignRight + } + Shotcut.SliderSpinner { + id: rollSlider + minimumValue: -180 + maximumValue: 180 + spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1; + onValueChanged: updateProperty_roll(getPosition()) + } + Shotcut.KeyframesButton { id: rollKeyframesButton; onToggled: { var value = rollSlider.value; if (checked) { blockUpdate = true; if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("roll"); rollSlider.enabled = true; } filter.clearSimpleAnimation("roll"); blockUpdate = false; filter.set("roll", value, getPosition()); } else { filter.resetProperty("roll"); filter.set("roll", value); } } } + Shotcut.UndoButton { + id: rollUndo + onClicked: rollSlider.value = 0 + } + + Label { + text: qsTr('FOV') + Layout.alignment: Qt.AlignRight + } + Shotcut.SliderSpinner { + id: fovSlider + minimumValue: 0 + maximumValue: 180 + spinnerWidth: 120; suffix: ' deg'; decimals: 3; stepSize: 1; + onValueChanged: updateProperty_fov(getPosition()) + } + Shotcut.KeyframesButton { id: fovKeyframesButton; onToggled: { var value = fovSlider.value; if (checked) { blockUpdate = true; if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("fov"); fovSlider.enabled = true; } filter.clearSimpleAnimation("fov"); blockUpdate = false; filter.set("fov", value, getPosition()); } else { filter.resetProperty("fov"); filter.set("fov", value); } } } + Shotcut.UndoButton { + id: fovUndo + onClicked: fovSlider.value = 160 + } + + Label { + text: qsTr('Amount') + Layout.alignment: Qt.AlignRight + } + Shotcut.SliderSpinner { + id: amountSlider + minimumValue: 0 + maximumValue: 100 + suffix: ' %' + decimals: 0 + stepSize: 1 + onValueChanged: updateProperty_amount(getPosition()) + } + Shotcut.KeyframesButton { id: amountKeyframesButton; onToggled: { var value = amountSlider.value; if (checked) { blockUpdate = true; if (filter.animateIn > 0 || filter.animateOut > 0) { filter.resetProperty("amount"); amountSlider.enabled = true; } filter.clearSimpleAnimation("amount"); blockUpdate = false; filter.set("amount", value, getPosition()); } else { filter.resetProperty("amount"); filter.set("amount", value); } } } + Shotcut.UndoButton { + id: amountUndo + onClicked: amountSlider.value = 100 + } + Item { + Layout.fillHeight: true + } + } + + Connections { + target: filter + onPropertyChanged: setControls() + } + + Connections { + target: producer + onPositionChanged: setControls() + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml --- old/shotcut-21.08.29/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml 1970-01-01 01:00:00.000000000 +0100 +++ new/shotcut-21.09.13/src/qml/filters/bigsh0t_eq_to_stereo/vui.qml 2021-09-13 07:05:06.000000000 +0200 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Meltytech, LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import QtQuick 2.1 +import Shotcut.Controls 1.0 as Shotcut + +Shotcut.VuiBase { + property var keyframableParameters: ['yaw', 'pitch', 'roll', 'fov'] + property var startValues: [] + property var middleValues: [] + property var endValues: [] + + Connections { + target: filter + onChanged: mouseArea.enabled = filter.get('disable') !== '1' + } + + MouseArea { + id: mouseArea + anchors.fill: parent + + property double startYaw + property double startPitch + property double startRoll + property int startX + property int startY + onPressed: { + startX = mouse.x + startY = mouse.y + startYaw = filter.getDouble('yaw', getPosition()) + startPitch = filter.getDouble('pitch', getPosition()) + startRoll = filter.getDouble('roll', getPosition()) + } + onPositionChanged: { + if (mouse.modifiers == Qt.ControlModifier) { + updateProperty('roll', clamp(startRoll + 0.5 * (startY - mouse.y), -180, 180)) + } else { + updateProperty('yaw', clamp(startYaw + 0.2 * (startX - mouse.x), -360, 360)) + updateProperty('pitch', clamp(startPitch + 0.1 * (mouse.y - startY), -180, 180)) + } + } + onWheel: { + var fov = filter.getDouble('fov', getPosition()) + updateProperty('fov', clamp(fov + 0.03 * wheel.angleDelta.y, 0, 180)) + } + } + + function clamp(x, min, max) { + return Math.max(min, Math.min(max, x)) + } + + function getPosition() { + return Math.max(producer.position - (filter.in - producer.in), 0) + } + + function updateProperty(name, value) { + var index = keyframableParameters.indexOf(name) + var position = getPosition() + + if (position !== null) { + if (position <= 0 && filter.animateIn > 0) { + startValues[index] = value + } else if (position >= filter.duration - 1 && filter.animateOut > 0) { + endValues[index] = value + } else { + middleValues[index] = value + } + } if (filter.animateIn > 0 || filter.animateOut > 0) { + filter.resetProperty(name) + if (filter.animateIn > 0) { + filter.set(name, startValues[index], 0) + filter.set(name, middleValues[index], filter.animateIn - 1) + } + if (filter.animateOut > 0) { + filter.set(name, middleValues[index], filter.duration - filter.animateOut) + filter.set(name, endValues[index], filter.duration - 1) + } + } else if (filter.keyframeCount(name) <= 0) { + filter.resetProperty(name) + filter.set(name, middleValues[index]) + } else if (position !== null) { + filter.set(name, value, position) + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/qml/filters/fadein_movit/ui.qml new/shotcut-21.09.13/src/qml/filters/fadein_movit/ui.qml --- old/shotcut-21.08.29/src/qml/filters/fadein_movit/ui.qml 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/qml/filters/fadein_movit/ui.qml 2021-09-13 07:05:06.000000000 +0200 @@ -19,6 +19,7 @@ import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import Shotcut.Controls 1.0 as Shotcut +import org.shotcut.qml 1.0 Item { width: 100 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/qml/filters/fadeout_movit/ui.qml new/shotcut-21.09.13/src/qml/filters/fadeout_movit/ui.qml --- old/shotcut-21.08.29/src/qml/filters/fadeout_movit/ui.qml 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/qml/filters/fadeout_movit/ui.qml 2021-09-13 07:05:06.000000000 +0200 @@ -20,6 +20,7 @@ import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import Shotcut.Controls 1.0 as Shotcut +import org.shotcut.qml 1.0 Item { width: 100 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/util.cpp new/shotcut-21.09.13/src/util.cpp --- old/shotcut-21.08.29/src/util.cpp 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/util.cpp 2021-09-13 07:05:06.000000000 +0200 @@ -164,13 +164,13 @@ static inline bool isValidGoProFirstFilePrefix(const QFileInfo& info) { - QStringList list {"GOPR", "GH01", "GS01", "GX01"}; + QStringList list {"GOPR", "GH01", "GL01", "GM01", "GS01", "GX01"}; return list.contains(info.baseName().left(4).toUpper()); } static inline bool isValidGoProPrefix(const QFileInfo& info) { - QStringList list {"GP", "GH", "GS", "GX"}; + QStringList list {"GP", "GH", "GL", "GM", "GS", "GX"}; return list.contains(info.baseName().left(2).toUpper()); } @@ -544,3 +544,25 @@ gcd = m; return gcd; } + +void Util::normalizeFrameRate(double fps, int& numerator, int& denominator) +{ + // Convert some common non-integer frame rates to fractions. + if (qRound(fps * 1000000.0) == 23976024) { + numerator = 24000; + denominator = 1001; + } else if (qRound(fps * 100000.0) == 2997003) { + numerator = 30000; + denominator = 1001; + } else if (qRound(fps * 1000000.0) == 47952048) { + numerator = 48000; + denominator = 1001; + } else if (qRound(fps * 100000.0) == 5994006) { + numerator = 60000; + denominator = 1001; + } else { + // Workaround storing QDoubleSpinBox::value() loses precision. + numerator = qRound(fps * 1000000.0); + denominator = 1000000; + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shotcut-21.08.29/src/util.h new/shotcut-21.09.13/src/util.h --- old/shotcut-21.08.29/src/util.h 2021-08-29 07:29:22.000000000 +0200 +++ new/shotcut-21.09.13/src/util.h 2021-09-13 07:05:06.000000000 +0200 @@ -60,6 +60,7 @@ static bool isMemoryLow(); static QString removeQueryString(const QString& s); static int greatestCommonDivisor(int m, int n); + static void normalizeFrameRate(double fps, int& numerator, int& denominator); }; #endif // UTIL_H