├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_template.yml │ └── feature_request.yml └── workflows │ ├── release_flatpak.yml │ ├── release_github.yml │ ├── release_pypi.yml │ └── release_snap.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .versionrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── gridplayer ├── __init__.py ├── __main__.py ├── dialogs │ ├── __init__.py │ ├── about.py │ ├── about_dialog_ui.py │ ├── add_urls.py │ ├── exception.py │ ├── exception_dialog_ui.py │ ├── input_dialog.py │ ├── messagebox.py │ ├── rename_dialog.py │ ├── settings.py │ └── settings_dialog_ui.py ├── exceptions.py ├── main │ ├── __init__.py │ ├── init_app.py │ ├── init_app_env.py │ ├── init_icons.py │ ├── init_log.py │ ├── init_resources.py │ ├── init_translator.py │ └── run.py ├── models │ ├── __init__.py │ ├── grid_state.py │ ├── playlist.py │ ├── recent_list.py │ ├── resolver_patterns.py │ ├── stream.py │ ├── video.py │ └── video_uri.py ├── multiprocess │ ├── __init__.py │ ├── command_loop.py │ ├── instance_process.py │ ├── process_manager.py │ └── safe_shared_memory.py ├── params │ ├── __init__.py │ ├── actions.py │ ├── env.py │ ├── extensions.py │ ├── languages.py │ ├── menu.py │ └── static.py ├── player │ ├── __init__.py │ ├── manager.py │ ├── managers │ │ ├── __init__.py │ │ ├── actions.py │ │ ├── active_block.py │ │ ├── add_videos.py │ │ ├── base.py │ │ ├── dialogs.py │ │ ├── drag_n_drop.py │ │ ├── grid.py │ │ ├── instance_listener.py │ │ ├── log.py │ │ ├── macos_fileopen.py │ │ ├── menu.py │ │ ├── mouse_hide.py │ │ ├── playlist.py │ │ ├── recent_list.py │ │ ├── screensaver.py │ │ ├── settings.py │ │ ├── single_mode.py │ │ ├── snapshots.py │ │ ├── stream_proxy.py │ │ ├── video_blocks.py │ │ ├── video_driver.py │ │ └── window_state.py │ └── player.py ├── resources_bin.py ├── settings.py ├── utils │ ├── __init__.py │ ├── app_dir.py │ ├── aspect_calc.py │ ├── command_helpers.py │ ├── darkmode.py │ ├── excepthook.py │ ├── files.py │ ├── keepawake.py │ ├── keepawake_macos.py │ ├── libvlc.py │ ├── libvlc_fixer.py │ ├── libvlc_options_parser.py │ ├── log.py │ ├── log_config.py │ ├── misc.py │ ├── next_file.py │ ├── qt.py │ ├── single_instance.py │ ├── stream_proxy │ │ ├── __init__.py │ │ ├── m3u8.py │ │ ├── server.py │ │ ├── session.py │ │ ├── stream_proxy.py │ │ └── wrappers.py │ ├── time_txt.py │ └── url_resolve │ │ ├── __init__.py │ │ ├── resolver_base.py │ │ ├── resolver_streamlink.py │ │ ├── resolver_yt_dlp.py │ │ ├── static.py │ │ ├── stream_detect.py │ │ └── url_resolve.py ├── version.py ├── vlc_player │ ├── __init__.py │ ├── image_decoder.py │ ├── instance.py │ ├── libvlc.py │ ├── player_base.py │ ├── player_base_threaded.py │ ├── player_event_manager.py │ ├── player_event_waiter.py │ ├── player_tracks_manager.py │ ├── static.py │ ├── video_driver_base.py │ └── video_driver_base_threaded.py └── widgets │ ├── __init__.py │ ├── custom_menu.py │ ├── language_list.py │ ├── resolver_patterns_list.py │ ├── video_block.py │ ├── video_frame_dummy.py │ ├── video_frame_vlc_base.py │ ├── video_frame_vlc_hw.py │ ├── video_frame_vlc_hw_sp.py │ ├── video_frame_vlc_sw.py │ ├── video_overlay.py │ ├── video_overlay_buttons.py │ ├── video_overlay_elements.py │ ├── video_overlay_icons.py │ ├── video_status.py │ ├── video_status_info.py │ └── video_status_loading.py ├── poetry.lock ├── pyproject.toml ├── resources ├── fonts │ └── Hack │ │ ├── Hack-Bold.ttf │ │ ├── Hack-BoldItalic.ttf │ │ ├── Hack-Italic.ttf │ │ ├── Hack-Regular.ttf │ │ └── LICENSE.md ├── icons │ ├── basic │ │ ├── 001-achievement.svg │ │ ├── 002-flag.svg │ │ ├── 003-add.svg │ │ ├── 004-contacs.svg │ │ ├── 005-aim.svg │ │ ├── 006-clock.svg │ │ ├── 007-anchor.svg │ │ ├── 008-tick.svg │ │ ├── 009-archive.svg │ │ ├── 010-attach.svg │ │ ├── 011-backwards.svg │ │ ├── 012-badge.svg │ │ ├── 013-bag.svg │ │ ├── 014-ban.svg │ │ ├── 015-low battery.svg │ │ ├── 016-bell.svg │ │ ├── 017-binocular.svg │ │ ├── 018-bluetooth.svg │ │ ├── 019-board.svg │ │ ├── 020-book.svg │ │ ├── 021-bookmark.svg │ │ ├── 022-notebook.svg │ │ ├── 023-box.svg │ │ ├── 024-briefcase.svg │ │ ├── 025-brush.svg │ │ ├── 026-identification.svg │ │ ├── 027-bar.svg │ │ ├── 028-calendar.svg │ │ ├── 029-call.svg │ │ ├── 030-telephone.svg │ │ ├── 031-cancel.svg │ │ ├── 032-cart.svg │ │ ├── 033-center align.svg │ │ ├── 034-center align.svg │ │ ├── 035-full battery.svg │ │ ├── 036-writing.svg │ │ ├── 037-tick.svg │ │ ├── 038-writing.svg │ │ ├── 039-chemistry.svg │ │ ├── 040-chemistry.svg │ │ ├── 041-tubes.svg │ │ ├── 042-sand clock.svg │ │ ├── 043-code.svg │ │ ├── 044-code.svg │ │ ├── 045-gear.svg │ │ ├── 046-compass.svg │ │ ├── 047-compress.svg │ │ ├── 048-monitor.svg │ │ ├── 049-compress.svg │ │ ├── 050-contact book.svg │ │ ├── 051-cpu.svg │ │ ├── 052-crop.svg │ │ ├── 053-headphones.svg │ │ ├── 054-curriculum.svg │ │ ├── 055-pendrive.svg │ │ ├── 056-calendar.svg │ │ ├── 057-calendar.svg │ │ ├── 058-anchor point.svg │ │ ├── 059-smartphone.svg │ │ ├── 060-diamond.svg │ │ ├── 061-diary.svg │ │ ├── 062-syringe.svg │ │ ├── 063-stethoscope.svg │ │ ├── 064-approved.svg │ │ ├── 065-archives.svg │ │ ├── 066-down arrow.svg │ │ ├── 067-down arrow.svg │ │ ├── 068-drink.svg │ │ ├── 069-pen.svg │ │ ├── 070-writing.svg │ │ ├── 071-writing.svg │ │ ├── 072-writing.svg │ │ ├── 073-edit.svg │ │ ├── 074-pen.svg │ │ ├── 075-event.svg │ │ ├── 076-calendar.svg │ │ ├── 077-calendar.svg │ │ ├── 078-calendar.svg │ │ ├── 079-event.svg │ │ ├── 080-calendar.svg │ │ ├── 081-tube.svg │ │ ├── 082-eye.svg │ │ ├── 083-backward.svg │ │ ├── 084-forward.svg │ │ ├── 085-paper.svg │ │ ├── 086-flag.svg │ │ ├── 087-flag.svg │ │ ├── 088-thunder.svg │ │ ├── 089-flask.svg │ │ ├── 090-flask.svg │ │ ├── 091-diagram.svg │ │ ├── 092-folder.svg │ │ ├── 093-folder.svg │ │ ├── 094-cutlery.svg │ │ ├── 095-forward.svg │ │ ├── 096-full battery.svg │ │ ├── 097-present.svg │ │ ├── 098-present.svg │ │ ├── 099-squares.svg │ │ ├── 100-gym.svg │ │ ├── 101-headphones.svg │ │ ├── 102-blind.svg │ │ ├── 103-identification.svg │ │ ├── 104-name.svg │ │ ├── 105-postal.svg │ │ ├── 106-image.svg │ │ ├── 107-image.svg │ │ ├── 108-inbox.svg │ │ ├── 109-In.svg │ │ ├── 110-router.svg │ │ ├── 111-layers.svg │ │ ├── 112-left alignment.svg │ │ ├── 113-left arrow.svg │ │ ├── 114-lamp.svg │ │ ├── 115-placeholder.svg │ │ ├── 116-placeholder.svg │ │ ├── 117-In.svg │ │ ├── 118-out.svg │ │ ├── 119-wand.svg │ │ ├── 120-magnet.svg │ │ ├── 121-letter.svg │ │ ├── 122-measurement.svg │ │ ├── 123-menu.svg │ │ ├── 124-message.svg │ │ ├── 125-microphone.svg │ │ ├── 126-minimize.svg │ │ ├── 127-mobile.svg │ │ ├── 128-monitor.svg │ │ ├── 129-more.svg │ │ ├── 130-note.svg │ │ ├── 131-notepad.svg │ │ ├── 132-note.svg │ │ ├── 133-slider.svg │ │ ├── 134-slider.svg │ │ ├── 135-organization.svg │ │ ├── 136-log out.svg │ │ ├── 137-package.svg │ │ ├── 138-pages.svg │ │ ├── 139-page.svg │ │ ├── 140-pause.svg │ │ ├── 141-writing.svg │ │ ├── 142-physics.svg │ │ ├── 143-pin.svg │ │ ├── 144-placeholder.svg │ │ ├── 145-play.svg │ │ ├── 146-presentation.svg │ │ ├── 147-profile.svg │ │ ├── 148-profile.svg │ │ ├── 149-book.svg │ │ ├── 150-recycle.svg │ │ ├── 151-recycle.svg │ │ ├── 152-recycle.svg │ │ ├── 153-refresh.svg │ │ ├── 154-resume.svg │ │ ├── 155-ribbon.svg │ │ ├── 156-right alignment.svg │ │ ├── 157-right.svg │ │ ├── 158-roller.svg │ │ ├── 159-router.svg │ │ ├── 160-ruler.svg │ │ ├── 161-sand clock.svg │ │ ├── 162-monitor.svg │ │ ├── 163-search.svg │ │ ├── 164-rulers.svg │ │ ├── 165-set square.svg │ │ ├── 166-gear.svg │ │ ├── 167-share.svg │ │ ├── 168-shield.svg │ │ ├── 169-shopping bag.svg │ │ ├── 170-shopping cart.svg │ │ ├── 171-shopping cart.svg │ │ ├── 172-wristwatch.svg │ │ ├── 173-star.svg │ │ ├── 174-street light.svg │ │ ├── 175-syringe.svg │ │ ├── 176-tablet.svg │ │ ├── 177-tag.svg │ │ ├── 178-test tube.svg │ │ ├── 179-document.svg │ │ ├── 180-ticket.svg │ │ ├── 181-tie.svg │ │ ├── 182-chronometer.svg │ │ ├── 183-sand clock.svg │ │ ├── 184-tissue roll.svg │ │ ├── 185-top alignment.svg │ │ ├── 186-torch.svg │ │ ├── 187-recycle.svg │ │ ├── 188-recycle.svg │ │ ├── 189-trash.svg │ │ ├── 190-trolley.svg │ │ ├── 191-up arrow.svg │ │ ├── 192-upload.svg │ │ ├── 193-upload.svg │ │ ├── 194-upload.svg │ │ ├── 195-usb cable.svg │ │ ├── 196-usb.svg │ │ ├── 197-add user.svg │ │ ├── 198-user.svg │ │ ├── 199-user.svg │ │ ├── 200-user.svg │ │ ├── 201-delete user.svg │ │ ├── 202-user.svg │ │ ├── 203-user.svg │ │ ├── 204-user.svg │ │ ├── 205-user.svg │ │ ├── 206-user.svg │ │ ├── 207-user.svg │ │ ├── 208-user.svg │ │ ├── 209-contact information.svg │ │ ├── 210-user.svg │ │ ├── 211-user.svg │ │ ├── 212-user.svg │ │ ├── 213-block user.svg │ │ ├── 214-user.svg │ │ ├── 215-user.svg │ │ ├── 216-user.svg │ │ ├── 217-users.svg │ │ ├── 218-user.svg │ │ ├── 219-user.svg │ │ ├── 220-vector.svg │ │ ├── 221-verified.svg │ │ ├── 222-video camera.svg │ │ ├── 223-video.svg │ │ ├── 224-eye.svg │ │ ├── 225-mute.svg │ │ ├── 226-mute.svg │ │ ├── 227-low volume.svg │ │ ├── 228-wall clock.svg │ │ ├── 229-wristwatch.svg │ │ ├── 230-drop.svg │ │ ├── 231-web.svg │ │ ├── 232-wristwatch.svg │ │ ├── 233-writing.svg │ │ ├── 234-zoom.svg │ │ ├── 235-zoom in.svg │ │ └── 236-zoom out.svg │ ├── custom │ │ ├── 001-minimize window.svg │ │ ├── 002-close all.svg │ │ ├── 003-aspect fit.svg │ │ ├── 004-aspect stretch.svg │ │ ├── 005-aspect none.svg │ │ ├── 006-aspect.svg │ │ ├── 007-rows first.svg │ │ ├── 008-columns first.svg │ │ ├── 009-all.svg │ │ ├── 010-jump.svg │ │ ├── 011-random.svg │ │ ├── 012-loop reset.svg │ │ ├── 013-loop start.svg │ │ ├── 014-loop end.svg │ │ ├── 015-fullscreen.svg │ │ ├── 016-playlist open.svg │ │ ├── 017-playlist save.svg │ │ ├── 018-playlist close.svg │ │ ├── 019-save file.svg │ │ ├── 020-plus-1.svg │ │ ├── 021-plus-5.svg │ │ ├── 022-plus-10.svg │ │ ├── 023-minus-1.svg │ │ ├── 024-minus-5.svg │ │ ├── 025-minus-10.svg │ │ ├── 026-speed.svg │ │ ├── 027-speed faster.svg │ │ ├── 028-speed slower.svg │ │ ├── 029-speed reset.svg │ │ ├── 030-question.svg │ │ ├── 031-grid fit.svg │ │ ├── 032-grid size.svg │ │ ├── 033-seek sync.svg │ │ ├── 034-previous frame.svg │ │ ├── 035-next frame.svg │ │ ├── 036-loop single.svg │ │ ├── 037-loop dir.svg │ │ ├── 038-loop dir shuffle.svg │ │ ├── 039-information.svg │ │ ├── 040-stream quality.svg │ │ ├── 041-audio only.svg │ │ ├── 042-error.svg │ │ ├── 043-network error.svg │ │ ├── 044-seek sync time.svg │ │ ├── 045-seek sync percent.svg │ │ ├── 046-empty.svg │ │ ├── 047-warning.svg │ │ ├── 048-settings.svg │ │ ├── 049-checkmark.svg │ │ ├── 050-track.svg │ │ ├── 051-add url.svg │ │ ├── 052-media file.svg │ │ ├── 053-recent list.svg │ │ ├── 054-video.svg │ │ ├── 055-stereo mode.svg │ │ ├── 056-playback.svg │ │ ├── 057-volume increase.svg │ │ ├── 058-volume decrease.svg │ │ ├── 059-volume unmute.svg │ │ ├── 060-single mode on.svg │ │ ├── 061-single mode off.svg │ │ ├── 062-play pause.svg │ │ ├── 063-rotate.svg │ │ └── 064-crop.svg │ ├── flags │ │ ├── ar_SA.svg │ │ ├── de_DE.svg │ │ ├── en_US.svg │ │ ├── es_ES.svg │ │ ├── fr_FR.svg │ │ ├── hu_HU.svg │ │ ├── it_IT.svg │ │ ├── ja_JP.svg │ │ ├── ko_KR.svg │ │ ├── nl_NL.svg │ │ ├── pl_PL.svg │ │ ├── pt_BR.svg │ │ ├── ru_RU.svg │ │ └── zh_CN.svg │ ├── main │ │ ├── png │ │ │ ├── 128x128.png │ │ │ ├── 16x16.png │ │ │ ├── 16x16@2x.png │ │ │ ├── 24x24.png │ │ │ ├── 24x24@2x.png │ │ │ ├── 256x256.png │ │ │ ├── 256x256@2x.png │ │ │ ├── 256x256@4x.png │ │ │ ├── 32x32.png │ │ │ ├── 32x32@2x.png │ │ │ ├── 48x48.png │ │ │ ├── 48x48@2x.png │ │ │ └── 64x64.png │ │ ├── src │ │ │ └── main_src.svg │ │ ├── svg │ │ │ ├── big.svg │ │ │ ├── normal.svg │ │ │ └── symbolic.svg │ │ └── sys │ │ │ ├── macos.icns │ │ │ └── windows.ico │ └── playlist │ │ ├── png │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 16x16@2x.png │ │ ├── 24x24.png │ │ ├── 24x24@2x.png │ │ ├── 256x256.png │ │ ├── 256x256@2x.png │ │ ├── 256x256@4x.png │ │ ├── 32x32.png │ │ ├── 32x32@2x.png │ │ ├── 48x48.png │ │ ├── 48x48@2x.png │ │ └── 64x64.png │ │ ├── src │ │ ├── playlist_src.svg │ │ └── playlist_symbolic_src.svg │ │ ├── svg │ │ ├── big.svg │ │ ├── normal.svg │ │ └── symbolic.svg │ │ └── sys │ │ ├── macos.icns │ │ └── windows.ico ├── public │ ├── dl_appimage.png │ ├── dl_dmg.png │ ├── dl_flathub.png │ ├── dl_snap.png │ ├── dl_windows_installer.png │ ├── dl_windows_portable.png │ ├── logo.svg │ ├── screenshot-001-thumb.png │ ├── screenshot-001.png │ ├── screenshot-002-thumb.png │ ├── screenshot-002.png │ ├── screenshot-003-thumb.png │ ├── screenshot-003.png │ ├── screenshot-004-thumb.png │ └── screenshot-004.png ├── resources.csv ├── translations │ ├── ar_SA.ts │ ├── de_DE.ts │ ├── en_US.ts │ ├── es_ES.ts │ ├── fr_FR.ts │ ├── hu_HU.ts │ ├── it_IT.ts │ ├── ja_JP.ts │ ├── ko_KR.ts │ ├── nl_NL.ts │ ├── pl_PL.ts │ ├── pt_BR.ts │ ├── ru_RU.ts │ └── zh_CN.ts └── ui │ ├── about_dialog.ui │ ├── exception_dialog.ui │ └── settings_dialog.ui ├── scripts ├── _helpers │ ├── blacklist_clean.sh │ ├── blacklist_pyqt.txt │ ├── blacklist_snap.txt │ ├── blacklist_vlc_linux.txt │ ├── kacl.py │ └── render-bitmaps.py ├── _local │ ├── appimage.sh │ ├── changelog.sh │ ├── flatpak.sh │ ├── flatpak_git.sh │ ├── macos.sh │ ├── snap.sh │ └── windows.sh ├── appimage │ ├── AppRun.c │ ├── build.sh │ └── build_docker.sh ├── flatpak │ ├── app.yml │ ├── app_git.yml │ ├── app_local.yml │ ├── build.sh │ ├── build_git.sh │ ├── dependencies │ │ └── pyqt.yml │ ├── generate_dependencies.sh │ └── libvlc │ │ ├── gsm-makefile.patch │ │ ├── gsm.patch │ │ ├── libebml-gcc11.patch │ │ ├── libkate.patch │ │ ├── libshout-openssl11.patch │ │ ├── libvlc.yml │ │ ├── live555-add-pkgconfig-file.patch │ │ └── live555-nosignal.patch ├── init_app_vars.sh ├── linux_meta │ ├── app.appdata.xml │ ├── app.desktop │ ├── build.sh │ └── mime.xml ├── macos │ └── build_dmg.sh ├── pyinstaller │ ├── build_mac.sh │ ├── build_win.sh │ ├── mime_vlc.plist │ ├── pyinstaller_mac.spec │ ├── pyinstaller_win.spec │ ├── version_info.py │ └── vlc_plugins_wl.txt ├── qt_resources │ ├── build_resources.py │ ├── build_resources.sh │ └── build_ui.sh ├── snap │ ├── apt_dl.sh │ ├── build.sh │ └── snapcraft.yaml ├── translations │ ├── build_ts.sh │ ├── crowdin.yml │ ├── download.sh │ ├── strip_locations.sh │ └── upload.sh └── windows │ ├── build_packages.sh │ ├── generate_file_associations.py │ └── installer.iss └── tests ├── __init__.py └── test_utils_time_txt.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://www.buymeacoffee.com/vzhd1701", "https://github.com/vzhd1701/donation-wallets"] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_template.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug in GridPlayer 3 | title: "[Bug]: " 4 | labels: "bug" 5 | body: 6 | - type: input 7 | attributes: 8 | label: GridPlayer version 9 | description: What version of GridPlayer are you using? If unsure, open About dialog [F1]. 10 | validations: 11 | required: true 12 | - type: dropdown 13 | attributes: 14 | label: What OS are you using? 15 | options: 16 | - Windows 17 | - Linux 18 | - MacOS 19 | validations: 20 | required: true 21 | - type: input 22 | attributes: 23 | label: OS Version / Linux distribution 24 | description: What OS version or Linux distribution version are you using? 25 | placeholder: "e.g. Windows 10, Ubuntu 20.04" 26 | validations: 27 | required: true 28 | - type: dropdown 29 | attributes: 30 | label: What distribution channel are you using? [LINUX ONLY] 31 | options: 32 | - Flathub 33 | - Snap Store 34 | - AppImage 35 | - type: textarea 36 | attributes: 37 | label: Bug description 38 | description: A clear description of the bug and how to reproduce it. 39 | validations: 40 | required: true 41 | - type: textarea 42 | attributes: 43 | label: Log excerpt 44 | description: If possible, please attach log file excerpt with log level set to debug. You can find it in setting dialog. 45 | render: shell 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Request a feature 3 | title: "[Feature request]: " 4 | labels: "enhancement" 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Suggestion 9 | description: A clear and concise description of what you would like to see in the upcoming releases. 10 | validations: 11 | required: true 12 | -------------------------------------------------------------------------------- /.github/workflows/release_pypi.yml: -------------------------------------------------------------------------------- 1 | name: release_pypi 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v[0-9]+.[0-9]+.[0-9]+" 7 | 8 | env: 9 | BUILD_PYTHON_VERSION: 3.8 10 | BUILD_POETRY_VERSION: 1.4 11 | 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v3 18 | 19 | - name: Set up Python ${{ env.BUILD_PYTHON_VERSION }} 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: ${{ env.BUILD_PYTHON_VERSION }} 23 | 24 | - name: Set up Poetry ${{ env.BUILD_POETRY_VERSION }} 25 | uses: abatilo/actions-poetry@v2.0.0 26 | with: 27 | poetry-version: ${{ env.BUILD_POETRY_VERSION }} 28 | 29 | - name: Build project for distribution 30 | run: poetry build 31 | 32 | - name: Publish to PyPI 33 | env: 34 | POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} 35 | run: poetry publish 36 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com/ for usage and config 2 | # pre-commit install 3 | # need commit-msg hook for commitzen 4 | # pre-commit install --hook-type commit-msg 5 | 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v3.4.0 9 | hooks: 10 | - id: check-vcs-permalinks 11 | - id: end-of-file-fixer 12 | exclude_types: [svg] 13 | - id: trailing-whitespace 14 | exclude: \.(patch)$ 15 | args: [--markdown-linebreak-ext=md] 16 | - id: mixed-line-ending 17 | exclude_types: [svg] 18 | args: ['--fix=lf'] 19 | - id: check-toml 20 | - id: check-yaml 21 | - id: no-commit-to-branch 22 | 23 | - repo: local 24 | hooks: 25 | - id: isort 26 | name: isort 27 | stages: [commit] 28 | language: system 29 | entry: poetry run isort 30 | types: [python] 31 | 32 | - id: black 33 | name: black 34 | stages: [commit] 35 | language: system 36 | entry: poetry run black 37 | types: [python] 38 | exclude: resources_bin.py 39 | 40 | - id: mdformat 41 | name: mdformat 42 | stages: [commit] 43 | language: system 44 | entry: poetry run mdformat 45 | types: [markdown] 46 | exclude: CHANGELOG.md 47 | -------------------------------------------------------------------------------- /gridplayer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/__init__.py -------------------------------------------------------------------------------- /gridplayer/__main__.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | warnings.filterwarnings("ignore", category=DeprecationWarning) 4 | 5 | import sys 6 | from multiprocessing import freeze_support 7 | 8 | from gridplayer.main.init_app_env import init_app_env 9 | from gridplayer.main.init_log import init_log 10 | from gridplayer.main.run import run_app 11 | from gridplayer.params import env 12 | from gridplayer.settings import Settings 13 | from gridplayer.utils.excepthook import excepthook 14 | from gridplayer.utils.log import log_environment 15 | from gridplayer.utils.single_instance import is_delegated_to_primary 16 | 17 | 18 | def main(): 19 | freeze_support() 20 | 21 | init_app_env() 22 | 23 | init_log() 24 | 25 | sys.excepthook = excepthook 26 | 27 | # MacOS has OpenFile events 28 | if not env.IS_MACOS: 29 | exit_if_delegated() 30 | 31 | log_environment() 32 | 33 | ret = run_app() 34 | 35 | sys.exit(ret) 36 | 37 | 38 | def exit_if_delegated(): 39 | if Settings().get("player/one_instance") and is_delegated_to_primary(sys.argv): 40 | sys.exit(0) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /gridplayer/dialogs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/dialogs/__init__.py -------------------------------------------------------------------------------- /gridplayer/exceptions.py: -------------------------------------------------------------------------------- 1 | class PlayerException(Exception): 2 | """Global exception""" 3 | -------------------------------------------------------------------------------- /gridplayer/main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/main/__init__.py -------------------------------------------------------------------------------- /gridplayer/main/init_app.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from PyQt5.QtCore import Qt 4 | from PyQt5.QtGui import QFont 5 | from PyQt5.QtWidgets import QApplication, QStyleFactory 6 | 7 | from gridplayer.main.init_icons import init_icon, switch_icon_theme 8 | from gridplayer.main.init_resources import init_resources 9 | from gridplayer.main.init_translator import init_translator 10 | from gridplayer.params.static import FONT_SIZE_MAIN 11 | 12 | 13 | def init_app(): 14 | app = QApplication(sys.argv) 15 | 16 | app.paletteChanged.connect(switch_icon_theme) 17 | 18 | init_resources() 19 | 20 | app.setStyle(QStyleFactory.create("Fusion")) 21 | 22 | app.setAttribute(Qt.AA_DisableWindowContextHelpButton) 23 | app.styleHints().setShowShortcutsInContextMenus(True) 24 | 25 | switch_icon_theme() 26 | 27 | init_icon(app) 28 | 29 | app.setFont(QFont("Hack", FONT_SIZE_MAIN)) 30 | 31 | init_translator(app) 32 | 33 | return app 34 | -------------------------------------------------------------------------------- /gridplayer/main/init_app_env.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import Qt 2 | from PyQt5.QtGui import QGuiApplication 3 | from PyQt5.QtWidgets import QApplication 4 | 5 | from gridplayer.params import env 6 | from gridplayer.version import ( 7 | __app_id__, 8 | __app_name__, 9 | __author_name__, 10 | __display_name__, 11 | __version__, 12 | ) 13 | 14 | 15 | def init_app_env_id(): 16 | QApplication.setApplicationName(__app_name__) 17 | QApplication.setApplicationDisplayName(__display_name__) 18 | QApplication.setOrganizationName(__author_name__) 19 | QApplication.setApplicationVersion(__version__) 20 | 21 | 22 | def init_app_env(): 23 | if env.IS_WINDOWS: 24 | from PyQt5.QtWinExtras import QtWin # noqa: WPS433 25 | 26 | QtWin.setCurrentProcessExplicitAppUserModelID(__app_id__) 27 | 28 | init_app_env_id() 29 | 30 | QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) 31 | QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) 32 | 33 | QGuiApplication.setHighDpiScaleFactorRoundingPolicy( 34 | Qt.HighDpiScaleFactorRoundingPolicy.PassThrough 35 | ) 36 | -------------------------------------------------------------------------------- /gridplayer/main/init_icons.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtGui import QIcon 2 | 3 | from gridplayer.params import env 4 | from gridplayer.utils.darkmode import is_dark_mode 5 | 6 | 7 | def init_icon(app): 8 | if env.IS_MACOS: 9 | app.setWindowIcon(QIcon(":/icons/main_ico_mac.icns")) 10 | elif env.IS_WINDOWS: 11 | app.setWindowIcon(QIcon(":/icons/main_ico_win.ico")) 12 | elif app.desktop().devicePixelRatio() == 1: 13 | app.setWindowIcon(QIcon(":/icons/main_ico_48.png")) 14 | else: 15 | app.setWindowIcon(QIcon(":/icons/main_ico_svg.svg")) 16 | 17 | 18 | def switch_icon_theme(): 19 | if is_dark_mode(): 20 | QIcon.setThemeName("dark") 21 | else: 22 | QIcon.setThemeName("light") 23 | -------------------------------------------------------------------------------- /gridplayer/main/init_log.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import qInstallMessageHandler 2 | 3 | from gridplayer.settings import Settings 4 | from gridplayer.utils import log_config 5 | from gridplayer.utils.app_dir import get_app_data_dir 6 | from gridplayer.version import __app_name__ 7 | 8 | 9 | def init_log(): 10 | log_path = get_app_data_dir() / f"{__app_name__.lower()}.log" 11 | 12 | if Settings().get("logging/log_limit"): 13 | max_log_size = max(Settings().get("logging/log_limit_size"), 1) * 1024 * 1024 14 | max_log_backups = max(Settings().get("logging/log_limit_backups"), 1) 15 | else: 16 | max_log_size = None 17 | max_log_backups = None 18 | 19 | log_config.config_log( 20 | log_path=log_path, 21 | log_level=Settings().get("logging/log_level"), 22 | max_log_size=max_log_size, 23 | max_log_backups=max_log_backups, 24 | ) 25 | 26 | log_qt = log_config.QtLogHandler() 27 | qInstallMessageHandler(log_qt.handle) 28 | -------------------------------------------------------------------------------- /gridplayer/main/init_resources.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QDir, QDirIterator 2 | from PyQt5.QtGui import QFontDatabase 3 | 4 | 5 | def init_resources(): 6 | # noinspection PyUnresolvedReferences 7 | from gridplayer import resources_bin # noqa:F401,WPS433 8 | 9 | fonts = QDirIterator(":/fonts", ("*.ttf",), QDir.Files) 10 | 11 | while fonts.hasNext(): 12 | font = fonts.next() # noqa: B305 13 | QFontDatabase.addApplicationFont(font) 14 | -------------------------------------------------------------------------------- /gridplayer/main/init_translator.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from PyQt5.QtCore import QLibraryInfo, QLocale, QTranslator 4 | 5 | from gridplayer.params import env 6 | from gridplayer.settings import Settings 7 | 8 | 9 | def init_translator(app): 10 | log = logging.getLogger(__name__) 11 | 12 | lang = Settings().get("player/language") 13 | if lang == "en_US": 14 | return 15 | 16 | log.debug(f"Loading translation for {lang}") 17 | 18 | if env.IS_PYINSTALLER and env.IS_WINDOWS: 19 | qt_translations_path = str( 20 | env.PYINSTALLER_LIB_ROOT / "PyQt5" / "Qt5" / "translations" 21 | ) 22 | else: 23 | qt_translations_path = QLibraryInfo.location(QLibraryInfo.TranslationsPath) 24 | log.debug(f"QT translations path: {qt_translations_path}") 25 | 26 | translator_qt = QTranslator(app) 27 | if translator_qt.load(QLocale(lang), "qtbase_", "", qt_translations_path): 28 | app.installTranslator(translator_qt) 29 | else: 30 | log.warning(f"Failed to load QT translation for {lang}") 31 | 32 | translator = QTranslator(app) 33 | if translator.load(lang, ":/translations/"): 34 | app.installTranslator(translator) 35 | else: 36 | log.warning(f"Failed to load translation for {lang}") 37 | -------------------------------------------------------------------------------- /gridplayer/main/run.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from gridplayer.dialogs.messagebox import QCustomMessageBox 4 | from gridplayer.main.init_app import init_app 5 | from gridplayer.params import env 6 | from gridplayer.utils.libvlc import init_vlc 7 | from gridplayer.utils.qt import translate 8 | 9 | 10 | def run_app(): 11 | app = init_app() 12 | 13 | try: 14 | vlc_version, vlc_python_version = init_vlc() 15 | except FileNotFoundError: 16 | QCustomMessageBox.critical( 17 | None, 18 | translate("Dialog", "Error"), 19 | translate( 20 | "Error", 21 | "
VLC player is required!
Please visit" 22 | ' VLC official site' 23 | " for instructions on how to install it.
", 24 | ), 25 | ) 26 | return 1 27 | 28 | env.VLC_VERSION = vlc_version 29 | env.VLC_PYTHON_VERSION = vlc_python_version 30 | 31 | # Need to postpone import parts that depend on vlc 32 | # because python-vlc loads VLC DLL on import 33 | # and we need to set environment vars before that 34 | from gridplayer.player import Player # noqa: WPS433 35 | 36 | player = Player() 37 | player.show() 38 | 39 | app.installEventFilter(player) 40 | 41 | if sys.argv[1:]: 42 | player.process_arguments(sys.argv[1:]) 43 | 44 | return app.exec_() 45 | -------------------------------------------------------------------------------- /gridplayer/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/models/__init__.py -------------------------------------------------------------------------------- /gridplayer/models/grid_state.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | from gridplayer.params.static import GridMode 4 | from gridplayer.settings import default_field 5 | 6 | 7 | class GridState(BaseModel): 8 | mode: GridMode = default_field("playlist/grid_mode") 9 | is_fit: bool = default_field("playlist/grid_fit") 10 | size: int = default_field("playlist/grid_size") 11 | -------------------------------------------------------------------------------- /gridplayer/models/video_uri.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Union 3 | 4 | from pydantic import AnyUrl, FilePath, PydanticValueError 5 | 6 | from gridplayer.params.extensions import SUPPORTED_MEDIA_EXT 7 | 8 | 9 | class VideoURL(AnyUrl): 10 | allowed_schemes = {"http", "https", "rtp", "rtsp", "rtmp", "udp", "mms", "mmsh"} 11 | max_length = 2083 12 | 13 | 14 | class PathNotAbsoluteError(PydanticValueError): 15 | code = "path.not_absolute" 16 | msg_template = 'path "{path}" is not absolute' 17 | 18 | 19 | class PathExtensionNotSupportedError(PydanticValueError): 20 | code = "path.ext_not_supported" 21 | msg_template = 'path extension "{path}" is not supported' 22 | 23 | 24 | class AbsoluteFilePath(FilePath): 25 | @classmethod 26 | def validate(cls, path: Path) -> Path: 27 | super().validate(path) 28 | 29 | if not path.is_absolute(): 30 | raise PathNotAbsoluteError(path=path) 31 | 32 | return path 33 | 34 | 35 | VideoURI = Union[VideoURL, AbsoluteFilePath] 36 | -------------------------------------------------------------------------------- /gridplayer/multiprocess/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/multiprocess/__init__.py -------------------------------------------------------------------------------- /gridplayer/params/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/params/__init__.py -------------------------------------------------------------------------------- /gridplayer/params/env.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import sys 4 | from pathlib import Path 5 | 6 | IS_LINUX = platform.system() == "Linux" 7 | IS_MACOS = platform.system() == "Darwin" 8 | IS_WINDOWS = platform.system() == "Windows" 9 | 10 | IS_PYINSTALLER = getattr(sys, "frozen", False) 11 | IS_SNAP = IS_LINUX and "SNAP" in os.environ 12 | IS_APPIMAGE = IS_LINUX and "APPIMAGE" in os.environ 13 | IS_FLATPAK = IS_LINUX and "FLATPAK_ID" in os.environ 14 | 15 | PYINSTALLER_LIB_ROOT = ( 16 | Path(sys._MEIPASS) if IS_PYINSTALLER else Path.cwd() # noqa: WPS437 17 | ) 18 | 19 | 20 | VLC_VERSION = None 21 | VLC_PYTHON_VERSION = None 22 | 23 | if IS_FLATPAK: 24 | FLATPAK_RUNTIME_DIR = ( 25 | Path(os.environ["XDG_RUNTIME_DIR"]) 26 | / "app" 27 | / os.environ["FLATPAK_ID"] 28 | / "gridplayer" 29 | ) 30 | FLATPAK_RUNTIME_DIR.mkdir(parents=True, exist_ok=True) 31 | else: 32 | FLATPAK_RUNTIME_DIR = None 33 | -------------------------------------------------------------------------------- /gridplayer/player/__init__.py: -------------------------------------------------------------------------------- 1 | from gridplayer.player.player import Player 2 | -------------------------------------------------------------------------------- /gridplayer/player/managers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/player/managers/__init__.py -------------------------------------------------------------------------------- /gridplayer/player/managers/base.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import logging 3 | 4 | from PyQt5.QtCore import QObject 5 | 6 | 7 | class ManagerBase(QObject): 8 | def __init__(self, context, **kwargs): 9 | super().__init__(**kwargs) 10 | 11 | self._log = logging.getLogger(self.__class__.__name__) 12 | 13 | self._ctx = context 14 | 15 | def eventFilter(self, event_object, event) -> bool: 16 | try: 17 | event_function = self.event_map.get(event.type()) 18 | except AttributeError: 19 | return False 20 | 21 | if event_function is not None: 22 | if not inspect.signature(event_function).parameters: 23 | return event_function() is True 24 | return event_function(event) is True 25 | 26 | return False 27 | -------------------------------------------------------------------------------- /gridplayer/player/managers/dialogs.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import pyqtSlot 2 | 3 | from gridplayer.dialogs.about import AboutDialog 4 | from gridplayer.dialogs.messagebox import QCustomMessageBox 5 | from gridplayer.player.managers.base import ManagerBase 6 | from gridplayer.utils.qt import translate 7 | 8 | 9 | class DialogsManager(ManagerBase): 10 | @property 11 | def commands(self): 12 | return { 13 | "about": lambda: AboutDialog(self.parent()).exec_(), 14 | "warning": self.warning, 15 | } 16 | 17 | @pyqtSlot(str) 18 | def error(self, message): 19 | QCustomMessageBox.critical(self.parent(), translate("Dialog", "Error"), message) 20 | 21 | @pyqtSlot(str) 22 | def warning(self, message): 23 | QCustomMessageBox.warning( 24 | self.parent(), translate("Dialog", "Warning"), message 25 | ) 26 | -------------------------------------------------------------------------------- /gridplayer/player/managers/instance_listener.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import pyqtSignal 2 | 3 | from gridplayer.player.managers.base import ManagerBase 4 | from gridplayer.utils.qt import qt_connect 5 | from gridplayer.utils.single_instance import Listener 6 | 7 | 8 | class InstanceListenerManager(ManagerBase): 9 | files_opened = pyqtSignal(list) 10 | 11 | def __init__(self, **kwargs): 12 | super().__init__(**kwargs) 13 | 14 | self._instance_listener = Listener() 15 | self._instance_listener.start() 16 | 17 | qt_connect( 18 | (self._instance_listener.open_files, self.files_opened), 19 | ) 20 | 21 | def cleanup(self): 22 | self._instance_listener.cleanup() 23 | -------------------------------------------------------------------------------- /gridplayer/player/managers/log.py: -------------------------------------------------------------------------------- 1 | from gridplayer.player.managers.base import ManagerBase 2 | from gridplayer.utils import log_config 3 | 4 | 5 | class LogManager(ManagerBase): 6 | @staticmethod 7 | def set_log_level(log_level): # noqa: WPS602 8 | log_config.set_root_level(log_level) 9 | -------------------------------------------------------------------------------- /gridplayer/player/managers/macos_fileopen.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from PyQt5.QtCore import QEvent, pyqtSignal 4 | 5 | from gridplayer.player.managers.base import ManagerBase 6 | from gridplayer.settings import Settings 7 | from gridplayer.utils.single_instance import is_the_only_instance 8 | from gridplayer.version import __app_name__ 9 | 10 | 11 | class MacOSFileOpenManager(ManagerBase): 12 | file_opened = pyqtSignal(list) 13 | 14 | @property 15 | def event_map(self): 16 | return {QEvent.FileOpen: self.handle_macos_fileopen} 17 | 18 | def handle_macos_fileopen(self, event): 19 | input_file = event.file() 20 | 21 | if not input_file: 22 | return 23 | 24 | Settings().sync() 25 | 26 | is_empty = not self._ctx.video_blocks 27 | is_only_empty = is_the_only_instance() and is_empty 28 | 29 | if Settings().get("player/one_instance") or is_only_empty: 30 | self.file_opened.emit([input_file]) 31 | else: 32 | subprocess.run( # noqa: S603, S607 33 | [ 34 | "open", 35 | "-n", 36 | f"/Applications/{__app_name__}.app", 37 | "--args", 38 | input_file, 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /gridplayer/player/managers/screensaver.py: -------------------------------------------------------------------------------- 1 | from gridplayer.player.managers.base import ManagerBase 2 | from gridplayer.settings import Settings 3 | from gridplayer.utils.keepawake import KeepAwake 4 | 5 | 6 | class ScreensaverManager(ManagerBase): 7 | def __init__(self, **kwargs): 8 | super().__init__(**kwargs) 9 | 10 | self.keepawake = KeepAwake() 11 | 12 | def screensaver_check(self, playing_videos_count): 13 | if not Settings().get("player/inhibit_screensaver"): 14 | self.keepawake.screensaver_on() 15 | return 16 | 17 | is_something_playing = playing_videos_count > 0 18 | 19 | if is_something_playing: 20 | self.keepawake.screensaver_off() 21 | else: 22 | self.keepawake.screensaver_on() 23 | -------------------------------------------------------------------------------- /gridplayer/player/managers/stream_proxy.py: -------------------------------------------------------------------------------- 1 | from threading import Lock 2 | 3 | from gridplayer.models.stream import Stream 4 | from gridplayer.player.managers.base import ManagerBase 5 | from gridplayer.utils.stream_proxy.stream_proxy import StreamProxy 6 | 7 | 8 | class StreamProxyManager(ManagerBase): 9 | def __init__(self, **kwargs): 10 | super().__init__(**kwargs) 11 | 12 | self._stream_proxy_lock = Lock() 13 | self._stream_proxy = None 14 | 15 | @property 16 | def stream_proxy(self): 17 | with self._stream_proxy_lock: 18 | if self._stream_proxy is None: 19 | self._stream_proxy = StreamProxy(parent=self) 20 | self._stream_proxy.start() 21 | 22 | return self._stream_proxy 23 | 24 | @property 25 | def commands(self): 26 | return { 27 | "add_stream": self.cmd_add_stream, 28 | } 29 | 30 | def cmd_add_stream(self, stream: Stream) -> str: 31 | return self.stream_proxy.add_stream(stream) 32 | 33 | def cleanup(self): 34 | if self._stream_proxy is not None: 35 | self._stream_proxy.cleanup() 36 | self._stream_proxy = None 37 | -------------------------------------------------------------------------------- /gridplayer/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/utils/__init__.py -------------------------------------------------------------------------------- /gridplayer/utils/app_dir.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | from PyQt5.QtCore import QStandardPaths 5 | 6 | from gridplayer.params import env 7 | 8 | PORTABLE_APP_DIR = "portable_data" 9 | 10 | 11 | def is_portable() -> bool: 12 | if not (env.IS_WINDOWS and env.IS_PYINSTALLER): 13 | return False 14 | 15 | portable_data_dir = Path(sys.executable).parent / PORTABLE_APP_DIR 16 | 17 | return portable_data_dir.is_dir() 18 | 19 | 20 | def get_app_data_dir() -> Path: 21 | if is_portable(): 22 | return Path(sys.executable).parent / PORTABLE_APP_DIR 23 | 24 | app_dir = Path(QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)) 25 | 26 | if not app_dir.is_dir(): 27 | app_dir.mkdir(parents=True) 28 | 29 | return app_dir 30 | -------------------------------------------------------------------------------- /gridplayer/utils/aspect_calc.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from gridplayer.params.static import VideoAspect 4 | 5 | 6 | def calc_resize_scale( 7 | video_dimensions: Tuple[int, int], 8 | size: Tuple[int, int], 9 | aspect: VideoAspect, 10 | scale: float, 11 | ) -> float: 12 | scr_x, scr_y = size 13 | vid_x, vid_y = video_dimensions 14 | 15 | if vid_x == 0 or vid_y == 0: 16 | return 0 17 | 18 | if scale > 1: 19 | if aspect == VideoAspect.FIT: 20 | resize_scale = max(scr_x / vid_x, scr_y / vid_y) * scale 21 | else: 22 | resize_scale = min(scr_x / vid_x, scr_y / vid_y) * scale 23 | 24 | else: 25 | resize_scale = 0 26 | 27 | return resize_scale 28 | 29 | 30 | def calc_crop( 31 | video_dimensions: Tuple[int, int], size: Tuple[int, int], aspect: VideoAspect 32 | ): 33 | scr_x, scr_y = size 34 | vid_x, vid_y = video_dimensions 35 | 36 | scaling = { 37 | VideoAspect.STRETCH: {"aspect": (scr_x, scr_y), "crop": (scr_x, scr_y)}, 38 | VideoAspect.FIT: {"aspect": (vid_x, vid_y), "crop": (scr_x, scr_y)}, 39 | VideoAspect.NONE: {"aspect": (vid_x, vid_y), "crop": (vid_x, vid_y)}, 40 | } 41 | 42 | return scaling[aspect]["aspect"], scaling[aspect]["crop"] 43 | -------------------------------------------------------------------------------- /gridplayer/utils/command_helpers.py: -------------------------------------------------------------------------------- 1 | class LOGIC(object): 2 | def __init__(self, *args): 3 | self.args = args 4 | 5 | 6 | class LOGIC_UNARY(object): 7 | def __init__(self, arg): 8 | self.arg = arg 9 | 10 | 11 | class NOT(LOGIC_UNARY): 12 | ... 13 | 14 | 15 | class AND(LOGIC): 16 | ... 17 | 18 | 19 | class OR(LOGIC): 20 | ... 21 | -------------------------------------------------------------------------------- /gridplayer/utils/darkmode.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtGui import QPalette 2 | 3 | from gridplayer.params import env 4 | 5 | if env.IS_MACOS: 6 | from Foundation import NSUserDefaults as NSUD 7 | 8 | def is_dark_mode(): 9 | style = NSUD.standardUserDefaults().stringForKey_("AppleInterfaceStyle") 10 | return style == "Dark" 11 | 12 | 13 | else: 14 | 15 | def is_dark_mode(): 16 | brightness_threshold = 128 17 | 18 | return QPalette().color(QPalette.Window).lightness() < brightness_threshold 19 | -------------------------------------------------------------------------------- /gridplayer/utils/excepthook.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import traceback 3 | 4 | from PyQt5.QtWidgets import QApplication 5 | 6 | from gridplayer.dialogs.exception import ExceptionDialog 7 | from gridplayer.utils.misc import force_terminate 8 | 9 | 10 | def excepthook(exc_type, exc_value, exc_tb): 11 | if exc_type is KeyboardInterrupt: 12 | logging.info("KeyboardInterrupt") 13 | force_terminate() 14 | 15 | # Log into file 16 | exception_txt = "".join(traceback.format_exception(exc_type, exc_value, exc_tb)) 17 | 18 | logging.getLogger("UNHANDLED").critical(exception_txt) 19 | 20 | # Terminate if Qt is not up 21 | if QApplication.instance() is None: 22 | force_terminate(1) 23 | 24 | for w in QApplication.topLevelWidgets(): 25 | w.hide() 26 | QApplication.restoreOverrideCursor() 27 | 28 | exc_dialog = ExceptionDialog(exception_txt) 29 | exc_dialog.exec_() 30 | 31 | force_terminate() 32 | -------------------------------------------------------------------------------- /gridplayer/utils/libvlc_options_parser.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from gridplayer.models.video import Video 4 | from gridplayer.params.static import VideoTransform 5 | 6 | TransformMap = { 7 | VideoTransform.ROTATE_90: "90", 8 | VideoTransform.ROTATE_180: "180", 9 | VideoTransform.ROTATE_270: "270", 10 | VideoTransform.HFLIP: "hflip", 11 | VideoTransform.VFLIP: "vflip", 12 | VideoTransform.TRANSPOSE: "transpose", 13 | VideoTransform.ANTITRANSPOSE: "antitranspose", 14 | } 15 | 16 | 17 | def get_vlc_options(video_params: Optional[Video]): 18 | vlc_options = [] 19 | 20 | if video_params is None: 21 | return vlc_options 22 | 23 | if video_params.transform != VideoTransform.NONE: 24 | option_str = TransformMap[video_params.transform] 25 | vlc_options.append(f"--video-filter=transform{{type='{option_str}'}}") 26 | 27 | return vlc_options 28 | -------------------------------------------------------------------------------- /gridplayer/utils/misc.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import signal 4 | from contextlib import suppress 5 | from multiprocessing.process import active_children 6 | 7 | from gridplayer.params import env 8 | 9 | 10 | def force_terminate_resource_tracker(): 11 | # windows doesn't have resource tracker 12 | if env.IS_WINDOWS: 13 | return 14 | 15 | # kill resource_tracker first so it won't complain about leaked resources 16 | from multiprocessing.resource_tracker import _resource_tracker # noqa: WPS450 17 | 18 | if _resource_tracker._pid is not None: # noqa: WPS437 19 | os.kill(_resource_tracker._pid, signal.SIGKILL) # noqa: WPS437 20 | 21 | 22 | def force_terminate_children(): 23 | for p in active_children(): 24 | p.terminate() 25 | 26 | 27 | def force_terminate_children_all(): 28 | force_terminate_resource_tracker() 29 | force_terminate_children() 30 | 31 | 32 | def force_terminate(exit_code: int = 0): 33 | with suppress(ValueError): 34 | force_terminate_children_all() 35 | os._exit(exit_code) # noqa: WPS437 36 | 37 | 38 | def is_url(s) -> bool: 39 | return bool(re.match("^[a-z]+://", s)) 40 | -------------------------------------------------------------------------------- /gridplayer/utils/next_file.py: -------------------------------------------------------------------------------- 1 | import random 2 | from pathlib import Path 3 | from typing import Optional 4 | 5 | from gridplayer.params.extensions import SUPPORTED_MEDIA_EXT 6 | 7 | 8 | def next_video_file(file: Path, is_shuffle=False) -> Optional[Path]: 9 | siblings = _file_siblings(file) 10 | 11 | if is_shuffle: 12 | random.shuffle(siblings) 13 | 14 | try: 15 | next_id = siblings.index(file) + 1 16 | except ValueError: 17 | return None 18 | 19 | if next_id >= len(siblings): 20 | next_id = 0 21 | 22 | return siblings[next_id] 23 | 24 | 25 | def previous_video_file(file: Path) -> Optional[Path]: 26 | siblings = _file_siblings(file) 27 | 28 | try: 29 | next_id = siblings.index(file) - 1 30 | except ValueError: 31 | return None 32 | 33 | if next_id >= len(siblings): 34 | next_id = 0 35 | 36 | return siblings[next_id] 37 | 38 | 39 | def _file_siblings(file: Path): 40 | return sorted( 41 | f 42 | for f in file.parent.iterdir() 43 | if f.is_file() and f.suffix[1:].lower() in SUPPORTED_MEDIA_EXT 44 | ) 45 | -------------------------------------------------------------------------------- /gridplayer/utils/qt.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from types import MappingProxyType 3 | 4 | from PyQt5.QtCore import QCoreApplication, QObject, Qt 5 | from PyQt5.QtWidgets import QApplication 6 | 7 | from gridplayer.params.static import VideoAspect 8 | 9 | QT_ASPECT_MAP = MappingProxyType( 10 | { 11 | VideoAspect.FIT: Qt.KeepAspectRatioByExpanding, 12 | VideoAspect.STRETCH: Qt.IgnoreAspectRatio, 13 | VideoAspect.NONE: Qt.KeepAspectRatio, 14 | } 15 | ) 16 | QT_LOG_IGNORED = ("requestActivate() called for",) 17 | 18 | 19 | class QABC(type(QObject), ABCMeta): # noqa: WPS606 20 | """Meta for abstract classes derived from QObject""" 21 | 22 | 23 | def is_modal_open(): 24 | return bool(QApplication.activeModalWidget() or QApplication.activePopupWidget()) 25 | 26 | 27 | def qt_connect(*connections): 28 | for c_sig, c_slot in connections: 29 | c_sig.connect(c_slot) 30 | 31 | 32 | def tr(text): 33 | return QCoreApplication.translate("@default", text) 34 | 35 | 36 | def translate(context, text, disambiguation=None): 37 | return QCoreApplication.translate(context, text, disambiguation) 38 | 39 | 40 | def is_qt_log_ignored(message): 41 | for ignored in QT_LOG_IGNORED: 42 | if ignored in message: 43 | return True 44 | 45 | return False 46 | -------------------------------------------------------------------------------- /gridplayer/utils/stream_proxy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/utils/stream_proxy/__init__.py -------------------------------------------------------------------------------- /gridplayer/utils/stream_proxy/stream_proxy.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from PyQt5.QtCore import QThread 4 | 5 | from gridplayer.models.stream import Stream 6 | from gridplayer.utils.stream_proxy.server import ProxyRequestHandler, StreamProxyServer 7 | 8 | 9 | class StreamProxy(QThread): 10 | def __init__(self, **kwargs): 11 | super().__init__(**kwargs) 12 | 13 | self._log = logging.getLogger(self.__class__.__name__) 14 | 15 | self.server = StreamProxyServer(("127.0.0.1", 0), ProxyRequestHandler) 16 | 17 | def run(self) -> None: 18 | self._log.debug("Starting stream proxy server") 19 | 20 | self.server.serve_forever() 21 | 22 | def add_stream(self, stream: Stream) -> str: 23 | return self.server.add_stream(stream) 24 | 25 | def cleanup(self) -> None: 26 | self._log.debug("Shutting down stream proxy server") 27 | 28 | self.server.shutdown() 29 | self.wait() 30 | -------------------------------------------------------------------------------- /gridplayer/utils/time_txt.py: -------------------------------------------------------------------------------- 1 | import time 2 | from typing import Optional 3 | 4 | HOUR_SECONDS = 3600 5 | DAY_SECONDS = HOUR_SECONDS * 24 6 | 7 | 8 | def get_time_txt( 9 | seconds: int, max_seconds: Optional[int] = None, strip: bool = False 10 | ) -> str: 11 | if max_seconds and max_seconds < seconds: 12 | max_seconds = None 13 | 14 | seconds_cnt = max_seconds or seconds 15 | 16 | if seconds >= DAY_SECONDS: 17 | days, seconds_cnt = divmod(seconds, DAY_SECONDS) 18 | else: 19 | days = 0 20 | 21 | clock = _fmt_time(seconds, seconds_cnt) 22 | 23 | if strip and seconds_cnt >= 60: 24 | clock = clock.lstrip("0") 25 | 26 | if days: 27 | return f"{days}d {clock}" 28 | 29 | return clock 30 | 31 | 32 | def _fmt_time(seconds, seconds_cnt): 33 | if seconds_cnt >= HOUR_SECONDS: 34 | return time.strftime("%H:%M:%S", time.gmtime(seconds)) 35 | elif seconds_cnt >= 60: 36 | return time.strftime("%M:%S", time.gmtime(seconds)) 37 | 38 | return time.strftime("0:%S", time.gmtime(seconds)) 39 | -------------------------------------------------------------------------------- /gridplayer/utils/url_resolve/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/utils/url_resolve/__init__.py -------------------------------------------------------------------------------- /gridplayer/utils/url_resolve/static.py: -------------------------------------------------------------------------------- 1 | import re 2 | from dataclasses import dataclass 3 | from types import MappingProxyType 4 | 5 | from gridplayer.models.stream import Streams 6 | 7 | YOUTUBE_MATCH = re.compile(r"^(?:https?://)?(?:www\.)?(?:youtube\.com|youtu\.be)") 8 | 9 | PLUGIN_URLS = MappingProxyType( 10 | { 11 | "streamlink": "https://streamlink.github.io/plugins.html", 12 | "yt-dlp": "https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md", 13 | } 14 | ) 15 | 16 | 17 | class BadURLException(Exception): 18 | """Exception for bad URLs""" 19 | 20 | 21 | class NoResolverPlugin(Exception): 22 | """Exception for no resolver plugin""" 23 | 24 | 25 | class StreamOfflineError(Exception): 26 | """Exception for offline streams""" 27 | 28 | 29 | @dataclass 30 | class ResolvedVideo(object): 31 | title: str 32 | is_live: bool 33 | streams: Streams 34 | -------------------------------------------------------------------------------- /gridplayer/version.py: -------------------------------------------------------------------------------- 1 | __app_name__ = "GridPlayer" 2 | __display_name__ = "GridPlayer" 3 | __author_name__ = "vzhd1701" 4 | __author_contact__ = "vzhd1701@gmail.com" 5 | __app_id__ = "com.vzhd1701.gridplayer" 6 | __version__ = "0.5.3" 7 | __version_date__ = "2023-10-17" 8 | __app_url__ = "https://github.com/vzhd1701/gridplayer" 9 | __app_license_url__ = "https://github.com/vzhd1701/gridplayer/blob/master/LICENSE" 10 | __app_bugtracker_url__ = "https://github.com/vzhd1701/gridplayer/issues" 11 | -------------------------------------------------------------------------------- /gridplayer/vlc_player/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/vlc_player/__init__.py -------------------------------------------------------------------------------- /gridplayer/vlc_player/libvlc.py: -------------------------------------------------------------------------------- 1 | from gridplayer.utils.libvlc_fixer import importing_embed_vlc 2 | 3 | # Need to set env variables before importing vlc 4 | with importing_embed_vlc(): 5 | import vlc # noqa: E402, F401, WPS433 6 | -------------------------------------------------------------------------------- /gridplayer/vlc_player/player_base_threaded.py: -------------------------------------------------------------------------------- 1 | from gridplayer.multiprocess.command_loop import CommandLoopThreaded 2 | from gridplayer.vlc_player.player_base import VlcPlayerBase 3 | 4 | 5 | class VlcPlayerThreaded(CommandLoopThreaded, VlcPlayerBase): 6 | def start(self): 7 | self.cmd_loop_start_thread(self.init_player) 8 | 9 | def notify_update_status(self, status, percent=0): 10 | self.cmd_send("update_status_emit", status, percent) 11 | 12 | def notify_error(self, error): 13 | self.cmd_send("error_state", error) 14 | 15 | def notify_time_changed(self, new_time): 16 | self.cmd_send("time_changed_emit", new_time) 17 | 18 | def notify_playback_status_changed(self, is_paused): 19 | self.cmd_send("playback_status_changed_emit", is_paused) 20 | 21 | def notify_load_video_done(self, media_track): 22 | self.cmd_send("load_video_done", media_track) 23 | 24 | def notify_snapshot_taken(self, snapshot_path): 25 | self.cmd_send("snapshot_taken_emit", snapshot_path) 26 | 27 | def loopback_load_video_st2_set_media(self): 28 | self.cmd_send_self("load_video_st2_set_media") 29 | 30 | def loopback_load_video_st3_extract_media_track(self): 31 | self.cmd_send_self("load_video_st3_extract_media_track") 32 | 33 | def loopback_load_video_st4_loaded(self): 34 | self.cmd_send_self("load_video_st4_loaded") 35 | -------------------------------------------------------------------------------- /gridplayer/widgets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/gridplayer/widgets/__init__.py -------------------------------------------------------------------------------- /resources/fonts/Hack/Hack-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/fonts/Hack/Hack-Bold.ttf -------------------------------------------------------------------------------- /resources/fonts/Hack/Hack-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/fonts/Hack/Hack-BoldItalic.ttf -------------------------------------------------------------------------------- /resources/fonts/Hack/Hack-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/fonts/Hack/Hack-Italic.ttf -------------------------------------------------------------------------------- /resources/fonts/Hack/Hack-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/fonts/Hack/Hack-Regular.ttf -------------------------------------------------------------------------------- /resources/icons/basic/001-achievement.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/003-add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/008-tick.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/010-attach.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/011-backwards.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/013-bag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/014-ban.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/015-low battery.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/018-bluetooth.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/019-board.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/020-book.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/021-bookmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/022-notebook.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/023-box.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/024-briefcase.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/027-bar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/033-center align.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/034-center align.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/035-full battery.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/042-sand clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/044-code.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/048-monitor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/052-crop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/055-pendrive.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/057-calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/059-smartphone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/061-diary.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/063-stethoscope.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/064-approved.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/067-down arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/074-pen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/083-backward.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/084-forward.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/085-paper.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/086-flag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/087-flag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/088-thunder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/090-flask.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/092-folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/093-folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/095-forward.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/099-squares.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/101-headphones.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/112-left alignment.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/113-left arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/115-placeholder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/121-letter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/122-measurement.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/123-menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/124-message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/125-microphone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/126-minimize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/127-mobile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/128-monitor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/129-more.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/130-note.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/133-slider.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/134-slider.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/136-log out.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/140-pause.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/145-play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/148-profile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/156-right alignment.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/157-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/163-search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/165-set square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/168-shield.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/173-star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/175-syringe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/176-tablet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/177-tag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/179-document.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/180-ticket.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/181-tie.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/185-top alignment.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/195-usb cable.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/196-usb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/223-video.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/226-mute.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/227-low volume.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/228-wall clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/234-zoom.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/235-zoom in.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/basic/236-zoom out.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/custom/001-minimize window.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/custom/006-aspect.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /resources/icons/custom/007-rows first.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/008-columns first.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/009-all.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /resources/icons/custom/010-jump.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/011-random.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /resources/icons/custom/013-loop start.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/014-loop end.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/019-save file.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /resources/icons/custom/020-plus-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/custom/021-plus-5.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/022-plus-10.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /resources/icons/custom/023-minus-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/custom/024-minus-5.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/025-minus-10.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /resources/icons/custom/030-question.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /resources/icons/custom/031-grid fit.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /resources/icons/custom/034-previous frame.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/035-next frame.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/039-information.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /resources/icons/custom/041-audio only.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/custom/042-error.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /resources/icons/custom/044-seek sync time.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /resources/icons/custom/045-seek sync percent.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /resources/icons/custom/046-empty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/icons/custom/047-warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /resources/icons/custom/049-checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/custom/050-track.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /resources/icons/custom/051-add url.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /resources/icons/custom/052-media file.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/054-video.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/custom/055-stereo mode.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/custom/057-volume increase.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/058-volume decrease.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/059-volume unmute.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /resources/icons/custom/062-play pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /resources/icons/custom/064-crop.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /resources/icons/flags/de_DE.svg: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /resources/icons/flags/fr_FR.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /resources/icons/flags/hu_HU.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /resources/icons/flags/it_IT.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /resources/icons/flags/ja_JP.svg: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /resources/icons/flags/ko_KR.svg: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /resources/icons/flags/nl_NL.svg: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /resources/icons/flags/pl_PL.svg: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /resources/icons/flags/ru_RU.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /resources/icons/flags/zh_CN.svg: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /resources/icons/main/png/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/128x128.png -------------------------------------------------------------------------------- /resources/icons/main/png/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/16x16.png -------------------------------------------------------------------------------- /resources/icons/main/png/16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/16x16@2x.png -------------------------------------------------------------------------------- /resources/icons/main/png/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/24x24.png -------------------------------------------------------------------------------- /resources/icons/main/png/24x24@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/24x24@2x.png -------------------------------------------------------------------------------- /resources/icons/main/png/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/256x256.png -------------------------------------------------------------------------------- /resources/icons/main/png/256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/256x256@2x.png -------------------------------------------------------------------------------- /resources/icons/main/png/256x256@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/256x256@4x.png -------------------------------------------------------------------------------- /resources/icons/main/png/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/32x32.png -------------------------------------------------------------------------------- /resources/icons/main/png/32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/32x32@2x.png -------------------------------------------------------------------------------- /resources/icons/main/png/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/48x48.png -------------------------------------------------------------------------------- /resources/icons/main/png/48x48@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/48x48@2x.png -------------------------------------------------------------------------------- /resources/icons/main/png/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/png/64x64.png -------------------------------------------------------------------------------- /resources/icons/main/sys/macos.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/sys/macos.icns -------------------------------------------------------------------------------- /resources/icons/main/sys/windows.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/main/sys/windows.ico -------------------------------------------------------------------------------- /resources/icons/playlist/png/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/128x128.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/16x16.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/16x16@2x.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/24x24.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/24x24@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/24x24@2x.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/256x256.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/256x256@2x.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/256x256@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/256x256@4x.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/32x32.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/32x32@2x.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/48x48.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/48x48@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/48x48@2x.png -------------------------------------------------------------------------------- /resources/icons/playlist/png/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/png/64x64.png -------------------------------------------------------------------------------- /resources/icons/playlist/sys/macos.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/sys/macos.icns -------------------------------------------------------------------------------- /resources/icons/playlist/sys/windows.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/icons/playlist/sys/windows.ico -------------------------------------------------------------------------------- /resources/public/dl_appimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/dl_appimage.png -------------------------------------------------------------------------------- /resources/public/dl_dmg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/dl_dmg.png -------------------------------------------------------------------------------- /resources/public/dl_flathub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/dl_flathub.png -------------------------------------------------------------------------------- /resources/public/dl_snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/dl_snap.png -------------------------------------------------------------------------------- /resources/public/dl_windows_installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/dl_windows_installer.png -------------------------------------------------------------------------------- /resources/public/dl_windows_portable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/dl_windows_portable.png -------------------------------------------------------------------------------- /resources/public/screenshot-001-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-001-thumb.png -------------------------------------------------------------------------------- /resources/public/screenshot-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-001.png -------------------------------------------------------------------------------- /resources/public/screenshot-002-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-002-thumb.png -------------------------------------------------------------------------------- /resources/public/screenshot-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-002.png -------------------------------------------------------------------------------- /resources/public/screenshot-003-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-003-thumb.png -------------------------------------------------------------------------------- /resources/public/screenshot-003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-003.png -------------------------------------------------------------------------------- /resources/public/screenshot-004-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-004-thumb.png -------------------------------------------------------------------------------- /resources/public/screenshot-004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vzhd1701/gridplayer/939dd5a5235e8a852aa1b0e51b00538923a5bbbe/resources/public/screenshot-004.png -------------------------------------------------------------------------------- /scripts/_helpers/blacklist_clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIST_DIR="$1" 4 | BL_LIST_FILE="$2" 5 | 6 | while IFS= read -r del_file; do 7 | if [[ "$del_file" =~ ^#.* ]]; then 8 | continue 9 | fi 10 | 11 | echo "Removing $DIST_DIR/$del_file" 12 | find "$DIST_DIR" -iwholename "$DIST_DIR/$del_file" -exec rm -rf {} + 13 | done < "$BL_LIST_FILE" 14 | -------------------------------------------------------------------------------- /scripts/_helpers/kacl.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | from pathlib import Path 4 | 5 | import keepachangelog 6 | 7 | 8 | def extract_version(version_file: Path): 9 | version_file_text = version_file.read_text() 10 | version_match = re.search( 11 | r"^__version__ = ['\"]([^'\"]+)['\"]", version_file_text, re.M 12 | ) 13 | if version_match is None: 14 | raise ValueError("Could not find version in version.py") 15 | return version_match.group(1) 16 | 17 | 18 | def ensure_unix_newlines(file: Path): 19 | file_bytes = file.read_bytes() 20 | file_bytes = file_bytes.replace(b"\r\n", b"\n") 21 | file.write_bytes(file_bytes) 22 | 23 | 24 | def main(): 25 | parser = argparse.ArgumentParser( 26 | description="Manipulate CHANGELOG.md according to keep-a-changelog standard" 27 | ) 28 | 29 | parser.add_argument("version_file", help="Path to version.py", type=Path) 30 | parser.add_argument("changelog", help="Path to CHANGELOG.md file", type=Path) 31 | 32 | args = parser.parse_args() 33 | 34 | new_version = extract_version(args.version_file) 35 | keepachangelog.release(args.changelog, new_version) 36 | 37 | ensure_unix_newlines(args.changelog) 38 | 39 | 40 | if __name__ == "__main__": 41 | main() 42 | -------------------------------------------------------------------------------- /scripts/_local/appimage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd "$( dirname $0 )" && pwd )" 6 | 7 | . "scripts/init_app_vars.sh" 8 | 9 | if [ "$1" == "run" ]; then 10 | "$DIST_DIR"/*.AppImage "${@:2}" 11 | exit 0 12 | fi 13 | 14 | rm -f "$DIST_DIR"/*.whl 15 | poetry build -f wheel 16 | 17 | bash "$SCRIPTS_DIR/linux_meta/build.sh" 18 | 19 | bash "$SCRIPTS_DIR/appimage/build.sh" "$@" 20 | -------------------------------------------------------------------------------- /scripts/_local/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" == "all" ]; then 4 | conventional-changelog -u 5 | exit 0 6 | fi 7 | 8 | conventional-changelog -p conventionalcommits 9 | -------------------------------------------------------------------------------- /scripts/_local/flatpak.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd "$( dirname $0 )" && pwd )" 6 | 7 | . "scripts/init_app_vars.sh" 8 | 9 | if [ "$1" == "run" ]; then 10 | flatpak run ${APP_ID} "${@:2}" 11 | exit 0 12 | elif [ "$1" == "del" ]; then 13 | flatpak uninstall -y ${APP_ID} 14 | exit 0 15 | fi 16 | 17 | if ! command -v flatpak; then 18 | sudo apt install flatpak 19 | flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 20 | 21 | flatpak install flathub org.kde.Platform//5.15-22.08 22 | flatpak install flathub org.kde.Sdk//5.15-22.08 23 | fi 24 | 25 | poetry build -f sdist 26 | poetry export --without-hashes -f requirements.txt --output "$DIST_DIR/requirements.txt" 27 | 28 | bash "$SCRIPTS_DIR/linux_meta/build.sh" 29 | bash "$SCRIPTS_DIR/flatpak/generate_dependencies.sh" 30 | 31 | bash "$SCRIPTS_DIR/flatpak/build.sh" "$@" 32 | -------------------------------------------------------------------------------- /scripts/_local/flatpak_git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd "$( dirname $0 )" && pwd )" 6 | 7 | . "scripts/init_app_vars.sh" 8 | 9 | poetry build -f sdist 10 | 11 | . "$ROOT_DIR"/.local/secrets 12 | 13 | bash "$SCRIPTS_DIR/linux_meta/build.sh" 14 | 15 | bash "$SCRIPTS_DIR/flatpak/build_git.sh" "$@" 16 | -------------------------------------------------------------------------------- /scripts/_local/macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd "$( dirname $0 )" && pwd )" 6 | 7 | check() { 8 | which $1 >/dev/null 9 | } 10 | 11 | # install poetry 12 | if ! check poetry; then 13 | echo "Installing poetry" 14 | 15 | ln -s /etc/ssl/* /Library/Frameworks/Python.framework/Versions/3.*/etc/openssl 16 | curl -sSL https://install.python-poetry.org | python3 - 17 | echo "export PATH=\"/Users/admin/Library/Python/3.8/bin:\$PATH\"" >> ~/.bash_profile 18 | source ~/.bash_profile 19 | fi 20 | 21 | brew install gnu-sed wget node graphicsmagick imagemagick 22 | check create-dmg || npm install --global create-dmg 23 | 24 | pip3 install urllib3 virtualenv 25 | 26 | . "scripts/init_app_vars.sh" 27 | 28 | mkdir -p "$BUILD_DIR" 29 | 30 | poetry export --without-hashes -o "$BUILD_DIR/requirements.txt" 31 | 32 | bash "$SCRIPTS_DIR/pyinstaller/build_mac.sh" 33 | 34 | bash "$SCRIPTS_DIR/macos/build_dmg.sh" 35 | -------------------------------------------------------------------------------- /scripts/_local/snap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd "$( dirname $0 )" && pwd )" 6 | 7 | . "scripts/init_app_vars.sh" 8 | 9 | if [ "$1" == "run" ]; then 10 | snap run ${APP_ID} "${@:2}" 11 | exit 0 12 | elif [ "$1" == "del" ]; then 13 | sudo snap remove ${APP_ID} 14 | exit 0 15 | fi 16 | 17 | poetry build -f wheel 18 | 19 | bash "$SCRIPTS_DIR/linux_meta/build.sh" 20 | 21 | bash "$SCRIPTS_DIR/snap/build.sh" "$@" 22 | -------------------------------------------------------------------------------- /scripts/_local/windows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd "$( dirname $0 )" && pwd )" 6 | 7 | . "scripts/init_app_vars.sh" 8 | 9 | # install python with venv, poetry 10 | # install wget, zip 11 | 12 | mkdir -p "$BUILD_DIR" 13 | poetry export --without-hashes -o "$BUILD_DIR/requirements.txt" 14 | 15 | bash "$SCRIPTS_DIR/pyinstaller/build_win.sh" 16 | 17 | if [ "$1" == "build" ]; then 18 | exit 0 19 | fi 20 | 21 | bash "$SCRIPTS_DIR/windows/build_packages.sh" 22 | 23 | # Remove directory produced by pyinstaller 24 | #find "$DIST_DIR" -maxdepth 1 -mindepth 1 -type d -exec rm -r {} \; 25 | -------------------------------------------------------------------------------- /scripts/appimage/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | BASE_IMAGE_NAME="manylinux2014_x86_64" 6 | BASE_IMAGE_URL="quay.io/pypa/$BASE_IMAGE_NAME" 7 | NAME="appimage_build" 8 | 9 | if [ "$1" == "test" ]; then 10 | docker run --rm -it \ 11 | -v $(pwd)/:/build/ \ 12 | -w /build/ \ 13 | "$BASE_IMAGE_URL" bash 14 | exit 0 15 | fi 16 | 17 | if [ ! "$(docker ps -a -q -f name=$NAME)" ]; then 18 | echo "Creating new container" 19 | 20 | docker run --name "$NAME" \ 21 | -v $(pwd)/:/build/ \ 22 | -w /build/ \ 23 | "$BASE_IMAGE_URL" \ 24 | bash "scripts/appimage/build_docker.sh" 25 | else 26 | echo "Re-running existing container" 27 | 28 | docker start -a "$NAME" 29 | fi 30 | -------------------------------------------------------------------------------- /scripts/flatpak/app.yml: -------------------------------------------------------------------------------- 1 | app-id: "{APP_ID}" 2 | runtime: org.kde.Platform 3 | runtime-version: "5.15-22.08" 4 | sdk: org.kde.Sdk 5 | command: "{APP_MODULE}" 6 | finish-args: 7 | # OpenGL 8 | - "--device=dri" 9 | # GUI 10 | - "--share=ipc" 11 | - "--socket=x11" 12 | # Sound 13 | - "--socket=pulseaudio" 14 | # Files & network 15 | - "--share=network" 16 | - "--filesystem=host" 17 | - "--filesystem=xdg-run/gvfs" 18 | # Screen inhibit 19 | - "--talk-name=org.freedesktop.ScreenSaver" 20 | # Can't use Wayland 21 | - "--env=QT_QPA_PLATFORM=xcb" 22 | # Disable annoying warnings 23 | # Qt: Session managment error: Could not open network socket 24 | # https://stackoverflow.com/questions/986964/qt-session-management-error 25 | - "--env=SESSION_MANAGER=" 26 | 27 | cleanup: 28 | - "/include" 29 | - "/lib/*.a" 30 | - "/lib/*.la" 31 | - "/lib/debug" 32 | - "/lib/pkgconfig" 33 | - "/share/man" 34 | 35 | modules: 36 | - libvlc.yml 37 | - pyqt.yml 38 | - dependencies.yml 39 | -------------------------------------------------------------------------------- /scripts/flatpak/app_git.yml: -------------------------------------------------------------------------------- 1 | - name: poetry-core 2 | cleanup: 3 | - "*" 4 | buildsystem: simple 5 | build-commands: 6 | - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" 7 | --prefix=${FLATPAK_DEST} "poetry-core==1.6.0" --no-build-isolation 8 | sources: 9 | - type: file 10 | url: https://files.pythonhosted.org/packages/a6/6d/7d86771d647b033876cbaeb84678a0c2d538e1e1bfcf54bb6595b656a7cb/poetry_core-1.6.0-py3-none-any.whl 11 | sha256: ff96620579f02ec30ee9f789fa5dfdcddd9ab4ac848394f6f8f2f4e88306b534 12 | - name: {APP_MODULE} 13 | buildsystem: simple 14 | build-commands: 15 | - pip3 install --no-index --no-deps --no-build-isolation --prefix=${FLATPAK_DEST} --root=/ . 16 | 17 | - install -Dm 644 meta/{APP_ID}.desktop ${FLATPAK_DEST}/share/applications/{APP_ID}.desktop 18 | - install -Dm 644 meta/{APP_ID}.appdata.xml ${FLATPAK_DEST}/share/metainfo/{APP_ID}.appdata.xml 19 | - install -Dm 644 meta/{APP_ID}.xml ${FLATPAK_DEST}/share/mime/packages/{APP_ID}.xml 20 | - (cd meta/icons && find * -type f -exec install -Dm 644 "{}" "${FLATPAK_DEST}/share/icons/{}" \;) 21 | sources: 22 | - type: git 23 | url: {GIT_URL} 24 | commit: {GIT_COMMIT} 25 | -------------------------------------------------------------------------------- /scripts/flatpak/app_local.yml: -------------------------------------------------------------------------------- 1 | - name: poetry-core 2 | cleanup: 3 | - "*" 4 | buildsystem: simple 5 | build-commands: 6 | - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" 7 | --prefix=${FLATPAK_DEST} "poetry-core==1.6.0" --no-build-isolation 8 | sources: 9 | - type: file 10 | url: https://files.pythonhosted.org/packages/a6/6d/7d86771d647b033876cbaeb84678a0c2d538e1e1bfcf54bb6595b656a7cb/poetry_core-1.6.0-py3-none-any.whl 11 | sha256: ff96620579f02ec30ee9f789fa5dfdcddd9ab4ac848394f6f8f2f4e88306b534 12 | - name: {APP_MODULE} 13 | buildsystem: simple 14 | build-commands: 15 | - pip3 install --no-index --no-deps --no-build-isolation --prefix=${FLATPAK_DEST} --root=/ . 16 | sources: 17 | - type: archive 18 | path: {TAR_FILE} 19 | sha256: {TAR_FILE_SHA256} 20 | - name: meta 21 | buildsystem: simple 22 | build-commands: 23 | - install -Dm 644 {APP_ID}.desktop ${FLATPAK_DEST}/share/applications/{APP_ID}.desktop 24 | - install -Dm 644 {APP_ID}.appdata.xml ${FLATPAK_DEST}/share/metainfo/{APP_ID}.appdata.xml 25 | - install -Dm 644 {APP_ID}.xml ${FLATPAK_DEST}/share/mime/packages/{APP_ID}.xml 26 | - (cd icons && find * -type f -exec install -Dm 644 "{}" "${FLATPAK_DEST}/share/icons/{}" \;) 27 | sources: 28 | - type: dir 29 | path: ./meta 30 | -------------------------------------------------------------------------------- /scripts/flatpak/libvlc/gsm-makefile.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Makefile b/Makefile 2 | index f7455de..620f773 100644 3 | --- a/Makefile 4 | +++ b/Makefile 5 | @@ -389,6 +389,7 @@ $(GSM_INSTALL_MAN)/gsm_print.3: $(MAN)/gsm_print.3 6 | 7 | $(GSM_INSTALL_INC)/gsm.h: $(INC)/gsm.h 8 | -rm $(RMFLAGS) $@ 9 | + mkdir $(GSM_INSTALL_INC) 10 | cp $? $@ 11 | chmod 444 $@ 12 | 13 | -------------------------------------------------------------------------------- /scripts/flatpak/libvlc/libebml-gcc11.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/EbmlString.cpp b/src/EbmlString.cpp 2 | index 27e55fd..4c05fcf 100644 3 | --- a/src/EbmlString.cpp 4 | +++ b/src/EbmlString.cpp 5 | @@ -34,6 +34,7 @@ 6 | \author Steve Lhomme