[UI+Backend] Improved playlist menu and added thumbnails.
This commit is contained in:
parent
7b8beeb57b
commit
b909b4d189
|
@ -25,6 +25,8 @@ set(SOURCES
|
||||||
src/DirectMpvPlayerBackend.cpp
|
src/DirectMpvPlayerBackend.cpp
|
||||||
src/utils.cpp
|
src/utils.cpp
|
||||||
src/enums.hpp
|
src/enums.hpp
|
||||||
|
src/Process.cpp
|
||||||
|
src/ThumbnailCache.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(MPV_VERSION VERSION_GREATER_EQUAL "1.28.0")
|
if(MPV_VERSION VERSION_GREATER_EQUAL "1.28.0")
|
||||||
|
|
|
@ -132,7 +132,7 @@ MpvPlayerBackend::MpvPlayerBackend(QQuickItem* parent)
|
||||||
if (!mpv)
|
if (!mpv)
|
||||||
throw std::runtime_error("could not create mpv context");
|
throw std::runtime_error("could not create mpv context");
|
||||||
|
|
||||||
mpv_set_option_string(mpv, "terminal", "yes");
|
mpv_set_option_string(mpv, "terminal", "off");
|
||||||
mpv_set_option_string(mpv, "msg-level", "all=v");
|
mpv_set_option_string(mpv, "msg-level", "all=v");
|
||||||
|
|
||||||
// Fix?
|
// Fix?
|
||||||
|
|
22
src/Process.cpp
Normal file
22
src/Process.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include "Process.h"
|
||||||
|
|
||||||
|
Process::Process(QObject* parent)
|
||||||
|
: QProcess(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
Process::start(const QString& program, const QVariantList& arguments)
|
||||||
|
{
|
||||||
|
QStringList args;
|
||||||
|
|
||||||
|
for (int i = 0; i < arguments.length(); i++)
|
||||||
|
args << arguments[i].toString();
|
||||||
|
|
||||||
|
QProcess::start(program, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Process::getOutput()
|
||||||
|
{
|
||||||
|
return QProcess::readAllStandardOutput();
|
||||||
|
}
|
14
src/Process.h
Normal file
14
src/Process.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
class Process : public QProcess
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Process(QObject* parent = 0);
|
||||||
|
|
||||||
|
Q_INVOKABLE void start(const QString& program, const QVariantList& arguments);
|
||||||
|
|
||||||
|
Q_INVOKABLE QString getOutput();
|
||||||
|
};
|
59
src/ThumbnailCache.cpp
Normal file
59
src/ThumbnailCache.cpp
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#include "ThumbnailCache.h"
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QImageReader>
|
||||||
|
|
||||||
|
ThumbnailCache::ThumbnailCache(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, manager(new QNetworkAccessManager(this))
|
||||||
|
{
|
||||||
|
cacheFolder =
|
||||||
|
QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
|
||||||
|
"/thumbs");
|
||||||
|
if (!cacheFolder.exists()) {
|
||||||
|
cacheFolder.mkpath(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ThumbnailCache::addURL(const QString& name, const QString& mediaURL)
|
||||||
|
{
|
||||||
|
|
||||||
|
QString hashedURL = QString(
|
||||||
|
QCryptographicHash::hash(name.toUtf8(), QCryptographicHash::Md5).toHex());
|
||||||
|
QString cacheFilename = hashedURL + ".jpg";
|
||||||
|
QString cachedFilePath = cacheFolder.absoluteFilePath(cacheFilename);
|
||||||
|
if (cacheFolder.exists(cacheFilename)) {
|
||||||
|
emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString url(mediaURL);
|
||||||
|
QFileInfo isFile = QFileInfo(url);
|
||||||
|
if (isFile.exists()) {
|
||||||
|
QImageReader reader(url);
|
||||||
|
QImage image = reader.read();
|
||||||
|
|
||||||
|
image.save(cachedFilePath, "JPG");
|
||||||
|
|
||||||
|
emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
|
||||||
|
QNetworkReply* reply = manager->get(request);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, [=] {
|
||||||
|
QByteArray response_data = reply->readAll();
|
||||||
|
|
||||||
|
QBuffer buffer(&response_data);
|
||||||
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QImageReader reader(&buffer);
|
||||||
|
QImage image = reader.read();
|
||||||
|
|
||||||
|
image.save(cachedFilePath, "JPG");
|
||||||
|
|
||||||
|
emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath);
|
||||||
|
});
|
||||||
|
}
|
32
src/ThumbnailCache.h
Normal file
32
src/ThumbnailCache.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QSequentialIterable>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QtCore>
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
|
class ThumbnailCache : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ThumbnailCache(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
Q_INVOKABLE void addURL(const QString& name, const QString& url);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void thumbnailReady(const QString& name,
|
||||||
|
const QString& url,
|
||||||
|
const QString& filePath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager* manager;
|
||||||
|
QDir cacheFolder;
|
||||||
|
};
|
43
src/main.cpp
43
src/main.cpp
|
@ -7,23 +7,19 @@
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "Process.h"
|
||||||
#include "enums.hpp"
|
#include "enums.hpp"
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
|
#include <QtQml>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include "setenv_mingw.hpp"
|
#include "setenv_mingw.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef GIT_COMMIT_HASH
|
#include "ThumbnailCache.h"
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QSequentialIterable>
|
|
||||||
#include <QtNetwork>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
@ -80,36 +76,7 @@ main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkForUpdates) {
|
if (checkForUpdates) {
|
||||||
QString current_version = QString(GIT_COMMIT_HASH);
|
Utils::checkForUpdates();
|
||||||
qDebug() << "Current Version: " << current_version;
|
|
||||||
|
|
||||||
QNetworkRequest request(QUrl("https://api.github.com/repos/NamedKitten/"
|
|
||||||
"KittehPlayer/releases/tags/continuous"));
|
|
||||||
|
|
||||||
QNetworkAccessManager nam;
|
|
||||||
QNetworkReply* reply = nam.get(request);
|
|
||||||
|
|
||||||
while (!reply->isFinished()) {
|
|
||||||
qApp->processEvents();
|
|
||||||
}
|
|
||||||
QByteArray response_data = reply->readAll();
|
|
||||||
QJsonDocument json = QJsonDocument::fromJson(response_data);
|
|
||||||
|
|
||||||
if (json["target_commitish"].toString().length() != 0) {
|
|
||||||
if (json["target_commitish"].toString().endsWith(current_version) == 0) {
|
|
||||||
qDebug() << "Latest Version: " << json["target_commitish"].toString();
|
|
||||||
qDebug() << "Update Available. Please update ASAP.";
|
|
||||||
QProcess notifier;
|
|
||||||
notifier.setProcessChannelMode(QProcess::ForwardedChannels);
|
|
||||||
notifier.start("notify-send",
|
|
||||||
QStringList() << "KittehPlayer"
|
|
||||||
<< "New update avalable!"
|
|
||||||
<< "--icon=KittehPlayer");
|
|
||||||
notifier.waitForFinished();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "Couldn't check for new version.";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -148,6 +115,8 @@ main(int argc, char* argv[])
|
||||||
qRegisterMetaType<Enums::VolumeStatus>("Enums.VolumeStatus");
|
qRegisterMetaType<Enums::VolumeStatus>("Enums.VolumeStatus");
|
||||||
qRegisterMetaType<Enums::Backends>("Enums.Backends");
|
qRegisterMetaType<Enums::Backends>("Enums.Backends");
|
||||||
qRegisterMetaType<Enums::Commands>("Enums.Commands");
|
qRegisterMetaType<Enums::Commands>("Enums.Commands");
|
||||||
|
qmlRegisterType<Process>("player", 1, 0, "Process");
|
||||||
|
qmlRegisterType<ThumbnailCache>("player", 1, 0, "ThumbnailCache");
|
||||||
|
|
||||||
qmlRegisterType<UtilsClass>("player", 1, 0, "Utils");
|
qmlRegisterType<UtilsClass>("player", 1, 0, "Utils");
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,99 @@ Dialog {
|
||||||
height: Math.max(480, childrenRect.height * playlistListView.count)
|
height: Math.max(480, childrenRect.height * playlistListView.count)
|
||||||
width: 720
|
width: 720
|
||||||
modality: Qt.NonModal
|
modality: Qt.NonModal
|
||||||
|
property int thumbnailJobsRunning: 0
|
||||||
|
property variant thumbnailJobs: []
|
||||||
|
property int titleJobsRunning: 0
|
||||||
|
property variant titleJobs: []
|
||||||
|
|
||||||
|
function addThumbnailToCache(name, output) {
|
||||||
|
output = output.replace("maxresdefault", "sddefault").split('\n')[0]
|
||||||
|
thumbnailCache.addURL(name, output)
|
||||||
|
thumbnailJobs.shift()
|
||||||
|
thumbnailJobsRunning -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ThumbnailCache {
|
||||||
|
id: thumbnailCache
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: false
|
||||||
|
id: titleGetter
|
||||||
|
signal titleFound(string name, string title)
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 100
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
running: true
|
||||||
|
onTriggered: {
|
||||||
|
if (thumbnailJobsRunning < 2) {
|
||||||
|
if (thumbnailJobs.length > 0) {
|
||||||
|
if (thumbnailJobs[0].startsWith(
|
||||||
|
"https://www.youtube.com/playlist?list=")) {
|
||||||
|
thumbnailJobs.shift()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var component = Qt.createComponent("ThumbnailProcess.qml")
|
||||||
|
var thumbnailerProcess = component.createObject(
|
||||||
|
playlistDialog, {
|
||||||
|
name: thumbnailJobs[0]
|
||||||
|
})
|
||||||
|
if (String(titleJobs[0]).indexOf("://") !== -1) {
|
||||||
|
|
||||||
|
thumbnailerProcess.start(
|
||||||
|
"youtube-dl",
|
||||||
|
["--get-thumbnail", thumbnailJobs[0]])
|
||||||
|
} else {
|
||||||
|
thumbnailerProcess.start(
|
||||||
|
"ffmpegthumbnailer",
|
||||||
|
["-i", thumbnailJobs[0], "-o", "/tmp/" + Qt.md5(
|
||||||
|
thumbnailJobs[0]) + ".png"])
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbnailJobsRunning += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 100
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
running: true
|
||||||
|
onTriggered: {
|
||||||
|
if (titleJobsRunning < 5) {
|
||||||
|
if (titleJobs.length > 0) {
|
||||||
|
if (titleJobs[0].startsWith(
|
||||||
|
"https://www.youtube.com/playlist?list=")) {
|
||||||
|
titleJobs.shift()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var component = Qt.createComponent("TitleProcess.qml")
|
||||||
|
var titleProcess = component.createObject(playlistDialog, {
|
||||||
|
name: titleJobs[0]
|
||||||
|
})
|
||||||
|
titleProcess.start("youtube-dl",
|
||||||
|
["--get-title", titleJobs[0]])
|
||||||
|
titleJobs.shift()
|
||||||
|
titleJobsRunning += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: player
|
target: player
|
||||||
enabled: true
|
enabled: true
|
||||||
onPlaylistChanged: function (playlist) {
|
onPlaylistChanged: function (playlist) {
|
||||||
playlistModel.clear()
|
playlistModel.clear()
|
||||||
|
thumbnailJobs = []
|
||||||
|
titleJobs = []
|
||||||
|
titleJobsRunning = 0
|
||||||
|
thumbnailJobsRunning = 0
|
||||||
for (var thing in playlist) {
|
for (var thing in playlist) {
|
||||||
var item = playlist[thing]
|
var item = playlist[thing]
|
||||||
playlistModel.append({
|
playlistModel.append({
|
||||||
|
@ -33,20 +121,59 @@ Dialog {
|
||||||
id: playlistDelegate
|
id: playlistDelegate
|
||||||
Item {
|
Item {
|
||||||
id: playlistItem
|
id: playlistItem
|
||||||
|
property string itemURL: ""
|
||||||
|
property string itemTitle: ""
|
||||||
width: playlistDialog.width
|
width: playlistDialog.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
function getText(title, filename) {
|
||||||
|
var itemText = ""
|
||||||
|
if (title.length > 0) {
|
||||||
|
itemText += '<b>Title:</b> ' + title + "<br>"
|
||||||
|
}
|
||||||
|
if (filename.length > 0) {
|
||||||
|
itemText += '<b>Filename:</b> ' + filename
|
||||||
|
}
|
||||||
|
return itemText
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: thumbnailCache
|
||||||
|
onThumbnailReady: function (name, url, path) {
|
||||||
|
if (name == playlistItem.itemURL) {
|
||||||
|
thumbnail.source = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: titleGetter
|
||||||
|
onTitleFound: function (name, title) {
|
||||||
|
if (name == playlistItem.itemURL) {
|
||||||
|
titleJobsRunning -= 1
|
||||||
|
playlistItem.itemTitle = title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: thumbnail
|
||||||
|
source: ""
|
||||||
|
height: source.toString().length > 1 ? 144 : 0
|
||||||
|
width: source.toString().length > 1 ? 256 : 0
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
width: parent.width
|
width: parent.width - 20
|
||||||
id: playlistItemButton
|
id: playlistItemButton
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
padding: 0
|
padding: 0
|
||||||
|
anchors.left: thumbnail.right
|
||||||
bottomPadding: 0
|
bottomPadding: 0
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
id: playlistItemText
|
id: playlistItemText
|
||||||
font: parent.font
|
font: parent.font
|
||||||
bottomPadding: 0
|
bottomPadding: 0
|
||||||
color: "white"
|
color: "white"
|
||||||
text: playlistItemButton.text
|
text: playlistItem.getText(itemTitle, itemURL)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
@ -64,14 +191,18 @@ Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
var itemText = ""
|
|
||||||
if (typeof playlistItemTitle !== "undefined") {
|
if (typeof playlistItemTitle !== "undefined") {
|
||||||
itemText += '<b>Title:</b> ' + playlistItemTitle + "<br>"
|
playlistItem.itemTitle = playlistItemTitle
|
||||||
|
} else {
|
||||||
|
playlistDialog.titleJobs.push(playlistItemFilename)
|
||||||
}
|
}
|
||||||
if (typeof playlistItemFilename !== "undefined") {
|
if (typeof playlistItemFilename !== "undefined") {
|
||||||
itemText += '<b>Filename:</b> ' + playlistItemFilename
|
playlistItem.itemURL = playlistItemFilename
|
||||||
|
} else {
|
||||||
|
playlistItem.itemURL = ""
|
||||||
}
|
}
|
||||||
playlistItemText.text = itemText
|
playlistDialog.thumbnailJobs.push(playlistItemFilename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,6 +216,12 @@ Dialog {
|
||||||
delegate: playlistDelegate
|
delegate: playlistDelegate
|
||||||
highlight: Item {
|
highlight: Item {
|
||||||
}
|
}
|
||||||
|
snapMode: ListView.SnapToItem
|
||||||
|
flickableDirection: Flickable.VerticalFlick
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
active: playlistListView.count > 1 ? true : true
|
||||||
|
}
|
||||||
focus: true
|
focus: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
src/qml/Items/ThumbnailProcess.qml
Normal file
20
src/qml/Items/ThumbnailProcess.qml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import QtQuick 2.11
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Dialogs 1.3
|
||||||
|
import QtQuick.Window 2.11
|
||||||
|
import Qt.labs.settings 1.0
|
||||||
|
import Qt.labs.platform 1.0 as LabsPlatform
|
||||||
|
import player 1.0
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: thumbnailerProcess
|
||||||
|
property string name: ""
|
||||||
|
onFinished: function () {
|
||||||
|
if (String(name).indexOf("://") !== -1) {
|
||||||
|
playlistDialog.addThumbnailToCache(name, getOutput())
|
||||||
|
} else {
|
||||||
|
playlistDialog.addThumbnailToCache(name,
|
||||||
|
"/tmp/" + Qt.md5(name) + ".png")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/qml/Items/TitleProcess.qml
Normal file
15
src/qml/Items/TitleProcess.qml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import QtQuick 2.11
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Dialogs 1.3
|
||||||
|
import QtQuick.Window 2.11
|
||||||
|
import Qt.labs.settings 1.0
|
||||||
|
import Qt.labs.platform 1.0 as LabsPlatform
|
||||||
|
import player 1.0
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: titleProcess
|
||||||
|
property string name: ""
|
||||||
|
onReadyRead: function () {
|
||||||
|
titleGetter.titleFound(name, getOutput())
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,8 @@
|
||||||
<file alias="ChapterMarkerItem.qml">Items/ChapterMarkerItem.qml</file>
|
<file alias="ChapterMarkerItem.qml">Items/ChapterMarkerItem.qml</file>
|
||||||
<file alias="TrackItem.qml">Items/TrackItem.qml</file>
|
<file alias="TrackItem.qml">Items/TrackItem.qml</file>
|
||||||
<file alias="AudioDeviceItem.qml">Items/AudioDeviceItem.qml</file>
|
<file alias="AudioDeviceItem.qml">Items/AudioDeviceItem.qml</file>
|
||||||
|
<file alias="ThumbnailProcess.qml">Items/ThumbnailProcess.qml</file>
|
||||||
|
<file alias="TitleProcess.qml">Items/TitleProcess.qml</file>
|
||||||
<file alias="CustomMenuItem.qml">Items/CustomMenuItem.qml</file>
|
<file alias="CustomMenuItem.qml">Items/CustomMenuItem.qml</file>
|
||||||
<file alias="PlaylistDialog.qml">Dialogs/PlaylistDialog.qml</file>
|
<file alias="PlaylistDialog.qml">Dialogs/PlaylistDialog.qml</file>
|
||||||
<file>icons/YouTube/play.svg</file>
|
<file>icons/YouTube/play.svg</file>
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QSequentialIterable>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#ifdef ENABLE_X11
|
#ifdef ENABLE_X11
|
||||||
|
@ -34,6 +37,45 @@ launchAboutQt()
|
||||||
qapp->aboutQt();
|
qapp->aboutQt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
checkForUpdates()
|
||||||
|
{
|
||||||
|
#ifdef GIT_COMMIT_HASH
|
||||||
|
QString current_version = QString(GIT_COMMIT_HASH);
|
||||||
|
#else
|
||||||
|
QString current_version = QString("Unknown");
|
||||||
|
#endif
|
||||||
|
qDebug() << "Current Version: " << current_version;
|
||||||
|
|
||||||
|
QNetworkRequest request(QUrl("https://api.github.com/repos/NamedKitten/"
|
||||||
|
"KittehPlayer/releases/tags/continuous"));
|
||||||
|
|
||||||
|
QNetworkAccessManager nam;
|
||||||
|
QNetworkReply* reply = nam.get(request);
|
||||||
|
|
||||||
|
while (!reply->isFinished()) {
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
QByteArray response_data = reply->readAll();
|
||||||
|
QJsonDocument json = QJsonDocument::fromJson(response_data);
|
||||||
|
|
||||||
|
if (json["target_commitish"].toString().length() != 0) {
|
||||||
|
if (json["target_commitish"].toString().endsWith(current_version) == 0) {
|
||||||
|
qDebug() << "Latest Version: " << json["target_commitish"].toString();
|
||||||
|
qDebug() << "Update Available. Please update ASAP.";
|
||||||
|
QProcess notifier;
|
||||||
|
notifier.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
|
notifier.start("notify-send",
|
||||||
|
QStringList() << "KittehPlayer"
|
||||||
|
<< "New update avalable!"
|
||||||
|
<< "--icon=KittehPlayer");
|
||||||
|
notifier.waitForFinished();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Couldn't check for new version.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
updateAppImage()
|
updateAppImage()
|
||||||
{
|
{
|
||||||
|
@ -50,19 +92,19 @@ updateAppImage()
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.youtube.com/watch?v=nXaxk27zwlk&feature=youtu.be&t=56m34s
|
// https://www.youtube.com/watch?v=nXaxk27zwlk&feature=youtu.be&t=56m34s
|
||||||
int
|
inline const int
|
||||||
fast_mod(const int input, const int ceil)
|
fast_mod(const int input, const int ceil)
|
||||||
{
|
{
|
||||||
return input >= ceil ? input % ceil : input;
|
return input >= ceil ? input % ceil : input;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
createTimestamp(int seconds)
|
createTimestamp(const int seconds)
|
||||||
{
|
{
|
||||||
|
|
||||||
int s = fast_mod(seconds, 60);
|
const int s = fast_mod(seconds, 60);
|
||||||
int m = fast_mod(seconds, 3600) / 60;
|
const int m = fast_mod(seconds, 3600) / 60;
|
||||||
int h = fast_mod(seconds, 86400) / 3600;
|
const int h = fast_mod(seconds, 86400) / 3600;
|
||||||
|
|
||||||
if (h > 0) {
|
if (h > 0) {
|
||||||
return QString::asprintf("%02d:%02d:%02d", h, m, s);
|
return QString::asprintf("%02d:%02d:%02d", h, m, s);
|
||||||
|
|
|
@ -14,9 +14,9 @@ SetScreensaver(WId wid, bool on);
|
||||||
void
|
void
|
||||||
AlwaysOnTop(WId wid, bool on);
|
AlwaysOnTop(WId wid, bool on);
|
||||||
void
|
void
|
||||||
|
checkForUpdates();
|
||||||
|
void
|
||||||
updateAppImage();
|
updateAppImage();
|
||||||
int
|
|
||||||
fast_mod(const int input, const int ceil);
|
|
||||||
QString
|
QString
|
||||||
createTimestamp(int seconds);
|
createTimestamp(int seconds);
|
||||||
void
|
void
|
||||||
|
|
77
tmp
Normal file
77
tmp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#include "ThumbnailCache.h"
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
ThumbnailCache::ThumbnailCache(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, manager(new QNetworkAccessManager(this))
|
||||||
|
{
|
||||||
|
cacheFolder =
|
||||||
|
QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
|
||||||
|
"/thumbs");
|
||||||
|
if (!cacheFolder.exists()) {
|
||||||
|
cacheFolder.mkpath(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ThumbnailCache::addURL(const QString& mediaURL)
|
||||||
|
{
|
||||||
|
|
||||||
|
QString hashedURL =
|
||||||
|
QString(QCryptographicHash::hash(mediaURL.toUtf8(), QCryptographicHash::Md5)
|
||||||
|
.toHex());
|
||||||
|
qDebug() << hashedURL;
|
||||||
|
QString cacheFilename = hashedURL + ".png";
|
||||||
|
QString cachedFilePath = cacheFolder.absoluteFilePath(cacheFilename);
|
||||||
|
if (cacheFolder.exists(cacheFilename)) {
|
||||||
|
qDebug() << mediaURL << " is in cache at " << cachedFilePath;
|
||||||
|
emit thumbnailReady(mediaURL, "file://" + cachedFilePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QProcess* thumbnailerProcess = new QProcess(this);
|
||||||
|
QStringList params;
|
||||||
|
params << "--get-thumbnail" << mediaURL;
|
||||||
|
|
||||||
|
connect(thumbnailerProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||||
|
[=](int exitCode, QProcess::ExitStatus exitStatus){ /* ... */ });
|
||||||
|
|
||||||
|
connect(thumbnailerProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||||
|
|
||||||
|
[=] (int exitCode, QProcess::ExitStatus exitStatus)
|
||||||
|
{
|
||||||
|
qDebug() << "finished. Exit code: " + exitCode ;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
connect(thumbnailerProcess,
|
||||||
|
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
|
||||||
|
&QProcess::finished),
|
||||||
|
[=](int, QProcess::ExitStatus) {
|
||||||
|
qDebug() << "NYA!";
|
||||||
|
QString url(thumbnailerProcess->readAll());
|
||||||
|
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
|
||||||
|
QNetworkReply* reply = manager->get(request);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, [=] {
|
||||||
|
QByteArray response_data = reply->readAll();
|
||||||
|
|
||||||
|
QPixmap p;
|
||||||
|
p.loadFromData(response_data);
|
||||||
|
p.save(cachedFilePath, "PNG");
|
||||||
|
QString extension =
|
||||||
|
url.right(url.length() - url.lastIndexOf(".") - 1).toUpper();
|
||||||
|
qDebug() << extension;
|
||||||
|
|
||||||
|
emit thumbnailReady(mediaURL, "file://" + cachedFilePath);
|
||||||
|
});
|
||||||
|
});*/
|
||||||
|
thumbnailerProcess->start("youtube-dl", params);
|
||||||
|
while (thumbnailerProcess->exitStatus() == QProcess::NormalExit) {
|
||||||
|
qDebug() << "NYA!";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue