Metadata-Version: 2.4
Name: coomplexer
Version: 1.10.0
Summary: One box office for all your mpv screens: grid walls, subreddit scraping, side-by-side stacking.
Author: kpg-anon
License-Expression: MIT
Project-URL: Homepage, https://github.com/kpg-anon/coomplexer
Project-URL: Repository, https://github.com/kpg-anon/coomplexer
Project-URL: Documentation, https://github.com/kpg-anon/coomplexer/blob/main/USAGE.md
Project-URL: Changelog, https://github.com/kpg-anon/coomplexer/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/kpg-anon/coomplexer/issues
Keywords: mpv,video-wall,videowall,multi-monitor,media-player,video-player,playlist,m3u,yt-dlp,scraper,cli
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Video :: Display
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: screeninfo>=0.8
Provides-Extra: probe
Requires-Dist: pillow; extra == "probe"
Requires-Dist: av; extra == "probe"
Provides-Extra: net
Requires-Dist: curl_cffi; extra == "net"
Provides-Extra: tui
Requires-Dist: prompt_toolkit; extra == "tui"
Provides-Extra: all
Requires-Dist: pillow; extra == "all"
Requires-Dist: av; extra == "all"
Requires-Dist: curl_cffi; extra == "all"
Requires-Dist: prompt_toolkit; extra == "all"
Dynamic: license-file

<div align="center">

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/.github/assets/hero.svg" alt="coomplexer — one box office for all your mpv screens" width="880">

<p>
  <a href="https://pypi.org/project/coomplexer/"><img src="https://img.shields.io/pypi/v/coomplexer?logo=pypi&logoColor=white&label=PyPI&color=8b5cf6" alt="PyPI"></a>
  <a href="https://github.com/kpg-anon/coomplexer/blob/main/CHANGELOG.md"><img src="https://img.shields.io/badge/release-v1.10.0-8b5cf6" alt="Release v1.10.0"></a>
  <img src="https://img.shields.io/badge/python-3.11%2B-3776AB?logo=python&logoColor=white" alt="Python 3.11+">
  <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license">
  <img src="https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-6f42c1" alt="Platforms">
</p>

<p><b>Turn any folder, playlist, or subreddit into a wall of synchronized <a href="https://mpv.io/">mpv</a> players — with one command.</b></p>

</div>

---

## What it is

`coomplexer` splits your monitors into a grid and opens one independent mpv player per tile, dealing your media across them. It also scrapes video sites into playlists, keeps a searchable index of everything it has seen, and lets you drive a running wall from the terminal.

```console
coomplexer grid ~/videos          # a 3x1 wall of everything in a folder
coomplexer ctl pause              # ...pause all of it
coomplexer stop                   # ...and close it
```

https://github.com/user-attachments/assets/e5b7ca7e-7773-4f3a-a2d5-d2eb27a61e32

> [!NOTE]
> Everything below the **Basics** section is optional. You can use this tool forever with three commands and never touch a config file.

## Install

```console
pipx install coomplexer     # recommended: isolated, and puts `coomplexer` on your PATH
```

<sub>No pipx? <code>pip install coomplexer</code> works too.</sub>

Then check your environment — this tells you exactly what is missing and what each missing piece would have done:

```console
coomplexer doctor
```

You need [**mpv**](https://mpv.io/) on your `PATH`. That is the only hard requirement. Everything else is optional: [yt-dlp](https://github.com/yt-dlp/yt-dlp) for URLs and scraping, and `ffprobe` or `pillow`/`av` for faster media probing.

<details>
<summary><b>From source</b> — clone and install</summary>

```console
git clone https://github.com/kpg-anon/coomplexer.git
cd coomplexer
pipx install .            # or: pip install .
```

For a live checkout where edits take effect immediately:

```console
pip install -e .
```

</details>

<details>
<summary><b>Without installing</b> — run straight from a clone</summary>

```console
git clone https://github.com/kpg-anon/coomplexer.git
cd coomplexer
pip install -r requirements.txt
python -m coomplexer --help
```

Every command works the same way, `python -m coomplexer` standing in for `coomplexer`.

</details>

<details>
<summary><b>Optional extras</b> — faster probing, better scraping</summary>

```console
pip install "coomplexer[probe]"   # pillow + av: faster orientation probing for -p/-L
pip install "coomplexer[net]"     # curl_cffi: browser-TLS fetching for the scraper
pip install "coomplexer[tui]"     # prompt_toolkit: tab completion in the playlists picker
pip install "coomplexer[all]"     # everything
```

None of these are required — coomplexer falls back to `ffprobe` for probing and to stdlib HTTP for scraping. `coomplexer doctor` reports which are present.

</details>

Requires Python 3.11+. Windows, Linux and macOS; the pixel-exact window pinning and taskbar handling are Windows-specific, elsewhere placement is left to your window manager.

---

# Basics

Four commands cover almost everything.

### 1. Build a wall

```console
coomplexer grid ~/videos                  # 3x1 wall (the default)
coomplexer grid ~/videos -g 2x2           # 2 across, 2 down
coomplexer grid ~/videos -g 3x2 -v 30     # 6 tiles at 30% volume
coomplexer grid ~/pics -t images -d 5     # image slideshow, 5s per picture
```

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/screenshots/wall.png" alt="A 3x1 coomplexer wall: three mpv tiles playing side by side">

Point it at anything: folders, individual files, `.m3u` playlists, URLs, or a mix. Files are shuffled and dealt round-robin, so no two tiles play the same thing.

| Flag | Does |
|------|------|
| `-g 3x1` | layout, as `COLSxROWS` |
| `-s 0.5` | playback speed |
| `-v 30` | volume (0–100, default muted) |
| `-t videos` | only videos (or `images`, `mixed`) |
| `-r` | search folders recursively |
| `-p` / `-L` | portrait-only / landscape-only |
| `-n` | dry run — list what *would* play |

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/screenshots/grid.png" alt="coomplexer grid launching a 3x1 wall">

### 2. Drive it while it runs

```console
coomplexer ctl pause          # also: resume, toggle, next, prev
coomplexer ctl speed 1.5      # also: volume 40, mute, unmute
coomplexer ctl list           # what's running, and on which display
```

Commands reach every tile at once, over mpv's own IPC channel. Name a wall
(`grid --name office`) and several of them become a group you can drive or
close together:

```console
coomplexer ctl pause -S office   # every wall named "office"
coomplexer ctl stop  -S office   # close them all at once
```

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/screenshots/ctl.png" alt="Pausing a running wall with coomplexer ctl">

### 3. Close it

```console
coomplexer stop               # closes the wall you opened most recently
coomplexer stop --list        # what's running?
coomplexer stop --all         # close every wall
```

Open two walls on two monitors and `stop` only closes the newest — the other keeps playing.

### 4. Find things again

```console
coomplexer playlists          # every playlist you've made, newest first
coomplexer grid --recent      # reopen the newest one
coomplexer grid --recent kazuha   # newest one matching "kazuha"
```

`playlists` then asks which one to open. Answer with a number, plus any `grid`
flags you want for this launch:

```
open> 2 -g 2x2 -s 0.5
```

**Tab** completes at every position — the numbers, then the flag names, then a
flag's own values (`-t ` offers `images videos mixed`, `-g ` offers the usual
layouts). **q** or Ctrl-C backs out. Add `--no-interactive` to just list and
exit.

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/screenshots/playlists.png" alt="coomplexer playlists listing saved playlists newest first">

> [!TIP]
> Stuck? Two commands answer almost every question: `coomplexer doctor` checks your environment, and adding `--verbose` to any command explains every failure it would otherwise handle silently.

---

# Advanced

<details>
<summary><b>Multiple monitors</b> — spanning, per-screen layouts, hiding the taskbar</summary>

```console
coomplexer grid ~/videos -D 2            # second monitor
coomplexer grid ~/videos -D all          # every monitor
coomplexer grid ~/videos -D 1,2 -g 3x1,2x2   # a different layout per monitor
```

`-D` takes a 1-based index, a comma list, or `all`. One `-g` layout repeats on every screen; a comma list of layouts pairs one per monitor in `-D` order. Files are dealt across the whole combined wall.

**The taskbar.** By default tiles are measured against the *work area*, so nothing hides behind the taskbar. To use the full screen instead:

```console
coomplexer grid ~/videos --hide-taskbar
```

The taskbar comes back when you `coomplexer stop`. If one ever gets stranded — a wall closed some other way — `coomplexer stop --all` always restores it. Set `hide-taskbar = true` under `[grid]` in your config to make it the default. Windows only.

</details>

<details>
<summary><b>Scraping</b> — subreddits, Korean gif boards, translation, downloads</summary>

```console
coomplexer scrape karina                     # newest matches -> a playlist
coomplexer scrape karina -g                  # ...and open it as a wall
coomplexer scrape karina -d                  # ...or download it instead
coomplexer scrape -n 99 --limit-mode live karina   # keep going until 99 live URLs
```

Results are resolved to live direct media URLs and written to a self-describing `.m3u`.

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/screenshots/scrape.png" alt="A multi-source scrape resolving links">

**Sources** — `--source reddit` (default), `kkzz`, `kgirls`, `youtube`, or `all`:

```console
coomplexer scrape --source all shuhua
coomplexer scrape --source youtube 카즈하      # YouTube Shorts by #hashtag
```

`youtube` searches `youtube.com/hashtag/<term>/shorts`. Its links go into the
playlist as **YouTube page URLs, deliberately unresolved** — YouTube's direct
media URLs expire quickly and are tied to the client that requested them, so a
resolved playlist would be dead on arrival. mpv's own yt-dlp hook opens each
one when a tile reaches it, and coomplexer keeps that hook enabled whenever a
playlist contains such links.

`-n` is split between the sources, and the term is auto-translated per source: reddit searches `shuhua`, the Korean boards search `슈화`. Name both scripts yourself with a piped term (`"iroha|이로하"`), or turn it off with `--no-translate`.

reddit's JS bot-wall is cleared headlessly (install [Deno](https://deno.com/) for the robust solver, or pass `--cookies firefox`). kgirls.net sits behind Cloudflare and needs a [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) endpoint: `--flaresolverr http://host:8191/v1`.

**Incremental scraping.** Every URL a run collects is remembered, so:

```console
coomplexer scrape karina --new-only -n 25 -d --download-archive
```

only fetches what previous runs never saw, and skips videos already downloaded.

**Merging.** `-a/--append` folds existing playlists, folders and files into a combined playlist alongside the fresh results:

```console
coomplexer scrape -g 3x1 --source all "iroha|이로하" -a old.m3u -a ~/pics/iroha -t videos -p
```

</details>

<details>
<summary><b>The media index</b> — search, filter and export without rescanning</summary>

Every local file a grid scan touches is indexed automatically (path, type, orientation, dimensions), so it grows into a catalog of your libraries.

```console
coomplexer db                              # what's in there
coomplexer db index ~/pics ~/videos -r     # add a library explicitly
coomplexer db search kazuha                # where is that file?
coomplexer db prune -n                     # preview dead entries, then drop the -n
```

Export new playlists from it instantly — no rescanning, no re-probing:

```console
coomplexer db export -t videos -p                 # portrait videos
coomplexer db export --order random --limit 50    # 50 at random
coomplexer db export --min-height 1080            # 1080p and up
coomplexer db export -L --db other.db             # from a secondary index
```

`--order` takes `path`, `random`, `newest`, `oldest`, `largest`, `smallest`.

Add `-g` to any export to open it as a wall straight away:

```console
coomplexer db export -t videos -p -g 3x1 -D all
```

**Master playlists** keep one file per set of filters and rewrite it in place,
so you can point at it forever instead of collecting timestamped exports:

```console
coomplexer db master -t videos -p        # -> playlists/master_videos_portrait.m3u
coomplexer db master -t videos -g -D all # refresh it, then shuffle it everywhere
```

Re-running reports the drift — `642 entries (+12 new, -3 gone)`.

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/screenshots/db.png" alt="Searching the media index">

</details>

<details>
<summary><b>Stacking, in a row or a column</b></summary>

```console
coomplexer stack clip.webm                 # one clip, tripled across the row
coomplexer stack -r 1 a.mp4 b.mp4 c.mp4    # three clips, each rotated 90°
coomplexer stack -V -D 3 a.mp4 b.mp4 c.mp4 # a column of three, filling portrait monitor 3
coomplexer stack https://youtu.be/XXXX     # a URL, resolved by mpv
coomplexer stack -z 1.2 clip.webm          # zoomed 20% into the centre
```

Uses mpv's filtergraph to stack the panes in a single window: `hstack` into a row by default, or `vstack` into a column with `-V/--vertical`. Panes are normalized to one height for a row and one width for a column, so mixed resolutions can be stacked together. Three 16:9 clips make a 16:27 column, which is close enough to fill a portrait monitor. Panes without an audio track are handled automatically. `-D 3` fills a chosen monitor, and `-z` zooms into the centre of the source without changing the layout.

</details>

<details>
<summary><b>Config file</b> — defaults and named profiles</summary>

```console
coomplexer config --init    # generate a fully commented starter
coomplexer config --edit    # open it in your editor
coomplexer config --path    # where is it?
```

[`config.example.toml`](https://github.com/kpg-anon/coomplexer/blob/main/config.example.toml) in the repo root is exactly what
`--init` writes: every key, commented, with its default. Read it there without
installing anything.

```toml
[grid]
grid = "3x1"
volume = 20
hide-taskbar = true

[scrape]
subreddit = "kpopfap"
solver = "deno"

[profiles.default]          # applied automatically to: coomplexer grid PLAYLIST.m3u
grid = "3x1"
speed = 0.5

[profiles.wall]             # applied with: --profile wall
grid = "4x2"
portrait = true
type = "videos"
```

Precedence, lowest to highest: **built-in defaults → `[command]` table → `[profiles.default]` for grid → `--profile NAME` → explicit CLI flags.** Unknown keys are rejected with a suggestion rather than ignored.

The file lives in the repo root from a checkout, or your per-user config dir (`%APPDATA%\coomplexer`, `~/.config/coomplexer`, `~/Library/Application Support/coomplexer`) when installed.

</details>

<details>
<summary><b>Sessions</b> — how walls are tracked</summary>

Each launch records a session: its tiles, their process ids, and their mpv IPC endpoints. That is what lets `stop` close one wall without touching another, and what `ctl` talks to.

```console
coomplexer stop --list        # ids, live tile counts, layouts, start times
coomplexer ctl next -S 6a66dec        # target one wall explicitly
coomplexer stop -S 6a66dec
```

Records are cleaned up once their processes are gone. Walls launched before v1.9 have no IPC channel and must be relaunched to be controllable.

</details>

<details>
<summary><b>Troubleshooting</b></summary>

<img src="https://raw.githubusercontent.com/kpg-anon/coomplexer/main/screenshots/doctor.png" alt="coomplexer doctor checking tools, packages and state">

Start with `coomplexer doctor`. Then re-run the failing command with `--verbose` — every failure the tool handles quietly (a source falling back, a retried request, an unreadable file, a URL yt-dlp gave up on) is reported there with its cause.

| Symptom | Fix |
|---------|-----|
| `mpv not found on PATH` | install mpv, make sure its folder is on `PATH` |
| `-p`/`-L` filters everything out | probing failed — install `pillow`/`av` or `ffprobe`, then `coomplexer db prune` and retry |
| tiles on the wrong monitor | `-D` is 1-based; the error message lists what was detected |
| gaps along the bottom of the wall | that strip is the taskbar — try `--hide-taskbar` |
| scrape returns 0 links | try a broader term or a higher `-n`; `--verbose` shows which source refused |
| scrape stops early | you pressed `q`, or a source ran out — links collected so far are already flushed to the playlist |

Full reference for every flag and config key: **[USAGE.md](https://github.com/kpg-anon/coomplexer/blob/main/USAGE.md)**.

</details>

---

## Documentation

| | |
|---|---|
| **[USAGE.md](https://github.com/kpg-anon/coomplexer/blob/main/USAGE.md)** | complete reference — every command, flag and config key |
| **[CHANGELOG.md](https://github.com/kpg-anon/coomplexer/blob/main/CHANGELOG.md)** | what changed, and why |
| `coomplexer --help` | colorized help, with examples, for any command |

## Development

```console
pip install -e . pytest ruff
pytest          # 386 tests, no mpv or network required
ruff check .
```

## License

[MIT](https://github.com/kpg-anon/coomplexer/blob/main/LICENSE).
