Merge 9c4a510f6f into 8ea7dd0711
This commit is contained in:
commit
cf4b478ab1
4 changed files with 47 additions and 5 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -14,6 +14,7 @@ speed-measure-plugin*.json
|
||||||
|
|
||||||
# IDEs and editors
|
# IDEs and editors
|
||||||
/ui/.idea
|
/ui/.idea
|
||||||
|
.idea/
|
||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
.c9/
|
.c9/
|
||||||
|
|
@ -44,6 +45,12 @@ testem.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Python
|
||||||
__pycache__
|
__pycache__
|
||||||
.venv
|
.venv
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Downloads directory
|
||||||
|
downloads/
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ def get_format(format: str, quality: str) -> str:
|
||||||
|
|
||||||
if format in AUDIO_FORMATS:
|
if format in AUDIO_FORMATS:
|
||||||
# Audio quality needs to be set post-download, set in opts
|
# 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 format in ("mp4", "any"):
|
||||||
if quality == "audio":
|
if quality == "audio":
|
||||||
|
|
|
||||||
12
app/ytdl.py
12
app/ytdl.py
|
|
@ -97,7 +97,7 @@ class Download:
|
||||||
'outtmpl': { "default": self.output_template, "chapter": self.output_template_chapter },
|
'outtmpl': { "default": self.output_template, "chapter": self.output_template_chapter },
|
||||||
'format': self.format,
|
'format': self.format,
|
||||||
'socket_timeout': 30,
|
'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],
|
'progress_hooks': [put_status],
|
||||||
'postprocessor_hooks': [put_status_postprocessor],
|
'postprocessor_hooks': [put_status_postprocessor],
|
||||||
**self.ytdl_opts,
|
**self.ytdl_opts,
|
||||||
|
|
@ -281,7 +281,7 @@ class DownloadQueue:
|
||||||
self.seq_lock = asyncio.Lock()
|
self.seq_lock = asyncio.Lock()
|
||||||
elif self.config.DOWNLOAD_MODE == 'limited':
|
elif self.config.DOWNLOAD_MODE == 'limited':
|
||||||
self.semaphore = asyncio.Semaphore(int(self.config.MAX_CONCURRENT_DOWNLOADS))
|
self.semaphore = asyncio.Semaphore(int(self.config.MAX_CONCURRENT_DOWNLOADS))
|
||||||
|
|
||||||
self.done.load()
|
self.done.load()
|
||||||
|
|
||||||
async def __import_queue(self):
|
async def __import_queue(self):
|
||||||
|
|
@ -343,6 +343,13 @@ class DownloadQueue:
|
||||||
'ignore_no_formats_error': True,
|
'ignore_no_formats_error': True,
|
||||||
'noplaylist': playlist_strict_mode,
|
'noplaylist': playlist_strict_mode,
|
||||||
'paths': {"home": self.config.DOWNLOAD_DIR, "temp": self.config.TEMP_DIR},
|
'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,
|
**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 {}),
|
**({'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)
|
}).extract_info(url, download=False)
|
||||||
|
|
@ -420,7 +427,6 @@ class DownloadQueue:
|
||||||
ytdl_options = dict(self.config.YTDL_OPTIONS)
|
ytdl_options = dict(self.config.YTDL_OPTIONS)
|
||||||
if playlist_item_limit > 0:
|
if playlist_item_limit > 0:
|
||||||
log.info(f'playlist limit is set. Processing only first {playlist_item_limit} entries')
|
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:
|
if auto_start is True:
|
||||||
download = Download(dldirectory, self.config.TEMP_DIR, output, output_chapter, quality, format, ytdl_options, dl)
|
download = Download(dldirectory, self.config.TEMP_DIR, output, output_chapter, quality, format, ytdl_options, dl)
|
||||||
self.queue.put(download)
|
self.queue.put(download)
|
||||||
|
|
|
||||||
27
docker-compose-dev.yml
Normal file
27
docker-compose-dev.yml
Normal file
|
|
@ -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"]
|
||||||
Loading…
Reference in a new issue