├── .clang-format ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── support-request.md └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── cmake ├── CreateCpackPackage.cmake ├── GetGitVersion.cmake └── GetMpvWinDev.cmake ├── include ├── config.h ├── helpers │ ├── imgui.h │ ├── lang.h │ ├── nfd.h │ └── utils.h ├── mpv.h ├── player.h ├── theme.h ├── views │ ├── about.h │ ├── command_palette.h │ ├── context_menu.h │ ├── debug.h │ ├── quickview.h │ ├── settings.h │ └── view.h └── window.h ├── resources ├── icon.png ├── linux │ └── implay.desktop ├── macos │ ├── AppIcon.icns │ └── Info.plist.in ├── romfs │ ├── icon.png │ ├── lang │ │ ├── en-US.json │ │ ├── it-IT.json │ │ ├── uk-UA.json │ │ └── zh-Hans.json │ └── mpv │ │ ├── input.conf │ │ ├── mpv.conf │ │ └── osc.lua └── win32 │ ├── app.ico │ ├── app.manifest │ ├── app.rc.in │ └── wix │ └── patch.xml ├── screenshot ├── 1.jpg ├── 2.jpg └── 3.jpg ├── source ├── config.cpp ├── helpers │ ├── imgui.cpp │ ├── lang.cpp │ ├── nfd.cpp │ └── utils.cpp ├── main.cpp ├── mpv.cpp ├── player.cpp ├── theme.cpp ├── views │ ├── about.cpp │ ├── command_palette.cpp │ ├── context_menu.cpp │ ├── debug.cpp │ ├── quickview.cpp │ ├── settings.cpp │ └── view.cpp └── window.cpp └── third_party ├── fmt ├── CMakeLists.txt ├── LICENSE.rst ├── include │ └── fmt │ │ ├── args.h │ │ ├── chrono.h │ │ ├── color.h │ │ ├── compile.h │ │ ├── core.h │ │ ├── format-inl.h │ │ ├── format.h │ │ ├── os.h │ │ ├── ostream.h │ │ ├── printf.h │ │ ├── ranges.h │ │ ├── std.h │ │ └── xchar.h └── src │ ├── format.cc │ └── os.cc ├── glad ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include │ ├── GL │ │ └── gl.h │ ├── GLES3 │ │ └── gl3.h │ └── KHR │ │ └── khrplatform.h └── src │ ├── gl.c │ └── gles3.c ├── glfw ├── CMake │ ├── GenerateMappings.cmake │ └── modules │ │ ├── FindEpollShim.cmake │ │ ├── FindOSMesa.cmake │ │ ├── FindWaylandProtocols.cmake │ │ └── FindXKBCommon.cmake ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── cmake_uninstall.cmake.in ├── deps │ └── mingw │ │ ├── _mingw_dxhelper.h │ │ ├── dinput.h │ │ └── xinput.h ├── include │ └── GLFW │ │ ├── glfw3.h │ │ └── glfw3native.h └── src │ ├── CMakeLists.txt │ ├── cocoa_init.m │ ├── cocoa_joystick.h │ ├── cocoa_joystick.m │ ├── cocoa_monitor.m │ ├── cocoa_platform.h │ ├── cocoa_time.c │ ├── cocoa_window.m │ ├── context.c │ ├── egl_context.c │ ├── egl_context.h │ ├── glfw3.pc.in │ ├── glfw3Config.cmake.in │ ├── glfw_config.h.in │ ├── glx_context.c │ ├── glx_context.h │ ├── init.c │ ├── input.c │ ├── internal.h │ ├── linux_joystick.c │ ├── linux_joystick.h │ ├── mappings.h │ ├── mappings.h.in │ ├── monitor.c │ ├── nsgl_context.h │ ├── nsgl_context.m │ ├── null_init.c │ ├── null_joystick.c │ ├── null_joystick.h │ ├── null_monitor.c │ ├── null_platform.h │ ├── null_window.c │ ├── osmesa_context.c │ ├── osmesa_context.h │ ├── posix_thread.c │ ├── posix_thread.h │ ├── posix_time.c │ ├── posix_time.h │ ├── vulkan.c │ ├── wgl_context.c │ ├── wgl_context.h │ ├── win32_init.c │ ├── win32_joystick.c │ ├── win32_joystick.h │ ├── win32_monitor.c │ ├── win32_platform.h │ ├── win32_thread.c │ ├── win32_time.c │ ├── win32_window.c │ ├── window.c │ ├── wl_init.c │ ├── wl_monitor.c │ ├── wl_platform.h │ ├── wl_window.c │ ├── x11_init.c │ ├── x11_monitor.c │ ├── x11_platform.h │ ├── x11_window.c │ ├── xkb_unicode.c │ └── xkb_unicode.h ├── imgui ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── include │ ├── fonts │ │ ├── cascadia.h │ │ ├── fontawesome.h │ │ └── unifont.h │ ├── imconfig.h │ ├── imgui.h │ ├── imgui_freetype.h │ ├── imgui_impl_glfw.h │ ├── imgui_impl_opengl3.h │ ├── imgui_impl_opengl3_loader.h │ ├── imgui_internal.h │ ├── imstb_rectpack.h │ ├── imstb_textedit.h │ ├── imstb_truetype.h │ └── stb_image.h └── source │ ├── fonts │ ├── cascadia.c │ ├── fontawesome.c │ └── unifont.c │ ├── imgui.cpp │ ├── imgui_demo.cpp │ ├── imgui_draw.cpp │ ├── imgui_freetype.cpp │ ├── imgui_impl_glfw.cpp │ ├── imgui_impl_opengl3.cpp │ ├── imgui_tables.cpp │ └── imgui_widgets.cpp ├── inipp ├── CMakeLists.txt ├── LICENSE.txt └── include │ └── inipp.h ├── json ├── CMakeLists.txt ├── LICENSE.MIT └── include │ └── nlohmann │ └── json.hpp ├── libromfs ├── CMakeLists.txt ├── LICENSE ├── generator │ ├── CMakeLists.txt │ └── source │ │ └── main.cpp └── lib │ ├── CMakeLists.txt │ ├── include │ └── romfs │ │ └── romfs.hpp │ └── source │ └── romfs.cpp ├── nativefiledialog ├── CMakeLists.txt ├── LICENSE └── src │ ├── CMakeLists.txt │ ├── include │ ├── nfd.h │ └── nfd.hpp │ ├── nfd_cocoa.m │ ├── nfd_gtk.cpp │ ├── nfd_portal.cpp │ └── nfd_win.cpp └── natsort ├── CMakeLists.txt ├── include └── strnatcmp.h └── src └── strnatcmp.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | Language: Cpp 3 | ColumnLimit: 120 4 | IndentWidth: 2 5 | TabWidth: 2 6 | UseTab: Never 7 | SortIncludes: false -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | third_party/** linguist-vendored -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: tsl0922 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | - OS: [e.g. Windows 10] 28 | - Version [e.g. 22H2] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support Request 3 | about: Support request or question 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | Describe your problem or question here. -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: ["*"] 6 | pull_request: 7 | workflow_call: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | linux: 12 | runs-on: ubuntu-22.04 13 | name: Ubuntu 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Install dependencies 19 | run: | 20 | sudo add-apt-repository universe 21 | sudo apt-get update 22 | sudo apt-get install -y build-essential cmake ninja-build git pkg-config \ 23 | libfuse2 libgtk-3-dev libglfw3-dev libfreetype6-dev libmpv-dev 24 | - name: Build ImPlay 25 | env: 26 | DEPLOY_GTK_VERSION: 3 27 | run: | 28 | mkdir build && cd build 29 | cmake -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCREATE_PACKAGE=ON -G Ninja .. 30 | cmake --build . --target package 31 | cmake --install . --prefix AppDir/usr 32 | curl -LO https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage 33 | curl -LO https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh 34 | chmod +x linuxdeploy-x86_64.AppImage linuxdeploy-plugin-gtk.sh 35 | ./linuxdeploy-x86_64.AppImage --appdir $(pwd)/AppDir --plugin gtk --output appimage 36 | mv ImPlay*.AppImage $(echo ImPlay-*.deb | sed 's/deb/AppImage/') 37 | - uses: actions/upload-artifact@v4 38 | with: 39 | name: ImPlay-linux 40 | path: | 41 | build/ImPlay-*.AppImage 42 | build/ImPlay-*.deb 43 | macos: 44 | runs-on: macos-12 45 | name: macOS 46 | steps: 47 | - uses: actions/checkout@v4 48 | with: 49 | fetch-depth: 0 50 | - name: Install dependencies 51 | env: 52 | HOMEBREW_NO_AUTO_UPDATE: 1 53 | HOMEBREW_NO_INSTALL_CLEANUP: 1 54 | run: | 55 | brew install cmake ninja git mpv freetype2 pkg-config 56 | - name: Build ImPlay 57 | env: 58 | PKG_CONFIG_PATH: /usr/local/opt/libarchive/lib/pkgconfig 59 | run: | 60 | mkdir build && cd build 61 | cmake -DCMAKE_BUILD_TYPE=RELEASE -DUSE_PATCHED_GLFW=ON -DCREATE_PACKAGE=ON -G Ninja .. 62 | cmake --build . --target package 63 | - uses: actions/upload-artifact@v4 64 | with: 65 | name: ImPlay-macOS 66 | path: build/ImPlay-*.dmg 67 | win: 68 | runs-on: windows-2019 69 | name: Windows 70 | steps: 71 | - name: Prepare git 72 | run: | 73 | git config --global core.autocrlf false 74 | git config --global core.eol lf 75 | - uses: actions/checkout@v4 76 | with: 77 | fetch-depth: 0 78 | - uses: msys2/setup-msys2@v2 79 | with: 80 | msystem: mingw64 81 | install: >- 82 | base-devel 83 | git 84 | p7zip 85 | mingw-w64-x86_64-gcc 86 | mingw-w64-x86_64-cmake 87 | mingw-w64-x86_64-ninja 88 | mingw-w64-x86_64-freetype 89 | update: true 90 | - name: Build ImPlay 91 | shell: msys2 {0} 92 | run: | 93 | mkdir build && cd build 94 | cmake -DCMAKE_BUILD_TYPE=RELEASE -DUSE_PATCHED_GLFW=ON -DUSE_OPENGL_ES3=ON -DCREATE_PACKAGE=ON -G Ninja .. 95 | cmake --build . --target package 96 | - uses: actions/upload-artifact@v4 97 | with: 98 | name: ImPlay-win64 99 | path: | 100 | build/ImPlay-*.msi 101 | build/ImPlay-*.zip 102 | publish: 103 | needs: [linux, macos, win] 104 | runs-on: ubuntu-latest 105 | if: ${{ github.ref == 'refs/heads/main' }} 106 | steps: 107 | - uses: actions/checkout@v4 108 | - uses: actions/download-artifact@v4 109 | - run: | 110 | for f in ImPlay-*/*; do 111 | mv "$f" "${f/ImPlay-*-/ImPlay-dev-}" 112 | done 113 | - uses: rickstaa/action-create-tag@v1 114 | with: 115 | tag: continuous 116 | force_push_tag: true 117 | - uses: ncipollo/release-action@v1 118 | with: 119 | commit: ${{ github.sha }} 120 | tag: continuous 121 | artifacts: "ImPlay-dev-*" 122 | allowUpdates: true 123 | prerelease: true 124 | name: Continuous build 125 | body: | 126 | > WARNING: This is the DEV version, may contains bugs or unfinished features, [Stable version](https://github.com/tsl0922/ImPlay/releases/latest). 127 | # Install 128 | ## Windows 129 | - MSI 130 | - Download the msi executable 131 | - Run the MSI installer 132 | - Run ImPlay from Start Menu 133 | - Zip 134 | - Download the windows zip 135 | - Extract the zip 136 | - Run `ImPlay.exe` 137 | ## macOS 138 | - Download the dmg file 139 | - Double click the dmg to show it's contents 140 | - Drag ImPlay to `Applications` folder 141 | - Run ImPlay from Launchpad 142 | ## Linux 143 | - Debian Package 144 | - Download the deb file 145 | - Install: `sudo apt install ./ImPlay-*.deb` 146 | - Run `ImPlay` 147 | - AppImage 148 | - Download the AppImage file 149 | - Run `chmod u+x ImPlay-*.AppImage && ./ImPlay-*.AppImage` 150 | 151 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: ["*"] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | uses: ./.github/workflows/build.yml 11 | publish: 12 | needs: [build] 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/download-artifact@v4 17 | - uses: ncipollo/release-action@v1 18 | with: 19 | commit: ${{ github.sha }} 20 | artifacts: "ImPlay-*/*" 21 | body: | 22 | # Install 23 | ## Windows 24 | - MSI 25 | - Download the msi executable 26 | - Run the MSI installer 27 | - Run ImPlay from Start Menu 28 | - Zip 29 | - Download the windows zip 30 | - Extract the zip 31 | - Run `ImPlay.exe` 32 | ## macOS 33 | - Download the dmg file 34 | - Double click the dmg to show it's contents 35 | - Drag ImPlay to `Applications` folder 36 | - Run ImPlay from Launchpad 37 | ## Linux 38 | - Debian Package 39 | - Download the deb file 40 | - Install: `sudo apt install ./ImPlay-*.deb` 41 | - Run `ImPlay` 42 | - AppImage 43 | - Download the AppImage file 44 | - Run `chmod u+x ImPlay-*.AppImage && ./ImPlay-*.AppImage` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Libraries 15 | *.lib 16 | *.a 17 | *.la 18 | *.lo 19 | 20 | # Shared objects (inc. Windows DLLs) 21 | *.dll 22 | *.so 23 | *.so.* 24 | *.dylib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | *.i*86 31 | *.x86_64 32 | *.hex 33 | 34 | # Debug files 35 | *.dSYM/ 36 | *.su 37 | 38 | # Cmake files 39 | CMakeCache.txt 40 | CMakeFiles 41 | CMakeScripts 42 | cmake_install.cmake 43 | install_manifest.txt 44 | CTestTestfile.cmake 45 | build 46 | 47 | # Clion files 48 | .idea/ 49 | 50 | # VSCode files 51 | .vscode/ 52 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 4 | 5 | include(GetGitVersion) 6 | 7 | get_git_version(GIT_VERSION SEM_VER) 8 | 9 | project(ImPlay VERSION "${SEM_VER}") 10 | set(CMAKE_CXX_STANDARD 20) 11 | 12 | include(CMakeDependentOption) 13 | 14 | option(USE_OPENGL_ES3 "Compile with OpenGL ES 3.0 loader" OFF) 15 | option(USE_PATCHED_GLFW "Use patched GLFW to support additional features" OFF) 16 | option(CREATE_PACKAGE "Create binary packages with CPack" OFF) 17 | cmake_dependent_option(USE_MPV_WIN_BUILD "Use Prebuilt static mpv dll on Windows" ON "WIN32" OFF) 18 | cmake_dependent_option(USE_XDG_PORTAL "Use xdg-desktop-portal for file dialogs on Linux" OFF "UNIX;NOT APPLE" OFF) 19 | 20 | find_package(Threads REQUIRED) 21 | find_package(PkgConfig REQUIRED) 22 | 23 | if(USE_MPV_WIN_BUILD) 24 | include(GetMpvWinDev) 25 | get_mpv_win_dev(mpv_dev) 26 | else() 27 | pkg_search_module(MPV REQUIRED mpv>=0.33.0) 28 | endif() 29 | if(USE_PATCHED_GLFW) 30 | add_subdirectory(third_party/glfw) 31 | set(GLFW_LIBRARIES glfw) 32 | else() 33 | pkg_search_module(GLFW REQUIRED glfw3>=3.1) 34 | endif() 35 | if(USE_XDG_PORTAL) 36 | set(NFD_PORTAL ON CACHE BOOL "Use xdg-desktop-portal for file dialogs on Linux" FORCE) 37 | endif() 38 | 39 | set(LIBROMFS_PROJECT_NAME ${PROJECT_NAME}) 40 | set(LIBROMFS_RESOURCE_LOCATION "${CMAKE_SOURCE_DIR}/resources/romfs") 41 | set(OPENGL_LIBRARIES "glad") 42 | 43 | add_subdirectory(third_party/glad) 44 | add_subdirectory(third_party/fmt) 45 | add_subdirectory(third_party/natsort) 46 | add_subdirectory(third_party/json) 47 | add_subdirectory(third_party/inipp) 48 | add_subdirectory(third_party/imgui) 49 | add_subdirectory(third_party/nativefiledialog) 50 | add_subdirectory(third_party/libromfs) 51 | 52 | set(SOURCE_FILES 53 | source/helpers/imgui.cpp 54 | source/helpers/lang.cpp 55 | source/helpers/nfd.cpp 56 | source/helpers/utils.cpp 57 | source/views/view.cpp 58 | source/views/command_palette.cpp 59 | source/views/context_menu.cpp 60 | source/views/debug.cpp 61 | source/views/about.cpp 62 | source/views/quickview.cpp 63 | source/views/settings.cpp 64 | source/theme.cpp 65 | source/config.cpp 66 | source/mpv.cpp 67 | source/player.cpp 68 | source/window.cpp 69 | source/main.cpp 70 | ) 71 | set(INCLUDE_DIRS include ${MPV_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIR}) 72 | set(LINK_LIBS glad fmt natsort json inipp nfd imgui ${CMAKE_THREAD_LIBS_INIT} ${MPV_LIBRARIES} ${GLFW_LIBRARIES} ${LIBROMFS_LIBRARY}) 73 | 74 | if(WIN32) 75 | configure_file(${PROJECT_SOURCE_DIR}/resources/win32/app.rc.in ${PROJECT_BINARY_DIR}/app.rc @ONLY) 76 | list(APPEND SOURCE_FILES ${PROJECT_BINARY_DIR}/app.rc) 77 | endif() 78 | 79 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 80 | target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIRS}) 81 | target_link_directories(${PROJECT_NAME} PRIVATE ${MPV_LIBRARY_DIRS}) 82 | target_link_libraries(${PROJECT_NAME} PRIVATE ${LINK_LIBS}) 83 | target_compile_definitions(${PROJECT_NAME} PRIVATE 84 | APP_VERSION="${GIT_VERSION}" 85 | $<$:IMGUI_IMPL_OPENGL_ES3> 86 | $<$:GLFW_PATCHED> 87 | ) 88 | if(USE_MPV_WIN_BUILD) 89 | add_dependencies(${PROJECT_NAME} mpv_dev) 90 | endif() 91 | 92 | if(CREATE_PACKAGE) 93 | include(CreateCpackPackage) 94 | prepare_package() 95 | create_package() 96 | else() 97 | include(GNUInstallDirs) 98 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 99 | if(UNIX AND NOT APPLE) 100 | install(FILES ${PROJECT_SOURCE_DIR}/resources/linux/implay.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) 101 | install(FILES ${PROJECT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps RENAME implay.png) 102 | endif() 103 | endif() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![build](https://github.com/tsl0922/ImPlay/actions/workflows/build.yml/badge.svg)](https://github.com/tsl0922/ImPlay/actions/workflows/build.yml) 2 | [![GitHub Releases](https://img.shields.io/github/downloads/tsl0922/ImPlay/total)](https://github.com/tsl0922/ImPlay/releases) 3 | ![GitHub](https://img.shields.io/github/license/tsl0922/ImPlay) 4 | 5 | # ImPlay 6 | 7 | ImPlay is a Cross-Platform Desktop Media Player, built on top of [mpv](https://mpv.io) and [ImGui](https://github.com/ocornut/imgui). 8 | 9 | ImPlay aims to be mpv compatible, which means almost all mpv features from the [manual](https://mpv.io/manual) are (or will be) available. 10 | 11 | # Features 12 | 13 | - Highly compatible with mpv 14 | - GPU Video Decoding 15 | - High Quality Video Output 16 | - [Lua](https://mpv.io/manual/stable/#lua-scripting) and [Javascript](https://mpv.io/manual/stable/#javascript) Scripting 17 | - [User Scripts](https://github.com/mpv-player/mpv/wiki/User-Scripts) and [Config Files](https://mpv.io/manual/stable/#configuration-files) 18 | - [Command Line](https://mpv.io/manual/stable/#usage) Interface 19 | - [Keyboard / Mouse](https://mpv.io/manual/stable/#interactive-control) Control 20 | - [On Screen Controler](https://mpv.io/manual/stable/#on-screen-controller) (OSC) 21 | - Compatible with popular OSC scripts: [mpv-osc-modern](https://github.com/maoiscat/mpv-osc-modern), [thumbfast](https://github.com/po5/thumbfast) 22 | - Take Video Screenshot 23 | - Used as Image Viewer 24 | - Graphical User Interface 25 | - Context Menu with most commonly used commands 26 | - Command Palette to quickly search commands and keys 27 | - Quick Settings View with convenient controls 28 | - Playlist / Chapter Manager 29 | - Audio / Video / Subtitle Settings 30 | - Audio / Video Equalizer Support 31 | - Open Dialog for Media Files / Folders 32 | - Open Clipboard / DVD / Blu-ray / ISO Image 33 | - Shadow and Rounding effect for Interface 34 | - Notable additional features 35 | - Single Instance Mode 36 | - Space to play last file on IDLE 37 | - Play recently opened files 38 | - Scripting Developer Friendly 39 | - Visual view of mpv's internal properties 40 | - Console with completion, history support 41 | - Colorful mpv logs view with filter support 42 | - Cross platform: Window, Linux, macOS 43 | 44 | # Installation 45 | 46 | - Binary version: download from the [Releases](https://github.com/tsl0922/ImPlay/releases) page 47 | - Build from source: check the [Compiling](https://github.com/tsl0922/ImPlay/wiki/Compiling) document 48 | 49 | Read the [FAQ](https://github.com/tsl0922/ImPlay/wiki/FAQ). 50 | 51 | # Screenshots 52 | ### Context Menu 53 | 54 | ![screenshot](screenshot/1.jpg) 55 | 56 | ### Command Palette 57 | 58 | ![screenshot](screenshot/2.jpg) 59 | 60 | ### Quick Settings & Debug 61 | 62 | ![screenshot](screenshot/3.jpg) 63 | 64 | # Credits 65 | 66 | ImPlay uses the following projects, thanks to their authors and contributors. 67 | 68 | - [mpv](https://mpv.io): Command line video player 69 | - [imgui](https://github.com/ocornut/imgui): Bloat-free Graphical User interface for C++ with minimal dependencies 70 | - [stb_image.h](https://github.com/nothings/stb/blob/master/stb_image.h): public domain image loader 71 | - [glfw](https://www.glfw.org): an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan development on the desktop 72 | - [glad](https://glad.dav1d.de): Multi-Language GL/GLES/EGL/GLX/WGL Loader-Generator based on the official specs 73 | - [fmt](https://fmt.dev): A modern formatting library 74 | - [json](https://json.nlohmann.me): JSON for Modern C++ 75 | - [inipp](https://github.com/mcmtroffaes/inipp): Simple C++ ini parser 76 | - [libromfs](https://github.com/WerWolv/libromfs): Simple library for embedding static resources into C++ binaries using CMake 77 | - [nativefiledialog](https://github.com/btzy/nativefiledialog-extended): Cross platform (Windows, Mac, Linux) native file dialog library 78 | - [Cascadia Code](https://github.com/microsoft/cascadia-code) / [Font Awesome](https://fontawesome.com) / [Unifont](https://unifoundry.com/unifont.html): Fonts embeded in ImPlay 79 | 80 | # License 81 | 82 | [GPLv2](LICENSE.txt). 83 | -------------------------------------------------------------------------------- /cmake/CreateCpackPackage.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24.0") 3 | cmake_policy(SET CMP0135 NEW) 4 | endif() 5 | 6 | function(get_mpv_win_bin name) 7 | ExternalProject_Add(${name} 8 | URL https://downloads.sourceforge.net/mpv-player-windows/mpv-x86_64-20240818-git-a3baf94.7z 9 | URL_HASH SHA256=b49bdacfa67922ef58d4b121cf1ae7de814d55128e43838775f40fdbf4301a98 10 | DOWNLOAD_NO_PROGRESS ON 11 | UPDATE_COMMAND "" 12 | CONFIGURE_COMMAND "" 13 | BUILD_COMMAND "" 14 | INSTALL_COMMAND ${CMAKE_COMMAND} -E copy /mpv.com ${CMAKE_BINARY_DIR}/ImPlay.com 15 | COMMAND ${CMAKE_COMMAND} -E copy_directory /doc ${CMAKE_BINARY_DIR}/doc 16 | ) 17 | endfunction() 18 | 19 | function(get_yt_dlp_bin name) 20 | ExternalProject_Add(${name} 21 | URL https://github.com/yt-dlp/yt-dlp/releases/download/2024.08.06/yt-dlp.exe 22 | URL_HASH SHA256=468a6f8bf1d156ad173e000a40f696d4fbd69c5aa7360229329b9063a388e7d0 23 | DOWNLOAD_NO_PROGRESS ON 24 | DOWNLOAD_NO_EXTRACT ON 25 | UPDATE_COMMAND "" 26 | CONFIGURE_COMMAND "" 27 | BUILD_COMMAND "" 28 | INSTALL_COMMAND ${CMAKE_COMMAND} -E copy /yt-dlp.exe ${CMAKE_BINARY_DIR} 29 | ) 30 | endfunction() 31 | 32 | function(get_electron_bin name) 33 | ExternalProject_Add(${name} 34 | URL https://github.com/electron/electron/releases/download/v22.3.27/electron-v22.3.27-win32-x64.zip 35 | URL_HASH SHA256=ad723ed7dad32f9459f7a9de1fd6d718cf713c4809c2431503bea62ce8f786e6 36 | DOWNLOAD_NO_PROGRESS ON 37 | UPDATE_COMMAND "" 38 | CONFIGURE_COMMAND "" 39 | BUILD_COMMAND "" 40 | INSTALL_COMMAND ${CMAKE_COMMAND} -E copy /libEGL.dll ${CMAKE_BINARY_DIR} 41 | COMMAND ${CMAKE_COMMAND} -E copy /libGLESv2.dll ${CMAKE_BINARY_DIR} 42 | ) 43 | endfunction() 44 | 45 | macro(prepare_package) 46 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") 47 | cmake_policy(SET CMP0087 NEW) 48 | endif() 49 | 50 | if(WIN32) 51 | get_mpv_win_bin(mpv_bin) 52 | get_yt_dlp_bin(yt_dlp) 53 | add_dependencies(${PROJECT_NAME} mpv_bin yt_dlp) 54 | 55 | target_link_options(${PROJECT_NAME} PRIVATE -mwindows) 56 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION .) 57 | install(DIRECTORY ${CMAKE_BINARY_DIR}/doc DESTINATION .) 58 | install(FILES ${CMAKE_BINARY_DIR}/ImPlay.com DESTINATION .) 59 | install(FILES ${CMAKE_BINARY_DIR}/yt-dlp.exe DESTINATION .) 60 | 61 | if(USE_OPENGL_ES3) 62 | get_electron_bin(electron_bin) 63 | add_dependencies(${PROJECT_NAME} electron_bin) 64 | install(FILES ${CMAKE_BINARY_DIR}/libEGL.dll DESTINATION .) 65 | install(FILES ${CMAKE_BINARY_DIR}/libGLESv2.dll DESTINATION .) 66 | endif() 67 | 68 | install(CODE [[file(GET_RUNTIME_DEPENDENCIES 69 | EXECUTABLES $ 70 | RESOLVED_DEPENDENCIES_VAR _r_deps 71 | UNRESOLVED_DEPENDENCIES_VAR _u_deps 72 | DIRECTORIES $ENV{PATH} 73 | POST_EXCLUDE_REGEXES ".*system32/.*\\.dll" 74 | ) 75 | 76 | if(_u_deps) 77 | message(WARNING "Unresolved dependencies detected: ${_u_deps}!") 78 | endif() 79 | 80 | foreach(_file ${_r_deps}) 81 | file(INSTALL 82 | DESTINATION ${CMAKE_INSTALL_PREFIX} 83 | TYPE SHARED_LIBRARY 84 | FOLLOW_SYMLINK_CHAIN 85 | FILES "${_file}" 86 | ) 87 | endforeach()]]) 88 | elseif (APPLE) 89 | set_target_properties(${PROJECT_NAME} PROPERTIES 90 | BUILD_RPATH "@executable_path/../Frameworks" 91 | INSTALL_RPATH "@executable_path/../Frameworks" 92 | MACOSX_RPATH TRUE 93 | MACOSX_BUNDLE TRUE 94 | MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/resources/macos/Info.plist.in 95 | ) 96 | 97 | target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/resources/macos/AppIcon.icns) 98 | set_source_files_properties(${PROJECT_SOURCE_DIR}/resources/macos/AppIcon.icns 99 | PROPERTIES MACOSX_PACKAGE_LOCATION Resources 100 | ) 101 | 102 | install(TARGETS ${PROJECT_NAME} BUNDLE DESTINATION .) 103 | 104 | set(APP "\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app") 105 | set(DIRS "/usr/local/lib" "/lib" "/usr/lib") 106 | file(GLOB_RECURSE LIBS "${APP}/Contents/MacOS/*.dylib") 107 | install(CODE "include(BundleUtilities) 108 | fixup_bundle(\"${APP}\" \"${LIBS}\" \"${DIRS}\" IGNORE_ITEM Python) 109 | execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath 110 | \"@executable_path/../Frameworks/\" 111 | \"${APP}/Contents/MacOS/${PROJECT_NAME}\" 112 | )") 113 | else() 114 | include(GNUInstallDirs) 115 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 116 | install(FILES ${PROJECT_SOURCE_DIR}/resources/linux/implay.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) 117 | install(FILES ${PROJECT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps RENAME implay.png) 118 | endif() 119 | endmacro() 120 | 121 | macro(create_package) 122 | if(WIN32) 123 | set(CPACK_GENERATOR ZIP WIX) 124 | set(CPACK_WIX_PATCH_FILE "${PROJECT_SOURCE_DIR}/resources/win32/wix/patch.xml") 125 | set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/win32/app.ico") 126 | set(CPACK_WIX_UPGRADE_GUID "D7438EFE-D62A-4E94-A024-6E71AE1A7A63") 127 | set(CPACK_WIX_PROGRAM_MENU_FOLDER ".") 128 | set_property(INSTALL "$" PROPERTY CPACK_START_MENU_SHORTCUTS "ImPlay") 129 | elseif (APPLE) 130 | set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) 131 | set(MACOSX_BUNDLE_ICON_FILE "AppIcon") 132 | set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}") 133 | set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}") 134 | set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") 135 | set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.tsl0922.ImPlay") 136 | set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2022 tsl0922. All rights reserved." ) 137 | 138 | set(CPACK_GENERATOR DragNDrop) 139 | set(CPACK_BUNDLE_NAME ${PROJECT_NAME}) 140 | set(CPACK_BUNDLE_ICON ${PROJECT_SOURCE_DIR}/resources/macos/app.icns) 141 | set(CPACK_BUNDLE_PLIST ${CMAKE_BINARY_DIR}/ImHex.app/Contents/Info.plist) 142 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") 143 | set(CPACK_GENERATOR TGZ DEB) 144 | set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}") 145 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "tsl0922") 146 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS TRUE) 147 | else() 148 | set(CPACK_GENERATOR TGZ) 149 | endif() 150 | 151 | set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") 152 | set(CPACK_PACKAGE_VENDOR "tsl0922") 153 | set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) 154 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A Cross-Platform Desktop Media Player") 155 | set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE.txt") 156 | set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README.md") 157 | 158 | include(CPack) 159 | endmacro() -------------------------------------------------------------------------------- /cmake/GetGitVersion.cmake: -------------------------------------------------------------------------------- 1 | find_package(Git) 2 | 3 | function(get_git_version var1 var2) 4 | if(GIT_EXECUTABLE) 5 | execute_process( 6 | COMMAND ${GIT_EXECUTABLE} describe --tags --match "[0-9]*.[0-9]*.[0-9]*" --abbrev=8 7 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 8 | RESULT_VARIABLE status 9 | OUTPUT_VARIABLE GIT_VERSION 10 | ) 11 | if (${status}) 12 | set(GIT_VERSION "0.0.0") 13 | else() 14 | string(STRIP ${GIT_VERSION} GIT_VERSION) 15 | string(REGEX REPLACE "-[0-9]+-g" "-" GIT_VERSION ${GIT_VERSION}) 16 | endif() 17 | else() 18 | set(GIT_VERSION "0.0.0") 19 | endif() 20 | 21 | string(REGEX MATCH "^[0-9]+.[0-9]+.[0-9]+" SEM_VER "${GIT_VERSION}") 22 | 23 | message("-- Git Tag: ${GIT_VERSION}, Sem Ver: ${SEM_VER}") 24 | 25 | set(${var1} ${GIT_VERSION} PARENT_SCOPE) 26 | set(${var2} ${SEM_VER} PARENT_SCOPE) 27 | endfunction() -------------------------------------------------------------------------------- /cmake/GetMpvWinDev.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24.0") 3 | cmake_policy(SET CMP0135 NEW) 4 | endif() 5 | 6 | function(get_mpv_win_dev name) 7 | ExternalProject_Add(${name} 8 | URL https://downloads.sourceforge.net/mpv-player-windows/mpv-dev-x86_64-20240818-git-a3baf94.7z 9 | URL_HASH SHA256=1a7eb75247e06f4acf8e81a0d0bb1e8cacf8fc1a00de402d3cb4f22d19818ce8 10 | DOWNLOAD_NO_PROGRESS ON 11 | UPDATE_COMMAND "" 12 | CONFIGURE_COMMAND "" 13 | BUILD_COMMAND "" 14 | INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /include /include 15 | COMMAND ${CMAKE_COMMAND} -E copy /libmpv.dll.a 16 | COMMAND ${CMAKE_COMMAND} -E copy /libmpv-2.dll ${CMAKE_BINARY_DIR} 17 | ) 18 | ExternalProject_Get_property(${name} BINARY_DIR) 19 | set(MPV_DEV_DIR ${BINARY_DIR}) 20 | 21 | set(MPV_INCLUDE_DIRS ${MPV_DEV_DIR}/include PARENT_SCOPE) 22 | set(MPV_LIBRARY_DIRS ${MPV_DEV_DIR} PARENT_SCOPE) 23 | set(MPV_LIBRARIES mpv PARENT_SCOPE) 24 | endfunction() -------------------------------------------------------------------------------- /include/config.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ImPlay { 12 | struct ConfigData { 13 | struct Interface_ { 14 | std::string Lang = "en-US"; 15 | std::string Theme = "light"; 16 | float Scale = 0; 17 | int Fps = 30; 18 | bool Docking = false; 19 | bool Viewports = false; 20 | bool Rounding = true; 21 | bool Shadow = true; 22 | bool operator==(const Interface_&) const = default; 23 | } Interface; 24 | struct Mpv_ { 25 | bool UseConfig = false; 26 | bool UseWid = false; 27 | bool WatchLater = false; 28 | int Volume = 100; 29 | bool operator==(const Mpv_&) const = default; 30 | } Mpv; 31 | struct Window_ { 32 | bool Save = false; 33 | bool Single = false; 34 | int X = 0, Y = 0; 35 | int W = 0, H = 0; 36 | bool operator==(const Window_&) const = default; 37 | } Window; 38 | struct Font_ { 39 | std::string Path; 40 | int Size = 13; 41 | int GlyphRange = 0; 42 | bool operator==(const Font_&) const = default; 43 | } Font; 44 | struct Debug_ { 45 | std::string LogLevel = "status"; 46 | int LogLimit = 500; 47 | bool operator==(const Debug_&) const = default; 48 | } Debug; 49 | struct Recent_ { 50 | int Limit = 10; 51 | bool SpaceToPlayLast = false; 52 | bool operator==(const Recent_&) const = default; 53 | } Recent; 54 | bool operator==(const ConfigData&) const = default; 55 | }; 56 | 57 | class Config { 58 | public: 59 | Config(); 60 | ~Config() = default; 61 | 62 | enum GlyphRange_ { 63 | GlyphRange_Default = 0, 64 | GlyphRange_Chinese = 1 << 0, 65 | GlyphRange_Cyrillic = 1 << 1, 66 | GlyphRange_Japanese = 1 << 2, 67 | GlyphRange_Korean = 1 << 3, 68 | GlyphRange_Thai = 1 << 4, 69 | GlyphRange_Vietnamese = 1 << 5, 70 | }; 71 | 72 | struct RecentItem { 73 | std::string path; 74 | std::string title; 75 | bool operator==(const RecentItem&) const = default; 76 | }; 77 | 78 | void load(); 79 | void save(); 80 | 81 | std::string dir() const { return configDir; } 82 | std::string ipcSocket(); 83 | std::vector &getRecentFiles(); 84 | void addRecentFile(const std::string& path, const std::string& title); 85 | void clearRecentFiles(); 86 | 87 | const ImWchar* buildGlyphRanges(); 88 | 89 | ConfigData Data; 90 | bool FontReload = false; 91 | 92 | private: 93 | inipp::Ini ini; 94 | std::string configFile; 95 | std::string configDir; 96 | 97 | std::vector recentFiles; 98 | }; 99 | } // namespace ImPlay -------------------------------------------------------------------------------- /include/helpers/imgui.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | 7 | namespace ImGui { 8 | bool IsAnyKeyPressed(); 9 | void HalignCenter(const char* text); 10 | void TextCentered(const char* text, bool disabled = false); 11 | void TextEllipsis(const char* text, float maxWidth = 0); 12 | void Hyperlink(const char* label, const char* url); 13 | void HelpMarker(const char* desc); 14 | ImTextureID LoadTexture(const char* path, ImVec2* size = nullptr); 15 | } // namespace ImGui -------------------------------------------------------------------------------- /include/helpers/lang.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ImPlay { 10 | struct LangFont { 11 | std::string path; 12 | int size = 0; 13 | int glyph_range = 0; 14 | }; 15 | 16 | struct LangData { 17 | std::string code; 18 | std::string title; 19 | std::vector fonts; 20 | std::map entries; 21 | 22 | std::string get(std::string& key); 23 | }; 24 | 25 | class LangStr { 26 | public: 27 | explicit LangStr(std::string key); 28 | 29 | operator std::string() const; 30 | operator std::string_view() const; 31 | operator const char*() const; 32 | 33 | private: 34 | std::string m_str; 35 | }; 36 | 37 | inline std::string format_as(LangStr s) { return s; } 38 | 39 | const ImWchar* getLangGlyphRanges(); 40 | 41 | std::map& getLangs(); 42 | std::string& getLangFallback(); 43 | std::string& getLang(); 44 | 45 | std::string i18n(std::string key); 46 | template 47 | inline std::string i18n_a(std::string key, T... args) { 48 | return fmt::vformat(i18n(key), fmt::make_format_args(args...)); 49 | } 50 | inline LangStr operator""_i18n(const char* key, size_t) { return LangStr(key); } 51 | } // namespace ImPlay -------------------------------------------------------------------------------- /include/helpers/nfd.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace NFD { 12 | using Filters = std::vector>; 13 | 14 | std::optional openFile(Filters filters); 15 | std::optional> openFiles(Filters filters); 16 | std::optional openFolder(); 17 | } // namespace NFD -------------------------------------------------------------------------------- /include/helpers/utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "lang.h" 17 | 18 | namespace ImPlay { 19 | struct OptionParser { 20 | std::map options; 21 | std::vector paths; 22 | 23 | void parse(int argc, char** argv); 24 | bool check(std::string key, std::string value); 25 | }; 26 | 27 | inline float scaled(float n) { return n * ImGui::GetFontSize(); } 28 | inline ImVec2 scaled(const ImVec2& vector) { return vector * ImGui::GetFontSize(); } 29 | 30 | bool fileExists(std::string path); 31 | 32 | int openUrl(std::string url); 33 | void revealInFolder(std::string path); 34 | 35 | std::filesystem::path dataPath(); 36 | 37 | inline std::wstring UTF8ToWide(const std::string& str) { 38 | return std::wstring_convert, wchar_t>().from_bytes(str); 39 | } 40 | inline std::string WideToUTF8(const std::wstring& str) { 41 | return std::wstring_convert, wchar_t>().to_bytes(str); 42 | } 43 | 44 | inline std::string tolower(std::string s) { 45 | std::string str = s; 46 | std::transform(str.begin(), str.end(), str.begin(), ::tolower); 47 | return str; 48 | } 49 | inline std::string toupper(std::string s) { 50 | std::string str = s; 51 | std::transform(str.begin(), str.end(), str.begin(), ::toupper); 52 | return str; 53 | } 54 | 55 | inline bool iequals(std::string a, std::string b) { 56 | return std::equal(a.begin(), a.end(), b.begin(), b.end(), 57 | [](char a, char b) { return ::tolower(a) == ::tolower(b); }); 58 | } 59 | 60 | inline std::string trim(std::string s) { 61 | std::string str = s; 62 | const char* ws = " \t\n\r\f\v"; 63 | str.erase(0, str.find_first_not_of(ws)); 64 | str.erase(str.find_last_not_of(ws) + 1); 65 | return str; 66 | } 67 | 68 | inline std::string join(std::vector v, std::string_view sep) { 69 | return fmt::format("{}", fmt::join(v, sep)); 70 | } 71 | 72 | std::vector split(const std::string& str, const std::string& sep); 73 | 74 | bool findCase(std::string haystack, std::string needle); 75 | } // namespace ImPlay 76 | -------------------------------------------------------------------------------- /include/mpv.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace ImPlay { 13 | typedef void *(*GLAddrLoadFunc)(const char *name); 14 | class Mpv { 15 | public: 16 | Mpv(); 17 | ~Mpv(); 18 | 19 | using EventHandler = std::function; 20 | using LogHandler = std::function; 21 | using Callback = std::function; 22 | 23 | void init(GLAddrLoadFunc load, int64_t wid = 0); 24 | void render(int w, int h, int fbo = 0, bool flip = true); 25 | bool wantRender(); 26 | void reportSwap(); 27 | void waitEvent(double timeout = 0); 28 | void requestLog(const char *level, LogHandler handler); 29 | int loadConfig(const char *path); 30 | 31 | bool playing() { return playlistPlayingPos != -1; } 32 | bool allowDrag() { return windowDragging && !fullscreen; } 33 | 34 | Callback &wakeupCb() { return wakeupCb_; } 35 | Callback &updateCb() { return updateCb_; } 36 | 37 | inline int command(std::string args) { return mpv_command_string(mpv, args.c_str()); } 38 | inline int command(const char *args) { return mpv_command_string(mpv, args); } 39 | inline int command(const char *args[]) { return mpv_command_async(mpv, 0, args); } 40 | int commandv(const char *arg, ...); 41 | 42 | std::string property(const char *name) { 43 | char *data = mpv_get_property_string(mpv, name); 44 | std::string ret = data ? data : ""; 45 | mpv_free(data); 46 | return ret; 47 | } 48 | int property(const char *name, const char *data) { return mpv_set_property_string(mpv, name, data); } 49 | template 50 | T property(const char *name) { 51 | T data{0}; 52 | mpv_get_property(mpv, name, format, &data); 53 | return data; 54 | } 55 | template 56 | int property(const char *name, T data) { 57 | return mpv_set_property(mpv, name, format, static_cast(&data)); 58 | } 59 | 60 | int option(const char *name, const char *data) { return mpv_set_option_string(mpv, name, data); } 61 | template 62 | int option(const char *name, T data) { 63 | return mpv_set_option(mpv, name, format, static_cast(&data)); 64 | } 65 | 66 | void observeEvent(mpv_event_id event, const EventHandler &handler) { events.emplace_back(event, handler); } 67 | template 68 | void observeProperty(const std::string &name, const std::function &handler) { 69 | propertyEvents.emplace_back(name, format, [=](void *data) { handler(*(T *)data); }); 70 | mpv_observe_property(mpv, 0, name.c_str(), format); 71 | } 72 | 73 | struct TrackItem { 74 | int64_t id = -1; 75 | std::string type; 76 | std::string title; 77 | std::string lang; 78 | bool selected; 79 | }; 80 | 81 | struct PlayItem { 82 | int64_t id = -1; 83 | std::string title; 84 | std::filesystem::path path; 85 | 86 | inline std::string filename() const { return path.filename().string(); } 87 | }; 88 | 89 | struct ChapterItem { 90 | int64_t id = -1; 91 | std::string title; 92 | double time; 93 | }; 94 | 95 | struct BindingItem { 96 | std::string section; 97 | std::string key; 98 | std::string cmd; 99 | std::string comment; 100 | int64_t priority; 101 | bool weak; 102 | }; 103 | 104 | struct AudioDevice { 105 | std::string name; 106 | std::string description; 107 | }; 108 | 109 | // cached mpv properties 110 | std::vector playlist; 111 | std::vector chapters; 112 | std::vector tracks; 113 | std::vector audioDevices; 114 | std::vector bindings; 115 | std::vector profiles; 116 | std::string aid, vid, sid, sid2, audioDevice, cursorAutohide; 117 | int64_t chapter, volume, playlistPos, playlistPlayingPos, timePos; 118 | int64_t brightness, contrast, saturation, gamma, hue; 119 | double audioDelay, subDelay, subScale; 120 | bool pause, mute, fullscreen, sidv, sidv2, forceWindow, ontop; 121 | bool keepaspect, keepaspectWindow, windowDragging, autoResize; 122 | 123 | private: 124 | void eventLoop(); 125 | 126 | void observeProperties(); 127 | void initPlaylist(mpv_node &node); 128 | void initChapters(mpv_node &node); 129 | void initTracks(mpv_node &node); 130 | void initAudioDevices(mpv_node &node); 131 | void initBindings(mpv_node &node); 132 | void initProfiles(const char *payload); 133 | 134 | int64_t wid = 0; 135 | mpv_handle *main = nullptr; 136 | mpv_handle *mpv = nullptr; 137 | mpv_render_context *renderCtx = nullptr; 138 | LogHandler logHandler = nullptr; 139 | Callback wakeupCb_, updateCb_; 140 | 141 | std::vector> events; 142 | std::vector> propertyEvents; 143 | }; 144 | } // namespace ImPlay -------------------------------------------------------------------------------- /include/player.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #ifdef IMGUI_IMPL_OPENGL_ES3 10 | #include 11 | #else 12 | #include 13 | #endif 14 | #include "mpv.h" 15 | #include "config.h" 16 | #include "views/view.h" 17 | #include "views/about.h" 18 | #include "views/debug.h" 19 | #include "views/quickview.h" 20 | #include "views/settings.h" 21 | #include "views/context_menu.h" 22 | #include "views/command_palette.h" 23 | #include "helpers/imgui.h" 24 | #include "helpers/nfd.h" 25 | #include "helpers/utils.h" 26 | 27 | #define PLAYER_NAME "ImPlay" 28 | 29 | namespace ImPlay { 30 | class Player { 31 | public: 32 | explicit Player(Config *config); 33 | ~Player(); 34 | 35 | protected: 36 | bool init(std::map &options); 37 | void shutdown(); 38 | 39 | void initGui(); 40 | void exitGui(); 41 | void saveState(); 42 | void restoreState(); 43 | 44 | void loadFonts(); 45 | void render(); 46 | void renderVideo(); 47 | 48 | void onCursorEvent(double x, double y); 49 | void onScrollEvent(double x, double y); 50 | void onKeyEvent(std::string name); 51 | void onKeyDownEvent(std::string name); 52 | void onKeyUpEvent(std::string name); 53 | void onDropEvent(int count, const char **paths); 54 | 55 | Config *config = nullptr; 56 | Mpv *mpv = nullptr; 57 | int width = 1280, height = 720; 58 | 59 | private: 60 | void updateWindowState(); 61 | void initObservers(); 62 | void writeMpvConf(); 63 | 64 | void draw(); 65 | void drawVideo(); 66 | void execute(int n_args, const char **args_); 67 | 68 | void openFileDlg(NFD::Filters filters, bool append = false); 69 | void openFilesDlg(NFD::Filters filters, bool append = false); 70 | void openFolderDlg(bool append = false, bool disk = false); 71 | void openClipboard(); 72 | void openURL(); 73 | void openDvd(std::filesystem::path path); 74 | void openBluray(std::filesystem::path path); 75 | 76 | void playlistSort(bool reverse = false); 77 | 78 | void drawOpenURL(); 79 | void drawDialog(); 80 | void messageBox(std::string title, std::string msg); 81 | 82 | void load(std::vector files, bool append = false, bool disk = false); 83 | bool isMediaFile(std::string file); 84 | bool isSubtitleFile(std::string file); 85 | 86 | virtual int64_t GetWid() { return 0; } 87 | virtual GLAddrLoadFunc GetGLAddrFunc() = 0; 88 | virtual std::string GetClipboardString() = 0; 89 | virtual void GetMonitorSize(int *w, int *h) = 0; 90 | virtual int GetMonitorRefreshRate() = 0; 91 | virtual void GetFramebufferSize(int *w, int *h) = 0; 92 | virtual void MakeContextCurrent() = 0; 93 | virtual void DeleteContext() = 0; 94 | virtual void SwapBuffers() = 0; 95 | virtual void SetSwapInterval(int interval) = 0; 96 | virtual void BackendNewFrame() = 0; 97 | virtual void GetWindowScale(float *x, float *y) = 0; 98 | virtual void GetWindowPos(int *x, int *y) = 0; 99 | virtual void SetWindowPos(int x, int y) = 0; 100 | virtual void GetWindowSize(int *w, int *h) = 0; 101 | virtual void SetWindowSize(int w, int h) = 0; 102 | virtual void SetWindowTitle(std::string) = 0; 103 | virtual void SetWindowAspectRatio(int num, int den) = 0; 104 | virtual void SetWindowMaximized(bool m) = 0; 105 | virtual void SetWindowMinimized(bool m) = 0; 106 | virtual void SetWindowDecorated(bool d) = 0; 107 | virtual void SetWindowFloating(bool f) = 0; 108 | virtual void SetWindowFullscreen(bool fs) = 0; 109 | virtual void SetWindowShouldClose(bool c) = 0; 110 | 111 | bool idle = true; 112 | GLuint fbo = 0, tex = 0; 113 | ImTextureID logoTexture = nullptr; 114 | std::mutex contextLock; 115 | 116 | bool m_openURL = false; 117 | bool m_dialog = false; 118 | std::string m_dialog_title = "Dialog"; 119 | std::string m_dialog_msg = "Message"; 120 | 121 | Views::About *about; 122 | Views::Debug *debug; 123 | Views::Quickview *quickview; 124 | Views::Settings *settings; 125 | Views::ContextMenu *contextMenu; 126 | Views::CommandPalette *commandPalette; 127 | 128 | const std::vector videoTypes = { 129 | "yuv", "y4m", "m2ts", "m2t", "mts", "mtv", "ts", "tsv", "tsa", "tts", "trp", "mpeg", "mpg", 130 | "mpe", "mpeg2", "m1v", "m2v", "mp2v", "mpv", "mpv2", "mod", "vob", "vro", "evob", "evo", "mpeg4", 131 | "m4v", "mp4", "mp4v", "mpg4", "h264", "avc", "x264", "264", "hevc", "h265", "x265", "265", "ogv", 132 | "ogm", "ogx", "mkv", "mk3d", "webm", "avi", "vfw", "divx", "3iv", "xvid", "nut", "flic", "fli", 133 | "flc", "nsv", "gxf", "mxf", "wm", "wmv", "asf", "dvr-ms", "dvr", "wtv", "dv", "hdv", "flv", 134 | "f4v", "qt", "mov", "hdmov", "rm", "rmvb", "3gpp", "3gp", "3gp2", "3g2"}; 135 | const std::vector audioTypes = { 136 | "ac3", "a52", "eac3", "mlp", "dts", "dts-hd", "dtshd", "true-hd", "thd", "truehd", "thd+ac3", "tta", "pcm", 137 | "wav", "aiff", "aif", "aifc", "amr", "awb", "au", "snd", "lpcm", "ape", "wv", "shn", "adts", 138 | "adt", "mpa", "m1a", "m2a", "mp1", "mp2", "mp3", "m4a", "aac", "flac", "oga", "ogg", "opus", 139 | "spx", "mka", "weba", "wma", "f4a", "ra", "ram", "3ga", "3ga2", "ay", "gbs", "gym", "hes", 140 | "kss", "nsf", "nsfe", "sap", "spc", "vgm", "vgz", "m3u", "m3u8", "pls", "cue"}; 141 | const std::vector imageTypes = {"jpg", "bmp", "png", "gif", "webp"}; 142 | const std::vector subtitleTypes = {"srt", "ass", "idx", "sub", "sup", 143 | "ttxt", "txt", "ssa", "smi", "mks"}; 144 | 145 | const std::vector> mediaFilters = { 146 | {"Videos Files", fmt::format("{}", join(videoTypes, ","))}, 147 | {"Audio Files", fmt::format("{}", join(audioTypes, ","))}, 148 | {"Image Files", fmt::format("{}", join(imageTypes, ","))}, 149 | }; 150 | const std::vector> subtitleFilters = { 151 | {"Subtitle Files", fmt::format("{}", join(subtitleTypes, ","))}, 152 | }; 153 | const std::vector> isoFilters = { 154 | {"ISO Image Files", "iso"}, 155 | }; 156 | 157 | struct ContextGuard { 158 | public: 159 | inline ContextGuard(Player *p) : p(p) { 160 | p->contextLock.lock(); 161 | p->MakeContextCurrent(); 162 | } 163 | inline ~ContextGuard() { 164 | p->DeleteContext(); 165 | p->contextLock.unlock(); 166 | } 167 | 168 | // Disable copy from lvalue. 169 | ContextGuard(const ContextGuard &) = delete; 170 | ContextGuard &operator=(const ContextGuard &) = delete; 171 | 172 | private: 173 | Player *p; 174 | }; 175 | }; 176 | } // namespace ImPlay -------------------------------------------------------------------------------- /include/theme.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #include 5 | #include 6 | 7 | namespace ImGui { 8 | std::vector Themes(); 9 | void SetTheme(const char* theme, ImGuiStyle* dst = nullptr, bool rounding = true, bool shadow = true); 10 | void StyleColorsSpectrum(ImGuiStyle* dst = nullptr); 11 | void StyleColorsDracula(ImGuiStyle* dst = nullptr); 12 | void StyleColorsDeepDark(ImGuiStyle* dst = nullptr); 13 | } // namespace ImGui -------------------------------------------------------------------------------- /include/views/about.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include "view.h" 6 | 7 | namespace ImPlay::Views { 8 | class About : public View { 9 | public: 10 | void draw() override; 11 | }; 12 | } // namespace ImPlay::Views -------------------------------------------------------------------------------- /include/views/command_palette.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "view.h" 10 | 11 | namespace ImPlay::Views { 12 | class CommandPalette : public View { 13 | public: 14 | CommandPalette(Config *config, Mpv *mpv); 15 | 16 | struct CommandItem { 17 | std::string title; 18 | std::string tooltip; 19 | std::string label; 20 | int64_t id; 21 | std::function callback; 22 | }; 23 | 24 | void show(int n, const char **args); 25 | void draw() override; 26 | 27 | private: 28 | void drawInput(); 29 | void drawList(float width); 30 | void match(const std::string &input); 31 | 32 | std::map> providers; 33 | std::vector buffer = std::vector(1024, 0x00); 34 | std::vector items; 35 | std::vector matches; 36 | int64_t pos = -1; 37 | bool filtered = false; 38 | bool focusInput = false; 39 | bool justOpened = false; 40 | }; 41 | } // namespace ImPlay::Views -------------------------------------------------------------------------------- /include/views/context_menu.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include "view.h" 9 | 10 | namespace ImPlay::Views { 11 | class ContextMenu : public View { 12 | public: 13 | using View::View; 14 | enum ItemType { TYPE_NORMAL, TYPE_SEPARATOR, TYPE_SUBMENU, TYPE_CALLBACK }; 15 | struct Item { 16 | ItemType type; 17 | std::string cmd; 18 | std::string label; 19 | std::string icon; 20 | std::string shortcut; 21 | bool enabled = true; 22 | bool selected = false; 23 | std::vector submenu; 24 | std::function callback; 25 | }; 26 | 27 | void draw() override; 28 | std::vector build(); 29 | 30 | private: 31 | void draw(std::vector items); 32 | 33 | void drawPlaylist(std::vector items); 34 | void drawChapterlist(std::vector items); 35 | void drawTracklist(const char *type, const char *prop, std::string pos); 36 | void drawAudioDeviceList(); 37 | void drawThemelist(); 38 | void drawProfilelist(); 39 | void drawRecentFiles(); 40 | }; 41 | } // namespace ImPlay::Views -------------------------------------------------------------------------------- /include/views/debug.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "view.h" 10 | 11 | namespace ImPlay::Views { 12 | class Debug : public View { 13 | public: 14 | Debug(Config *config, Mpv *mpv); 15 | ~Debug(); 16 | 17 | void init(); 18 | void show() override; 19 | void draw() override; 20 | 21 | private: 22 | struct Console { 23 | explicit Console(Mpv *mpv); 24 | ~Console(); 25 | 26 | void init(const char *level, int limit); 27 | void draw(); 28 | 29 | void ClearLog(); 30 | void AddLog(const char *level, const char *fmt, ...); 31 | void ExecCommand(const char *command_line); 32 | int TextEditCallback(ImGuiInputTextCallbackData *data); 33 | void initCommands(std::vector> &commands); 34 | ImFont *GetFont(const char *str); 35 | 36 | ImVec4 LogColor(const char *level); 37 | 38 | const std::vector builtinCommands = {"HELP", "CLEAR", "HISTORY"}; 39 | 40 | struct LogItem { 41 | char *Str; 42 | const char *Lev; 43 | int FontIdx; 44 | }; 45 | 46 | Mpv *mpv; 47 | char InputBuf[256]; 48 | ImVector Items; 49 | ImVector Commands; 50 | ImVector History; 51 | int HistoryPos = -1; // -1: new line, 0..History.Size-1 browsing history. 52 | ImGuiTextFilter Filter; 53 | bool AutoScroll = true; 54 | bool ScrollToBottom = false; 55 | bool CommandInited = false; 56 | std::string LogLevel = "status"; 57 | int LogLimit = 500; 58 | }; 59 | 60 | void drawHeader(); 61 | void drawConsole(); 62 | void drawBindings(); 63 | void drawCommands(); 64 | void drawProperties(const char *title, std::vector &props); 65 | void drawPropNode(const char *name, mpv_node &node, int depth = 0); 66 | 67 | void initData(); 68 | 69 | Console *console = nullptr; 70 | std::string version; 71 | std::string m_node = "Console"; 72 | bool m_demo = false, m_metrics = false; 73 | 74 | std::vector options; 75 | std::vector properties; 76 | std::vector> commands; 77 | }; 78 | } // namespace ImPlay::Views -------------------------------------------------------------------------------- /include/views/quickview.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "view.h" 5 | 6 | namespace ImPlay::Views { 7 | class Quickview : public View { 8 | public: 9 | Quickview(Config *config, Mpv *mpv); 10 | 11 | void show(const char *tab = nullptr); 12 | void draw() override; 13 | 14 | private: 15 | #define FREQ_COUNT 10 16 | struct AudioEqItem { 17 | std::string name; 18 | int values[FREQ_COUNT]; 19 | std::string toFilter(const char *name, int channels = 2); 20 | }; 21 | 22 | struct Tab { 23 | std::string name; 24 | std::string title; 25 | std::function draw; 26 | }; 27 | 28 | void drawWindow(); 29 | void drawPopup(); 30 | void drawTabBar(); 31 | void drawTracks(const char *title, const char *type, const char *prop, std::string pos); 32 | void drawTracks(const char *type, const char *prop, std::string pos); 33 | void drawPlaylistTabContent(); 34 | void drawChaptersTabContent(); 35 | void drawVideoTabContent(); 36 | void drawAudioTabContent(); 37 | void drawSubtitleTabContent(); 38 | 39 | void drawAudioEq(); 40 | void applyAudioEq(bool osd = true); 41 | void toggleAudioEq(); 42 | void selectAudioEq(int index); 43 | void setAudioEqValue(int freqIndex, float gain); 44 | void updateAudioEqChannels(); 45 | 46 | void alignRight(const char *label); 47 | bool iconButton(const char *icon, const char *cmd, const char *tooltip = nullptr, bool sameline = true); 48 | bool toggleButton(const char *label, bool toggle, const char *tooltip = nullptr, ImGuiCol col = ImGuiCol_Button); 49 | bool toggleButton(bool toggle, const char *tooltip = nullptr, const char *id = nullptr); 50 | void emptyLabel(); 51 | void addTab(std::string name, std::string title, std::function draw) { tabs.push_back({name, title, draw}); } 52 | 53 | bool winMode = false; 54 | bool tabSwitched = false; 55 | std::string curTab = "Video"; 56 | std::vector tabs; 57 | 58 | const char *audioEqFreqs[FREQ_COUNT] = {"31.25", "62.5", "125", "250", "500", "1k", "2k", "4k", "8k", "16k"}; 59 | std::vector audioEqPresets = { 60 | {"views.quickview.audio.equalizer.presets.flat", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 61 | {"views.quickview.audio.equalizer.presets.classical", {0, 0, 0, 0, 0, 0, -41, -41, -41, -53}}, 62 | {"views.quickview.audio.equalizer.presets.club", {0, 0, 47, 29, 29, 29, 17, 0, 0, 0}}, 63 | {"views.quickview.audio.equalizer.presets.dance", {53, 41, 11, 0, 0, -29, -41, -41, 0, 0}}, 64 | {"views.quickview.audio.equalizer.presets.full_bass", {53, 53, 53, 29, 5, -23, -47, -59, -65, -65}}, 65 | {"views.quickview.audio.equalizer.presets.full_bass_and_treble", {41, 29, 0, -41, -23, 5, 47, 65, 71, 71}}, 66 | {"views.quickview.audio.equalizer.presets.full_treble", {-53, -53, -53, -23, 11, 65, 95, 95, 95, 95}}, 67 | {"views.quickview.audio.equalizer.presets.headphones", {23, 65, 29, -17, -11, 5, 23, 53, 71, 83}}, 68 | {"views.quickview.audio.equalizer.presets.large_hall", {59, 59, 29, 29, 0, -23, -23, -23, 0, 0}}, 69 | {"views.quickview.audio.equalizer.presets.live", {-23, 0, 23, 29, 29, 29, 23, 11, 11, 11}}, 70 | {"views.quickview.audio.equalizer.presets.party", {41, 41, 0, 0, 0, 0, 0, 0, 41, 41}}, 71 | {"views.quickview.audio.equalizer.presets.pop", {-5, 23, 41, 47, 29, 0, -11, -11, -5, -5}}, 72 | {"views.quickview.audio.equalizer.presets.reggae", {0, 0, 0, -29, 0, 35, 35, 0, 0, 0}}, 73 | {"views.quickview.audio.equalizer.presets.rock", {47, 23, 29, -47, -17, 23, 47, 65, 65, 65}}, 74 | {"views.quickview.audio.equalizer.presets.ska", {-11, -23, -23, 0, 23, 29, 47, 53, 65, 53}}, 75 | {"views.quickview.audio.equalizer.presets.soft", {23, 5, 0, -11, 0, 23, 47, 53, 65, 71}}, 76 | {"views.quickview.audio.equalizer.presets.soft_rock", {23, 23, 11, 0, -23, -29, -17, 0, 11, 47}}, 77 | {"views.quickview.audio.equalizer.presets.techno", {47, 29, 0, -29, -23, 0, 47, 53, 53, 47}}, 78 | {"views.quickview.audio.equalizer.custom", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 79 | }; 80 | int audioEqIndex = -1; 81 | int audioEqChannels = 2; 82 | bool audioEqEnabled = false; 83 | }; 84 | } // namespace ImPlay::Views -------------------------------------------------------------------------------- /include/views/settings.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include "view.h" 8 | 9 | namespace ImPlay::Views { 10 | class Settings : public View { 11 | public: 12 | using View::View; 13 | 14 | void show() override; 15 | void draw() override; 16 | void drawButtons(); 17 | void drawGeneralTab(); 18 | void drawInterfaceTab(); 19 | void drawFontTab(); 20 | 21 | private: 22 | ConfigData data; 23 | std::vector> appliers; 24 | }; 25 | } // namespace ImPlay::Views -------------------------------------------------------------------------------- /include/views/view.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include "config.h" 6 | #include "mpv.h" 7 | 8 | namespace ImPlay::Views { 9 | class View { 10 | public: 11 | View(Config *config, Mpv *mpv); 12 | View() = default; 13 | virtual ~View() = default; 14 | 15 | virtual void draw() = 0; 16 | virtual void show() { m_open = true; } 17 | 18 | protected: 19 | Config *config = nullptr; 20 | Mpv *mpv = nullptr; 21 | bool m_open = false; 22 | }; 23 | } // namespace ImPlay::Views -------------------------------------------------------------------------------- /include/window.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #pragma once 5 | #include "player.h" 6 | #ifdef _WIN32 7 | #include 8 | #include 9 | #define GLFW_EXPOSE_NATIVE_WIN32 10 | #elif defined(__APPLE__) 11 | #define GLFW_EXPOSE_NATIVE_COCOA 12 | #else 13 | #define GLFW_EXPOSE_NATIVE_X11 14 | #endif 15 | #include 16 | #include 17 | #include 18 | 19 | namespace ImPlay { 20 | class Window : Player { 21 | public: 22 | explicit Window(Config *config); 23 | ~Window(); 24 | 25 | bool init(OptionParser &parser); 26 | void run(); 27 | 28 | private: 29 | void initGLFW(); 30 | void wakeup(); 31 | void updateCursor(); 32 | 33 | void handleKey(int key, int action, int mods); 34 | void handleMouse(int button, int action, int mods); 35 | 36 | void sendKeyEvent(std::string key, bool action); 37 | void translateMod(std::vector &keys, int mods); 38 | 39 | void installCallbacks(GLFWwindow *target); 40 | GLFWmonitor *getMonitor(GLFWwindow *target); 41 | 42 | #ifdef _WIN32 43 | int64_t GetWid() override; 44 | #endif 45 | GLAddrLoadFunc GetGLAddrFunc() override; 46 | std::string GetClipboardString() override; 47 | void GetMonitorSize(int *w, int *h) override; 48 | int GetMonitorRefreshRate() override; 49 | void GetFramebufferSize(int *w, int *h) override; 50 | void MakeContextCurrent() override; 51 | void DeleteContext() override; 52 | void SwapBuffers() override; 53 | void SetSwapInterval(int interval) override; 54 | void BackendNewFrame() override; 55 | void GetWindowScale(float *x, float *y) override; 56 | void GetWindowPos(int *x, int *y) override; 57 | void SetWindowPos(int x, int y) override; 58 | void GetWindowSize(int *w, int *h) override; 59 | void SetWindowSize(int w, int h) override; 60 | void SetWindowTitle(std::string title) override; 61 | void SetWindowAspectRatio(int num, int den) override; 62 | void SetWindowMaximized(bool m) override; 63 | void SetWindowMinimized(bool m) override; 64 | void SetWindowDecorated(bool d) override; 65 | void SetWindowFloating(bool f) override; 66 | void SetWindowFullscreen(bool fs) override; 67 | void SetWindowShouldClose(bool c) override; 68 | 69 | GLFWwindow *window = nullptr; 70 | bool ownCursor = true; 71 | double lastInputAt = 0; 72 | #ifdef _WIN32 73 | bool borderless = false; 74 | bool oleOk = false; 75 | HWND hwnd; 76 | WNDPROC wndProcOld = nullptr; 77 | ITaskbarList3 *taskbarList = nullptr; 78 | 79 | void setupWin32Taskbar(); 80 | static LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 81 | #endif 82 | 83 | struct Waiter { 84 | public: 85 | void wait(); 86 | void wait_until(std::chrono::steady_clock::time_point time); 87 | void notify(); 88 | 89 | private: 90 | std::mutex lock; 91 | std::condition_variable cond; 92 | bool notified = false; 93 | }; 94 | 95 | struct Waiter videoWaiter; 96 | 97 | // clang-format off 98 | const std::map keyMappings = { 99 | {GLFW_KEY_SPACE, "SPACE"}, {GLFW_KEY_APOSTROPHE, "'"}, 100 | {GLFW_KEY_COMMA, ","}, {GLFW_KEY_MINUS, "-"}, 101 | {GLFW_KEY_PERIOD, "."}, {GLFW_KEY_SLASH, "/"}, 102 | 103 | {GLFW_KEY_0, "0"}, {GLFW_KEY_1, "1"}, {GLFW_KEY_2, "2"}, 104 | {GLFW_KEY_3, "3"}, {GLFW_KEY_4, "4"}, {GLFW_KEY_5, "5"}, 105 | {GLFW_KEY_6, "6"}, {GLFW_KEY_7, "7"}, {GLFW_KEY_8, "8"}, 106 | {GLFW_KEY_9, "9"}, 107 | 108 | {GLFW_KEY_SEMICOLON, ";"}, {GLFW_KEY_EQUAL, "="}, 109 | 110 | {GLFW_KEY_A, "a"}, {GLFW_KEY_B, "b"}, {GLFW_KEY_C, "c"}, 111 | {GLFW_KEY_D, "d"}, {GLFW_KEY_E, "e"}, {GLFW_KEY_F, "f"}, 112 | {GLFW_KEY_G, "g"}, {GLFW_KEY_H, "h"}, {GLFW_KEY_I, "i"}, 113 | {GLFW_KEY_J, "j"}, {GLFW_KEY_K, "k"}, {GLFW_KEY_L, "l"}, 114 | {GLFW_KEY_M, "m"}, {GLFW_KEY_N, "n"}, {GLFW_KEY_O, "o"}, 115 | {GLFW_KEY_P, "p"}, {GLFW_KEY_Q, "q"}, {GLFW_KEY_R, "r"}, 116 | {GLFW_KEY_S, "s"}, {GLFW_KEY_T, "t"}, {GLFW_KEY_U, "u"}, 117 | {GLFW_KEY_V, "v"}, {GLFW_KEY_W, "w"}, {GLFW_KEY_X, "x"}, 118 | {GLFW_KEY_Y, "y"}, {GLFW_KEY_Z, "z"}, 119 | 120 | {GLFW_KEY_LEFT_BRACKET, "["}, {GLFW_KEY_BACKSLASH, "\\"}, 121 | {GLFW_KEY_RIGHT_BRACKET, "]"}, {GLFW_KEY_GRAVE_ACCENT, "`"}, 122 | {GLFW_KEY_ESCAPE, "ESC"}, {GLFW_KEY_ENTER, "ENTER"}, 123 | {GLFW_KEY_TAB, "TAB"}, {GLFW_KEY_BACKSPACE, "BS"}, 124 | {GLFW_KEY_INSERT, "INS"}, {GLFW_KEY_DELETE, "DEL"}, 125 | {GLFW_KEY_RIGHT, "RIGHT"}, {GLFW_KEY_LEFT, "LEFT"}, 126 | {GLFW_KEY_DOWN, "DOWN"}, {GLFW_KEY_UP, "UP"}, 127 | {GLFW_KEY_PAGE_UP, "PGUP"}, {GLFW_KEY_PAGE_DOWN, "PGDWN"}, 128 | {GLFW_KEY_HOME, "HOME"}, {GLFW_KEY_END, "END"}, 129 | {GLFW_KEY_PRINT_SCREEN, "PRINT"}, {GLFW_KEY_PAUSE, "PAUSE"}, 130 | 131 | {GLFW_KEY_F1, "F1"}, {GLFW_KEY_F2, "F2"}, {GLFW_KEY_F3, "F3"}, 132 | {GLFW_KEY_F4, "F4"}, {GLFW_KEY_F5, "F5"}, {GLFW_KEY_F6, "F6"}, 133 | {GLFW_KEY_F7, "F7"}, {GLFW_KEY_F8, "F8"}, {GLFW_KEY_F9, "F9"}, 134 | {GLFW_KEY_F10, "F10"}, {GLFW_KEY_F11, "F11"}, {GLFW_KEY_F12, "F12"}, 135 | {GLFW_KEY_F13, "F13"}, {GLFW_KEY_F14, "F14"}, {GLFW_KEY_F15, "F15"}, 136 | {GLFW_KEY_F16, "F16"}, {GLFW_KEY_F17, "F17"}, {GLFW_KEY_F18, "F18"}, 137 | {GLFW_KEY_F19, "F19"}, {GLFW_KEY_F20, "F20"}, {GLFW_KEY_F21, "F21"}, 138 | {GLFW_KEY_F22, "F22"}, {GLFW_KEY_F23, "F23"}, {GLFW_KEY_F24, "F24"}, 139 | 140 | {GLFW_KEY_KP_0, "KP0"}, {GLFW_KEY_KP_1, "KP1"}, {GLFW_KEY_KP_2, "KP2"}, 141 | {GLFW_KEY_KP_3, "KP3"}, {GLFW_KEY_KP_4, "KP4"}, {GLFW_KEY_KP_5, "KP5"}, 142 | {GLFW_KEY_KP_6, "KP6"}, {GLFW_KEY_KP_7, "KP7"}, {GLFW_KEY_KP_8, "KP8"}, 143 | {GLFW_KEY_KP_9, "KP9"}, {GLFW_KEY_KP_ENTER, "KP_ENTER"}, 144 | }; 145 | 146 | const std::map shiftMappings = { 147 | {GLFW_KEY_0, ")"}, {GLFW_KEY_1, "!"}, {GLFW_KEY_2, "@"}, 148 | {GLFW_KEY_3, "#"}, {GLFW_KEY_4, "$"}, {GLFW_KEY_5, "%"}, 149 | {GLFW_KEY_6, "^"}, {GLFW_KEY_7, "&"}, {GLFW_KEY_8, "*"}, 150 | {GLFW_KEY_9, "("}, {GLFW_KEY_MINUS, "_"}, {GLFW_KEY_EQUAL, "+"}, 151 | {GLFW_KEY_LEFT_BRACKET, "{"}, {GLFW_KEY_RIGHT_BRACKET, "}"}, 152 | {GLFW_KEY_BACKSLASH, "|"}, {GLFW_KEY_SEMICOLON, ":"}, 153 | {GLFW_KEY_APOSTROPHE, "\""}, {GLFW_KEY_COMMA, "<"}, 154 | {GLFW_KEY_PERIOD, ">"}, {GLFW_KEY_SLASH, "?"}, 155 | }; 156 | 157 | const std::map mbtnMappings = { 158 | {GLFW_MOUSE_BUTTON_LEFT, "MBTN_LEFT"}, {GLFW_MOUSE_BUTTON_MIDDLE, "MBTN_MID"}, 159 | {GLFW_MOUSE_BUTTON_RIGHT, "MBTN_RIGHT"}, {GLFW_MOUSE_BUTTON_4, "MP_MBTN_BACK"}, 160 | {GLFW_MOUSE_BUTTON_5, "MP_MBTN_FORWARD"}, 161 | }; 162 | // clang-format on 163 | }; 164 | } // namespace ImPlay -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsl0922/ImPlay/d3f85011b6a89cf85a8ba310e56f51bbaf62208d/resources/icon.png -------------------------------------------------------------------------------- /resources/linux/implay.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=ImPlay 4 | GenericName=Media Player 5 | Comment=Play videos 6 | Icon=implay 7 | Exec=ImPlay %U 8 | Terminal=false 9 | Categories=AudioVideo;Audio;Video;Player;TV; 10 | 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; 11 | X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb,srt,rist 12 | StartupWMClass=ImPlay 13 | -------------------------------------------------------------------------------- /resources/macos/AppIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsl0922/ImPlay/d3f85011b6a89cf85a8ba310e56f51bbaf62208d/resources/macos/AppIcon.icns -------------------------------------------------------------------------------- /resources/romfs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsl0922/ImPlay/d3f85011b6a89cf85a8ba310e56f51bbaf62208d/resources/romfs/icon.png -------------------------------------------------------------------------------- /resources/romfs/mpv/mpv.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Example mpv configuration file 3 | # 4 | # Warning: 5 | # 6 | # The commented example options usually do _not_ set the default values. Call 7 | # mpv with --list-options to see the default values for most options. There is 8 | # no builtin or example mpv.conf with all the defaults. 9 | # 10 | # 11 | # Configuration files are read system-wide from /usr/local/etc/mpv.conf 12 | # and per-user from ~/.config/mpv/mpv.conf, where per-user settings override 13 | # system-wide settings, all of which are overridden by the command line. 14 | # 15 | # Configuration file settings and the command line options use the same 16 | # underlying mechanisms. Most options can be put into the configuration file 17 | # by dropping the preceding '--'. See the man page for a complete list of 18 | # options. 19 | # 20 | # Lines starting with '#' are comments and are ignored. 21 | # 22 | # See the CONFIGURATION FILES section in the man page 23 | # for a detailed description of the syntax. 24 | # 25 | # Profiles should be placed at the bottom of the configuration file to ensure 26 | # that settings wanted as defaults are not restricted to specific profiles. 27 | 28 | ################## 29 | # video settings # 30 | ################## 31 | 32 | # Start in fullscreen mode by default. 33 | #fs=yes 34 | 35 | # force starting with centered window 36 | #geometry=50%:50% 37 | 38 | # don't allow a new window to have a size larger than 90% of the screen size 39 | #autofit-larger=90%x90% 40 | 41 | # Do not close the window on exit. 42 | #keep-open=yes 43 | 44 | # Do not wait with showing the video window until it has loaded. (This will 45 | # resize the window once video is loaded. Also always shows a window with 46 | # audio.) 47 | #force-window=immediate 48 | 49 | # Disable the On Screen Controller (OSC). 50 | #osc=no 51 | 52 | # Keep the player window on top of all other windows. 53 | #ontop=yes 54 | 55 | # Specify fast video rendering preset (for --vo= only) 56 | # Recommended for mobile devices or older hardware with limited processing power 57 | #profile=fast 58 | 59 | # Specify high quality video rendering preset (for --vo= only) 60 | # Offers superior image fidelity and visual quality for an enhanced viewing 61 | # experience on capable hardware 62 | #profile=high-quality 63 | 64 | # Force video to lock on the display's refresh rate, and change video and audio 65 | # speed to some degree to ensure synchronous playback - can cause problems 66 | # with some drivers and desktop environments. 67 | #video-sync=display-resample 68 | 69 | # Enable hardware decoding if available. Often, this does not work with all 70 | # video outputs, but should work well with default settings on most systems. 71 | # If performance or energy usage is an issue, forcing the vdpau or vaapi VOs 72 | # may or may not help. 73 | #hwdec=auto 74 | 75 | ################## 76 | # audio settings # 77 | ################## 78 | 79 | # Specify default audio device. You can list devices with: --audio-device=help 80 | # The option takes the device string (the stuff between the '...'). 81 | #audio-device=alsa/default 82 | 83 | # Do not filter audio to keep pitch when changing playback speed. 84 | #audio-pitch-correction=no 85 | 86 | # Output 5.1 audio natively, and upmix/downmix audio with a different format. 87 | #audio-channels=5.1 88 | # Disable any automatic remix, _if_ the audio output accepts the audio format. 89 | # of the currently played file. See caveats mentioned in the manpage. 90 | # (The default is "auto-safe", see manpage.) 91 | #audio-channels=auto 92 | 93 | ################## 94 | # other settings # 95 | ################## 96 | 97 | # Pretend to be a web browser. Might fix playback with some streaming sites, 98 | # but also will break with shoutcast streams. 99 | #user-agent="Mozilla/5.0" 100 | 101 | # cache settings 102 | # 103 | # Use a large seekable RAM cache even for local input. 104 | #cache=yes 105 | # 106 | # Use extra large RAM cache (needs cache=yes to make it useful). 107 | #demuxer-max-bytes=500M 108 | #demuxer-max-back-bytes=100M 109 | # 110 | # Disable the behavior that the player will pause if the cache goes below a 111 | # certain fill size. 112 | #cache-pause=no 113 | # 114 | # Store cache payload on the hard disk instead of in RAM. (This may negatively 115 | # impact performance unless used for slow input such as network.) 116 | #cache-dir=~/.cache/ 117 | #cache-on-disk=yes 118 | 119 | # Display English subtitles if available. 120 | #slang=en 121 | 122 | # Play Finnish audio if available, fall back to English otherwise. 123 | #alang=fi,en 124 | 125 | # Change subtitle encoding. For Arabic subtitles use 'cp1256'. 126 | # If the file seems to be valid UTF-8, prefer UTF-8. 127 | # (You can add '+' in front of the codepage to force it.) 128 | #sub-codepage=cp1256 129 | 130 | # You can also include other configuration files. 131 | #include=/path/to/the/file/you/want/to/include 132 | 133 | ############ 134 | # Profiles # 135 | ############ 136 | 137 | # The options declared as part of profiles override global default settings, 138 | # but only take effect when the profile is active. 139 | 140 | # The following profile can be enabled on the command line with: --profile=eye-cancer 141 | 142 | #[eye-cancer] 143 | #sharpen=5 144 | -------------------------------------------------------------------------------- /resources/win32/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsl0922/ImPlay/d3f85011b6a89cf85a8ba310e56f51bbaf62208d/resources/win32/app.ico -------------------------------------------------------------------------------- /resources/win32/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | ImPlay 10 | 11 | 12 | UTF-8 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/win32/app.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define VERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 4 | #define VERSION_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.0\0" 5 | 6 | GLFW_ICON ICON "@CMAKE_CURRENT_SOURCE_DIR@/resources/win32/app.ico" 7 | 8 | VS_VERSION_INFO VERSIONINFO 9 | FILEVERSION VERSION 10 | PRODUCTVERSION VERSION 11 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 12 | FILEFLAGS 0 13 | FILEOS VOS__WINDOWS32 14 | FILETYPE VFT_APP 15 | FILESUBTYPE VFT_UNKNOWN 16 | BEGIN 17 | BLOCK "StringFileInfo" 18 | BEGIN 19 | BLOCK "040904b0" 20 | BEGIN 21 | VALUE "FileDescription", "ImPlay\0" 22 | VALUE "FileVersion", VERSION_STR 23 | VALUE "InternalName", "ImPlay\0" 24 | VALUE "ProductName", "ImPlay\0" 25 | VALUE "OriginalFilename", "ImPlay.exe\0" 26 | VALUE "ProductVersion", VERSION_STR 27 | VALUE "LegalCopyright", "Copyright (C) 2022-2023 tsl0922\0" 28 | END 29 | END 30 | 31 | BLOCK "VarFileInfo" 32 | BEGIN 33 | VALUE "Translation", 0x409, 1200 34 | END 35 | END 36 | 37 | 1 RT_MANIFEST "@CMAKE_CURRENT_SOURCE_DIR@/resources/win32/app.manifest" -------------------------------------------------------------------------------- /screenshot/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsl0922/ImPlay/d3f85011b6a89cf85a8ba310e56f51bbaf62208d/screenshot/1.jpg -------------------------------------------------------------------------------- /screenshot/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsl0922/ImPlay/d3f85011b6a89cf85a8ba310e56f51bbaf62208d/screenshot/2.jpg -------------------------------------------------------------------------------- /screenshot/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsl0922/ImPlay/d3f85011b6a89cf85a8ba310e56f51bbaf62208d/screenshot/3.jpg -------------------------------------------------------------------------------- /source/helpers/imgui.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #define STB_IMAGE_IMPLEMENTATION 5 | #include 6 | #ifdef IMGUI_IMPL_OPENGL_ES3 7 | #include 8 | #else 9 | #include 10 | #endif 11 | #include 12 | #include "helpers/utils.h" 13 | #include "helpers/imgui.h" 14 | 15 | bool ImGui::IsAnyKeyPressed() { 16 | for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; ++key) 17 | if (ImGui::IsKeyPressed((ImGuiKey)key)) return true; 18 | return false; 19 | } 20 | 21 | void ImGui::HalignCenter(const char* text) { 22 | ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize(text).x) * 0.5f); 23 | } 24 | 25 | void ImGui::TextCentered(const char* text, bool disabled) { 26 | ImGui::HalignCenter(text); 27 | if (disabled) 28 | ImGui::TextDisabled("%s", text); 29 | else 30 | ImGui::Text("%s", text); 31 | } 32 | 33 | void ImGui::TextEllipsis(const char* text, float maxWidth) { 34 | if (maxWidth == 0) maxWidth = ImGui::GetContentRegionAvail().x; 35 | ImGuiStyle style = ImGui::GetStyle(); 36 | ImGuiWindow* window = ImGui::GetCurrentWindow(); 37 | ImVec2 textSize = ImGui::CalcTextSize(text); 38 | ImVec2 min = ImGui::GetCursorScreenPos(); 39 | ImVec2 max = min + ImVec2(maxWidth - style.FramePadding.x, textSize.y + style.FramePadding.y); 40 | ImRect textRect(min, max); 41 | ImGui::ItemSize(textRect); 42 | if (ImGui::ItemAdd(textRect, window->GetID(text))) 43 | ImGui::RenderTextEllipsis(ImGui::GetWindowDrawList(), min, max, max.x, max.x, text, nullptr, &textSize); 44 | } 45 | 46 | void ImGui::Hyperlink(const char* label, const char* url) { 47 | auto style = ImGui::GetStyle(); 48 | ImGui::PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_CheckMark]); 49 | ImGui::Text("%s", label ? label : url); 50 | if (ImGui::IsItemHovered()) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); 51 | if (ImGui::IsItemClicked()) ImPlay::openUrl(url); 52 | ImGui::PopStyleColor(); 53 | } 54 | 55 | void ImGui::HelpMarker(const char* desc) { 56 | ImGui::TextDisabled("(?)"); 57 | if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) { 58 | ImGui::PushTextWrapPos(35 * ImGui::GetFontSize()); 59 | ImGui::TextUnformatted(desc); 60 | ImGui::PopTextWrapPos(); 61 | ImGui::EndTooltip(); 62 | } 63 | } 64 | 65 | ImTextureID ImGui::LoadTexture(const char* path, ImVec2* size) { 66 | int w, h; 67 | auto icon = romfs::get(path); 68 | auto data = stbi_load_from_memory(reinterpret_cast(icon.data()), icon.size(), &w, &h, NULL, 4); 69 | if (data == nullptr) return nullptr; 70 | 71 | GLuint texture; 72 | glGenTextures(1, &texture); 73 | glBindTexture(GL_TEXTURE_2D, texture); 74 | 75 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 76 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 77 | 78 | #if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__) 79 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 80 | #endif 81 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 82 | 83 | glBindTexture(GL_TEXTURE_2D, 0); 84 | stbi_image_free(data); 85 | 86 | if (size != nullptr) { 87 | size->x = w; 88 | size->y = h; 89 | } 90 | 91 | return reinterpret_cast(static_cast(texture)); 92 | } -------------------------------------------------------------------------------- /source/helpers/lang.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #include 5 | #include 6 | #include 7 | #include "helpers/utils.h" 8 | #include "helpers/lang.h" 9 | 10 | namespace ImPlay { 11 | std::string LangData::get(std::string& key) { 12 | auto it = entries.find(key); 13 | if (it != entries.end()) return it->second; 14 | return key; 15 | } 16 | 17 | LangStr::LangStr(std::string key) { m_str = i18n(key); } 18 | 19 | LangStr::operator std::string() const { return m_str; } 20 | 21 | LangStr::operator std::string_view() const { return m_str; } 22 | 23 | LangStr::operator const char*() const { return m_str.c_str(); } 24 | 25 | const ImWchar* getLangGlyphRanges() { 26 | static ImVector glyphRanges; 27 | if (glyphRanges.empty()) { 28 | ImFontGlyphRangesBuilder builder; 29 | for (auto& [code, lang] : getLangs()) { 30 | builder.AddText(lang.title.c_str()); 31 | for (auto& [key, value] : lang.entries) builder.AddText(value.c_str()); 32 | } 33 | builder.BuildRanges(&glyphRanges); 34 | } 35 | return &glyphRanges[0]; 36 | } 37 | 38 | static std::pair parseLang(nlohmann::json& j) { 39 | LangData lang; 40 | const auto& code = j["code"]; 41 | const auto& title = j["title"]; 42 | const auto& fonts = j["fonts"]; 43 | const auto& entries = j["entries"]; 44 | if (!code.is_string() && !title.is_string() && !entries.is_object()) return {lang, false}; 45 | if (j.contains("fallback")) { 46 | const auto& fallback = j["fallback"]; 47 | if (fallback.is_boolean() && fallback.get()) getLangFallback() = code.get(); 48 | } 49 | lang.code = code.get(); 50 | lang.title = title.get(); 51 | if (fonts.is_array()) { 52 | for (auto& [key, value] : fonts.items()) { 53 | auto& path = value["path"]; 54 | if (!path.is_string()) continue; 55 | LangFont font{path.get()}; 56 | auto& size = value["size"]; 57 | auto& glyph_range = value["glyph-range"]; 58 | if (size.is_number_integer()) font.size = size.get(); 59 | if (glyph_range.is_number_integer()) font.glyph_range = glyph_range.get(); 60 | lang.fonts.emplace_back(font); 61 | } 62 | } 63 | for (auto& [key, value] : entries.items()) { 64 | if (key == "" || !value.is_string()) continue; 65 | lang.entries[key] = value.get(); 66 | } 67 | return {lang, true}; 68 | } 69 | 70 | std::map& getLangs() { 71 | static std::map langs; 72 | static bool loaded = false; 73 | if (loaded) return langs; 74 | 75 | auto langDir = dataPath() / "lang"; 76 | if (std::filesystem::exists(langDir)) { 77 | for (auto& entry : std::filesystem::directory_iterator(langDir)) { 78 | if (entry.is_directory() || entry.path().extension() != ".json") continue; 79 | std::ifstream f(entry.path()); 80 | auto j = nlohmann::json::parse(f); 81 | if (auto [lang, ok] = parseLang(j); ok) langs.insert({lang.code, lang}); 82 | } 83 | } 84 | 85 | for (auto& path : romfs::list("lang")) { 86 | auto file = romfs::get(path); 87 | auto j = nlohmann::json::parse(file.data(), file.data() + file.size()); 88 | if (auto [lang, ok] = parseLang(j); ok) langs.insert({lang.code, lang}); 89 | } 90 | 91 | loaded = true; 92 | return langs; 93 | } 94 | 95 | std::string& getLangFallback() { 96 | static std::string fallback = "en-US"; 97 | return fallback; 98 | } 99 | 100 | std::string& getLang() { 101 | static std::string lang = "en-US"; 102 | return lang; 103 | } 104 | 105 | std::string i18n(std::string key) { 106 | static std::string lang; 107 | static std::map entries; 108 | if (entries.empty() || lang != getLang()) { 109 | entries.clear(); 110 | auto langs = getLangs(); 111 | auto it = langs.find(getLang()); 112 | if (it != langs.end()) { 113 | for (auto& [key, value] : it->second.entries) { 114 | if (value != "") entries.insert({key, value}); 115 | } 116 | } 117 | it = langs.find(getLangFallback()); 118 | if (it != langs.end()) { 119 | for (auto& [key, value] : it->second.entries) { 120 | if (value != "") entries.insert({key, value}); 121 | } 122 | } 123 | lang = getLang(); 124 | } 125 | auto it = entries.find(key); 126 | if (it != entries.end()) return it->second; 127 | return key; 128 | } 129 | } // namespace ImPlay -------------------------------------------------------------------------------- /source/helpers/nfd.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #include 5 | #include 6 | #define NFD_THROWS_EXCEPTIONS 7 | #include 8 | #include "helpers/nfd.h" 9 | 10 | static bool checkError(nfdresult_t result) { 11 | if (result == NFD_ERROR) { 12 | const char* err = NFD::GetError(); 13 | throw std::runtime_error(fmt::format("NFD Error: {}", err ? err : "unknown")); 14 | } 15 | return result == NFD_OKAY; 16 | } 17 | 18 | std::optional NFD::openFile(NFD::Filters filters) { 19 | NFD::Guard nfdGuard; 20 | NFD::UniquePath outPath; 21 | std::vector items; 22 | 23 | for (auto& [n, s] : filters) items.emplace_back(nfdu8filteritem_t{n.c_str(), s.c_str()}); 24 | if (!checkError(NFD::OpenDialog(outPath, items.data(), items.size()))) return std::nullopt; 25 | return std::make_optional(reinterpret_cast(outPath.get())); 26 | } 27 | 28 | std::optional> NFD::openFiles(NFD::Filters filters) { 29 | NFD::Guard nfdGuard; 30 | NFD::UniquePathSet outPaths; 31 | std::vector items; 32 | 33 | for (auto& [n, s] : filters) items.emplace_back(nfdu8filteritem_t{n.c_str(), s.c_str()}); 34 | if (!checkError(NFD::OpenDialogMultiple(outPaths, items.data(), items.size()))) return std::nullopt; 35 | 36 | std::vector paths; 37 | nfdpathsetsize_t numPaths; 38 | NFD::PathSet::Count(outPaths, numPaths); 39 | for (auto i = 0; i < numPaths; i++) { 40 | NFD::UniquePathSetPath path; 41 | NFD::PathSet::GetPath(outPaths, i, path); 42 | paths.emplace_back(reinterpret_cast(path.get())); 43 | } 44 | return std::make_optional(paths); 45 | } 46 | 47 | std::optional NFD::openFolder() { 48 | NFD::Guard nfdGuard; 49 | NFD::UniquePath outPath; 50 | 51 | if (!checkError(NFD::PickFolder(outPath))) return std::nullopt; 52 | return std::make_optional(reinterpret_cast(outPath.get())); 53 | } 54 | -------------------------------------------------------------------------------- /source/helpers/utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef _WIN32 9 | #include 10 | #include 11 | #elif defined(__APPLE__) 12 | #include 13 | #include 14 | #include 15 | #endif 16 | #include "helpers/utils.h" 17 | 18 | namespace ImPlay { 19 | void OptionParser::parse(int argc, char** argv) { 20 | bool optEnd = false; 21 | #ifdef _WIN32 22 | int wideArgc; 23 | wchar_t** wideArgv = CommandLineToArgvW(GetCommandLineW(), &wideArgc); 24 | for (int i = 1; i < wideArgc; i++) { 25 | std::string arg = WideToUTF8(wideArgv[i]); 26 | #else 27 | for (int i = 1; i < argc; i++) { 28 | std::string arg = argv[i]; 29 | #endif 30 | if (arg[0] == '-' && !optEnd) { 31 | if (arg[1] == '\0') { 32 | paths.emplace_back(arg); 33 | break; 34 | } 35 | if (arg[1] == '-') { 36 | if (arg[2] == '\0') { 37 | optEnd = true; 38 | continue; 39 | } else { 40 | arg = arg.substr(2); 41 | } 42 | } else { 43 | arg = arg.substr(1); 44 | } 45 | if (arg.starts_with("no-")) { 46 | if (arg[3] == '\0') continue; 47 | options.emplace(arg.substr(3), "no"); 48 | } else if (auto n = arg.find_first_of('='); n != std::string::npos) { 49 | options.emplace(arg.substr(0, n), arg.substr(n + 1)); 50 | } else { 51 | options.emplace(arg, "yes"); 52 | } 53 | } else { 54 | paths.emplace_back(arg); 55 | } 56 | } 57 | } 58 | 59 | bool OptionParser::check(std::string key, std::string value) { 60 | auto it = options.find(key); 61 | return it != options.end() && it->second == value; 62 | } 63 | 64 | bool fileExists(std::string path) { 65 | if (path == "") return false; 66 | auto fp = std::filesystem::path(reinterpret_cast(path.data())); 67 | return std::filesystem::exists(fp); 68 | } 69 | 70 | int openUrl(std::string url) { 71 | #ifdef __APPLE__ 72 | return system(fmt::format("open '{}'", url).c_str()); 73 | #elif defined(_WIN32) || defined(__CYGWIN__) 74 | return ShellExecuteW(0, 0, UTF8ToWide(url).c_str(), 0, 0, SW_SHOW) > (HINSTANCE)32 ? 0 : 1; 75 | #else 76 | char command[256]; 77 | return system(fmt::format("xdg-open '{}'", url).c_str()); 78 | #endif 79 | } 80 | 81 | void revealInFolder(std::string path) { 82 | auto fp = std::filesystem::path(reinterpret_cast(path.data())); 83 | if (!std::filesystem::exists(fp)) return; 84 | #ifdef __APPLE__ 85 | system(fmt::format("open -R '{}'", path).c_str()); 86 | #elif defined(_WIN32) || defined(__CYGWIN__) 87 | std::string arg = fmt::format("/select,\"{}\"", path); 88 | ShellExecuteW(0, 0, L"explorer", UTF8ToWide(arg).c_str(), 0, SW_SHOW); 89 | #else 90 | auto status = std::filesystem::status(fp); 91 | auto target = std::filesystem::is_directory(status) ? path : fp.parent_path().string(); 92 | system(fmt::format("xdg-open '{}'", target).c_str()); 93 | #endif 94 | } 95 | 96 | std::filesystem::path dataPath() { 97 | std::string dataDir; 98 | #ifdef _WIN32 99 | std::wstring path(MAX_PATH, '\0'); 100 | if (GetModuleFileNameW(nullptr, path.data(), path.length()) > 0) { 101 | auto dir = std::filesystem::path(path).parent_path() / "portable_config"; 102 | if (std::filesystem::exists(dir) && std::filesystem::is_directory(dir)) return dir; 103 | } 104 | wchar_t* dir = nullptr; 105 | if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, nullptr, &dir))) { 106 | dataDir = WideToUTF8(dir); 107 | CoTaskMemFree(dir); 108 | } 109 | #elif defined(__APPLE__) 110 | char path[PATH_MAX]; 111 | auto state = sysdir_start_search_path_enumeration(SYSDIR_DIRECTORY_APPLICATION_SUPPORT, SYSDIR_DOMAIN_MASK_USER); 112 | while ((state = sysdir_get_next_search_path_enumeration(state, path)) != 0) { 113 | glob_t g; 114 | if (glob(path, GLOB_TILDE, nullptr, &g) == 0) { 115 | dataDir = g.gl_pathv[0]; 116 | globfree(&g); 117 | } 118 | break; 119 | } 120 | #else 121 | char* home = getenv("HOME"); 122 | char* xdg_dir = getenv("XDG_CONFIG_HOME"); 123 | if (xdg_dir != nullptr) 124 | dataDir = xdg_dir; 125 | else if (home != nullptr) 126 | dataDir = fmt::format("{}/.config", home); 127 | #endif 128 | return std::filesystem::path(dataDir) / "implay"; 129 | } 130 | 131 | std::vector split(const std::string& str, const std::string& sep) { 132 | std::vector v; 133 | std::string::size_type pos1 = 0, pos2 = 0; 134 | while ((pos2 = str.find(sep, pos1)) != std::string::npos) { 135 | v.push_back(str.substr(pos1, pos2 - pos1)); 136 | pos1 = pos2 + sep.size(); 137 | } 138 | if (pos1 != str.length()) v.push_back(str.substr(pos1)); 139 | return v; 140 | } 141 | 142 | bool findCase(std::string haystack, std::string needle) { 143 | auto it = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), 144 | [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }); 145 | return it != haystack.end(); 146 | } 147 | } // namespace ImPlay -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 tsl0922. All rights reserved. 2 | // SPDX-License-Identifier: GPL-2.0-only 3 | 4 | #include 5 | #include 6 | #ifdef _WIN32 7 | #include 8 | #else 9 | #include 10 | #include 11 | #include 12 | #include 13 | #endif 14 | #include 15 | #include "helpers/utils.h" 16 | #include "window.h" 17 | 18 | static const char* usage = 19 | "Usage: ImPlay [options] [url|path/]filename\n" 20 | "\n" 21 | "Basic options:\n" 22 | " --start=