name: metasort services: # Backend service. Reachable on the pcs network as `metasort-app`. # Public ingress goes through the `metasort` hash-lock proxy below. metasort-app: image: ghcr.io/worph/meta-sort:1.0.11 container_name: metasort-app hostname: metasort-app restart: unless-stopped cpu_shares: 20 mem_limit: 2g memswap_limit: 2g expose: - 80 volumes: # Shared infrastructure state (leader election, Redis dump, service discovery) # Owned by MetaCore; mounted rw here for service registration + leader reads. # 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. # Application cache storage - ${DATA_ROOT:-/DATA}/AppData/metasort/cache:/data/cache # Docker socket for container plugin management - /var/run/docker.sock:/var/run/docker.sock:ro # Config files from this app's seed tree (Apps/MetaSort/seed/config/), # which Maison writes only when absent — an operator's edits survive # updates. The image ships no plugins.yml, so without it a stock install # spawns ZERO plugins; the supervisord override drops the image's bundled # follower meta-core, which restart-loops against the standalone MetaCore # app; the nginx conf is the image's stock one, mounted so a box can tune # it (watch.nsl.sh 503s /api/stats, which sweeps every record). - ${DATA_ROOT:-/DATA}/AppData/metasort/config/plugins.yml:/app/plugins.yml:ro - ${DATA_ROOT:-/DATA}/AppData/metasort/config/supervisord-metasort.conf:/etc/supervisor/conf.d/supervisord.conf:ro - ${DATA_ROOT:-/DATA}/AppData/metasort/config/nginx-metasort.conf:/etc/nginx/nginx.conf:ro # Note: no /files mounts — files are accessed via WebDAV from MetaCore # (Architecture V3). MetaCore is the single source of truth for media files. environment: - NODE_ENV=production - CACHE_FOLDER_PATH=/data/cache - SERVICE_NAME=meta-sort - SERVICE_VERSION=1.0.0 - BASE_URL=https://metasort-$APP_DOMAIN - TMDB_API_KEY= - TMDB_LANGUAGE=en-US - TZ=$TZ # Container plugin configuration - CONTAINER_CALLBACK_URL=http://metasort-app/api/plugins/callback - CONTAINER_NETWORK=pcs # meta-core URL handed to plugin containers. Port 3000, not 80: the baked # nginx has no /meta/ location, so plugin writes through :80 would 404. # (The code default, http://meta-sort, names no host on this network.) - CONTAINER_META_CORE_URL=http://metasort-app:3000 # Compose project label stamped on the plugin containers meta-sort spawns, # so they group under one stack instead of floating loose. - PLUGIN_STACK_NAME=metasort-plugins # Note: Runs as root for nginx, Redis, and file management user: 0:0 networks: - pcs # OIDC perimeter for the MetaSort dashboard. Self-registers as an OIDC client # with the platform's auth-registrar on first login (no secrets to manage). # # Naming: container_name MUST equal the URL-prefix of the public hostname so # the registrar's redirect-URI allowlist accepts `metasort-.`. metasort: image: ghcr.io/yundera/appshield:2.0.9 cpu_shares: 80 container_name: metasort hostname: metasort restart: unless-stopped user: "0:0" expose: - 80 environment: OIDC_REGISTRAR_URL: "http://auth-registrar:9092" BACKEND_HOST: "metasort-app" BACKEND_PORT: "80" LISTEN_PORT: "80" REDIRECT_HOST_SUFFIXES: "${APP_DOMAIN},${APP_PUBLIC_IP_DASH}.nip.io,${APP_PUBLIC_IP_DASH}.sslip.io" depends_on: - metasort-app labels: # Only /health bypasses hash-lock — internal cluster traffic (plugin # callbacks, /meta/*, /webdav/*, /file/cid) reaches metasort-app via # the pcs network and never traverses the public proxy, so no public # bypass is required for those paths. # ---- metasort-${APP_DOMAIN} (gateway TLS) ---- caddy_0: metasort-${APP_DOMAIN} caddy_0.import: gateway_tls caddy_0.0_handle: "/health" caddy_0.0_handle.reverse_proxy: "metasort-app:80" caddy_0.1_handle: "" caddy_0.1_handle.reverse_proxy: "{{upstreams 80}}" # ---- metasort-IP.nip.io (gateway TLS) ---- caddy_1: metasort-${APP_PUBLIC_IP_DASH}.nip.io caddy_1.import: gateway_tls caddy_1.0_handle: "/health" caddy_1.0_handle.reverse_proxy: "metasort-app:80" caddy_1.1_handle: "" caddy_1.1_handle.reverse_proxy: "{{upstreams 80}}" # ---- metasort-IP.sslip.io (Let's Encrypt) ---- caddy_2: metasort-${APP_PUBLIC_IP_DASH}.sslip.io caddy_2.0_handle: "/health" caddy_2.0_handle.reverse_proxy: "metasort-app:80" caddy_2.1_handle: "" caddy_2.1_handle.reverse_proxy: "{{upstreams 80}}" networks: - pcs networks: pcs: name: ${APP_NET:-pcs} external: true x-casaos: architectures: - amd64 - arm64 author: Worph Team category: Media developer: Aptero Team description: en_us: | **Smart metadata extraction and file indexing for massive media libraries.** MetaSort is the brain of the MetaMesh ecosystem. It watches your media folders, extracts metadata using intelligent plugins, and stores everything in a distributed database for other services to use. **Key Features:** - **Plugin-Based Processing** - Containerized plugins for FFmpeg analysis, TMDB enrichment, filename parsing, and more - **Smart Metadata Extraction** - Automatically extracts titles, seasons, episodes, quality, language, codecs - **Duplicate Detection** - Finds exact duplicates and different quality versions of the same content - **Real-time Monitoring** - Watches folders and instantly processes new files - **Built for Scale** - Handles collections up to 900k+ files and 100TB+ - **Leader Election** - Distributed architecture with automatic failover **Requires:** MetaCore (install first) **Works with:** MetaFuse (virtual filesystem), MetaStremio (streaming) **Dashboard Features:** File monitoring, plugin status, duplicate detection, metadata browser, processing queue fr_fr: | **Extraction intelligente de metadonnees et indexation de fichiers pour d'immenses bibliotheques multimedia.** MetaSort est le cerveau de l'ecosysteme MetaMesh. Il surveille vos dossiers multimedia, extrait les metadonnees a l'aide de plugins intelligents et stocke tout dans une base de donnees distribuee. **Fonctionnalites Cles:** - **Traitement par Plugins** - Plugins conteneurises pour analyse FFmpeg, enrichissement TMDB, parsing de noms de fichiers - **Extraction Intelligente** - Extrait automatiquement titres, saisons, episodes, qualite, langue, codecs - **Detection de Doublons** - Trouve les doublons exacts et differentes versions de qualite - **Surveillance Temps Reel** - Surveille les dossiers et traite instantanement les nouveaux fichiers - **Concu pour l'Echelle** - Gere des collections jusqu'a 900k+ fichiers et 100TB+ **Necessite:** MetaCore (installer en premier) **Fonctionne avec:** MetaFuse (systeme de fichiers virtuel), MetaStremio (streaming) tagline: en_us: Smart metadata extraction engine - Index and organize massive media libraries with intelligent plugins fr_fr: Moteur d'extraction de metadonnees - Indexez et organisez d'immenses bibliotheques avec des plugins intelligents title: en_us: MetaSort icon: https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaSort/icon.png screenshot_link: - https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaSort/screenshot-1.png - https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaSort/screenshot-2.png - https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaSort/screenshot-3.png thumbnail: https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaSort/thumbnail.png index: / store_app_id: metasort main: metasort is_uncontrolled: false tips: before_install: en_us: | **Requires MetaCore** - Install MetaCore first. MetaSort connects to MetaCore for metadata storage. The dashboard is protected by Yundera's built-in Authelia via OIDC — sign in with your existing Yundera account. # 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/metacore/meta-core user: $PUID group: $PGID - path: /DATA/AppData/metasort/cache user: $PUID group: $PGID # Same string as the caddy_0 label, so the tile link and the route agree. webui-host: metasort-${domain} webui-path: "/"