Invidious pozwala oglądać YouTube bez reklam i śledzenia, ale niczego nie przechowuje. Gdy autor usunie film, znika on także z Invidious. Ten wpis opisuje dwa narzędzia, które zapisują filmy na własnym dysku:

  • MeTube pobiera pojedynczy film lub playlistę z wklejonego linku.
  • Tube Archivist subskrybuje kanały i sam pobiera nowe filmy, tworząc lokalne archiwum z odtwarzaczem.

YouTube często zmienia player i zabezpieczenia przed botami, przez co pobieranie co jakiś czas przestaje działać. Oba narzędzia korzystają z yt-dlp, którego poprawki pojawiają się zwykle w ciągu kilku dni. Dlatego te kontenery trzeba regularnie aktualizować, najlepiej automatycznie przez Watchtower .

MeTube

MeTube rozwiązuje prosty problem: chcę mieć konkretny film na dysku, np. wykład do obejrzenia offline albo utwór jako plik audio. Wklejam link w przeglądarce, wybieram jakość i format, a plik ląduje w katalogu na serwerze. Działa nie tylko z YouTube, ale też z kilkuset innymi serwisami.

MeTube — ekran główny z formularzem pobierania i listą pobranych plików
services:
  metube:
    image: alexta69/metube
    container_name: metube
    restart: unless-stopped
    volumes:
      - ./downloads:/downloads
    networks: [proxy]

networks:
  proxy:
    external: true

Tube Archivist

Tube Archivist to mini YouTube działający lokalnie. Subskrybuję w nim kanały i playlisty, a aplikacja według harmonogramu synchronizuje je z YouTube i pobiera nowe filmy na dysk serwera. Potem całe archiwum przegląda się w interfejsie podobnym do YouTube: strona główna z najnowszymi filmami, widoki kanałów i playlist, odtwarzacz, wyszukiwarka, historia obejrzanych. Filmy zostają na dysku nawet wtedy, gdy autor usunie je z YouTube.

Tube Archivist — strona główna z ostatnio pobranymi filmami

Konfiguracja składa się z trzech kontenerów:

Kontenery Tube Archivist
KontenerFunkcja
tubearchivistAplikacja webowa z interfejsem i odtwarzaczem. Planuje synchronizację kanałów i pobiera filmy przez yt-dlp.
archivist-redisRedis z kolejką zadań: pobierania, skanowania subskrypcji i zadań harmonogramu.
archivist-esElasticsearch z indeksem metadanych filmów, kanałów i playlist. Na nim działa wyszukiwarka.
services:
  tubearchivist:
    image: bbilly1/tubearchivist:latest
    container_name: tubearchivist
    restart: unless-stopped
    env_file: .env
    environment:
      ES_URL: http://archivist-es:9200
      REDIS_CON: redis://archivist-redis:6379
      HOST_UID: "1000"
      HOST_GID: "1000"
      TA_HOST: http://tubearchivist.mini-1.lan
      TA_USERNAME: robert
      TZ: Europe/Warsaw
    volumes:
      - /home/robert/data/tubearchivist/media:/youtube
      - /home/robert/data/tubearchivist/cache:/cache
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/api/health/"]
      interval: 2m
      timeout: 10s
      retries: 3
      start_period: 30s
    depends_on:
      - archivist-es
      - archivist-redis
    networks: [proxy, default]
    deploy:
      resources:
        limits:
          memory: 2G
          cpus: "2.0"

  archivist-redis:
    image: redis:7-alpine
    container_name: archivist-redis
    restart: unless-stopped
    volumes:
      - /home/robert/data/tubearchivist/redis:/data
    networks: [default]
    deploy:
      resources:
        limits:
          memory: 256M

  archivist-es:
    image: bbilly1/tubearchivist-es:latest
    container_name: archivist-es
    restart: unless-stopped
    environment:
      ELASTIC_PASSWORD: ${ELASTIC_PASSWORD}
      ES_JAVA_OPTS: "-Xms1g -Xmx1g"
      xpack.security.enabled: "true"
      discovery.type: single-node
      path.repo: /usr/share/elasticsearch/data/snapshot
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - /home/robert/data/tubearchivist/es:/usr/share/elasticsearch/data
    networks: [default]
    deploy:
      resources:
        limits:
          memory: 2G
          cpus: "1.0"

networks:
  proxy:
    external: true

Hasła trzymane są w pliku .env poza repozytorium:

TA_PASSWORD=<haslo-do-konta-w-tube-archivist>
ELASTIC_PASSWORD=<losowy-ciag>     # openssl rand -hex 16

Elasticsearch w kontenerze działa jako użytkownik o UID 1000, więc katalog z bind mounta musi do niego należeć. Inaczej archivist-es przerwie start z błędem uprawnień:

mkdir -p /home/robert/data/tubearchivist/es
sudo chown -R 1000:1000 /home/robert/data/tubearchivist/es
Zmienne środowiskowe Tube Archivist
ZmiennaOpis
ES_URLAdres Elasticsearch, tu http://archivist-es:9200.
REDIS_CONAdres Redisa, tu redis://archivist-redis:6379.
TA_HOSTPełny adres, pod którym aplikacja jest otwierana w przeglądarce. Inny adres kończy się błędem przy logowaniu.
TA_USERNAMELogin konta tworzonego przy pierwszym starcie.
TA_PASSWORDHasło tego konta (w .env). Później zmienia się je już w panelu aplikacji.
ELASTIC_PASSWORDHasło do Elasticsearch (w .env). Czytają je oba kontenery: archivist-es i tubearchivist.
HOST_UID, HOST_GIDWłaściciel pobranych plików na hoście.
TZStrefa czasowa, według której działa harmonogram synchronizacji.
ES_JAVA_OPTSRozmiar heapu Elasticsearch, tu 1 GiB.
discovery.type, xpack.security.enabledPojedynczy węzeł Elasticsearch z włączonym uwierzytelnianiem.

Katalog media/ rośnie szybko, bo każdy subskrybowany kanał dokłada pełne pliki wideo. Warto od razu ograniczyć w ustawieniach maksymalną rozdzielczość i liczbę filmów pobieranych z kanału.

Linki