From 095c3bd8d6827cf043894422d4c824c2e1a0989a Mon Sep 17 00:00:00 2001 From: naj59 Date: Fri, 1 Mar 2019 09:24:38 +0100 Subject: [PATCH 1/8] readme and issue template update --- ISSUE_TEMPLATE.md | 1 + README.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 15137c1b6..0fb20820e 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -5,6 +5,7 @@ __Olive version:__ __Source:__ *(AppImage, Website etc.)* __Operating system:__ *(Ubuntu 18.04 64-bit)* __CPU:__ *Intel i5-4300U* +__RAM:__ *8GB* __GPU:__ *NVIDIA Geforce GT 1030 2GB (Driver ver xxx.xx.xx)* ### Detailed Description diff --git a/README.md b/README.md index 26468ee41..73658dc7b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -# Olive Video Editor +# Olive Video Editor [![Build Status](https://travis-ci.org/olive-editor/olive.svg?branch=master)](https://travis-ci.org/olive-editor/olive) Olive is a free non-linear video editor for Windows, macOS, and Linux. +![screen](https://www.olivevideoeditor.org/img/screenshot.jpg) + Discover more and download binaries at: https://www.olivevideoeditor.org/ Please consider supporting Olive: From 82e0682a4d1c517ebab5189b016d475fa5067942 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 1 Mar 2019 09:08:32 -0800 Subject: [PATCH 2/8] see if explicit svg linking is actually necessary --- .travis/install.sh | 4 ++-- olive.pro | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index 217361a4f..d38d4e291 100644 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -8,11 +8,11 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then if [ "$ARCH" == "x86_64" ]; then - sudo apt-get -y install qt59base qt59multimedia qt59svg libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins fuse + sudo apt-get -y install qt59base qt59multimedia libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins fuse fi if [ "$ARCH" == "i386" ]; then - sudo apt-get -y install gcc-multilib g++-multilib qt59base:i386 qt59multimedia:i386 qt59svg:i386 libavformat-dev:i386 libavcodec-dev:i386 libavfilter-dev:i386 libavutil-dev:i386 libswscale-dev:i386 libswresample-dev:i386 frei0r-plugins-dev:i386 frei0r-plugins:i386 pkg-config:i386 libgl1-mesa-dev:i386 fuse:i386 + sudo apt-get -y install gcc-multilib g++-multilib qt59base:i386 qt59multimedia:i386 libavformat-dev:i386 libavcodec-dev:i386 libavfilter-dev:i386 libavutil-dev:i386 libswscale-dev:i386 libswresample-dev:i386 frei0r-plugins-dev:i386 frei0r-plugins:i386 pkg-config:i386 libgl1-mesa-dev:i386 fuse:i386 fi source /opt/qt*/bin/qt*-env.sh diff --git a/olive.pro b/olive.pro index 0a38ee49a..95066c9b0 100644 --- a/olive.pro +++ b/olive.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui multimedia opengl svg +QT += core gui multimedia opengl greaterThan(QT_MAJOR_VERSION, 4): QT += widgets From 9bdf9cf3e30262c1dbf4b8af8a01ba0707cb7eab Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 1 Mar 2019 09:31:22 -0800 Subject: [PATCH 3/8] travis dependencies updated --- .travis/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index d38d4e291..217361a4f 100644 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -8,11 +8,11 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then if [ "$ARCH" == "x86_64" ]; then - sudo apt-get -y install qt59base qt59multimedia libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins fuse + sudo apt-get -y install qt59base qt59multimedia qt59svg libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins fuse fi if [ "$ARCH" == "i386" ]; then - sudo apt-get -y install gcc-multilib g++-multilib qt59base:i386 qt59multimedia:i386 libavformat-dev:i386 libavcodec-dev:i386 libavfilter-dev:i386 libavutil-dev:i386 libswscale-dev:i386 libswresample-dev:i386 frei0r-plugins-dev:i386 frei0r-plugins:i386 pkg-config:i386 libgl1-mesa-dev:i386 fuse:i386 + sudo apt-get -y install gcc-multilib g++-multilib qt59base:i386 qt59multimedia:i386 qt59svg:i386 libavformat-dev:i386 libavcodec-dev:i386 libavfilter-dev:i386 libavutil-dev:i386 libswscale-dev:i386 libswresample-dev:i386 frei0r-plugins-dev:i386 frei0r-plugins:i386 pkg-config:i386 libgl1-mesa-dev:i386 fuse:i386 fi source /opt/qt*/bin/qt*-env.sh From 77e0960cf10713fc0c1dfbae8a4b76f98fc3893c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 1 Mar 2019 10:18:53 -0800 Subject: [PATCH 4/8] added svg dependency to all packing systems --- debian/control | 2 +- olive.pro | 2 +- snap/snapcraft.yaml | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/control b/debian/control index ef8994c44..c156631bf 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: olive-editor Section: video Priority: optional Maintainer: Olive Team -Build-Depends: debhelper (>=9), build-essential, qt5-default, qtmultimedia5-dev, libqt5opengl5-dev, libqt5multimedia5-plugins, libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev, libswresample-dev, libavfilter-dev, libpostproc-dev, git, frei0r-plugins-dev, qttools5-dev-tools +Build-Depends: debhelper (>=9), build-essential, qt5-default, qtmultimedia5-dev, libqt5opengl5-dev, libqt5svg5-dev, libqt5multimedia5-plugins, libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev, libswresample-dev, libavfilter-dev, libpostproc-dev, git, frei0r-plugins-dev, qttools5-dev-tools Standards-Version: 3.9.6 Homepage: https://olivevideoeditor.org/ diff --git a/olive.pro b/olive.pro index 95066c9b0..0a38ee49a 100644 --- a/olive.pro +++ b/olive.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui multimedia opengl +QT += core gui multimedia opengl svg greaterThan(QT_MAJOR_VERSION, 4): QT += widgets diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a31bf2697..9b8160989 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -31,7 +31,8 @@ parts: source: . build-packages: - qt5-default - - qtmultimedia5-dev + - qtmultimedia5-dev + - libqt5svg5-dev - libavformat-dev - libavfilter-dev - frei0r-plugins-dev From 64bad4de4efd6d675700a4d8b8b87461c89fd919 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 1 Mar 2019 10:19:57 -0800 Subject: [PATCH 5/8] fixed #572 --- panels/timeline.cpp | 7 +- ui/resizablescrollbar.cpp | 136 +++++++++++++++++++------------------- 2 files changed, 72 insertions(+), 71 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 6bdb36602..f0ce8d905 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -756,12 +756,13 @@ void Timeline::set_zoom_value(double v) { zoom_just_changed = true; // set scrollbar to center the playhead - if (olive::ActiveSequence != nullptr - && !horizontalScrollBar->is_resizing()) { + if (olive::ActiveSequence != nullptr) { // update scrollbar maximum value for new zoom set_sb_max(); - center_scroll_to_playhead(horizontalScrollBar, zoom, olive::ActiveSequence->playhead); + if (!horizontalScrollBar->is_resizing()) { + center_scroll_to_playhead(horizontalScrollBar, zoom, olive::ActiveSequence->playhead); + } } // repaint the timeline for the new zoom/location diff --git a/ui/resizablescrollbar.cpp b/ui/resizablescrollbar.cpp index 284a04073..aa23c960a 100644 --- a/ui/resizablescrollbar.cpp +++ b/ui/resizablescrollbar.cpp @@ -29,98 +29,98 @@ #define RESIZE_HANDLE_SIZE 10 ResizableScrollBar::ResizableScrollBar(QWidget *parent) : - QScrollBar(parent), - resize_init(false), - resize_proc(false) + QScrollBar(parent), + resize_init(false), + resize_proc(false) { - setSingleStep(20); - setMaximum(0); - setMouseTracking(true); + setSingleStep(20); + setMaximum(0); + setMouseTracking(true); } bool ResizableScrollBar::is_resizing() { - return resize_proc; + return resize_proc; } void ResizableScrollBar::resizeEvent(QResizeEvent *event) { - setPageStep(event->size().width()); + setPageStep(event->size().width()); } void ResizableScrollBar::mousePressEvent(QMouseEvent *e) { - if (resize_init) { - QStyleOptionSlider opt; - initStyleOption(&opt); + if (resize_init) { + QStyleOptionSlider opt; + initStyleOption(&opt); - QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, - QStyle::SC_ScrollBarSlider, this); + QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, + QStyle::SC_ScrollBarSlider, this); - resize_proc = true; - resize_start = e->pos().x(); + resize_proc = true; + resize_start = e->pos().x(); - resize_start_max = maximum(); - resize_start_width = sr.width(); - } else { - QScrollBar::mousePressEvent(e); - } + resize_start_max = maximum(); + resize_start_width = sr.width(); + } else { + QScrollBar::mousePressEvent(e); + } } void ResizableScrollBar::mouseMoveEvent(QMouseEvent *e) { - QStyleOptionSlider opt; - initStyleOption(&opt); + QStyleOptionSlider opt; + initStyleOption(&opt); - QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, - QStyle::SC_ScrollBarSlider, this); - QRect gr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, - QStyle::SC_ScrollBarGroove, this); + QRect sr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, + QStyle::SC_ScrollBarSlider, this); + QRect gr = style()->subControlRect(QStyle::CC_ScrollBar, &opt, + QStyle::SC_ScrollBarGroove, this); - if (resize_proc) { - int diff = (e->pos().x() - resize_start); - if (resize_top) diff = -diff; - double scale = double(sr.width())/double(sr.width()+diff); - if (!qIsInf(scale) && !qIsNull(scale)) { - emit resize_move(scale); - resize_start = e->pos().x(); + if (resize_proc) { + int diff = (e->pos().x() - resize_start); + if (resize_top) diff = -diff; + double scale = double(sr.width())/double(sr.width()+diff); + if (!qIsInf(scale) && !qIsNull(scale)) { + emit resize_move(scale); + resize_start = e->pos().x(); - if (resize_top) { - int slider_min = gr.x(); - int slider_max = gr.right() - (sr.width()+diff); - int val = QStyle::sliderValueFromPosition(minimum(), maximum(), e->pos().x() - slider_min, slider_max - slider_min, opt.upsideDown); + if (resize_top) { + int slider_min = gr.x(); + int slider_max = gr.right() - (sr.width()+diff); + int val = QStyle::sliderValueFromPosition(minimum(), maximum(), e->pos().x() - slider_min, slider_max - slider_min, opt.upsideDown); - setValue(val); - } else { - setValue(qRound(value() * scale)); - } - } - } else { - bool new_resize_init = false; + setValue(val); + } else { + setValue(qRound(value() * scale)); + } + } + } else { + bool new_resize_init = false; - if ((orientation() == Qt::Horizontal && e->pos().x() > sr.left()-RESIZE_HANDLE_SIZE && e->pos().x() < sr.left()+RESIZE_HANDLE_SIZE) - || (orientation() == Qt::Vertical && e->pos().y() > sr.top()-RESIZE_HANDLE_SIZE && e->pos().y() < sr.top()+RESIZE_HANDLE_SIZE)) { - new_resize_init = true; - resize_top = true; - } else if ((orientation() == Qt::Horizontal && e->pos().x() > sr.right()-RESIZE_HANDLE_SIZE && e->pos().x() < sr.right()+RESIZE_HANDLE_SIZE) - || (orientation() == Qt::Vertical && e->pos().y() > sr.bottom()-RESIZE_HANDLE_SIZE && e->pos().y() < sr.bottom()+RESIZE_HANDLE_SIZE)) { - new_resize_init = true; - resize_top = false; - } + if ((orientation() == Qt::Horizontal && e->pos().x() > sr.left()-RESIZE_HANDLE_SIZE && e->pos().x() < sr.left()+RESIZE_HANDLE_SIZE) + || (orientation() == Qt::Vertical && e->pos().y() > sr.top()-RESIZE_HANDLE_SIZE && e->pos().y() < sr.top()+RESIZE_HANDLE_SIZE)) { + new_resize_init = true; + resize_top = true; + } else if ((orientation() == Qt::Horizontal && e->pos().x() > sr.right()-RESIZE_HANDLE_SIZE && e->pos().x() < sr.right()+RESIZE_HANDLE_SIZE) + || (orientation() == Qt::Vertical && e->pos().y() > sr.bottom()-RESIZE_HANDLE_SIZE && e->pos().y() < sr.bottom()+RESIZE_HANDLE_SIZE)) { + new_resize_init = true; + resize_top = false; + } - if (resize_init != new_resize_init) { - if (new_resize_init) { - setCursor(Qt::SizeHorCursor); - } else { - unsetCursor(); - } - resize_init = new_resize_init; - } + if (resize_init != new_resize_init) { + if (new_resize_init) { + setCursor(Qt::SizeHorCursor); + } else { + unsetCursor(); + } + resize_init = new_resize_init; + } - QScrollBar::mouseMoveEvent(e); - } + QScrollBar::mouseMoveEvent(e); + } } void ResizableScrollBar::mouseReleaseEvent(QMouseEvent *e) { - if (resize_proc) { - resize_proc = false; - } else { - QScrollBar::mouseReleaseEvent(e); - } + if (resize_proc) { + resize_proc = false; + } else { + QScrollBar::mouseReleaseEvent(e); + } } From cabd2f9ca9753c8eed9bb6ded2f951576ec4f98f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 1 Mar 2019 11:56:32 -0800 Subject: [PATCH 6/8] fixed #571 --- mainwindow.cpp | 89 +++++++++++++++++++++++++++++++++++++-- panels/effectcontrols.cpp | 13 +++++- panels/effectcontrols.h | 5 +++ panels/project.cpp | 2 +- ui/panel.cpp | 7 +++ ui/panel.h | 4 ++ 6 files changed, 113 insertions(+), 7 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index bd2b25bc2..5421f99a9 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -76,17 +76,71 @@ void MainWindow::setup_layout(bool reset) { QFile panel_config(get_config_dir().filePath("layout")); if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { - // default to resetting unless we find the tag in the XML file + // default to resetting unless we find layout data in the XML file reset = true; // read XML layout file QXmlStreamReader stream(&panel_config); + + // loop through XML for all data while (!stream.atEnd()) { - stream.readNextStartElement(); - if (stream.name() == "panels") { - restoreState(QByteArray::fromBase64(stream.readElementText().toUtf8()), 0); + stream.readNext(); + + if (stream.name() == "panels" && stream.isStartElement()) { + + // element contains MainWindow layout data to restore + stream.readNext(); + restoreState(QByteArray::fromBase64(stream.text().toUtf8()), 0); reset = false; + + } else if (stream.name() == "panel" && stream.isStartElement()) { + + // element contains layout data specific to a panel, we'll find the panel and load it + + // get panel name from XML attribute + QString panel_name; + const QXmlStreamAttributes& attributes = stream.attributes(); + for (int i=0;iobjectName() == panel_name) { + + // found the panel, so we can load its state + stream.readNext(); + panel->LoadLayoutState(QByteArray::fromBase64(stream.text().toUtf8())); + + // we found it, no more need to loop through panels + found_panel = true; + + break; + + } + + } + + if (!found_panel) { + qWarning() << "Panel specified in layout data doesn't exist. Layout wasn't loaded."; + } + + } + } + } panel_config.close(); @@ -904,10 +958,37 @@ void MainWindow::closeEvent(QCloseEvent *e) { QFile panel_config(get_config_dir().filePath("layout")); if (panel_config.open(QFile::WriteOnly)) { QXmlStreamWriter stream(&panel_config); + stream.setAutoFormatting(true); stream.writeStartDocument(); + stream.writeStartElement("layout"); + stream.writeTextElement("panels", saveState(0).toBase64()); + // if the panels have any specific layout data to save, save it now + for (int i=0;iSaveLayoutState(); + + if (!layout_data.isEmpty()) { + + // layout data is matched with the panel's objectName(), which we can't do if the panel has no name + const QString& panel_name = olive::panels.at(i)->objectName(); + if (panel_name.isEmpty()) { + qWarning() << "Panel" << i << "had layout state data but no objectName(). Layout was not saved."; + } else { + stream.writeStartElement("panel"); + + stream.writeAttribute("name", panel_name); + + stream.writeCharacters(layout_data.toBase64()); + + stream.writeEndElement(); + } + } + } + + stream.writeEndElement(); // layout + stream.writeEndDocument(); panel_config.close(); } else { diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index b6aa690e3..174baa795 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -321,7 +320,7 @@ void EffectControls::setup_ui() { hlayout->setSpacing(0); hlayout->setMargin(0); - QSplitter* splitter = new QSplitter(); + splitter = new QSplitter(); splitter->setOrientation(Qt::Horizontal); splitter->setChildrenCollapsible(false); @@ -507,6 +506,16 @@ void EffectControls::Retranslate() { UpdateTitle(); } +void EffectControls::LoadLayoutState(const QByteArray &data) +{ + splitter->restoreState(data); +} + +QByteArray EffectControls::SaveLayoutState() +{ + return splitter->saveState(); +} + void EffectControls::update_scrollbar() { verticalScrollBar->setMaximum(qMax(0, effects_area->height() - keyframeView->height() - headers->height())); verticalScrollBar->setPageStep(verticalScrollBar->height()); diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index fb5c16014..4cc3a9116 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "project/projectelements.h" #include "ui/timelineheader.h" @@ -78,6 +79,9 @@ public: void add_effect_paste_action(QMenu* menu); virtual void Retranslate() override; + + virtual void LoadLayoutState(const QByteArray& data) override; + virtual QByteArray SaveLayoutState() override; public slots: void cut(); void copy(bool del = false); @@ -112,6 +116,7 @@ private: QString panel_name; int mode; + QSplitter* splitter; QPushButton* btnAddVideoEffect; QLabel* lblVideoEffects; QPushButton* btnAddVideoTransition; diff --git a/panels/project.cpp b/panels/project.cpp index ebbe01e0a..1458ba4f0 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -1185,7 +1185,7 @@ void Project::save_project(bool autorecovery) { sequence_id = 1; QFile file(autorecovery ? autorecovery_filename : olive::ActiveProjectFilename); - if (!file.open(QIODevice::WriteOnly/* | QIODevice::Text*/)) { + if (!file.open(QIODevice::WriteOnly)) { qCritical() << "Could not open file"; return; } diff --git a/ui/panel.cpp b/ui/panel.cpp index 2892be2e8..ed54ea43d 100644 --- a/ui/panel.cpp +++ b/ui/panel.cpp @@ -37,6 +37,13 @@ Panel::~Panel() olive::panels.removeAll(this); } +void Panel::LoadLayoutState(const QByteArray &) {} + +QByteArray Panel::SaveLayoutState() +{ + return QByteArray(); +} + void Panel::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { diff --git a/ui/panel.h b/ui/panel.h index db7054b13..c4c9f2cbe 100644 --- a/ui/panel.h +++ b/ui/panel.h @@ -28,7 +28,11 @@ class Panel : public QDockWidget { public: Panel(QWidget* parent = nullptr); virtual ~Panel() override; + virtual void Retranslate() = 0; + + virtual void LoadLayoutState(const QByteArray& data); + virtual QByteArray SaveLayoutState(); protected: virtual void changeEvent(QEvent* e) override; }; From 2b158c22b71172104ed147107daccb55c8ca00e5 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 1 Mar 2019 12:02:15 -0800 Subject: [PATCH 7/8] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 73658dc7b..25127ad47 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Olive is a free non-linear video editor for Windows, macOS, and Linux. -![screen](https://www.olivevideoeditor.org/img/screenshot.jpg) +**Discover more:** [Website](https://www.olivevideoeditor.org/) | [Twitter](https://twitter.com/oliveteam) | [Discord](https://discord.gg/rvJMEYQ) -Discover more and download binaries at: https://www.olivevideoeditor.org/ +![screen](https://www.olivevideoeditor.org/img/screenshot.jpg) Please consider supporting Olive: From f19eded76859d1ad3fd627d44336e003c5a5be7e Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 1 Mar 2019 12:02:53 -0800 Subject: [PATCH 8/8] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 25127ad47..f12d36719 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Olive is a free non-linear video editor for Windows, macOS, and Linux. -**Discover more:** [Website](https://www.olivevideoeditor.org/) | [Twitter](https://twitter.com/oliveteam) | [Discord](https://discord.gg/rvJMEYQ) - ![screen](https://www.olivevideoeditor.org/img/screenshot.jpg) +**Discover more:** [Website](https://www.olivevideoeditor.org/) | [Twitter](https://twitter.com/oliveteam) | [Discord](https://discord.gg/rvJMEYQ) + Please consider supporting Olive: [![Become a Patron](https://olivevideoeditor.org/img/become_a_patron_button.png)](https://www.patreon.com/olivevideoeditor)