feat: optional YouTube Music source (cookies volume + wizard step)

Adds a step_youtube wizard prompt (enable + cookies host folder), the @YOUTUBE_VOLUME@ token in templates/compose/backend.yml substituted in compose_gen, a YOUTUBE_ENABLED/cookies env block, ensure_youtube_dir in lifecycle, en/ru strings, and a README step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Senko-san
2026-06-14 14:04:49 +03:00
parent 4108121984
commit 3a63ced4d4
8 changed files with 89 additions and 1 deletions
+3
View File
@@ -26,6 +26,9 @@ That's it. The wizard walks you through:
domain), or no bundled proxy when you publish ports / run your own
8. The first administrator account
9. An optional ML service URL
10. Metadata enrichment — an optional AcoustID key + contact email
11. YouTube Music source — enable search/download, with an optional folder for a
yt-dlp `cookies.txt` (mounted into the backend for restricted items)
Then it generates the config, pulls images, migrates, seeds the admin, starts
everything, and waits for the API health check before declaring success.
+20
View File
@@ -39,6 +39,7 @@ CFG_ADMIN_CREATE="no"; CFG_ADMIN_USER=""; CFG_ADMIN_PASS=""
CFG_ML_URL=""
CFG_ACOUSTID_KEY=""
CFG_MUSICBRAINZ_OWNER_EMAIL=""
CFG_YOUTUBE="no"; CFG_YOUTUBE_COOKIES_HOST_PATH="./data/youtube"
CFG_JWT_SECRET=""
# ==========================================================================
@@ -195,6 +196,19 @@ step_enrichment() {
ui_input "$(t musicbrainz_email_prompt)" "" v_email_opt; CFG_MUSICBRAINZ_OWNER_EMAIL="$UI_VALUE"
}
step_youtube() {
ui_title "$(t step_youtube)"
ui_dim "$(t youtube_note)"
if ui_yesno "$(t youtube_q)" "yes"; then
CFG_YOUTUBE="yes"
ui_dim "$(t youtube_cookies_note)"
ui_input "$(t youtube_cookies_prompt)" "./data/youtube" v_nonempty
CFG_YOUTUBE_COOKIES_HOST_PATH="$UI_VALUE"
else
CFG_YOUTUBE="no"
fi
}
access_url() {
if [[ "$CFG_WEBUI" == "yes" ]]; then
if [[ "$CFG_PROXY" == "yes" ]]; then
@@ -222,6 +236,7 @@ step_summary() {
printf ' %-12s %s\n' "$(t summary_db):" "$dbl"
printf ' %-12s %s\n' "$(t summary_redis):" "$rdl"
printf ' %-12s %s\n' "$(t summary_storage):" "$stl"
printf ' %-12s %s\n' "$(t summary_youtube):" "$([[ "$CFG_YOUTUBE" == yes ]] && t enabled || t disabled)"
printf ' %-12s %s\n' "$(t summary_access):" "$(access_url)"
echo
ui_yesno "$(t confirm_start)" "yes" || { ui_warn "$(t aborted)"; exit 0; }
@@ -232,6 +247,10 @@ step_done() {
ui_ok "$(t done_url): $(access_url)"
ui_info "$(t done_config): ${ENV_FILE}"
[[ "$CFG_ADMIN_CREATE" == "yes" ]] && ui_info "$(t done_admin_login): ${CFG_ADMIN_USER}"
if [[ "$CFG_YOUTUBE" == "yes" ]]; then
ui_info "$(t done_youtube_title)"
ui_dim "$(t done_youtube_cookies "${CFG_YOUTUBE_COOKIES_HOST_PATH}/cookies.txt")"
fi
ui_dim "$(t done_commands)"
}
@@ -247,6 +266,7 @@ run_wizard() {
step_admin
step_ml
step_enrichment
step_youtube
gen_hex 32; CFG_JWT_SECRET="$SECRET"
+12
View File
@@ -106,6 +106,16 @@ MSG[acoustid_prompt]="AcoustID API key (leave blank to use embedded tags only)"
MSG[musicbrainz_note]="MusicBrainz/AcoustID require a contact email in their User-Agent or they may throttle requests."
MSG[musicbrainz_email_prompt]="Contact email for MusicBrainz/AcoustID (leave blank to use the project's default)"
# -- youtube source --------------------------------------------------------
MSG[step_youtube]="YouTube Music (search + download)"
MSG[youtube_note]="Lets users search YouTube Music and download tracks into the library (via yt-dlp). Search and most downloads work with no setup."
MSG[youtube_q]="Enable the YouTube Music source?"
MSG[youtube_cookies_note]="Optional: a browser cookies file (Netscape format) lets yt-dlp fetch age/region-restricted items. The folder below is mounted into the backend; drop your cookies.txt in it any time (leave default if unsure)."
MSG[youtube_cookies_prompt]="Host folder to mount for the cookies file"
MSG[summary_youtube]="YouTube"
MSG[done_youtube_title]="YouTube downloads are enabled."
MSG[done_youtube_cookies]="For restricted items, place an exported cookies.txt in: %s"
# -- summary / run ---------------------------------------------------------
MSG[summary_title]="Summary (secrets hidden)"
MSG[summary_services]="Services"
@@ -117,6 +127,8 @@ MSG[summary_access]="Access"
MSG[confirm_start]="Generate config and start now?"
MSG[embedded]="built-in"
MSG[external]="external"
MSG[enabled]="enabled"
MSG[disabled]="disabled"
MSG[pull_images]="Pulling images (%s)..."
MSG[pull_hint]="If pulls fail with 'unauthorized', run 'docker login git.ollyhearn.ru' first (private registry)."
+12
View File
@@ -106,6 +106,16 @@ MSG[acoustid_prompt]="API-ключ AcoustID (пусто — только вст
MSG[musicbrainz_note]="MusicBrainz/AcoustID требуют контактный email в User-Agent, иначе запросы могут ограничиваться (throttling)."
MSG[musicbrainz_email_prompt]="Контактный email для MusicBrainz/AcoustID (пусто — использовать значение по умолчанию)"
# -- youtube source --------------------------------------------------------
MSG[step_youtube]="YouTube Music (поиск + скачивание)"
MSG[youtube_note]="Позволяет искать в YouTube Music и скачивать треки в библиотеку (через yt-dlp). Поиск и большинство загрузок работают без настройки."
MSG[youtube_q]="Включить источник YouTube Music?"
MSG[youtube_cookies_note]="Опционально: файл cookies браузера (формат Netscape) позволяет yt-dlp скачивать контент с возрастными/региональными ограничениями. Папка ниже монтируется в backend; положите cookies.txt в неё в любой момент (если не уверены — оставьте по умолчанию)."
MSG[youtube_cookies_prompt]="Папка на хосте для монтирования файла cookies"
MSG[summary_youtube]="YouTube"
MSG[done_youtube_title]="Скачивание из YouTube включено."
MSG[done_youtube_cookies]="Для контента с ограничениями положите экспортированный cookies.txt в: %s"
# -- summary / run ---------------------------------------------------------
MSG[summary_title]="Сводка (секреты скрыты)"
MSG[summary_services]="Сервисы"
@@ -117,6 +127,8 @@ MSG[summary_access]="Доступ"
MSG[confirm_start]="Сгенерировать конфиг и запустить сейчас?"
MSG[embedded]="встроенный"
MSG[external]="внешний"
MSG[enabled]="включено"
MSG[disabled]="выключено"
MSG[pull_images]="Скачивание образов (%s)..."
MSG[pull_hint]="Если pull падает с 'unauthorized', выполните 'docker login git.ollyhearn.ru' (приватный регистри)."
+8
View File
@@ -31,6 +31,13 @@ generate_compose() {
webui_ports=" ports:${nl} - \"\${WEBUI_PORT}:80\""
fi
# YouTube cookies volume: mounted into api+worker only when the source is
# enabled. The host path comes from .env.deploy (YOUTUBE_COOKIES_HOST_PATH).
local youtube_volume=""
if [[ "$CFG_YOUTUBE" == "yes" ]]; then
youtube_volume=" - \${YOUTUBE_COOKIES_HOST_PATH}:/data/youtube"
fi
# Caddy publish ports: 80/443 with a domain (auto-HTTPS), else plain HTTP.
if [[ "$CFG_HTTPS" == "yes" ]]; then
caddy_ports=" - \"80:80\"${nl} - \"443:443\""
@@ -41,6 +48,7 @@ generate_compose() {
# -- assemble ---------------------------------------------------------
local backend webui caddy
backend="$(_frag backend.yml)"
backend="${backend//@YOUTUBE_VOLUME@/$youtube_volume}"
backend="${backend//@API_PORTS@/$api_ports}"
backend="${backend//@API_DEPENDS@/$api_depends}"
backend="${backend//@WORKER_DEPENDS@/$worker_depends}"
+19
View File
@@ -93,5 +93,24 @@ generate_env() {
} >>"$ENV_FILE"
fi
# -- YouTube Music source ---------------------------------------------
# COOKIES_HOST_PATH is consumed by the compose volume; COOKIES_PATH is the
# in-container path the backend reads (only used when the file is present).
if [[ "${CFG_YOUTUBE:-no}" == "yes" ]]; then
{
echo ""
echo "# -- YouTube Music source -----------------------------------------------"
echo "YOUTUBE_ENABLED=true"
echo "YOUTUBE_COOKIES_HOST_PATH=${CFG_YOUTUBE_COOKIES_HOST_PATH}"
echo "YOUTUBE_COOKIES_PATH=/data/youtube/cookies.txt"
} >>"$ENV_FILE"
else
{
echo ""
echo "# -- YouTube Music source (disabled) ------------------------------------"
echo "YOUTUBE_ENABLED=false"
} >>"$ENV_FILE"
fi
chmod 600 "$ENV_FILE"
}
+13 -1
View File
@@ -36,6 +36,17 @@ ensure_media_dir() {
mkdir -p "$p"
}
ensure_youtube_dir() {
# YOUTUBE_COOKIES_HOST_PATH is only present when the YouTube source is
# enabled; create the folder so the bind mount has a real host directory
# (the user drops cookies.txt into it later).
local p
p="$(grep -E '^YOUTUBE_COOKIES_HOST_PATH=' "$ENV_FILE" | cut -d= -f2-)"
[[ -n "$p" ]] || return 0
[[ "$p" = /* ]] || p="${BOOTSTRAP_DIR}/${p#./}"
mkdir -p "$p"
}
lifecycle_pull() {
ui_info "$(t pull_images "$(grep -E '^MCMA_IMAGE_TAG=' "$ENV_FILE" | cut -d= -f2-)")"
ui_dim "$(t pull_hint)"
@@ -64,6 +75,7 @@ sys.exit(1)
lifecycle_start() {
local create_admin="${1:-no}"
ensure_media_dir
ensure_youtube_dir
lifecycle_pull
local deps; deps="$(backing_services)"
@@ -111,7 +123,7 @@ lifecycle_update() {
ui_ok "$(t done_title)"
}
lifecycle_up() { ensure_media_dir; dc up -d; }
lifecycle_up() { ensure_media_dir; ensure_youtube_dir; dc up -d; }
lifecycle_down() { dc down; }
lifecycle_logs() { dc logs -f --tail=100; }
+2
View File
@@ -6,6 +6,7 @@
volumes:
- ${MEDIA_HOST_PATH}:${MEDIA_PATH}
- transcode_cache:${TRANSCODE_CACHE_PATH}
@YOUTUBE_VOLUME@
@API_PORTS@
@API_DEPENDS@
@@ -17,4 +18,5 @@
volumes:
- ${MEDIA_HOST_PATH}:${MEDIA_PATH}
- transcode_cache:${TRANSCODE_CACHE_PATH}
@YOUTUBE_VOLUME@
@WORKER_DEPENDS@