initial bits to make a ytdl addition

This commit is contained in:
Chris Cochrun 2023-04-21 13:42:18 -05:00
parent 405a310e02
commit 666c22ae66
12 changed files with 353 additions and 1 deletions

View file

@ -53,6 +53,7 @@
#include "cxx-qt-gen/slide_obj.cxxqt.h"
#include "cxx-qt-gen/slide_model.cxxqt.h"
#include "cxx-qt-gen/settings.cxxqt.h"
#include "cxx-qt-gen/ytdl.cxxqt.h"
// #include "cxx-qt-gen/image_model.cxxqt.h"
static QWindow *windowFromEngine(QQmlApplicationEngine *engine)
@ -203,6 +204,7 @@ int main(int argc, char *argv[])
qmlRegisterType<ImageSqlModel>("org.presenter", 1, 0, "ImageSqlModel");
qmlRegisterType<PresentationSqlModel>("org.presenter", 1, 0, "PresentationSqlModel");
qmlRegisterType<FileHelper>("org.presenter", 1, 0, "FileHelper");
qmlRegisterType<Ytdl>("org.presenter", 1, 0, "Ytdl");
qmlRegisterType<ServiceThing>("org.presenter", 1, 0, "ServiceThing");
qmlRegisterType<SlideObject>("org.presenter", 1, 0, "SlideHelper");
qmlRegisterSingletonInstance("org.presenter", 1, 0,

View file

@ -68,6 +68,7 @@ Item {
count: innerModel.count()
newItemFunction: (function() {
videoProxyModel.setFilterRegularExpression("");
newVideo.open();
})
deleteItemFunction: (function(rows) {
videoProxyModel.deleteVideos(rows)
@ -75,6 +76,19 @@ Item {
}
Presenter.NewVideo {
id: newVideo
}
Timer {
id: videoDLTimer
interval: 3000
running: !newVideo.sheetOpen
onTriggered: {
newVideo.clear();
}
}
Presenter.LibraryItem {
id: imageLibrary
Layout.alignment: Qt.AlignTop

View file

@ -0,0 +1,133 @@
import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter
import org.presenter 1.0
import Qt.labs.settings 1.0
Kirigami.OverlaySheet {
id: root
property bool ytdlLoaded: false
header: Kirigami.Heading {
text: "Add a new video"
}
ColumnLayout {
Controls.ToolBar {
id: toolbar
Layout.fillWidth: true
RowLayout {
anchors.fill: parent
Controls.Label {
id: videoInputLabel
text: "Enter a video"
}
Controls.TextField {
id: videoInput
Layout.fillWidth: true
hoverEnabled: true
placeholderText: "Enter a video url..."
text: ""
onEditingFinished: videoInput.text.startsWith("http") ? loadVideo() : showPassiveNotification("Coach called, this isn't it.");
}
}
}
Item {
id: centerItem
Layout.preferredHeight: Kirigami.Units.gridUnit * 25
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
visible: true
Controls.BusyIndicator {
id: loadingIndicator
anchors.centerIn: parent
running: ytdl.loading
}
Ytdl {
id: ytdl
loaded: false
loading: false
}
/* Rectangle { */
/* color: "blue" */
/* anchors.fill: parent */
/* } */
ColumnLayout {
id: loadedItem
anchors.fill: parent
visible: ytdl.loaded
Image {
id: thumbnailImage
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
Layout.preferredHeight: width * 9 / 16
source: ytdl.thumbnail
fillMode: Image.PreserveAspectFit
clip: true
Item {
id: mask
anchors.fill: thumbnailImage
visible: false
Rectangle {
color: "white"
radius: 20
anchors.centerIn: parent
width: thumbnailImage.paintedWidth
height: thumbnailImage.paintedHeight
}
}
OpacityMask {
anchors.fill: thumbnailImage
source: thumbnailImage
maskSource: mask
}
}
Item {
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.fillWidth: true
Layout.fillHeight: true
Controls.Label {
id: videoTitle
text: ytdl.title
}
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
Controls.Button {
anchors.right: parent.right
text: "Ok"
onClicked: { clear(); root.close();}
}
}
}
}
}
function loadVideo() {
if (ytdl.getVideo(videoInput.text))
loadingIndicator.visible = true;
}
function clear() {
ytdl.title = "";
ytdl.thumbnail = "";
ytdl.loaded = false;
ytdl.loading = false;
}
}

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts 1.15
import org.kde.kirigami 2.13 as Kirigami
import "./" as Presenter
import mpv 1.0
import org.presenter 1.0
Item {
id: root

View file

@ -23,6 +23,7 @@
<file>qml/presenter/PreviewSlide.qml</file>
<file>qml/presenter/Settings.qml</file>
<file>qml/presenter/RangedSlider.qml</file>
<file>qml/presenter/NewVideo.qml</file>
<file>assets/parallel.jpg</file>
<file>assets/black.jpg</file>
</qresource>

View file

@ -8,4 +8,5 @@ mod settings;
mod slide_model;
mod slide_obj;
pub mod video_model;
pub mod ytdl;
// mod video_thumbnail;

69
src/rust/ytdl.rs Normal file
View file

@ -0,0 +1,69 @@
#[cxx_qt::bridge]
mod ytdl {
use dirs;
use std::{fs, thread};
use youtube_dl::YoutubeDl;
unsafe extern "C++" {
include!("cxx-qt-lib/qurl.h");
type QUrl = cxx_qt_lib::QUrl;
include!("cxx-qt-lib/qstring.h");
type QString = cxx_qt_lib::QString;
}
#[derive(Clone, Default)]
#[cxx_qt::qobject]
pub struct Ytdl {
#[qproperty]
title: QString,
#[qproperty]
thumbnail: QUrl,
#[qproperty]
loaded: bool,
#[qproperty]
loading: bool,
}
impl qobject::Ytdl {
#[qinvokable]
pub fn get_video(mut self: Pin<&mut Self>, url: QUrl) -> bool {
if !url.is_valid() {
false
} else {
let data_dir = dirs::data_local_dir().unwrap();
if let Some(mut data_dir) = dirs::data_local_dir() {
data_dir.push("librepresenter");
data_dir.push("ytdl");
if !data_dir.exists() {
fs::create_dir(&data_dir);
}
println!("{:?}", data_dir);
}
self.as_mut().set_loading(true);
let thread = self.qt_thread();
thread::spawn(move || {
let url = url.to_string();
let output_dirs = "/home/chris/Videos/";
let ytdl = YoutubeDl::new(url)
.socket_timeout("15")
.output_directory(output_dirs)
.download(true)
.run()
.unwrap();
let output = ytdl.into_single_video().unwrap();
println!("{:?}", output.title);
println!("{:?}", output.thumbnail);
let title = QString::from(&output.title);
let thumbnail = QUrl::from(&output.thumbnail.unwrap());
thread.queue(move |mut qobject_ytdl| {
qobject_ytdl.as_mut().set_loaded(true);
qobject_ytdl.as_mut().set_loading(false);
qobject_ytdl.as_mut().set_title(title);
qobject_ytdl.as_mut().set_thumbnail(thumbnail);
})
});
true
}
}
}
}