This commit is contained in:
2025-12-02 14:56:45 +01:00
parent a0cb7643ed
commit b0c017ca91
5 changed files with 91 additions and 7 deletions

1
.gitignore vendored
View File

@@ -47,3 +47,4 @@ venv.bak/
config/ config/
downloads/ downloads/
*.log *.log
music/

View File

@@ -2,6 +2,9 @@ import threading
import queue import queue
import logging import logging
import sys import sys
import os
import shutil
import pathlib
from typing import Dict, List from typing import Dict, List
from tidal_dl_ng.download import Download from tidal_dl_ng.download import Download
from tidal_dl_ng.config import Settings, Tidal from tidal_dl_ng.config import Settings, Tidal
@@ -175,6 +178,10 @@ class DownloadManager:
if not tidal.session.check_login(): if not tidal.session.check_login():
raise Exception("Not logged in") raise Exception("Not logged in")
# Move any existing albums to destination before starting new download
if task["type"] in ["album", "artist"]:
self._move_albums_to_destination()
# Use environment variable for download path, default to /app/downloads # Use environment variable for download path, default to /app/downloads
import os import os
settings.data.download_base_path = os.getenv("DOWNLOAD_PATH", "/app/downloads") settings.data.download_base_path = os.getenv("DOWNLOAD_PATH", "/app/downloads")
@@ -249,7 +256,7 @@ class DownloadManager:
task["current_item"] = track.name task["current_item"] = track.name
downloader.item( downloader.item(
file_template=settings.data.format_track, file_template=settings.data.format_album,
media=track, media=track,
quality_audio=settings.data.quality_audio, quality_audio=settings.data.quality_audio,
is_parent_album=True, is_parent_album=True,
@@ -298,12 +305,53 @@ class DownloadManager:
task["current_item"] = track.name task["current_item"] = track.name
downloader.item( downloader.item(
file_template=settings.data.format_track, file_template=settings.data.format_album,
media=track, media=track,
quality_audio=settings.data.quality_audio, quality_audio=settings.data.quality_audio,
is_parent_album=True, # Treat as album tracks to keep folder structure is_parent_album=True, # Treat as album tracks to keep folder structure
list_position=track.track_num, # Use original track num list_position=track.track_num, # Use original track num
list_total=track.album.num_tracks list_total=track.album.num_tracks
) )
# Move albums to destination after download completes
if task["type"] in ["album", "artist"]:
self._move_albums_to_destination()
def _move_albums_to_destination(self):
"""Move all albums from downloads/Albums/ to destination path."""
dest_base = os.getenv("ALBUM_DESTINATION_PATH")
if not dest_base:
return
download_base = os.getenv("DOWNLOAD_PATH", "/app/downloads")
albums_path = pathlib.Path(download_base) / "Albums"
if not albums_path.exists():
return
try:
moved_count = 0
# Move all album folders to destination
for album_dir in albums_path.iterdir():
if not album_dir.is_dir():
continue
# Destination path: [ALBUM_DESTINATION_PATH]/[Album folder name]
dest_album_path = pathlib.Path(dest_base) / album_dir.name
logger.info(f"Moving {album_dir.name} to {dest_album_path}")
# Remove destination if it exists
if dest_album_path.exists():
shutil.rmtree(dest_album_path)
# Move the album
shutil.move(str(album_dir), str(dest_album_path))
moved_count += 1
if moved_count > 0:
logger.info(f"Successfully moved {moved_count} album(s) to {dest_base}")
except Exception as e:
logger.error(f"Failed to move albums: {e}")

View File

@@ -14,13 +14,11 @@
<div class="card"> <div class="card">
<h2>Search</h2> <h2>Search</h2>
<input type="text" id="query" placeholder="Search for tracks, albums..." onkeypress="handleEnter(event)"> <input type="text" id="query" placeholder="Search for artists, albums..." onkeypress="handleEnter(event)">
<select id="type" <select id="type"
style="padding: 10px; background: #2d2d2d; color: white; border: 1px solid #333; border-radius: 4px;"> style="padding: 10px; background: #2d2d2d; color: white; border: 1px solid #333; border-radius: 4px;">
<option value="track">Track</option>
<option value="album">Album</option>
<option value="artist">Artist</option> <option value="artist">Artist</option>
<option value="playlist">Playlist</option> <option value="album">Album</option>
</select> </select>
<button onclick="search()">Search</button> <button onclick="search()">Search</button>
</div> </div>

35
docker-compose.local.yml Normal file
View File

@@ -0,0 +1,35 @@
services:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
env_file:
- .env
ports:
- "8002:8080" # Expose tidal-dl-web port via gluetun
restart: always
web:
build: .
container_name: tidal-dl-web
network_mode: "service:gluetun"
volumes:
- ./downloads:/app/downloads
- ./config:/app/config
- ./music:/app/music
# Mount source for development
- ./app:/app/app
command: uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
environment:
- PYTHONUNBUFFERED=1
- DOWNLOAD_PATH=/app/downloads
- XDG_CONFIG_HOME=/app/config
- ALBUM_DESTINATION_PATH=${ALBUM_DESTINATION_PATH:-}
restart: unless-stopped
depends_on:
gluetun:
condition: service_healthy
networks:
proxy:
external: true

View File

@@ -33,6 +33,7 @@ services:
volumes: volumes:
- ./downloads:/app/downloads - ./downloads:/app/downloads
- ./config:/app/config - ./config:/app/config
- ./music:/app/music
# Mount source for development # Mount source for development
- ./app:/app/app - ./app:/app/app
command: uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload command: uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
@@ -40,6 +41,7 @@ services:
- PYTHONUNBUFFERED=1 - PYTHONUNBUFFERED=1
- DOWNLOAD_PATH=/app/downloads - DOWNLOAD_PATH=/app/downloads
- XDG_CONFIG_HOME=/app/config - XDG_CONFIG_HOME=/app/config
- ALBUM_DESTINATION_PATH=${ALBUM_DESTINATION_PATH:-}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
gluetun: gluetun: