Files
MetaAppStore/Apps/MetaWatch/docker-compose.yml
T

581 lines
27 KiB
YAML

name: metawatch
# =============================================================================
# MetaWatch — Stremio-style, video-only client for the MetaMesh network.
#
# Self-contained stack (mirrors packages/meta-watch/dev/): the meta-watch app
# plus its OWN dedicated discovery (meta-search), transport (meta-share) and
# meta-core. It does NOT share the standalone MetaCore app — its core holds
# only this client's identity keystore + ingested records, so MetaWatch can be
# installed alongside MetaGateway and the standalone Meta* apps with zero
# container-name / data-path collisions.
#
# metawatch-core → /DATA/AppData/metawatch/metacore (per-stack meta-core)
# metawatch-search → discovery (Kamilata federated search, identity proxy)
# metawatch-share → pure content transport (byte Range, bitswap, BT)
# metawatch-app → the UI/back-end the user browses (behind OIDC)
#
# Federation: meta-search/meta-share join the PUBLIC libp2p swarm (kad-DHT,
# default cohort) on distinct host ports (4003/4004) + mDNS on `pcs`, so they
# discover MetaGateway's records both same-host (mDNS) and across nodes (kad).
# =============================================================================
services:
# ---------------------------------------------------------------------------
# metawatch-core — dedicated meta-core for this client (identity keystore +
# ingested records). File watcher OFF: MetaWatch is a consumer, not a library
# indexer. Data lives under /DATA/AppData/metawatch/ so CasaOS archives it.
# ---------------------------------------------------------------------------
metawatch-core:
# 1.0.1 carries the identity API (commit 52f5b5c) required for MetaWatch
# sign-in / keypair generation; 1.0.0 predates it.
image: ipcr-watch.nsl.sh/ipns/metadec.eth/metadec.eth/meta-core:2.1.0.1
container_name: metawatch-core
# Hard RAM cap, swap disabled (memswap_limit == mem_limit) → OOM
# instead of swap. Mirrors the dev sub-stack limits.
mem_limit: 768m
memswap_limit: 768m
hostname: metawatch-core
restart: unless-stopped
privileged: true
user: "0:0"
cpu_shares: 30
expose:
- 9000
volumes:
- ${DATA_ROOT:-/DATA}/AppData/metawatch/metacore:/meta-core:rw
# (meta-core 2.0.0 keeps its rebuildable index cache at
# /meta-core/db-cache/cache.db inside this bind — excluded from backups.)
# Subtitle store: player-uploaded subtitles land in /files/plugin/subtitles
# (WebDAV PUT + POST /api/files/register, needs meta-core with that route).
# Plugin output only — no /files/watch mount, watcher stays off.
- ${DATA_ROOT:-/DATA}/AppData/metawatch/files/plugin:/files/plugin:rw
environment:
- BASE_URL=http://metawatch-core:9000
- ENABLE_FILE_WATCHER=false
- TZ=$TZ
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9000/health"]
interval: 5s
timeout: 3s
retries: 30
start_period: 30s
networks:
- metawatch
# ---------------------------------------------------------------------------
# metawatch-search — DISCOVERY peer. Federated Kamilata search, record
# metadata, poster/thumbnail recovery, shared prefs, User Data Layer and the
# identity/sign chain (meta-watch → meta-search → meta-core). Public libp2p on
# host port 4004; mDNS on pcs for same-host peer discovery.
# ---------------------------------------------------------------------------
metawatch-search:
# 1.0.2 = rsa-enabled libp2p — required to join the public IPFS DHT (the
# RSA-keyed bootstrap.libp2p.io nodes) and therefore to DISCOVER remote
# gateways via the kad `metamesh-gateway` provider namespace. Without it
# this peer only ever sees same-host mDNS peers.
image: ipcr-watch.nsl.sh/ipns/metadec.eth/metadec.eth/meta-search:1.3.2
container_name: metawatch-search
# Hard RAM cap, swap disabled (memswap_limit == mem_limit) → OOM
# instead of swap. Mirrors the dev sub-stack limits.
mem_limit: 1g
memswap_limit: 1g
hostname: metawatch-search
restart: unless-stopped
user: "0:0"
cpu_shares: 30
expose:
- 3000
ports:
# libp2p TCP — host-published so the swarm can dial this peer.
- "4004:4001"
volumes:
# No /meta-core mount: this service locates meta-core over UDP
# (beacon v2) instead of reading kv-leader.info off a shared
# volume. See docs/project-architecture/beacon-v2.md.
# META_SHARE_DATA (default /data/meta-share): prefs.json (language prefs)
# + the redb blockstore holding poster/thumbnail blocks recovered off the
# swarm. Without this mount both are on the container's writable layer and
# every image bump loses the prefs and re-fetches every poster.
- ${DATA_ROOT:-/DATA}/AppData/metawatch/search-data:/data:rw
# Claim-signing keys (meta-core 2.0.0 claims model), one file per writer
# service in this stack. Config — backed up, NOT in backup.exclude.
- ${DATA_ROOT:-/DATA}/AppData/metawatch/keys:/keys:rw
environment:
P2P_LISTEN: "/ip4/0.0.0.0/tcp/4001"
# Public IPFS DHT bootstraps by explicit /dns4/.../tcp/4001 (the nested
# dnsaddr default resolves to wss-only in some container resolvers).
# NOTE: no PUBLIC_ADDR here ON PURPOSE. meta-search supports it, but this
# peer only needs to WALK the DHT: with the bootstraps below it discovers
# remote gateways (kad `metamesh-gateway` provider records) and dials them
# outbound. Announcing would publish this client's index to the swarm for
# no benefit. The gateway is the peer that must be dialable — see
# MetaGateway's META_GATEWAY_PUBLIC_ADDR.
# Includes mars.i.ipfs.io — the only surviving legacy *.i.ipfs.io
# bootstrapper (neptune/pluto/uranus/earth/saturn/venus are all NXDOMAIN).
# It is here for resilience: the four IPFS-Foundation bootstrappers can
# refuse an individual IP, and when they do a peer cannot join the DHT AT
# ALL — no provider record, no discovery, no gateway fan-out. Observed on
# watch.nsl.sh 2026-09-01: 2 connections with the four alone, 126 once a
# reachable fifth was added. Costs nothing when the four work.
KAD_BOOTSTRAP_PEERS: "/dns4/sg1.bootstrap.libp2p.io/tcp/4001/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt,/dns4/sv15.bootstrap.libp2p.io/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN,/dns4/ny5.bootstrap.libp2p.io/tcp/4001/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa,/dns4/am6.bootstrap.libp2p.io/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb,/dns4/mars.i.ipfs.io/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"
# Was LOAD-BEARING up to meta-search:1.0.2, which defaulted this to 8s — too
# short for the gateway fan-out (Prowlarr -> public indexers), so search
# returned empty. 300 is the default on the 1.1.0 pin; kept explicit so a
# future default change can't silently shorten it again.
SEARCH_TIMEOUT_SECS: "300"
META_CORE_URL: "http://metawatch-core:9000"
BASE_URL: "http://metawatch-search:3000"
# Own claim-signing key (relayed claims are forwarded verbatim, never re-signed); generated on first boot if missing.
METAMESH_SIGNING_KEY_FILE: "/keys/metawatch-search.key"
# Renamed at the 1.1.0 bump (2026-08-26), as the old note here said to do:
# META_SHARE_PEER_API_URL was the pre-1.0.8 spelling and now only logs a
# deprecation warning. Do NOT set both.
INTERNAL_BASE_URL: "http://metawatch-search:3000"
# NOTE: KAD_NAMESPACE was dropped — it merely restated the default cohort
# (`metamesh-share-default`). Set it to a non-default value on EVERY peer
# to scope a private cohort; they must all agree or search returns zero.
TZ: "$TZ"
depends_on:
metawatch-core:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
networks:
- metawatch
- pcs
# ---------------------------------------------------------------------------
# metawatch-share (+ metawatch-share-ipfs) — content TRANSPORT peer: the
# meta-share hull with one ipfs plugin. Byte Range proxy for playback/probe/
# warm/playback-focus over bitswap; seeds whatever it fetches; Usenet cids are
# forwarded to a credentialed gateway. Public libp2p on host port 4003.
# ---------------------------------------------------------------------------
metawatch-share:
# The meta-share HULL (2.x): HTTP API, material index, seed manifest,
# eviction, playback focus, meta-core ingest. Every byte-moving protocol is
# a transport plugin; this bundle ships ONE — metawatch-share-ipfs below — which
# makes it the playback cache: bitswap fetch + re-seed, and Usenet cids forwarded to a credentialed gateway. Torrent and local NZB tiers are not bundled
# (META_SHARE_PLUGIN_{TORRENT,NZB}_URL=off keeps the hull from seeding the
# MetaShare app's `metashare-<tier>` hostnames, which on a shared `pcs`
# would be ANOTHER hull's plugins writing to another /data).
# - 3000 HTTP API - 3001 plugin callback API (internal only)
image: ipcr-watch.nsl.sh/ipns/metadec.eth/metadec.eth/meta-share:2.7.0
container_name: metawatch-share
# The swarm and blockstore moved to the ipfs plugin, so the hull needs far
# less than the monolith did.
mem_limit: 1g
memswap_limit: 1g
hostname: metawatch-share
restart: unless-stopped
user: "0:0"
cpu_shares: 30
# material.redb writes its allocator state on Drop; give SIGTERM room.
stop_grace_period: 30s
expose:
- 3000
- 3001
volumes:
# settings.json — network endpoints and the plugin list.
- ${DATA_ROOT:-/DATA}/AppData/metawatch/metashare-config:/config:rw
# The shared data volume: cache/, tmp/, seeds.json, material.redb are the
# hull's; ipfs/blocks.redb is the ipfs plugin's corner of the SAME folder
# (layout unchanged from 1.0.x, so an existing cache carries over).
- ${DATA_ROOT:-/DATA}/AppData/metawatch/share-data:/data:rw
# Claim-signing keys (meta-core 2.0.0 claims model), one file per writer
# service in this stack. Config — backed up, NOT in backup.exclude.
- ${DATA_ROOT:-/DATA}/AppData/metawatch/keys:/keys:rw
environment:
META_CORE_URL: "http://metawatch-core:9000"
BASE_URL: "http://metawatch-share:3000"
INTERNAL_BASE_URL: "http://metawatch-share:3000"
META_SHARE_PLUGIN_IPFS_URL: "http://metawatch-share-ipfs:3000"
META_SHARE_PLUGIN_TORRENT_URL: "off"
META_SHARE_PLUGIN_NZB_URL: "off"
# Own claim-signing key; generated on first boot if missing.
METAMESH_SIGNING_KEY_FILE: "/keys/metawatch-share.key"
TZ: "$TZ"
depends_on:
metawatch-core:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
networks:
- metawatch
- pcs
metawatch-share-ipfs:
# The hull's only transport plugin: the libp2p swarm (kad-DHT, identify,
# mDNS, bitswap), the blockstore, gateway discovery and the peer directory
# the hull uses to forward Usenet cids. Waits up to 60 s for the hull's
# internal /health before opening blocks.redb, so no depends_on.
image: ipcr-watch.nsl.sh/ipns/metadec.eth/metadec.eth/meta-transport-ipfs:1.3.4
container_name: metawatch-share-ipfs
hostname: metawatch-share-ipfs
restart: unless-stopped
user: "0:0"
cpu_shares: 30
mem_limit: 2g
memswap_limit: 2g
stop_grace_period: 30s
expose:
- 3000
# libp2p TCP must be host-published (kad lookups dial it directly; Caddy
# cannot proxy it). Same host port the 1.0.x monolith used.
ports:
- "4003:4001"
volumes:
- ${DATA_ROOT:-/DATA}/AppData/metawatch/share-data:/data:rw
- ${DATA_ROOT:-/DATA}/AppData/metawatch/share-ipfs-state:/state:rw
environment:
META_SHARE_HULL_URL: "http://metawatch-share:3001"
# Beacon v2: only this bundle's hull may list this plugin.
BEACON_BINDS: "metawatch-share"
# ${APP_PUBLIC_IPV4}, NOT ${APP_PUBLIC_IP}: on an IPv6-primary PCS the
# latter is a v6 address, and /ip4/<v6>/… is not a parseable multiaddr.
PUBLIC_ADDR: "/ip4/${APP_PUBLIC_IPV4}/tcp/4003"
# Public IPFS DHT bootstraps by explicit /dns4/…/tcp/4001, plus
# mars.i.ipfs.io in case the foundation four refuse this IP.
KAD_BOOTSTRAP_PEERS: "/dns4/sg1.bootstrap.libp2p.io/tcp/4001/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt,/dns4/sv15.bootstrap.libp2p.io/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN,/dns4/ny5.bootstrap.libp2p.io/tcp/4001/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa,/dns4/am6.bootstrap.libp2p.io/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb,/dns4/mars.i.ipfs.io/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"
TZ: "$TZ"
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/health"]
interval: 10s
timeout: 3s
retries: 10
start_period: 30s
networks:
- metawatch
- pcs
# ---------------------------------------------------------------------------
# metawatch-app — the back-end the browser talks to. Zero media work: flat
# Range/206 byte proxy to metawatch-share; search/identity via metawatch-search.
# My List + Continue Watching persist in /DATA/AppData/metawatch/data.
# ---------------------------------------------------------------------------
metawatch-app:
image: ipcr-watch.nsl.sh/ipns/metadec.eth/metadec.eth/meta-watch:1.2.2
container_name: metawatch-app
# Run as the PUID that Maison creates the app folders with (as the Shared
# variant does). The image default user (uid 10001) cannot write them on a
# fresh install: "create ipfs dir … Permission denied".
user: "1000:1000"
hostname: metawatch-app
restart: unless-stopped
cpu_shares: 30
# Hard RAM cap with swap disabled (memswap_limit == mem_limit → zero swap
# budget on cgroup v2) so the container is OOM-killed instead of pushing
# the host into swap. Same value the dev stack pins (METAWATCH_MEM=512m).
mem_limit: 512m
memswap_limit: 512m
expose:
- 3000
volumes:
- ${DATA_ROOT:-/DATA}/AppData/metawatch/data:/app/data
# Claim-signing keys (meta-core 2.0.0 claims model), one file per writer
# service in this stack. Config — backed up, NOT in backup.exclude.
- ${DATA_ROOT:-/DATA}/AppData/metawatch/keys:/keys:rw
environment:
META_SHARE_URL: "http://metawatch-share:3000"
META_SEARCH_URL: "http://metawatch-search:3000"
# Browser-reachable topbar chips — the AppShield gates below expose the
# co-located meta-share transport and meta-search discovery peers behind
# Yundera OIDC, and these URLs are what the topbar chip clicks open.
META_SHARE_PUBLIC_URL: "https://metawatchshare-${APP_DOMAIN}"
META_SEARCH_PUBLIC_URL: "https://metawatchsearch-${APP_DOMAIN}"
META_WATCH_SYNC_PEERS: "http://metawatch-search:3000"
# This box's own core — subtitle store only (uploads + curated matches).
META_CORE_URL: "http://metawatch-core:9000"
# Own claim-signing key for the subtitle-store claims it writes; generated on first boot if missing.
METAMESH_SIGNING_KEY_FILE: "/keys/metawatch-app.key"
TZ: "$TZ"
depends_on:
metawatch-search:
condition: service_healthy
metawatch-share:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
networks:
- metawatch
# ---------------------------------------------------------------------------
# OIDC perimeter for the MetaWatch UI. Self-registers as an OIDC client with
# the platform's auth-registrar on first login. container_name MUST equal the
# URL prefix (`metawatch`) so the registrar's redirect-URI allowlist matches
# `metawatch-<user>.<domain>`.
# ---------------------------------------------------------------------------
metawatch:
image: ghcr.io/yundera/appshield:2.0.9
cpu_shares: 80
container_name: metawatch
hostname: metawatch
restart: unless-stopped
user: "0:0"
expose:
- 80
environment:
OIDC_REGISTRAR_URL: "http://auth-registrar:9092"
BACKEND_HOST: "metawatch-app"
BACKEND_PORT: "3000"
LISTEN_PORT: "80"
REDIRECT_HOST_SUFFIXES: "${APP_HOST_SUFFIXES:-${APP_DOMAIN},${APP_PUBLIC_IP_DASH}.${APP_IP_ZONE},${APP_PUBLIC_IP_DASH}.sslip.io}"
depends_on:
- metawatch-app
labels:
# ---- metawatch-${APP_DOMAIN} (gateway TLS) ----
caddy_0: metawatch-${APP_DOMAIN}
caddy_0.import: gateway_tls
caddy_0.0_handle: "/health"
caddy_0.0_handle.reverse_proxy: "metawatch-app:3000"
caddy_0.1_handle: ""
caddy_0.1_handle.reverse_proxy: "{{upstreams 80}}"
# ---- metawatch-IP.<IP_ZONE> (gateway TLS) ----
caddy_1: metawatch-${APP_PUBLIC_IP_DASH}.${APP_IP_ZONE}
caddy_1.import: gateway_tls
caddy_1.0_handle: "/health"
caddy_1.0_handle.reverse_proxy: "metawatch-app:3000"
caddy_1.1_handle: ""
caddy_1.1_handle.reverse_proxy: "{{upstreams 80}}"
# ---- metawatch-IP.sslip.io (Let's Encrypt) ----
caddy_2: metawatch-${APP_PUBLIC_IP_DASH}.sslip.io
caddy_2.0_handle: "/health"
caddy_2.0_handle.reverse_proxy: "metawatch-app:3000"
caddy_2.1_handle: ""
caddy_2.1_handle.reverse_proxy: "{{upstreams 80}}"
networks:
- metawatch
- pcs
# ---------------------------------------------------------------------------
# AppShield gates — expose metawatch-share and metawatch-search dashboards
# behind Yundera OIDC SSO so the meta-watch topbar chips (🔎 meta-search /
# 🔗 meta-share) can open them. container_name MUST equal the subdomain
# prefix (auth-registrar derives the OIDC client_id from it), so these use
# collision-free prefixes — the co-located backends are `metawatch-*` and
# keep their internal ports. Same pattern MetaGateway uses for its own
# metagatewaycore / metagatewayshare gates.
# ---------------------------------------------------------------------------
metawatchshare:
image: ghcr.io/yundera/appshield:2.0.9
cpu_shares: 80
container_name: metawatchshare
hostname: metawatchshare
restart: unless-stopped
user: "0:0"
expose: ["80"]
environment:
OIDC_REGISTRAR_URL: "http://auth-registrar:9092"
REDIRECT_HOST_SUFFIXES: "${APP_HOST_SUFFIXES:-${APP_DOMAIN},${APP_PUBLIC_IP_DASH}.${APP_IP_ZONE},${APP_PUBLIC_IP_DASH}.sslip.io}"
BACKEND_HOST: "metawatch-share"
BACKEND_PORT: "3000"
LISTEN_PORT: "80"
labels:
caddy_0: metawatchshare-${APP_DOMAIN}
caddy_0.import: gateway_tls
caddy_0.reverse_proxy: "{{upstreams 80}}"
caddy_1: metawatchshare-${APP_PUBLIC_IP_DASH}.${APP_IP_ZONE}
caddy_1.import: gateway_tls
caddy_1.reverse_proxy: "{{upstreams 80}}"
caddy_2: metawatchshare-${APP_PUBLIC_IP_DASH}.sslip.io
caddy_2.reverse_proxy: "{{upstreams 80}}"
depends_on:
- metawatch-share
networks:
- metawatch
- pcs
metawatchsearch:
image: ghcr.io/yundera/appshield:2.0.9
cpu_shares: 80
container_name: metawatchsearch
hostname: metawatchsearch
restart: unless-stopped
user: "0:0"
expose: ["80"]
environment:
OIDC_REGISTRAR_URL: "http://auth-registrar:9092"
REDIRECT_HOST_SUFFIXES: "${APP_HOST_SUFFIXES:-${APP_DOMAIN},${APP_PUBLIC_IP_DASH}.${APP_IP_ZONE},${APP_PUBLIC_IP_DASH}.sslip.io}"
BACKEND_HOST: "metawatch-search"
BACKEND_PORT: "3000"
LISTEN_PORT: "80"
labels:
caddy_0: metawatchsearch-${APP_DOMAIN}
caddy_0.import: gateway_tls
caddy_0.reverse_proxy: "{{upstreams 80}}"
caddy_1: metawatchsearch-${APP_PUBLIC_IP_DASH}.${APP_IP_ZONE}
caddy_1.import: gateway_tls
caddy_1.reverse_proxy: "{{upstreams 80}}"
caddy_2: metawatchsearch-${APP_PUBLIC_IP_DASH}.sslip.io
caddy_2.reverse_proxy: "{{upstreams 80}}"
depends_on:
- metawatch-search
networks:
- metawatch
- pcs
networks:
metawatch:
name: metawatch
driver: bridge
pcs:
name: ${APP_NET:-pcs}
external: true
x-casaos:
architectures:
- amd64
- arm64
author: Worph Team
category: Media
# `needs-public-ip` is the reserved functional tag: this app publishes a raw
# libp2p TCP port that remote peers must dial inbound. Behind CGNAT or the
# mesh-router tunnel it still installs and starts, but peers cannot reach it.
tags: ["media", "needs-public-ip"]
developer: Aptero Team
description:
en_us: |
**A Stremio-style, video-only player for the decentralized MetaMesh network.**
MetaWatch browses curated rows and filtered search across the MetaMesh
federation and plays back in your browser — the server does zero media
work (flat byte-range streaming, no transcode). Anything without a poster
and a description is hidden, so the wall is always clean.
This is a **self-contained** stack: it ships its own discovery
(meta-search), content transport (meta-share) and a dedicated meta-core
for identity and state. Install it on its own — it does not depend on the
standalone MetaCore app, and coexists with MetaGateway without conflicts.
**Key Features:**
- **Curated Rows + Search** - Popular / trending / top-rated / anime rows, plus federated search
- **Browser-Side Playback** - SegmentPlayer streams via Range/206; the server never transcodes
- **Poster-Gated Wall** - Only titles with poster + description are shown
- **Show Aggregation** - Per-episode records grouped into show pages by tmdbid/imdbid/title
- **My List + Continue Watching** - Persistent, backed by the User Data Layer op-log
- **TV Remote Navigation** - D-pad spatial navigation for the 10-foot UI
- **Federated Discovery** - Joins the public libp2p swarm to find MetaGateway records
fr_fr: |
**Un lecteur vidéo façon Stremio pour le réseau décentralisé MetaMesh.**
MetaWatch parcourt des rangées sélectionnées et une recherche filtrée à
travers la fédération MetaMesh et lit la vidéo dans votre navigateur — le
serveur ne fait aucun travail média (streaming par plages d'octets, sans
transcodage). Tout ce qui n'a ni affiche ni description est masqué.
C'est une stack **autonome** : elle embarque sa propre découverte
(meta-search), son transport de contenu (meta-share) et un meta-core dédié
pour l'identité et l'état. Installez-la seule — elle ne dépend pas de
l'app MetaCore et cohabite avec MetaGateway sans conflit.
**Fonctionnalités Clés:**
- **Rangées + Recherche** - Populaire / tendances / mieux notés / anime, plus recherche fédérée
- **Lecture Côté Navigateur** - SegmentPlayer en Range/206 ; le serveur ne transcode jamais
- **Mur à Affiches** - Seuls les titres avec affiche + description sont montrés
- **Agrégation de Séries** - Épisodes regroupés en pages de série par tmdbid/imdbid/titre
- **Ma Liste + Reprendre** - Persistant, via l'op-log de la couche de données utilisateur
- **Navigation Télécommande** - Navigation spatiale D-pad pour l'interface salon
tagline:
en_us: Stremio-style video player for the decentralized MetaMesh network
fr_fr: Lecteur vidéo façon Stremio pour le réseau décentralisé MetaMesh
title:
en_us: MetaWatch
icon: https://gitea-watch.nsl.sh/metastar/MetaAppStore/raw/branch/main/Apps/MetaWatch/icon.png
screenshot_link:
- https://gitea-watch.nsl.sh/metastar/MetaAppStore/raw/branch/main/Apps/MetaWatch/screenshot-1.png
- https://gitea-watch.nsl.sh/metastar/MetaAppStore/raw/branch/main/Apps/MetaWatch/screenshot-2.png
- https://gitea-watch.nsl.sh/metastar/MetaAppStore/raw/branch/main/Apps/MetaWatch/screenshot-3.png
thumbnail: https://gitea-watch.nsl.sh/metastar/MetaAppStore/raw/branch/main/Apps/MetaWatch/thumbnail.png
index: /
store_app_id: metawatch
main: metawatch
is_uncontrolled: false
tips:
before_install:
en_us: |
**Self-contained — no MetaCore required.** MetaWatch ships its own
discovery, transport and meta-core. Just install and open the dashboard.
**Authentication:** The UI is protected by Yundera's built-in Authelia
via OIDC — sign in with your existing Yundera account on first open.
**Federation ports (optional).** meta-search and meta-share host-publish
their libp2p listeners on TCP **4004** and **4003** so the public IPFS
kad-DHT and other nodes can dial in. Open them inbound if you want this
client to be reachable across networks; same-host discovery (e.g. a
MetaGateway on the same box) works over mDNS without them.
**Content comes from gateways.** To see anything, at least one
MetaGateway (or another MetaShare archivist) must be reachable on the
swarm. Install **MetaGateway** for the torznab/Prowlarr + Tribler feed.
**After installation:**
1. Open `https://metawatch-$APP_DOMAIN/`
2. Sign in via the Authelia portal
3. Browse the rows or search; results stream in as gateways respond
# Maison (Yundera dashboard) configuration. Read in preference to x-casaos.
# schema_version 2 is required for `folders`: an older Maison refuses the app
# outright rather than starting it without its directories.
x-compose-app:
schema_version: 2
# Created and chowned by Maison before any image is pulled, before any hook
# runs and before the containers start — on the first boot and every one after.
# Compose would otherwise create a missing bind source as a root-owned dir.
folders:
- path: /DATA/AppData/metawatch/metacore
user: $PUID
group: $PGID
# meta-core 2.0.0 rebuildable index cache (default
# /meta-core/db-cache/cache.db). Excluded from backups below.
- path: /DATA/AppData/metawatch/metacore/db-cache
user: $PUID
group: $PGID
- path: /DATA/AppData/metawatch/search-data
user: $PUID
group: $PGID
- path: /DATA/AppData/metawatch/metashare-config
user: $PUID
group: $PGID
- path: /DATA/AppData/metawatch/share-data
user: $PUID
group: $PGID
- path: /DATA/AppData/metawatch/share-ipfs-state
user: $PUID
group: $PGID
- path: /DATA/AppData/metawatch/data
user: $PUID
group: $PGID
- path: /DATA/AppData/metawatch/keys
user: $PUID
group: $PGID
# Derived data only: meta-core rebuilds its index cache from the truth file
# (metacore/db/meta-core.db, which IS backed up). keys/ stays backed up.
backup:
exclude:
- metacore/db-cache/
# Same string as the caddy_0 label, so the tile link and the route agree.
webui-host: metawatch-${domain}
webui-path: "/"