Refactored the code

pull/3/head
reionwong 5 years ago
parent 3585dcd536
commit 4b1a0f28c1

@ -1,75 +1,55 @@
#
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.14)
project(cutefish-videoplayer LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package(ECM 1.0.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(KDEInstallDirs)
include(ECMInstallIcons)
include(FeatureSummary)
find_package(Qt5Core)
set_package_properties(Qt5Core PROPERTIES TYPE REQUIRED)
find_package(Qt5DBus)
set_package_properties(Qt5DBus PROPERTIES TYPE REQUIRED)
find_package(Qt5Qml)
set_package_properties(Qt5Qml PROPERTIES TYPE REQUIRED)
find_package(Qt5Quick)
set_package_properties(Qt5Quick PROPERTIES TYPE REQUIRED)
find_package(Qt5QuickControls2)
set_package_properties(Qt5QuickControls2 PROPERTIES TYPE REQUIRED)
find_package(Qt5 COMPONENTS Core Quick DBus Widgets REQUIRED)
find_package(Libmpv)
set_package_properties(Libmpv PROPERTIES TYPE REQUIRED
URL "https://mpv.io")
find_package(KF5Config)
set_package_properties(KF5Config PROPERTIES TYPE REQUIRED
URL "https://api.kde.org/frameworks/kconfig/html/index.html")
find_package(KF5CoreAddons)
set_package_properties(KF5CoreAddons PROPERTIES TYPE REQUIRED
URL "https://api.kde.org/frameworks/kcoreaddons/html/index.html")
find_package(KF5DocTools)
set_package_properties(KF5DocTools PROPERTIES
DESCRIPTION "https://api.kde.org/frameworks/kdoctools/html/index.html")
find_package(KF5FileMetaData)
set_package_properties(KF5FileMetaData PROPERTIES TYPE REQUIRED
URL "https://api.kde.org/frameworks/kfilemetadata/html/index.html")
find_package(KF5KIO)
set_package_properties(KF5KIO PROPERTIES TYPE REQUIRED
URL "https://api.kde.org/frameworks/kio/html/index.html")
#find_package(KF5Kirigami2)
#set_package_properties(KF5Kirigami2 PROPERTIES TYPE REQUIRED
# URL "https://api.kde.org/frameworks/kirigami/html/index.html")
find_package(KF5XmlGui)
set_package_properties(KF5XmlGui PROPERTIES TYPE REQUIRED
URL "https://api.kde.org/frameworks/kxmlgui/html/index.html")
#option(InstallColorSchemes "Installs breeze color schemes." OFF)
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
add_subdirectory(src)
qt5_add_dbus_interface(screensaver_inhibit_SRCS ./org.freedesktop.ScreenSaver.xml screensaverdbusinterface)
set(PROJECT_SOURCES
src/main.cpp
src/application.cpp
src/playlistmodel.cpp
src/playlistitem.cpp
src/track.cpp
src/tracksmodel.cpp
src/mpvobject.cpp
src/qthelper.h
src/lockmanager.cpp
src/mpris2/mediaplayer2.cpp
src/mpris2/mediaplayer2player.cpp
qml.qrc
${screensaver_inhibit_SRCS}
)
add_executable(cutefish-videoplayer
${PROJECT_SOURCES}
)
target_link_libraries(cutefish-videoplayer
PRIVATE
Qt5::Core
Qt5::Quick
Qt5::DBus
Qt5::Widgets
Libmpv::Libmpv
)
install(TARGETS cutefish-videoplayer DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES
cutefish-videoplayer.desktop
DESTINATION /usr/share/applications/
COMPONENT Runtime
)

@ -2,46 +2,15 @@
An open source video player built with Qt/QML and libmpv.
Based on [haruna](https://github.com/g-fb/haruna)
### Third Party Code
# Features
[haruna](https://github.com/g-fb/haruna)
these are just some features that set Haruna apart from others players
## Dependencies
- play online videos, through youtube-dl
- toggle playlist with mouse-over, playlist overlays the video
- auto skip chapter containing certain words
- configurable shortcuts and mouse buttons
- quick jump to next chapter by middle click on progress bar
# Dependencies
### Build time
- Qt5Core
- Qt5DBus
- Qt5Qml
- Qt5Quick
- Qt5QuickControls2
- Libmpv
- ExtraCmakeModules
- KF5Config
- KF5CoreAddons
- KF5FileMetaData
- KF5I18n
- KF5IconThemes
- KF5KIO
- KF5XmlGui
### Runtime
- Kio-extras
- Breeze icons
- Breeze widgets style
- QQC2-Desktop-Style
- Youtube-dl
```sh
sudo apt install extra-cmake-modules qtbase5-dev qtdeclarative5-dev qtquickcontrols2-5-dev libmpv-dev
```
# Build and Install
@ -55,4 +24,4 @@ sudo make install
# License
This project has been licensed by GPLv3.
This project has been licensed by GPLv3.

@ -1,9 +1,8 @@
[Desktop Entry]
Name=Cutefish Video Player
Exec=cutefish-videoplayer %u
Icon=videoplayer
Icon=cutefish-videoplayer
Type=Application
Categories=Qt;KDE;AudioVideo;Player;Video;TV;
Categories=Qt;AudioVideo;Player;Video;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,srt
Terminal=false
Terminal=false

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

@ -0,0 +1,11 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -.00047914)" fill="#ffffff">
<rect y="6" width="5" height="4" ry="1" fill-rule="evenodd"/>
<path d="m6 3-3 3v4l3 3z"/>
<g fill-rule="evenodd">
<path d="M 13.139,1.428 C 12.594,1 12,1.652 12.455,2.16 A 8,8 0 0 1 15,8 8,8 0 0 1 12.446,13.852 C 12,14.323 12.626,15 13.123,14.58 A 9,9 0 0 0 16,8 9,9 0 0 0 13.139,1.428 Z"/>
<path d="m10.775 2.889c-0.587-0.535-1.199 0.225-0.684 0.732a6 6 0 0 1 1.909 4.379 6 6 0 0 1-1.916 4.389c-0.469 0.446 0.173 1.146 0.68 0.728a7 7 0 0 0 2.236-5.117 7 7 0 0 0-2.225-5.111z"/>
<path d="M 8.41,4.35 C 7.89,3.856 7.255,4.57 7.727,5.08 A 4,4 0 0 1 9,8 4,4 0 0 1 7.723,10.926 c -0.473,0.43 0.14,1.152 0.68,0.73 A 5,5 0 0 0 10,8 5,5 0 0 0 8.41,4.35 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 820 B

@ -0,0 +1,11 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -.00047914)" fill="#ffffff">
<rect y="6" width="5" height="4" ry="1" fill-rule="evenodd"/>
<path d="m6 3-3 3v4l3 3z"/>
<g fill-rule="evenodd">
<path d="M 13.139,1.428 C 12.594,1 12,1.652 12.455,2.16 A 8,8 0 0 1 15,8 8,8 0 0 1 12.446,13.852 C 12,14.323 12.626,15 13.123,14.58 A 9,9 0 0 0 16,8 9,9 0 0 0 13.139,1.428 Z" opacity=".35"/>
<path d="m10.775 2.889c-0.587-0.535-1.199 0.225-0.684 0.732a6 6 0 0 1 1.909 4.379 6 6 0 0 1-1.916 4.389c-0.469 0.446 0.173 1.146 0.68 0.728a7 7 0 0 0 2.236-5.117 7 7 0 0 0-2.225-5.111z" opacity=".35"/>
<path d="M 8.41,4.35 C 7.89,3.856 7.255,4.57 7.727,5.08 A 4,4 0 0 1 9,8 4,4 0 0 1 7.723,10.926 c -0.473,0.43 0.14,1.152 0.68,0.73 A 5,5 0 0 0 10,8 5,5 0 0 0 8.41,4.35 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 848 B

@ -0,0 +1,11 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -.00047914)" fill="#ffffff">
<rect y="6" width="5" height="4" ry="1" fill-rule="evenodd"/>
<path d="m6 3-3 3v4l3 3z"/>
<g fill-rule="evenodd">
<path d="M 13.139,1.428 C 12.594,1 12,1.652 12.455,2.16 A 8,8 0 0 1 15,8 8,8 0 0 1 12.446,13.852 C 12,14.323 12.626,15 13.123,14.58 A 9,9 0 0 0 16,8 9,9 0 0 0 13.139,1.428 Z" opacity=".35"/>
<path d="m10.775 2.889c-0.587-0.535-1.199 0.225-0.684 0.732a6 6 0 0 1 1.909 4.379 6 6 0 0 1-1.916 4.389c-0.469 0.446 0.173 1.146 0.68 0.728a7 7 0 0 0 2.236-5.117 7 7 0 0 0-2.225-5.111z"/>
<path d="M 8.41,4.35 C 7.89,3.856 7.255,4.57 7.727,5.08 A 4,4 0 0 1 9,8 4,4 0 0 1 7.723,10.926 c -0.473,0.43 0.14,1.152 0.68,0.73 A 5,5 0 0 0 10,8 5,5 0 0 0 8.41,4.35 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 834 B

@ -0,0 +1,3 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m12.738 1.291c-0.37563 0.026826-0.62454 0.48828-0.2832 0.86914 1.6202 1.5104 2.5416 3.6249 2.5449 5.8398-0.0026 2.2205-0.92792 4.3401-2.5547 5.8516-0.44531 0.47165 0.18064 1.1484 0.67774 0.72852 1.8304-1.6992 2.8725-4.0825 2.877-6.5801-0.0026-2.4925-1.0386-4.8722-2.8613-6.5723-0.13636-0.10693-0.27518-0.14566-0.40039-0.13672zm-2.3906 1.416c-0.39562 0.011891-0.64227 0.53363-0.25586 0.91406 1.2147 1.1326 1.9056 2.7181 1.9082 4.3789-0.0019 1.6654-0.69596 3.2551-1.916 4.3887-0.46835 0.44623 0.17346 1.1467 0.67969 0.72852 1.423-1.3216 2.233-3.1751 2.2363-5.1172-0.0019-1.9383-0.8074-3.789-2.2246-5.1113-0.14685-0.13372-0.29586-0.1856-0.42774-0.18164zm-4.3477 0.29297-3 3h-2c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h2l3 3v-10zm2.0176 1.1836c-0.37603 0.016385-0.64465 0.51365-0.29102 0.89648 0.81045 0.75501 1.2715 1.8123 1.2734 2.9199-0.00128 1.1103-0.464 2.1701-1.2773 2.9258-0.47259 0.42993 0.13957 1.1522 0.67969 0.73047 1.0168-0.94425 1.5955-2.2686 1.5977-3.6562-0.00172-1.3844-0.57742-2.7062-1.5898-3.6504-0.13024-0.12342-0.26724-0.17148-0.39258-0.16602z" fill="#ffffff" fill-rule="evenodd" opacity=".5" style="paint-order:normal"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
viewBox="0 0 22 22"
id="svg17"
sodipodi:docname="view-list-tree.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata21">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2160"
inkscape:window-height="1312"
id="namedview19"
showgrid="false"
inkscape:zoom="33.409091"
inkscape:cx="11"
inkscape:cy="11"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<defs
id="defs3">
<style
id="current-color-scheme"
type="text/css">.ColorScheme-Text {
color:#FFFFFF;
}</style>
</defs>
<rect
class="ColorScheme-Text"
x="7"
y="5"
width="12"
height="1"
ry="0"
fill="currentColor"
id="rect5" />
<rect
class="ColorScheme-Text"
x="7"
y="11"
width="12"
height="1"
ry="0"
fill="currentColor"
id="rect9" />
<rect
class="ColorScheme-Text"
x="7"
y="17"
width="12"
height="1"
ry="0"
fill="currentColor"
id="rect13" />
<rect
style="fill:#FFFFFF;fill-opacity:1;stroke-width:0.768904"
id="rect855"
width="3"
height="3"
x="3"
y="4"
ry="1.5" />
<rect
style="fill:#FFFFFF;fill-opacity:1;stroke-width:0.768904"
id="rect855-3"
width="3"
height="3"
x="3"
y="10"
ry="1.5" />
<rect
style="fill:#FFFFFF;fill-opacity:1;stroke-width:0.768904"
id="rect855-3-6"
width="3"
height="3"
x="3"
y="16"
ry="1.5" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,6 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1.25,0,0,1.25,-2,-2)" fill="#fff" stroke-width="1.8257">
<rect x="4" y="3" width="2" height="10" rx="1" ry="1"/>
<rect x="10" y="3" width="2" height="10" rx="1" ry="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 311 B

@ -0,0 +1,3 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m4.1288 1.6066c-0.3219 0.039894-0.58908 0.17974-0.80457 0.42182s-0.32418 0.52688-0.32418 0.85144v10.241c0 0.32456 0.10871 0.60936 0.32418 0.85144 0.21549 0.24208 0.48272 0.38191 0.80457 0.42182 0.3219 0.03991 0.61401-0.03125 0.8827-0.21481l7.4286-5.1202c0.23677-0.16228 0.40168-0.38357 0.4921-0.65225 0.09047-0.27134 0.09047-0.53977 0-0.80849-0.090471-0.26869-0.25408-0.48733-0.48821-0.65225l-7.4326-5.1243c-0.2678-0.18622-0.56169-0.25561-0.8827-0.21481z" fill="#fff" stroke-width="1.9996"/>
</svg>

After

Width:  |  Height:  |  Size: 611 B

@ -0,0 +1,4 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m13.119 3.0087c0.2513 0.031145 0.45989 0.14032 0.62812 0.32931s0.25308 0.41133 0.25308 0.66471v7.9948c0 0.25338-0.08487 0.47572-0.25308 0.66471-0.16823 0.18899-0.37685 0.29815-0.62812 0.32931-0.2513 0.03116-0.47935-0.0244-0.68911-0.1677l-5.7994-3.9973c-0.18484-0.12669-0.31359-0.29945-0.38418-0.5092-0.07063-0.21183-0.07063-0.42139 0-0.63118 0.07063-0.20976 0.19836-0.38045 0.38114-0.5092l5.8025-4.0005c0.20907-0.14538 0.4385-0.19955 0.68911-0.1677z" fill="#FFFFFF" stroke-width="1.5611"/>
<rect x="2" y="3" width="2" height="10" rx="1" ry="1" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4164"/>
</svg>

After

Width:  |  Height:  |  Size: 750 B

@ -0,0 +1,4 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m2.8812 3.0087c-0.2513 0.031145-0.45989 0.14032-0.62812 0.32931s-0.25308 0.41133-0.25308 0.66471v7.9948c0 0.25338 0.08487 0.47572 0.25308 0.66471 0.16823 0.18899 0.37685 0.29815 0.62812 0.32931 0.2513 0.03116 0.47935-0.0244 0.68911-0.1677l5.7994-3.9973c0.18484-0.12669 0.31359-0.29945 0.38418-0.5092 0.07063-0.21183 0.07063-0.42139 0-0.63118-0.07063-0.20976-0.19836-0.38045-0.38114-0.5092l-5.8025-4.0005c-0.20907-0.14538-0.4385-0.19955-0.68911-0.1677z" fill="#FFFFFF" stroke-width="1.5611"/>
<rect transform="scale(-1,1)" x="-14" y="3" width="2" height="10" rx="1" ry="1" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4164"/>
</svg>

After

Width:  |  Height:  |  Size: 778 B

@ -0,0 +1,11 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -.00047914)" fill="#363636">
<rect y="6" width="5" height="4" ry="1" fill-rule="evenodd"/>
<path d="m6 3-3 3v4l3 3z"/>
<g fill-rule="evenodd">
<path d="M 13.139,1.428 C 12.594,1 12,1.652 12.455,2.16 A 8,8 0 0 1 15,8 8,8 0 0 1 12.446,13.852 C 12,14.323 12.626,15 13.123,14.58 A 9,9 0 0 0 16,8 9,9 0 0 0 13.139,1.428 Z"/>
<path d="m10.775 2.889c-0.587-0.535-1.199 0.225-0.684 0.732a6 6 0 0 1 1.909 4.379 6 6 0 0 1-1.916 4.389c-0.469 0.446 0.173 1.146 0.68 0.728a7 7 0 0 0 2.236-5.117 7 7 0 0 0-2.225-5.111z"/>
<path d="M 8.41,4.35 C 7.89,3.856 7.255,4.57 7.727,5.08 A 4,4 0 0 1 9,8 4,4 0 0 1 7.723,10.926 c -0.473,0.43 0.14,1.152 0.68,0.73 A 5,5 0 0 0 10,8 5,5 0 0 0 8.41,4.35 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 820 B

@ -0,0 +1,11 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -.00047914)" fill="#363636">
<rect y="6" width="5" height="4" ry="1" fill-rule="evenodd"/>
<path d="m6 3-3 3v4l3 3z"/>
<g fill-rule="evenodd">
<path d="M 13.139,1.428 C 12.594,1 12,1.652 12.455,2.16 A 8,8 0 0 1 15,8 8,8 0 0 1 12.446,13.852 C 12,14.323 12.626,15 13.123,14.58 A 9,9 0 0 0 16,8 9,9 0 0 0 13.139,1.428 Z" opacity=".35"/>
<path d="m10.775 2.889c-0.587-0.535-1.199 0.225-0.684 0.732a6 6 0 0 1 1.909 4.379 6 6 0 0 1-1.916 4.389c-0.469 0.446 0.173 1.146 0.68 0.728a7 7 0 0 0 2.236-5.117 7 7 0 0 0-2.225-5.111z" opacity=".35"/>
<path d="M 8.41,4.35 C 7.89,3.856 7.255,4.57 7.727,5.08 A 4,4 0 0 1 9,8 4,4 0 0 1 7.723,10.926 c -0.473,0.43 0.14,1.152 0.68,0.73 A 5,5 0 0 0 10,8 5,5 0 0 0 8.41,4.35 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 848 B

@ -0,0 +1,11 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -.00047914)" fill="#363636">
<rect y="6" width="5" height="4" ry="1" fill-rule="evenodd"/>
<path d="m6 3-3 3v4l3 3z"/>
<g fill-rule="evenodd">
<path d="M 13.139,1.428 C 12.594,1 12,1.652 12.455,2.16 A 8,8 0 0 1 15,8 8,8 0 0 1 12.446,13.852 C 12,14.323 12.626,15 13.123,14.58 A 9,9 0 0 0 16,8 9,9 0 0 0 13.139,1.428 Z" opacity=".35"/>
<path d="m10.775 2.889c-0.587-0.535-1.199 0.225-0.684 0.732a6 6 0 0 1 1.909 4.379 6 6 0 0 1-1.916 4.389c-0.469 0.446 0.173 1.146 0.68 0.728a7 7 0 0 0 2.236-5.117 7 7 0 0 0-2.225-5.111z"/>
<path d="M 8.41,4.35 C 7.89,3.856 7.255,4.57 7.727,5.08 A 4,4 0 0 1 9,8 4,4 0 0 1 7.723,10.926 c -0.473,0.43 0.14,1.152 0.68,0.73 A 5,5 0 0 0 10,8 5,5 0 0 0 8.41,4.35 Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 834 B

@ -0,0 +1,3 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m12.738 1.291c-0.37563 0.026826-0.62454 0.48828-0.2832 0.86914 1.6202 1.5104 2.5416 3.6249 2.5449 5.8398-0.0026 2.2205-0.92792 4.3401-2.5547 5.8516-0.44531 0.47165 0.18064 1.1484 0.67774 0.72852 1.8304-1.6992 2.8725-4.0825 2.877-6.5801-0.0026-2.4925-1.0386-4.8722-2.8613-6.5723-0.13636-0.10693-0.27518-0.14566-0.40039-0.13672zm-2.3906 1.416c-0.39562 0.011891-0.64227 0.53363-0.25586 0.91406 1.2147 1.1326 1.9056 2.7181 1.9082 4.3789-0.0019 1.6654-0.69596 3.2551-1.916 4.3887-0.46835 0.44623 0.17346 1.1467 0.67969 0.72852 1.423-1.3216 2.233-3.1751 2.2363-5.1172-0.0019-1.9383-0.8074-3.789-2.2246-5.1113-0.14685-0.13372-0.29586-0.1856-0.42774-0.18164zm-4.3477 0.29297-3 3h-2c-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h2l3 3v-10zm2.0176 1.1836c-0.37603 0.016385-0.64465 0.51365-0.29102 0.89648 0.81045 0.75501 1.2715 1.8123 1.2734 2.9199-0.00128 1.1103-0.464 2.1701-1.2773 2.9258-0.47259 0.42993 0.13957 1.1522 0.67969 0.73047 1.0168-0.94425 1.5955-2.2686 1.5977-3.6562-0.00172-1.3844-0.57742-2.7062-1.5898-3.6504-0.13024-0.12342-0.26724-0.17148-0.39258-0.16602z" fill="#363636" fill-rule="evenodd" opacity=".5" style="paint-order:normal"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
viewBox="0 0 22 22"
id="svg17"
sodipodi:docname="view-list-tree.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata21">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2160"
inkscape:window-height="1312"
id="namedview19"
showgrid="false"
inkscape:zoom="33.409091"
inkscape:cx="11"
inkscape:cy="11"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<defs
id="defs3">
<style
id="current-color-scheme"
type="text/css">.ColorScheme-Text {
color:#363636;
}</style>
</defs>
<rect
class="ColorScheme-Text"
x="7"
y="5"
width="12"
height="1"
ry="0"
fill="currentColor"
id="rect5" />
<rect
class="ColorScheme-Text"
x="7"
y="11"
width="12"
height="1"
ry="0"
fill="currentColor"
id="rect9" />
<rect
class="ColorScheme-Text"
x="7"
y="17"
width="12"
height="1"
ry="0"
fill="currentColor"
id="rect13" />
<rect
style="fill:#363636;fill-opacity:1;stroke-width:0.768904"
id="rect855"
width="3"
height="3"
x="3"
y="4"
ry="1.5" />
<rect
style="fill:#363636;fill-opacity:1;stroke-width:0.768904"
id="rect855-3"
width="3"
height="3"
x="3"
y="10"
ry="1.5" />
<rect
style="fill:#363636;fill-opacity:1;stroke-width:0.768904"
id="rect855-3-6"
width="3"
height="3"
x="3"
y="16"
ry="1.5" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,6 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1.25,0,0,1.25,-2,-2)" fill="#363636" stroke-width="1.8257">
<rect x="4" y="3" width="2" height="10" rx="1" ry="1"/>
<rect x="10" y="3" width="2" height="10" rx="1" ry="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 314 B

@ -0,0 +1,3 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m4.1288 1.6066c-0.3219 0.039894-0.58908 0.17974-0.80457 0.42182s-0.32418 0.52688-0.32418 0.85144v10.241c0 0.32456 0.10871 0.60936 0.32418 0.85144 0.21549 0.24208 0.48272 0.38191 0.80457 0.42182 0.3219 0.03991 0.61401-0.03125 0.8827-0.21481l7.4286-5.1202c0.23677-0.16228 0.40168-0.38357 0.4921-0.65225 0.09047-0.27134 0.09047-0.53977 0-0.80849-0.090471-0.26869-0.25408-0.48733-0.48821-0.65225l-7.4326-5.1243c-0.2678-0.18622-0.56169-0.25561-0.8827-0.21481z" fill="#363636" stroke-width="1.9996"/>
</svg>

After

Width:  |  Height:  |  Size: 614 B

@ -0,0 +1,4 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m13.119 3.0087c0.2513 0.031145 0.45989 0.14032 0.62812 0.32931s0.25308 0.41133 0.25308 0.66471v7.9948c0 0.25338-0.08487 0.47572-0.25308 0.66471-0.16823 0.18899-0.37685 0.29815-0.62812 0.32931-0.2513 0.03116-0.47935-0.0244-0.68911-0.1677l-5.7994-3.9973c-0.18484-0.12669-0.31359-0.29945-0.38418-0.5092-0.07063-0.21183-0.07063-0.42139 0-0.63118 0.07063-0.20976 0.19836-0.38045 0.38114-0.5092l5.8025-4.0005c0.20907-0.14538 0.4385-0.19955 0.68911-0.1677z" fill="#363636" stroke-width="1.5611"/>
<rect x="2" y="3" width="2" height="10" rx="1" ry="1" fill="#363636" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4164"/>
</svg>

After

Width:  |  Height:  |  Size: 750 B

@ -0,0 +1,4 @@
<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m2.8812 3.0087c-0.2513 0.031145-0.45989 0.14032-0.62812 0.32931s-0.25308 0.41133-0.25308 0.66471v7.9948c0 0.25338 0.08487 0.47572 0.25308 0.66471 0.16823 0.18899 0.37685 0.29815 0.62812 0.32931 0.2513 0.03116 0.47935-0.0244 0.68911-0.1677l5.7994-3.9973c0.18484-0.12669 0.31359-0.29945 0.38418-0.5092 0.07063-0.21183 0.07063-0.42139 0-0.63118-0.07063-0.20976-0.19836-0.38045-0.38114-0.5092l-5.8025-4.0005c-0.20907-0.14538-0.4385-0.19955-0.68911-0.1677z" fill="#363636" stroke-width="1.5611"/>
<rect transform="scale(-1,1)" x="-14" y="3" width="2" height="10" rx="1" ry="1" fill="#363636" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4164"/>
</svg>

After

Width:  |  Height:  |  Size: 778 B

@ -1,3 +0,0 @@
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: CC-BY-4.0

@ -0,0 +1,30 @@
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/MpvVideo.qml</file>
<file>qml/Footer.qml</file>
<file>qml/HProgressBar.qml</file>
<file>qml/PlayList.qml</file>
<file>qml/PlayListItem.qml</file>
<file>qml/Actions.qml</file>
<file>images/cutefish-videoplayer.svg</file>
<file>images/light/media-playback-pause-symbolic.svg</file>
<file>images/light/media-playback-start-symbolic.svg</file>
<file>images/light/media-skip-backward-symbolic.svg</file>
<file>images/light/media-skip-forward-symbolic.svg</file>
<file>images/dark/media-playback-pause-symbolic.svg</file>
<file>images/dark/media-playback-start-symbolic.svg</file>
<file>images/dark/media-skip-backward-symbolic.svg</file>
<file>images/dark/media-skip-forward-symbolic.svg</file>
<file>images/dark/audio-volume-high-symbolic.svg</file>
<file>images/dark/audio-volume-low-symbolic.svg</file>
<file>images/dark/audio-volume-medium-symbolic.svg</file>
<file>images/dark/audio-volume-muted-symbolic.svg</file>
<file>images/light/audio-volume-high-symbolic.svg</file>
<file>images/light/audio-volume-low-symbolic.svg</file>
<file>images/light/audio-volume-medium-symbolic.svg</file>
<file>images/light/audio-volume-muted-symbolic.svg</file>
<file>images/dark/list.svg</file>
<file>images/light/list.svg</file>
</qresource>
</RCC>

@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
QtObject {
id: root
property var list: ({})
property Action togglePlaylistAction: Action {
id: togglePlaylistAction
Component.onCompleted: list["togglePlaylistAction"] = togglePlaylistAction
onTriggered: {
if (playList.state === "visible") {
playList.state = "hidden"
} else {
playList.state = "visible"
}
}
}
property Action playPreviousAction: Action {
id: playPreviousAction
// property var qaction: app.action("playPrevious")
// text: qaction.text
// shortcut: qaction.shortcutName
// icon.name: qaction.iconName()
Component.onCompleted: list["playPreviousAction"] = playPreviousAction
onTriggered: {
if (mpv.playlistModel.getPlayingVideo() !== 0) {
const previousFileRow = mpv.playlistModel.getPlayingVideo() - 1
const previousFile = mpv.playlistModel.getPath(previousFileRow)
const updateLastPlayedFile = !playList.isYouTubePlaylist
mpv.playlistModel.setPlayingVideo(previousFileRow)
mpv.loadFile(previousFile, updateLastPlayedFile)
}
}
}
property Action muteAction: Action {
id: muteAction
// property var qaction: app.action("mute")
// text: qaction.text
// shortcut: qaction.shortcutName
// icon.name: qaction.iconName()
Component.onCompleted: list["muteAction"] = muteAction
onTriggered: {
mpv.setProperty("mute", !mpv.getProperty("mute"))
if (mpv.getProperty("mute")) {
text = qsTr("Unmute")
icon.name = "player-volume-muted"
} /*else {
text = qaction.text
icon.name = qaction.iconName()
}*/
}
}
property Action playNextAction: Action {
id: playNextAction
// property var qaction: app.action("playNext")
// text: qaction.text
// shortcut: qaction.shortcutName
// icon.name: qaction.iconName()
Component.onCompleted: list["playNextAction"] = playNextAction
onTriggered: {
const nextFileRow = mpv.playlistModel.getPlayingVideo() + 1
const updateLastPlayedFile = !playList.isYouTubePlaylist
if (nextFileRow < playList.playlistView.count) {
const nextFile = mpv.playlistModel.getPath(nextFileRow)
mpv.playlistModel.setPlayingVideo(nextFileRow)
mpv.loadFile(nextFile, updateLastPlayedFile)
} else {
// Last file in playlist
// if (PlaylistSettings.repeat) {
mpv.playlistModel.setPlayingVideo(0)
mpv.loadFile(mpv.playlistModel.getPath(0), updateLastPlayedFile)
// }
}
}
}
property Action playPauseAction: Action {
id: playPauseAction
text: qsTr("Play/Pause")
icon.name: "media-playback-pause"
shortcut: "Space"
Component.onCompleted: list["playPauseAction"] = playPauseAction
onTriggered: {
mpv.setProperty("pause", !mpv.getProperty("pause"))
}
}
}

@ -0,0 +1,215 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import FishUI 1.0 as FishUI
Item {
id: control
property alias progressBar: progressBar
property alias footerRow: footerRow
property alias timeInfo: timeInfo
property alias volume: volume
height: mainLayout.childrenRect.height + FishUI.Units.smallSpacing
enabled: opacity !== 0
visible: mpv.mouseY > rootWindow.height - footer.height && playList.state === "hidden"
ShaderEffectSource {
id: shaderEffect
visible: true
anchors.fill: parent
sourceItem: mpv
sourceRect: Qt.rect(0, 0, control.width, control.height)
}
FastBlur {
visible: true
anchors.fill: shaderEffect
source: shaderEffect
radius: 100
}
Rectangle {
id: _background
anchors.fill: parent
color: FishUI.Theme.backgroundColor
opacity: 0.7
}
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.topMargin: 0
anchors.bottomMargin: FishUI.Units.smallSpacing
spacing: FishUI.Units.smallSpacing
HProgressBar {
id: progressBar
Layout.fillWidth: true
enabled: playList.playlistView.count >= 1
}
RowLayout {
id: footerRow
Item {
width: FishUI.Units.smallSpacing
height: 30
}
Item {
id: leftItem
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout {
anchors.fill: parent
Label {
id: timeInfo
text: app.formatTime(mpv.position) + " / " + app.formatTime(mpv.duration)
font.pointSize: 10
visible: playList.playlistView.count >= 1
Layout.alignment: Qt.AlignVCenter
}
}
}
Item {
id: middleItem
Layout.preferredWidth: middleLayout.childrenRect.width
Layout.fillHeight: true
RowLayout {
id: middleLayout
anchors.fill: parent
FishUI.RoundImageButton {
width: 32
height: 32
iconMargins: FishUI.Units.smallSpacing
source: "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/media-skip-backward-symbolic.svg"
onClicked: actions.playPauseAction.triggered()
visible: playList.playlistView.count > 1
}
FishUI.RoundImageButton {
width: 32
height: 32
iconMargins: FishUI.Units.smallSpacing
source: mpv.pause ? "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/media-playback-start-symbolic.svg"
: "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/media-playback-pause-symbolic.svg"
onClicked: actions.playPauseAction.triggered()
}
FishUI.RoundImageButton {
width: 32
height: 32
iconMargins: FishUI.Units.smallSpacing
source: "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/media-skip-forward-symbolic.svg"
onClicked: actions.playPauseAction.triggered()
visible: playList.playlistView.count > 1
}
// ToolButton {
// id: playPreviousFile
// action: actions.playPreviousAction
// text: ""
// focusPolicy: Qt.NoFocus
// enabled: playList.playlistView.count > 1
// }
// ToolButton {
// id: playPauseButton
// action: actions.playPauseAction
// text: ""
// icon.name: "media-playback-pause"
// focusPolicy: Qt.NoFocus
// }
// ToolButton {
// id: playNextFile
// action: actions.playNextAction
// text: ""
// focusPolicy: Qt.NoFocus
// enabled: playList.playlistView.count > 1
// }
}
}
Item {
id: rightItem
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout {
anchors.fill: parent
spacing: FishUI.Units.smallSpacing
Item {
Layout.fillWidth: true
}
FishUI.RoundImageButton {
id: mute
width: 32
height: 32
iconMargins: FishUI.Units.smallSpacing + 3
source: mpv.getProperty("mute") ? "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/audio-volume-muted-symbolic.svg"
: "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/audio-volume-high-symbolic.svg"
onClicked: {
mpv.setProperty("mute", !mpv.getProperty("mute"))
if (mpv.getProperty("mute")) {
mute.source = "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/audio-volume-muted-symbolic.svg"
} else {
mute.source = "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/audio-volume-high-symbolic.svg"
}
}
}
Slider {
id: volume
from: 0
to: 100
value: mpv.volume
implicitWidth: 100
implicitHeight: 25
wheelEnabled: true
leftPadding: 0
rightPadding: 0
onValueChanged: {
mpv.volume = value.toFixed(0)
// Settings.volume = value.toFixed(0)
}
}
Item {
width: 1
}
FishUI.RoundImageButton {
width: 32
height: 32
iconMargins: FishUI.Units.smallSpacing + 2
source: "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/list.svg"
onClicked: actions.togglePlaylistAction.triggered()
}
Item {
width: FishUI.Units.smallSpacing
}
}
}
}
}
}

@ -12,7 +12,6 @@ import QtQuick.Shapes 1.12
import QtGraphicalEffects 1.0
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
Slider {
id: root
@ -24,7 +23,7 @@ Slider {
from: 0
to: mpv.duration
implicitWidth: 200
implicitHeight: 10
implicitHeight: 7
leftPadding: 0
rightPadding: 0
@ -34,19 +33,6 @@ Slider {
id: progressBarBackground
color: FishUI.Theme.alternateBackgroundColor
// layer.enabled: true
// layer.effect: OpacityMask {
// maskSource: Item {
// width: progressBarBackground.width
// height: progressBarBackground.height
// Rectangle {
// anchors.fill: parent
// radius: FishUI.Theme.smallRadius
// }
// }
// }
Rectangle {
id: loopIndicator
property double startPosition: -1
@ -122,7 +108,8 @@ Slider {
// create markers for the chapters
Repeater {
id: chaptersInstantiator
model: GeneralSettings.showChapterMarkers ? chapters : 0
// model: GeneralSettings.showChapterMarkers ? chapters : 0
model: 0
delegate: Shape {
id: chapterMarkerShape
@ -191,7 +178,7 @@ Slider {
id: skipChaptersMenuItem
text: qsTr("Skip Chapters")
checkable: true
checked: PlaybackSettings.skipChapters
// checked: PlaybackSettings.skipChapters
onCheckedChanged: {
PlaybackSettings.skipChapters = checked
PlaybackSettings.save()

@ -0,0 +1,183 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Window 2.12
import FishUI 1.0 as FishUI
import mpv 1.0
MpvObject {
id: control
property int mouseX: mouseArea.mouseX
property int mouseY: mouseArea.mouseY
onReady: {
if (app.argument(0) !== "") {
rootWindow.openFile(app.argument(0), true)
}
}
onFileLoaded: {
// loadingIndicatorParent.visible = false
// header.audioTracks = getProperty("track-list").filter(track => track["type"] === "audio")
// header.subtitleTracks = getProperty("track-list").filter(track => track["type"] === "sub")
if (playList.playlistView.count <= 1) {
setProperty("loop-file", "inf")
}
setProperty("ab-loop-a", "no")
setProperty("ab-loop-b", "no")
mpv.pause = loadTimePosition() !== 0
position = loadTimePosition()
}
onEndFile: {
if (reason === "error") {
if (playlistModel.rowCount() === 0) {
return
}
// osd.message(i18n("Could not play: %1").arg(title))
}
const nextFileRow = playlistModel.getPlayingVideo() + 1
if (nextFileRow < playList.playlistView.count) {
const nextFile = playlistModel.getPath(nextFileRow)
playlistModel.setPlayingVideo(nextFileRow)
loadFile(nextFile)
} else {
// Last file in playlist
if (PlaylistSettings.repeat) {
playlistModel.setPlayingVideo(0)
loadFile(playlistModel.getPath(0))
}
}
}
onPauseChanged: {
if (pause) {
lockManager.setInhibitionOff()
} else {
lockManager.setInhibitionOn()
}
}
Timer {
id: hideCursorTimer
property double tx: mouseArea.mouseX
property double ty: mouseArea.mouseY
property int timeNotMoved: 0
running: rootWindow.isFullScreen() && mouseArea.containsMouse
repeat: true
interval: 50
onTriggered: {
if (mouseArea.mouseX === tx && mouseArea.mouseY === ty) {
if (timeNotMoved > 2000) {
app.hideCursor()
}
} else {
app.showCursor()
timeNotMoved = 0
}
tx = mouseArea.mouseX
ty = mouseArea.mouseY
timeNotMoved += interval
}
}
Connections {
target: mediaPlayer2Player
function onPlaypause() {
actions.playPauseAction.trigger()
}
function onPlay() {
control.pause = false
}
function onPause() {
control.pause = true
}
function onStop() {
control.position = 0
control.pause = true
}
function onNext() {
actions.playNextAction.trigger()
}
function onPrevious() {
actions.playPreviousAction.trigger()
}
function onSeek() {
control.command(["add", "time-pos", offset])
}
function onOpenUri() {
control.command(["add", "time-pos", offset])
}
}
Component.onCompleted: {
mediaPlayer2Player.mpv = control
}
// Window move
DragHandler {
acceptedDevices: PointerDevice.GenericPointer
grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything
onActiveChanged: if (active) rootWindow.helper.startSystemMove(rootWindow)
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onDoubleClicked: {
if (mouse.button == Qt.LeftButton)
rootWindow.toggleFullScreen()
}
onClicked: {
if (mouse.button == Qt.RightButton)
actions.playPauseAction.triggered()
}
onPressed: {
playList.state = "hidden"
}
}
DropArea {
id: dropArea
anchors.fill: parent
keys: ["text/uri-list"]
property var acceptedSubtitleTypes: ["application/x-subrip", "text/x-ssa"]
onDropped: {
if (acceptedSubtitleTypes.includes(app.mimeType(drop.urls[0]))) {
const subFile = drop.urls[0].replace("file://", "")
command(["sub-add", drop.urls[0], "select"])
}
if (app.mimeType(drop.urls[0]).startsWith("video/") || app.mimeType(drop.urls[0]).startsWith("audio/")) {
rootWindow.openFile(drop.urls[0], true)
}
}
}
}

@ -0,0 +1,155 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
* SPDX-FileCopyrightText: 2021 Reion Wong <aj@cutefishos.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import FishUI 1.0 as FishUI
import mpv 1.0
Item {
id: control
height: mpv.height - FishUI.Units.largeSpacing * 6
width: FishUI.Units.gridUnit * 16
z: 9999
x: parent.width
y: rootWindow.header.height + FishUI.Units.largeSpacing
state: "hidden"
property string position: "right"
property alias scrollPositionTimer: scrollPositionTimer
property alias playlistView: playlistView
Label {
anchors.centerIn: parent
z: 999
text: qsTr("Playlist is empty")
visible: playlistView.count == 0
}
ScrollView {
id: playlistScrollView
z: 20
anchors.fill: parent
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ListView {
id: playlistView
model: mpv.playlistModel
spacing: 1
delegate: PlayListItem {
onDoubleClicked: control.state = "hidden"
}
}
}
Timer {
id: scrollPositionTimer
interval: 50
running: true
repeat: true
onTriggered: {
setPlayListScrollPosition()
scrollPositionTimer.stop()
}
}
ShaderEffectSource {
id: shaderEffect
anchors.fill: playlistScrollView
sourceItem: mpv
sourceRect: position === "right"
? Qt.rect(mpv.width - control.width, mpv.y, control.width, control.height)
: Qt.rect(0, 0, control.width, control.height)
}
FastBlur {
anchors.fill: shaderEffect
radius: 100
source: shaderEffect
}
Rectangle {
anchors.fill: parent
color: FishUI.Theme.backgroundColor
opacity: 0.7
}
states: [
State {
name: "hidden"
PropertyChanges { target: control; x: position === "right" ? parent.width : -width }
PropertyChanges { target: control; visible: false }
},
State {
name : "visible"
PropertyChanges { target: control; x: position === "right" ? parent.width - control.width - FishUI.Units.largeSpacing : 0 }
PropertyChanges { target: control; visible: true }
}
]
transitions: [
Transition {
from: "visible"
to: "hidden"
SequentialAnimation {
NumberAnimation {
target: control
property: "x"
duration: 150
easing.type: Easing.InQuad
}
PropertyAction {
target: control
property: "visible"
value: false
}
}
},
Transition {
from: "hidden"
to: "visible"
SequentialAnimation {
PropertyAction {
target: control
property: "visible"
value: true
}
NumberAnimation {
target: control
property: "x"
duration: 150
easing.type: Easing.OutQuad
}
}
}
]
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {
width: control.width
height: control.height
Rectangle {
anchors.fill: parent
radius: FishUI.Theme.bigRadius
}
}
}
function setPlayListScrollPosition() {
playlistView.positionViewAtIndex(playlistView.model.playingVideo, ListView.Beginning)
}
}

@ -10,17 +10,15 @@ import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
Item {
id: root
property bool isPlaying: model.isPlaying
property string rowNumber: (index + 1).toString()
property var alpha: PlaylistSettings.overlayVideo ? 0.6 : 1
property var alpha: 1
height: label.font.pointSize * 3 + PlaylistSettings.rowHeight
height: label.font.pointSize * 3 + 10
width: ListView.view.width
signal doubleClicked()
@ -31,15 +29,32 @@ Item {
opacity: 0.5
}
Menu {
id: _itemMenu
MenuItem {
text: qsTr("Play")
onTriggered: play()
}
MenuItem {
text: qsTr("Remove from list")
onTriggered: mpv.playlistModel.remove(index)
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.RightButton)
_itemMenu.popup()
}
onDoubleClicked: {
root.doubleClicked()
mpv.playlistModel.setPlayingVideo(index)
mpv.loadFile(path, !isYouTubePlaylist)
mpv.pause = false
play()
}
}
@ -49,8 +64,8 @@ Item {
Label {
text: pad(root.rowNumber, playlistView.count.toString().length)
visible: PlaylistSettings.showRowNumber
font.pointSize: (window.isFullScreen() && playList.bigFont)
visible: true
font.pointSize: (rootWindow.isFullScreen() && playList.bigFont)
? FishUI.Units.gridUnit
: FishUI.Units.gridUnit - 6
horizontalAlignment: Qt.AlignCenter
@ -63,41 +78,37 @@ Item {
}
}
FishUI.IconItem {
source: "media-playback-start"
width: FishUI.Units.iconSizes.small
height: FishUI.Units.iconSizes.small
visible: isPlaying
Layout.leftMargin: PlaylistSettings.showRowNumber ? 0 : FishUI.Units.largeSpacing
}
LabelWithTooltip {
Label {
id: label
toolTipFontSize: label.font.pointSize + 2
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
elide: Text.ElideRight
font.pointSize: (window.isFullScreen() && playList.bigFont)
font.pointSize: (rootWindow.isFullScreen() && playList.bigFont)
? FishUI.Units.gridUnit
: FishUI.Units.gridUnit - 6
font.weight: Font.Normal
color: isPlaying ? FishUI.Theme.highlightColor : FishUI.Theme.textColor
text: PlaylistSettings.showMediaTitle ? model.title : model.name
text: model.name
layer.enabled: true
Layout.fillWidth: true
Layout.leftMargin: PlaylistSettings.showRowNumber || isPlaying ? 0 : FishUI.Units.largeSpacing
// Layout.leftMargin: PlaylistSettings.showRowNumber || isPlaying ? 0 : FishUI.Units.largeSpacing
}
Label {
text: model.duration
visible: model.duration.length > 0
font.pointSize: (window.isFullScreen() && playList.bigFont)
font.pointSize: (rootWindow.isFullScreen() && playList.bigFont)
? FishUI.Units.gridUnit
: FishUI.Units.gridUnit - 6
horizontalAlignment: Qt.AlignCenter
Layout.margins: FishUI.Units.largeSpacing
}
}
function play() {
mpv.playlistModel.setPlayingVideo(index)
mpv.loadFile(path)
mpv.pause = false
}
}

@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2021 Reion Wong <reion@cutefishos.com>
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import Qt.labs.platform 1.0 as Platform
import FishUI 1.0 as FishUI
import mpv 1.0
FishUI.Window {
id: rootWindow
width: 720
height: 480
minimumWidth: 700
minimumHeight: 450
color: FishUI.Theme.backgroundColor
header.visible: mpv.mouseY >= 0 && mpv.mouseY <= header.height
headerBackground.color: FishUI.Theme.secondBackgroundColor
headerBackground.opacity: 0.95
contentTopMargin: 0
property int preFullScreenVisibility
onVisibilityChanged: {
if (!rootWindow.isFullScreen()) {
preFullScreenVisibility = visibility
}
}
headerItem: Item {
Label {
id: headerLabel
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: FishUI.Units.smallSpacing * 1.5
anchors.leftMargin: FishUI.Units.largeSpacing
text: mpv.mediaTitle
color: "white"
z: 100
}
DropShadow {
anchors.fill: headerLabel
source: headerLabel
z: -1
horizontalOffset: 1
verticalOffset: 1
radius: Math.round(6 * FishUI.Units.devicePixelRatio)
samples: radius * 2 + 1
spread: 0.35
color: Qt.rgba(0, 0, 0, 0.5)
opacity: 0.4
visible: true
}
}
Actions {
id: actions
}
MpvVideo {
id: mpv
anchors.fill: parent
Image {
id: _logo
anchors.centerIn: parent
width: 128
height: 128
source: "qrc:/images/cutefish-videoplayer.svg"
sourceSize: Qt.size(width, height)
visible: playList.playlistView.count === 0
}
Button {
visible: _logo.visible
anchors.top: _logo.bottom
anchors.topMargin: FishUI.Units.largeSpacing
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Open")
onClicked: fileDialog.open()
}
}
Platform.FileDialog {
id: fileDialog
property url location: ""
folder: location
title: "Select file"
fileMode: Platform.FileDialog.OpenFile
onAccepted: {
openFile(fileDialog.file.toString(), true)
// the timer scrolls the playlist to the playing file
// once the table view rows are loaded
playList.scrollPositionTimer.start()
mpv.focus = true
}
onRejected: mpv.focus = true
}
PlayList {
id: playList
}
Footer {
id: footer
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: mpv.bottom
}
function openFile(path, startPlayback) {
mpv.playlistModel.addVideo(path)
// mpv.playlistModel.clear()
mpv.pause = !startPlayback
// mpv.playlistModel.getVideos(path)
mpv.loadFile(path)
}
function isFullScreen() {
return rootWindow.visibility === Window.FullScreen
}
function toggleFullScreen() {
if (!isFullScreen()) {
rootWindow.showFullScreen()
} else {
if (rootWindow.preFullScreenVisibility === Window.Windowed) {
rootWindow.showNormal()
}
if (rootWindow.preFullScreenVisibility === Window.Maximized) {
rootWindow.show()
rootWindow.showMaximized()
}
}
app.showCursor()
playList.scrollPositionTimer.start()
}
}

@ -1,68 +0,0 @@
#
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
qt5_add_dbus_interface(screensaver_inhibit_SRCS ../org.freedesktop.ScreenSaver.xml screensaverdbusinterface)
add_executable(cutefish-videoplayer)
target_sources(cutefish-videoplayer
PRIVATE
application.cpp
global.cpp
haction.cpp
lockmanager.cpp
main.cpp
models/customcommandsmodel.cpp
models/subtitlesfoldersmodel.cpp
models/tracksmodel.cpp
models/playlistmodel.cpp
mpris2/mediaplayer2.cpp
mpris2/mediaplayer2player.cpp
mpvobject.cpp
qthelper.h
playlistitem.cpp
thumbnailimageprovider.cpp
track.cpp
worker.cpp
qml.qrc
${screensaver_inhibit_SRCS}
)
kconfig_add_kcfg_files(cutefish-videoplayer GENERATE_MOC settings/audiosettings.kcfgc)
kconfig_add_kcfg_files(cutefish-videoplayer GENERATE_MOC settings/generalsettings.kcfgc)
kconfig_add_kcfg_files(cutefish-videoplayer GENERATE_MOC settings/mousesettings.kcfgc)
kconfig_add_kcfg_files(cutefish-videoplayer GENERATE_MOC settings/playbacksettings.kcfgc)
kconfig_add_kcfg_files(cutefish-videoplayer GENERATE_MOC settings/playlistsettings.kcfgc)
kconfig_add_kcfg_files(cutefish-videoplayer GENERATE_MOC settings/subtitlessettings.kcfgc)
kconfig_add_kcfg_files(cutefish-videoplayer GENERATE_MOC settings/videosettings.kcfgc)
target_include_directories(cutefish-videoplayer PRIVATE models mpris2)
target_compile_definitions(cutefish-videoplayer PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(cutefish-videoplayer PRIVATE
Qt5::Core
Qt5::DBus
Qt5::Qml
Qt5::Quick
Qt5::QuickControls2
KF5::ConfigCore
KF5::ConfigGui
KF5::CoreAddons
KF5::DocTools
KF5::FileMetaData
KF5::KIOCore
KF5::KIOFileWidgets
KF5::KIOWidgets
KF5::KIONTLM
# KF5::Kirigami2
KF5::XmlGui
Libmpv::Libmpv
)
install(TARGETS cutefish-videoplayer DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

@ -1,15 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __DEBUG_H
#define __DEBUG_H
#include <QDebug>
#define DEBUG qDebug() << Q_FUNC_INFO << ':'
#endif

@ -1,40 +1,26 @@
/*
* SPDX-FileCopyrightText: 2021 Reion Wong <reion@cutefishos.com>
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
* SPDX-FileCopyrightText: 2021 Reion Wong <support@cutefishos.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "_debug.h"
#include "application.h"
#include "audiosettings.h"
#include "customcommandsmodel.h"
#include "generalsettings.h"
#include "global.h"
#include "haction.h"
#include "lockmanager.h"
#include "mediaplayer2.h"
#include "mediaplayer2player.h"
#include "mousesettings.h"
#include "mpvobject.h"
#include "playbacksettings.h"
#include "playlistitem.h"
#include "playlistmodel.h"
#include "playlistsettings.h"
#include "subtitlesfoldersmodel.h"
#include "subtitlessettings.h"
#include "thumbnailimageprovider.h"
#include "playlistitem.h"
#include "tracksmodel.h"
#include "videosettings.h"
#include "worker.h"
#include <clocale>
#include "track.h"
#include "lockmanager.h"
#include "mpris2/mediaplayer2.h"
#include "mpris2/mediaplayer2player.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QDesktopServices>
#include <QDir>
#include <QTime>
#include <QDBusConnection>
#include <QFileInfo>
#include <QGuiApplication>
@ -44,69 +30,46 @@
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickItem>
#include <QQuickStyle>
#include <QQuickView>
#include <QStandardPaths>
#include <QStyle>
#include <QTime>
#include <QStyleFactory>
#include <QThread>
#include <KAboutApplicationDialog>
#include <KAboutData>
#include <KColorSchemeManager>
#include <KConfig>
#include <KConfigGroup>
#include <KFileMetaData/Properties>
#include <KShortcutsDialog>
#include <KTreeWidgetSearchLine>
#include <memory>
static QApplication *createApplication(int &argc, char **argv, const QString &applicationName)
{
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setOrganizationName("georgefb");
QApplication::setOrganizationName("cutefishos");
QApplication::setApplicationName(applicationName);
QApplication::setOrganizationDomain("georgefb.com");
QApplication::setOrganizationDomain("cutefishos.com");
QApplication::setApplicationDisplayName("Video Player");
QApplication::setApplicationVersion(Application::version());
QApplication::setWindowIcon(QIcon::fromTheme("org.kde.haruna"));
QApplication::setWindowIcon(QIcon::fromTheme("cutefish-videoplayer"));
QApplication *app = new QApplication(argc, argv);
return app;
}
Application::Application(int &argc, char **argv, const QString &applicationName)
: m_app(createApplication(argc, argv, applicationName))
, m_collection(this)
{
m_config = KSharedConfig::openConfig(Global::instance()->appConfigFilePath());
m_shortcuts = new KConfigGroup(m_config, "Shortcuts");
m_schemes = new KColorSchemeManager(this);
m_systemDefaultStyle = m_app->style()->objectName();
setupUserActions();
// register mpris dbus service
QString mspris2Name(QStringLiteral("org.mpris.MediaPlayer2.haruna"));
QString mspris2Name(QStringLiteral("org.mpris.MediaPlayer2.cutefish.videoplayer"));
QDBusConnection::sessionBus().registerService(mspris2Name);
QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/mpris/MediaPlayer2"), this, QDBusConnection::ExportAdaptors);
// org.mpris.MediaPlayer2 mpris2 interface
new MediaPlayer2(this);
if (GeneralSettings::guiStyle() != QStringLiteral("System")) {
QApplication::setStyle(GeneralSettings::guiStyle());
}
// Qt sets the locale in the QGuiApplication constructor, but libmpv
// requires the LC_NUMERIC category to be set to "C", so change it back.
std::setlocale(LC_NUMERIC, "C");
setupWorkerThread();
setupAboutData();
setupCommandLineParser();
m_engine = new QQmlApplicationEngine(this);
registerQmlTypes();
setupQmlSettingsTypes();
setupQmlContextProperties();
m_engine = new QQmlApplicationEngine(this);
const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
auto onObjectCreated = [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl) {
@ -116,14 +79,8 @@ Application::Application(int &argc, char **argv, const QString &applicationName)
QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated,
m_app, onObjectCreated, Qt::QueuedConnection);
m_engine->addImportPath("qrc:/qml");
m_engine->addImageProvider("thumbnail", new ThumbnailImageProvider());
setupQmlContextProperties();
m_engine->load(url);
}
Application::~Application()
{
delete m_engine;
m_engine->load(url);
}
int Application::run()
@ -131,171 +88,11 @@ int Application::run()
return m_app->exec();
}
void Application::setupWorkerThread()
{
auto worker = Worker::instance();
auto thread = new QThread();
worker->moveToThread(thread);
QObject::connect(thread, &QThread::finished,
worker, &Worker::deleteLater);
QObject::connect(thread, &QThread::finished,
thread, &QThread::deleteLater);
thread->start();
}
void Application::setupAboutData()
{
m_aboutData = KAboutData(QStringLiteral("haruna"),
tr("Video Player"),
Application::version());
m_aboutData.setShortDescription(tr("A configurable video player."));
m_aboutData.setLicense(KAboutLicense::GPL_V3);
m_aboutData.setCopyrightStatement(tr("(c) 2019-2021"));
m_aboutData.setHomepage(QStringLiteral("https://invent.kde.org/multimedia/haruna"));
m_aboutData.setBugAddress(QStringLiteral("https://bugs.kde.org").toUtf8());
m_aboutData.setDesktopFileName("org.kde.haruna");
m_aboutData.addAuthor(tr("George Florea Bănuș"),
tr("Developer"),
QStringLiteral("georgefb899@gmail.com"),
QStringLiteral("https://georgefb.com"));
KAboutData::setApplicationData(m_aboutData);
}
void Application::setupCommandLineParser()
{
QCommandLineParser parser;
m_aboutData.setupCommandLine(&parser);
parser.addPositionalArgument(QStringLiteral("file"), tr("File to open"));
parser.process(*m_app);
m_aboutData.processCommandLine(&parser);
for (auto i = 0; i < parser.positionalArguments().size(); ++i) {
addArgument(i, parser.positionalArguments().at(i));
}
}
void Application::registerQmlTypes()
{
qmlRegisterType<MpvObject>("mpv", 1, 0, "MpvObject");
qRegisterMetaType<PlayListModel*>();
qRegisterMetaType<PlayListItem*>();
qRegisterMetaType<QAction*>();
qRegisterMetaType<TracksModel*>();
qRegisterMetaType<KFileMetaData::PropertyMap>("KFileMetaData::PropertyMap");
}
void Application::setupQmlSettingsTypes()
{
auto audioProvider = [](QQmlEngine *, QJSEngine *) -> QObject * { return AudioSettings::self(); };
qmlRegisterSingletonType<AudioSettings>("org.kde.haruna", 1, 0, "AudioSettings", audioProvider);
auto generalProvider = [](QQmlEngine *, QJSEngine *) -> QObject * { return GeneralSettings::self(); };
qmlRegisterSingletonType<GeneralSettings>("org.kde.haruna", 1, 0, "GeneralSettings", generalProvider);
auto mouseProvider = [](QQmlEngine *, QJSEngine *) -> QObject * { return MouseSettings::self(); };
qmlRegisterSingletonType<MouseSettings>("org.kde.haruna", 1, 0, "MouseSettings", mouseProvider);
auto playbackProvider = [](QQmlEngine *, QJSEngine *) -> QObject * { return PlaybackSettings::self(); };
qmlRegisterSingletonType<PlaybackSettings>("org.kde.haruna", 1, 0, "PlaybackSettings", playbackProvider);
auto playlistProvider = [](QQmlEngine *, QJSEngine *) -> QObject * { return PlaylistSettings::self(); };
qmlRegisterSingletonType<PlaylistSettings>("org.kde.haruna", 1, 0, "PlaylistSettings", playlistProvider);
auto subtitlesProvider = [](QQmlEngine *, QJSEngine *) -> QObject * { return SubtitlesSettings::self(); };
qmlRegisterSingletonType<SubtitlesSettings>("org.kde.haruna", 1, 0, "SubtitlesSettings", subtitlesProvider);
auto videoProvider = [](QQmlEngine *, QJSEngine *) -> QObject * { return VideoSettings::self(); };
qmlRegisterSingletonType<VideoSettings>("org.kde.haruna", 1, 0, "VideoSettings", videoProvider);
}
void Application::setupQmlContextProperties()
{
std::unique_ptr<LockManager> lockManager = std::make_unique<LockManager>();
std::unique_ptr<SubtitlesFoldersModel> subsFoldersModel = std::make_unique<SubtitlesFoldersModel>();
m_engine->rootContext()->setContextProperty(QStringLiteral("app"), this);
qmlRegisterUncreatableType<Application>("Application", 1, 0, "Application",
QStringLiteral("Application should not be created in QML"));
m_engine->rootContext()->setContextProperty(QStringLiteral("mediaPlayer2Player"), new MediaPlayer2Player(this));
m_engine->rootContext()->setContextProperty(QStringLiteral("lockManager"), lockManager.release());
qmlRegisterUncreatableType<LockManager>("LockManager", 1, 0, "LockManager",
QStringLiteral("LockManager should not be created in QML"));
m_engine->rootContext()->setContextProperty(QStringLiteral("subsFoldersModel"), subsFoldersModel.release());
auto customCommandsModel = new CustomCommandsModel();
m_engine->rootContext()->setContextProperty(QStringLiteral("customCommandsModel"), customCommandsModel);
auto proxyCustomCommandsModel = new ProxyCustomCommandsModel();
proxyCustomCommandsModel->setSourceModel(customCommandsModel);
m_engine->rootContext()->setContextProperty(QStringLiteral("proxyCustomCommandsModel"), proxyCustomCommandsModel);
}
QUrl Application::configFilePath()
{
auto configPath = QStandardPaths::writableLocation(m_config->locationType());
auto configFilePath = configPath.append(QStringLiteral("/")).append(m_config->name());
QUrl url(configFilePath);
url.setScheme("file");
return url;
}
QUrl Application::configFolderPath()
{
auto configPath = QStandardPaths::writableLocation(m_config->locationType());
auto configFilePath = configPath.append(QStringLiteral("/")).append(m_config->name());
QFileInfo fileInfo(configFilePath);
QUrl url(fileInfo.absolutePath());
url.setScheme("file");
return url;
}
QString Application::version()
{
return QStringLiteral("0.6.3");
}
bool Application::hasYoutubeDl()
{
return !QStandardPaths::findExecutable(QStringLiteral("youtube-dl")).isEmpty();
}
QUrl Application::parentUrl(const QString &path)
{
QUrl url(path);
if (!url.isValid()) {
return QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
}
QFileInfo fileInfo;
if (url.isLocalFile()) {
fileInfo.setFile(url.toLocalFile());
} else {
fileInfo.setFile(url.toString());
}
QUrl parentFolderUrl(fileInfo.absolutePath());
parentFolderUrl.setScheme("file");
return parentFolderUrl;
}
QUrl Application::pathToUrl(const QString &path)
{
QUrl url(path);
if (!url.isValid()) {
return QUrl();
}
url.setScheme("file");
return url;
}
bool Application::isYoutubePlaylist(const QString &path)
QString Application::mimeType(const QString &file)
{
return path.contains("youtube.com/playlist?list");
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(file);
return mime.name();
}
QString Application::formatTime(const double time)
@ -305,16 +102,6 @@ QString Application::formatTime(const double time)
return formattedTime;
}
void Application::hideCursor()
{
QApplication::setOverrideCursor(Qt::BlankCursor);
}
void Application::showCursor()
{
QApplication::setOverrideCursor(Qt::ArrowCursor);
}
QString Application::argument(int key)
{
return m_args[key];
@ -325,569 +112,48 @@ void Application::addArgument(int key, const QString &value)
m_args.insert(key, value);
}
QAction *Application::action(const QString &name)
{
auto resultAction = m_collection.action(name);
if (!resultAction) {
setupActions(name);
resultAction = m_collection.action(name);
}
return resultAction;
}
QString Application::getFileContent(const QString &file)
{
QFile f(file);
f.open(QIODevice::ReadOnly);
QString content = f.readAll();
f.close();
return content;
}
QString Application::mimeType(const QString &file)
{
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(file);
return mime.name();
}
QStringList Application::availableGuiStyles()
{
return QStyleFactory::keys();
}
void Application::setGuiStyle(const QString &style)
{
if (style == "Default") {
QApplication::setStyle(m_systemDefaultStyle);
return;
}
QApplication::setStyle(style);
}
QAbstractItemModel *Application::colorSchemesModel()
void Application::hideCursor()
{
return m_schemes->model();
QApplication::setOverrideCursor(Qt::BlankCursor);
}
void Application::activateColorScheme(const QString &name)
void Application::showCursor()
{
m_schemes->activateScheme(m_schemes->indexForScheme(name));
QApplication::setOverrideCursor(Qt::ArrowCursor);
}
void Application::configureShortcuts(const QString &name)
void Application::setupCommandLineParser()
{
KShortcutsDialog dlg(KShortcutsEditor::ApplicationAction, KShortcutsEditor::LetterShortcutsAllowed, nullptr);
connect(&dlg, &KShortcutsDialog::accepted, this, [ = ](){
m_collection.writeSettings(m_shortcuts);
m_config->sync();
});
if (!name.isEmpty()) {
auto searchLine = dlg.findChild<KTreeWidgetSearchLine *>();
if (searchLine != nullptr) {
searchLine->setText(name);
}
}
dlg.setModal(true);
dlg.addCollection(&m_collection);
dlg.configure(false);
}
QCommandLineParser parser;
parser.addPositionalArgument(QStringLiteral("file"), tr("File to open"));
parser.process(*m_app);
void Application::aboutApplication()
{
static QPointer<QDialog> dialog;
if (!dialog) {
dialog = new KAboutApplicationDialog(KAboutData::applicationData(), nullptr);
dialog->setAttribute(Qt::WA_DeleteOnClose);
for (auto i = 0; i < parser.positionalArguments().size(); ++i) {
addArgument(i, parser.positionalArguments().at(i));
}
dialog->show();
}
void Application::setupActions(const QString &actionName)
void Application::setupQmlContextProperties()
{
if (actionName == QStringLiteral("screenshot")) {
auto action = new HAction();
action->setText(tr("Screenshot"));
action->setIcon(QIcon::fromTheme("image-x-generic"));
m_collection.setDefaultShortcut(action, Qt::Key_S);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("file_quit")) {
auto action = new HAction();
action->setText(tr("Quit"));
action->setIcon(QIcon::fromTheme("application-exit"));
connect(action, &QAction::triggered, m_app, &QApplication::quit);
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_Q);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("options_configure_keybinding")) {
auto action = new HAction();
action->setText(tr("Configure Keyboard Shortcuts"));
action->setIcon(QIcon::fromTheme("configure-shortcuts"));
connect(action, &QAction::triggered, this, [=]() { configureShortcuts(); });
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_S);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("configure")) {
auto action = new HAction();
action->setText(tr("Configure"));
action->setIcon(QIcon::fromTheme("configure"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_Comma);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("togglePlaylist")) {
auto action = new HAction();
action->setText(tr("Playlist"));
action->setIcon(QIcon::fromTheme("view-media-playlist"));
m_collection.setDefaultShortcut(action, Qt::Key_P);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("openContextMenu")) {
auto action = new HAction();
action->setText(tr("Open Context Menu"));
action->setIcon(QIcon::fromTheme("application-menu"));
m_collection.setDefaultShortcut(action, Qt::Key_Menu);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("toggleFullscreen")) {
auto action = new HAction();
action->setText(tr("Toggle Fullscreen"));
action->setIcon(QIcon::fromTheme("view-fullscreen"));
m_collection.setDefaultShortcut(action, Qt::Key_F);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("openFile")) {
auto action = new HAction();
action->setText(tr("Open File"));
action->setIcon(QIcon::fromTheme("folder-videos"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_O);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("openUrl")) {
auto action = new HAction();
action->setText(tr("Open Url"));
action->setIcon(QIcon::fromTheme("internet-services"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_O);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("aboutHaruna")) {
auto action = new HAction();
action->setText(tr("About Haruna"));
action->setIcon(QIcon::fromTheme("help-about"));
m_collection.setDefaultShortcut(action, Qt::Key_F1);
m_collection.addAction(actionName, action);
connect(action, &QAction::triggered, this, &Application::aboutApplication);
}
// mpv actions
if (actionName == QStringLiteral("contrastUp")) {
auto action = new HAction();
action->setText(tr("Contrast Up"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_1);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("contrastDown")) {
auto action = new HAction();
action->setText(tr("Contrast Down"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_2);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("contrastReset")) {
auto action = new HAction();
action->setText(tr("Contrast Reset"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_1);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("brightnessUp")) {
auto action = new HAction();
action->setText(tr("Brightness Up"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_3);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("brightnessDown")) {
auto action = new HAction();
action->setText(tr("Brightness Down"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_4);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("brightnessReset")) {
auto action = new HAction();
action->setText(tr("Brightness Reset"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_3);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("gammaUp")) {
auto action = new HAction();
action->setText(tr("Gamma Up"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_5);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("gammaDown")) {
auto action = new HAction();
action->setText(tr("Gamma Down"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_6);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("gammaReset")) {
auto action = new HAction();
action->setText(tr("Gamma Reset"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_5);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("saturationUp")) {
auto action = new HAction();
action->setText(tr("Saturation Up"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_7);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("saturationDown")) {
auto action = new HAction();
action->setText(tr("Saturation Down"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::Key_8);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("saturationReset")) {
auto action = new HAction();
action->setText(tr("Saturation Reset"));
action->setIcon(QIcon::fromTheme("contrast"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_7);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("playNext")) {
auto action = new HAction();
action->setText(tr("Play Next"));
action->setIcon(QIcon::fromTheme("media-skip-forward"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_Period);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("playPrevious")) {
auto action = new HAction();
action->setText(tr("Play Previous"));
action->setIcon(QIcon::fromTheme("media-skip-backward"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_Comma);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("volumeUp")) {
auto action = new HAction();
action->setText(tr("Volume Up"));
action->setIcon(QIcon::fromTheme("audio-volume-high"));
m_collection.setDefaultShortcut(action, Qt::Key_9);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("volumeDown")) {
auto action = new HAction();
action->setText(tr("Volume Down"));
action->setIcon(QIcon::fromTheme("audio-volume-low"));
m_collection.setDefaultShortcut(action, Qt::Key_0);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("mute")) {
auto action = new HAction();
action->setText(tr("Mute"));
action->setIcon(QIcon::fromTheme("player-volume"));
m_collection.setDefaultShortcut(action, Qt::Key_M);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekForwardSmall")) {
auto action = new HAction();
action->setText(tr("Seek Small Step Forward"));
action->setIcon(QIcon::fromTheme("media-seek-forward"));
m_collection.setDefaultShortcut(action, Qt::Key_Right);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekBackwardSmall")) {
auto action = new HAction();
action->setText(tr("Seek Small Step Backward"));
action->setIcon(QIcon::fromTheme("media-seek-backward"));
m_collection.setDefaultShortcut(action, Qt::Key_Left);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekForwardMedium")) {
auto action = new HAction();
action->setText(tr("Seek Medium Step Forward"));
action->setIcon(QIcon::fromTheme("media-seek-forward"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_Right);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekBackwardMedium")) {
auto action = new HAction();
action->setText(tr("Seek Medium Step Backward"));
action->setIcon(QIcon::fromTheme("media-seek-backward"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_Left);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekForwardBig")) {
auto action = new HAction();
action->setText(tr("Seek Big Step Forward"));
action->setIcon(QIcon::fromTheme("media-seek-forward"));
m_collection.setDefaultShortcut(action, Qt::Key_Up);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekBackwardBig")) {
auto action = new HAction();
action->setText(tr("Seek Big Step Backward"));
action->setIcon(QIcon::fromTheme("media-seek-backward"));
m_collection.setDefaultShortcut(action, Qt::Key_Down);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekPreviousChapter")) {
auto action = new HAction();
action->setText(tr("Seek Previous Chapter"));
action->setIcon(QIcon::fromTheme("media-seek-backward"));
m_collection.setDefaultShortcut(action, Qt::Key_PageDown);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekNextChapter")) {
auto action = new HAction();
action->setText(tr("Seek Next Chapter"));
action->setIcon(QIcon::fromTheme("media-seek-forward"));
m_collection.setDefaultShortcut(action, Qt::Key_PageUp);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekNextSubtitle")) {
auto action = new HAction();
action->setText(tr("Seek To Next Subtitle"));
action->setIcon(QIcon::fromTheme("media-seek-forward"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_Right);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("seekPreviousSubtitle")) {
auto action = new HAction();
action->setText(tr("Seek To Previous Subtitle"));
action->setIcon(QIcon::fromTheme("media-seek-backward"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_Left);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("frameStep")) {
auto action = new HAction();
action->setText(tr("Move one frame forward, then pause"));
m_collection.setDefaultShortcut(action, Qt::Key_Period);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("frameBackStep")) {
auto action = new HAction();
action->setText(tr("Move one frame backward, then pause"));
m_collection.setDefaultShortcut(action, Qt::Key_Comma);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("increasePlayBackSpeed")) {
auto action = new HAction();
action->setText(tr("Playback speed increase"));
m_collection.setDefaultShortcut(action, Qt::Key_BracketRight);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("decreasePlayBackSpeed")) {
auto action = new HAction();
action->setText(tr("Playback speed decrease"));
m_collection.setDefaultShortcut(action, Qt::Key_BracketLeft);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("resetPlayBackSpeed")) {
auto action = new HAction();
action->setText(tr("Playback speed reset"));
m_collection.setDefaultShortcut(action, Qt::Key_Backspace);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("subtitleQuicken")) {
auto action = new HAction();
action->setText(tr("Subtitle Quicken"));
m_collection.setDefaultShortcut(action, Qt::Key_Z);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("subtitleDelay")) {
auto action = new HAction();
action->setText(tr("Subtitle Delay"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_Z);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("subtitleToggle")) {
auto action = new HAction();
action->setText(tr("Subtitle Toggle"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_S);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("audioCycleUp")) {
auto action = new HAction();
action->setText(tr("Cycle Audio Up"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_3);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("audioCycleDown")) {
auto action = new HAction();
action->setText(tr("Cycle Audio Down"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_2);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("subtitleCycleUp")) {
auto action = new HAction();
action->setText(tr("Cycle Subtitle Up"));
m_collection.setDefaultShortcut(action, Qt::Key_J);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("subtitleCycleDown")) {
auto action = new HAction();
action->setText(tr("Cycle Subtitle Down"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_J);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("zoomIn")) {
auto action = new HAction();
action->setText(tr("Zoom In"));
action->setIcon(QIcon::fromTheme("zoom-in"));
m_collection.setDefaultShortcut(action, Qt::ALT + Qt::Key_Plus);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("zoomOut")) {
auto action = new HAction();
action->setText(tr("Zoom Out"));
action->setIcon(QIcon::fromTheme("zoom-out"));
m_collection.setDefaultShortcut(action, Qt::ALT + Qt::Key_Minus);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("zoomReset")) {
auto action = new HAction();
action->setText(tr("Zoom Reset"));
action->setIcon(QIcon::fromTheme("zoom-original"));
m_collection.setDefaultShortcut(action, Qt::ALT + Qt::Key_Backspace);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("videoPanXLeft")) {
auto action = new HAction();
action->setText(tr("Video pan x left"));
m_collection.setDefaultShortcut(action, Qt::ALT + Qt::Key_Left);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("videoPanXRight")) {
auto action = new HAction();
action->setText(tr("Video pan x right"));
m_collection.setDefaultShortcut(action, Qt::ALT + Qt::Key_Right);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("videoPanYUp")) {
auto action = new HAction();
action->setText(tr("Video pan y up"));
m_collection.setDefaultShortcut(action, Qt::ALT + Qt::Key_Up);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("videoPanYDown")) {
auto action = new HAction();
action->setText(tr("Video pan y down"));
m_collection.setDefaultShortcut(action, Qt::ALT + Qt::Key_Down);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("toggleMenuBar")) {
auto action = new HAction();
action->setText(tr("Toggle Menu Bar"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_M);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("toggleHeader")) {
auto action = new HAction();
action->setText(tr("Toggle Header"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_H);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("setLoop")) {
auto action = new HAction();
action->setText(tr("Set Loop"));
m_collection.setDefaultShortcut(action, Qt::Key_L);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("increaseSubtitleFontSize")) {
auto action = new HAction();
action->setText(tr("Increase Subtitle Font Size"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_Z);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("decreaseSubtitleFontSize")) {
auto action = new HAction();
action->setText(tr("Decrease Subtitle Font Size"));
m_collection.setDefaultShortcut(action, Qt::CTRL + Qt::Key_X);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("subtitlePositionUp")) {
auto action = new HAction();
action->setText(tr("Move Subtitle Up"));
m_collection.setDefaultShortcut(action, Qt::Key_R);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("subtitlePositionDown")) {
auto action = new HAction();
action->setText(tr("Move Subtitle Down"));
m_collection.setDefaultShortcut(action, Qt::SHIFT + Qt::Key_R);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("toggleDeinterlacing")) {
auto action = new HAction();
action->setText(tr("Toggle deinterlacing"));
m_collection.setDefaultShortcut(action, Qt::Key_D);
m_collection.addAction(actionName, action);
}
if (actionName == QStringLiteral("exitFullscreen")) {
auto action = new HAction();
action->setText(tr("Exit Fullscreen"));
m_collection.setDefaultShortcut(action, Qt::Key_Escape);
m_collection.addAction(actionName, action);
}
m_collection.readSettings(m_shortcuts);
}
// C++ 14
std::unique_ptr<LockManager> lockManager = std::make_unique<LockManager>();
void Application::createUserAction(const QString &text)
{
KSharedConfig::Ptr m_customCommandsConfig;
QString ccConfig = Global::instance()->appConfigFilePath(Global::ConfigFile::CustomCommands);
m_customCommandsConfig = KSharedConfig::openConfig(ccConfig, KConfig::SimpleConfig);
int counter = m_customCommandsConfig->group(QString()).readEntry("Counter", 0);
const QString &name = QString("Command_%1").arg(counter);
m_engine->rootContext()->setContextProperty(QStringLiteral("app"), this);
qmlRegisterUncreatableType<Application>("Application", 1, 0, "Application",
QStringLiteral("Application should not be created in QML"));
auto action = new HAction();
action->setText(text);
m_collection.addAction(name, action);
m_collection.readSettings(m_shortcuts);
}
m_engine->rootContext()->setContextProperty(QStringLiteral("lockManager"), lockManager.release());
qmlRegisterUncreatableType<LockManager>("LockManager", 1, 0, "LockManager",
QStringLiteral("LockManager should not be created in QML"));
void Application::openDocs(const QString &page)
{
QDesktopServices::openUrl(QUrl(page));
m_engine->rootContext()->setContextProperty(QStringLiteral("mediaPlayer2Player"), new MediaPlayer2Player(this));
}
void Application::setupUserActions()
void Application::registerQmlTypes()
{
KSharedConfig::Ptr m_customCommandsConfig;
QString ccConfig = Global::instance()->appConfigFilePath(Global::ConfigFile::CustomCommands);
m_customCommandsConfig = KSharedConfig::openConfig(ccConfig, KConfig::SimpleConfig);
QStringList groups = m_customCommandsConfig->groupList();
for (const QString &_group : qAsConst((groups))) {
auto configGroup = m_customCommandsConfig->group(_group);
QString command = configGroup.readEntry("Command", QString());
if (configGroup.readEntry("Type", QString()) == "shortcut") {
auto action = new HAction();
action->setText(command);
m_collection.addAction(_group, action);
}
}
m_collection.readSettings(m_shortcuts);
qmlRegisterType<MpvObject>("mpv", 1, 0, "MpvObject");
qRegisterMetaType<PlayListModel *>();
qRegisterMetaType<PlayListItem *>();
qRegisterMetaType<QAction *>();
qRegisterMetaType<TracksModel *>();
}

@ -1,4 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Reion Wong <reion@cutefishos.com>
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
@ -8,75 +9,37 @@
#define APPLICATION_H
#include <QAbstractItemModel>
#include <QAction>
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QApplication>
#include <QObject>
#include <KAboutData>
#include <KActionCollection>
#include <KSharedConfig>
class KActionCollection;
class KConfigDialog;
class KColorSchemeManager;
class QAction;
#include <QAction>
class Application : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractItemModel* colorSchemesModel READ colorSchemesModel CONSTANT)
Q_PROPERTY(QUrl configFilePath READ configFilePath CONSTANT)
Q_PROPERTY(QUrl configFolderPath READ configFolderPath CONSTANT)
public:
explicit Application(int &argc, char **argv, const QString &applicationName);
~Application();
Application(int &argc, char **argv, const QString &applicationName);
int run();
QUrl configFilePath();
QUrl configFolderPath();
Q_INVOKABLE QUrl parentUrl(const QString &path);
Q_INVOKABLE QUrl pathToUrl(const QString &path);
Q_INVOKABLE QString mimeType(const QString &file);
Q_INVOKABLE static QString formatTime(const double time);
Q_INVOKABLE QString argument(int key);
Q_INVOKABLE void addArgument(int key, const QString &value);
Q_INVOKABLE QAction *action(const QString &name);
Q_INVOKABLE QString getFileContent(const QString &file);
Q_INVOKABLE QString mimeType(const QString &file);
Q_INVOKABLE QStringList availableGuiStyles();
Q_INVOKABLE void setGuiStyle(const QString &style);
Q_INVOKABLE void activateColorScheme(const QString &name);
Q_INVOKABLE void configureShortcuts(const QString &name = QString());
Q_INVOKABLE void createUserAction(const QString &text);
Q_INVOKABLE void openDocs(const QString &page);
static QString version();
Q_INVOKABLE static bool hasYoutubeDl();
Q_INVOKABLE static bool isYoutubePlaylist(const QString &path);
Q_INVOKABLE static QString formatTime(const double time);
Q_INVOKABLE static void hideCursor();
Q_INVOKABLE static void showCursor();
private:
void setupWorkerThread();
void setupAboutData();
void setupCommandLineParser();
void registerQmlTypes();
void setupQmlSettingsTypes();
void setupQmlContextProperties();
void aboutApplication();
void setupActions(const QString &actionName);
void setupUserActions();
QAbstractItemModel *colorSchemesModel();
void registerQmlTypes();
private:
QApplication *m_app;
QQmlApplicationEngine *m_engine;
KAboutData m_aboutData;
KActionCollection m_collection;
KSharedConfig::Ptr m_config;
KConfigGroup *m_shortcuts;
QMap<int, QString> m_args;
KColorSchemeManager *m_schemes;
QString m_systemDefaultStyle;
};
#endif // APPLICATION_H

@ -1,43 +0,0 @@
#include "global.h"
#include "_debug.h"
#include <QFileInfo>
Global* Global::sm_instance = nullptr;
Global *Global::instance()
{
if (!sm_instance) {
sm_instance = new Global();
}
return sm_instance;
}
Global::Global()
{
m_config = KSharedConfig::openConfig("haruna/haruna.conf");
m_ccConfig = KSharedConfig::openConfig("haruna/haruna-custom-commands.conf");
}
const QString Global::systemConfigPath()
{
return QStandardPaths::writableLocation(m_config->locationType()).append("/");
}
const QString Global::appConfigDirPath()
{
QFileInfo configFile(QString(systemConfigPath()).append(m_config->name()));
return configFile.absolutePath();
}
const QString Global::appConfigFilePath(ConfigFile configFile)
{
switch (configFile) {
case ConfigFile::Main:
return QString(systemConfigPath()).append(m_config->name());
case ConfigFile::CustomCommands:
return QString(systemConfigPath()).append(m_ccConfig->name());
}
}

@ -1,29 +0,0 @@
#ifndef GLOBALS_H
#define GLOBALS_H
#include <QStandardPaths>
#include <KSharedConfig>
class Global
{
public:
Global();
enum ConfigFile {
Main,
CustomCommands
};
const QString systemConfigPath();
const QString appConfigDirPath();
const QString appConfigFilePath(ConfigFile configFile = ConfigFile::Main);
static Global *instance();
private:
KSharedConfig::Ptr m_config;
KSharedConfig::Ptr m_ccConfig;
static Global *sm_instance;
};
#endif // GLOBALS_H

@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "haction.h"
#include <QKeySequence>
HAction::HAction(QObject *parent) : QAction(parent)
{
}
QString HAction::shortcutName()
{
return shortcut().toString();
}
QString HAction::iconName()
{
return icon().name();
}

@ -1,29 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef HACTION_H
#define HACTION_H
#include <QAction>
/*
* Needed because QAction fails to trigger for non english languages
*/
class HAction : public QAction
{
Q_OBJECT
Q_PROPERTY(QString shortcutName READ shortcutName NOTIFY changed)
public:
explicit HAction(QObject *parent = nullptr);
public slots:
QString shortcutName();
QString iconName();
};
#endif // HACTION_H

@ -1,15 +1,12 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "application.h"
int main(int argc, char *argv[])
{
Application app(argc, argv, "Haruna");
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
Application app(argc, argv, "cutefish-videoplayer");
return app.run();
}

@ -1,157 +0,0 @@
#include "customcommandsmodel.h"
#include "_debug.h"
#include <KConfigGroup>
#include <global.h>
CustomCommandsModel::CustomCommandsModel(QObject *parent)
: QAbstractListModel(parent)
{
QString ccConfig = Global::instance()->appConfigFilePath(Global::ConfigFile::CustomCommands);
m_customCommandsConfig = KSharedConfig::openConfig(ccConfig, KConfig::SimpleConfig);
QStringList groups = m_customCommandsConfig->groupList();
beginInsertRows(QModelIndex(), 0, groups.size());
for (const QString &groupName : qAsConst((groups))) {
auto configGroup = m_customCommandsConfig->group(groupName);
auto c = new Command();
c->commandId = groupName;
c->command = configGroup.readEntry("Command", QString());
c->osdMessage = configGroup.readEntry("OsdMessage", QString());
c->type = configGroup.readEntry("Type", QString());
c->order = configGroup.readEntry("Order", 0);
m_customCommands << c;
}
std::sort(m_customCommands.begin(), m_customCommands.end(), [=](Command *c1, Command *c2) {
return c1->order < c2->order;
});
endInsertRows();
connect(this, &QAbstractListModel::rowsMoved, this, [=]() {
for (int i = 0; i < m_customCommands.size(); ++i) {
auto configGroup = m_customCommandsConfig->group(m_customCommands[i]->commandId);
configGroup.writeEntry("Order", i);
configGroup.sync();
}
});
}
int CustomCommandsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_customCommands.size();
}
QVariant CustomCommandsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
Command *command = m_customCommands[index.row()];
switch (role) {
case CommandIdRole:
return QVariant(command->commandId);
case CommandRole:
return QVariant(command->command);
case OsdMessageRole:
return QVariant(command->osdMessage);
case TypeRole:
return QVariant(command->type);
}
return QVariant();
}
QHash<int, QByteArray> CustomCommandsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[CommandIdRole] = "commandId";
roles[OsdMessageRole] = "osdMessage";
roles[CommandRole] = "command";
roles[TypeRole] = "type";
return roles;
}
void CustomCommandsModel::moveRows(int oldIndex, int newIndex)
{
if (oldIndex < newIndex) {
beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex + 1);
} else {
beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
}
Command *c = m_customCommands.takeAt(oldIndex);
m_customCommands.insert(newIndex, c);
endMoveRows();
}
void CustomCommandsModel::saveCustomCommand(const QString &command, const QString &osdMessage, const QString &type)
{
int counter = m_customCommandsConfig->group(QString()).readEntry("Counter", 0);
const QString &groupName = QString("Command_%1").arg(counter);
if (m_customCommandsConfig->group(groupName).exists()) {
return;
}
m_customCommandsConfig->group(groupName).writeEntry(QStringLiteral("Command"), command);
m_customCommandsConfig->group(groupName).writeEntry(QStringLiteral("OsdMessage"), osdMessage);
m_customCommandsConfig->group(groupName).writeEntry(QStringLiteral("Type"), type);
m_customCommandsConfig->group(groupName).writeEntry(QStringLiteral("Order"), rowCount());
m_customCommandsConfig->group(QString()).writeEntry(QStringLiteral("Counter"), counter + 1);
m_customCommandsConfig->sync();
beginInsertRows(QModelIndex(), rowCount(), rowCount());
auto configGroup = m_customCommandsConfig->group(groupName);
auto p = new Command();
p->commandId = groupName;
p->command = configGroup.readEntry("Command", QString());
p->osdMessage = configGroup.readEntry("OsdMessage", QString()),
p->type = configGroup.readEntry("Type", QString());
m_customCommands << p;
endInsertRows();
}
void CustomCommandsModel::editCustomCommand(int row, const QString &command,
const QString &osdMessage, const QString &type)
{
auto customCommand = m_customCommands[row];
customCommand->command = command;
customCommand->osdMessage = osdMessage;
customCommand->type = type;
QString groupName = customCommand->commandId;
m_customCommandsConfig->group(groupName).writeEntry(QStringLiteral("Command"), command);
m_customCommandsConfig->group(groupName).writeEntry(QStringLiteral("OsdMessage"), osdMessage);
m_customCommandsConfig->group(groupName).writeEntry(QStringLiteral("Type"), type);
m_customCommandsConfig->sync();
emit dataChanged(index(row, 0), index(row, 0));
}
void CustomCommandsModel::deleteCustomCommand(const QString &groupName, int row)
{
beginRemoveRows(QModelIndex(), row, row);
m_customCommandsConfig->deleteGroup(groupName);
m_customCommandsConfig->sync();
m_customCommands.removeAt(row);
endRemoveRows();
KSharedConfig::Ptr config = KSharedConfig::openConfig(Global::instance()->appConfigFilePath());
config->group("Shortcuts").deleteEntry(groupName);
config->sync();
}
ProxyCustomCommandsModel::ProxyCustomCommandsModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
setDynamicSortFilter(true);
setFilterRole(CustomCommandsModel::TypeRole);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setFilterFixedString(QStringLiteral("shortcut"));
}

@ -1,59 +0,0 @@
#ifndef CUSTOMCOMMANDSMODEL_H
#define CUSTOMCOMMANDSMODEL_H
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <KSharedConfig>
class ProxyCustomCommandsModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit ProxyCustomCommandsModel(QObject *parent = nullptr);
};
class CustomCommandsModel : public QAbstractListModel
{
Q_OBJECT
struct Command {
QString commandId;
QString command;
QString osdMessage;
QString type;
int order;
};
public:
explicit CustomCommandsModel(QObject *parent = nullptr);
enum Roles {
CommandIdRole = Qt::UserRole + 1,
CommandRole,
OsdMessageRole,
TypeRole,
};
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const;
Q_INVOKABLE void moveRows(int oldIndex, int newIndex);
Q_INVOKABLE void saveCustomCommand(const QString &command,
const QString &osdMessage,
const QString &type);
Q_INVOKABLE void editCustomCommand(int row,
const QString &command,
const QString &osdMessage,
const QString &type);
Q_INVOKABLE void deleteCustomCommand(const QString &groupName, int row);
private:
KSharedConfig::Ptr m_customCommandsConfig;
QList<Command *> m_customCommands;
};
#endif // CUSTOMCOMMANDSMODEL_H

@ -1,75 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "_debug.h"
#include "subtitlesfoldersmodel.h"
#include "subtitlessettings.h"
#include <KConfig>
#include <KConfigDialog>
#include <KConfigGroup>
#include <global.h>
SubtitlesFoldersModel::SubtitlesFoldersModel(QObject *parent)
: QAbstractListModel(parent)
{
m_config = KSharedConfig::openConfig(Global::instance()->appConfigFilePath());
m_list = m_config->group("Subtitles").readPathEntry("Folders", QStringList());
}
int SubtitlesFoldersModel::rowCount(const QModelIndex &parent) const
{
// For list models only the root node (an invalid parent) should return the list's size. For all
// other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
if (parent.isValid())
return 0;
return m_list.size();
}
QVariant SubtitlesFoldersModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
QString path = m_list[index.row()];
switch (role) {
case Qt::DisplayRole:
return QVariant(path);
}
return QVariant();
}
void SubtitlesFoldersModel::updateFolder(const QString &folder, int row)
{
m_list.replace(row, folder);
QStringList newList = m_list;
// remove empty strings
// removing directly from m_list messes with the ui logic
newList.removeAll(QString(""));
SubtitlesSettings::self()->setSubtitlesFolders(newList);
SubtitlesSettings::self()->save();
}
void SubtitlesFoldersModel::deleteFolder(int row)
{
beginRemoveRows(QModelIndex(), row, row);
m_list.removeAt(row);
endRemoveRows();
m_config->group("Subtitles").writePathEntry("Folders", m_list);
m_config->sync();
}
void SubtitlesFoldersModel::addFolder()
{
beginInsertRows(QModelIndex(), m_list.size(), m_list.size());
m_list.append(QStringLiteral());
endInsertRows();
}

@ -1,35 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef SUBTITLESFOLDERSMODEL_H
#define SUBTITLESFOLDERSMODEL_H
#include <QAbstractListModel>
#include <KSharedConfig>
class SubtitlesFoldersModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit SubtitlesFoldersModel(QObject *parent = nullptr);
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
void updateFolder(const QString &folder, int row);
void deleteFolder(int row);
void addFolder();
private:
QStringList m_list;
KSharedConfig::Ptr m_config;
};
#endif // SUBTITLESFOLDERSMODEL_H

@ -5,9 +5,7 @@
*/
#include "mediaplayer2.h"
#include "_debug.h"
#include <KAboutData>
#include <QApplication>
MediaPlayer2::MediaPlayer2(QObject *obj)
@ -42,12 +40,12 @@ bool MediaPlayer2::HasTrackList() const
QString MediaPlayer2::Identity() const
{
return QStringLiteral("Haruna");
return QStringLiteral("CutefishPlayer");
}
QString MediaPlayer2::DesktopEntry() const
{
return KAboutData::applicationData().desktopFileName();
return "";
}
QStringList MediaPlayer2::SupportedUriSchemes() const

@ -5,8 +5,7 @@
*/
#include "mediaplayer2player.h"
#include "_debug.h"
#include "mpvobject.h"
#include "../mpvobject.h"
#include <QDBusConnection>
#include <QDBusMessage>
@ -125,7 +124,7 @@ QVariantMap MediaPlayer2Player::Metadata()
metadata.insert(QStringLiteral("mpris:length"),
m_mpv->getProperty("duration").toDouble() * 1000 * 1000);
metadata.insert(QStringLiteral("mpris:trackid"),
QVariant::fromValue<QDBusObjectPath>(QDBusObjectPath("/org/kde/haruna")));
QVariant::fromValue<QDBusObjectPath>(QDBusObjectPath("/com/cutefish/videoplayer")));
metadata.insert(QStringLiteral("xesam:title"),
m_mpv->getProperty("filename").toString());
QUrl url(m_mpv->getProperty("path").toString());

@ -4,17 +4,13 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "_debug.h"
#include "mpvobject.h"
#include "application.h"
#include "generalsettings.h"
#include "playbacksettings.h"
#include "videosettings.h"
#include "playlistitem.h"
#include "track.h"
#include "tracksmodel.h"
#include "global.h"
#include <QSettings>
#include <QCryptographicHash>
#include <QDir>
#include <QJsonArray>
@ -28,8 +24,6 @@
#include <QStandardPaths>
#include <QtGlobal>
#include <KShell>
void on_mpv_redraw(void *ctx)
{
QMetaObject::invokeMethod(static_cast<MpvObject*>(ctx), "update", Qt::QueuedConnection);
@ -110,9 +104,9 @@ MpvObject::MpvObject(QQuickItem * parent)
// setProperty("terminal", "yes");
// setProperty("msg-level", "all=v");
QString hwdec = PlaybackSettings::useHWDecoding() ? PlaybackSettings::hWDecoding() : "no";
setProperty("hwdec", hwdec);
setProperty("screenshot-template", VideoSettings::screenshotTemplate());
// QString hwdec = PlaybackSettings::useHWDecoding() ? PlaybackSettings::hWDecoding() : "no";
setProperty("hwdec", "auto");
// setProperty("screenshot-template", VideoSettings::screenshotTemplate());
setProperty("sub-auto", "exact");
setProperty("volume-max", "100");
@ -137,17 +131,17 @@ MpvObject::MpvObject(QQuickItem * parent)
// run user commands
KSharedConfig::Ptr m_customPropsConfig;
QString ccConfig = Global::instance()->appConfigFilePath(Global::ConfigFile::CustomCommands);
m_customPropsConfig = KSharedConfig::openConfig(ccConfig, KConfig::SimpleConfig);
QStringList groups = m_customPropsConfig->groupList();
for (const QString &_group : qAsConst((groups))) {
auto configGroup = m_customPropsConfig->group(_group);
QString type = configGroup.readEntry("Type", QString());
if (type == "startup") {
userCommand(configGroup.readEntry("Command", QString()));
}
}
// KSharedConfig::Ptr m_customPropsConfig;
// QString ccConfig = Global::instance()->appConfigFilePath(Global::ConfigFile::CustomCommands);
// m_customPropsConfig = KSharedConfig::openConfig(ccConfig, KConfig::SimpleConfig);
// QStringList groups = m_customPropsConfig->groupList();
// for (const QString &_group : qAsConst((groups))) {
// auto configGroup = m_customPropsConfig->group(_group);
// QString type = configGroup.readEntry("Type", QString());
// if (type == "startup") {
// userCommand(configGroup.readEntry("Command", QString()));
// }
// }
mpv_set_wakeup_callback(mpv, MpvObject::mpvEvents, this);
@ -391,18 +385,19 @@ QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
return new MpvRenderer(const_cast<MpvObject *>(this));
}
void MpvObject::loadFile(const QString &file, bool updateLastPlayedFile)
void MpvObject::loadFile(const QString &file)
{
setProperty("ytdl-format", PlaybackSettings::ytdlFormat());
// setProperty("ytdl-format", PlaybackSettings::ytdlFormat());
command(QStringList() << "loadfile" << file);
if (updateLastPlayedFile) {
GeneralSettings::setLastPlayedFile(file);
GeneralSettings::self()->save();
} else {
GeneralSettings::setLastPlaylistIndex(m_playlistModel->getPlayingVideo());
GeneralSettings::self()->save();
}
// if (updateLastPlayedFile) {
// GeneralSettings::setLastPlayedFile(file);
// GeneralSettings::self()->save();
// } else {
// GeneralSettings::setLastPlaylistIndex(m_playlistModel->getPlayingVideo());
// GeneralSettings::self()->save();
// }
}
void MpvObject::mpvEvents(void *ctx)
@ -605,7 +600,7 @@ void MpvObject::getYouTubePlaylist(const QString &path)
video->setMediaTitle(!title.isEmpty() ? title : url);
video->setFileName(!title.isEmpty() ? title : url);
video->setDuration(Application::formatTime(duration));
// video->setDuration(Application::formatTime(duration));
m_playList.append(video);
playlistFileContent += QString("%1,%2,%3\n").arg(url, title, QString::number(duration));
@ -622,7 +617,7 @@ int MpvObject::setProperty(const QString &name, const QVariant &value, bool debu
{
auto result = mpv::qt::set_property(mpv, name, value);
if (debug) {
DEBUG << mpv::qt::get_error(result);
qDebug() << mpv::qt::get_error(result);
}
return result;
}
@ -631,7 +626,7 @@ QVariant MpvObject::getProperty(const QString &name, bool debug)
{
auto result = mpv::qt::get_property(mpv, name);
if (debug) {
DEBUG << mpv::qt::get_error(result);
qDebug() << mpv::qt::get_error(result);
}
return result;
}
@ -644,58 +639,59 @@ QVariant MpvObject::command(const QVariant &params)
void MpvObject::saveTimePosition()
{
// saving position is disabled
if (PlaybackSettings::minDurationToSavePosition() == -1) {
return;
}
// if (PlaybackSettings::minDurationToSavePosition() == -1) {
// return;
// }
// position is saved only for files longer than PlaybackSettings::minDurationToSavePosition()
if (getProperty("duration").toInt() < PlaybackSettings::minDurationToSavePosition() * 60) {
if (getProperty("duration").toInt() < 60) {
return;
}
auto hash = md5(getProperty("path").toString());
auto timePosition = getProperty("time-pos");
auto configPath = Global::instance()->appConfigDirPath();
KConfig *config = new KConfig(configPath.append("/watch-later/").append(hash));
config->group("").writeEntry("TimePosition", timePosition);
config->sync();
// auto configPath = Global::instance()->appConfigDirPath();
// KConfig *config = new KConfig(configPath.append("/watch-later/").append(hash));
// config->group("").writeEntry("TimePosition", timePosition);
// config->sync();
}
double MpvObject::loadTimePosition()
{
// saving position is disabled
if (PlaybackSettings::minDurationToSavePosition() == -1) {
return 0;
}
// position is saved only for files longer than PlaybackSettings::minDurationToSavePosition()
// but there can be cases when there is a saved position for files lower than minDurationToSavePosition()
// when minDurationToSavePosition() was increased after position was already saved
if (getProperty("duration").toInt() < PlaybackSettings::minDurationToSavePosition() * 60) {
if (getProperty("duration").toInt() < 60) {
return 0;
}
auto hash = md5(getProperty("path").toString());
auto configPath = Global::instance()->appConfigDirPath();
KConfig *config = new KConfig(configPath.append("/watch-later/").append(hash));
int position = config->group("").readEntry("TimePosition", QString::number(0)).toDouble();
// auto hash = md5(getProperty("path").toString());
// QSettings settings("cutefishos", "cutefish-videoplayer");
// auto configPath = QFileInfo(settings.fileName()).absoluteDir();
// qDebug() << configPath;
// KConfig *config = new KConfig(configPath.append("/watch-later/").append(hash));
// QSettings set(configPath.append("/watch-later/"));
// int position = config->group("").readEntry("TimePosition", QString::number(0)).toDouble();
return position;
return 0;
}
void MpvObject::resetTimePosition()
{
auto hash = md5(getProperty("path").toString());
auto configPath = Global::instance()->appConfigDirPath();
QFile f(configPath.append("/watch-later/").append(hash));
// auto hash = md5(getProperty("path").toString());
// auto configPath = Global::instance()->appConfigDirPath();
// QFile f(configPath.append("/watch-later/").append(hash));
if (f.exists()) {
f.remove();
}
// if (f.exists()) {
// f.remove();
// }
}
void MpvObject::userCommand(const QString &commandString)
{
QStringList args = KShell::splitArgs(commandString);
command(args);
// QStringList args = KShell::splitArgs(commandString);
// command(args);
}
QString MpvObject::md5(const QString &str)

@ -1,4 +1,4 @@
/*
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
@ -11,6 +11,7 @@
#include <mpv/client.h>
#include <mpv/render_gl.h>
#include "qthelper.h"
#include "playlistmodel.h"
#include "tracksmodel.h"
@ -120,7 +121,7 @@ class MpvObject : public QQuickFramebufferObject
bool pause();
void setPause(bool value);
int volume();
void setVolume(int value);
@ -164,7 +165,7 @@ public:
virtual ~MpvObject();
virtual Renderer *createRenderer() const;
Q_INVOKABLE void loadFile(const QString &file, bool updateLastPlayedFile = true);
Q_INVOKABLE void loadFile(const QString &file);
Q_INVOKABLE void getYouTubePlaylist(const QString &path);
Q_INVOKABLE QVariant command(const QVariant &params);
Q_INVOKABLE QVariant getProperty(const QString &name, bool debug = false);

@ -4,7 +4,6 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "_debug.h"
#include "playlistitem.h"
#include <QFileInfo>

@ -1,39 +1,53 @@
/*
* SPDX-FileCopyrightText: 2021 Reion Wong <reion@cutefishos.com>
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "playlistmodel.h"
#include "playlistitem.h"
#include "_debug.h"
#include "application.h"
#include "worker.h"
#include <QStandardPaths>
#include <QCollator>
#include <QDirIterator>
#include <QFileInfo>
#include <QMimeDatabase>
#include <QUrl>
#include <global.h>
PlayListModel::PlayListModel(QObject *parent)
: QAbstractListModel(parent)
, m_settings("cutefishos", "cutefish-videoplayer")
{
m_config = KSharedConfig::openConfig(Global::instance()->appConfigFilePath());
connect(this, &PlayListModel::videoAdded,
Worker::instance(), &Worker::getMetaData);
// Reion: init list.
QStringList list = m_settings.value("List").toString().split(',');
int idx = 0;
connect(Worker::instance(), &Worker::metaDataReady, this, [ = ](int i, KFileMetaData::PropertyMap metaData) {
auto duration = metaData[KFileMetaData::Property::Duration].toInt();
auto title = metaData[KFileMetaData::Property::Title].toString();
for (const QString &path : list) {
if (!QFile(path).exists())
continue;
m_playList[i]->setDuration(Application::formatTime(duration));
m_playList[i]->setMediaTitle(title);
auto video = new PlayListItem(path, idx , this);
m_playList.append(video);
++idx;
}
if (!m_playList.isEmpty())
setPlayingVideo(0);
// m_config = KSharedConfig::openConfig(Global::instance()->appConfigFilePath());
// connect(this, &PlayListModel::videoAdded,
// Worker::instance(), &Worker::getMetaData);
emit dataChanged(index(i, 0), index(i, 0));
// connect(Worker::instance(), &Worker::metaDataReady, this, [ = ](int i, KFileMetaData::PropertyMap metaData) {
// auto duration = metaData[KFileMetaData::Property::Duration].toInt();
// auto title = metaData[KFileMetaData::Property::Title].toString();
});
// m_playList[i]->setDuration(Application::formatTime(duration));
// m_playList[i]->setMediaTitle(title);
// emit dataChanged(index(i, 0), index(i, 0));
// });
}
int PlayListModel::rowCount(const QModelIndex &parent) const
@ -123,16 +137,6 @@ Playlist PlayListModel::items() const
return m_playList;
}
QString PlayListModel::configFolder()
{
auto configPath = QStandardPaths::writableLocation(m_config->locationType());
auto configFilePath = configPath.append(QStringLiteral("/")).append(m_config->name());
QFileInfo fileInfo(configFilePath);
return fileInfo.absolutePath();
}
Playlist PlayListModel::getPlayList() const
{
return m_playList;
@ -159,6 +163,56 @@ void PlayListModel::clear()
endResetModel();
}
void PlayListModel::save()
{
QString value;
for (int i = 0; i < m_playList.size(); ++i) {
const QString path = m_playList.at(i)->filePath();
if (value.isEmpty())
value.append(path);
else
value.append(',' + path);
}
m_settings.setValue("List", value);
}
void PlayListModel::addVideo(const QString &path)
{
QString newPath = QUrl(path).toLocalFile().isEmpty() ? path : QUrl(path).toLocalFile();
// Skip
for (int i = 0; i < m_playList.size(); ++i) {
if (m_playList.at(i)->filePath() == newPath) {
return;
}
}
beginInsertRows(QModelIndex(), rowCount(), rowCount());
auto video = new PlayListItem(newPath, m_playList.size() - 1, this);
m_playList.append(video);
setPlayingVideo(m_playList.size() - 1);
endInsertRows();
save();
}
void PlayListModel::remove(int i)
{
if (i < 0 || i > m_playList.size())
return;
setPlayingVideo(i);
beginRemoveRows(QModelIndex(), i, i);
PlayListItem *item = m_playList.at(i);
m_playList.removeAt(i);
item->deleteLater();
endRemoveRows();
save();
}
QString PlayListModel::getPath(int i)
{
// when restoring a youtube playlist

@ -8,13 +8,13 @@
#define PLAYLISTMODEL_H
#include <QAbstractTableModel>
#include <KSharedConfig>
#include <map>
#include <memory>
#include <QSettings>
class PlayListItem;
#include "playlistitem.h"
using Playlist = QList<PlayListItem*>;
using Playlist = QList<PlayListItem *>;
class PlayListModel : public QAbstractListModel
{
@ -47,6 +47,10 @@ public:
Q_INVOKABLE int getPlayingVideo() const;
Q_INVOKABLE void getVideos(QString path);
Q_INVOKABLE void clear();
Q_INVOKABLE void save();
Q_INVOKABLE void addVideo(const QString &path);
Q_INVOKABLE void remove(int i);
Playlist getPlayList() const;
void setPlayList(const Playlist &playList);
@ -57,10 +61,9 @@ signals:
private:
Playlist items() const;
QString configFolder();
Playlist m_playList;
int m_playingVideo = 0;
KSharedConfig::Ptr m_config;
QSettings m_settings;
};
#endif // PLAYLISTMODEL_H

@ -1,40 +0,0 @@
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/PlayListItem.qml</file>
<file>qml/PlayList.qml</file>
<file>qml/MpvVideo.qml</file>
<file>qml/HProgressBar.qml</file>
<file>qml/Header.qml</file>
<file>qml/Footer.qml</file>
<file>qml/Osd.qml</file>
<file>qml/Actions.qml</file>
<file>qml/VolumeSlider.qml</file>
<file>qml/Settings/SettingsEditor.qml</file>
<file>qml/Haruna/Components/SubtitlesFolders.qml</file>
<file alias="General.qml">qml/Settings/General.qml</file>
<file alias="Mouse.qml">qml/Settings/Mouse.qml</file>
<file alias="Navigation.qml">qml/Settings/Navigation.qml</file>
<file alias="Playlist.qml">qml/Settings/Playlist.qml</file>
<file alias="Audio.qml">qml/Settings/Audio.qml</file>
<file alias="Subtitles.qml">qml/Settings/Subtitles.qml</file>
<file alias="Playback.qml">qml/Settings/Playback.qml</file>
<file>qml/Menus/FileMenu.qml</file>
<file>qml/Menus/PlaybackMenu.qml</file>
<file>qml/Menus/SubtitlesMenu.qml</file>
<file>qml/Menus/SettingsMenu.qml</file>
<file>qml/Menus/AudioMenu.qml</file>
<file>qml/Menus/ViewMenu.qml</file>
<file>qml/Menus/TrackMenuItems.qml</file>
<file>qml/Haruna/Components/SelectActionPopup.qml</file>
<file alias="VideoSettings.qml">qml/Settings/VideoSettings.qml</file>
<file>qml/Menus/HelpMenu.qml</file>
<file>qml/Haruna/Components/LabelWithTooltip.qml</file>
<file>qml/Haruna/Components/SettingsHeader.qml</file>
<file>qml/Haruna/Components/SettingsBasePage.qml</file>
<file>qml/Haruna/Components/qmldir</file>
<file>qml/Haruna/Components/ImageAdjustmentSlider.qml</file>
<file alias="CustomCommands.qml">qml/Settings/CustomCommands.qml</file>
<file alias="EditCustomCommand.qml">qml/Settings/EditCustomCommand.qml</file>
</qresource>
</RCC>

@ -1,3 +0,0 @@
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: CC-BY-4.0

File diff suppressed because it is too large Load Diff

@ -1,209 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
* SPDX-FileCopyrightText: 2021 Reion Wong <aj@cutefishos.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
Item {
id: root
property alias progressBar: progressBar
property alias footerRow: footerRow
property alias timeInfo: timeInfo
property alias playPauseButton: playPauseButton
property alias volume: volume
height: progressBar.height + 50 + FishUI.Units.smallSpacing
// height: mainLayout.childrenRect.height + FishUI.Units.smallSpacing
opacity: mpv.mouseY > window.height - footer.height - FishUI.Units.largeSpacing * 3
&& playList.state === "hidden" ? 1 : 0
enabled: opacity !== 0
visible: true
Behavior on opacity {
NumberAnimation {
duration: 180
}
}
ShaderEffectSource {
id: shaderEffect
visible: true
anchors.fill: parent
sourceItem: mpv
sourceRect: Qt.rect(0, 0, root.width, root.height)
}
FastBlur {
visible: true
anchors.fill: shaderEffect
source: shaderEffect
radius: 100
}
Rectangle {
id: _background
anchors.fill: parent
color: FishUI.Theme.backgroundColor
opacity: 0.8
}
// layer.enabled: true
// layer.effect: OpacityMask {
// maskSource: Item {
// width: root.width
// height: root.height
// Rectangle {
// anchors.fill: parent
// radius: FishUI.Theme.bigRadius
// }
// }
// }
Component {
id: togglePlaylistButton
ToolButton {
action: actions.togglePlaylistAction
}
}
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.topMargin: 0
anchors.bottomMargin: FishUI.Units.smallSpacing
spacing: FishUI.Units.smallSpacing
HProgressBar {
id: progressBar
Layout.fillWidth: true
}
RowLayout {
id: footerRow
Item {
width: FishUI.Units.smallSpacing
}
Item {
id: leftItem
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout {
anchors.fill: parent
Label {
id: timeInfo
text: app.formatTime(mpv.position) + " / " + app.formatTime(mpv.duration)
font.pointSize: FishUI.Units.gridUnit - 6
Layout.alignment: Qt.AlignVCenter
}
}
}
Item {
id: middleItem
Layout.preferredWidth: middleLayout.childrenRect.width
Layout.fillHeight: true
RowLayout {
id: middleLayout
anchors.fill: parent
ToolButton {
id: playPreviousFile
action: actions.playPreviousAction
text: ""
focusPolicy: Qt.NoFocus
enabled: playList.playlistView.count > 1
ToolTip {
text: qsTr("Play previous file")
}
}
ToolButton {
id: playPauseButton
action: actions.playPauseAction
text: ""
icon.name: "media-playback-start"
focusPolicy: Qt.NoFocus
ToolTip {
id: playPauseButtonToolTip
text: mpv.pause ? qsTr("Start Playback") : qsTr("Pause Playback")
}
}
ToolButton {
id: playNextFile
action: actions.playNextAction
text: ""
focusPolicy: Qt.NoFocus
enabled: playList.playlistView.count > 1
ToolTip {
text: qsTr("Play next file")
}
}
}
}
Item {
id: rightItem
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout {
anchors.fill: parent
spacing: FishUI.Units.smallSpacing
Item {
Layout.fillWidth: true
}
ToolButton {
id: mute
action: actions.muteAction
text: ""
focusPolicy: Qt.NoFocus
ToolTip {
text: actions.muteAction.text
}
}
VolumeSlider { id: volume }
Item {
width: 1
}
Loader {
sourceComponent: togglePlaylistButton
visible: true
}
Item {
width: FishUI.Units.smallSpacing
}
}
}
}
}
}

@ -1,41 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
RowLayout {
id: root
property int value: 0
signal sliderValueChanged(int value)
Slider {
id: slider
from: -100
to: 100
value: root.value
wheelEnabled: true
stepSize: 1
onValueChanged: root.sliderValueChanged(value.toFixed(0))
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.MiddleButton
onClicked: slider.value = 0
}
Component.onCompleted: background.activeControl = ""
}
Label {
text: slider.value
horizontalAlignment: Qt.AlignHCenter
Layout.preferredWidth: 40
}
}

@ -1,33 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.12
QQC2.Label {
id: root
property string toolTipText
property int toolTipFontSize
property bool alwaysShowToolTip
QQC2.ToolTip {
id: toolTip
visible: (root.alwaysShowToolTip && mouseArea.containsMouse) || (mouseArea.containsMouse && root.truncated)
text: root.toolTipText ? root.toolTipText : root.text
font.pointSize: root.toolTipFontSize ? root.toolTipFontSize : root.font.pointSize
}
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: true
}
}

@ -1,115 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.11 as Kirigami
Popup {
id: root
property string headerTitle
property int buttonIndex: -1
signal actionSelected(string actionName)
implicitHeight: parent.height * 0.9
implicitWidth: parent.width * 0.9
modal: true
anchors.centerIn: parent
focus: true
onOpened: {
filterActionsField.text = ""
filterActionsField.focus = true
}
onActionSelected: close()
Action {
shortcut: "ctrl+f"
onTriggered: filterActionsField.forceActiveFocus(Qt.ShortcutFocusReason)
}
ColumnLayout {
anchors.fill: parent
Kirigami.Heading {
text: root.headerTitle
}
Label {
text: i18n("Double click to set action")
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
}
TextField {
id: filterActionsField
placeholderText: i18n("Type to filter...")
focus: true
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
KeyNavigation.up: actionsListView
KeyNavigation.down: clearActionButton
onTextChanged: {
const menuModel = actionsListView.actionsList
actionsListView.model = menuModel.filter(action => action.toLowerCase().includes(text))
}
}
Button {
id: clearActionButton
Layout.fillWidth: true
text: i18n("Clear current action")
KeyNavigation.up: filterActionsField
KeyNavigation.down: actionsListView
onClicked: actionSelected("")
Keys.onEnterPressed: actionSelected("")
Keys.onReturnPressed: actionSelected("")
}
ListView {
id: actionsListView
property var actionsList: Object.keys(window.appActions).sort()
implicitHeight: 30 * model.count
model: actionsList
spacing: 1
clip: true
currentIndex: focus ? 0 : -1
delegate: Kirigami.BasicListItem {
height: 30
width: root.width
label: modelData
reserveSpaceForIcon: false
onDoubleClicked: actionSelected(modelData)
Keys.onEnterPressed: actionSelected(modelData)
Keys.onReturnPressed: actionSelected(modelData)
}
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignTop
KeyNavigation.up: clearActionButton
KeyNavigation.down: filterActionsField
Keys.onPressed: {
if (event.key === Qt.Key_End) {
actionsListView.currentIndex = actionsListView.count - 1
actionsListView.positionViewAtIndex(actionsListView.currentIndex,ListView.Center)
}
if (event.key === Qt.Key_Home) {
actionsListView.currentIndex = 0
actionsListView.positionViewAtIndex(actionsListView.currentIndex,ListView.Center)
}
}
}
}
}

@ -1,53 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQml 2.12
import org.kde.kirigami 2.11 as Kirigami
import org.kde.haruna 1.0
Kirigami.ScrollablePage {
id: root
property bool hasHelp: false
property string helpFile: ""
property string docPage: ""
actions {
contextualActions: [
Kirigami.Action {
text: qsTr("Help!")
iconName: "system-help"
Kirigami.Action {
text: qsTr("Haruna help window (english only)")
enabled: root.hasHelp
onTriggered: root.hasHelp ? helpWindow.show() : undefined
}
Kirigami.Action {
text: qsTr("KHelpCenter")
enabled: root.docPage !== ""
onTriggered: enabled ? app.openDocs(root.docPage) : undefined
}
},
Kirigami.Action {
text: qsTr("Open config ...")
iconName: "folder"
Kirigami.Action {
text: qsTr("File")
onTriggered: Qt.openUrlExternally(app.configFilePath)
}
Kirigami.Action {
text: qsTr("Folder")
onTriggered: Qt.openUrlExternally(app.configFolderPath)
}
}
]
}
}

@ -1,44 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import org.kde.kirigami 2.11 as Kirigami
ColumnLayout {
id: root
property string text: ""
property int topMargin: Kirigami.Units.gridUnit
spacing: 0
Item {
width: 1
height: root.topMargin
visible: root.topMargin > 0
}
RowLayout {
Rectangle {
width: Kirigami.Units.gridUnit
height: 1
color: Kirigami.Theme.alternateBackgroundColor
}
Kirigami.Heading {
text: root.text
}
Rectangle {
height: 1
color: Kirigami.Theme.alternateBackgroundColor
Layout.fillWidth: true
}
}
}

@ -1,165 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import org.kde.kirigami 2.11 as Kirigami
// Subtitles Folders
Item {
id: root
// prevent creating multiple empty items
// until the new one has been saved
property bool canAddFolder: true
Layout.fillWidth: true
implicitHeight: sectionTitle.height + sfListView.implicitHeight + sfAddFolder.height + 25
Label {
id: sectionTitle
text: i18n("Load subtitles from")
bottomPadding: 10
}
ListView {
id: sfListView
property int sfDelegateHeight: 40
anchors.top: sectionTitle.bottom
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: count > 5
? 5 * sfListView.sfDelegateHeight + (sfListView.spacing * 4)
: count * sfListView.sfDelegateHeight + (sfListView.spacing * (count - 1))
spacing: 5
clip: true
model: subsFoldersModel
Layout.fillWidth: true
ScrollBar.vertical: ScrollBar { id: scrollBar }
delegate: Rectangle {
id: sfDelegate
width: root.width
height: sfListView.sfDelegateHeight
color: Kirigami.Theme.alternateBackgroundColor
Loader {
id: sfLoader
anchors.fill: parent
sourceComponent: model.display === "" ? sfEditComponent : sfDisplayComponent
}
Component {
id: sfDisplayComponent
RowLayout {
Label {
id: sfLabel
text: model.display
leftPadding: 10
Layout.fillWidth: true
}
Button {
icon.name: "edit-entry"
flat: true
onClicked: {
sfLoader.sourceComponent = sfEditComponent
}
}
Item { width: scrollBar.width }
}
} // Component: display
Component {
id: sfEditComponent
RowLayout {
TextField {
id: editField
leftPadding: 10
text: model.display
Layout.leftMargin: 5
Layout.fillWidth: true
Component.onCompleted: editField.forceActiveFocus(Qt.MouseFocusReason)
}
Button {
property bool canDelete: editField.text === ""
icon.name: "delete"
flat: true
onClicked: {
if (!canDelete) {
text = i18n("Confirm deletion")
canDelete = true
return
}
if (model.row === sfListView.count - 1) {
root.canAddFolder = true
}
subsFoldersModel.deleteFolder(model.row)
const rows = sfListView.count
sfListView.implicitHeight = rows > 5
? 5 * sfListView.sfDelegateHeight + (sfListView.spacing * 4)
: rows * sfListView.sfDelegateHeight + (sfListView.spacing * (rows - 1))
}
ToolTip {
text: i18n("Delete this folder from list")
}
}
Button {
icon.name: "dialog-ok"
flat: true
enabled: editField.text !== "" ? true : false
onClicked: {
subsFoldersModel.updateFolder(editField.text, model.row)
sfLoader.sourceComponent = sfDisplayComponent
if (model.row === sfListView.count - 1) {
root.canAddFolder = true
}
}
ToolTip {
text: i18n("Save changes")
}
}
Item { width: scrollBar.width }
}
} // Component: edit
} // delegate: Rectangle
}
Item {
id: spacer
anchors.top: sfListView.bottom
height: 5
}
Button {
id: sfAddFolder
anchors.top: spacer.bottom
icon.name: "list-add"
text: i18n("Add new folder")
enabled: root.canAddFolder
onClicked: {
subsFoldersModel.addFolder()
const rows = sfListView.count
sfListView.implicitHeight = rows > 5
? 5 * sfListView.sfDelegateHeight + (sfListView.spacing * 4)
: rows * sfListView.sfDelegateHeight + (sfListView.spacing * (rows - 1))
root.canAddFolder = false
}
}
}

@ -1,13 +0,0 @@
#
# SPDX-FileCopyrightText: 2021 George Florea Bănuș <georgefb899@gmail.com>
#
# SPDX-License-Identifier: CC-BY-4.0
#
module Haruna.Components
LabelWithTooltip 1.0 LabelWithTooltip.qml
ImageAdjustmentSlider 1.0 ImageAdjustmentSlider.qml
SettingsBasePage 1.0 SettingsBasePage.qml
SettingsHeader 1.0 SettingsHeader.qml
SubtitlesFolders 1.0 SubtitlesFolders.qml
SelectActionPopup 1.0 SelectActionPopup.qml

@ -1,179 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQml 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import "Menus"
ToolBar {
id: root
property var audioTracks
property var subtitleTracks
position: ToolBar.Header
visible: !window.isFullScreen() && GeneralSettings.showHeader
RowLayout {
id: headerRow
width: parent.width
ToolButton {
action: actions.openAction
focusPolicy: Qt.NoFocus
}
ToolButton {
action: actions.openUrlAction
focusPolicy: Qt.NoFocus
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.MiddleButton
onClicked: {
openUrlTextField.clear()
openUrlTextField.paste()
window.openFile(openUrlTextField.text, true, false)
}
}
}
ToolSeparator {
padding: vertical ? 10 : 2
topPadding: vertical ? 2 : 10
bottomPadding: vertical ? 2 : 10
contentItem: Rectangle {
implicitWidth: parent.vertical ? 1 : 24
implicitHeight: parent.vertical ? 24 : 1
color: FishUI.Theme.textColor
}
}
ToolButton {
id: subtitleMenuButton
property var model: 0
text: qsTr("Subtitles")
icon.name: "add-subtitle"
focusPolicy: Qt.NoFocus
onClicked: {
if (subtitleMenuButton.model === 0) {
subtitleMenuButton.model = mpv.subtitleTracksModel
}
subtitleMenu.visible = !subtitleMenu.visible
}
Menu {
id: subtitleMenu
y: parent.height
Menu {
id: secondarySubtitleMenu
title: qsTr("Secondary Subtitle")
Instantiator {
id: secondarySubtitleMenuInstantiator
model: subtitleMenuButton.model
onObjectAdded: secondarySubtitleMenu.insertItem( index, object )
onObjectRemoved: secondarySubtitleMenu.removeItem( object )
delegate: MenuItem {
enabled: model.id !== mpv.subtitleId || model.id === 0
checkable: true
checked: model.id === mpv.secondarySubtitleId
text: model.text
onTriggered: mpv.secondarySubtitleId = model.id
}
}
}
MenuSeparator {}
MenuItem {
text: qsTr("Primary Subtitle")
hoverEnabled: false
}
Instantiator {
id: primarySubtitleMenuInstantiator
model: subtitleMenuButton.model
onObjectAdded: subtitleMenu.addItem( object )
onObjectRemoved: subtitleMenu.removeItem( object )
delegate: MenuItem {
enabled: model.id !== mpv.secondarySubtitleId || model.id === 0
checkable: true
checked: model.id === mpv.subtitleId
text: model.text
onTriggered: mpv.subtitleId = model.id
}
}
}
}
ToolButton {
text: qsTr("Audio")
icon.name: "audio-volume-high"
focusPolicy: Qt.NoFocus
onClicked: {
if (audioMenuInstantiator.model === 0) {
audioMenuInstantiator.model = mpv.audioTracksModel
}
audioMenu.visible = !audioMenu.visible
}
Menu {
id: audioMenu
y: parent.height
Instantiator {
id: audioMenuInstantiator
model: 0
onObjectAdded: audioMenu.insertItem( index, object )
onObjectRemoved: audioMenu.removeItem( object )
delegate: MenuItem {
id: audioMenuItem
checkable: true
checked: model.id === mpv.audioId
text: model.text
onTriggered: mpv.audioId = model.id
}
}
}
}
Item {
Layout.fillWidth: true
}
ToolButton {
action: actions.configureAction
focusPolicy: Qt.NoFocus
}
ToolButton {
// using `action: actions.quitApplicationAction` breaks the action
// doens't work on the first try in certain circumstances
text: actions.quitApplicationAction.text
icon: actions.quitApplicationAction.icon
focusPolicy: Qt.NoFocus
onClicked: actions.quitApplicationAction.trigger()
}
}
}

@ -1,47 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.12
Menu {
id: root
title: i18n("&Audio")
Menu {
id: audioMenu
title: i18n("&Primary Track")
Instantiator {
id: audioMenuInstantiator
model: 0
onObjectAdded: audioMenu.insertItem( index, object )
onObjectRemoved: audioMenu.removeItem( object )
delegate: MenuItem {
id: audioMenuItem
checkable: true
checked: model.id === mpv.audioId
text: model.text
onTriggered: mpv.audioId = model.id
}
}
Connections {
target: mpv
onFileLoaded: {
audioMenuInstantiator.model = mpv.audioTracksModel
}
}
}
MenuSeparator {}
MenuItem { action: actions["muteAction"] }
MenuItem { action: actions["volumeUpAction"] }
MenuItem { action: actions["volumeDownAction"] }
}

@ -1,21 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
Menu {
id: root
title: i18n("&File")
MenuItem { action: actions["openAction"] }
MenuItem { action: actions["openUrlAction"] }
MenuSeparator {}
MenuItem { action: actions["quitApplicationAction"] }
}

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
Menu {
id: root
title: i18n("&Help")
MenuItem { action: actions["aboutHarunaAction"] }
}

@ -1,57 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
Menu {
id: root
title: i18n("&Playback")
MenuItem { action: actions["playPauseAction"] }
MenuItem { action: actions["playNextAction"] }
MenuItem { action: actions["playPreviousAction"] }
MenuSeparator {}
MenuItem { action: actions["increasePlayBackSpeedAction"] }
MenuItem { action: actions["decreasePlayBackSpeedAction"] }
MenuItem { action: actions["resetPlayBackSpeedAction"] }
MenuSeparator {}
Menu {
title: "Seek"
MenuItem { action: actions["seekForwardSmallAction"] }
MenuItem { action: actions["seekBackwardSmallAction"] }
MenuSeparator {}
MenuItem { action: actions["seekForwardMediumAction"] }
MenuItem { action: actions["seekBackwardMediumAction"] }
MenuSeparator {}
MenuItem { action: actions["seekForwardBigAction"] }
MenuItem { action: actions["seekBackwardBigAction"] }
MenuSeparator {}
MenuItem { action: actions["seekNextSubtitleAction"] }
MenuItem { action: actions["seekPrevSubtitleAction"] }
MenuSeparator {}
MenuItem { action: actions["seekPreviousChapterAction"] }
MenuItem { action: actions["seekNextChapterAction"] }
MenuSeparator {}
MenuItem { action: actions["frameStepAction"] }
MenuItem { action: actions["frameBackStepAction"] }
}
}

@ -1,17 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
Menu {
id: root
title: i18n("&Settings")
MenuItem { action: actions["configureAction"] }
MenuItem { action: actions["configureShortcutsAction"] }
}

@ -1,63 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.12
Menu {
id: root
title: i18n("&Subtitles")
Menu {
id: primarySubtitleMenu
title: i18n("Primary Subtitle")
onOpened: primarySubtitleMenuInstantiator.model = mpv.subtitleTracksModel
Instantiator {
id: primarySubtitleMenuInstantiator
model: 0
onObjectAdded: primarySubtitleMenu.insertItem( index, object )
onObjectRemoved: primarySubtitleMenu.removeItem( object )
delegate: MenuItem {
enabled: model.id !== mpv.secondarySubtitleId || model.id === 0
checkable: true
checked: model.id === mpv.subtitleId
text: model.text
onTriggered: mpv.subtitleId = model.id
}
}
}
Menu {
id: secondarySubtitleMenu
title: i18n("Secondary Subtitle")
onOpened: secondarySubtitleMenuInstantiator.model = mpv.subtitleTracksModel
Instantiator {
id: secondarySubtitleMenuInstantiator
model: 0
onObjectAdded: secondarySubtitleMenu.insertItem( index, object )
onObjectRemoved: secondarySubtitleMenu.removeItem( object )
delegate: MenuItem {
enabled: model.id !== mpv.subtitleId || model.id === 0
checkable: true
checked: model.id === mpv.secondarySubtitleId
text: model.text
onTriggered: mpv.secondarySubtitleId = model.id
}
}
}
MenuSeparator {}
MenuItem { action: actions["subtitleQuickenAction"] }
MenuItem { action: actions["subtitleDelayAction"] }
MenuItem { action: actions["subtitleToggleAction"] }
}

@ -1,28 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml 2.12
Instantiator {
id: root
property bool isFirst: true
property var menu
signal subtitleChanged(int id, int index)
model: 0
onObjectAdded: menu.addItem( object )
onObjectRemoved: menu.removeItem( object )
delegate: MenuItem {
checkable: true
checked: isFirst ? model.isFirstTrack : model.isSecondTrack
text: model.text
onTriggered: subtitleChanged(model.id, model.index)
}
}

@ -1,23 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
Menu {
id: root
title: i18n("&View")
MenuItem {
action: actions["toggleMenuBarAction"]
text: menuBar.visible ? i18n("Hide Menu Bar") : i18n("Show Menu Bar")
}
MenuItem {
action: actions["toggleHeaderAction"]
text: header.visible ? i18n("Hide Header") : i18n("Show Header")
}
}

@ -1,392 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import mpv 1.0
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
MpvObject {
id: root
property int mouseX: mouseArea.mouseX
property int mouseY: mouseArea.mouseY
signal setSubtitle(int id)
signal setSecondarySubtitle(int id)
signal setAudio(int id)
width: parent.width
height: window.isFullScreen() ? parent.height : parent.height - footer.height
anchors.left: PlaylistSettings.overlayVideo
? parent.left
: (PlaylistSettings.position === "left" ? playList.right : parent.left)
anchors.right: PlaylistSettings.overlayVideo
? parent.right
: (PlaylistSettings.position === "right" ? playList.left : parent.right)
anchors.top: parent.top
anchors.bottom: parent.bottom
volume: GeneralSettings.volume
onSetSubtitle: {
setProperty("sid", id)
}
onSetSecondarySubtitle: {
setProperty("secondary-sid", id)
}
onSetAudio: {
setProperty("aid", id)
}
onReady: {
setProperty("screenshot-template", VideoSettings.screenshotTemplate)
setProperty("screenshot-format", VideoSettings.screenshotFormat)
const preferredAudioTrack = AudioSettings.preferredTrack
setProperty("aid", preferredAudioTrack === 0 ? "auto" : preferredAudioTrack)
setProperty("alang", AudioSettings.preferredLanguage)
const preferredSubTrack = SubtitlesSettings.preferredTrack
setProperty("sid", preferredSubTrack === 0 ? "auto" : preferredSubTrack)
setProperty("slang", SubtitlesSettings.preferredLanguage)
setProperty("sub-file-paths", SubtitlesSettings.subtitlesFolders.join(":"))
if (app.argument(0) !== "") {
window.openFile(app.argument(0), true, PlaylistSettings.loadSiblings)
} else {
// open last played file
if (app.isYoutubePlaylist(GeneralSettings.lastPlayedFile)) {
getYouTubePlaylist(GeneralSettings.lastPlayedFile)
playList.isYouTubePlaylist = true
} else {
// file is local, open normally
window.openFile(GeneralSettings.lastPlayedFile, false, PlaylistSettings.loadSiblings)
}
}
}
onYoutubePlaylistLoaded: {
mpv.command(["loadfile", playlistModel.getPath(GeneralSettings.lastPlaylistIndex)])
playlistModel.setPlayingVideo(GeneralSettings.lastPlaylistIndex)
playList.setPlayListScrollPosition()
}
onFileStarted: {
if (typeof getProperty("path") === "string" && getProperty("path").startsWith("http")) {
loadingIndicatorParent.visible = true
}
}
onFileLoaded: {
loadingIndicatorParent.visible = false
header.audioTracks = getProperty("track-list").filter(track => track["type"] === "audio")
header.subtitleTracks = getProperty("track-list").filter(track => track["type"] === "sub")
if (playList.playlistView.count <= 1) {
setProperty("loop-file", "inf")
}
setProperty("ab-loop-a", "no")
setProperty("ab-loop-b", "no")
mpv.pause = loadTimePosition() !== 0
position = loadTimePosition()
}
onChapterChanged: {
if (!PlaybackSettings.skipChapters) {
return
}
const chapters = mpv.getProperty("chapter-list")
const chaptersToSkip = PlaybackSettings.chaptersToSkip
if (chapters.length === 0 || chaptersToSkip === "") {
return
}
const words = chaptersToSkip.split(",")
for (let i = 0; i < words.length; ++i) {
if (chapters[mpv.chapter] && chapters[mpv.chapter].title.toLowerCase().includes(words[i].trim())) {
actions.seekNextChapterAction.trigger()
if (PlaybackSettings.showOsdOnSkipChapters) {
osd.message(i18n("Skipped chapter: %1").arg(chapters[mpv.chapter-1].title))
}
// a chapter title can match multiple words
// return to prevent skipping multiple chapters
return
}
}
}
onEndFile: {
if (reason === "error") {
if (playlistModel.rowCount() === 0) {
return
}
const title = playlistModel.getItem(playlistModel.getPlayingVideo()).mediaTitle()
osd.message(i18n("Could not play: %1").arg(title))
// only skip to next video if it's a youtube playList
// to do: figure out why playback fails and act accordingly
if (!playList.isYouTubePlaylist) {
return
}
}
const nextFileRow = playlistModel.getPlayingVideo() + 1
if (nextFileRow < playList.playlistView.count) {
const nextFile = playlistModel.getPath(nextFileRow)
playlistModel.setPlayingVideo(nextFileRow)
loadFile(nextFile, !playList.isYouTubePlaylist)
} else {
// Last file in playlist
if (PlaylistSettings.repeat) {
playlistModel.setPlayingVideo(0)
loadFile(playlistModel.getPath(0), !playList.isYouTubePlaylist)
}
}
}
onPauseChanged: {
if (pause) {
footer.playPauseButton.icon.name = "media-playback-start"
lockManager.setInhibitionOff()
} else {
footer.playPauseButton.icon.name = "media-playback-pause"
lockManager.setInhibitionOn()
}
}
Timer {
id: saveWatchLaterFileTimer
interval: 1000
running: !mpv.pause
repeat: true
onTriggered: handleTimePosition()
}
Timer {
id: hideCursorTimer
property double tx: mouseArea.mouseX
property double ty: mouseArea.mouseY
property int timeNotMoved: 0
running: window.isFullScreen() && mouseArea.containsMouse
repeat: true
interval: 50
onTriggered: {
if (mouseArea.mouseX === tx && mouseArea.mouseY === ty) {
if (timeNotMoved > 2000) {
app.hideCursor()
}
} else {
app.showCursor()
timeNotMoved = 0
}
tx = mouseArea.mouseX
ty = mouseArea.mouseY
timeNotMoved += interval
}
}
// Window move
DragHandler {
acceptedDevices: PointerDevice.GenericPointer
grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything
onActiveChanged: if (active) window.helper.startSystemMove(window)
}
MouseArea {
id: mouseArea
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
if (!playList.canToggleWithMouse || playList.playlistView.count <= 1) {
return
}
// if (playList.position === "right") {
// if (mouseX > width - 50) {
// playList.state = "visible"
// }
// if (mouseX < width - playList.width - 20) {
// playList.state = "hidden"
// }
// } else {
// if (mouseX < 50) {
// playList.state = "visible"
// }
// if (mouseX > playList.width + 20) {
// playList.state = "hidden"
// }
// }
}
onWheel: {
if (wheel.angleDelta.y > 0) {
if (MouseSettings.scrollUp) {
actions[MouseSettings.scrollUp].trigger()
}
} else if (wheel.angleDelta.y) {
if (MouseSettings.scrollDown) {
actions[MouseSettings.scrollDown].trigger()
}
}
}
onPressed: {
focus = true
// Hide play list
playList.state = "hidden"
if (mouse.button === Qt.LeftButton) {
if (MouseSettings.left) {
actions[MouseSettings.left].trigger()
}
} else if (mouse.button === Qt.MiddleButton) {
if (MouseSettings.middle) {
actions[MouseSettings.middle].trigger()
}
} else if (mouse.button === Qt.RightButton) {
if (MouseSettings.right) {
actions[MouseSettings.right].trigger()
}
}
}
onDoubleClicked: {
if (mouse.button === Qt.LeftButton) {
if (MouseSettings.leftx2) {
actions[MouseSettings.leftx2].trigger()
}
} else if (mouse.button === Qt.MiddleButton) {
if (MouseSettings.middlex2) {
actions[MouseSettings.middlex2].trigger()
}
} else if (mouse.button === Qt.RightButton) {
if (MouseSettings.rightx2) {
actions[MouseSettings.rightx2].trigger()
}
}
}
}
DropArea {
id: dropArea
property var acceptedSubtitleTypes: ["application/x-subrip", "text/x-ssa"]
anchors.fill: parent
keys: ["text/uri-list"]
onDropped: {
if (acceptedSubtitleTypes.includes(app.mimeType(drop.urls[0]))) {
const subFile = drop.urls[0].replace("file://", "")
command(["sub-add", drop.urls[0], "select"])
}
if (app.mimeType(drop.urls[0]).startsWith("video/") || app.mimeType(drop.urls[0]).startsWith("audio/")) {
window.openFile(drop.urls[0], true, PlaylistSettings.loadSiblings)
}
}
}
Connections {
target: mediaPlayer2Player
function onPlaypause() {
actions.playPauseAction.trigger()
}
function onPlay() {
root.pause = false
}
function onPause() {
root.pause = true
}
function onStop() {
root.position = 0
root.pause = true
}
function onNext() {
actions.playNextAction.trigger()
}
function onPrevious() {
actions.playPreviousAction.trigger()
}
function onSeek() {
root.command(["add", "time-pos", offset])
}
function onOpenUri() {
root.command(["add", "time-pos", offset])
}
}
Rectangle {
id: loadingIndicatorParent
visible: false
anchors.centerIn: parent
color: {
let color = FishUI.Theme.backgroundColor
Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, 0.2)
}
FishUI.IconItem {
id: loadingIndicator
source: "view-refresh"
anchors.centerIn: parent
width: FishUI.Units.iconSizes.large
height: FishUI.Units.iconSizes.large
RotationAnimator {
target: loadingIndicator;
from: 0;
to: 360;
duration: 1500
loops: Animation.Infinite
running: true
}
Component.onCompleted: {
parent.width = width + 10
parent.height = height + 10
}
}
}
Component.onCompleted: {
mediaPlayer2Player.mpv = root
}
function handleTimePosition() {
if (mpv.position < mpv.duration - 10) {
saveTimePosition()
} else {
resetTimePosition()
}
}
}

@ -1,57 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
Item {
id: root
property alias label: label
Label {
id: label
x: 10
y: 10
visible: false
color: FishUI.Theme.textColor
background: Rectangle {
color: FishUI.Theme.backgroundColor
}
padding: 5
font.pointSize: parseInt(GeneralSettings.osdFontSize)
}
Timer {
id: timer
running: false
repeat: false
interval: 3000
onTriggered: {
label.visible = false
}
}
function message(text) {
const osdFontSize = parseInt(GeneralSettings.osdFontSize)
label.text = text
if (osdFontSize === 0) {
return;
}
if(label.visible) {
timer.restart()
} else {
timer.start()
}
label.visible = true
}
}

@ -1,164 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
* SPDX-FileCopyrightText: 2021 Reion Wong <aj@cutefishos.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
Item {
id: root
property alias scrollPositionTimer: scrollPositionTimer
property alias playlistView: playlistView
// property bool canToggleWithMouse: PlaylistSettings.canToggleWithMouse
property bool canToggleWithMouse: true
property string position: PlaylistSettings.position
property int rowHeight: PlaylistSettings.rowHeight
property int bigFont: PlaylistSettings.bigFontFullscreen
property bool isYouTubePlaylist: false
z: 9999
height: mpv.height - FishUI.Units.largeSpacing * 6
width: {
if (PlaylistSettings.style === "compact") {
return FishUI.Units.gridUnit * 20
} else {
const w = FishUI.Units.gridUnit * 30
return (parent.width * 0.33) < w ? w : parent.width * 0.33
}
}
x: position === "right" ? parent.width : -width
y: FishUI.Units.largeSpacing * 3
state: "hidden"
ScrollView {
id: playlistScrollView
z: 20
anchors.fill: parent
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ListView {
id: playlistView
model: mpv.playlistModel
spacing: 1
delegate: PlayListItem {
onDoubleClicked: root.state = "hidden"
}
}
}
Timer {
id: scrollPositionTimer
interval: 50
running: true
repeat: true
onTriggered: {
setPlayListScrollPosition()
scrollPositionTimer.stop()
}
}
ShaderEffectSource {
id: shaderEffect
visible: PlaylistSettings.overlayVideo
anchors.fill: playlistScrollView
sourceItem: mpv
sourceRect: position === "right"
? Qt.rect(mpv.width - root.width, mpv.y, root.width, root.height)
: Qt.rect(0, 0, root.width, root.height)
}
FastBlur {
visible: PlaylistSettings.overlayVideo
anchors.fill: shaderEffect
radius: 100
source: shaderEffect
}
Rectangle {
anchors.fill: parent
color: FishUI.Theme.backgroundColor
opacity: 0.7
}
states: [
State {
name: "hidden"
PropertyChanges { target: root; x: position === "right" ? parent.width : -width }
PropertyChanges { target: root; visible: false }
},
State {
name : "visible"
PropertyChanges { target: root; x: position === "right" ? parent.width - root.width - FishUI.Units.largeSpacing : 0 }
PropertyChanges { target: root; visible: true }
}
]
transitions: [
Transition {
from: "visible"
to: "hidden"
SequentialAnimation {
NumberAnimation {
target: root
property: "x"
duration: 150
easing.type: Easing.InQuad
}
PropertyAction {
target: root
property: "visible"
value: false
}
}
},
Transition {
from: "hidden"
to: "visible"
SequentialAnimation {
PropertyAction {
target: root
property: "visible"
value: true
}
NumberAnimation {
target: root
property: "x"
duration: 150
easing.type: Easing.OutQuad
}
}
}
]
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {
width: root.width
height: root.height
Rectangle {
anchors.fill: parent
radius: FishUI.Theme.bigRadius
}
}
}
function setPlayListScrollPosition() {
playlistView.positionViewAtIndex(playlistView.model.playingVideo, ListView.Beginning)
}
}

@ -1,71 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: false
helpFile: ""
GridLayout {
id: content
columns: 2
Label {
text: i18n("Preferred language")
Layout.alignment: Qt.AlignRight
}
TextField {
text: AudioSettings.preferredLanguage
placeholderText: "eng,ger etc."
Layout.fillWidth: true
onTextEdited: {
AudioSettings.preferredLanguage = text
AudioSettings.save()
mpv.setProperty("alang", text)
}
ToolTip {
text: i18n("Do not use spaces.")
}
}
Label {
text: i18n("Preferred track")
Layout.alignment: Qt.AlignRight
}
SpinBox {
from: 0
to: 100
value: AudioSettings.preferredTrack
editable: true
onValueChanged: {
AudioSettings.preferredTrack = value
AudioSettings.save()
if (value === 0) {
mpv.setProperty("aid", "auto")
} else {
mpv.setProperty("aid", value)
}
}
}
Item {
width: FishUI.Units.gridUnit
height: FishUI.Units.gridUnit
}
}
}

@ -1,107 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import org.kde.kirigami 2.11 as Kirigami
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: true
helpFile: ":/CustomCommandsSettings.html"
docPage: "help:/haruna/CustomCommandsSettings.html"
Component {
id: customCommandDelegate
Kirigami.AbstractListItem {
id: customCommandItem
height: Kirigami.Units.gridUnit * 3
padding: 0
contentItem: RowLayout {
anchors.fill: parent
spacing: 0
Kirigami.ListItemDragHandle {
listItem: customCommandItem
listView: customCommandsView
onMoveRequested: customCommandsModel.moveRows(oldIndex, newIndex)
}
Kirigami.Icon {
source: model.type === "shortcut" ? "configure-shortcuts" : "code-context"
width: Kirigami.Units.iconSizes.small
height: Kirigami.Units.iconSizes.small
}
LabelWithTooltip {
text: model.command
elide: Text.ElideRight
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing
}
ToolButton {
text: i18n("Shortcut")
icon.name: "configure-shortcuts"
visible: model.type === "shortcut"
onClicked: app.configureShortcuts(model.command)
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
}
ToolButton {
icon.name: "edit-entry"
Layout.rightMargin: Kirigami.Units.largeSpacing
onClicked: applicationWindow().pageStack.replace("qrc:/EditCustomCommand.qml",
{
command: model.command,
osdMessage: model.osdMessage,
type: model.type,
commandId: model.commandId,
index: model.index,
mode: EditCustomCommand.Mode.Edit
})
}
}
}
}
ListView {
id: customCommandsView
model: customCommandsModel
delegate: Kirigami.DelegateRecycler {
width: customCommandsView.width
sourceComponent: customCommandDelegate
}
}
footer: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
id: addButton
text: i18n("&Add")
icon.name: "list-add"
onClicked: applicationWindow().pageStack.replace("qrc:/EditCustomCommand.qml")
Layout.alignment: Qt.AlignRight
}
}
}
}

@ -1,168 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
property string command: ""
property string osdMessage: ""
property string type: "shortcut"
property string commandId: ""
property int index: -1
property int mode: EditCustomCommand.Mode.Create
enum Mode {
Create = 0,
Edit
}
hasHelp: true
helpFile: ":/CustomCommandsSettings.html"
Action {
shortcut: "esc"
onTriggered: applicationWindow().pageStack.replace("qrc:/CustomCommands.qml")
}
GridLayout {
columns: 2
Label {
text: i18n("Command")
Layout.alignment: Qt.AlignRight
}
TextField {
id: commandTextField
text: root.command
placeholderText: "add volume +10"
Layout.fillWidth: true
Component.onCompleted: forceActiveFocus()
}
Label {
text: i18n("OSD Message")
Layout.alignment: Qt.AlignRight
}
TextField {
id: osdMessageTextField
text: root.osdMessage
enabled: typeGroup.checkedButton.optionName === "shortcut"
placeholderText: "Filename: ${filename}"
Layout.fillWidth: true
}
Label {
text: i18n("Type")
Layout.alignment: Qt.AlignRight | Qt.AlignTop
}
ButtonGroup {
id: typeGroup
buttons: typeGroupItems.children
}
Column {
id: typeGroupItems
spacing: FishUI.Units.largeSpacing
RadioButton {
property string optionName: "shortcut"
checked: optionName === root.type
text: i18n("Keyboard shortcut")
}
RadioButton {
property string optionName: "startup"
checked: optionName === root.type
text: i18n("Run at startup")
}
}
}
footer: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
text: i18n("Delete")
icon.name: "delete"
visible: root.mode === EditCustomCommand.Mode.Edit
onClicked: {
customCommandsModel.deleteCustomCommand(root.commandId, root.index)
applicationWindow().pageStack.replace("qrc:/CustomCommands.qml")
}
}
Item {
Layout.fillWidth: true
}
ToolButton {
text: i18n("Cancel")
icon.name: "dialog-cancel"
onClicked: applicationWindow().pageStack.replace("qrc:/CustomCommands.qml")
Layout.alignment: Qt.AlignRight
}
ToolButton {
text: i18n("&Save")
icon.name: "document-save"
enabled: commandTextField.text !== ""
onClicked: saveCustomCommand()
Layout.alignment: Qt.AlignRight
function saveCustomCommand() {
if (commandTextField.text === "") {
return
}
if (typeGroup.checkedButton.optionName === "shortcut") {
// creates action and adds it to the action collection
// so that its shortcut can be set by the user
app.createUserAction(commandTextField.text)
} else {
// execute the user command
mpv.userCommand(commandTextField.text)
}
switch (root.mode) {
case EditCustomCommand.Mode.Create:
// save new command to config file
customCommandsModel.saveCustomCommand(commandTextField.text,
osdMessageTextField.text,
typeGroup.checkedButton.optionName)
break
case EditCustomCommand.Mode.Edit:
// update existing command
customCommandsModel.editCustomCommand(root.index,
commandTextField.text,
osdMessageTextField.text,
typeGroup.checkedButton.optionName)
break
}
applicationWindow().pageStack.replace("qrc:/CustomCommands.qml")
}
}
}
}
}

@ -1,336 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import org.kde.kirigami 2.11 as Kirigami
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: true
helpFile: ":/GeneralSettings.html"
docPage: "help:/haruna/GeneralSettings.html"
GridLayout {
id: content
columns: 2
// OSD Font Size
Label {
text: qsTr("Osd font size")
Layout.alignment: Qt.AlignRight
}
Item {
height: osdFontSize.height
SpinBox {
id: osdFontSize
// used to prevent osd showing when opening the page
property bool completed: false
editable: true
from: 0
to: 100
value: GeneralSettings.osdFontSize
onValueChanged: {
if (completed) {
osd.label.font.pointSize = osdFontSize.value
osd.message("Test osd font size")
GeneralSettings.osdFontSize = osdFontSize.value
GeneralSettings.save()
}
}
Component.onCompleted: completed = true
}
Layout.fillWidth: true
}
// Volume Step
Label {
text: qsTr("Volume step")
Layout.alignment: Qt.AlignRight
}
Item {
height: volumeStep.height
SpinBox {
id: volumeStep
editable: true
from: 0
to: 100
value: GeneralSettings.volumeStep
onValueChanged: {
if (root.visible) {
GeneralSettings.volumeStep = volumeStep.value
GeneralSettings.save()
}
}
}
Layout.fillWidth: true
}
// Seek Small Step
Label {
text: qsTr("Seek small step")
Layout.alignment: Qt.AlignRight
}
Item {
height: seekSmallStep.height
SpinBox {
id: seekSmallStep
editable: true
from: 0
to: 100
value: GeneralSettings.seekSmallStep
onValueChanged: {
GeneralSettings.seekSmallStep = seekSmallStep.value
GeneralSettings.save()
}
}
Layout.fillWidth: true
}
// Seek Medium Step
Label {
text: qsTr("Seek medium step")
Layout.alignment: Qt.AlignRight
}
Item {
height: seekMediumStep.height
RowLayout {
SpinBox {
id: seekMediumStep
editable: true
from: 0
to: 100
value: GeneralSettings.seekMediumStep
onValueChanged: {
GeneralSettings.seekMediumStep = seekMediumStep.value
GeneralSettings.save()
}
}
ToolButton {
icon.name: "documentinfo"
checkable: true
checked: false
ToolTip {
text: qsTr("This is also used for mouse wheel seeking, when mouse is over the progress bar")
visible: parent.checked
delay: 0
timeout: -1
closePolicy: Popup.NoAutoClose
}
}
}
Layout.fillWidth: true
}
// Seek Big Step
Label {
text: qsTr("Seek big step")
Layout.alignment: Qt.AlignRight
}
Item {
height: seekBigStep.height
SpinBox {
id: seekBigStep
editable: true
from: 0
to: 100
value: GeneralSettings.seekBigStep
onValueChanged: {
GeneralSettings.seekBigStep = seekBigStep.value
GeneralSettings.save()
}
}
Layout.fillWidth: true
}
Label {
text: qsTr("File dialog location")
Layout.alignment: Qt.AlignRight
}
Item {
height: fileDialogLocation.height
Layout.fillWidth: true
TextField {
id: fileDialogLocation
text: GeneralSettings.fileDialogLocation
onEditingFinished: {
GeneralSettings.fileDialogLocation = fileDialogLocation.text
GeneralSettings.save()
}
ToolTip {
text: qsTr("If empty the file dialog will remember the last opened location.")
}
}
}
SettingsHeader {
text: qsTr("Interface")
Layout.columnSpan: 2
Layout.fillWidth: true
}
CheckBox {
text: qsTr("Show MenuBar")
checked: GeneralSettings.showMenuBar
onCheckedChanged: {
GeneralSettings.showMenuBar = checked
GeneralSettings.save()
}
Layout.row: 8
Layout.column: 1
}
CheckBox {
text: qsTr("Show Header")
checked: GeneralSettings.showHeader
onCheckedChanged: {
GeneralSettings.showHeader = checked
GeneralSettings.save()
}
Layout.row: 9
Layout.column: 1
}
CheckBox {
text: qsTr("Show chapter markers")
checked: GeneralSettings.showChapterMarkers
onCheckedChanged: {
GeneralSettings.showChapterMarkers = checked
GeneralSettings.save()
}
Layout.row: 10
Layout.column: 1
}
Label {
text: qsTr("Color scheme")
Layout.alignment: Qt.AlignRight
}
ComboBox {
id: colorThemeSwitcher
textRole: "display"
model: app.colorSchemesModel
delegate: ItemDelegate {
Kirigami.Theme.colorSet: Kirigami.Theme.View
width: colorThemeSwitcher.width
highlighted: model.display === GeneralSettings.colorScheme
contentItem: RowLayout {
Kirigami.Icon {
source: model.decoration
Layout.preferredHeight: Kirigami.Units.iconSizes.small
Layout.preferredWidth: Kirigami.Units.iconSizes.small
}
Label {
text: model.display
color: highlighted ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor
Layout.fillWidth: true
}
}
}
onActivated: {
GeneralSettings.colorScheme = colorThemeSwitcher.textAt(index)
GeneralSettings.save()
app.activateColorScheme(GeneralSettings.colorScheme)
}
Component.onCompleted: currentIndex = find(GeneralSettings.colorScheme)
}
Label {
text: qsTr("GUI Style")
Layout.alignment: Qt.AlignRight
}
ComboBox {
id: guiStyleComboBox
textRole: "key"
model: ListModel {
id: stylesModel
ListElement { key: "Default"; }
}
onActivated: {
GeneralSettings.guiStyle = model.get(index).key
app.setGuiStyle(GeneralSettings.guiStyle)
// some themes can cause a crash
// the timer prevents saving the crashing theme,
// which would cause the app to crash on startup
saveGuiStyleTimer.start()
}
Timer {
id: saveGuiStyleTimer
interval: 1000
running: false
repeat: false
onTriggered: GeneralSettings.save()
}
Component.onCompleted: {
// populate the model with the available styles
for (let i = 0; i < app.availableGuiStyles().length; ++i) {
stylesModel.append({key: app.availableGuiStyles()[i]})
}
// set the saved style as the current item in the combo box
for (let j = 0; j < stylesModel.count; ++j) {
if (stylesModel.get(j).key === GeneralSettings.guiStyle) {
currentIndex = j
break
}
}
}
}
CheckBox {
text: qsTr("Use Breeze icon theme")
checked: GeneralSettings.useBreezeIconTheme
onCheckedChanged: {
GeneralSettings.useBreezeIconTheme = checked
GeneralSettings.save()
}
Layout.row: 14
Layout.column: 1
ToolTip {
text: qsTr("Sets the icon theme to breeze.\nRequires restart.")
}
}
Item {
width: Kirigami.Units.gridUnit
height: Kirigami.Units.gridUnit
}
}
}

@ -1,111 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQml 2.12
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import org.kde.kirigami 2.11 as Kirigami
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: false
helpFile: ""
ColumnLayout {
id: content
spacing: Kirigami.Units.largeSpacing
ListModel {
id: mouseActionsModel
ListElement {
label: "Left"
key: "left"
}
ListElement {
label: "Left double click"
key: "leftx2"
}
ListElement {
label: "Right"
key: "right"
}
ListElement {
label: "Right double click"
key: "rightx2"
}
ListElement {
label: "Middle"
key: "middle"
}
ListElement {
label: "Middle double click"
key: "middlex2"
}
ListElement {
label: "ScrollUp"
key: "scrollUp"
}
ListElement {
label: "ScrollDown"
key: "scrollDown"
}
}
ListView {
id: mouseButtonsListView
property int delegateHeight
implicitHeight: delegateHeight * (mouseButtonsListView.count + 1)
model: mouseActionsModel
delegate: Kirigami.BasicListItem {
id: delegate
label: model.label
subtitle: MouseSettings[model.key] ? MouseSettings[model.key] : "No action set"
icon: MouseSettings[model.key] ? "checkmark" : ""
reserveSpaceForIcon: true
width: content.width
highlighted: false
onClicked: openSelectActionPopup()
Component.onCompleted: mouseButtonsListView.delegateHeight = height
Connections {
target: selectActionPopup
onActionSelected: {
if (selectActionPopup.buttonIndex === model.index) {
MouseSettings[model.key] = actionName
MouseSettings.save()
}
}
}
function openSelectActionPopup() {
selectActionPopup.buttonIndex = model.index
selectActionPopup.headerTitle = model.label
selectActionPopup.open()
}
}
}
Item {
width: Kirigami.Units.gridUnit
height: Kirigami.Units.gridUnit
}
SelectActionPopup { id: selectActionPopup }
}
}

@ -1,91 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import org.kde.kirigami 2.11 as Kirigami
Kirigami.Page
{
padding: 0
leftPadding: 0
topPadding: 0
rightPadding: 0
bottomPadding: 0
Component.onCompleted: applicationWindow().pageStack.columnView.columnWidth = 250
footer: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
text: qsTr("Configure shortcuts")
icon.name: "configure-shortcuts"
onClicked: appActions.configureShortcutsAction.trigger()
Layout.fillWidth: true
}
}
}
ListModel {
id: settingsPagesModel
ListElement {
name: "General"
iconName: "configure"
page: "qrc:/General.qml"
}
ListElement {
name: "Playback"
iconName: "media-playback-start"
page: "qrc:/Playback.qml"
}
ListElement {
name: "Video"
iconName: "video-x-generic"
page: "qrc:/VideoSettings.qml"
}
ListElement {
name: "Audio"
iconName: "player-volume"
page: "qrc:/Audio.qml"
}
ListElement {
name: "Subtitles"
iconName: "add-subtitle"
page: "qrc:/Subtitles.qml"
}
ListElement {
name: "Playlist"
iconName: "view-media-playlist"
page: "qrc:/Playlist.qml"
}
ListElement {
name: "Mouse"
iconName: "input-mouse"
page: "qrc:/Mouse.qml"
}
ListElement {
name: "Custom commands"
iconName: "configure"
page: "qrc:/CustomCommands.qml"
}
}
ListView {
id: settingsPagesList
anchors.fill: parent
model: settingsPagesModel
delegate: Kirigami.BasicListItem {
text: qsTr(name)
icon: iconName
onClicked: applicationWindow().pageStack.push(model.page)
}
}
}

@ -1,281 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: true
helpFile: ":/PlaybackSettings.html"
docPage: "help:/haruna/PlaybackSettings.html"
GridLayout {
id: content
columns: 2
CheckBox {
id: hwDecodingCheckBox
text: i18n("Use hardware decoding")
checked: PlaybackSettings.useHWDecoding
onCheckedChanged: {
mpv.hwDecoding = checked
PlaybackSettings.useHWDecoding = checked
PlaybackSettings.save()
}
}
ComboBox {
id: hwDecodingComboBox
enabled: hwDecodingCheckBox.checked
textRole: "key"
model: ListModel {
id: hwDecModel
ListElement { key: "auto"; }
ListElement { key: "auto-safe"; }
ListElement { key: "auto-copy"; }
ListElement { key: "vdpau"; }
ListElement { key: "vdpau-copy"; }
ListElement { key: "vaapi"; }
ListElement { key: "vaapi-copy"; }
ListElement { key: "videotoolbox"; }
ListElement { key: "videotoolbox-copy"; }
ListElement { key: "dxva2"; }
ListElement { key: "dxva2-copy"; }
ListElement { key: "d3d11va"; }
ListElement { key: "d3d11va-copy"; }
ListElement { key: "mediacodec"; }
ListElement { key: "mediacodec-copy"; }
ListElement { key: "mmal"; }
ListElement { key: "mmal-copy"; }
ListElement { key: "nvdec"; }
ListElement { key: "nvdec-copy"; }
ListElement { key: "cuda"; }
ListElement { key: "cuda-copy"; }
ListElement { key: "crystalhd"; }
ListElement { key: "rkmpp"; }
}
onActivated: {
PlaybackSettings.hWDecoding = model.get(index).key
PlaybackSettings.save()
mpv.setProperty("hwdec", PlaybackSettings.hWDecoding)
}
Component.onCompleted: {
for (let i = 0; i < hwDecModel.count; ++i) {
if (hwDecModel.get(i).key === PlaybackSettings.hWDecoding) {
currentIndex = i
break
}
}
}
}
Label {
text: i18n("Remember time position")
}
RowLayout {
SpinBox {
id: timePositionSaving
from: -1
to: 9999
value: PlaybackSettings.minDurationToSavePosition
onValueChanged: {
PlaybackSettings.minDurationToSavePosition = value
PlaybackSettings.save()
}
}
LabelWithTooltip {
text: {
if (timePositionSaving.value === -1) {
return i18n("Disabled")
} else if (timePositionSaving.value === 0) {
return i18n("For all files")
} else if (timePositionSaving.value === 1) {
return i18n("For files longer than %1 minute").arg(timePositionSaving.value)
} else {
return i18n("For files longer than %1 minutes").arg(timePositionSaving.value)
}
}
elide: Text.ElideRight
Layout.fillWidth: true
}
}
CheckBox {
id: skipChaptersCheckBox
text: i18n("Skip chapters")
checked: PlaybackSettings.skipChapters
onCheckedChanged: {
PlaybackSettings.skipChapters = checked
PlaybackSettings.save()
}
Layout.columnSpan: 2
}
Label {
text: i18n("Skip chapters containing the following words")
enabled: skipChaptersCheckBox.checked
Layout.columnSpan: 2
}
TextField {
text: PlaybackSettings.chaptersToSkip
placeholderText: "op, ed, chapter 1"
enabled: skipChaptersCheckBox.checked
Layout.fillWidth: true
onEditingFinished: {
PlaybackSettings.chaptersToSkip = text
PlaybackSettings.save()
}
Layout.columnSpan: 2
ToolTip {
text: i18n("Separate words with a comma")
}
}
CheckBox {
text: i18n("Show an osd message when skipping chapters")
enabled: skipChaptersCheckBox.checked
checked: PlaybackSettings.showOsdOnSkipChapters
onCheckedChanged: {
PlaybackSettings.showOsdOnSkipChapters = checked
PlaybackSettings.save()
}
Layout.columnSpan: 2
}
// ------------------------------------
// Youtube-dl format settings
// ------------------------------------
SettingsHeader {
text: i18n("Youtube-dl")
Layout.columnSpan: 2
Layout.fillWidth: true
}
Label {
text: i18n("Format selection")
Layout.alignment: Qt.AlignRight
}
Item {
height: ytdlFormatComboBox.height
ComboBox {
id: ytdlFormatComboBox
property string hCurrentvalue: ""
textRole: "key"
model: ListModel {
id: leftButtonModel
ListElement { key: "Custom"; value: "" }
ListElement { key: "2160"; value: "bestvideo[height<=2160]+bestaudio/best" }
ListElement { key: "1440"; value: "bestvideo[height<=1440]+bestaudio/best" }
ListElement { key: "1080"; value: "bestvideo[height<=1080]+bestaudio/best" }
ListElement { key: "720"; value: "bestvideo[height<=720]+bestaudio/best" }
ListElement { key: "480"; value: "bestvideo[height<=480]+bestaudio/best" }
}
ToolTip {
text: i18n("Selects the best video with a height lower than or equal to the selected value.")
}
onActivated: {
hCurrentvalue = model.get(index).value
if (index === 0) {
ytdlFormatField.text = PlaybackSettings.ytdlFormat
}
if(index > 0) {
ytdlFormatField.focus = true
ytdlFormatField.text = model.get(index).value
}
PlaybackSettings.ytdlFormat = ytdlFormatField.text
PlaybackSettings.save()
mpv.setProperty("ytdl-format", PlaybackSettings.ytdlFormat)
}
Component.onCompleted: {
let i = hIndexOfValue(PlaybackSettings.ytdlFormat)
currentIndex = (i === -1) ? 0 : i
}
function hIndexOfValue(value) {
if (value === "bestvideo[height<=2160]+bestaudio/best") {
return 1
}
if (value === "bestvideo[height<=1440]+bestaudio/best") {
return 2
}
if (value === "bestvideo[height<=1080]+bestaudio/best") {
return 3
}
if (value === "bestvideo[height<=720]+bestaudio/best") {
return 4
}
if (value === "bestvideo[height<=480]+bestaudio/best") {
return 5
}
return 0
}
}
Layout.fillWidth: true
}
TextField {
id: ytdlFormatField
text: PlaybackSettings.ytdlFormat
onEditingFinished: {
PlaybackSettings.ytdlFormat = text
PlaybackSettings.save()
}
placeholderText: i18n("bestvideo+bestaudio/best")
onTextChanged: {
if (ytdlFormatComboBox.hCurrentvalue !== ytdlFormatField.text) {
ytdlFormatComboBox.currentIndex = 0
return;
}
if (ytdlFormatComboBox.hIndexOfValue(ytdlFormatField.text) !== -1) {
ytdlFormatComboBox.currentIndex = ytdlFormatComboBox.hIndexOfValue(ytdlFormatField.text)
return;
}
}
Layout.fillWidth: true
Layout.columnSpan: 2
}
TextEdit {
text: i18n("Leave empty for default value: <i>bestvideo+bestaudio/best</i>")
color: FishUI.Theme.textColor
readOnly: true
wrapMode: Text.WordWrap
textFormat: TextEdit.RichText
selectByMouse: true
Layout.fillWidth: true
Layout.columnSpan: 2
}
// ------------------------------------
// END - Youtube-dl format settings
// ------------------------------------
Item {
width: FishUI.Units.gridUnit
height: FishUI.Units.gridUnit
}
}
}

@ -1,179 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: false
helpFile: ""
GridLayout {
id: content
columns: 2
Label {
text: i18n("Position")
Layout.alignment: Qt.AlignRight
}
ComboBox {
textRole: "key"
model: ListModel {
ListElement { key: "Left"; value: "left" }
ListElement { key: "Right"; value: "right" }
}
Component.onCompleted: {
for (let i = 0; i < model.count; ++i) {
if (model.get(i).value === PlaylistSettings.position) {
currentIndex = i
break
}
}
}
onActivated: {
PlaylistSettings.position = model.get(index).value
PlaylistSettings.save()
playList.position = model.get(index).value
}
}
Label {
text: i18n("Row height")
Layout.alignment: Qt.AlignRight
}
SpinBox {
from: 0
to: 100
value: PlaylistSettings.rowHeight
enabled: PlaylistSettings.style === "compact" ? false : true
onValueChanged: {
PlaylistSettings.rowHeight = value
PlaylistSettings.save()
playList.rowHeight = value
playList.playlistView.forceLayout()
}
}
Label {
text: i18n("Playlist style")
Layout.alignment: Qt.AlignRight
}
ComboBox {
textRole: "display"
model: ListModel {
ListElement { display: "Default"; value: "default" }
ListElement { display: "WithThumbnails"; value: "withThumbnails" }
ListElement { display: "Compact"; value: "compact" }
}
Component.onCompleted: {
for (let i = 0; i < model.count; ++i) {
if (model.get(i).value === PlaylistSettings.style) {
currentIndex = i
break
}
}
}
onActivated: {
PlaylistSettings.style = model.get(index).value
PlaylistSettings.save()
}
}
Item { width: 1; height: 1 }
CheckBox {
checked: PlaylistSettings.overlayVideo
text: i18n("Overlay video")
onCheckStateChanged: {
PlaylistSettings.overlayVideo = checked
PlaylistSettings.save()
}
ToolTip {
text: i18n("When checked the playlist goes on top of the video\nWhen unchecked the video is resized")
}
}
Item { width: 1; height: 1 }
CheckBox {
checked: PlaylistSettings.showMediaTitle
text: i18n("Show media title instead of file name")
onCheckStateChanged: {
PlaylistSettings.showMediaTitle = checked
PlaylistSettings.save()
}
}
Item { width: 1; height: 1 }
CheckBox {
checked: PlaylistSettings.loadSiblings
text: i18n("Auto load videos from same folder")
onCheckStateChanged: {
PlaylistSettings.loadSiblings = checked
PlaylistSettings.save()
}
}
Item { width: 1; height: 1 }
CheckBox {
checked: PlaylistSettings.repeat
text: i18n("Repeat")
onCheckStateChanged: {
PlaylistSettings.repeat = checked
PlaylistSettings.save()
}
}
Item { width: 1; height: 1 }
CheckBox {
checked: PlaylistSettings.showRowNumber
text: i18n("Show row number")
onCheckStateChanged: {
PlaylistSettings.showRowNumber = checked
PlaylistSettings.save()
}
}
Item { width: 1; height: 1 }
CheckBox {
checked: PlaylistSettings.canToggleWithMouse
text: i18n("Toggle with mouse")
onCheckStateChanged: {
PlaylistSettings.canToggleWithMouse = checked
PlaylistSettings.save()
playList.canToggleWithMouse = checked
}
}
Item { width: 1; height: 1 }
CheckBox {
text: i18n("Increase font size when fullscreen")
checked: PlaylistSettings.bigFontFullscreen
enabled: PlaylistSettings.style === "compact" ? false : true
onCheckStateChanged: {
PlaylistSettings.bigFontFullscreen = checked
PlaylistSettings.save()
playList.bigFont = checked
playList.playlistView.forceLayout()
}
}
Item {
width: FishUI.Units.gridUnit
height: FishUI.Units.gridUnit
}
}
}

@ -1,80 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.10
import QtQuick.Window 2.1
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.12
import org.kde.kirigami 2.11 as Kirigami
import org.kde.haruna 1.0 as Haruna
Kirigami.ApplicationWindow {
id: root
width: 800
height: 600
title: qsTr("Haruna Settings")
visible: false
pageStack.initialPage: "qrc:/Navigation.qml"
Component.onCompleted: pageStack.push("qrc:/General.qml")
Window {
id: helpWindow
width: 700
height: 600
title: qsTr("Help")
color: Kirigami.Theme.backgroundColor
onVisibleChanged: info.text = app.getFileContent(applicationWindow().pageStack.currentItem.helpFile)
Flickable {
id: scrollView
property int scrollStepSize: 100
anchors.fill: parent
contentHeight: info.height
ScrollBar.vertical: ScrollBar {
id: scrollbar
policy: ScrollBar.AlwaysOn
stepSize: scrollView.scrollStepSize/scrollView.contentHeight
}
MouseArea {
anchors.fill: parent
onWheel: {
if (wheel.angleDelta.y > 0) {
scrollbar.decrease()
} else {
scrollbar.increase()
}
}
}
TextArea {
id: info
background: Rectangle {
color: "transparent"
border.color: "transparent"
}
width: parent.width
color: Kirigami.Theme.textColor
readOnly: true
textFormat: Text.RichText
wrapMode: Text.WordWrap
selectByMouse: true
rightPadding: scrollbar.width
onLinkActivated: Qt.openUrlExternally(link)
onHoveredLinkChanged: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}
}

@ -1,77 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: false
helpFile: ""
GridLayout {
id: content
columns: 2
SubtitlesFolders {
id: subtitleFolders
implicitWidth: root.width
Layout.fillWidth: true
Layout.columnSpan: 2
}
Label {
text: i18n("Preferred language")
Layout.alignment: Qt.AlignRight
}
TextField {
text: SubtitlesSettings.preferredLanguage
placeholderText: "eng,ger etc."
Layout.fillWidth: true
onTextEdited: {
SubtitlesSettings.preferredLanguage = text
SubtitlesSettings.save()
mpv.setProperty("slang", text)
}
ToolTip {
text: i18n("Do not use spaces.")
}
}
Label {
text: i18n("Preferred track")
Layout.alignment: Qt.AlignRight
}
SpinBox {
from: 0
to: 100
value: SubtitlesSettings.preferredTrack
editable: true
onValueChanged: {
SubtitlesSettings.preferredTrack = value
SubtitlesSettings.save()
if (value === 0) {
mpv.setProperty("sid", "auto")
} else {
mpv.setProperty("sid", value)
}
}
}
Item {
width: FishUI.Units.gridUnit
height: FishUI.Units.gridUnit
}
}
}

@ -1,178 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import Haruna.Components 1.0
SettingsBasePage {
id: root
hasHelp: true
helpFile: ":/VideoSettings.html"
docPage: "help:/haruna/VideoSettings.html"
GridLayout {
id: content
columns: 2
SettingsHeader {
text: i18n("Screenshots")
topMargin: 0
Layout.columnSpan: 2
Layout.fillWidth: true
}
// ------------------------------------
// Screenshot Format
// ------------------------------------
Label {
text: i18n("Format")
Layout.alignment: Qt.AlignRight
}
Item {
height: screenshotFormat.height
ComboBox {
id: screenshotFormat
textRole: "key"
model: ListModel {
ListElement { key: "PNG"; }
ListElement { key: "JPG"; }
ListElement { key: "WebP"; }
}
onActivated: {
VideoSettings.screenshotFormat = model.get(index).key
VideoSettings.save()
mpv.setProperty("screenshot-format", VideoSettings.screenshotFormat)
}
Component.onCompleted: {
if (VideoSettings.screenshotFormat === "PNG") {
currentIndex = 0
}
if (VideoSettings.screenshotFormat === "JPG") {
currentIndex = 1
}
if (VideoSettings.screenshotFormat === "WebP") {
currentIndex = 2
}
}
}
Layout.fillWidth: true
}
// ------------------------------------
// Screenshot template
// ------------------------------------
Label {
text: i18n("Template")
Layout.alignment: Qt.AlignRight
}
Item {
height: screenshotTemplate.height
TextField {
id: screenshotTemplate
text: VideoSettings.screenshotTemplate
onEditingFinished: {
VideoSettings.screenshotTemplate = text
VideoSettings.save()
mpv.setProperty("screenshot-template", VideoSettings.screenshotTemplate)
}
Layout.fillWidth: true
}
}
SettingsHeader {
text: i18n("Image adjustments")
Layout.columnSpan: 2
Layout.fillWidth: true
}
// ------------------------------------
// CONTRAST
// ------------------------------------
Label {
text: i18n("Contrast")
Layout.alignment: Qt.AlignRight
}
ImageAdjustmentSlider {
id: contrastSlider
value: mpv.contrast
onSliderValueChanged: mpv.contrast = value.toFixed(0)
Layout.topMargin: FishUI.Units.largeSpacing
}
// ------------------------------------
// BRIGHTNESS
// ------------------------------------
Label {
text: i18n("Brightness")
Layout.alignment: Qt.AlignRight
}
ImageAdjustmentSlider {
id: brightnessSlider
value: mpv.brightness
onSliderValueChanged: mpv.brightness = value.toFixed(0)
Layout.topMargin: FishUI.Units.largeSpacing
}
// ------------------------------------
// GAMMA
// ------------------------------------
Label {
text: i18n("Gamma")
Layout.alignment: Qt.AlignRight
}
ImageAdjustmentSlider {
id: gammaSlider
value: mpv.gamma
onSliderValueChanged: mpv.gamma = value.toFixed(0)
Layout.topMargin: FishUI.Units.largeSpacing
}
// ------------------------------------
// SATURATION
// ------------------------------------
Label {
text: i18n("Saturation")
Layout.alignment: Qt.AlignRight
}
ImageAdjustmentSlider {
id: saturationSlider
value: mpv.saturation
onSliderValueChanged: mpv.saturation = value.toFixed(0)
Layout.topMargin: FishUI.Units.largeSpacing
}
Label {
text: i18n("Middle click on the sliders to reset them")
Layout.columnSpan: 2
Layout.topMargin: FishUI.Units.largeSpacing
}
}
}

@ -1,34 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Shapes 1.12
import QtGraphicalEffects 1.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
Slider {
id: root
from: 0
to: 100
value: mpv.volume
implicitWidth: 100
implicitHeight: 25
wheelEnabled: true
leftPadding: 0
rightPadding: 0
onValueChanged: {
mpv.volume = value.toFixed(0)
GeneralSettings.volume = value.toFixed(0)
GeneralSettings.save()
}
}

@ -1,236 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
import Qt.labs.platform 1.0 as Platform
import QtQml 2.12
import FishUI 1.0 as FishUI
import org.kde.haruna 1.0
import mpv 1.0
import "Menus"
import "Settings"
FishUI.Window {
id: window
property var configure: app.action("configure")
property int preFullScreenVisibility
property var appActions: actions.list
visible: true
title: mpv.mediaTitle
width: 1200
minimumWidth: 700
height: 720
minimumHeight: 450
color: FishUI.Theme.backgroundColor
header.visible: false
onVisibilityChanged: {
if (!window.isFullScreen()) {
preFullScreenVisibility = visibility
}
}
contentTopMargin: 0
Header { id: header }
// menuBar: MenuBar {
// hoverEnabled: true
// visible: !window.isFullScreen() && GeneralSettings.showMenuBar
// background: Rectangle {
// color: Kirigami.Theme.backgroundColor
// }
// Kirigami.Theme.colorSet: Kirigami.Theme.Header
// FileMenu {}
// ViewMenu {}
// PlaybackMenu {}
// SubtitlesMenu {}
// AudioMenu {}
// SettingsMenu {}
// HelpMenu {}
// }
// Menu {
// id: mpvContextMenu
// modal: true
// FileMenu {}
// ViewMenu {}
// PlaybackMenu {}
// SubtitlesMenu {}
// AudioMenu {}
// SettingsMenu {}
// HelpMenu {}
// }
// SystemPalette { id: systemPalette; colorGroup: SystemPalette.Active }
SettingsEditor { id: settingsEditor }
Actions { id: actions }
MpvVideo {
id: mpv
Osd { id: osd }
}
PlayList { id: playList }
Footer {
id: footer
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: mpv.bottom
// anchors.leftMargin: FishUI.Units.largeSpacing * 2
// anchors.rightMargin: FishUI.Units.largeSpacing * 2
// anchors.bottomMargin: FishUI.Units.largeSpacing * 2
}
Instantiator {
model: proxyCustomCommandsModel
delegate: Action {
property var qaction: app.action(model.commandId)
text: qaction.text
shortcut: qaction.shortcutName
onTriggered: {
mpv.userCommand(model.command)
osd.message(mpv.command(["expand-text", model.osdMessage]))
}
}
}
Platform.FileDialog {
id: fileDialog
property url location: GeneralSettings.fileDialogLocation
? app.pathToUrl(GeneralSettings.fileDialogLocation)
: app.pathToUrl(GeneralSettings.fileDialogLastLocation)
folder: location
title: "Select file"
fileMode: Platform.FileDialog.OpenFile
onAccepted: {
openFile(fileDialog.file.toString(), true, PlaylistSettings.loadSiblings)
// the timer scrolls the playlist to the playing file
// once the table view rows are loaded
playList.scrollPositionTimer.start()
mpv.focus = true
GeneralSettings.fileDialogLastLocation = app.parentUrl(fileDialog.file)
GeneralSettings.save()
}
onRejected: mpv.focus = true
}
Popup {
id: openUrlPopup
x: 10
y: 10
onOpened: {
openUrlTextField.forceActiveFocus(Qt.MouseFocusReason)
openUrlTextField.selectAll()
}
RowLayout {
anchors.fill: parent
Label {
text: qsTr("<a href=\"https://youtube-dl.org\">Youtube-dl</a> was not found.")
visible: !app.hasYoutubeDl()
onLinkActivated: Qt.openUrlExternally(link)
}
TextField {
id: openUrlTextField
visible: app.hasYoutubeDl()
Layout.preferredWidth: 400
Layout.fillWidth: true
Component.onCompleted: text = GeneralSettings.lastUrl
Keys.onPressed: {
if (event.key === Qt.Key_Enter
|| event.key === Qt.Key_Return) {
openUrlButton.clicked()
}
if (event.key === Qt.Key_Escape) {
openUrlPopup.close()
}
}
}
Button {
id: openUrlButton
visible: app.hasYoutubeDl()
text: qsTr("Open")
onClicked: {
openFile(openUrlTextField.text, true, false)
GeneralSettings.lastUrl = openUrlTextField.text
// in case the url is a playList, it opens the first video
GeneralSettings.lastPlaylistIndex = 0
openUrlPopup.close()
openUrlTextField.clear()
}
}
}
}
Component.onCompleted: app.activateColorScheme(GeneralSettings.colorScheme)
function openFile(path, startPlayback, loadSiblings) {
if (app.isYoutubePlaylist(path)) {
mpv.getYouTubePlaylist(path);
playList.isYouTubePlaylist = true
} else {
playList.isYouTubePlaylist = false
}
mpv.playlistModel.clear()
mpv.pause = !startPlayback
if (loadSiblings) {
// get video files from same folder as the opened file
mpv.playlistModel.getVideos(path)
}
mpv.loadFile(path)
}
function isFullScreen() {
return window.visibility === Window.FullScreen
}
function toggleFullScreen() {
if (!isFullScreen()) {
window.showFullScreen()
} else {
if (window.preFullScreenVisibility === Window.Windowed) {
window.showNormal()
}
if (window.preFullScreenVisibility === Window.Maximized) {
window.show()
window.showMaximized()
}
}
app.showCursor()
playList.scrollPositionTimer.start()
}
}

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="haruna/haruna.conf" />
<group name="Audio">
<entry name="PreferredLanguage" type="String">
<label>Preferred language</label>
</entry>
<entry name="PreferredTrack" type="Int">
<label>Preferred track</label>
</entry>
</group>
</kcfg>

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
File=audiosettings.kcfg
ClassName=AudioSettings
Mutators=true
DefaultValueGetters=true
GenerateProperties=true
ParentInConstructor=true
Singleton=true

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="haruna/haruna.conf" />
<include>QStandardPaths</include>
<group name="General">
<entry name="OsdFontSize" type="Int">
<label>Osd font size</label>
<default>25</default>
</entry>
<entry name="VolumeStep" type="Int">
<label>Volume step</label>
<default>5</default>
</entry>
<entry name="SeekSmallStep" type="Int">
<label>Seek small step</label>
<default>5</default>
</entry>
<entry name="SeekMediumStep" type="Int">
<label>Seek medium step</label>
<default>15</default>
</entry>
<entry name="SeekBigStep" type="Int">
<label>Seek big step</label>
<default>30</default>
</entry>
<entry name="ShowMenuBar" type="bool">
<label>Show menu bar</label>
<default>true</default>
</entry>
<entry name="ShowHeader" type="bool">
<label>Show header</label>
<default>true</default>
</entry>
<entry name="ShowChapterMarkers" type="bool">
<label>Show chapter markers</label>
<default>true</default>
</entry>
<entry name="ColorScheme" type="String">
<label>Color scheme</label>
</entry>
<entry name="UseBreezeIconTheme" type="bool">
<default>false</default>
</entry>
<entry name="GuiStyle" type="String">
<default>Default</default>
</entry>
<entry name="Volume" type="Int">
<label>Volume</label>
<default>80</default>
</entry>
<entry name="LastPlayedFile" type="String">
<label>Last played file</label>
</entry>
<entry name="LastPlaylistIndex" type="Int">
<default>0</default>
</entry>
<entry name="FileDialogLocation" type="String">
<label>File default dialog location</label>
</entry>
<entry name="FileDialogLastLocation" type="String">
<label>File dialog last location</label>
<code>
QString videoFolder = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
</code>
<default code="true">videoFolder</default>
</entry>
<entry name="LastUrl" type="String">
<label>Last url</label>
</entry>
</group>
</kcfg>

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
File=generalsettings.kcfg
ClassName=GeneralSettings
Mutators=true
DefaultValueGetters=true
GenerateProperties=true
ParentInConstructor=true
Singleton=true

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="haruna/haruna.conf" />
<group name="Mouse">
<entry name="Left" type="String">
<label>Action for left click</label>
</entry>
<entry name="Leftx2" type="String">
<label>Action for left double click</label>
<default>toggleFullscreenAction</default>
</entry>
<entry name="Middle" type="String">
<label>Action for middle click</label>
<default>muteAction</default>
</entry>
<entry name="Middlex2" type="String">
<label>Action for middle double click</label>
<default>configureAction</default>
</entry>
<entry name="Right" type="String">
<label>Action for right click</label>
<default>playPauseAction</default>
</entry>
<entry name="Rightx2" type="String">
<label>Action for right double click</label>
</entry>
<entry name="ScrollUp" type="String">
<label>Action for scroll up</label>
<default>volumeUpAction</default>
</entry>
<entry name="ScrollDown" type="String">
<label>Action for scroll down</label>
<default>volumeDownAction</default>
</entry>
</group>
</kcfg>

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
File=mousesettings.kcfg
ClassName=MouseSettings
Mutators=true
DefaultValueGetters=true
GenerateProperties=true
ParentInConstructor=true
Singleton=true

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="haruna/haruna.conf" />
<group name="Playback">
<entry name="MinDurationToSavePosition" type="int">
<default>1</default>
</entry>
<entry name="SkipChapters" type="bool">
<label>Skip chapters</label>
<default>false</default>
</entry>
<entry name="ChaptersToSkip" type="String">
<label>Chapters to skip</label>
</entry>
<entry name="ShowOsdOnSkipChapters" type="bool">
<label>Show osd on skip chapters</label>
<default>true</default>
</entry>
<entry name="YtdlFormat" type="String">
<label>Ytdl format selection</label>
</entry>
<entry name="UseHWDecoding" type="bool">
<default>false</default>
</entry>
<entry name="HWDecoding" type="String">
<default>auto</default>
</entry>
</group>
</kcfg>

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
File=playbacksettings.kcfg
ClassName=PlaybackSettings
Mutators=true
DefaultValueGetters=true
GenerateProperties=true
ParentInConstructor=true
Singleton=true

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="haruna/haruna.conf" />
<group name="Playlist">
<entry name="Position" type="String">
<label>Position</label>
<default>right</default>
</entry>
<entry name="RowHeight" type="Int">
<label>Row height</label>
<default>10</default>
</entry>
<entry name="Style" type="String">
<default>default</default>
</entry>
<entry name="OverlayVideo" type="bool">
<default>true</default>
</entry>
<entry name="ShowThumbnails" type="bool">
<label>Show thumbnails</label>
<default>false</default>
</entry>
<entry name="ShowMediaTitle" type="bool">
<label>Show media title instead of file name</label>
<default>true</default>
</entry>
<entry name="LoadSiblings" type="bool">
<label>Auto load videos from same folder</label>
<default>true</default>
</entry>
<entry name="Repeat" type="bool">
<label>Repeat</label>
<default>true</default>
</entry>
<entry name="ShowRowNumber" type="bool">
<label>Show row number</label>
<default>true</default>
</entry>
<entry name="CanToggleWithMouse" type="bool">
<label>Toggle with mouse</label>
<default>true</default>
</entry>
<entry name="BigFontFullscreen" type="bool">
<label>Increase font size when fullscreen</label>
<default>true</default>
</entry>
</group>
</kcfg>

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
File=playlistsettings.kcfg
ClassName=PlaylistSettings
Mutators=true
DefaultValueGetters=true
GenerateProperties=true
ParentInConstructor=true
Singleton=true

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="haruna/haruna.conf" />
<group name="Subtitles">
<entry name="SubtitlesFolders" type="StringList">
<label>Subtitles folders</label>
<default>subs</default>
</entry>
<entry name="PreferredLanguage" type="String">
<label>Preferred language</label>
</entry>
<entry name="PreferredTrack" type="Int">
<label>Preferred track</label>
</entry>
</group>
</kcfg>

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
File=subtitlessettings.kcfg
ClassName=SubtitlesSettings
Mutators=true
DefaultValueGetters=true
GenerateProperties=true
ParentInConstructor=true
Singleton=true

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="haruna/haruna.conf" />
<include>QStandardPaths</include>
<group name="Video">
<entry name="ScreenshotFormat" type="String">
<label>Screenshot format</label>
<default>jpg</default>
</entry>
<entry name="ScreenshotTemplate" type="String">
<label>Screenshot template</label>
<code>
QString screenshotsFolder = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
screenshotsFolder.append("/Haruna screenshots/%F-%n");
</code>
<default code="true">screenshotsFolder</default>
</entry>
</group>
</kcfg>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save