# Inside GridPlayer: a PyQt5 grid over VLC, streamlink and yt-dlp

> GridPlayer wraps VLC in a PyQt5 window that lays many videos out as a grid and saves position, volume, loops and aspect ratio per video into a playlist. Its install routes differ more than the feature list does, since a Python install leaves VLC to the system while the macOS disk image is Apple Silicon only.

**vzhd1701/gridplayer** — Play videos side-by-side

- Repository: https://github.com/vzhd1701/gridplayer
- Stars: 2,126 · Forks: 192
- Language: Python
- License: GPL-3.0
- Published: 2026-10-14 · Updated: 2026-10-14 · Language: en
- Canonical page: https://hysenlabs.com/projects/vzhd1701-gridplayer

## One PyQt5 window over VLC, arranged as a grid

GridPlayer exists for one situation: watching more than one video at once while keeping separate control of each one. The README calls it a "Simple VLC-based media player that can play multiple videos at the same time", and the count of simultaneous videos is limited only by your hardware. That premise dictates the design. Each cell of the grid holds one video, and the controls that matter for a single-file player apply per cell: aspect ratio, playback speed, zoom, and loop fragments cut with frame precision. Videos move between cells by drag and drop rather than by reopening a file.

The Python package registers one console script named `gridplayer`, bound to `gridplayer.__main__:main`, so every action the window offers also has a command line front door. State outlives the window too. A saved playlist retains the position, sound volume, loops and aspect ratio of each video, which means closing and reopening the grid does not reset a dozen players to zero.

That per-video memory is the part worth judging the project on. Any grid of players can show six videos at once; keeping six independent playheads, zoom levels and loop fragments is the work that stops being trivial the moment the session lasts longer than an evening.

## Four install routes, four different prerequisites

Choosing an install route is the first real decision, because each one hands you a different set of prerequisites. On Windows you can take the installer or the portable archive, or let Scoop do it:

```shell
scoop install gridplayer
```

The README states the Windows build is compatible with Windows 7, 8, 10 and 11. Linux users have a Flathub app, a Snap store package and an AppImage, and the README prefers Flathub for better system integration. The AppImage was built using Ubuntu Focal Fossa libraries, so compatibility is Ubuntu 20 or newer, and the file arrives without an execute bit:

```shell
chmod +x GridPlayer-0.5.5-x86_64.AppImage
```

macOS has exactly one artifact, a DMG image that is not signed and targets Apple Silicon. If Gatekeeper refuses it with "GridPlayer is damaged and can't be opened", the README gives this fix:

```shell
sudo xattr -rd com.apple.quarantine /Applications/GridPlayer.app
```

The fourth route runs from Python and is the one a developer reaches for. It needs Python 3.10 or later:

```shell
uv tool install gridplayer
```

Expect this last one to fail on a machine with no VLC, because the Python route does not bundle one. The same caution applies to the source route.

## The command line opens files, playlists and stream URLs

The console script takes a bare path list, which makes GridPlayer usable as a target from a file manager or a shell loop:

```shell
gridplayer [options] [FILE|URL ...]
```

Anything that is not an option is treated as a media file, a `.gpls` playlist, or a streaming URL such as `https://` or `rtsp://`, and all of it opens on startup. Unrecognised options are rejected with an error rather than quietly ignored, and a filename that begins with a dash has to be preceded by a double dash. Two options exist purely for information. `--version` prints the application version and exits, while `--help` lists the rest.

The third one does real work. `--user-data-dir PATH` stores settings and log files in the directory you name instead of the default location, with relative paths resolved against the current working directory.

Where that default lives is only half documented. Settings and logs normally go to the system application data directory, and on the Windows portable build a `portable_data` folder sitting next to the executable takes over automatically. Beyond those two rules the README does not spell out a path for each operating system, so a script that wants to find or back up the settings directory has to look.

## Streaming goes through streamlink and yt-dlp, not VLC alone

Coverage of streaming sites is credited to two front ends rather than to VLC, and that choice decides what actually plays. The declared dependencies pin `streamlink` in the range 8.4.0 to 9.0.0 and `yt-dlp` with the `curl-cffi` extra in the range 2026.07.04 to 2027.0.0. Two consequences follow.

Site support ages at the pace of those tools, because a platform that changes its web player breaks an extractor rather than the media shell wrapped around it. And the yt-dlp window has a closed end: once the year reaches 2027, nothing inside the declared range matches, so the constraint has to be moved by hand before an install will resolve.

Two capabilities sit on top of that resolution layer. Cookies can be supplied for streams that need a login, which is what turns GridPlayer from a local-file tool into one that can follow a stream sitting behind a signed-in session. SponsorBlock support skips marked sections of YouTube videos, cutting out segments nobody asked to sit through. Neither is documented in terms of where a cookie file goes or which SponsorBlock categories apply, so both read as things to try rather than things to configure precisely.

## The Intel Mac build exists in the justfile but not in the README

Reading the build recipes turns up a gap between what the project can produce and what it offers. The justfile defines a macOS PyInstaller target that defaults to Intel silicon:

```shell
BUILD_MACOS_ARCH=x86_64 ./scripts/pyinstaller/build_mac.sh
```

and a second target that passes the architecture explicitly:

```shell
BUILD_MACOS_ARCH=arm64 ./scripts/pyinstaller/build_mac.sh
```

Each one is followed by a packaging step that produces a DMG, so the machinery for an `x86_64` disk image sits in the recipe. What the README links is only `GridPlayer.0.5.5_arm64.dmg`, and it says outright that the DMG image is not signed and targets Apple Silicon. An Intel Mac user therefore has a build path and no artifact to download.

The naming makes the split visible too. Windows and Linux artifacts join the version with a dash, as in `GridPlayer-0.5.5-x86_64.AppImage`, while the macOS artifact separates the same parts with dots. The same README is otherwise careful about old systems, claiming Windows compatibility back to Windows 7, so the omission is specific to macOS rather than a general policy about aging hardware.

## The justfile is the entire packaging pipeline

Packaging runs through a justfile rather than through continuous integration alone, and reading it shows what a maintainer does before an artifact exists. The Linux chain compiles a locked requirements file, builds a wheel, runs a platform metadata script, then hands off to an AppImage, Snap or Flatpak script. The compile step is this one:

```shell
uv pip compile pyproject.toml -q --universal --no-annotate --no-header -o build/requirements.txt
```

Flatpak takes the long path, requiring that compiled requirements file, an sdist and the metadata step before `./scripts/flatpak/build.sh` runs. AppImage skips it because it consumes the wheel directly. Windows goes through PyInstaller and a separate packaging script, with Chocolatey built from a third. Interface files are generated rather than hand-edited, by `./scripts/qt_resources/build_ui.sh`.

The last push was on 2026-10-03, and the tree carries `uv.lock` beside `.pre-commit-config.yaml` and an `AGENTS.md`, so dependencies are pinned in two places and checked before commit. A reader who only wants to watch video needs none of this. A reader who wants to rebuild needs a working uv, a just runner and the target platform's own packaging tools.

## A grid of embedded panes against separate player windows

The honest alternative is to open each video in its own player window and arrange them with the window manager, which costs nothing and gives you one video per window. The difference is not the picture. It is what survives a restart. GridPlayer writes per-video state into a playlist and into the user data directory, and the README names exactly what is kept: position, sound volume, loops and aspect ratio. A window manager remembers window geometry, and nothing about which video sat at which volume.

That state has a second consequence, which is the one to check before moving. It is kept in GridPlayer's own formats, a `.gpls` playlist plus the settings directory, so it does not travel to a different player and a different player cannot write it. There is no documented interchange format beyond those two.

If your work is one long session of streaming URLs, the grid buys simultaneous playback and little else, and a single window driven by the same stream tools may serve you better. If your work is four or six clips you keep scrubbing, comparing and replaying, the retained position and frame precision loops are the reason to stay.

## Where the grid is the wrong shape for the job

Several boundaries are worth naming before anyone adopts it. The first is arithmetic. Every cell needs its own decode, and no number is given anywhere, only the statement that your hardware is the limit. Hardware and software decoding are both settings, so on a machine where hardware decoding is unavailable you pay for a full software pipeline per cell.

The second is the interface. Everything documented is a desktop window. There is no headless mode, no library entry point, and no subcommand beyond opening files and moving the data directory. A script that wants frames rather than a window gets nothing here, and the drag-and-drop grid is built for a person at a desk rather than for a pipeline.

Terms matter for anyone planning to redistribute rather than install. The project is published under GPL-3.0-or-later, so a modified binary you hand to someone else carries source and notice obligations that an unmodified personal install does not; read the terms at the root of the source tree rather than treating that summary as advice. Check the codec path on the machine that matters too, since VLC covers the formats while hardware decoding depends on the graphics stack underneath it.

## Conclusion

GridPlayer earns its place when the job is watching several videos at once and finding each one where you left it, because the per-video playlist state is what a row of separate windows cannot give you. It is the wrong shape for scripting, for headless work or for a single stream. Verify first that a VLC install or vlc package exists on the machine if you install with uv, that hardware decoding works on the graphics stack underneath, and that your platform has an artifact you can actually download.

## FAQ

### Does GridPlayer need VLC installed on my system?

Only for the Python install route. The README states that installing with uv also requires VLC installed on Windows and Mac, or a vlc package present on Linux. Some distros, Ubuntu for example, might also require `libxcb-xinerama0`.

### Which platforms does GridPlayer ship a build for?

Windows has an installer and a portable archive, Linux has a Flathub app, a Snap store package and an AppImage, and macOS has a single DMG. The AppImage was built using Ubuntu Focal Fossa libraries, so compatibility is Ubuntu 20 or newer, while the Windows build is stated to work on Windows 7, 8, 10 and 11.

### Can GridPlayer play streaming URLs, and which tools handle them?

Yes, URLs over `https://` and `rtsp://` are accepted on the command line alongside local files. Coverage beyond that comes from streamlink and yt-dlp, which the package depends on in pinned version ranges. Cookies can be supplied for streams that need a login.

### Why does macOS report the GridPlayer app as damaged on first launch?

The DMG image is not signed and targets Apple Silicon, so Gatekeeper refuses it until the quarantine attribute is cleared. The README points at both an exception in System Settings and a command that strips the attribute from `/Applications/GridPlayer.app`.

### Does GridPlayer keep my per-video settings between sessions?

A saved playlist retains the position, sound volume, loops and aspect ratio of each video. Settings and logs otherwise live in the system application data directory, or wherever `--user-data-dir PATH` points, and on the Windows portable build a `portable_data` folder beside the executable is used automatically.

## Sources

- [Issues](https://github.com/vzhd1701/gridplayer/issues)
- [License: GPL-3.0](https://github.com/vzhd1701/gridplayer/blob/master/LICENSE)
- [README](https://github.com/vzhd1701/gridplayer/blob/master/README.md)
- [Releases](https://github.com/vzhd1701/gridplayer/releases)
- [vzhd1701/gridplayer on GitHub](https://github.com/vzhd1701/gridplayer)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/vzhd1701-gridplayer
