├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── [QC]_screenshot-qc.txt ├── dependabot.yml └── workflows │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.MD ├── Justfile ├── LICENSE ├── README.MD ├── build-aux ├── build.sh ├── generate-lupdate-project-file.py ├── generate-qt-creator-project-file.py ├── icon.ico └── insert-dependency-versions.py ├── data ├── config │ ├── backup-template.jinja │ ├── export-template.jinja │ ├── input.conf │ ├── mpv-linux.conf │ └── mpv-windows.conf ├── fonts │ ├── NotoSans-Bold.ttf │ ├── NotoSans-Italic.ttf │ ├── NotoSans-Regular.ttf │ ├── NotoSans-SemiBold.ttf │ ├── NotoSansHebrew-Bold.ttf │ ├── NotoSansHebrew-Regular.ttf │ ├── NotoSansHebrew-SemiBold.ttf │ └── NotoSansMono-Regular.ttf ├── icon.svg ├── icons │ ├── aspect_ratio_black_24dp.svg │ ├── close_black_24dp.svg │ ├── close_fullscreen_black_24dp.svg │ ├── comment_black_24dp.svg │ ├── content_copy_black_24dp.svg │ ├── delete_black_24dp.svg │ ├── done_black_24dp.svg │ ├── download_black_24dp.svg │ ├── edit_black_24dp.svg │ ├── exit_to_app_black_24dp.svg │ ├── expand_more_black_24dp.svg │ ├── file_open_black_24dp.svg │ ├── horizontal_split_black_24dp.svg │ ├── info_black_24dp.svg │ ├── inventory_black_24dp.svg │ ├── keyboard_arrow_down_black_24dp.svg │ ├── keyboard_arrow_left_black_24dp.svg │ ├── keyboard_arrow_right_black_24dp.svg │ ├── keyboard_arrow_up_black_24dp.svg │ ├── keyboard_backspace_black_24dp.svg │ ├── keyboard_black_24dp.svg │ ├── keyboard_return_black_24dp.svg │ ├── language_black_24dp.svg │ ├── launch_black_24dp.svg │ ├── minimize_black_24dp.svg │ ├── more_vert_black_24dp.svg │ ├── movie_black_24dp.svg │ ├── movie_edit_black_24dp.svg │ ├── notes_black_24dp.svg │ ├── open_in_full_black_24dp.svg │ ├── palette_black_24dp.svg │ ├── play_arrow_black_24dp.svg │ ├── save_alt_black_24dp.svg │ ├── save_as_black_24dp.svg │ ├── save_black_24dp.svg │ ├── settings_backup_restore_black_24dp.svg │ ├── shortcut_black_24dp.svg │ ├── space_bar_black_24dp.svg │ ├── subtitles_black_24dp.svg │ ├── title_black_24dp.svg │ ├── update_black_24dp.svg │ ├── upload_black_24dp.svg │ └── vertical_split_black_24dp.svg └── themes │ ├── 01-dark.toml │ ├── 02-light.toml │ ├── Justfile │ ├── color-generator.py │ └── theme.template ├── docs ├── configuration.md ├── internationalization.md └── releasing.md ├── i18n ├── de-DE.ts ├── es-MX.ts ├── he-IL.ts └── it-IT.ts ├── main.py ├── mpvqc ├── __init__.py ├── application.py ├── injections.py ├── logging.py ├── models.py ├── pyobjects │ ├── __init__.py │ ├── application_paths.py │ ├── comment_model │ │ ├── __init__.py │ │ ├── item.py │ │ ├── model.py │ │ ├── roles.py │ │ ├── searcher.py │ │ ├── undo.py │ │ └── utils.py │ ├── comment_type_validator.py │ ├── export_template_model.py │ ├── extended_document_exporter.py │ ├── manager │ │ ├── __init__.py │ │ ├── impl.py │ │ └── state.py │ ├── player.py │ ├── player_files.py │ ├── player_framebuffer_object.py │ ├── player_properties.py │ ├── player_win_id.py │ ├── text_validator.py │ ├── themes.py │ ├── utility.py │ └── version_checker.py ├── services │ ├── __init__.py │ ├── application_environment.py │ ├── application_paths.py │ ├── comment_type_validator.py │ ├── document_exporter.py │ ├── document_importer.py │ ├── file_startup.py │ ├── font_loader.py │ ├── formatter_time.py │ ├── frameless │ │ ├── __init__.py │ │ ├── linux │ │ │ ├── __init__.py │ │ │ └── event.py │ │ ├── service.py │ │ └── win │ │ │ ├── __init__.py │ │ │ ├── c_structures.py │ │ │ ├── event.py │ │ │ └── utils.py │ ├── key_command.py │ ├── mimetype_provider.py │ ├── operating_system_zoom_detector.py │ ├── player.py │ ├── resource.py │ ├── resource_reader.py │ ├── reverse_translator.py │ ├── settings.py │ ├── theme │ │ ├── __init__.py │ │ ├── parser.py │ │ ├── schema.py │ │ ├── service.py │ │ └── utils.py │ ├── type_mapper.py │ ├── version_checker.py │ └── video_selector.py └── startup.py ├── pyproject.toml ├── qml ├── Main.qml ├── app │ ├── MpvqcApplication.qml │ ├── MpvqcContent.qml │ ├── MpvqcContentSplitView.qml │ ├── MpvqcDragAndDropHandler.qml │ ├── MpvqcLabelWidthCalculator.qml │ ├── MpvqcManager.qml │ ├── MpvqcQuitHandler.qml │ ├── MpvqcResizeToOriginalResolutionHandler.qml │ ├── MpvqcTheme.qml │ ├── MpvqcWindowVisibilityHandler.qml │ ├── qmldir │ ├── tst_MpvqcDragAndDropHandler.qml │ ├── tst_MpvqcLabelWidthCalculator.qml │ ├── tst_MpvqcQuitHandler.qml │ ├── tst_MpvqcResizeToOriginalResolutionHandler.qml │ └── tst_MpvqcWindowVisibilityHandler.qml ├── dialogs │ ├── MpvqcDialogExportDocument.qml │ ├── MpvqcDialogImportDocuments.qml │ ├── MpvqcDialogImportSubtitles.qml │ ├── MpvqcDialogImportVideo.qml │ ├── MpvqcMessageBoxDocumentNotCompatible.qml │ ├── MpvqcMessageBoxExtendedExport.qml │ ├── MpvqcMessageBoxNewDocument.qml │ ├── MpvqcMessageBoxQuit.qml │ ├── MpvqcMessageBoxVersionCheck.qml │ ├── MpvqcMessageBoxVideoFound.qml │ ├── about │ │ ├── MpvqcAboutView.qml │ │ ├── MpvqcCreditsView.qml │ │ ├── MpvqcDependenciesView.qml │ │ ├── MpvqcDependency.qml │ │ └── MpvqcDialogAbout.qml │ ├── appearance │ │ ├── MpvqcColorView.qml │ │ ├── MpvqcDialogAppearance.qml │ │ ├── MpvqcThemeView.qml │ │ ├── tst_MpvqcColorView.qml │ │ └── tst_MpvqcThemeView.qml │ ├── backup │ │ ├── MpvqcBackupView.qml │ │ ├── MpvqcDialogBackup.qml │ │ └── tst_MpvqcBackupView.qml │ ├── commenttypes │ │ ├── MpvqcCommentTypesView.qml │ │ ├── MpvqcCommentTypesViewController.qml │ │ ├── MpvqcDialogCommentTypes.qml │ │ ├── MpvqcInputComponent.qml │ │ ├── MpvqcInputControls.qml │ │ ├── MpvqcInputTextField.qml │ │ ├── MpvqcList.qml │ │ ├── MpvqcListControls.qml │ │ ├── tst_MpvqcCommentTypesViewController.qml │ │ ├── tst_MpvqcInputComponent.qml │ │ ├── tst_MpvqcInputControls.qml │ │ ├── tst_MpvqcInputTextField.qml │ │ ├── tst_MpvqcList.qml │ │ └── tst_MpvqcListControls.qml │ ├── editinput │ │ ├── MpvqcDialogEditInput.qml │ │ ├── MpvqcEditInputView.qml │ │ └── tst_MpvqcDialogEditInput.qml │ ├── editmpv │ │ ├── MpvqcDialogEditMpv.qml │ │ ├── MpvqcEditMpvView.qml │ │ └── tst_MpvqcDialogEditMpv.qml │ ├── export │ │ ├── MpvqcDialogExport.qml │ │ ├── MpvqcExportView.qml │ │ └── tst_MpvqcExportView.qml │ ├── import │ │ ├── MpvqcDialogImport.qml │ │ ├── MpvqcImportView.qml │ │ └── tst_MpvqcImportView.qml │ ├── qmldir │ ├── shortcuts │ │ ├── MpvqcDialogShortcuts.qml │ │ ├── MpvqcShortcut.qml │ │ ├── MpvqcShortcutButton.qml │ │ ├── MpvqcShortcutFilterModel.qml │ │ ├── MpvqcShortcutModel.qml │ │ └── MpvqcShortcutView.qml │ ├── tst_MpvqcDialogExportDocument.qml │ ├── tst_MpvqcDialogImportDocuments.qml │ ├── tst_MpvqcDialogImportSubtitles.qml │ └── tst_MpvqcDialogImportVideo.qml ├── footer │ ├── MpvqcFooter.qml │ ├── MpvqcFooterContent.qml │ ├── qmldir │ └── tst_MpvqcFooter.qml ├── header │ ├── MpvqcHeader.qml │ ├── MpvqcMenuFile.qml │ ├── MpvqcMenuHelp.qml │ ├── MpvqcMenuOptions.qml │ ├── MpvqcMenuVideo.qml │ ├── MpvqcSubMenuExtendedExport.qml │ ├── MpvqcSubMenuLanguage.qml │ ├── MpvqcSubMenuSplitViewOrientation.qml │ ├── MpvqcSubMenuWindowTitle.qml │ ├── MpvqcWindowControls.qml │ ├── MpvqcWindowTitle.qml │ ├── qmldir │ ├── tst_MpvqcMenuFile.qml │ ├── tst_MpvqcMenuHelp.qml │ ├── tst_MpvqcMenuOptions.qml │ ├── tst_MpvqcMenuVideo.qml │ ├── tst_MpvqcSubMenuLanguage.qml │ ├── tst_MpvqcSubMenuSplitViewOrientation.qml │ ├── tst_MpvqcSubMenuWindowTitle.qml │ ├── tst_MpvqcWindowControls.qml │ └── tst_MpvqcWindowTitle.qml ├── models │ ├── MpvqcArtworkModel.qml │ ├── MpvqcDeveloperModel.qml │ ├── MpvqcLanguageModel.qml │ ├── MpvqcLibraryModel.qml │ └── qmldir ├── player │ ├── MpvqcPlayerLinux.qml │ ├── MpvqcPlayerMouseArea.qml │ ├── MpvqcPlayerWindows.qml │ ├── qmldir │ └── tst_MpvqcPlayerMouseArea.qml ├── settings │ ├── MpvqcSettings.qml │ └── qmldir ├── shared │ ├── MpvqcDebugRectangle.qml │ ├── MpvqcDialog.qml │ ├── MpvqcHeader.qml │ ├── MpvqcKeyboardFocusableButtonBox.qml │ ├── MpvqcMenu.qml │ ├── MpvqcMessageBox.qml │ ├── MpvqcNewCommentMenu.qml │ ├── MpvqcSpinBoxRow.qml │ ├── MpvqcSwitchRow.qml │ ├── MpvqcTextFieldRow.qml │ ├── qmldir │ ├── tst_MpvqcDialog.qml │ ├── tst_MpvqcMenu.qml │ ├── tst_MpvqcNewCommentMenu.qml │ ├── tst_MpvqcSpinBoxRow.qml │ ├── tst_MpvqcSwitchRow.qml │ └── tst_MpvqcTextFieldRow.qml └── table │ ├── MpvqcCommentHighlighter.js │ ├── MpvqcCommentList.qml │ ├── MpvqcCommentListDelegate.qml │ ├── MpvqcContextMenu.qml │ ├── MpvqcDeleteCommentMessageBox.qml │ ├── MpvqcEditCommentPopup.qml │ ├── MpvqcEditCommentTypeMenu.qml │ ├── MpvqcEditTimePopup.qml │ ├── MpvqcPlaceholder.qml │ ├── MpvqcSearchBox.qml │ ├── MpvqcTable.qml │ ├── qmldir │ └── tst_MpvqcCommentList.qml ├── test ├── __init__.py ├── conftest.py ├── mocks.py ├── pyobjects │ ├── __init__.py │ ├── comment_model │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_add.py │ │ ├── test_clear.py │ │ ├── test_clipboard.py │ │ ├── test_get_all.py │ │ ├── test_import.py │ │ ├── test_remove.py │ │ ├── test_search.py │ │ ├── test_undo_redo.py │ │ └── test_update.py │ ├── manager │ │ ├── __init__.py │ │ ├── test_impl.py │ │ └── test_state.py │ └── test_export_templates_model.py ├── services │ ├── __init__.py │ ├── test_application_paths.py │ ├── test_comment_type_validator.py │ ├── test_document_exporter.py │ ├── test_document_importer.py │ ├── test_file_startup.py │ ├── test_font_loader.py │ ├── test_formatter_time.py │ ├── test_key_command.py │ ├── test_player.py │ ├── test_resource.py │ ├── test_resource_reader.py │ ├── test_reverse_translator.py │ ├── test_settings.py │ ├── test_version_checker.py │ ├── test_video_selector.py │ └── theme │ │ ├── __init__.py │ │ ├── test_parser.py │ │ ├── test_service.py │ │ └── test_utils.py └── test_application.py └── uv.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '...' 18 | 3. Scroll down to '...' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Data** 28 | 29 | - OS: \[e.g. Windows or Linux\] 30 | - Version and commit id\[e.g. 0.9.0-beta - 0b3138ac\] (see `Help` → `About mpvQC`) 31 | 32 | **mpv.conf** 33 | 34 | - If you have modified the mpv configuration (`Options` ⟶ `Edit mpv.conf...`), list all your changes or a copy of your config 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/[QC]_screenshot-qc.txt: -------------------------------------------------------------------------------- 1 | [FILE] 2 | 3 | [DATA] 4 | [00:00:21] [Timing] Title fades in too soon 5 | [00:00:27] [Typeset] Font could be bigger 6 | [00:00:51] [Note] 7 | [00:01:17] [Spelling] here! 8 | # total lines: 4 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | 3 | version: 2 4 | updates: 5 | 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to GitHub Actions every week 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: end-of-file-fixer 6 | - id: trailing-whitespace 7 | - repo: https://github.com/biomejs/pre-commit 8 | rev: v1.9.4 9 | hooks: 10 | - id: biome-check 11 | name: "format json" 12 | files: "\\.(jsonc?)$" 13 | args: 14 | - "--bracket-spacing=true" 15 | - "--indent-style=space" 16 | - "--indent-width=4" 17 | - "--line-ending=lf" 18 | - "--line-width=120" 19 | - repo: https://github.com/ComPWA/taplo-pre-commit 20 | rev: v0.9.3 21 | hooks: 22 | - id: taplo-format 23 | name: "format toml" 24 | args: 25 | - "--option" 26 | - "align_comments=true" 27 | - "--option" 28 | - "allowed_blank_lines=1" 29 | - "--option" 30 | - "array_auto_collapse=false" 31 | - "--option" 32 | - "indent_string= " 33 | - repo: https://github.com/google/yamlfmt 34 | rev: v0.16.0 35 | hooks: 36 | - id: yamlfmt 37 | name: "format yml" 38 | args: 39 | - "-formatter" 40 | - "line_ending=lf" 41 | - "-formatter" 42 | - "retain_line_breaks_single=true" 43 | - "-formatter" 44 | - "scan_folded_as_literal=true" 45 | - repo: https://github.com/hukkin/mdformat 46 | rev: 0.7.22 47 | hooks: 48 | - id: mdformat 49 | name: "format md" 50 | exclude: ".github/" 51 | args: 52 | - "--number" 53 | - "--wrap" 54 | - "keep" 55 | - "--end-of-line" 56 | - "lf" 57 | additional_dependencies: 58 | - mdformat-gfm 59 | - repo: https://github.com/astral-sh/ruff-pre-commit 60 | rev: v0.11.9 61 | hooks: 62 | - id: ruff 63 | name: "lint python" 64 | args: 65 | - "--fix" 66 | - id: ruff-format 67 | name: "format python" 68 | -------------------------------------------------------------------------------- /CONTRIBUTING.MD: -------------------------------------------------------------------------------- 1 | # Contributing to mpvQC 2 | 3 | Thank you for taking the time to contribute! We appreciate any help in improving mpvQC, whether it’s fixing bugs or 4 | adding new features. 5 | 6 | ______________________________________________________________________ 7 | 8 | ## Pull Request Guidelines 9 | 10 | 1. **Conventional Commits:**\ 11 | We use [Conventional Commits](https://www.conventionalcommits.org/) to structure our commit messages. Examples: 12 | 13 | - `fix: correct minor typos in code` 14 | - `feat: add new pirmary colors` 15 | 16 | 2. **Descriptive PRs:** 17 | 18 | - Provide a clear title and description. 19 | - Link any relevant issues (e.g., “Fixes #42”). 20 | - Keep your changes focused and self-contained. 21 | 22 | 3. **Code Reviews:** 23 | 24 | - Be open to feedback and discussion. 25 | - Make any requested changes or clarifications promptly. 26 | 27 | ______________________________________________________________________ 28 | 29 | ## Coding Standards 30 | 31 | - Run `just lint-python` to check for any lint issues. 32 | - Use `just format` to automatically format your code. 33 | 34 | ______________________________________________________________________ 35 | 36 | ## Testing 37 | 38 | - If you add or modify functionality, please include or update tests when possible. 39 | - Ensure all existing tests pass before making a pull request. 40 | 41 | ______________________________________________________________________ 42 | 43 | ## Issues and Bug Reports 44 | 45 | - **Search First:** Check if an issue already exists for your topic. 46 | - **Create a New Issue:** Clearly describe the bug or problem, including steps to reproduce it, and provide any relevant 47 | system information if applicable. 48 | 49 | ______________________________________________________________________ 50 | 51 | ## Feature Requests 52 | 53 | - If you’d like to implement a new feature, **please open a new issue** (or join a relevant discussion if one exists) 54 | before you start coding. This way, we can coordinate and ensure your contribution aligns with project goals. 55 | 56 | ______________________________________________________________________ 57 | 58 | ## License 59 | 60 | By contributing to mpvQC, you agree that your contributions will be licensed under the same license that governs this 61 | project. See [LICENSE](LICENSE) for details. 62 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | 3 | Logo 4 | 5 | A simple libmpv-based application for creating video file quality control reports.\ 6 | https://mpvqc.github.io 7 | 8 | ______________________________________________________________________ 9 | 10 | ## Development Setup 11 | 12 | 1. **Install these tools** 13 | 14 | - [Python 3.13 or later](https://www.python.org/downloads/) 15 | - [uv](https://github.com/astral-sh/uv) 16 | - [just](https://github.com/casey/just) 17 | - **Windows users also need** 18 | - [Git Bash](https://git-scm.com/downloads) 19 | - Be sure to run `just` inside Git Bash 20 | 21 | 2. **Clone the repository** 22 | 23 | 3. **Open a terminal** where you cloned it 24 | 25 | 4. **Initialize the environment**: 26 | 27 | ```shell 28 | just init 29 | ``` 30 | 31 | 5. **Add libmpv to your path** 32 | 33 | - **Linux**: Install `libmpv` through your package manager 34 | - **Windows**: Download [libmpv (mpv-dev-x86_64)](https://github.com/shinchiro/mpv-winbuild-cmake/releases), extract it, and place the `libmpv-*.dll` in the repository’s root folder 35 | 36 | 6. **Compile Resources:** 37 | 38 | ```shell 39 | just build-develop 40 | ``` 41 | 42 | 7. **Start the application:** 43 | 44 | ```shell 45 | uv run main.py 46 | ``` 47 | 48 | Whenever you change files in the `data`, `i18n`, or `qml` directories, run: 49 | 50 | ```shell 51 | just build-develop 52 | ``` 53 | 54 | This compiles them into a Python file in the mpvqc folder, so the app recognizes them on startup. 55 | 56 | **Tip:** Configure your IDE to run the `build-develop` before launching the application. 57 | 58 | ## Internationalization 59 | 60 | If you want to translate this application into more languages, see the [internationalization guide](docs/internationalization.md). 61 | Feel free to open a new issue if you need further assistance. 62 | -------------------------------------------------------------------------------- /build-aux/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Execute from repository root 4 | 5 | source .venv/bin/activate 6 | 7 | just clean 8 | just build-develop 9 | -------------------------------------------------------------------------------- /build-aux/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/build-aux/icon.ico -------------------------------------------------------------------------------- /data/config/backup-template.jinja: -------------------------------------------------------------------------------- 1 | [FILE] 2 | date : {{ date }} 3 | path : {{ video_path }} 4 | 5 | [DATA] 6 | {% for comment in comments -%} 7 | [{{ comment['time'] | as_time }}] [{{ comment['commentType'] | as_comment_type }}] {{ comment['comment'] | trim }} 8 | {% endfor -%} 9 | # total lines: {{ comments | count }} 10 | -------------------------------------------------------------------------------- /data/config/export-template.jinja: -------------------------------------------------------------------------------- 1 | [FILE] 2 | {{ 'date : ' + date + '\n' if write_date else '' -}} 3 | {{ 'generator : ' + generator + '\n' if write_generator else '' -}} 4 | {{ 'nick : ' + nickname + '\n' if write_nickname else '' -}} 5 | {{ 'path : ' + video_path + '\n' if write_video_path else '' -}} 6 | 7 | {{ '\n' }}[DATA] 8 | {% for comment in comments -%} 9 | [{{ comment['time'] | as_time }}] [{{ comment['commentType'] | as_comment_type }}] {{ comment['comment'] | trim }} 10 | {% endfor -%} 11 | # total lines: {{ comments | count }} 12 | -------------------------------------------------------------------------------- /data/config/input.conf: -------------------------------------------------------------------------------- 1 | ################################################## 2 | # The following keys can be bound to anything # 3 | # This is not a comprehensive list of all keys # 4 | # There are many more, like a, A, @, ö, é, î ... # 5 | # Please never bind 'quit' to any key! # 6 | ################################################## 7 | 8 | SPACE cycle pause 9 | LEFT no-osd seek -2 relative+exact 10 | RIGHT no-osd seek 2 relative+exact 11 | shift+LEFT osd-bar seek -5 relative+keyframes 12 | shift+RIGHT osd-bar seek 5 relative+keyframes 13 | ctrl+LEFT no-osd sub-seek -1 14 | ctrl+RIGHT no-osd sub-seek 1 15 | 16 | MOUSE_BTN0 cycle pause # Left click on mouse 17 | MOUSE_BTN3 add volume 2 # Mouse wheel up 18 | MOUSE_BTN4 add volume -2 # Mouse wheel down 19 | MOUSE_BTN5 add chapter -1 # Backward button on mouse 20 | MOUSE_BTN6 add chapter 1 # Forward button on mouse 21 | 22 | p cycle pause 23 | . frame-step 24 | , frame-back-step 25 | 9 add volume -2 26 | 0 add volume 2 27 | m cycle mute 28 | j cycle sub 29 | J cycle sub down 30 | SHARP cycle audio # SHARP assigns the # key 31 | l ab_loop 32 | s screenshot subtitles 33 | S screenshot window 34 | 35 | # This burns in subtitles (i.e. always render them at video resolution) 36 | # It cycles through the values "no" (Don't blend subtitles with the video) 37 | # "yes" (Blend at display resolution) 38 | # "video" (Blend at video resolution) 39 | b cycle blend-subtitles 40 | 41 | # This displays statistics of the currently played file 42 | i script-binding stats/display-stats-toggle 43 | -------------------------------------------------------------------------------- /data/config/mpv-linux.conf: -------------------------------------------------------------------------------- 1 | ######### 2 | # Video # 3 | ######### 4 | 5 | # HQ preset, uses Spline36 for upscaling and Mitchell-Netravali for downscaling 6 | profile=gpu-hq 7 | 8 | # Configure offset to match a frame rate of 60 fps; default 0.050 9 | # More info: https://github.com/mpvqc/mpvQC/issues/26 10 | video-timing-offset=0.016 11 | 12 | # Debanding is disabled because we don't want to alter the video while doing quality control 13 | deband=no 14 | 15 | # Potentially higher quality video output. Might be too demanding for old or low end hardware 16 | #scale=ewa_lanczossharp 17 | #cscale=ewa_lanczossoft 18 | #dscale=lanczos 19 | 20 | 21 | ############# 22 | # Subtitles # 23 | ############# 24 | 25 | # This disables the removal of very small gaps between subtitle lines 26 | # This might be a nice feature, but it hides flaws in the script 27 | # We don't want that while doing quality control 28 | sub-fix-timing=no 29 | 30 | # This makes sure that the current subtitle line is loaded after seeking 31 | demuxer-mkv-subtitle-preroll=yes 32 | 33 | 34 | ########### 35 | # OSC/OSD # 36 | ########### 37 | 38 | # Very slim On Screen Controller that consists of only a seekbar 39 | # Change the value of osc-valign to set the vertical position (Values between -1 and 1 are allowed) 40 | script-opts=osc-minmousemove=0,osc-hidetimeout=200,osc-layout=slimbox,osc-valign=0.6 41 | osd-bar-align-y=0 42 | 43 | # Bigger On Screen Controller with many buttons 44 | #script-opts=osc-minmousemove=0,osc-hidetimeout=200,osc-layout=box,osc-valign=0.5 45 | 46 | 47 | ############### 48 | # Screenshots # 49 | ############### 50 | 51 | #screenshot-directory= 52 | screenshot-format=png 53 | screenshot-high-bit-depth=no 54 | -------------------------------------------------------------------------------- /data/config/mpv-windows.conf: -------------------------------------------------------------------------------- 1 | ######### 2 | # Video # 3 | ######### 4 | 5 | # HQ preset, uses Spline36 for upscaling and Mitchell-Netravali for downscaling 6 | profile=gpu-hq 7 | 8 | # Default video output. Use vo=gpu-next for a more modern renderer 9 | vo=gpu 10 | 11 | # Debanding is disabled because we don't want to alter the video while doing quality control 12 | deband=no 13 | 14 | # Potentially higher quality video output. Might be too demanding for old or low end hardware 15 | #scale=ewa_lanczossharp 16 | #cscale=ewa_lanczossoft 17 | #dscale=lanczos 18 | 19 | 20 | ############# 21 | # Subtitles # 22 | ############# 23 | 24 | # This disables the removal of very small gaps between subtitle lines 25 | # This might be a nice feature, but it hides flaws in the script 26 | # We don't want that while doing quality control 27 | sub-fix-timing=no 28 | 29 | # This makes sure that the current subtitle line is loaded after seeking 30 | demuxer-mkv-subtitle-preroll=yes 31 | 32 | 33 | ########### 34 | # OSC/OSD # 35 | ########### 36 | 37 | # Very slim On Screen Controller that consists of only a seekbar 38 | # Change the value of osc-valign to set the vertical position (Values between -1 and 1 are allowed) 39 | script-opts=osc-minmousemove=0,osc-hidetimeout=200,osc-layout=slimbox,osc-valign=0.6 40 | osd-bar-align-y=0 41 | 42 | # Bigger On Screen Controller with many buttons 43 | #script-opts=osc-minmousemove=0,osc-hidetimeout=200,osc-layout=box,osc-valign=0.5 44 | 45 | 46 | ############### 47 | # Screenshots # 48 | ############### 49 | 50 | #screenshot-directory= 51 | screenshot-format=png 52 | screenshot-high-bit-depth=no 53 | -------------------------------------------------------------------------------- /data/fonts/NotoSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSans-Bold.ttf -------------------------------------------------------------------------------- /data/fonts/NotoSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSans-Italic.ttf -------------------------------------------------------------------------------- /data/fonts/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /data/fonts/NotoSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSans-SemiBold.ttf -------------------------------------------------------------------------------- /data/fonts/NotoSansHebrew-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSansHebrew-Bold.ttf -------------------------------------------------------------------------------- /data/fonts/NotoSansHebrew-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSansHebrew-Regular.ttf -------------------------------------------------------------------------------- /data/fonts/NotoSansHebrew-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSansHebrew-SemiBold.ttf -------------------------------------------------------------------------------- /data/fonts/NotoSansMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpvqc/mpvQC/de1083ea8a80220d792c0ffd35411d50516d8485/data/fonts/NotoSansMono-Regular.ttf -------------------------------------------------------------------------------- /data/icons/aspect_ratio_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/close_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/close_fullscreen_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/comment_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/content_copy_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/delete_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/done_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/download_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/edit_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/exit_to_app_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/expand_more_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/file_open_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/horizontal_split_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/info_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/inventory_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/keyboard_arrow_down_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/keyboard_arrow_left_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/keyboard_arrow_right_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/keyboard_arrow_up_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/keyboard_backspace_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/keyboard_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/keyboard_return_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/language_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/launch_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/minimize_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/more_vert_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/movie_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/movie_edit_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/notes_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/open_in_full_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/palette_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/play_arrow_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/save_alt_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/save_as_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/save_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/settings_backup_restore_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/shortcut_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/space_bar_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/subtitles_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/title_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/update_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/upload_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/icons/vertical_split_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/themes/theme.template: -------------------------------------------------------------------------------- 1 | # required 2 | # possible values: v1 3 | schema-version = "v1" 4 | 5 | # required 6 | theme-name = "" 7 | 8 | # required 9 | # possible values: dark, light 10 | theme-variant = "" 11 | 12 | # required, hex color (#xxx or #xxxxxx) 13 | # Something that represents this color scheme 14 | theme-preview = "" 15 | 16 | 17 | 18 | [[colors]] 19 | 20 | # required, hex color 21 | # default background color 22 | background = "" 23 | 24 | # required, hex color 25 | # default text color 26 | foreground = "" 27 | 28 | # required, hex color 29 | # default color for control elements like check boxes or radio buttons 30 | control = "" 31 | 32 | # required, hex color 33 | # row background color when highlighted 34 | row-highlight = "" 35 | 36 | # optional, hex color 37 | # default: foreground 38 | # text color if row is highlighted 39 | row-highlight-text = "" 40 | 41 | # optional, hex color 42 | # default: background 43 | # row background color when not highlighted 44 | row-base = "" 45 | 46 | # optional, hex color 47 | # default: foreground 48 | # row text color when not highlighted 49 | row-base-text = "" 50 | 51 | # optional, hex color 52 | # default: 53 | # - if variant is light: 'row-base' color but 10% darker 54 | # - if variant is dark, 'row-base' color but 30% lighter 55 | # alternative row background color when not highlighted 56 | row-base-alternate = "" 57 | 58 | # optional, hex color 59 | # default: foreground 60 | # alternative row text color when not highlighted 61 | row-base-alternate-text = "" 62 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuring mpvQC 2 | 3 | mpvQC can be configured using the following environment variables: 4 | 5 | | **Name** | **Default Value** | **Operating System** | **Description** | 6 | | ---------------------------- | ----------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 7 | | `MPVQC_DEBUG` | _No default_ | All | Enables debug mode, intended primarily for developers and testing. | 8 | | `MPVQC_VIDEO_SCALING_FACTOR` | `1.0` | Linux | Specifies the desktop scaling factor. Because Linux does not provide a universal method to retrieve fractional scaling from the desktop environment, Linux users must set it manually. | 9 | -------------------------------------------------------------------------------- /docs/internationalization.md: -------------------------------------------------------------------------------- 1 | # Adding Languages 2 | 3 | - Checkout repository 4 | - Make sure development environment is set up correctly for your OS 5 | - Create a new translation file by running 6 | ```shell 7 | just add-translation # just add-translation fr_FR 8 | ``` 9 | - New `.ts` file appears in the `i18n` directory 10 | - Translate the `ts` file using Qt Linguist 6: 11 | ```shell 12 | pyside6-linguist i18n/.ts # pyside6-linguist i18n/fr_FR.ts 13 | ``` 14 | - To test the translation: 15 | - Make the application portable 16 | ```shell 17 | touch portable 18 | ``` 19 | - Run 20 | ```shell 21 | just build-develop 22 | ``` 23 | - Start the application and close it 24 | - Edit the `appdata/settings.ini` 25 | ```ini 26 | [Common] 27 | language= 28 | ``` 29 | - Start the application 30 | - Add a new entry in the `qml/models/MpvqcLanguageModel.qml` file 31 | - Open a new pull request 32 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # mpvQC 4 | # 5 | # Copyright (C) 2024 mpvQC developers 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | 21 | def main(): 22 | import platform 23 | 24 | if platform.system() == "Windows": 25 | _add_directory_to_path() 26 | 27 | from mpvqc.startup import perform_startup 28 | 29 | perform_startup() 30 | 31 | 32 | def _add_directory_to_path(): 33 | import os 34 | import sys 35 | 36 | os.environ["PATH"] = os.path.dirname(sys.argv[0]) + os.pathsep + os.environ["PATH"] # noqa: PTH120 37 | os.environ["PATH"] = os.path.dirname(__file__) + os.pathsep + os.environ["PATH"] # noqa: PTH120 38 | 39 | 40 | if __name__ == "__main__": 41 | main() 42 | -------------------------------------------------------------------------------- /mpvqc/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /mpvqc/logging.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from collections.abc import Callable 19 | 20 | 21 | def qt_log_handler() -> Callable: 22 | from PySide6.QtCore import QtMsgType 23 | 24 | levels = { 25 | QtMsgType.QtInfoMsg: "INFO", 26 | QtMsgType.QtWarningMsg: "WARNING", 27 | QtMsgType.QtCriticalMsg: "CRITICAL", 28 | QtMsgType.QtFatalMsg: "FATAL", 29 | QtMsgType.QtDebugMsg: "DEBUG", 30 | } 31 | 32 | def handler(message_type: QtMsgType, _, message): 33 | level = levels.get(message_type) 34 | print(f"{level} {message}") 35 | 36 | return handler 37 | -------------------------------------------------------------------------------- /mpvqc/models.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from dataclasses import dataclass 19 | 20 | 21 | @dataclass 22 | class Comment: 23 | time: int 24 | comment_type: str 25 | comment: str 26 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # ruff: noqa: F401 19 | from .application_paths import MpvqcApplicationPathsPyObject 20 | from .comment_model import MpvqcCommentModelPyObject 21 | from .comment_type_validator import MpvqcCommentTypeValidatorPyObject 22 | from .export_template_model import MpvqcExportTemplateModelPyObject 23 | from .extended_document_exporter import MpvqcExtendedDocumentExporterPyObject 24 | from .manager import MpvqcManagerPyObject 25 | from .player import MpvqcMpvPlayerPyObject 26 | from .player_files import MpvqcPlayerFilesPyObject 27 | from .player_framebuffer_object import MpvqcMpvFrameBufferObjectPyObject 28 | from .player_properties import MpvqcMpvPlayerPropertiesPyObject 29 | from .player_win_id import MpvWindowPyObject 30 | from .text_validator import MpvqcDefaultTextValidatorPyObject 31 | from .themes import MpvqcThemesPyObject 32 | from .utility import MpvqcUtilityPyObject 33 | from .version_checker import MpvqcVersionCheckerPyObject 34 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/application_paths.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import Property, QObject, QUrl 20 | from PySide6.QtQml import QmlElement 21 | 22 | from mpvqc.services import ApplicationPathsService, TypeMapperService 23 | 24 | QML_IMPORT_NAME = "pyobjects" 25 | QML_IMPORT_MAJOR_VERSION = 1 26 | 27 | 28 | @QmlElement 29 | class MpvqcApplicationPathsPyObject(QObject): 30 | _paths = inject.attr(ApplicationPathsService) 31 | _type_mapper: TypeMapperService = inject.attr(TypeMapperService) 32 | 33 | @Property(str, constant=True, final=True) 34 | def input_conf(self) -> str: 35 | return self._type_mapper.map_path_to_str(self._paths.file_input_conf) 36 | 37 | @Property(str, constant=True, final=True) 38 | def mpv_conf(self) -> str: 39 | return self._type_mapper.map_path_to_str(self._paths.file_mpv_conf) 40 | 41 | @Property(QUrl, constant=True, final=True) 42 | def dir_backup(self) -> QUrl: 43 | return self._type_mapper.map_path_to_url(self._paths.dir_backup) 44 | 45 | @Property(QUrl, constant=True, final=True) 46 | def settings(self) -> QUrl: 47 | return self._type_mapper.map_path_to_url(self._paths.file_settings) 48 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/comment_model/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # ruff: noqa: F401 19 | from .model import MpvqcCommentModelPyObject 20 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/comment_model/item.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | import itertools 20 | 21 | from PySide6.QtGui import QStandardItem 22 | 23 | from .roles import Role 24 | 25 | ID_COUNTER = itertools.count() 26 | 27 | 28 | class CommentItem(QStandardItem): 29 | def __init__(self): 30 | super().__init__() 31 | self._id = next(ID_COUNTER) 32 | 33 | def __lt__(self, other: "CommentItem") -> bool: 34 | this_time = self.data(Role.TIME) 35 | that_time = other.data(Role.TIME) 36 | 37 | if this_time < that_time: 38 | return True 39 | if this_time > that_time: 40 | return False 41 | return self._id < other._id 42 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/comment_model/roles.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from PySide6.QtCore import QByteArray 19 | 20 | 21 | class Role: 22 | TIME = 1010 23 | TYPE = 1020 24 | COMMENT = 1030 25 | 26 | MAPPING = { 27 | TIME: QByteArray(b"time"), 28 | TYPE: QByteArray(b"commentType"), 29 | COMMENT: QByteArray(b"comment"), 30 | } 31 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/comment_model/utils.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from typing import Any 19 | 20 | from PySide6.QtGui import QStandardItem, QStandardItemModel 21 | 22 | from mpvqc.models import Comment 23 | 24 | from .item import CommentItem 25 | from .roles import Role 26 | 27 | 28 | def retrieve_comments_from(model: QStandardItemModel) -> list[dict[str, Any]]: 29 | comments = [] 30 | for row in range(model.rowCount()): 31 | item = model.item(row, column=0) 32 | comment = create_comment_from(item) 33 | comments.append(comment) 34 | return comments 35 | 36 | 37 | def create_comment_from(item: QStandardItem) -> dict[str, Any]: 38 | return { 39 | "time": int(item.data(Role.TIME)), 40 | "commentType": item.data(Role.TYPE), 41 | "comment": item.data(Role.COMMENT), 42 | } 43 | 44 | 45 | def create_item_from(comment: dict[str, Any] | Comment) -> CommentItem: 46 | item = CommentItem() 47 | if isinstance(comment, Comment): 48 | item.setData(comment.time, Role.TIME) 49 | item.setData(comment.comment_type, Role.TYPE) 50 | item.setData(comment.comment, Role.COMMENT) 51 | else: 52 | item.setData(comment["time"], Role.TIME) 53 | item.setData(comment["commentType"], Role.TYPE) 54 | item.setData(comment["comment"], Role.COMMENT) 55 | return item 56 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/comment_type_validator.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import QObject, Slot 20 | from PySide6.QtQml import QmlElement 21 | 22 | from mpvqc.services import CommentTypeValidatorService 23 | 24 | QML_IMPORT_NAME = "pyobjects" 25 | QML_IMPORT_MAJOR_VERSION = 1 26 | 27 | 28 | @QmlElement 29 | class MpvqcCommentTypeValidatorPyObject(QObject): 30 | _validator: CommentTypeValidatorService = inject.attr(CommentTypeValidatorService) 31 | 32 | @Slot(str, list, result=str or None) 33 | def validate_new_comment_type(self, new_comment_type: str, existing_comment_types: list[str]) -> str | None: 34 | return self._validator.validate_new_comment_type(new_comment_type, existing_comment_types) 35 | 36 | @Slot(str, str, list, result=str or None) 37 | def validate_editing_of_comment_type( 38 | self, new_comment_type: str, comment_type_being_edited: str, existing_comment_types: list[str] 39 | ) -> str | None: 40 | return self._validator.validate_editing_of_comment_type( 41 | new_comment_type, comment_type_being_edited, existing_comment_types 42 | ) 43 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/export_template_model.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import QByteArray 20 | from PySide6.QtGui import QStandardItem, QStandardItemModel 21 | from PySide6.QtQml import QmlElement 22 | 23 | from mpvqc.services import ApplicationPathsService, TypeMapperService 24 | 25 | QML_IMPORT_NAME = "pyobjects" 26 | QML_IMPORT_MAJOR_VERSION = 1 27 | 28 | 29 | @QmlElement 30 | class MpvqcExportTemplateModelPyObject(QStandardItemModel): 31 | _app_paths: ApplicationPathsService = inject.attr(ApplicationPathsService) 32 | _type_mapper: TypeMapperService = inject.attr(TypeMapperService) 33 | 34 | def __init__(self): 35 | super().__init__() 36 | self.setItemRoleNames(Role.MAPPING) 37 | self.setSortRole(Role.NAME) 38 | self._initialize_model() 39 | 40 | def _initialize_model(self): 41 | for template in self._app_paths.files_export_templates: 42 | url = self._type_mapper.map_path_to_url(template) 43 | 44 | item = QStandardItem() 45 | item.setData(url, Role.PATH) 46 | item.setData(template.stem, Role.NAME) 47 | self.appendRow(item) 48 | self.sort(0) 49 | 50 | 51 | class Role: 52 | """ 53 | See: https://doc.qt.io/qt-6/qstandarditem.html#ItemType-enum 54 | """ 55 | 56 | NAME = 1010 57 | PATH = 1020 58 | 59 | MAPPING = { 60 | NAME: QByteArray(b"name"), 61 | PATH: QByteArray(b"path"), 62 | } 63 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/extended_document_exporter.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import QObject, QUrl, Signal, Slot 20 | from PySide6.QtQml import QmlElement 21 | 22 | from mpvqc.services import DocumentExportService, TypeMapperService 23 | 24 | QML_IMPORT_NAME = "pyobjects" 25 | QML_IMPORT_MAJOR_VERSION = 1 26 | 27 | 28 | @QmlElement 29 | class MpvqcExtendedDocumentExporterPyObject(QObject): 30 | _exporter: DocumentExportService = inject.attr(DocumentExportService) 31 | _type_mapper: TypeMapperService = inject.attr(TypeMapperService) 32 | 33 | exportErrorOccurred = Signal(str, int or None) 34 | 35 | @Slot(result=QUrl) 36 | def generate_file_path_proposal(self) -> QUrl: 37 | path = self._exporter.generate_file_path_proposal() 38 | return self._type_mapper.map_path_to_url(path) 39 | 40 | @Slot(QUrl, QUrl) 41 | def export(self, template_url: QUrl, file_url: QUrl): 42 | file = self._type_mapper.map_url_to_path(file_url) 43 | template = self._type_mapper.map_url_to_path(template_url) 44 | 45 | error = self._exporter.export(file, template) 46 | 47 | if error: 48 | self.exportErrorOccurred.emit(error.message, error.line_nr) 49 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/manager/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # ruff: noqa: F401 19 | from .impl import MpvqcManagerPyObject 20 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/player.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import QObject, Slot 20 | from PySide6.QtQml import QmlElement 21 | 22 | from mpvqc.services import KeyCommandGeneratorService, PlayerService 23 | 24 | QML_IMPORT_NAME = "pyobjects" 25 | QML_IMPORT_MAJOR_VERSION = 1 26 | 27 | 28 | @QmlElement 29 | class MpvqcMpvPlayerPyObject(QObject): 30 | _player: PlayerService = inject.attr(PlayerService) 31 | _command_generator: KeyCommandGeneratorService = inject.attr(KeyCommandGeneratorService) 32 | 33 | @Slot() 34 | def pause(self) -> None: 35 | self._player.pause() 36 | 37 | @Slot(int, int) 38 | def handle_key_event(self, key: int, modifiers: int): 39 | if command := self._command_generator.generate_command(key, modifiers): 40 | self._player.execute(command) 41 | 42 | @Slot(int) 43 | def jump_to(self, seconds: int) -> None: 44 | self._player.jump_to(seconds) 45 | 46 | @Slot(int, int) 47 | def move_mouse(self, x, y) -> None: 48 | self._player.move_mouse(x, y) 49 | 50 | @Slot() 51 | def scroll_up(self) -> None: 52 | self._player.scroll_up() 53 | 54 | @Slot() 55 | def scroll_down(self) -> None: 56 | self._player.scroll_down() 57 | 58 | @Slot() 59 | def press_mouse_left(self) -> None: 60 | self._player.press_mouse_left() 61 | 62 | @Slot() 63 | def press_mouse_middle(self) -> None: 64 | self._player.press_mouse_middle() 65 | 66 | @Slot() 67 | def release_mouse_left(self) -> None: 68 | self._player.release_mouse_left() 69 | 70 | @Slot() 71 | def terminate(self) -> None: 72 | self._player.terminate() 73 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/player_files.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import Property, QObject, QUrl 20 | from PySide6.QtQml import QmlElement 21 | 22 | from mpvqc.services import ApplicationPathsService, ResourceService, TypeMapperService 23 | 24 | QML_IMPORT_NAME = "pyobjects" 25 | QML_IMPORT_MAJOR_VERSION = 1 26 | 27 | 28 | @QmlElement 29 | class MpvqcPlayerFilesPyObject(QObject): 30 | _paths: ApplicationPathsService = inject.attr(ApplicationPathsService) 31 | _type_mapper: TypeMapperService = inject.attr(TypeMapperService) 32 | _resources: ResourceService = inject.attr(ResourceService) 33 | 34 | @Property(QUrl, constant=True, final=True) 35 | def input_conf_url(self) -> QUrl: 36 | return self._type_mapper.map_path_to_url(self._paths.file_input_conf) 37 | 38 | @Property(QUrl, constant=True, final=True) 39 | def mpv_conf_url(self) -> QUrl: 40 | return self._type_mapper.map_path_to_url(self._paths.file_mpv_conf) 41 | 42 | @Property(str, constant=True, final=True) 43 | def default_input_conf_content(self) -> str: 44 | return self._resources.input_conf_content 45 | 46 | @Property(str, constant=True, final=True) 47 | def default_mpv_conf_content(self) -> str: 48 | return self._resources.mpv_conf_content 49 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/player_win_id.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import QCoreApplication, QObject 20 | from PySide6.QtQml import QmlElement 21 | from PySide6.QtQuick import QQuickWindow 22 | 23 | from mpvqc.services import FramelessWindowService, PlayerService 24 | 25 | QML_IMPORT_NAME = "pyobjects" 26 | QML_IMPORT_MAJOR_VERSION = 1 27 | 28 | 29 | # noinspection PyUnresolvedReferences 30 | @QmlElement 31 | class MpvWindowPyObject(QQuickWindow): 32 | _player: PlayerService = inject.attr(PlayerService) 33 | _frameless_window: FramelessWindowService = inject.attr(FramelessWindowService) 34 | 35 | def __init__(self): 36 | super().__init__() 37 | self._player.init(win_id=self.winId()) 38 | q_app = QCoreApplication.instance() 39 | q_app.application_ready.connect(lambda: self._on_application_ready()) 40 | 41 | def _on_application_ready(self): 42 | player_properties = QCoreApplication.instance().find_object(QObject, "mpvqcPlayerProperties") 43 | player_properties.init() 44 | 45 | self._frameless_window.event_filter.set_embedded_player_hwnd(self.winId()) 46 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/text_validator.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from PySide6.QtCore import Slot 19 | from PySide6.QtGui import QValidator 20 | from PySide6.QtQml import QmlElement 21 | 22 | QML_IMPORT_NAME = "pyobjects" 23 | QML_IMPORT_MAJOR_VERSION = 1 24 | 25 | 26 | @QmlElement 27 | class MpvqcDefaultTextValidatorPyObject(QValidator): 28 | def validate(self, user_input: str, position: int): 29 | return QValidator.State.Acceptable, self.replace_special_characters(user_input), position 30 | 31 | @Slot(str, result=str) 32 | def replace_special_characters(self, string_to_replace) -> str: 33 | # fmt: off 34 | return string_to_replace \ 35 | .replace("\xad", "") \ 36 | .replace("\r", "") \ 37 | .replace("\n", "") 38 | # fmt: on 39 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/themes.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import inject 19 | from PySide6.QtCore import QObject, Slot 20 | from PySide6.QtQml import QmlElement 21 | 22 | from mpvqc.services import ThemeService 23 | 24 | QML_IMPORT_NAME = "pyobjects" 25 | QML_IMPORT_MAJOR_VERSION = 1 26 | 27 | 28 | # noinspection PyPep8Naming 29 | @QmlElement 30 | class MpvqcThemesPyObject(QObject): 31 | _themes: ThemeService = inject.attr(ThemeService) 32 | 33 | @Slot(result=list) 34 | def getThemeSummaries(self) -> list[dict]: 35 | return self._themes.get_theme_summaries() 36 | 37 | @Slot(str, result=dict) 38 | def getThemeSummary(self, theme_identifier: str) -> dict: 39 | return self._themes.get_theme_summary(theme_identifier) 40 | 41 | @Slot(str, result=list) 42 | def getThemeColorOptions(self, theme_identifier: str) -> list[dict]: 43 | return self._themes.get_theme_colors(theme_identifier) 44 | 45 | @Slot(int, str, result=dict) 46 | def getThemeColorOption(self, color_option: int, theme_identifier: str): 47 | return self._themes.get_theme_color(color_option, theme_identifier) 48 | -------------------------------------------------------------------------------- /mpvqc/pyobjects/version_checker.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | from collections.abc import Callable 18 | 19 | import inject 20 | from PySide6.QtCore import QObject, QRunnable, QThreadPool, Signal, Slot 21 | from PySide6.QtQml import QmlElement 22 | 23 | from mpvqc.services import VersionCheckerService 24 | 25 | QML_IMPORT_NAME = "pyobjects" 26 | QML_IMPORT_MAJOR_VERSION = 1 27 | 28 | 29 | class VersionCheckRunnable(QRunnable): 30 | _checker: VersionCheckerService = inject.attr(VersionCheckerService) 31 | 32 | def __init__(self, callback: Callable[[str, str], None]): 33 | super().__init__() 34 | self._callback = callback 35 | 36 | @Slot() 37 | def run(self): 38 | title, text = self._checker.check_for_new_version() 39 | self._callback(title, text) 40 | 41 | 42 | @QmlElement 43 | class MpvqcVersionCheckerPyObject(QObject): 44 | versionChecked = Signal(str, str) 45 | 46 | @Slot() 47 | def check_for_new_version(self) -> None: 48 | check_runnable = VersionCheckRunnable(self.versionChecked.emit) 49 | QThreadPool().globalInstance().start(check_runnable) 50 | -------------------------------------------------------------------------------- /mpvqc/services/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # ruff: noqa: F401 19 | from .application_environment import ApplicationEnvironmentService 20 | from .application_paths import ApplicationPathsService 21 | from .comment_type_validator import CommentTypeValidatorService 22 | from .document_exporter import DocumentBackupService, DocumentExportService, DocumentRenderService 23 | from .document_importer import DocumentImporterService 24 | from .file_startup import FileStartupService 25 | from .font_loader import FontLoaderService 26 | from .formatter_time import TimeFormatterService 27 | from .frameless import FramelessWindowService 28 | from .key_command import KeyCommandGeneratorService 29 | from .mimetype_provider import MimetypeProviderService 30 | from .operating_system_zoom_detector import OperatingSystemZoomDetectorService 31 | from .player import PlayerService 32 | from .resource import ResourceService 33 | from .resource_reader import ResourceReaderService 34 | from .reverse_translator import ReverseTranslatorService 35 | from .settings import SettingsService 36 | from .theme import ThemeService 37 | from .type_mapper import TypeMapperService 38 | from .version_checker import VersionCheckerService 39 | from .video_selector import VideoSelectorService 40 | -------------------------------------------------------------------------------- /mpvqc/services/application_environment.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import sys 19 | from functools import cached_property 20 | from pathlib import Path 21 | 22 | 23 | class ApplicationEnvironmentService: 24 | """""" 25 | 26 | @cached_property 27 | def is_portable(self) -> bool: 28 | return self.executing_directory.joinpath("portable").is_file() 29 | 30 | @cached_property 31 | def executing_directory(self) -> Path: 32 | return Path(sys.argv[0]).parent 33 | -------------------------------------------------------------------------------- /mpvqc/services/file_startup.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from pathlib import Path 19 | 20 | import inject 21 | 22 | from .application_paths import ApplicationPathsService 23 | from .resource import ResourceService 24 | 25 | 26 | class FileStartupService: 27 | _paths: ApplicationPathsService = inject.attr(ApplicationPathsService) 28 | _resources: ResourceService = inject.attr(ResourceService) 29 | 30 | def create_missing_directories(self) -> None: 31 | self._paths.dir_config.mkdir(exist_ok=True, parents=True) 32 | self._paths.dir_backup.mkdir(exist_ok=True, parents=True) 33 | self._paths.dir_screenshots.mkdir(exist_ok=True, parents=True) 34 | self._paths.dir_export_templates.mkdir(exist_ok=True, parents=True) 35 | 36 | def create_missing_files(self) -> None: 37 | self._create_missing_input_conf() 38 | self._create_missing_mpv_conf() 39 | 40 | def _create_missing_input_conf(self) -> None: 41 | self._create_missing_file(path=self._paths.file_input_conf, content=self._resources.input_conf_content) 42 | 43 | def _create_missing_mpv_conf(self) -> None: 44 | self._create_missing_file(path=self._paths.file_mpv_conf, content=self._resources.mpv_conf_content) 45 | 46 | @staticmethod 47 | def _create_missing_file(path: Path, content: str) -> None: 48 | if not path.exists(): 49 | path.write_text(content, encoding="utf-8", newline="\n") 50 | -------------------------------------------------------------------------------- /mpvqc/services/font_loader.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from PySide6.QtCore import QDir 19 | from PySide6.QtGui import QFont, QFontDatabase 20 | 21 | 22 | class FontLoaderService: 23 | """""" 24 | 25 | @staticmethod 26 | def load_application_fonts(): 27 | for entry_info in QDir(":/data/fonts").entryInfoList(): 28 | resource_path = entry_info.filePath() 29 | if not QFontDatabase.addApplicationFont(resource_path) >= 0: 30 | msg = f"Cannot load font from {resource_path}" 31 | raise ValueError(msg) 32 | 33 | QFont.insertSubstitution("Noto Sans", "Noto Sans Hebrew") 34 | -------------------------------------------------------------------------------- /mpvqc/services/formatter_time.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | class TimeFormatterService: 20 | """""" 21 | 22 | @staticmethod 23 | def format_time_to_string(input_seconds: float, *, long_format: bool) -> str: 24 | hours, remainder = divmod(input_seconds, 3600) 25 | minutes, seconds = divmod(remainder, 60) 26 | if long_format: 27 | return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d}" 28 | else: # noqa: RET505 29 | return f"{int(minutes):02d}:{int(seconds):02d}" 30 | -------------------------------------------------------------------------------- /mpvqc/services/frameless/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, version 3. 6 | # 7 | # This program is distributed in the hope that it will be useful, but 8 | # WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 | # General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program. If not, see . 14 | 15 | # ruff: noqa: F401 16 | from .service import FramelessWindowService 17 | -------------------------------------------------------------------------------- /mpvqc/services/frameless/linux/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, version 3. 6 | # 7 | # This program is distributed in the hope that it will be useful, but 8 | # WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 | # General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program. If not, see . 14 | 15 | # Inspired and based on: 16 | # - https://github.com/zhiyiYo/PyQt-Frameless-Window 17 | # - https://gitee.com/Virace/pyside6-qml-frameless-window/tree/main 18 | 19 | # ruff: noqa: F401 20 | from .event import LinuxEventFilter 21 | -------------------------------------------------------------------------------- /mpvqc/services/frameless/service.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, version 3. 6 | # 7 | # This program is distributed in the hope that it will be useful, but 8 | # WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 | # General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program. If not, see . 14 | 15 | import sys 16 | 17 | from PySide6.QtGui import QGuiApplication, QWindow 18 | 19 | 20 | class FramelessWindowService: 21 | _event_filter = None 22 | 23 | def __init__(self): 24 | if sys.platform == "win32": 25 | from mpvqc.services.frameless.win import WindowsEventFilter 26 | 27 | # We need a reference to this filter as soon as possible 28 | # Needs to bound to a class variable, else garbage collector will clean up immediately 29 | self._event_filter = WindowsEventFilter() 30 | 31 | @property 32 | def event_filter(self): 33 | return self._event_filter 34 | 35 | def configure_for(self, app: QGuiApplication, top_lvl_window: QWindow): 36 | def configure_for_linux(): 37 | from .linux import LinuxEventFilter 38 | 39 | # Needs to bound to a class variable, else garbage collector will clean up immediately 40 | self._event_filter = LinuxEventFilter(top_lvl_window, app) 41 | app.installEventFilter(self._event_filter) 42 | 43 | def configure_for_windows(): 44 | hwnd_top_lvl = top_lvl_window.winId() 45 | self._event_filter.set_top_lvl_hwnd(hwnd_top_lvl) 46 | 47 | app.installNativeEventFilter(self._event_filter) 48 | 49 | from .win import configure_gwl_style, extend_frame_into_client_area 50 | 51 | extend_frame_into_client_area(hwnd_top_lvl) 52 | configure_gwl_style(hwnd_top_lvl) 53 | 54 | if sys.platform == "win32": 55 | configure_for_windows() 56 | elif sys.platform == "linux": 57 | configure_for_linux() 58 | else: 59 | msg = f"Unsupported platform: {sys.platform}" 60 | raise ValueError(msg) 61 | -------------------------------------------------------------------------------- /mpvqc/services/frameless/win/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, version 3. 6 | # 7 | # This program is distributed in the hope that it will be useful, but 8 | # WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 | # General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program. If not, see . 14 | 15 | # Inspired and based on: 16 | # - https://github.com/zhiyiYo/PyQt-Frameless-Window 17 | # - https://gitee.com/Virace/pyside6-qml-frameless-window/tree/main 18 | 19 | # ruff: noqa: F401 20 | from .event import WindowsEventFilter 21 | from .utils import configure_gwl_style, extend_frame_into_client_area 22 | -------------------------------------------------------------------------------- /mpvqc/services/frameless/win/c_structures.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, version 3. 6 | # 7 | # This program is distributed in the hope that it will be useful, but 8 | # WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 | # General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program. If not, see . 14 | 15 | # Inspired and based on: 16 | # - https://github.com/zhiyiYo/PyQt-Frameless-Window 17 | # - https://gitee.com/Virace/pyside6-qml-frameless-window/tree/main 18 | 19 | from ctypes import POINTER, Structure, c_int 20 | from ctypes.wintypes import HWND, RECT, UINT 21 | 22 | 23 | class MARGINS(Structure): 24 | _fields_ = [ 25 | ("cxLeftWidth", c_int), 26 | ("cxRightWidth", c_int), 27 | ("cyTopHeight", c_int), 28 | ("cyBottomHeight", c_int), 29 | ] 30 | 31 | 32 | class PWINDOWPOS(Structure): 33 | _fields_ = [ 34 | ("hWnd", HWND), 35 | ("hwndInsertAfter", HWND), 36 | ("x", c_int), 37 | ("y", c_int), 38 | ("cx", c_int), 39 | ("cy", c_int), 40 | ("flags", UINT), 41 | ] 42 | 43 | 44 | class NCCALCSIZE_PARAMS(Structure): 45 | _fields_ = [("rgrc", RECT * 3), ("lppos", POINTER(PWINDOWPOS))] 46 | 47 | 48 | LPNCCALCSIZE_PARAMS = POINTER(NCCALCSIZE_PARAMS) 49 | -------------------------------------------------------------------------------- /mpvqc/services/mimetype_provider.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from functools import cached_property 19 | 20 | from PySide6.QtCore import QMimeDatabase 21 | 22 | 23 | class MimetypeProviderService: 24 | @cached_property 25 | def video_file_glob_pattern(self): 26 | patterns = set() 27 | patterns.add("*.avi") 28 | patterns.add("*.mkv") 29 | patterns.add("*.mp4") 30 | 31 | for mime_type in QMimeDatabase().allMimeTypes(): 32 | if mime_type.name().startswith("video/"): 33 | patterns.update(mime_type.globPatterns()) 34 | 35 | return f" ({' '.join(sorted(patterns))})" 36 | 37 | @cached_property 38 | def subtitle_file_glob_pattern(self): 39 | patterns = (f"*.{ext}" for ext in self.subtitle_file_extensions) 40 | return f" ({' '.join(sorted(patterns))})" 41 | 42 | @cached_property 43 | def subtitle_file_extensions(self) -> list[str]: 44 | # fmt: off 45 | return [ 46 | "aqt", "ass", "idx", "js", "jss", "mks", "rt", "scc", "smi", 47 | "srt", "ssa", "sub", "sup", "utf", "utf-8", "utf8", "vtt" 48 | ] 49 | # fmt: on 50 | -------------------------------------------------------------------------------- /mpvqc/services/resource.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import platform 19 | from functools import cache 20 | 21 | import inject 22 | 23 | from .resource_reader import ResourceReaderService 24 | 25 | 26 | class ResourceService: 27 | _resource_reader = inject.attr(ResourceReaderService) 28 | 29 | @property 30 | def input_conf_content(self) -> str: 31 | return self._read_from_resource(path=":/data/config/input.conf") 32 | 33 | @property 34 | def mpv_conf_content(self) -> str: 35 | match platform.system(): 36 | case "Windows": 37 | return self._read_from_resource(path=":/data/config/mpv-windows.conf") 38 | case _: 39 | return self._read_from_resource(path=":/data/config/mpv-linux.conf") 40 | 41 | @property 42 | def backup_template(self) -> str: 43 | return self._read_from_resource(path=":/data/config/backup-template.jinja") 44 | 45 | @property 46 | def default_export_template(self) -> str: 47 | return self._read_from_resource(path=":/data/config/export-template.jinja") 48 | 49 | @cache 50 | def _read_from_resource(self, path: str) -> str: 51 | return self._resource_reader.read_from(path) 52 | -------------------------------------------------------------------------------- /mpvqc/services/resource_reader.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from PySide6.QtCore import QFile, QIODevice 19 | 20 | 21 | class ResourceReaderService: 22 | """""" 23 | 24 | def read_from(self, file_path: str) -> str: 25 | resource_path = self._make_resource_path_from(file_path) 26 | return self._read_from(resource_path) 27 | 28 | @staticmethod 29 | def _make_resource_path_from(file_path: str) -> str: 30 | if file_path.startswith(":/"): 31 | return file_path 32 | if file_path.startswith("/"): 33 | return ":" + file_path 34 | return ":/" + file_path 35 | 36 | @staticmethod 37 | def _read_from(resource_path: str) -> str: 38 | file = QFile(resource_path) 39 | if not file.exists(): 40 | raise FileNotFoundError(resource_path) 41 | try: 42 | if not file.open(QIODevice.OpenModeFlag.ReadOnly): 43 | msg = f"Can not open file to read: {resource_path}" 44 | raise ValueError(msg) 45 | return file.readAll().data().decode("utf-8") 46 | finally: 47 | if file.isOpen(): 48 | file.close() 49 | -------------------------------------------------------------------------------- /mpvqc/services/theme/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | # ruff: noqa: F401 19 | from .schema import Theme 20 | from .service import ThemeService 21 | -------------------------------------------------------------------------------- /mpvqc/services/theme/utils.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import re 19 | from decimal import ROUND_HALF_UP, Decimal 20 | 21 | from PySide6.QtGui import QColor 22 | 23 | from mpvqc.services.theme.schema import ThemeParseError 24 | 25 | WHOLE_NUMBER = Decimal(0) 26 | 27 | MULTI_WHITESPACE = re.compile(r"\s+") 28 | HEX_COLOR_RE = re.compile(r"^#(?=(?:.{3}|.{6})$)[a-f0-9]*$") 29 | INT_OR_FLOAT_RE = re.compile(r"^[-+]?\d+(\.\d+)?$") 30 | 31 | 32 | def parse_color(_color_input: str) -> QColor: 33 | color_input = MULTI_WHITESPACE.sub(" ", _color_input).strip().lower() 34 | 35 | match color_input.split(): 36 | case [color] if _is_hex(color): 37 | return QColor(color) 38 | case ["qt.darker", color, factor] if _is_hex(color) and _is_int_or_float(factor): 39 | return QColor(color).darker(_adapted_factor(factor)) 40 | case ["qt.lighter", color, factor] if _is_hex(color) and _is_int_or_float(factor): 41 | return QColor(color).lighter(_adapted_factor(factor)) 42 | case _: 43 | msg = f"Cannot parse color: {_color_input}" 44 | raise ThemeParseError(msg) 45 | 46 | 47 | def _is_hex(color: str) -> bool: 48 | return bool(HEX_COLOR_RE.fullmatch(color)) 49 | 50 | 51 | def _is_int_or_float(factor: str) -> bool: 52 | return bool(INT_OR_FLOAT_RE.fullmatch(factor)) 53 | 54 | 55 | def _adapted_factor(factor: str) -> int: 56 | f = Decimal(f"{float(factor) * 100}").quantize(WHOLE_NUMBER, ROUND_HALF_UP) 57 | return int(f) 58 | -------------------------------------------------------------------------------- /mpvqc/services/type_mapper.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from pathlib import Path 19 | 20 | from PySide6.QtCore import QUrl 21 | 22 | 23 | class TypeMapperService: 24 | """""" 25 | 26 | @staticmethod 27 | def map_urls_to_path(urls: list[QUrl]) -> list[Path]: 28 | return list(map(TypeMapperService.map_url_to_path, urls)) 29 | 30 | @staticmethod 31 | def map_url_to_path(url: QUrl) -> Path: 32 | return Path(url.toLocalFile()).resolve() 33 | 34 | @staticmethod 35 | def map_urls_to_path_strings(urls: list[QUrl]) -> list[str]: 36 | return list(map(TypeMapperService.map_url_to_path_string, urls)) 37 | 38 | @staticmethod 39 | def map_url_to_path_string(url: QUrl) -> str: 40 | return f"{Path(url.toLocalFile()).resolve()}" 41 | 42 | @staticmethod 43 | def map_path_to_url(path: Path) -> QUrl: 44 | return QUrl.fromLocalFile(f"{path.resolve()}") 45 | 46 | @staticmethod 47 | def map_path_to_str(path: Path) -> str: 48 | return f"{path.resolve()}" 49 | 50 | @staticmethod 51 | def normalize_path_str(path: str) -> str: 52 | return f"{Path(path).resolve()}" 53 | -------------------------------------------------------------------------------- /qml/Main.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import app 21 | 22 | MpvqcApplication {} 23 | -------------------------------------------------------------------------------- /qml/app/MpvqcQuitHandler.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | pragma ComponentBehavior: Bound 21 | 22 | import QtQuick 23 | 24 | import dialogs 25 | 26 | Item { 27 | id: root 28 | 29 | required property var mpvqcApplication 30 | required property bool canClose 31 | 32 | readonly property var mpvqcMpvPlayerPyObject: mpvqcApplication.mpvqcMpvPlayerPyObject 33 | readonly property var mpvqcManager: mpvqcApplication.mpvqcManager 34 | 35 | property bool userConfirmedClose: false 36 | 37 | property var quitDialog: null 38 | property var quitDialogFactory: Component { 39 | MpvqcMessageBoxQuit { 40 | mpvqcApplication: root.mpvqcApplication 41 | 42 | onAccepted: { 43 | root._close(); 44 | } 45 | } 46 | } 47 | 48 | function requestClose() { 49 | if (canClose || userConfirmedClose) { 50 | _close(); 51 | } else { 52 | quitDialog = quitDialogFactory.createObject(root); 53 | quitDialog.closed.connect(quitDialog.destroy); 54 | quitDialog.open(); 55 | } 56 | } 57 | 58 | function _close() { 59 | userConfirmedClose = true; 60 | _workaroundNativeWindowInterfering() 61 | mpvqcApplication.close(); 62 | } 63 | 64 | function _workaroundNativeWindowInterfering() { 65 | if (Qt.platform.os === "windows" && mpvqcManager.saved) { 66 | mpvqcMpvPlayerPyObject.terminate() 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /qml/app/MpvqcWindowVisibilityHandler.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | QtObject { 23 | id: root 24 | 25 | required property var mpvqcApplication 26 | 27 | readonly property bool maximized: mpvqcApplication.visibility === Window.Maximized 28 | readonly property bool fullscreen: mpvqcApplication.visibility === Window.FullScreen 29 | 30 | property bool wasMaximizedBefore: false 31 | 32 | function toggleMaximized() { 33 | if (maximized) { 34 | mpvqcApplication.showNormal(); 35 | } else { 36 | mpvqcApplication.showMaximized(); 37 | } 38 | } 39 | 40 | function toggleFullScreen() { 41 | if (fullscreen) { 42 | disableFullScreen(); 43 | } else { 44 | enableFullScreen(); 45 | } 46 | } 47 | 48 | function enableFullScreen() { 49 | if (!fullscreen) { 50 | wasMaximizedBefore = maximized; 51 | mpvqcApplication.showFullScreen(); 52 | } 53 | } 54 | 55 | function disableFullScreen() { 56 | if (fullscreen && wasMaximizedBefore) { 57 | mpvqcApplication.showMaximized(); 58 | } else if (fullscreen) { 59 | mpvqcApplication.showNormal(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /qml/app/qmldir: -------------------------------------------------------------------------------- 1 | module app 2 | 3 | MpvqcApplication MpvqcApplication.qml 4 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcDialogExportDocument.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Dialogs 21 | 22 | FileDialog { 23 | title: qsTranslate("FileInteractionDialogs", "Save QC Document As") 24 | fileMode: FileDialog.SaveFile 25 | defaultSuffix: "txt" 26 | nameFilters: [ 27 | qsTranslate("FileInteractionDialogs", "QC documents") + " (*.txt)", 28 | qsTranslate("FileInteractionDialogs", "All files") + " (*)", 29 | ] 30 | 31 | signal savePressed(url fileUrl) 32 | 33 | onAccepted: { 34 | savePressed(currentFile); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcDialogImportDocuments.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Dialogs 21 | 22 | FileDialog { 23 | required property var mpvqcApplication 24 | 25 | readonly property var mpvqcManager: mpvqcApplication.mpvqcManager 26 | readonly property var mpvqcSettings: mpvqcApplication.mpvqcSettings 27 | 28 | title: qsTranslate("FileInteractionDialogs", "Open QC Document(s)") 29 | currentFolder: mpvqcSettings.lastDirectoryDocuments 30 | fileMode: FileDialog.OpenFiles 31 | nameFilters: [ 32 | qsTranslate("FileInteractionDialogs", "QC documents") + " (*.txt)", 33 | qsTranslate("FileInteractionDialogs", "All files") + " (*)", 34 | ] 35 | 36 | onAccepted: { 37 | mpvqcSettings.lastDirectoryDocuments = currentFolder; 38 | mpvqcManager.openDocuments(selectedFiles); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcDialogImportSubtitles.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Dialogs 21 | 22 | FileDialog { 23 | required property var mpvqcApplication 24 | 25 | readonly property var mpvqcManager: mpvqcApplication.mpvqcManager 26 | readonly property var mpvqcSettings: mpvqcApplication.mpvqcSettings 27 | readonly property var mpvqcUtilityPyObject: mpvqcApplication.mpvqcUtilityPyObject 28 | 29 | title: qsTranslate("FileInteractionDialogs", "Open Subtitle(s)") 30 | currentFolder: mpvqcSettings.lastDirectorySubtitles 31 | fileMode: FileDialog.OpenFiles 32 | nameFilters: [ 33 | qsTranslate("FileInteractionDialogs", "Subtitle files") + mpvqcUtilityPyObject.subtitleFileGlobPattern, 34 | qsTranslate("FileInteractionDialogs", "All files") + " (*)", 35 | ] 36 | 37 | onAccepted: { 38 | mpvqcSettings.lastDirectorySubtitles = currentFolder; 39 | mpvqcManager.openSubtitles(selectedFiles); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcDialogImportVideo.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Dialogs 21 | 22 | FileDialog { 23 | required property var mpvqcApplication 24 | 25 | readonly property var mpvqcManager: mpvqcApplication.mpvqcManager 26 | readonly property var mpvqcSettings: mpvqcApplication.mpvqcSettings 27 | readonly property var mpvqcUtilityPyObject: mpvqcApplication.mpvqcUtilityPyObject 28 | 29 | title: qsTranslate("FileInteractionDialogs", "Open Video") 30 | currentFolder: mpvqcSettings.lastDirectoryVideo 31 | nameFilters: [ 32 | qsTranslate("FileInteractionDialogs", "Video files") + mpvqcUtilityPyObject.videoFileGlobPattern, 33 | qsTranslate("FileInteractionDialogs", "All files") + " (*)", 34 | ] 35 | 36 | onAccepted: { 37 | mpvqcSettings.lastDirectoryVideo = currentFolder; 38 | mpvqcManager.openVideo(currentFile); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcMessageBoxDocumentNotCompatible.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import shared 21 | 22 | MpvqcMessageBox { 23 | id: root 24 | 25 | required property int count 26 | 27 | title: count === 1 28 | ? qsTranslate("MessageBoxes", "Document Not Compatible") 29 | : qsTranslate("MessageBoxes", "Documents Not Compatible") 30 | 31 | } 32 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcMessageBoxExtendedExport.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2024 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | import shared 24 | 25 | MpvqcMessageBox { 26 | title: qsTranslate("MessageBoxes", "Extended Exports") 27 | 28 | contentItem: Label { 29 | //: %1 will be the link to the Jinja templating engine. %2 will be the link to mpvQC's documentation about export templates 30 | text: qsTranslate("MessageBoxes", "mpvQC allows for customizing report exports using the %1 engine. To begin, visit %2") 31 | .arg(`Jinja template`) 32 | .arg(`https://mpvqc.github.io/export-templates`) 33 | horizontalAlignment: Text.AlignLeft 34 | wrapMode: Label.WordWrap 35 | elide: Text.ElideLeft 36 | 37 | onLinkActivated: link => { 38 | Qt.openUrlExternally(link); 39 | } 40 | 41 | MouseArea { 42 | anchors.fill: parent 43 | acceptedButtons: Qt.NoButton 44 | cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor 45 | hoverEnabled: true 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcMessageBoxNewDocument.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Controls.Material 21 | 22 | import shared 23 | 24 | MpvqcMessageBox { 25 | title: qsTranslate("MessageBoxes", "Unsaved Changes") 26 | text: qsTranslate("MessageBoxes", "Do you really want to create a new QC document without saving your QC?") 27 | standardButtons: Dialog.Yes | Dialog.No 28 | } 29 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcMessageBoxQuit.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Controls.Material 21 | 22 | import shared 23 | 24 | MpvqcMessageBox { 25 | title: qsTranslate("MessageBoxes", "Unsaved Changes") 26 | text: qsTranslate("MessageBoxes", "Do you really want to quit without saving your QC?") 27 | standardButtons: Dialog.Yes | Dialog.Cancel 28 | } 29 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcMessageBoxVersionCheck.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | import shared 24 | 25 | MpvqcMessageBox { 26 | id: root 27 | 28 | readonly property var mpvqcVersionCheckerPyObject: mpvqcApplication.mpvqcVersionCheckerPyObject 29 | 30 | title: qsTranslate("MessageBoxes", "Checking for Updates...") 31 | 32 | contentItem: Label { 33 | text: qsTranslate("MessageBoxes", "Loading...") 34 | horizontalAlignment: Text.AlignLeft 35 | wrapMode: Label.WordWrap 36 | elide: Text.ElideLeft 37 | 38 | onLinkActivated: link => { 39 | Qt.openUrlExternally(link); 40 | } 41 | 42 | MouseArea { 43 | anchors.fill: parent 44 | acceptedButtons: Qt.NoButton 45 | cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor 46 | hoverEnabled: true 47 | } 48 | } 49 | 50 | Component.onCompleted: { 51 | root.mpvqcVersionCheckerPyObject.check_for_new_version(); 52 | } 53 | 54 | Connections { 55 | target: root.mpvqcVersionCheckerPyObject 56 | 57 | function onVersionChecked(title: string, text: string) { 58 | root.title = title; 59 | root.contentItem.text = text; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /qml/dialogs/MpvqcMessageBoxVideoFound.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Controls.Material 21 | 22 | import shared 23 | 24 | MpvqcMessageBox { 25 | title: qsTranslate("MessageBoxes", "Video Found") 26 | text: qsTranslate("MessageBoxes", "A video was found. Do you want to open it?") 27 | standardButtons: Dialog.Yes | Dialog.No 28 | } 29 | -------------------------------------------------------------------------------- /qml/dialogs/about/MpvqcDialogAbout.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import shared 21 | 22 | MpvqcDialog { 23 | id: root 24 | 25 | MpvqcAboutView { 26 | //: Title of a tab in the about dialog 27 | property string title: qsTranslate("AboutDialog", "About") 28 | 29 | width: root.width 30 | } 31 | 32 | MpvqcCreditsView { 33 | //: Title of a tab in the about dialog 34 | property string title: qsTranslate("AboutDialog", "Credits") 35 | 36 | width: root.width 37 | } 38 | 39 | MpvqcDependenciesView { 40 | //: Title of a tab in the about dialog 41 | property string title: qsTranslate("AboutDialog", "Dependencies") 42 | 43 | width: root.width 44 | mpvqcApplication: root.mpvqcApplication 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /qml/dialogs/appearance/MpvqcDialogAppearance.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | import shared 24 | 25 | MpvqcDialog { 26 | id: root 27 | 28 | ScrollView { 29 | property string title: qsTranslate("AppearanceDialog", "Appearance") 30 | 31 | width: parent.width 32 | 33 | Column { 34 | width: parent.width 35 | 36 | MpvqcHeader { 37 | text: qsTranslate("AppearanceDialog", "Theme") 38 | width: parent.width 39 | } 40 | 41 | MpvqcThemeView { 42 | id: _themeView 43 | 44 | mpvqcApplication: root.mpvqcApplication 45 | width: parent.width 46 | 47 | // workaround padding issue in rtl 48 | Binding on x { when: root.mirrored; value: -8 } 49 | } 50 | 51 | MpvqcHeader { 52 | text: qsTranslate("AppearanceDialog", "Color") 53 | width: parent.width 54 | } 55 | 56 | MpvqcColorView { 57 | id: _colorView 58 | 59 | mpvqcApplication: root.mpvqcApplication 60 | width: parent.width 61 | } 62 | } 63 | } 64 | 65 | onRejected: { 66 | _themeView.reset(); 67 | _colorView.reset(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /qml/dialogs/backup/MpvqcDialogBackup.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import shared 21 | 22 | MpvqcDialog { 23 | id: root 24 | 25 | MpvqcBackupView { 26 | id: _backupView 27 | 28 | property string title: qsTranslate("BackupDialog", "Backup Settings") 29 | 30 | width: root.width 31 | mpvqcApplication: root.mpvqcApplication 32 | } 33 | 34 | onAccepted: { 35 | _backupView.accept(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /qml/dialogs/commenttypes/MpvqcDialogCommentTypes.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Controls.Material 21 | 22 | import shared 23 | 24 | MpvqcDialog { 25 | id: root 26 | 27 | standardButtons: Dialog.Ok | Dialog.Cancel | Dialog.Reset 28 | 29 | MpvqcCommentTypesView { 30 | id: _view 31 | 32 | property string title: qsTranslate("CommentTypesDialog", "Comment Types") 33 | 34 | mpvqcApplication: root.mpvqcApplication 35 | width: parent.width 36 | } 37 | 38 | onAccepted: { 39 | _view.visible = false; 40 | _view.acceptTemporaryState(); 41 | } 42 | 43 | onReset: { 44 | _view.resetTemporaryEdits(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /qml/dialogs/commenttypes/tst_MpvqcList.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | Item { 24 | id: testHelper 25 | 26 | width: 400 27 | height: 400 28 | 29 | MpvqcList { 30 | id: objectUnderTest 31 | anchors.fill: testHelper 32 | 33 | model: ["Type 0", "Type 1", "Type 2", "Type 3", "Type 4", "Type 5"] 34 | itemHeight: 42 35 | 36 | mpvqcApplication: QtObject { 37 | property var mpvqcTheme: QtObject { 38 | property color control: "purple" 39 | } 40 | } 41 | } 42 | 43 | TestCase { 44 | name: "MpvqcList" 45 | when: windowShown 46 | 47 | function cleanup() { 48 | objectUnderTest.currentIndex = 0; 49 | } 50 | 51 | function test_selection_data() { 52 | return [ 53 | { 54 | tag: "row-3", 55 | index: 3 56 | }, 57 | { 58 | tag: "row-5", 59 | index: 5 60 | }, 61 | ]; 62 | } 63 | 64 | function test_selection(data) { 65 | ensureRenderedUntilItem(data.index + 1); 66 | compare(objectUnderTest.currentIndex, 0); 67 | 68 | const item = objectUnderTest.itemAtIndex(data.index); 69 | mouseClick(item); 70 | 71 | compare(objectUnderTest.currentIndex, data.index); 72 | } 73 | 74 | function ensureRenderedUntilItem(index: int): void { 75 | objectUnderTest.currentIndex = index; 76 | objectUnderTest.currentIndex = 0; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /qml/dialogs/editinput/MpvqcDialogEditInput.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | import shared 24 | 25 | MpvqcDialog { 26 | id: root 27 | 28 | property alias editView: _editView 29 | 30 | contentWidth: Math.min(1080, mpvqcApplication.width * 0.75) 31 | contentHeight: Math.min(1080, mpvqcApplication.height * 0.75) 32 | standardButtons: Dialog.Ok | Dialog.Cancel | Dialog.Reset 33 | 34 | MpvqcEditInputView { 35 | id: _editView 36 | 37 | property string title: qsTranslate("InputConfEditDialog", "Edit input.conf") 38 | 39 | width: root.contentWidth 40 | mpvqcApplication: root.mpvqcApplication 41 | } 42 | 43 | onAccepted: { 44 | _editView.accept(); 45 | } 46 | 47 | onReset: { 48 | _editView.reset(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /qml/dialogs/editinput/tst_MpvqcDialogEditInput.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | TestCase { 24 | id: testCase 25 | 26 | width: 400 27 | height: 400 28 | visible: true 29 | when: windowShown 30 | name: "MpvqcDialogEditInput" 31 | 32 | Component { 33 | id: objectUnderTest 34 | 35 | MpvqcDialogEditInput { 36 | 37 | mpvqcApplication: QtObject { 38 | property var mpvqcPlayerFilesPyObject: QtObject { 39 | property string default_input_conf_content: "default" 40 | property url input_conf_url: "" 41 | } 42 | } 43 | } 44 | } 45 | 46 | function test_edit_accept() { 47 | let acceptCalled = false; 48 | 49 | const control = createTemporaryObject(objectUnderTest, testCase, { 50 | "editView.accept": () => { 51 | acceptCalled = true; 52 | } 53 | }); 54 | verify(control); 55 | 56 | control.editView.textArea.text = "changed by user"; 57 | control.accepted(); 58 | verify(acceptCalled); 59 | } 60 | 61 | function test_edit_reset() { 62 | const control = createTemporaryObject(objectUnderTest, testCase); 63 | verify(control); 64 | 65 | control.editView.textArea.text = "changed by user"; 66 | 67 | control.reset(); 68 | compare(control.editView.textArea.text, "default"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /qml/dialogs/editmpv/MpvqcDialogEditMpv.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | import shared 24 | 25 | MpvqcDialog { 26 | id: root 27 | 28 | property alias editView: _editView 29 | 30 | contentWidth: Math.min(1080, mpvqcApplication.width * 0.75) 31 | contentHeight: Math.min(1080, mpvqcApplication.height * 0.75) 32 | standardButtons: Dialog.Ok | Dialog.Cancel | Dialog.Reset 33 | 34 | MpvqcEditMpvView { 35 | id: _editView 36 | 37 | property string title: qsTranslate("MpvConfEditDialog", "Edit mpv.conf") 38 | 39 | width: root.contentWidth 40 | mpvqcApplication: root.mpvqcApplication 41 | } 42 | 43 | onAccepted: { 44 | _editView.accept(); 45 | } 46 | 47 | onReset: { 48 | _editView.reset(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /qml/dialogs/editmpv/tst_MpvqcDialogEditMpv.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | TestCase { 24 | id: testCase 25 | 26 | width: 400 27 | height: 400 28 | visible: true 29 | when: windowShown 30 | name: "MpvqcDialogEditMpv" 31 | 32 | Component { 33 | id: objectUnderTest 34 | 35 | MpvqcDialogEditMpv { 36 | 37 | mpvqcApplication: QtObject { 38 | property var mpvqcPlayerFilesPyObject: QtObject { 39 | property string default_mpv_conf_content: "default" 40 | property url mpv_conf_url: "" 41 | } 42 | } 43 | } 44 | } 45 | 46 | function test_edit_accept() { 47 | let acceptCalled = false; 48 | 49 | const control = createTemporaryObject(objectUnderTest, testCase, { 50 | "editView.accept": () => { 51 | acceptCalled = true; 52 | } 53 | }); 54 | verify(control); 55 | 56 | control.editView.textArea.text = "changed by user"; 57 | control.accepted(); 58 | verify(acceptCalled); 59 | } 60 | 61 | function test_edit_reset() { 62 | const control = createTemporaryObject(objectUnderTest, testCase); 63 | verify(control); 64 | 65 | control.editView.textArea.text = "changed by user"; 66 | 67 | control.reset(); 68 | compare(control.editView.textArea.text, "default"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /qml/dialogs/export/MpvqcDialogExport.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import shared 21 | 22 | MpvqcDialog { 23 | id: root 24 | 25 | MpvqcExportView { 26 | id: _exportView 27 | 28 | property string title: qsTranslate("ExportSettingsDialog", "Export Settings") 29 | 30 | width: root.width 31 | mpvqcApplication: root.mpvqcApplication 32 | } 33 | 34 | onAccepted: { 35 | _exportView.accept(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /qml/dialogs/import/MpvqcDialogImport.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import shared 21 | 22 | MpvqcDialog { 23 | id: root 24 | 25 | MpvqcImportView { 26 | id: _importView 27 | 28 | property string title: qsTranslate("ImportSettingsDialog", "Import Settings") 29 | 30 | width: root.width 31 | mpvqcApplication: root.mpvqcApplication 32 | } 33 | 34 | onAccepted: { 35 | _importView.accept(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /qml/dialogs/qmldir: -------------------------------------------------------------------------------- 1 | module dialogs 2 | 3 | MpvqcDialogAbout about/MpvqcDialogAbout.qml 4 | 5 | MpvqcDialogAppearance appearance/MpvqcDialogAppearance.qml 6 | 7 | MpvqcDialogBackup backup/MpvqcDialogBackup.qml 8 | 9 | MpvqcDialogCommentTypes commenttypes/MpvqcDialogCommentTypes.qml 10 | 11 | MpvqcDialogImport import/MpvqcDialogImport.qml 12 | 13 | MpvqcDialogEditInput editinput/MpvqcDialogEditInput.qml 14 | MpvqcDialogEditMpv editmpv/MpvqcDialogEditMpv.qml 15 | 16 | MpvqcDialogExport export/MpvqcDialogExport.qml 17 | 18 | MpvqcDialogShortcuts shortcuts/MpvqcDialogShortcuts.qml 19 | 20 | MpvqcDialogExportDocument MpvqcDialogExportDocument.qml 21 | MpvqcDialogImportDocuments MpvqcDialogImportDocuments.qml 22 | MpvqcDialogImportSubtitles MpvqcDialogImportSubtitles.qml 23 | MpvqcDialogImportVideo MpvqcDialogImportVideo.qml 24 | 25 | MpvqcMessageBoxDocumentNotCompatible MpvqcMessageBoxDocumentNotCompatible.qml 26 | MpvqcMessageBoxExtendedExport MpvqcMessageBoxExtendedExport.qml 27 | MpvqcMessageBoxNewDocument MpvqcMessageBoxNewDocument.qml 28 | MpvqcMessageBoxQuit MpvqcMessageBoxQuit.qml 29 | MpvqcMessageBoxVersionCheck MpvqcMessageBoxVersionCheck.qml 30 | MpvqcMessageBoxVideoFound MpvqcMessageBoxVideoFound.qml 31 | -------------------------------------------------------------------------------- /qml/dialogs/shortcuts/MpvqcDialogShortcuts.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | import shared 23 | 24 | MpvqcDialog { 25 | id: root 26 | 27 | contentWidth: 500 28 | contentHeight: Math.min(1080, mpvqcApplication.height * 0.75) 29 | 30 | MpvqcShortcutView { 31 | property string title: qsTranslate("ShortcutsDialog", "Keyboard Shortcuts") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /qml/dialogs/shortcuts/MpvqcShortcutButton.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2024 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | Button { 24 | readonly property bool hasContent: text || icon.source.toString() 25 | 26 | enabled: false 27 | visible: hasContent 28 | height: hasContent ? implicitHeight : 0 29 | width: hasContent ? implicitWidth : 0 30 | } 31 | -------------------------------------------------------------------------------- /qml/dialogs/shortcuts/MpvqcShortcutFilterModel.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2024 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | // https://martin.rpdev.net/2019/01/15/using-delegatemodel-in-qml-for-sorting-and-filtering.html 21 | 22 | import QtQuick 23 | import QtQml.Models 24 | 25 | DelegateModel { 26 | property var filterAcceptsItem: item => true 27 | 28 | function update() { 29 | if (items.count > 0) { 30 | items.setGroups(0, items.count, "items"); 31 | } 32 | 33 | const visible = []; 34 | for (let i = 0; i < items.count; ++i) { 35 | const item = items.get(i); 36 | if (filterAcceptsItem(item.model)) { 37 | visible.push(item); 38 | } 39 | } 40 | 41 | for (let i = 0; i < visible.length; ++i) { 42 | const item = visible[i]; 43 | item.inVisible = true; 44 | if (item.visibleIndex !== i) { 45 | visibleItems.move(item.visibleIndex, i, 1); 46 | } 47 | } 48 | } 49 | 50 | groups: DelegateModelGroup { 51 | id: visibleItems 52 | 53 | name: "visible" 54 | includeByDefault: false 55 | } 56 | 57 | filterOnGroup: "visible" 58 | 59 | Component.onCompleted: { 60 | _filterModel.update(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /qml/dialogs/tst_MpvqcDialogExportDocument.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | TestCase { 24 | id: testCase 25 | 26 | readonly property url currentFile: "file:///hello.txt" 27 | 28 | name: "MpvqcDialogExportDocument" 29 | when: windowShown 30 | 31 | Component { 32 | id: objectUnderTest 33 | 34 | MpvqcDialogExportDocument { 35 | property url savedUrlInTest 36 | 37 | onSavePressed: fileUrl => { 38 | this.savedUrlInTest = fileUrl; 39 | } 40 | } 41 | } 42 | 43 | function test_save() { 44 | const control = createTemporaryObject(objectUnderTest, testCase); 45 | verify(control); 46 | 47 | control.currentFile = testCase.currentFile; 48 | control.accepted(); 49 | compare(control.savedUrlInTest, testCase.currentFile); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /qml/dialogs/tst_MpvqcDialogImportDocuments.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | TestCase { 24 | id: testCase 25 | 26 | width: 400 27 | height: 400 28 | visible: true 29 | when: windowShown 30 | name: "MpvqcDialogImportDocuments" 31 | 32 | Component { 33 | id: objectUnderTest 34 | 35 | MpvqcDialogImportDocuments { 36 | id: __objectUnderTest 37 | 38 | property bool openDocumentsCalled: false 39 | 40 | mpvqcApplication: QtObject { 41 | property var mpvqcManager: QtObject { 42 | function openDocuments(files) { 43 | __objectUnderTest.openDocumentsCalled = true; 44 | } 45 | } 46 | property var mpvqcSettings: QtObject { 47 | property string lastDirectoryDocuments: "initial directory" 48 | } 49 | } 50 | } 51 | } 52 | 53 | function test_import() { 54 | const control = createTemporaryObject(objectUnderTest, testCase); 55 | verify(control); 56 | 57 | control.currentFolder = "some directory"; 58 | control.accepted(); 59 | 60 | verify(control.openDocumentsCalled); 61 | verify(!control.mpvqcSettings.lastDirectoryDocuments.toString().includes("initial directory")); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /qml/dialogs/tst_MpvqcDialogImportSubtitles.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | TestCase { 24 | id: testCase 25 | 26 | width: 400 27 | height: 400 28 | visible: true 29 | when: windowShown 30 | name: "MpvqcDialogImportSubtitles" 31 | 32 | Component { 33 | id: objectUnderTest 34 | 35 | MpvqcDialogImportSubtitles { 36 | id: __objectUnderTest 37 | 38 | property bool openSubtitlesCalled: false 39 | 40 | mpvqcApplication: QtObject { 41 | property var mpvqcManager: QtObject { 42 | function openSubtitles(files) { 43 | __objectUnderTest.openSubtitlesCalled = true; 44 | } 45 | } 46 | property var mpvqcSettings: QtObject { 47 | property string lastDirectorySubtitles: "initial directory" 48 | } 49 | property var supportedSubtitleFileExtensions: ["ass"] 50 | } 51 | } 52 | } 53 | 54 | function test_import() { 55 | const control = createTemporaryObject(objectUnderTest, testCase); 56 | verify(control); 57 | 58 | control.currentFolder = "some directory"; 59 | control.accepted(); 60 | 61 | verify(control.openSubtitlesCalled); 62 | verify(!control.mpvqcSettings.lastDirectorySubtitles.toString().includes("initial directory")); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /qml/dialogs/tst_MpvqcDialogImportVideo.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | TestCase { 24 | id: testCase 25 | 26 | width: 400 27 | height: 400 28 | visible: true 29 | when: windowShown 30 | name: "MpvqcDialogImportVideo" 31 | 32 | Component { 33 | id: objectUnderTest 34 | 35 | MpvqcDialogImportVideo { 36 | id: __objectUnderTest 37 | 38 | property bool openVideoCalled: false 39 | 40 | mpvqcApplication: QtObject { 41 | property var mpvqcManager: QtObject { 42 | function openVideo(video) { 43 | __objectUnderTest.openVideoCalled = true; 44 | } 45 | } 46 | property var mpvqcSettings: QtObject { 47 | property string lastDirectoryVideo: "initial directory" 48 | } 49 | } 50 | } 51 | } 52 | 53 | function test_import() { 54 | const control = createTemporaryObject(objectUnderTest, testCase); 55 | verify(control); 56 | 57 | control.currentFolder = "some directory"; 58 | control.accepted(); 59 | 60 | verify(control.openVideoCalled); 61 | verify(!control.mpvqcSettings.lastDirectoryVideo.toString().includes("initial directory")); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /qml/footer/qmldir: -------------------------------------------------------------------------------- 1 | module footer 2 | 3 | MpvqcFooter MpvqcFooter.qml 4 | -------------------------------------------------------------------------------- /qml/header/MpvqcSubMenuLanguage.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | pragma ComponentBehavior: Bound 21 | 22 | import QtQuick 23 | import QtQuick.Controls.Material 24 | 25 | import models 26 | import shared 27 | 28 | MpvqcMenu { 29 | id: root 30 | 31 | required property var mpvqcApplication 32 | 33 | readonly property var mpvqcSettings: mpvqcApplication.mpvqcSettings 34 | 35 | readonly property alias repeater: _repeater 36 | 37 | title: qsTranslate("MainWindow", "Language") 38 | icon.source: "qrc:/data/icons/language_black_24dp.svg" 39 | icon.height: 24 40 | icon.width: 24 41 | 42 | Repeater { 43 | id: _repeater 44 | 45 | model: MpvqcLanguageModel {} 46 | 47 | MenuItem { 48 | id: item 49 | 50 | required property string language 51 | required property string identifier 52 | 53 | property var timer: Timer { 54 | interval: 125 55 | 56 | onTriggered: { 57 | Qt.uiLanguage = item.identifier; 58 | root.mpvqcSettings.language = item.identifier; 59 | } 60 | } 61 | 62 | text: qsTranslate("Languages", item.language) 63 | autoExclusive: true 64 | checkable: true 65 | checked: item.identifier === Qt.uiLanguage 66 | 67 | function changeLanguage() { 68 | timer.start(); 69 | } 70 | 71 | onTriggered: { 72 | changeLanguage(); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /qml/header/MpvqcSubMenuSplitViewOrientation.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | import shared 24 | 25 | MpvqcMenu { 26 | id: root 27 | 28 | required property var mpvqcApplication 29 | 30 | readonly property var mpvqcSettings: mpvqcApplication.mpvqcSettings 31 | 32 | readonly property bool isVerticalLayout: mpvqcSettings.layoutOrientation === Qt.Vertical 33 | 34 | readonly property alias verticalLayout: _verticalLayout 35 | readonly property alias horizontalLayout: _horizontalLayout 36 | 37 | title: qsTranslate("MainWindow", "Application Layout") 38 | icon.source: "qrc:/data/icons/vertical_split_black_24dp.svg" 39 | icon.height: 24 40 | icon.width: 24 41 | 42 | MenuItem { 43 | id: _verticalLayout 44 | 45 | text: qsTranslate("MainWindow", "Video Above Comments") 46 | autoExclusive: true 47 | checkable: true 48 | checked: root.isVerticalLayout 49 | 50 | onTriggered: { 51 | root.mpvqcSettings.layoutOrientation = Qt.Vertical; 52 | } 53 | } 54 | 55 | MenuItem { 56 | id: _horizontalLayout 57 | 58 | text: qsTranslate("MainWindow", "Video Next to Comments") 59 | autoExclusive: true 60 | checkable: true 61 | checked: !root.isVerticalLayout 62 | 63 | onTriggered: { 64 | root.mpvqcSettings.layoutOrientation = Qt.Horizontal; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /qml/header/qmldir: -------------------------------------------------------------------------------- 1 | module header 2 | 3 | MpvqcHeader MpvqcHeader.qml 4 | -------------------------------------------------------------------------------- /qml/header/tst_MpvqcSubMenuWindowTitle.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | import settings 24 | 25 | TestCase { 26 | id: testCase 27 | 28 | width: 400 29 | height: 400 30 | visible: true 31 | when: windowShown 32 | name: "MpvqcSubMenuWindowTitle" 33 | 34 | Component { 35 | id: objectUnderTest 36 | 37 | MpvqcSubMenuWindowTitle { 38 | mpvqcApplication: QtObject { 39 | property var mpvqcSettings: QtObject { 40 | property var windowTitleFormat: MpvqcSettings.WindowTitleFormat.DEFAULT 41 | } 42 | } 43 | } 44 | } 45 | 46 | function test_selection() { 47 | const control = createTemporaryObject(objectUnderTest, testCase); 48 | verify(control); 49 | 50 | control.defaultFormat.triggered(); 51 | compare(control.mpvqcApplication.mpvqcSettings.windowTitleFormat, MpvqcSettings.WindowTitleFormat.DEFAULT); 52 | verify(!control.fileNameFormat.checked); 53 | verify(!control.filePathFormat.checked); 54 | 55 | control.fileNameFormat.triggered(); 56 | compare(control.mpvqcApplication.mpvqcSettings.windowTitleFormat, MpvqcSettings.WindowTitleFormat.FILE_NAME); 57 | verify(!control.defaultFormat.checked); 58 | verify(!control.filePathFormat.checked); 59 | 60 | control.filePathFormat.triggered(); 61 | compare(control.mpvqcApplication.mpvqcSettings.windowTitleFormat, MpvqcSettings.WindowTitleFormat.FILE_PATH); 62 | verify(!control.defaultFormat.checked); 63 | verify(!control.fileNameFormat.checked); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /qml/models/MpvqcArtworkModel.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | ListModel { 23 | ListElement { 24 | name: "maleunam" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /qml/models/MpvqcDeveloperModel.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | ListModel { 23 | ListElement { 24 | name: "Elias Müller" 25 | } 26 | ListElement { 27 | name: "Frechdachs" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /qml/models/MpvqcLanguageModel.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | ListModel { 23 | readonly property string systemLanguage: { 24 | const uiLanguages = Qt.locale().uiLanguages; 25 | return _identifiers().find(language => uiLanguages.includes(language)) ?? "en-US"; 26 | } 27 | 28 | function _identifiers(): list { 29 | const marshalled = []; 30 | for (let i = 0; i < count; i++) { 31 | marshalled.push(get(i).identifier); 32 | } 33 | return marshalled; 34 | } 35 | 36 | ListElement { 37 | language: QT_TRANSLATE_NOOP("Languages", "German") 38 | identifier: "de-DE" 39 | } 40 | ListElement { 41 | language: QT_TRANSLATE_NOOP("Languages", "English") 42 | identifier: "en-US" 43 | } 44 | ListElement { 45 | language: QT_TRANSLATE_NOOP("Languages", "Spanish") 46 | identifier: "es-MX" 47 | translator: "CiferrC" 48 | } 49 | ListElement { 50 | language: QT_TRANSLATE_NOOP("Languages", "Hebrew") 51 | identifier: "he-IL" 52 | translator: "cN3rd" 53 | } 54 | ListElement { 55 | language: QT_TRANSLATE_NOOP("Languages", "Italian") 56 | identifier: "it-IT" 57 | translator: "maddo" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /qml/models/MpvqcLibraryModel.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | ListModel { 23 | ListElement { 24 | name: "inject" 25 | url: "https://github.com/ivankorobkov/python-inject" 26 | licence: "Apache-2.0" 27 | version: "@@pypi-inject@@" 28 | os: "linux, windows" 29 | } 30 | ListElement { 31 | name: "jinja2" 32 | url: "https://github.com/pallets/jinja/" 33 | licence: "BSD-3-Clause" 34 | version: "@@pypi-jinja2@@" 35 | os: "linux, windows" 36 | } 37 | ListElement { 38 | name: "python-mpv" 39 | url: "https://github.com/jaseg/python-mpv" 40 | licence: "GPL-3.0" 41 | version: "@@pypi-mpv@@" 42 | os: "linux, windows" 43 | } 44 | ListElement { 45 | name: "PySide6" 46 | url: "https://wiki.qt.io/Qt_for_Python" 47 | licence: "LGPL-3.0" 48 | version: "@@pypi-pyside6-essentials@@" 49 | os: "linux, windows" 50 | } 51 | ListElement { 52 | name: "pywin32" 53 | url: "https://github.com/mhammond/pywin32" 54 | licence: "PSF" 55 | version: "@@pypi-pywin32@@" 56 | os: "windows" 57 | } 58 | ListElement { 59 | name: "pytest" 60 | url: "https://github.com/pytest-dev/pytest" 61 | licence: "MIT" 62 | version: "@@pypi-pytest@@" 63 | os: "linux, windows" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /qml/models/qmldir: -------------------------------------------------------------------------------- 1 | module models 2 | MpvqcArtworkModel MpvqcArtworkModel.qml 3 | MpvqcLibraryModel MpvqcLibraryModel.qml 4 | MpvqcDeveloperModel MpvqcDeveloperModel.qml 5 | MpvqcLanguageModel MpvqcLanguageModel.qml 6 | -------------------------------------------------------------------------------- /qml/player/MpvqcPlayerLinux.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | import pyobjects 23 | import shared 24 | 25 | 26 | MpvqcMpvFrameBufferObjectPyObject { 27 | id: root 28 | 29 | required property var mpvqcApplication 30 | 31 | readonly property var mpvqcNewCommentMenu: mpvqcApplication.mpvqcNewCommentMenu 32 | 33 | MpvqcPlayerMouseArea { 34 | mpvqcApplication: root.mpvqcApplication 35 | anchors.fill: root 36 | 37 | onRightMouseButtonPressed: { 38 | root.mpvqcNewCommentMenu.popupMenu() 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /qml/player/MpvqcPlayerWindows.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | import pyobjects 23 | import shared 24 | 25 | 26 | WindowContainer { 27 | id: root 28 | 29 | required property var mpvqcApplication 30 | 31 | readonly property var mpvqcNewCommentMenu: mpvqcApplication.mpvqcNewCommentMenu 32 | 33 | window: MpvWindowPyObject { 34 | flags: Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus | Qt.WindowTransparentForInput 35 | color: "black" 36 | } 37 | 38 | MpvqcPlayerMouseArea { 39 | mpvqcApplication: root.mpvqcApplication 40 | anchors.fill: root 41 | 42 | onRightMouseButtonPressed: { 43 | root.mpvqcNewCommentMenu.popupMenu() 44 | } 45 | 46 | onPressed: { 47 | root.mpvqcApplication.requestActivate() 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /qml/player/qmldir: -------------------------------------------------------------------------------- 1 | module player 2 | 3 | MpvqcPlayerLinux MpvqcPlayerLinux.qml 4 | MpvqcPlayerWindows MpvqcPlayerWindows.qml 5 | -------------------------------------------------------------------------------- /qml/settings/qmldir: -------------------------------------------------------------------------------- 1 | module settings 2 | 3 | MpvqcSettings MpvqcSettings.qml 4 | -------------------------------------------------------------------------------- /qml/shared/MpvqcDebugRectangle.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | 22 | Rectangle { 23 | property var fill: parent 24 | 25 | anchors.fill: fill 26 | z: 10 27 | color: "transparent" 28 | border.color: "black" 29 | border.width: 1 30 | } 31 | -------------------------------------------------------------------------------- /qml/shared/MpvqcDialog.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | pragma ComponentBehavior: Bound 21 | 22 | import QtQuick 23 | import QtQuick.Controls.Material 24 | import QtQuick.Layouts 25 | 26 | Dialog { 27 | id: root 28 | 29 | required property var mpvqcApplication 30 | 31 | property alias bar: _bar 32 | property alias stack: _stack 33 | 34 | default property alias content: _stack.children 35 | 36 | popupType: Qt.platform.os === "windows" ? Popup.Window : Popup.Item 37 | anchors.centerIn: parent 38 | parent: mpvqcApplication.contentItem 39 | contentWidth: 370 40 | contentHeight: 450 41 | modal: true 42 | dim: false 43 | z: 2 44 | closePolicy: Popup.CloseOnEscape 45 | standardButtons: Dialog.Ok 46 | 47 | contentItem: ColumnLayout { 48 | id: _layout 49 | 50 | TabBar { 51 | id: _bar 52 | contentWidth: _layout.width 53 | 54 | Repeater { 55 | model: root.content.length 56 | 57 | delegate: TabButton { 58 | required property int index 59 | 60 | text: root.content[index].title 61 | } 62 | } 63 | } 64 | 65 | StackLayout { 66 | id: _stack 67 | currentIndex: _bar.currentIndex 68 | } 69 | } 70 | 71 | onClosed: { 72 | root.bar.currentIndex = 0; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /qml/shared/MpvqcHeader.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | Label { 24 | 25 | font.weight: Font.DemiBold 26 | font.pointSize: 14 27 | horizontalAlignment: Text.AlignHCenter 28 | topPadding: 15 29 | bottomPadding: 10 30 | } 31 | -------------------------------------------------------------------------------- /qml/shared/MpvqcMenu.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | Menu { 24 | 25 | readonly property bool mMirrored: count > 0 && itemAt(0).mirrored 26 | 27 | z: 2 28 | x: mMirrored ? -width + parent.width : 0 29 | popupType: Qt.platform.os === "windows" ? Popup.Window : Popup.Item 30 | dim: false 31 | 32 | width: calculateMenuWidths() 33 | 34 | function calculateMenuWidths(): int { 35 | // Adapted from: https://martin.rpdev.net/2018/03/13/qt-quick-controls-2-automatically-set-the-width-of-menus.html 36 | let result = 0; 37 | let padding = 0; 38 | for (let i = 0; i < count; ++i) { 39 | let item = itemAt(i); 40 | 41 | if (!isMenuSeparator(item)) { 42 | result = Math.max(item.contentItem.implicitWidth, result); 43 | padding = Math.max(item.padding, padding); 44 | } 45 | } 46 | return (result + padding * 2) * 1.03; 47 | } 48 | 49 | function isMenuSeparator(item: Item): bool { 50 | return item instanceof MenuSeparator; 51 | } 52 | 53 | // ********************************************************* 54 | // fixme: Workaround QTBUG-131786 to fake modal behavior on Windows 55 | onAboutToShow: enableFakeModal(); 56 | onAboutToHide: disableFakeModal(); 57 | // ********************************************************* 58 | } 59 | -------------------------------------------------------------------------------- /qml/shared/MpvqcMessageBox.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | Dialog { 24 | id: root 25 | 26 | required property var mpvqcApplication 27 | 28 | property alias text: _content.text 29 | 30 | popupType: Qt.platform.os === "windows" ? Popup.Window : Popup.Item 31 | width: 420 32 | z: 2 33 | parent: mpvqcApplication.contentItem 34 | standardButtons: Dialog.Ok 35 | closePolicy: Popup.CloseOnEscape 36 | anchors.centerIn: parent 37 | dim: false 38 | 39 | contentItem: Label { 40 | id: _content 41 | 42 | horizontalAlignment: Text.AlignLeft 43 | wrapMode: Label.WordWrap 44 | elide: Text.ElideLeft 45 | } 46 | 47 | footer: MpvqcKeyboardFocusableButtonBox {} 48 | } 49 | -------------------------------------------------------------------------------- /qml/shared/MpvqcSpinBoxRow.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | import QtQuick.Layouts 23 | 24 | RowLayout { 25 | id: root 26 | 27 | required property int prefWidth 28 | 29 | property int spinBoxWidth: 130 30 | 31 | property alias spinBox: _input 32 | property alias label: _label.text 33 | property alias suffix: _suffix.text 34 | property alias value: _input.value 35 | property alias valueFrom: _input.from 36 | property alias valueTo: _input.to 37 | 38 | signal valueModified(int value) 39 | 40 | Label { 41 | id: _label 42 | 43 | horizontalAlignment: Text.AlignRight 44 | wrapMode: Text.Wrap 45 | Layout.preferredWidth: root.prefWidth / 2 46 | } 47 | 48 | ColumnLayout { 49 | Layout.fillWidth: true 50 | Layout.leftMargin: 10 51 | Layout.topMargin: 20 52 | 53 | SpinBox { 54 | id: _input 55 | editable: true 56 | Layout.preferredWidth: root.spinBoxWidth 57 | 58 | onValueChanged: { 59 | root.valueModified(value); 60 | } 61 | } 62 | 63 | Label { 64 | id: _suffix 65 | horizontalAlignment: Text.AlignHCenter 66 | Layout.preferredWidth: root.spinBoxWidth 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /qml/shared/MpvqcSwitchRow.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | import QtQuick.Layouts 23 | 24 | RowLayout { 25 | id: root 26 | 27 | required property int prefWidth 28 | 29 | property alias toggle: _switch 30 | property alias checked: _switch.checked 31 | property alias label: _label.text 32 | 33 | signal toggled(bool checked) 34 | 35 | Label { 36 | id: _label 37 | 38 | horizontalAlignment: Text.AlignRight 39 | wrapMode: Text.Wrap 40 | Layout.preferredWidth: root.prefWidth / 2 41 | } 42 | 43 | Switch { 44 | id: _switch 45 | 46 | onCheckedChanged: { 47 | root.toggled(checked); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /qml/shared/MpvqcTextFieldRow.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | import QtQuick.Layouts 23 | 24 | RowLayout { 25 | id: root 26 | 27 | required property int prefWidth 28 | 29 | property alias label: _label.text 30 | property alias input: _textField.text 31 | property alias fontWeight: _textField.font.weight 32 | property alias implicitTextFieldWidth: _textField.implicitWidth 33 | 34 | signal textChanged(string text) 35 | 36 | Label { 37 | id: _label 38 | 39 | horizontalAlignment: Text.AlignRight 40 | wrapMode: Text.Wrap 41 | Layout.preferredWidth: root.prefWidth / 2 42 | } 43 | 44 | TextField { 45 | id: _textField 46 | 47 | focus: true 48 | selectByMouse: true 49 | bottomPadding: topPadding 50 | horizontalAlignment: Text.AlignLeft 51 | 52 | onTextChanged: { 53 | root.textChanged(text); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /qml/shared/qmldir: -------------------------------------------------------------------------------- 1 | module shared 2 | 3 | MpvqcDebugRectangle MpvqcDebugRectangle.qml 4 | MpvqcDialog MpvqcDialog.qml 5 | MpvqcHeader MpvqcHeader.qml 6 | MpvqcMenu MpvqcMenu.qml 7 | MpvqcMessageBox MpvqcMessageBox.qml 8 | MpvqcNewCommentMenu MpvqcNewCommentMenu.qml 9 | MpvqcSpinBoxRow MpvqcSpinBoxRow.qml 10 | MpvqcSwitchRow MpvqcSwitchRow.qml 11 | MpvqcTextFieldRow MpvqcTextFieldRow.qml 12 | -------------------------------------------------------------------------------- /qml/shared/tst_MpvqcMenu.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | import QtTest 23 | 24 | Item { 25 | 26 | MpvqcMenu { 27 | id: objectUnderTest1 28 | 29 | Action { 30 | text: "Short text" 31 | } 32 | } 33 | 34 | MpvqcMenu { 35 | id: objectUnderTest2 36 | 37 | Action { 38 | text: "Short text" 39 | } 40 | MenuSeparator {} 41 | } 42 | 43 | MpvqcMenu { 44 | id: objectUnderTest3 45 | 46 | Action { 47 | text: "Very very long text so that we can compare" 48 | } 49 | } 50 | 51 | TestCase { 52 | name: "MpvqcMenu" 53 | 54 | function test_width_data() { 55 | return [ 56 | { 57 | tag: "same", 58 | equals: "equals", 59 | obj1: objectUnderTest1, 60 | obj2: objectUnderTest2 61 | }, 62 | { 63 | tag: "different", 64 | obj1: objectUnderTest1, 65 | obj2: objectUnderTest3 66 | }, 67 | ]; 68 | } 69 | 70 | function test_width(data) { 71 | if (data.equals) { 72 | verify(data.obj1.width === data.obj2.width); 73 | } else { 74 | verify(data.obj1.width < data.obj2.width); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /qml/shared/tst_MpvqcSpinBoxRow.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | Item { 24 | id: testHelper 25 | 26 | width: 400 27 | height: 400 28 | 29 | MpvqcSpinBoxRow { 30 | id: objectUnderTest 31 | 32 | prefWidth: testHelper.width 33 | valueFrom: 15 34 | value: 30 35 | valueTo: 45 36 | 37 | property int newValue: -1 38 | 39 | onValueModified: value => { 40 | objectUnderTest.newValue = value; 41 | } 42 | 43 | TestCase { 44 | name: "MpvqcSpinBoxRow" 45 | when: windowShown 46 | 47 | SignalSpy { 48 | id: valueModifiedSpy 49 | target: objectUnderTest 50 | signalName: "valueModified" 51 | } 52 | 53 | function init() { 54 | valueModifiedSpy.clear(); 55 | objectUnderTest.newValue = -1; 56 | } 57 | 58 | function test_spinBox_data() { 59 | return [ 60 | { 61 | tag: "increase", 62 | value: 31, 63 | exec: () => { 64 | objectUnderTest.spinBox.increase(); 65 | } 66 | }, 67 | ]; 68 | } 69 | 70 | function test_spinBox(data) { 71 | data.exec(); 72 | compare(objectUnderTest.newValue, data.value); 73 | compare(valueModifiedSpy.count, 1); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /qml/shared/tst_MpvqcSwitchRow.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | Item { 24 | id: testHelper 25 | 26 | width: 400 27 | height: 400 28 | 29 | MpvqcSwitchRow { 30 | id: objectUnderTest 31 | 32 | property bool newChecked: false 33 | 34 | prefWidth: testHelper.width 35 | 36 | onCheckedChanged: { 37 | objectUnderTest.newChecked = checked; 38 | } 39 | 40 | TestCase { 41 | name: "MpvqcSwitchRow" 42 | when: windowShown 43 | 44 | SignalSpy { 45 | id: toggledSpy 46 | target: objectUnderTest 47 | signalName: "toggled" 48 | } 49 | 50 | function init() { 51 | objectUnderTest.newChecked = false; 52 | toggledSpy.clear(); 53 | } 54 | 55 | function test_toggle() { 56 | verify(!objectUnderTest.checked); 57 | 58 | mouseClick(objectUnderTest.toggle); 59 | compare(toggledSpy.count, 1); 60 | verify(objectUnderTest.checked); 61 | 62 | mouseClick(objectUnderTest.toggle); 63 | compare(toggledSpy.count, 2); 64 | verify(!objectUnderTest.checked); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /qml/shared/tst_MpvqcTextFieldRow.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtTest 22 | 23 | Item { 24 | id: testHelper 25 | 26 | width: 400 27 | height: 400 28 | 29 | MpvqcTextFieldRow { 30 | id: objectUnderTest 31 | 32 | property string newText: '' 33 | 34 | prefWidth: testHelper.width 35 | 36 | onTextChanged: text => { 37 | objectUnderTest.newText = text; 38 | } 39 | 40 | TestCase { 41 | name: "MpvqcTextFieldRow" 42 | 43 | SignalSpy { 44 | id: textChangedSpy 45 | target: objectUnderTest 46 | signalName: "textChanged" 47 | } 48 | 49 | function init() { 50 | objectUnderTest.newText = ''; 51 | textChangedSpy.clear(); 52 | } 53 | 54 | function test_text() { 55 | const firstText = "abc"; 56 | const secondText = "def"; 57 | 58 | objectUnderTest.input = firstText; 59 | verify(objectUnderTest.newText, firstText); 60 | compare(textChangedSpy.count, 1); 61 | 62 | objectUnderTest.input = secondText; 63 | verify(objectUnderTest.newText, secondText); 64 | compare(textChangedSpy.count, 2); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /qml/table/MpvqcCommentHighlighter.js: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/tc39/proposal-regex-escaping 3 | */ 4 | 5 | /** 6 | * 7 | * @param comment {string} 8 | * @param highlightedText {string} 9 | * @returns {string} 10 | */ 11 | function highlightComment(comment, highlightedText) { 12 | const re = new RegExp(RegExp.escape(highlightedText), "gi") 13 | return comment.replace(re, "$&") 14 | } 15 | 16 | // this is a direct translation to code of the spec 17 | if (!RegExp.escape) { 18 | RegExp.escape = S => { 19 | // 1. let str be ToString(S). 20 | // 2. ReturnIfAbrupt(str). 21 | let str = String(S) 22 | // 3. Let cpList be a List containing in order the code 23 | // points as defined in 6.1.4 of str, starting at the first element of str. 24 | let cpList = Array.from(str[Symbol.iterator]()) 25 | // 4. let cuList be a new List 26 | let cuList = [] 27 | // 5. For each code point c in cpList in List order, do: 28 | for (let c of cpList) { 29 | // i. If c is a SyntaxCharacter then do: 30 | if ("^$\\.*+?()[]{}|".indexOf(c) !== -1) { 31 | // a. Append "\" to cuList. 32 | cuList.push("\\") 33 | } 34 | // Append c to cpList. 35 | cuList.push(c) 36 | } 37 | // 7. Return a String whose elements are, in order, the elements of cuList. 38 | return cuList.join("") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /qml/table/MpvqcContextMenu.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 21 | import QtQuick.Controls.Material 22 | 23 | import "../shared" 24 | 25 | MpvqcMenu { 26 | id: root 27 | 28 | required property int currentListIndex 29 | required property point openedAt 30 | 31 | signal copyCommentClicked(index: int) 32 | signal deleteCommentClicked(index: int) 33 | signal editCommentClicked(index: int) 34 | 35 | modal: true 36 | 37 | x: root.mirrored ? openedAt.x - width : openedAt.x 38 | y: openedAt.y 39 | 40 | MenuItem { 41 | id: _editItem 42 | 43 | //: Context menu on right click in comments table 44 | text: qsTranslate("CommentTable", "Edit Comment") 45 | icon.source: "qrc:/data/icons/edit_black_24dp.svg" 46 | 47 | onTriggered: { 48 | root.exit = null; 49 | root.editCommentClicked(root.currentListIndex); 50 | } 51 | } 52 | 53 | MenuItem { 54 | id: _copyItem 55 | 56 | //: Context menu on right click in comments table 57 | text: qsTranslate("CommentTable", "Copy Comment") 58 | icon.source: "qrc:/data/icons/content_copy_black_24dp.svg" 59 | 60 | onTriggered: root.copyCommentClicked(root.currentListIndex) 61 | } 62 | 63 | MenuItem { 64 | id: _deleteItem 65 | 66 | //: Context menu on right click in comments table 67 | text: qsTranslate("CommentTable", "Delete Comment") 68 | icon.source: "qrc:/data/icons/delete_black_24dp.svg" 69 | 70 | onTriggered: root.deleteCommentClicked(root.currentListIndex) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /qml/table/MpvqcDeleteCommentMessageBox.qml: -------------------------------------------------------------------------------- 1 | /* 2 | mpvQC 3 | 4 | Copyright (C) 2022 mpvQC developers 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick.Controls.Material 21 | 22 | import "../shared" 23 | 24 | MpvqcMessageBox { 25 | id: root 26 | 27 | required property int commentIndex 28 | 29 | signal deleteCommentConfirmed(index: int) 30 | 31 | title: qsTranslate("MessageBoxes", "Delete Comment") 32 | text: qsTranslate("MessageBoxes", "Do you really want to delete this comment?") 33 | standardButtons: Dialog.Yes | Dialog.Cancel 34 | 35 | onAccepted: { 36 | root.deleteCommentConfirmed(root.commentIndex); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /qml/table/qmldir: -------------------------------------------------------------------------------- 1 | module table 2 | 3 | MpvqcTable MpvqcTable.qml 4 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | def add_repository_root_to_path(): 20 | import os 21 | from pathlib import Path 22 | 23 | os.environ["PATH"] = str(Path(__file__).parent.parent.absolute()) + os.pathsep + os.environ["PATH"] 24 | 25 | 26 | add_repository_root_to_path() 27 | -------------------------------------------------------------------------------- /test/conftest.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2025 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from collections.abc import Generator 19 | from importlib.util import find_spec 20 | from typing import Any 21 | 22 | import pytest 23 | 24 | from mpvqc.application import MpvqcApplication 25 | from mpvqc.services import TypeMapperService 26 | 27 | 28 | @pytest.fixture(scope="session", autouse=True) 29 | def check_generated_resources(): 30 | if find_spec("test.generated_resources") is None: 31 | message = ( 32 | "Can not find resource module 'test.generated_resources'\n" 33 | "To execute individual tests, please run 'just test-python' once before" 34 | ) 35 | raise FileNotFoundError(message) 36 | import test.generated_resources # noqa: F401 37 | 38 | 39 | @pytest.fixture(scope="session") 40 | def type_mapper() -> TypeMapperService: 41 | return TypeMapperService() 42 | 43 | 44 | @pytest.fixture(scope="session") 45 | def qt_app() -> Generator[MpvqcApplication, Any]: 46 | app = MpvqcApplication([]) 47 | yield app 48 | app.shutdown() 49 | -------------------------------------------------------------------------------- /test/mocks.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from PySide6.QtCore import QObject, QUrl, Signal 19 | 20 | 21 | class MockedMessageBox(QObject): 22 | accepted = Signal() 23 | rejected = Signal() 24 | closed = Signal() 25 | 26 | def open(self): 27 | pass 28 | 29 | 30 | class MockedDialog(QObject): 31 | accepted = Signal() 32 | rejected = Signal() 33 | savePressed = Signal(QUrl) 34 | 35 | def __init__(self): 36 | super().__init__() 37 | self.openCalled = False 38 | 39 | def open(self): 40 | self.openCalled = True 41 | -------------------------------------------------------------------------------- /test/pyobjects/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /test/pyobjects/comment_model/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /test/pyobjects/comment_model/test_clipboard.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | from PySide6.QtGui import QClipboard 20 | 21 | from mpvqc.models import Comment 22 | 23 | 24 | @pytest.fixture(scope="session") 25 | def clipboard(qt_app) -> QClipboard: 26 | return qt_app.clipboard() 27 | 28 | 29 | def test_copy_to_clipboard(make_model, clipboard): 30 | # noinspection PyArgumentList 31 | model, _ = make_model( 32 | set_comments=[ 33 | Comment(time=100, comment_type="Phrasing", comment="Comment Content 1"), 34 | Comment(time=200, comment_type="Translation", comment="Comment Content 2"), 35 | Comment(time=300, comment_type="Spelling", comment="Comment Content 3"), 36 | ], 37 | set_player_time=0, 38 | ) 39 | 40 | model.copy_to_clipboard(0) 41 | assert clipboard.text() == "[00:01:40] [Phrasing] Comment Content 1" 42 | 43 | model.copy_to_clipboard(1) 44 | assert clipboard.text() == "[00:03:20] [Translation] Comment Content 2" 45 | 46 | model.copy_to_clipboard(2) 47 | assert clipboard.text() == "[00:05:00] [Spelling] Comment Content 3" 48 | -------------------------------------------------------------------------------- /test/pyobjects/comment_model/test_get_all.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | 20 | from mpvqc.models import Comment 21 | 22 | DEFAULT_COMMENTS = [ 23 | Comment(time=0, comment_type="commentType", comment="Word 1"), 24 | Comment(time=5, comment_type="commentType", comment="Word 2"), 25 | Comment(time=10, comment_type="commentType", comment="Word 3"), 26 | Comment(time=15, comment_type="commentType", comment="Word 4"), 27 | Comment(time=20, comment_type="commentType", comment="Word 5"), 28 | ] 29 | 30 | 31 | @pytest.fixture 32 | def model(make_model): 33 | # noinspection PyArgumentList 34 | model, _ = make_model( 35 | set_comments=DEFAULT_COMMENTS, 36 | set_player_time=0, 37 | ) 38 | return model 39 | 40 | 41 | def test_get_all_comments(model): 42 | actual = [ 43 | Comment(time=comment["time"], comment_type=comment["commentType"], comment=comment["comment"]) 44 | for comment in model.comments() 45 | ] 46 | 47 | assert actual == DEFAULT_COMMENTS 48 | -------------------------------------------------------------------------------- /test/pyobjects/manager/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /test/pyobjects/test_export_templates_model.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from pathlib import Path 19 | from unittest.mock import MagicMock 20 | 21 | import inject 22 | 23 | from mpvqc.pyobjects.export_template_model import MpvqcExportTemplateModelPyObject, Role 24 | from mpvqc.services import ApplicationPathsService 25 | 26 | 27 | def make_model(mocked_paths: tuple[Path, ...]) -> MpvqcExportTemplateModelPyObject: 28 | mock = MagicMock() 29 | mock.files_export_templates = mocked_paths 30 | 31 | def config(binder: inject.Binder): 32 | binder.bind(ApplicationPathsService, mock) 33 | 34 | inject.configure(config, clear=True) 35 | # noinspection PyCallingNonCallable 36 | return MpvqcExportTemplateModelPyObject() 37 | 38 | 39 | def test_no_templates(): 40 | model = make_model(mocked_paths=()) 41 | assert model.rowCount() == 0 42 | 43 | 44 | def test_templates(): 45 | model = make_model(mocked_paths=(Path.home(), Path.cwd())) 46 | assert model.rowCount() == 2 47 | 48 | 49 | def test_templates_sorted(): 50 | model = make_model( 51 | mocked_paths=( 52 | Path("/xy"), 53 | Path("/z"), 54 | Path("/a"), 55 | Path("/b"), 56 | ) 57 | ) 58 | 59 | expected = ["a", "b", "xy", "z"] 60 | actual = [ 61 | model.item(0, 0).data(Role.NAME), 62 | model.item(1, 0).data(Role.NAME), 63 | model.item(2, 0).data(Role.NAME), 64 | model.item(3, 0).data(Role.NAME), 65 | ] 66 | assert actual == expected 67 | -------------------------------------------------------------------------------- /test/services/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /test/services/test_font_loader.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from PySide6.QtCore import QFile 19 | from PySide6.QtGui import QFontDatabase 20 | 21 | from mpvqc.services import FontLoaderService 22 | 23 | 24 | def test_fonts_present_in_resources(qt_app): 25 | variants = [ 26 | "NotoSans-Regular.ttf", 27 | "NotoSans-Italic.ttf", 28 | "NotoSans-Bold.ttf", 29 | "NotoSans-SemiBold.ttf", 30 | "NotoSansHebrew-Bold.ttf", 31 | "NotoSansHebrew-Regular.ttf", 32 | "NotoSansHebrew-SemiBold.ttf", 33 | "NotoSansMono-Regular.ttf", 34 | ] 35 | for variant in variants: 36 | file = QFile(f":/data/fonts/{variant}") 37 | assert file.exists(), f"Expected to find {variant} in resources but couldn't" 38 | 39 | 40 | def test_fonts_loaded(qt_app): 41 | # It's not possible to clear Qt's entire font database. Additionally, font backends on different OS's behave 42 | # differently. Therefore, we just test for the common font families. 43 | verifiable_font_families = [ 44 | "Noto Sans", 45 | "Noto Sans Hebrew", 46 | "Noto Sans Mono", 47 | ] 48 | 49 | FontLoaderService().load_application_fonts() 50 | loaded_font_families = QFontDatabase.families() 51 | 52 | for font_family in verifiable_font_families: 53 | assert font_family in loaded_font_families, ( 54 | f"Cannot find font family '{font_family}' in loaded font families {loaded_font_families}" 55 | ) 56 | -------------------------------------------------------------------------------- /test/services/test_formatter_time.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | 20 | from mpvqc.services import TimeFormatterService 21 | 22 | 23 | @pytest.fixture(scope="module") 24 | def service(): 25 | return TimeFormatterService() 26 | 27 | 28 | @pytest.mark.parametrize( 29 | ("expected", "input_seconds"), 30 | [ 31 | ("00:00:00", 0), 32 | ("00:01:08", 68), 33 | ("00:16:39", 999), 34 | ("02:46:40", 10000), 35 | ], 36 | ) 37 | def test_format_time_to_string_long(service, expected, input_seconds): 38 | actual = service.format_time_to_string(input_seconds, long_format=True) 39 | assert expected == actual 40 | 41 | 42 | @pytest.mark.parametrize( 43 | ("expected", "input_seconds"), 44 | [ 45 | ("00:00", 0), 46 | ("01:08", 68), 47 | ("16:39", 999), 48 | ], 49 | ) 50 | def test_format_time_to_string_short(service, expected, input_seconds): 51 | actual = service.format_time_to_string(input_seconds, long_format=False) 52 | assert expected == actual 53 | -------------------------------------------------------------------------------- /test/services/test_resource_reader.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from unittest.mock import patch 19 | 20 | import pytest 21 | 22 | from mpvqc.services import ResourceReaderService 23 | 24 | 25 | @pytest.fixture(scope="module") 26 | def service() -> ResourceReaderService: 27 | return ResourceReaderService() 28 | 29 | 30 | @pytest.mark.parametrize( 31 | "file_path", 32 | [ 33 | ":/data/icon.svg", 34 | "/data/icon.svg", 35 | "data/icon.svg", 36 | ], 37 | ) 38 | def test_read_from(service, file_path): 39 | assert service.read_from(file_path).startswith(">") 45 | 46 | module = "mpvqc.services.resource_reader" 47 | 48 | with ( 49 | patch(f"{module}.QFile.exists", return_value=True), 50 | patch(f"{module}.QFile.open", return_value=False), 51 | pytest.raises(ValueError), # noqa: PT011 52 | ): 53 | service.read_from("") 54 | -------------------------------------------------------------------------------- /test/services/test_reverse_translator.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2022 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | 20 | from mpvqc.services import ReverseTranslatorService 21 | 22 | 23 | @pytest.fixture 24 | def service() -> ReverseTranslatorService: 25 | return ReverseTranslatorService() 26 | 27 | 28 | @pytest.mark.parametrize( 29 | ("expected", "translated"), 30 | [ 31 | ("Spelling", "Spelling"), 32 | ("Spelling", "Rechtschreibung"), 33 | ("Spelling", "איות"), 34 | ("Spelling", "Typo"), 35 | ("Spelling", "Ortografía"), 36 | ("not-found", "not-found"), 37 | ], 38 | ) 39 | def test_lookup2(service, expected, translated): 40 | assert expected == service.lookup(translated) 41 | -------------------------------------------------------------------------------- /test/services/theme/__init__.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2025 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /test/services/theme/test_utils.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | from PySide6.QtGui import QColor 20 | 21 | from mpvqc.services.theme.schema import ThemeParseError 22 | from mpvqc.services.theme.utils import parse_color 23 | 24 | 25 | @pytest.mark.parametrize( 26 | ("string", "expected_hex"), 27 | [ 28 | ("#2e3440", "#2e3440"), 29 | ("Qt.darker #bf616a 1.3", "#934b52"), 30 | ("Qt.darker #bf616a 1.5", "#7f4147"), 31 | ("qt.Lighter #bf6 1.5", "#f4ffe5"), 32 | ("qt.lighter #bf616a 1.3", "#f87e8a"), 33 | ("Qt.lighter #bf616a 1.5", "#ffa1aa"), 34 | ], 35 | ) 36 | def test_parse_color(string: str, expected_hex: str) -> None: 37 | actual = parse_color(string).name(QColor.NameFormat.HexRgb) 38 | assert expected_hex == actual 39 | 40 | 41 | @pytest.mark.parametrize( 42 | "string", 43 | [ 44 | "bf616a", 45 | "Qt.darker bf616a 1.3", 46 | "Qt.lighter #bf616a", 47 | ], 48 | ) 49 | def test_parse_color_errors(string: str) -> None: 50 | with pytest.raises(ThemeParseError): 51 | parse_color(string) 52 | -------------------------------------------------------------------------------- /test/test_application.py: -------------------------------------------------------------------------------- 1 | # mpvQC 2 | # 3 | # Copyright (C) 2024 mpvQC developers 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | from PySide6.QtCore import QObject, QUrl 20 | 21 | QML = """ 22 | import QtQuick 23 | import QtQuick.Controls 24 | 25 | ApplicationWindow { 26 | visible: false; width: 50; height: 50 27 | 28 | Button { objectName: "button-click-me"; text: "Click Me" } 29 | } 30 | """ 31 | 32 | 33 | def test_find_object(qt_app): 34 | qt_app._engine.loadData(QML.encode(), QUrl()) 35 | obj = qt_app.find_object(QObject, "button-click-me") 36 | assert obj 37 | 38 | with pytest.raises(ValueError): # noqa: PT011 39 | qt_app.find_object(QObject, "other-button-that-does-not-exist") 40 | --------------------------------------------------------------------------------