diff --git a/.gitignore b/.gitignore index ac87c29..37e21f9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ speed-measure-plugin*.json # IDEs and editors /ui/.idea +.idea/ .project .classpath .c9/ @@ -44,6 +45,12 @@ testem.log .DS_Store Thumbs.db - +# Python __pycache__ .venv + +# Environment files +.env + +# Downloads directory +downloads/ diff --git a/app/dl_formats.py b/app/dl_formats.py index 5640ca4..5147451 100644 --- a/app/dl_formats.py +++ b/app/dl_formats.py @@ -28,7 +28,9 @@ def get_format(format: str, quality: str) -> str: if format in AUDIO_FORMATS: # Audio quality needs to be set post-download, set in opts - return f"bestaudio[ext={format}]/bestaudio/best" + # Use a more robust format string that ensures audio-only download + # and has better fallbacks + return f"bestaudio[ext={format}]/bestaudio[ext=m4a]/bestaudio[ext=mp3]/bestaudio/best[ext!=webm]/best[ext!=webm]" if format in ("mp4", "any"): if quality == "audio": diff --git a/app/ytdl.py b/app/ytdl.py index 5be7a29..b71baeb 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -97,7 +97,7 @@ class Download: 'outtmpl': { "default": self.output_template, "chapter": self.output_template_chapter }, 'format': self.format, 'socket_timeout': 30, - 'ignore_no_formats_error': True, + 'ignore_no_formats_error': False, # Changed to False to get better error messages 'progress_hooks': [put_status], 'postprocessor_hooks': [put_status_postprocessor], **self.ytdl_opts, @@ -281,7 +281,7 @@ class DownloadQueue: self.seq_lock = asyncio.Lock() elif self.config.DOWNLOAD_MODE == 'limited': self.semaphore = asyncio.Semaphore(int(self.config.MAX_CONCURRENT_DOWNLOADS)) - + self.done.load() async def __import_queue(self): @@ -343,6 +343,13 @@ class DownloadQueue: 'ignore_no_formats_error': True, 'noplaylist': playlist_strict_mode, 'paths': {"home": self.config.DOWNLOAD_DIR, "temp": self.config.TEMP_DIR}, + 'cookiefile': None, # Allow cookie usage if available + 'age_limit': None, # Don't restrict by age + 'geo_bypass': True, # Attempt to bypass geographic restrictions + 'geo_bypass_country': None, # Let yt-dlp choose the best bypass method + 'extractor_retries': 3, # Retry extraction up to 3 times + 'youtube_include_dash_manifest': True, # Include DASH formats for YouTube Music + 'youtube_skip_dash_manifest': False, # Don't skip DASH manifest **self.config.YTDL_OPTIONS, **({'impersonate': yt_dlp.networking.impersonate.ImpersonateTarget.from_str(self.config.YTDL_OPTIONS['impersonate'])} if 'impersonate' in self.config.YTDL_OPTIONS else {}), }).extract_info(url, download=False) @@ -420,7 +427,6 @@ class DownloadQueue: ytdl_options = dict(self.config.YTDL_OPTIONS) if playlist_item_limit > 0: log.info(f'playlist limit is set. Processing only first {playlist_item_limit} entries') - ytdl_options['playlistend'] = playlist_item_limit if auto_start is True: download = Download(dldirectory, self.config.TEMP_DIR, output, output_chapter, quality, format, ytdl_options, dl) self.queue.put(download) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml new file mode 100644 index 0000000..696f45f --- /dev/null +++ b/docker-compose-dev.yml @@ -0,0 +1,27 @@ +# MeTube - Self-hosted YouTube downloader with web UI +# This setup allows you to modify the code and see changes reflected in the running container + +services: + metube: + build: . + container_name: MeTube + restart: unless-stopped + ports: + - "8081:8081" + environment: + # Tells MeTube to use separate directories for video and audio downloads + - DOWNLOAD_DIR=/downloads/video + - AUDIO_DOWNLOAD_DIR=/downloads/audio + # Sensible filename templates for downloads + - OUTPUT_TEMPLATE=%(artist,uploader)s/%(artist,uploader)s - %(title)s%(genre& [%s]|)s (%(release_date>%Y-%m-%d,upload_date>%Y-%m-%d)s).%(ext)s + - OUTPUT_TEMPLATE_PLAYLIST=%(playlist_title)s/%(artist,uploader)s - %(title)s%(genre& [%s]|)s (%(release_date>%Y-%m-%d,upload_date>%Y-%m-%d)s).%(ext)s + # Development environment + - PYTHONUNBUFFERED=1 + volumes: + # Maps your local paths to the container paths + - ${VIDEO_DOWNLOAD_DIR:-./downloads/video}:/downloads/video + - ${AUDIO_DOWNLOAD_DIR:-./downloads/audio}:/downloads/audio + # Mount source code for live editing + - ./app:/app/app + # Override the entrypoint to run Python directly for easier development + command: ["python", "-u", "app/main.py"]