# Up Next > Twitch live channels, YouTube uploads and a watch-later queue in one feed. # Overview Source: https://upnext.timmo.dev/ Up Next watches the Twitch channels you follow, the YouTube channels you list and a watch-later queue, and keeps them in one feed. It sends a desktop notification when something goes live, and can open your favourite channels as soon as they start. ```bash # What's live, new and saved right now upnext feed # The feed now and after every change, as JSON lines for panels and scripts upnext watch --json ``` ## Watch later Save any web address to the queue. YouTube videos get their title, channel and thumbnail looked up; other links show the title you give them, or the address. ```bash upnext queue add https://youtu.be/ upnext queue add https://example.com/talk --title "That talk" # Remove a saved item, or hide a YouTube upload, by the ID from feed --json upnext watched youtube: ``` A saved video that's also one of a channel's recent uploads shows once, as the upload. Marking it watched removes both. ## How it fits together - `upnext serve` runs as a systemd user service. It checks each source, keeps the feed and sends notifications. - Every other command is a small client. It connects to the daemon socket, makes one request and exits, or streams the feed. - The [Omarchy panel](/omarchy) shows the feed in sections: live, upcoming, new uploads and watch later. - From TypeScript, use [`@timmo001/effect-upnext`](/libraries) to talk to the socket directly. ## Get started | Page | Why | | --- | --- | | [Install](/install) | Install the Arch package or a release build | | [Configuration](/configuration) | Set up Twitch, YouTube and your channels | | [Running Up Next](/running) | The user service, logs and upgrades | | [Omarchy panel](/omarchy) | The bar widget and panel | | [Commands](/commands) | Every command and flag | | [Libraries](/libraries) | Use the feed from your own Effect app | | [Migrating from twitch-notifications](/from-twitch-notifications) | What moves and what changes | ## Machine-readable docs | URL | Use | | --- | --- | | https://upnext.timmo.dev/llms.txt | Compact page index | | https://upnext.timmo.dev/llms-full.txt | Full docs bundle | | https://upnext.timmo.dev/mcp | Hosted MCP (search, page and navigation) | | `/{route}.md` | One page as Markdown | --- # Commands Source: https://upnext.timmo.dev/commands Each command has its own page with its help, as `upnext --help` prints it. | Command | Alias | | --- | --- | | [`serve`](/commands/serve) | None | | [`feed`](/commands/feed) | None | | [`watch`](/commands/watch) | None | | [`recheck`](/commands/recheck) | None | | [`auth`](/commands/auth) | None | | [`channel`](/commands/channel) | None | | [`queue`](/commands/queue) | None | | [`watched`](/commands/watched) | None | ## Global flags ```text DESCRIPTION Twitch live channels, YouTube uploads and a watch-later queue in one feed USAGE upnext [flags] FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) GLOBAL FLAGS --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command --completions Print shell completion script (choices: bash, zsh, fish, sh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none) SUBCOMMANDS serve Run the daemon and serve the feed on its socket feed Print what's live, new and saved watch Print the feed, then again after each change recheck Check sources now instead of waiting auth Sign in to a source that needs it channel Add or remove followed channels queue Manage the watch-later queue watched Hide a YouTube upload or remove a saved item ``` --- # upnext auth Source: https://upnext.timmo.dev/commands/auth Every `upnext auth` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext auth` ```text DESCRIPTION Sign in to a source that needs it USAGE upnext auth [flags] ARGUMENTS source choice The source to sign in to: twitch FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) ``` --- # upnext channel Source: https://upnext.timmo.dev/commands/channel Every `upnext channel` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext channel` ```text DESCRIPTION Add or remove followed channels USAGE upnext channel [flags] FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) ``` ## `upnext channel add` ```text DESCRIPTION Add a channel, or change whether it opens when live USAGE upnext channel add [flags] ARGUMENTS source choice twitch or youtube name string A Twitch login or a YouTube channel ID FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) --open Open the channel as soon as it goes live ``` ## `upnext channel remove` ```text DESCRIPTION Remove a channel USAGE upnext channel remove [flags] ARGUMENTS source choice twitch or youtube name string A Twitch login or a YouTube channel ID FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) ``` --- # upnext feed Source: https://upnext.timmo.dev/commands/feed Every `upnext feed` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext feed` ```text DESCRIPTION Print what's live, new and saved USAGE upnext feed [flags] FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) --json Print the feed as JSON ``` --- # upnext queue Source: https://upnext.timmo.dev/commands/queue Every `upnext queue` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext queue` ```text DESCRIPTION Manage the watch-later queue USAGE upnext queue [flags] FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) ``` ## `upnext queue add` ```text DESCRIPTION Save a URL to watch later USAGE upnext queue add [flags] ARGUMENTS url string What to watch later FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) --title string Title to show instead of the one looked up --json Print the saved item as JSON ``` --- # upnext recheck Source: https://upnext.timmo.dev/commands/recheck Every `upnext recheck` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext recheck` ```text DESCRIPTION Check sources now instead of waiting USAGE upnext recheck [flags] FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) --source choice Only check this source (choices: twitch, youtube, link) --open Open live channels set to auto-open, even if already live ``` --- # upnext serve Source: https://upnext.timmo.dev/commands/serve Every `upnext serve` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext serve` ```text DESCRIPTION Run the daemon and serve the feed on its socket USAGE upnext serve [flags] FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) ``` --- # upnext watch Source: https://upnext.timmo.dev/commands/watch Every `upnext watch` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext watch` ```text DESCRIPTION Print the feed, then again after each change USAGE upnext watch [flags] FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) --json Print each feed as one line of JSON ``` --- # upnext watched Source: https://upnext.timmo.dev/commands/watched Every `upnext watched` command and its help, as `--help` prints it. Each also accepts the [global flags](/commands#global-flags). ## `upnext watched` ```text DESCRIPTION Hide a YouTube upload or remove a saved item USAGE upnext watched [flags] ARGUMENTS id string The item ID, as shown by feed --json FLAGS --socket string Path to the daemon socket (default: $UPNEXT_SOCK, then $XDG_RUNTIME_DIR/upnext/upnext.sock) ``` --- # Configuration Source: https://upnext.timmo.dev/configuration Up Next reads two files from `$XDG_CONFIG_HOME/upnext`, which is usually `~/.config/upnext`. Only `upnext serve` reads them; every other command talks to the daemon socket. If you used twitch-notifications, Up Next imports its config on first run. See [Migrating from twitch-notifications](/from-twitch-notifications). ## config.yml ```yaml notify_on_startup: true sound_file: /usr/share/sounds/freedesktop/stereo/message-new-instant.oga twitch: client_id: ${TWITCH_CLIENT_ID} client_secret: ${TWITCH_CLIENT_SECRET} poll_interval: 60 youtube: api_key: ${YOUTUBE_API_KEY} poll_interval: 600 ``` Every setting is optional. - `notify_on_startup`: notify about channels that are already live when the daemon starts. Defaults to `true`. - `sound_file`: a sound to play with each notification. - `twitch.client_id` and `twitch.client_secret`: your Twitch application's credentials. - `twitch.poll_interval`: seconds between checks for channels that live notifications don't cover. Defaults to 60. - `youtube.api_key`: a YouTube Data API key. Without one, Up Next still shows new uploads, but can't tell which videos are live or upcoming. - `youtube.poll_interval`: seconds between YouTube checks. Defaults to 600. Values can use `$VAR` or `${VAR}` to read environment variables, so you can keep secrets out of the file. ## Twitch Up Next needs a Twitch application of your own: 1. Register one in the [Twitch developer console](https://dev.twitch.tv/console/apps). 2. Add `http://localhost:8080/oauth/callback` as an OAuth redirect URL. 3. Put its client ID and secret in `config.yml` under `twitch`. 4. Restart the daemon, then run `upnext auth twitch`. `upnext auth twitch` asks the daemon to open Twitch's sign-in page in your browser and waits until you've finished. The daemon listens on port 8080 only while you sign in. It keeps the tokens in `state.json` and refreshes them itself. If the tokens stop working, the Twitch status in the feed changes to `auth-required` and a notification asks you to sign in again. Clicking it runs `upnext auth twitch`. Live notifications are only for channels in `channels.yml`. Twitch's live events cover up to 10 of them, so those show up within seconds. The rest are checked every `poll_interval`. Another app using the same Twitch account's live events, such as twitch-notifications, can use up that allowance, and Up Next then relies on polling until it's free. ## YouTube Up Next reads each channel's RSS feed, which needs no account. Uploads from the last 7 days show in the feed, and new ones are announced. With `youtube.api_key`, Up Next also looks the videos up in the YouTube Data API, so live streams show as live, scheduled streams as upcoming, and you're notified when one goes live. Create a key in the [Google Cloud console](https://console.cloud.google.com/apis/credentials) with the YouTube Data API v3 enabled. Each check costs one unit of quota per 50 videos. Add channels by ID, which starts with `UC`, or paste the channel's `/channel/` URL: ```bash upnext channel add youtube UCxxxxxxxxxxxxxxxxxxxxxx ``` A channel you've just added doesn't announce its existing uploads. ## channels.yml ```yaml twitch: - name: some_streamer open: true - name: another_streamer youtube: - id: UCxxxxxxxxxxxxxxxxxxxxxx ``` - `twitch`: Twitch logins. Up Next also shows every channel you follow that's live, even if it isn't listed here. - `youtube`: YouTube channel IDs, the part after `/channel/` in a channel's URL. - `open`: open the channel in your browser as soon as it goes live. Defaults to `false`. `upnext channel add` and `upnext channel remove` change this file for you. If you keep `channels.yml` in a dotfiles repository and link it into place with stow, Up Next writes changes through the link, so your repository stays the source. ## State The daemon keeps files it writes for itself in `$XDG_STATE_HOME/upnext/state.json`, which is usually `~/.local/state/upnext/state.json`: Twitch tokens, videos you've marked watched and your watch-later queue. You don't need to edit it. The config and state directories are only readable by you (`0700`) and the files by you (`0600`). ## Socket path Commands find the daemon socket in this order: 1. `--socket ` 2. `$UPNEXT_SOCK` 3. `$XDG_RUNTIME_DIR/upnext/upnext.sock` 4. `$TMPDIR/upnext-$USER/upnext.sock`, falling back to `/tmp` and `default` `upnext serve` uses the same order to decide where to listen, so the defaults always match. --- # Migrating from twitch-notifications Source: https://upnext.timmo.dev/from-twitch-notifications Up Next replaces twitch-notifications. The packages replace `twitch-notifications-git`, so installing Up Next removes it. ## What's imported On first run, if `~/.config/upnext/config.yml` doesn't exist and `~/.config/twitch-notifications/config.yaml` does, Up Next copies: | From `twitch-notifications` | To `upnext` | | --- | --- | | `config.yaml` `notify_on_startup`, `sound_file` | `config.yml`, same keys | | `config.yaml` `poll_interval` | `config.yml` `twitch.poll_interval` | | `config.yaml` `twitch.client_id`, `twitch.client_secret` | `config.yml`, same keys, keeping any `${VAR}` references | | `channels.yml` `watched_channels` | `channels.yml` `twitch` | | `config.yaml` `twitch.access_token`, `twitch.refresh_token` | `state.json` | `system_tray` is dropped, as Up Next has no tray icon. If a twitch-notifications file is a link into a dotfiles repository, such as one made by stow, the new file is written into the same stow package and linked into place the same way. Your dotfiles stay the source. The old directory isn't changed, so you can go back to twitch-notifications if you need to. ## What changes | twitch-notifications | Up Next | | --- | --- | | Autostarted by your desktop session | `upnext.service` systemd user service | | `twitch-notifications --status-json` | `upnext feed --json` | | `twitch-notifications --recheck` | `upnext recheck` | | `twitch-notifications-recheck --open` | `upnext recheck --open` | | Opens the browser to sign in again by itself | Notifies you, then `upnext auth twitch` | | `twitch-notifications-restart` | `systemctl --user restart upnext.service` | | `timmo.twitch` Omarchy plugin | `timmo.upnext` Omarchy plugin | | System tray icon | The Omarchy panel | --- # Install Source: https://upnext.timmo.dev/install Up Next is a single Linux binary for x86_64 and aarch64. Every package includes the binary, the `upnext.service` [user service](/running) and shell completions for bash, zsh and fish. ## Arch Linux Packages are published to the unofficial `timmo` pacman repository. Add it before the other repository sections in `/etc/pacman.conf`: ```ini [timmo] SigLevel = PackageRequired DatabaseOptional TrustedOnly Server = https://packages.timmo.dev/$arch ``` Then install one of the two packages: | Package | Built from | | --- | --- | | `upnext-bin` | The latest release | | `upnext-git` | Every push to `main` | ```bash sudo pacman -Syu upnext-bin ``` The packages conflict with each other, so installing one replaces the other. Both replace `twitch-notifications-git`. The install hook enables `upnext.service` for every user's future logins. If you install with `sudo` from a running desktop session, it also starts the service for you. On upgrade it restarts the service only if it was already running, so a service you stopped stays stopped. ## Debian, Ubuntu and Fedora Each [GitHub release](https://github.com/timmo001/upnext/releases) has `.deb` and `.rpm` packages: ```bash # Debian and Ubuntu sudo apt install ./upnext__amd64.deb # Fedora sudo dnf install ./upnext--1.x86_64.rpm ``` These install the user service but don't enable it. See [Running Up Next](/running). ## Release archive Each release also has an `upnext--linux-.tar.gz` archive with just the binary. Put it somewhere on your `PATH`: ```bash tar -xzf upnext--linux-x86_64.tar.gz install -Dm755 upnext ~/.local/bin/upnext ``` Release assets come with a `SHA256SUMS` file and a Sigstore bundle. To check an asset was built by this repository's release workflow: ```bash gh attestation verify upnext--linux-x86_64.tar.gz --repo timmo001/upnext ``` ## Build from source You need [mise](https://mise.jdx.dev), which installs the pinned Bun and Node versions: ```bash git clone https://github.com/timmo001/upnext.git cd upnext mise install mise run build ``` The binary is written to `dist/upnext`. ## Next steps - [Configure](/configuration) Twitch, YouTube and your channels. - [Run Up Next](/running) as a user service. --- # Libraries Source: https://upnext.timmo.dev/libraries Up Next is built from Effect v4 libraries, published to npm and JSR. They work under Bun and Node. | Package | Use it to | | --- | --- | | [`@timmo001/effect-upnext`](https://github.com/timmo001/upnext/tree/main/packages/client) | Talk to a running daemon over its socket | | [`@timmo001/effect-twitch`](https://github.com/timmo001/upnext/tree/main/packages/twitch) | Sign in to Twitch, find live channels and turn them into `MediaItem`s, without the daemon | | [`@timmo001/effect-youtube`](https://github.com/timmo001/upnext/tree/main/packages/youtube) | Read YouTube channel feeds and live streams and turn them into `MediaItem`s, without the daemon | | [`@timmo001/effect-upnext-shared`](https://github.com/timmo001/upnext/tree/main/packages/shared) | Use the `MediaItem` schemas every source and client shares | The CLI is a client of the same RPCs that `effect-upnext` defines, so anything the CLI does, your app can do too. ## Client ```bash bun add @timmo001/effect-upnext @timmo001/effect-upnext-shared effect ``` `effect` is a peer dependency, so install the Effect v4 version your app already uses. `UpnextClient` is an Effect service built on `effect/rpc`. `UpnextClient.layer(socketPath)` opens the Unix socket and speaks newline-delimited JSON. The connection lives as long as the layer, so provide it once around the work that needs it. `resolveSocketPath` finds the socket the same way the CLI does. See [Socket path](/configuration#socket-path). ```ts import { BunRuntime, BunServices } from "@effect/platform-bun"; import { resolveSocketPath, UpnextClient } from "@timmo001/effect-upnext"; import { Console, Effect, Option, Stream } from "effect"; const program = Effect.gen(function* () { const client = yield* UpnextClient; yield* client.WatchFeed().pipe( Stream.runForEach((feed) => Console.log( `${feed.items.filter(({ item }) => item.kind === "live").length} live`, ), ), ); }); const main = Effect.gen(function* () { const socketPath = yield* resolveSocketPath(Option.none()); yield* program.pipe(Effect.provide(UpnextClient.layer(socketPath))); }); main.pipe(Effect.provide(BunServices.layer), BunRuntime.runMain); ``` ## RPCs | RPC | Does | | --- | --- | | `GetFeed` | Returns each source's status and every item | | `WatchFeed` | Streams the whole feed, then again after each change | | `Recheck` | Checks every source, or one, now | | `SignIn` | Starts signing in to Twitch and returns the page the daemon opened | | `AddChannel`, `RemoveChannel` | Change `channels.yml` | | `QueueAdd` | Saves a URL to watch later | | `MarkWatched` | Hides a YouTube upload or removes a saved item | Calls fail with `RpcClientError` when the daemon isn't reachable rather than waiting for it to come back. --- # Omarchy panel Source: https://upnext.timmo.dev/omarchy The `timmo.upnext` plugin adds a bar widget and a panel to Omarchy. It reads the feed from the daemon, so `upnext.service` needs to be running. ## Install ```bash omarchy plugin add https://github.com/timmo001/omarchy-upnext.git ``` Accept the prompt to enable the plugin. If you had `timmo.twitch` installed, remove it with `omarchy plugin remove timmo.twitch`. ## The widget The widget shows how many of your channels are live, and hides itself when nothing is. Hover over the bar to reveal it. - Click to open the panel. - Middle-click to recheck every source. - Right-click to restart `upnext.service`. ## The panel The panel shows the feed in sections: live, upcoming, new uploads and watch later. Live followed channels that aren't in `channels.yml` come after your own. - Type to filter, use Up and Down to move, and press Enter to open the selected item. - Press Shift+Enter, or right-click, to mark a YouTube upload or a saved item watched. - Press Ctrl+R to recheck. - If a source needs you to sign in, it shows at the top. Select it to run `upnext auth`. The panel follows `upnext watch --json`, so it updates as soon as the feed changes. When the daemon restarts, the panel reconnects within a few seconds. ## IPC The plugin exposes the `timmo.upnext` IPC target, with `recheck`, `restart`, `open`, `close`, `show`, `hide` and `toggle`: ```bash omarchy-shell shell toggle timmo.upnext ``` --- # Running Up Next Source: https://upnext.timmo.dev/running `upnext serve` keeps the feed. Every other command needs it running, so it normally runs as a systemd user service. ## User service The packages install `upnext.service` as a user unit. The Arch package enables it for every user's future logins. To start it now, or after installing a `.deb` or `.rpm`: ```bash systemctl --user daemon-reload systemctl --user enable --now upnext.service ``` Check it's running: ```bash systemctl --user status upnext.service journalctl --user -u upnext.service -f ``` A healthy start logs `Listening` with the socket path. The service restarts 5 seconds after it fails. ## Upgrades The Arch package restarts a running service after an upgrade, so it uses the new binary straight away. For other installs, restart it yourself: ```bash systemctl --user restart upnext.service ``` Watchers, such as `upnext watch`, exit when the daemon restarts. Run them under something that restarts them, such as a panel's restart interval or a systemd unit. ## Run in the foreground To troubleshoot, stop the service and run the daemon in a terminal with debug logs: ```bash systemctl --user stop upnext.service upnext serve --log-level debug ``` Use `--socket` to run a second daemon beside the service, for example with a different config: ```bash XDG_CONFIG_HOME=/tmp/upnext-test upnext serve --socket /tmp/upnext-test.sock upnext feed --socket /tmp/upnext-test.sock ``` ## Install the unit by hand When you build from source, copy the unit and point `ExecStart` at your binary: ```bash mkdir -p ~/.config/systemd/user cp .scripts/linux/upnext.service ~/.config/systemd/user/ sed -i "s#/usr/bin/upnext#$(command -v upnext)#" ~/.config/systemd/user/upnext.service systemctl --user daemon-reload systemctl --user enable --now upnext.service ```