Initial Commit!
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
build
|
||||
|
35
.travis.yml
Normal file
|
@ -0,0 +1,35 @@
|
|||
language: cpp
|
||||
compiler: gcc
|
||||
sudo: require
|
||||
dist: xenial
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:beineri/opt-qt-5.11.1-xenial -y
|
||||
- sudo add-apt-repository ppa:mc3man/xerus-media -y
|
||||
- sudo apt-get update -qq
|
||||
|
||||
install:
|
||||
- sudo apt-get -y install qt511-meta libmpv
|
||||
- source /opt/qt*/bin/qt*-env.sh
|
||||
|
||||
script:
|
||||
- qmake CONFIG+=release PREFIX=/usr
|
||||
- make -j$(nproc)
|
||||
- make INSTALL_ROOT=appdir -j$(nproc) install ; find appdir/
|
||||
- wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
||||
- chmod a+x linuxdeployqt-continuous-x86_64.AppImage
|
||||
- unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
|
||||
- export VERSION=$(git rev-parse --short HEAD) # linuxdeployqt uses this for naming the file
|
||||
- ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -qmldir=./qml/ -bundle-non-qt-libs
|
||||
- ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -qmldir=./qml/ -appimage
|
||||
|
||||
after_success:
|
||||
- find appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq
|
||||
- curl --upload-file APPNAME*.AppImage https://transfer.sh/APPNAME-git.$(git rev-parse --short HEAD)-x86_64.AppImage
|
||||
- wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh
|
||||
- bash upload.sh APPNAME*.AppImage*
|
||||
|
||||
branches:
|
||||
except:
|
||||
- # Do not build tags that we create when we upload to GitHub Releases
|
||||
- /^(?i:continuous)/
|
10
KittehPlayer.desktop
Normal file
|
@ -0,0 +1,10 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=KittehPlayer
|
||||
Comment=NamedKitten's Video Player
|
||||
Icon=KittehPlayer
|
||||
Exec=KittehPlayer %U
|
||||
Terminal=false
|
||||
Categories=AudioVideo;Audio;Video;Player;TV;
|
||||
MimeType=application/ogg;application/x-ogg;application/mxf;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/vnd.dolby.heaac.1;audio/vnd.dolby.heaac.2;audio/aiff;audio/x-aiff;audio/m4a;audio/x-m4a;application/x-extension-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/mpeg2;audio/mpeg3;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/musepack;audio/x-musepack;audio/ogg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg2;video/x-mpeg3;video/mp4v-es;video/x-m4v;video/mp4;application/x-extension-mp4;video/divx;video/vnd.divx;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;audio/x-ms-asf;application/vnd.ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/avi;video/x-flic;video/fli;video/x-flc;video/flv;video/x-flv;video/x-theora;video/x-theora+ogg;video/x-matroska;video/mkv;audio/x-matroska;application/x-matroska;video/webm;audio/webm;audio/vorbis;audio/x-vorbis;audio/x-vorbis+ogg;video/x-ogm;video/x-ogm+ogg;application/x-ogm;application/x-ogm-audio;application/x-ogm-video;application/x-shorten;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;audio/eac3;audio/amr-wb;video/mp2t;audio/flac;audio/mp4;application/x-mpegurl;video/vnd.mpegurl;application/vnd.apple.mpegurl;audio/x-pn-au;video/3gp;video/3gpp;video/3gpp2;audio/3gpp;audio/3gpp2;video/dv;audio/dv;audio/opus;audio/vnd.dts;audio/vnd.dts.hd;audio/x-adpcm;application/x-cue;audio/m3u;
|
||||
X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb,ytdl
|
BIN
KittehPlayer.png
Normal file
After Width: | Height: | Size: 236 KiB |
35
KittehPlayer.pro
Normal file
|
@ -0,0 +1,35 @@
|
|||
TARGET = KittehPlayer
|
||||
|
||||
TEMPLATE = app
|
||||
QT += qml quickcontrols2 widgets
|
||||
|
||||
SOURCES += src/main.cpp src/mpvobject.cpp
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig release
|
||||
PKGCONFIG += mpv
|
||||
|
||||
RESOURCES += src/qml/qml.qrc
|
||||
|
||||
unix {
|
||||
isEmpty(PREFIX) {
|
||||
PREFIX = /usr
|
||||
}
|
||||
|
||||
target.path = $$PREFIX/bin
|
||||
|
||||
desktop.files = KittehPlayer.desktop
|
||||
desktop.path = $$PREFIX/share/applications/
|
||||
icon.files += KittehPlayer.png
|
||||
icon.path = $$PREFIX/share/icons/hicolor/256x256/apps/
|
||||
|
||||
INSTALLS += desktop
|
||||
INSTALLS += icon
|
||||
}
|
||||
|
||||
INSTALLS += target
|
||||
|
||||
HEADERS += src/main.h src/mpvobject.h src/config.h
|
||||
|
||||
|
||||
DISTFILES += KittehPlayer.desktop KittehPlayer.png README.md LICENSE.txt
|
373
LICENSE.txt
Normal file
|
@ -0,0 +1,373 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
14
src/config.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <mpv/client.h>
|
||||
|
||||
#if MPV_CLIENT_API_VERSION >= MPV_MAKE_VERSION(1, 28)
|
||||
#define USE_RENDER
|
||||
#else
|
||||
#warning "Using deprecated MPV..."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif // CONFIG_H
|
38
src/main.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <stdexcept>
|
||||
#include <clocale>
|
||||
|
||||
#include "config.h"
|
||||
#include "mpvobject.h"
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QMainWindow>
|
||||
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
#include <QQuickView>
|
||||
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
setenv("QT_QUICK_CONTROLS_STYLE","Desktop",1);
|
||||
QApplication app(argc, argv);
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
qmlRegisterType<MpvObject>("player", 1, 0, "MpvObject");
|
||||
|
||||
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
|
||||
/*QQuickView *view = new QQuickView();
|
||||
view->setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
view->setSource(QUrl("qrc:///player/main.qml"));
|
||||
view->show();*/
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.load(QUrl(QStringLiteral("qrc:///player/main.qml")));
|
||||
return app.exec();
|
||||
}
|
6
src/main.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
|
||||
|
||||
#endif // MAIN_H
|
352
src/mpvobject.cpp
Normal file
|
@ -0,0 +1,352 @@
|
|||
|
||||
#include <stdexcept>
|
||||
#include <clocale>
|
||||
|
||||
#include "mpvobject.h"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <QOpenGLContext>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/QQuickView>
|
||||
#include <QQuickFramebufferObject>
|
||||
#include <QOpenGLContext>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/QQuickView>
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void wakeup(void *ctx)
|
||||
{
|
||||
QMetaObject::invokeMethod((MpvObject*)ctx, "on_mpv_events", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
#ifdef USE_RENDER
|
||||
void on_mpv_redraw(void *ctx)
|
||||
{
|
||||
MpvObject::on_update(ctx);
|
||||
}
|
||||
|
||||
static void *get_proc_address_mpv(void *ctx, const char *name)
|
||||
{
|
||||
Q_UNUSED(ctx)
|
||||
|
||||
QOpenGLContext *glctx = QOpenGLContext::currentContext();
|
||||
if (!glctx) return nullptr;
|
||||
|
||||
return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
class MpvRenderer : public QQuickFramebufferObject::Renderer
|
||||
{
|
||||
#ifdef USE_RENDER
|
||||
MpvObject *obj;
|
||||
#else
|
||||
static void *get_proc_address(void *ctx, const char *name) {
|
||||
(void)ctx;
|
||||
QOpenGLContext *glctx = QOpenGLContext::currentContext();
|
||||
if (!glctx)
|
||||
return NULL;
|
||||
return (void *)glctx->getProcAddress(QByteArray(name));
|
||||
}
|
||||
mpv::qt::Handle mpv;
|
||||
QQuickWindow *window;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
#ifdef USE_RENDER
|
||||
|
||||
MpvRenderer(MpvObject *new_obj)
|
||||
: obj{new_obj}
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MpvRenderer() {}
|
||||
|
||||
// This function is called when a new FBO is needed.
|
||||
// This happens on the initial frame.
|
||||
QOpenGLFramebufferObject * createFramebufferObject(const QSize &size)
|
||||
{
|
||||
// init mpv_gl:
|
||||
if (!obj->mpv_gl)
|
||||
{
|
||||
mpv_opengl_init_params gl_init_params{get_proc_address_mpv, nullptr, nullptr};
|
||||
mpv_render_param params[]{
|
||||
{MPV_RENDER_PARAM_API_TYPE, const_cast<char *>(MPV_RENDER_API_TYPE_OPENGL)},
|
||||
{MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},
|
||||
{MPV_RENDER_PARAM_INVALID, nullptr}
|
||||
};
|
||||
|
||||
if (mpv_render_context_create(&obj->mpv_gl, obj->mpv, params) < 0)
|
||||
throw std::runtime_error("failed to initialize mpv GL context");
|
||||
mpv_render_context_set_update_callback(obj->mpv_gl, on_mpv_redraw, obj);
|
||||
}
|
||||
|
||||
return QQuickFramebufferObject::Renderer::createFramebufferObject(size);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
MpvRenderer(const MpvObject *obj)
|
||||
: mpv(obj->mpv), window(obj->window()), mpv_gl(obj->mpv_gl)
|
||||
{
|
||||
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
|
||||
if (r < 0)
|
||||
throw std::runtime_error("could not initialize OpenGL");
|
||||
}
|
||||
|
||||
virtual ~MpvRenderer() {
|
||||
mpv_opengl_cb_uninit_gl(mpv_gl);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void render()
|
||||
{
|
||||
|
||||
#ifdef USE_RENDER
|
||||
obj->window()->resetOpenGLState();
|
||||
|
||||
QOpenGLFramebufferObject *fbo = framebufferObject();
|
||||
mpv_opengl_fbo mpfbo{.fbo = static_cast<int>(fbo->handle()), .w = fbo->width(), .h = fbo->height(), .internal_format = 0};
|
||||
int flip_y{0};
|
||||
|
||||
mpv_render_param params[] = {
|
||||
// Specify the default framebuffer (0) as target. This will
|
||||
// render onto the entire screen. If you want to show the video
|
||||
// in a smaller rectangle or apply fancy transformations, you'll
|
||||
// need to render into a separate FBO and draw it manually.
|
||||
{MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo},
|
||||
// Flip rendering (needed due to flipped GL coordinate system).
|
||||
{MPV_RENDER_PARAM_FLIP_Y, &flip_y},
|
||||
{MPV_RENDER_PARAM_INVALID, nullptr}
|
||||
};
|
||||
// See render_gl.h on what OpenGL environment mpv expects, and
|
||||
// other API details.
|
||||
mpv_render_context_render(obj->mpv_gl, params);
|
||||
|
||||
obj->window()->resetOpenGLState();
|
||||
#else
|
||||
QOpenGLFramebufferObject *fbo = framebufferObject();
|
||||
window->resetOpenGLState();
|
||||
mpv_opengl_cb_draw(mpv_gl, fbo->handle(), fbo->width(), fbo->height());
|
||||
window->resetOpenGLState();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
MpvObject::MpvObject(QQuickItem * parent)
|
||||
#ifdef USE_RENDER
|
||||
: QQuickFramebufferObject(parent), mpv{mpv_create()}, mpv_gl(nullptr)
|
||||
#else
|
||||
: QQuickFramebufferObject(parent), mpv_gl(0)
|
||||
#endif
|
||||
{
|
||||
|
||||
|
||||
#ifndef USE_RENDER
|
||||
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
|
||||
#endif
|
||||
|
||||
|
||||
if (!mpv)
|
||||
throw std::runtime_error("could not create mpv context");
|
||||
|
||||
mpv_set_option_string(mpv, "terminal", "yes");
|
||||
mpv_set_option_string(mpv, "msg-level", "all=v");
|
||||
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
throw std::runtime_error("could not initialize mpv context");
|
||||
|
||||
#ifndef USE_RENDER
|
||||
mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
|
||||
#endif
|
||||
// Fix?
|
||||
mpv::qt::set_option_variant(mpv, "ytdl", "yes");
|
||||
|
||||
mpv_set_option_string(mpv, "input-default-bindings", "yes");
|
||||
mpv_set_option_string(mpv, "input-vo-keyboard", "yes");
|
||||
|
||||
|
||||
|
||||
mpv::qt::set_option_variant(mpv, "hwdec", "off");
|
||||
mpv::qt::set_option_variant(mpv, "slang", "en");
|
||||
mpv::qt::set_option_variant(mpv, "sub-font", "Noto Sans");
|
||||
mpv::qt::set_option_variant(mpv, "sub-ass-override", "force");
|
||||
mpv::qt::set_option_variant(mpv, "sub-ass", "off");
|
||||
mpv::qt::set_option_variant(mpv, "sub-border-size", "0");
|
||||
mpv::qt::set_option_variant(mpv, "sub-bold", "off");
|
||||
mpv::qt::set_option_variant(mpv, "sub-scale-by-window", "on");
|
||||
mpv::qt::set_option_variant(mpv, "sub-scale-with-window", "on");
|
||||
|
||||
mpv::qt::set_option_variant(mpv, "sub-back-color", "#C0080808");
|
||||
|
||||
|
||||
|
||||
mpv_observe_property(mpv, 0, "playback-abort", MPV_FORMAT_NONE);
|
||||
mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE);
|
||||
mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE);
|
||||
|
||||
mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
|
||||
mpv_observe_property(mpv, 0, "media-title", MPV_FORMAT_STRING);
|
||||
|
||||
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
|
||||
mpv_set_wakeup_callback(mpv, wakeup, this);
|
||||
|
||||
#ifndef USE_RENDER
|
||||
mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
|
||||
if (!mpv_gl)
|
||||
throw std::runtime_error("OpenGL not compiled in");
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, MpvObject::on_update, (void *)this);
|
||||
#endif
|
||||
|
||||
connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
MpvObject::~MpvObject()
|
||||
{
|
||||
#ifdef USE_RENDER
|
||||
if (mpv_gl)
|
||||
{
|
||||
mpv_render_context_free(mpv_gl);
|
||||
}
|
||||
|
||||
mpv_terminate_destroy(mpv);
|
||||
#else
|
||||
if (mpv_gl)
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MpvObject::on_update(void *ctx)
|
||||
{
|
||||
MpvObject *self = (MpvObject *)ctx;
|
||||
emit self->onUpdate();
|
||||
}
|
||||
|
||||
// connected to onUpdate(); signal makes sure it runs on the GUI thread
|
||||
void MpvObject::doUpdate()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
QVariant MpvObject::getThumbnailFile(const QString &name) const
|
||||
{
|
||||
QProcess process;
|
||||
process.start("youtube-dl --get-thumbnail " + name);
|
||||
process.waitForFinished(-1);
|
||||
return process.readAllStandardOutput();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QVariant MpvObject::getProperty(const QString &name) const
|
||||
{
|
||||
return mpv::qt::get_property_variant(mpv, name);
|
||||
}
|
||||
|
||||
|
||||
void MpvObject::command(const QVariant& params)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, params);
|
||||
}
|
||||
|
||||
void MpvObject::setProperty(const QString& name, const QVariant& value)
|
||||
{
|
||||
mpv::qt::set_property_variant(mpv, name, value);
|
||||
}
|
||||
|
||||
void MpvObject::setOption(const QString& name, const QVariant& value)
|
||||
{
|
||||
mpv::qt::set_option_variant(mpv, name, value);
|
||||
}
|
||||
|
||||
void MpvObject::on_mpv_events()
|
||||
{
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
|
||||
if (event->event_id == MPV_EVENT_NONE) {
|
||||
break;
|
||||
}
|
||||
handle_mpv_event(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MpvObject::handle_mpv_event(mpv_event *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
mpv_event_property *prop = (mpv_event_property *)event->data;
|
||||
if (strcmp(prop->name, "time-pos") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;
|
||||
QMetaObject::invokeMethod(this,"setProgressBarValue",Q_ARG(QVariant,time));
|
||||
}
|
||||
} else if (strcmp(prop->name, "duration") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;Q_ARG(QVariant,"txt1"),
|
||||
QMetaObject::invokeMethod(this,"setProgressBarEnd",Q_ARG(QVariant,time));
|
||||
}
|
||||
} else if (strcmp(prop->name, "volume") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
QMetaObject::invokeMethod(this,"updateVolume");
|
||||
}
|
||||
} else if (strcmp(prop->name, "media-title") == 0) {
|
||||
if (prop->format == MPV_FORMAT_STRING) {
|
||||
QMetaObject::invokeMethod(this,"setTitle");
|
||||
}
|
||||
} else if (strcmp(prop->name, "chapter-list") == 0 || strcmp(prop->name, "track-list") == 0) {
|
||||
if (prop->format == MPV_FORMAT_NODE) {
|
||||
/*
|
||||
QVariant v = mpv::qt::node_to_variant((mpv_node *)prop->data);
|
||||
// Abuse JSON support for easily printing the mpv_node contents.
|
||||
QJsonDocument d = QJsonDocument::fromVariant(v);
|
||||
printf("Change property %s:\n", QString(prop->name).toStdString().c_str());
|
||||
printf(d.toJson().data());*/
|
||||
//QMetaObject::invokeMethod(this,"updatePlaylist");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_SHUTDOWN: {
|
||||
exit(0);
|
||||
}
|
||||
default: ;
|
||||
// Ignore uninteresting or unknown events.
|
||||
}
|
||||
}
|
||||
|
||||
QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
|
||||
{
|
||||
window()->setPersistentOpenGLContext(true);
|
||||
window()->setPersistentSceneGraph(true);
|
||||
#ifdef USE_RENDER
|
||||
return new MpvRenderer(const_cast<MpvObject *>(this));
|
||||
#else
|
||||
return new MpvRenderer(this);
|
||||
#endif
|
||||
}
|
78
src/mpvobject.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#ifndef MPVOBJECT_H
|
||||
#define MPVOBJECT_H
|
||||
|
||||
#include <QQuickFramebufferObject>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <mpv/client.h>
|
||||
|
||||
#ifdef USE_RENDER
|
||||
#include <mpv/render_gl.h>
|
||||
#else
|
||||
#include <mpv/opengl_cb.h>
|
||||
#endif
|
||||
|
||||
#include <mpv/qthelper.hpp>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <QOpenGLContext>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <QQuickFramebufferObject>
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/QQuickView>
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
class MpvRenderer;
|
||||
|
||||
class MpvObject : public QQuickFramebufferObject
|
||||
{
|
||||
Q_OBJECT
|
||||
#ifdef USE_RENDER
|
||||
mpv_handle *mpv;
|
||||
mpv_render_context *mpv_gl;
|
||||
#else
|
||||
mpv::qt::Handle mpv;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
#endif
|
||||
|
||||
friend class MpvRenderer;
|
||||
|
||||
public:
|
||||
static void on_update(void *ctx);
|
||||
|
||||
MpvObject(QQuickItem * parent = 0);
|
||||
virtual ~MpvObject();
|
||||
virtual Renderer *createRenderer() const;
|
||||
|
||||
|
||||
public slots:
|
||||
void command(const QVariant& params);
|
||||
void setProperty(const QString& name, const QVariant& value);
|
||||
void setOption(const QString& name, const QVariant& value);
|
||||
QVariant getProperty(const QString& name) const;
|
||||
QVariant getThumbnailFile(const QString& name) const;
|
||||
|
||||
|
||||
signals:
|
||||
void onUpdate();
|
||||
void positionChanged(int value);
|
||||
void mpv_events();
|
||||
|
||||
private slots:
|
||||
void doUpdate();
|
||||
void on_mpv_events();
|
||||
|
||||
private:
|
||||
void handle_mpv_event(mpv_event *event);
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
13
src/qml/codes.js
Normal file
BIN
src/qml/fonts/NotoSans.ttf
Normal file
2
src/qml/fonts/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Fonts
|
||||
- Noto Sans font comes from https://www.google.com/get/noto/
|
3
src/qml/icons/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Icons
|
||||
- Icons where all from https://material.io/tools/icons/?style=baseline
|
||||
- Exceptions: none yet
|
1
src/qml/icons/backward.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M24 10V2L14 12l10 10v-8c6.6 0 12 5.4 12 12s-5.4 12-12 12-12-5.4-12-12H8c0 8.8 7.2 16 16 16s16-7.2 16-16-7.2-16-16-16zm-2.2 22h-1.7v-6.5l-2 .6v-1.4l3.5-1.3h.2V32zm8.5-3.5c0 .6-.1 1.2-.2 1.6s-.3.8-.6 1.1-.6.5-.9.7-.7.2-1.2.2-.8-.1-1.2-.2-.7-.4-.9-.7-.5-.7-.6-1.1-.2-1-.2-1.6V27c0-.6.1-1.2.2-1.6s.3-.8.6-1.1.6-.5.9-.7.7-.2 1.2-.2.8.1 1.2.2.7.4.9.7.5.7.6 1.1.2 1 .2 1.6v1.5zm-1.6-1.7c0-.4 0-.7-.1-1s-.1-.5-.2-.6-.2-.3-.4-.3-.3-.1-.5-.1-.4 0-.5.1-.3.2-.4.3-.2.4-.2.6-.1.6-.1 1v1.9c0 .4 0 .7.1 1s.1.5.2.6.2.3.4.3.3.1.5.1.4 0 .5-.1.3-.2.4-.3.2-.4.2-.6.1-.6.1-1v-1.9z"/></svg>
|
After Width: | Height: | Size: 660 B |
1
src/qml/icons/forward.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M8 26c0 8.8 7.2 16 16 16s16-7.2 16-16h-4c0 6.6-5.4 12-12 12s-12-5.4-12-12 5.4-12 12-12v8l10-10L24 2v8c-8.8 0-16 7.2-16 16zm13.7 6H20v-6.5l-2 .6v-1.4l3.5-1.3h.2V32zm8.5-3.5c0 .6-.1 1.2-.2 1.6s-.3.8-.6 1.1-.6.5-.9.7-.7.2-1.2.2-.8-.1-1.2-.2-.7-.4-.9-.7-.5-.7-.6-1.1-.2-1-.2-1.6V27c0-.6.1-1.2.2-1.6s.3-.8.6-1.1.6-.5.9-.7.7-.2 1.2-.2.8.1 1.2.2.7.4.9.7.5.7.6 1.1.2 1 .2 1.6v1.5zm-1.7-1.7c0-.4 0-.7-.1-1s-.1-.5-.2-.6-.2-.3-.4-.3-.3-.1-.5-.1-.4 0-.5.1-.3.2-.4.3-.2.4-.2.6-.1.6-.1 1v1.9c0 .4 0 .7.1 1s.1.5.2.6.2.3.4.3.3.1.5.1.4 0 .5-.1.3-.2.4-.3.2-.4.2-.6.1-.6.1-1v-1.9z"/></svg>
|
After Width: | Height: | Size: 662 B |
1
src/qml/icons/fullscreen.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M14 28h-4v10h10v-4h-6v-6zm-4-8h4v-6h6v-4H10v10zm24 14h-6v4h10V28h-4v6zm-6-24v4h6v6h4V10H28z"/></svg>
|
After Width: | Height: | Size: 192 B |
1
src/qml/icons/next.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z"/></svg>
|
After Width: | Height: | Size: 141 B |
1
src/qml/icons/pause.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M12 38h8V10h-8v28zm16-28v28h8V10h-8z"/></svg>
|
After Width: | Height: | Size: 137 B |
1
src/qml/icons/play.svg
Executable file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M16 10v28l22-14z"/></svg>
|
After Width: | Height: | Size: 117 B |
1
src/qml/icons/playlist.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="-12 -12 48 48" enable-background="new -12 -12 48 48"><path d="M26 6H-8v4h34V6zm0-8H-8v4h34v-4zM-8 18h26v-4H-8v4zm30-4v12l10-6-10-6z"/></svg>
|
After Width: | Height: | Size: 212 B |
1
src/qml/icons/prev.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M12 12h4v24h-4zm7 12l17 12V12z"/></svg>
|
After Width: | Height: | Size: 131 B |
1
src/qml/icons/settings.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M38.86 25.95c.08-.64.14-1.29.14-1.95s-.06-1.31-.14-1.95l4.23-3.31c.38-.3.49-.84.24-1.28l-4-6.93c-.25-.43-.77-.61-1.22-.43l-4.98 2.01c-1.03-.79-2.16-1.46-3.38-1.97L29 4.84c-.09-.47-.5-.84-1-.84h-8c-.5 0-.91.37-.99.84l-.75 5.3c-1.22.51-2.35 1.17-3.38 1.97L9.9 10.1c-.45-.17-.97 0-1.22.43l-4 6.93c-.25.43-.14.97.24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31.14 1.95l-4.22 3.31c-.38.3-.49.84-.24 1.28l4 6.93c.25.43.77.61 1.22.43l4.98-2.01c1.03.79 2.16 1.46 3.38 1.97l.75 5.3c.08.47.49.84.99.84h8c.5 0 .91-.37.99-.84l.75-5.3c1.22-.51 2.35-1.17 3.38-1.97l4.98 2.01c.45.17.97 0 1.22-.43l4-6.93c.25-.43.14-.97-.24-1.28l-4.22-3.31zM24 31c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></svg>
|
After Width: | Height: | Size: 787 B |
1
src/qml/icons/subtitles.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M40 8H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4zM8 24h8v4H8v-4zm20 12H8v-4h20v4zm12 0h-8v-4h8v4zm0-8H20v-4h20v4z"/></svg>
|
After Width: | Height: | Size: 255 B |
1
src/qml/icons/volume-down.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M37 24c0-3.53-2.04-6.58-5-8.05v16.11c2.96-1.48 5-4.53 5-8.06zm-27-6v12h8l10 10V8L18 18h-8z"/></svg>
|
After Width: | Height: | Size: 191 B |
1
src/qml/icons/volume-mute.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M33 24c0-3.53-2.04-6.58-5-8.05v4.42l4.91 4.91c.06-.42.09-.85.09-1.28zm5 0c0 1.88-.41 3.65-1.08 5.28l3.03 3.03C41.25 29.82 42 27 42 24c0-8.56-5.99-15.72-14-17.54v4.13c5.78 1.72 10 7.07 10 13.41zM8.55 6L6 8.55 15.45 18H6v12h8l10 10V26.55l8.51 8.51c-1.34 1.03-2.85 1.86-4.51 2.36v4.13c2.75-.63 5.26-1.89 7.37-3.62L39.45 42 42 39.45l-18-18L8.55 6zM24 8l-4.18 4.18L24 16.36V8z"/></svg>
|
After Width: | Height: | Size: 472 B |
1
src/qml/icons/volume-up.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M6 18v12h8l10 10V8L14 18H6zm27 6c0-3.53-2.04-6.58-5-8.05v16.11c2.96-1.48 5-4.53 5-8.06zM28 6.46v4.13c5.78 1.72 10 7.07 10 13.41s-4.22 11.69-10 13.41v4.13c8.01-1.82 14-8.97 14-17.54S36.01 8.28 28 6.46z"/></svg>
|
After Width: | Height: | Size: 301 B |
640
src/qml/main.qml
Normal file
|
@ -0,0 +1,640 @@
|
|||
import QtQuick 2.11
|
||||
import QtQuick.Controls 2.4
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Dialogs 1.3
|
||||
import QtQuick.Layouts 1.11
|
||||
import QtQuick.Window 2.11
|
||||
import player 1.0
|
||||
|
||||
import "codes.js" as LanguageCodes
|
||||
|
||||
ApplicationWindow {
|
||||
id: mainWindow
|
||||
title: "Qt Quick Controls 2"
|
||||
visible: true
|
||||
width: 720
|
||||
height: 480
|
||||
|
||||
property int lastScreenVisibility
|
||||
|
||||
function updatePlayPauseIcon() {
|
||||
var paused = renderer.getProperty("pause")
|
||||
if (paused) {
|
||||
playPauseButton.icon.source = "qrc:/player/icons/play.svg"
|
||||
} else {
|
||||
playPauseButton.icon.source = "qrc:/player/icons/pause.svg"
|
||||
}
|
||||
}
|
||||
|
||||
function updateVolume() {
|
||||
var muted = renderer.getProperty("mute")
|
||||
var volume = renderer.getProperty("volume")
|
||||
|
||||
if (muted || volume === 0) {
|
||||
volumeButton.icon.source = "qrc:/player/icons/volume-mute.svg"
|
||||
} else {
|
||||
if (volume < 25) {
|
||||
volumeButton.icon.source = "qrc:/player/icons/volume-down.svg"
|
||||
} else {
|
||||
volumeButton.icon.source = "qrc:/player/icons/volume-up.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updatePrev() {
|
||||
var playlist_pos = renderer.getProperty("playlist-pos")
|
||||
if (playlist_pos > 0) {
|
||||
playlistPrevButton.visible = true
|
||||
playlistPrevButton.width = playPauseButton.width
|
||||
} else {
|
||||
playlistPrevButton.visible = false
|
||||
playlistPrevButton.width = 0
|
||||
}
|
||||
}
|
||||
|
||||
function updateControls() {
|
||||
updatePrev()
|
||||
updatePlayPauseIcon()
|
||||
updateVolume()
|
||||
}
|
||||
|
||||
function updatePlayPause() {
|
||||
renderer.command(["cycle", "pause"])
|
||||
updatePlayPauseIcon()
|
||||
}
|
||||
|
||||
function setSubtitle(sub) {
|
||||
console.log(sub)
|
||||
}
|
||||
|
||||
function tracksMenuUpdate() {
|
||||
var tracks = renderer.getProperty("track-list/count")
|
||||
var track = 0
|
||||
subModel.clear()
|
||||
audioModel.clear()
|
||||
vidModel.clear()
|
||||
|
||||
var aid = renderer.getProperty("aid")
|
||||
var sid = renderer.getProperty("sid")
|
||||
var vid = renderer.getProperty("vid")
|
||||
|
||||
console.log("Updating Track Menu, Total Tracks: " + tracks)
|
||||
for (track = 0; track <= tracks; track++) {
|
||||
var trackID = renderer.getProperty("track-list/" + track + "/id")
|
||||
var trackType = renderer.getProperty(
|
||||
"track-list/" + track + "/type")
|
||||
var trackLang = LanguageCodes.localeCodeToEnglish(
|
||||
String(renderer.getProperty(
|
||||
"track-list/" + track + "/lang")))
|
||||
var trackTitle = renderer.getProperty(
|
||||
"track-list/" + track + "/title")
|
||||
if (trackType == "sub") {
|
||||
subModel.append({
|
||||
key: trackLang,
|
||||
value: trackID
|
||||
})
|
||||
if (renderer.getProperty("track-list/" + track + "/selected")) {
|
||||
subList.currentIndex = subList.count
|
||||
}
|
||||
} else if (trackType == "audio") {
|
||||
audioModel.append({
|
||||
key: (trackTitle === undefined ? "" : trackTitle + " ")
|
||||
+ trackLang,
|
||||
value: trackID
|
||||
})
|
||||
if (renderer.getProperty("track-list/" + track + "/selected")) {
|
||||
audioList.currentIndex = audioList.count
|
||||
}
|
||||
} else if (trackType == "video") {
|
||||
vidModel.append({
|
||||
key: "Video " + trackID,
|
||||
value: trackID
|
||||
})
|
||||
if (renderer.getProperty("track-list/" + track + "/selected")) {
|
||||
vidList.currentIndex = vidList.count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MpvObject {
|
||||
id: renderer
|
||||
anchors.fill: parent
|
||||
Component.onCompleted: {
|
||||
var args = Qt.application.arguments
|
||||
var len = Qt.application.arguments.length
|
||||
var argNo = 0
|
||||
renderer.setOption("ytdl-format", "bestvideo[width<=" + Screen.width + "][height<=" + Screen.height + "]+bestaudio")
|
||||
if (len > 1) {
|
||||
for (argNo = 0; argNo < len; argNo++) {
|
||||
var argument = args[argNo]
|
||||
if (argument.startsWith("--")) {
|
||||
argument = argument.substr(2)
|
||||
if (argument.length > 0) {
|
||||
var splitArg = argument.split(/=(.+)/)
|
||||
renderer.setOption(splitArg[0], splitArg[1])
|
||||
}
|
||||
} else {
|
||||
renderer.command(["loadfile", argument, "append-play"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FontLoader {
|
||||
id: notoFont
|
||||
source: "fonts/NotoSans.ttf"
|
||||
}
|
||||
|
||||
function createTimestamp(d) {
|
||||
d = Number(d)
|
||||
var h = Math.floor(d / 3600)
|
||||
var m = Math.floor(d % 3600 / 60)
|
||||
var s = Math.floor(d % 3600 % 60)
|
||||
|
||||
var hour = h > 0 ? h + ":" : ""
|
||||
var minute = m + ":"
|
||||
var second = s > 10 ? s : "0" + s
|
||||
return hour + minute + second
|
||||
}
|
||||
|
||||
function setProgressBarEnd(val) {
|
||||
progressBar.to = val
|
||||
}
|
||||
|
||||
function setProgressBarValue(val) {
|
||||
timeLabel.text = createTimestamp(val) + " / " + createTimestamp(
|
||||
progressBar.to)
|
||||
progressBar.value = val
|
||||
}
|
||||
|
||||
function setTitle() {
|
||||
titleLabel.text = renderer.getProperty("media-title")
|
||||
}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: loadDialog
|
||||
title: "URL / File Path"
|
||||
standardButtons: StandardButton.Cancel | StandardButton.Open
|
||||
|
||||
onAccepted: {
|
||||
renderer.command(["loadfile", pathText.text])
|
||||
pathText.text = ""
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: pathText
|
||||
text: "/home/kitteh/babyshark.mkv"
|
||||
placeholderText: qsTr("URL / File Path")
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseAreaBar
|
||||
x: 0
|
||||
y: parent.height
|
||||
width: parent.width
|
||||
height: controlsBar.height + progressBar.height
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
updateControls()
|
||||
controlsBar.visible = true
|
||||
controlsBackground.visible = true
|
||||
titleBar.visible = true
|
||||
titleBackground.visible = true
|
||||
controlsBar.height = 35
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseAreaPlayer
|
||||
width: parent.width
|
||||
anchors.bottom: mouseAreaBar.top
|
||||
anchors.bottomMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.top: titleBar.bottom
|
||||
anchors.topMargin: 0
|
||||
hoverEnabled: true
|
||||
onClicked: loadDialog.open()
|
||||
onEntered: {
|
||||
if (subtitlesMenu.visible) {
|
||||
return
|
||||
} else {
|
||||
controlsBar.visible = false
|
||||
controlsBackground.visible = false
|
||||
titleBar.visible = false
|
||||
titleBackground.visible = false
|
||||
controlsBar.height = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: titleBackground
|
||||
height: titleBar.height
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: "black"
|
||||
opacity: 0.6
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: titleBar
|
||||
height: renderer.height / 16
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: parent.width / 128
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: parent.width / 128
|
||||
anchors.top: parent.top
|
||||
|
||||
visible: true
|
||||
color: "transparent"
|
||||
|
||||
Text {
|
||||
id: titleLabel
|
||||
text: "Title"
|
||||
color: "white"
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 4
|
||||
anchors.topMargin: 4
|
||||
anchors.top: parent.top
|
||||
font.family: notoFont.name
|
||||
fontSizeMode: Text.Fit
|
||||
minimumPixelSize: 10
|
||||
font.pixelSize: 72
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: controlsBackground
|
||||
height: controlsBar.height + (progressBar.topPadding * 2) - (progressBackground.height * 2)
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: "black"
|
||||
opacity: 0.6
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: controlsBar
|
||||
height: renderer.height / 16
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: parent.width / 128
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: parent.width / 128
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 1
|
||||
visible: true
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
id: subtitlesMenuBackground
|
||||
height: controlsBar.height + (progressBar.topPadding * 2)
|
||||
- (progressBackground.height * 2)
|
||||
anchors.fill: subtitlesMenu
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: false
|
||||
color: "black"
|
||||
opacity: 0.6
|
||||
radius: 5
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: subtitlesMenu
|
||||
color: "transparent"
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
visible: false
|
||||
anchors.right: subtitlesButton.right
|
||||
anchors.bottom: progressBar.top
|
||||
radius: 5
|
||||
|
||||
Text {
|
||||
id: audioLabel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: "Audio"
|
||||
color: "white"
|
||||
font.family: notoFont.name
|
||||
font.pixelSize: 14
|
||||
renderType: Text.NativeRendering
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: 1
|
||||
}
|
||||
ComboBox {
|
||||
id: audioList
|
||||
textRole: "key"
|
||||
anchors.top: audioLabel.bottom
|
||||
model: ListModel {
|
||||
id: audioModel
|
||||
}
|
||||
onActivated: {
|
||||
renderer.command(["set", "aid", String(audioModel.get(
|
||||
index).value)])
|
||||
}
|
||||
opacity: 1
|
||||
}
|
||||
Text {
|
||||
id: subLabel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: "Subtitles"
|
||||
color: "white"
|
||||
font.family: notoFont.name
|
||||
font.pixelSize: 14
|
||||
anchors.top: audioList.bottom
|
||||
renderType: Text.NativeRendering
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: 1
|
||||
}
|
||||
ComboBox {
|
||||
id: subList
|
||||
textRole: "key"
|
||||
anchors.top: subLabel.bottom
|
||||
model: ListModel {
|
||||
id: subModel
|
||||
}
|
||||
onActivated: {
|
||||
renderer.command(["set", "sid", String(subModel.get(
|
||||
index).value)])
|
||||
}
|
||||
opacity: 1
|
||||
}
|
||||
Text {
|
||||
id: vidLabel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: "Video"
|
||||
color: "white"
|
||||
font.family: notoFont.name
|
||||
font.pixelSize: 14
|
||||
anchors.top: subList.bottom
|
||||
renderType: Text.NativeRendering
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: 1
|
||||
}
|
||||
ComboBox {
|
||||
id: vidList
|
||||
textRole: "key"
|
||||
anchors.top: vidLabel.bottom
|
||||
model: ListModel {
|
||||
id: vidModel
|
||||
}
|
||||
onActivated: {
|
||||
renderer.command(["set", "vid", String(vidModel.get(
|
||||
index).value)])
|
||||
}
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: progressBar
|
||||
to: 1
|
||||
value: 0.0
|
||||
palette.dark: "#f00"
|
||||
anchors.bottom: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 0
|
||||
|
||||
bottomPadding: 0
|
||||
|
||||
onMoved: {
|
||||
renderer.command(["seek", progressBar.value, "absolute"])
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: progressBackground
|
||||
x: progressBar.leftPadding
|
||||
y: progressBar.topPadding + progressBar.availableHeight / 2 - height / 2
|
||||
implicitHeight: (renderer.height / 256) < 2 ? 2 : renderer.height / 256
|
||||
width: progressBar.availableWidth
|
||||
height: implicitHeight
|
||||
color: Qt.rgba(255, 255, 255, 0.4)
|
||||
|
||||
Rectangle {
|
||||
width: progressBar.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: "red"
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
x: progressBar.leftPadding + progressBar.visualPosition
|
||||
* (progressBar.availableWidth - width)
|
||||
y: progressBar.topPadding + progressBar.availableHeight / 2 - height / 2
|
||||
implicitWidth: 12
|
||||
implicitHeight: 12
|
||||
radius: 12
|
||||
color: "red"
|
||||
border.color: "red"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: playlistPrevButton
|
||||
icon.name: "prev"
|
||||
icon.source: "icons/prev.svg"
|
||||
icon.color: "white"
|
||||
display: AbstractButton.IconOnly
|
||||
visible: false
|
||||
width: 0
|
||||
onClicked: {
|
||||
renderer.command(["playlist-prev"])
|
||||
updatePrev()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: playPauseButton
|
||||
icon.name: "pause"
|
||||
icon.source: "icons/pause.svg"
|
||||
icon.color: "white"
|
||||
display: AbstractButton.IconOnly
|
||||
anchors.left: playlistPrevButton.right
|
||||
onClicked: {
|
||||
updatePlayPause()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: playlistNextButton
|
||||
icon.name: "next"
|
||||
icon.source: "icons/next.svg"
|
||||
icon.color: "white"
|
||||
display: AbstractButton.IconOnly
|
||||
anchors.left: playPauseButton.right
|
||||
onClicked: {
|
||||
renderer.command(["playlist-next", "force"])
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: volumeButton
|
||||
icon.name: "volume-up"
|
||||
icon.source: "icons/volume-up.svg"
|
||||
icon.color: "white"
|
||||
display: AbstractButton.IconOnly
|
||||
anchors.left: playlistNextButton.right
|
||||
onClicked: {
|
||||
renderer.command(["cycle", "mute"])
|
||||
updateVolume()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
Slider {
|
||||
id: volumeBar
|
||||
to: 100
|
||||
value: 100
|
||||
palette.dark: "#f00"
|
||||
|
||||
implicitWidth: Math.max(
|
||||
background ? background.implicitWidth : 0,
|
||||
(handle ? handle.implicitWidth : 0)
|
||||
+ leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(
|
||||
background ? background.implicitHeight : 0,
|
||||
(handle ? handle.implicitHeight : 0)
|
||||
+ topPadding + bottomPadding)
|
||||
|
||||
anchors.left: volumeButton.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
onMoved: {
|
||||
renderer.command(["set", "volume", Math.round(
|
||||
volumeBar.value).toString()])
|
||||
updateVolume()
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
x: volumeBar.leftPadding + volumeBar.visualPosition
|
||||
* (volumeBar.availableWidth - width)
|
||||
y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2
|
||||
implicitWidth: 12
|
||||
implicitHeight: 12
|
||||
radius: 12
|
||||
color: "#f6f6f6"
|
||||
border.color: "#f6f6f6"
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
x: volumeBar.leftPadding
|
||||
y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2
|
||||
implicitWidth: 60
|
||||
implicitHeight: 3
|
||||
width: volumeBar.availableWidth
|
||||
height: implicitHeight
|
||||
color: "#33333311"
|
||||
Rectangle {
|
||||
width: volumeBar.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: timeLabel
|
||||
text: "0:00 / 0:00"
|
||||
color: "white"
|
||||
anchors.left: volumeBar.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
padding: 5
|
||||
font.family: notoFont.name
|
||||
font.pixelSize: 12
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
Button {
|
||||
id: subtitlesButton
|
||||
icon.name: "subtitles"
|
||||
icon.source: "icons/subtitles.svg"
|
||||
icon.color: "white"
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
anchors.right: settingsButton.left
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
tracksMenuUpdate()
|
||||
subtitlesMenu.visible = !subtitlesMenu.visible
|
||||
subtitlesMenuBackground.visible = !subtitlesMenuBackground.visible
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: settingsButton
|
||||
icon.name: "settings"
|
||||
icon.source: "icons/settings.svg"
|
||||
icon.color: "white"
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
anchors.right: fullscreenButton.left
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
loadDialog.open()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: fullscreenButton
|
||||
icon.name: "fullscreen"
|
||||
icon.source: "icons/fullscreen.svg"
|
||||
icon.color: "white"
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
anchors.right: parent.right
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
if (mainWindow.visibility != Window.FullScreen) {
|
||||
lastScreenVisibility = mainWindow.visibility
|
||||
mainWindow.visibility = Window.FullScreen
|
||||
} else {
|
||||
mainWindow.visibility = lastScreenVisibility
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
20
src/qml/qml.qrc
Normal file
|
@ -0,0 +1,20 @@
|
|||
<RCC>
|
||||
<qresource prefix="/player">
|
||||
<file>main.qml</file>
|
||||
<file>icons/play.svg</file>
|
||||
<file>icons/pause.svg</file>
|
||||
<file>icons/forward.svg</file>
|
||||
<file>icons/backward.svg</file>
|
||||
<file>icons/settings.svg</file>
|
||||
<file>icons/fullscreen.svg</file>
|
||||
<file>icons/volume-up.svg</file>
|
||||
<file>icons/volume-mute.svg</file>
|
||||
<file>icons/volume-down.svg</file>
|
||||
<file>icons/next.svg</file>
|
||||
<file>icons/prev.svg</file>
|
||||
<file>icons/subtitles.svg</file>
|
||||
<file>fonts/NotoSans.ttf</file>
|
||||
<file>icons/playlist.svg</file>
|
||||
<file>codes.js</file>
|
||||
</qresource>
|
||||
</RCC>
|