diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index 5c05a05..d6ffee6 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -211,9 +211,13 @@ class QualitySuffixValueTests(unittest.TestCase): def test_video_numeric_quality_gets_p_suffix(self): self.assertEqual(_quality_suffix_value(self._download_info(quality="2160")), "2160p") - def test_audio_best_stays_best(self): + def test_audio_best_uses_actual_bitrate_template(self): info = self._download_info(download_type="audio", quality="best") - self.assertEqual(_quality_suffix_value(info), "best") + self.assertEqual(_quality_suffix_value(info), "%(abr).0fkbps") + + def test_audio_numeric_quality_gets_kbps_suffix(self): + info = self._download_info(download_type="audio", quality="192") + self.assertEqual(_quality_suffix_value(info), "192kbps") class CodecSuffixValueTests(unittest.TestCase): @@ -223,7 +227,7 @@ class CodecSuffixValueTests(unittest.TestCase): title="t", url="http://example.com/v", quality="best", - download_type="video", + download_type=kwargs.pop("download_type", "video"), codec=kwargs.pop("codec", "auto"), format="any", folder="", @@ -250,6 +254,10 @@ class CodecSuffixValueTests(unittest.TestCase): info = self._download_info(entry={"vcodec": "some codec/name"}) self.assertEqual(_codec_suffix_value(info), "some_codec_name") + def test_audio_has_no_codec_suffix(self): + info = self._download_info(download_type="audio", codec="auto", entry={"acodec": "mp4a.40.2"}) + self.assertEqual(_codec_suffix_value(info), "") + class DownloadInfoSetstateTests(unittest.TestCase): def _base_state(self, **kwargs): diff --git a/app/ytdl.py b/app/ytdl.py index 7e2fdf7..2dd6dbb 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -377,15 +377,24 @@ def _download_info_from_record(record: dict[str, Any]) -> DownloadInfo: def _quality_suffix_value(dl: DownloadInfo) -> str: quality = str(getattr(dl, 'quality', '') or '') - if getattr(dl, 'download_type', '') == 'video': + download_type = getattr(dl, 'download_type', '') + if download_type == 'video': if quality == 'best': return '%(height)sp' if re.fullmatch(r'\d+', quality): return f'{quality}p' + if download_type == 'audio': + if quality == 'best': + return '%(abr).0fkbps' + if re.fullmatch(r'\d+', quality): + return f'{quality}kbps' return quality def _codec_suffix_value(dl: DownloadInfo) -> str: + if getattr(dl, 'download_type', '') != 'video': + return '' + codec_value = str(getattr(dl, 'codec', '') or '') if codec_value.lower() != 'auto': return codec_value diff --git a/docker-compose.yml b/docker-compose.yml index 33127df..732bb51 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,6 @@ -version: '3.8' - services: mytube: - image: mytube + image: tonyblu/mytube:latest container_name: mytube restart: unless-stopped ports: @@ -28,64 +26,66 @@ services: # - /path/to/robots.txt:/etc/robots.txt:ro environment: + ENV_FILE: "" + # === Container Setup === - # - PUID=1000 - # - PGID=1000 - # - UMASK=022 - # - DEFAULT_THEME=auto - # - LOGLEVEL=INFO - # - ENABLE_ACCESSLOG=false + # PUID: "1000" + # PGID: "1000" + # UMASK: "022" + # DEFAULT_THEME: auto + # LOGLEVEL: INFO + # ENABLE_ACCESSLOG: "false" # === Directory Configuration === - # - DOWNLOAD_DIR=/downloads - # - STATE_DIR=/downloads/.metube - # - TEMP_DIR=/downloads - # - AUDIO_DOWNLOAD_DIR=/audio + # DOWNLOAD_DIR: /downloads + # STATE_DIR: /downloads/.metube + # TEMP_DIR: /downloads + # AUDIO_DOWNLOAD_DIR: /audio # === Web Server === - # - HOST=0.0.0.0 - # - PORT=8081 - - PUBLIC_MODE=true - # - URL_PREFIX=/mytube/ - # - PUBLIC_HOST_URL=download/ - # - PUBLIC_HOST_AUDIO_URL=audio_download/ - # - HTTPS=false - # - CERTFILE=/ssl/crt.pem - # - KEYFILE=/ssl/key.pem - # - CORS_ALLOWED_ORIGINS= - # - ROBOTS_TXT= + # HOST: 0.0.0.0 + # PORT: "8081" + # PUBLIC_MODE: "false" + # URL_PREFIX: /mytube/ + # PUBLIC_HOST_URL: download/ + # PUBLIC_HOST_AUDIO_URL: audio_download/ + # HTTPS: "false" + # CERTFILE: /ssl/crt.pem + # KEYFILE: /ssl/key.pem + # CORS_ALLOWED_ORIGINS: "" + # ROBOTS_TXT: "" # === Download Behavior === - # - MAX_CONCURRENT_DOWNLOADS=3 - # - DELETE_FILE_ON_TRASHCAN=false - # - DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT=0 - # - SUBSCRIPTION_DEFAULT_CHECK_INTERVAL=60 - # - SUBSCRIPTION_SCAN_PLAYLIST_END=50 - # - SUBSCRIPTION_MAX_SEEN_IDS=50000 - # - CLEAR_COMPLETED_AFTER=0 + # MAX_CONCURRENT_DOWNLOADS: "3" + # DELETE_FILE_ON_TRASHCAN: "false" + # DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT: "0" + # SUBSCRIPTION_DEFAULT_CHECK_INTERVAL: "60" + # SUBSCRIPTION_SCAN_PLAYLIST_END: "50" + # SUBSCRIPTION_MAX_SEEN_IDS: "50000" + # CLEAR_COMPLETED_AFTER: "0" # === File Naming & Output === - # - OUTPUT_TEMPLATE=%(title)s.%(ext)s - # - OUTPUT_TEMPLATE_CHAPTER=%(title)s - %(section_number)02d - %(section_title)s.%(ext)s - # - OUTPUT_TEMPLATE_PLAYLIST=%(playlist_title)s/%(title)s.%(ext)s - # - OUTPUT_TEMPLATE_CHANNEL=%(channel)s/%(title)s.%(ext)s + # OUTPUT_TEMPLATE: "%(title)s.%(ext)s" + # OUTPUT_TEMPLATE_CHAPTER: "%(title)s - %(section_number)02d - %(section_title)s.%(ext)s" + # OUTPUT_TEMPLATE_PLAYLIST: "%(playlist_title)s/%(title)s.%(ext)s" + # OUTPUT_TEMPLATE_CHANNEL: "%(channel)s/%(title)s.%(ext)s" # === yt-dlp Options === - # - YTDL_OPTIONS={} - # - YTDL_OPTIONS_FILE=/config/ytdl-options.json - # - YTDL_OPTIONS_PRESETS={} - # - YTDL_OPTIONS_PRESETS_FILE=/config/ytdl-presets.json - # - ALLOW_YTDL_OPTIONS_OVERRIDES=false + # YTDL_OPTIONS: "{}" + # YTDL_OPTIONS_FILE: /config/ytdl-options.json + # YTDL_OPTIONS_PRESETS: "{}" + # YTDL_OPTIONS_PRESETS_FILE: /config/ytdl-presets.json + # ALLOW_YTDL_OPTIONS_OVERRIDES: "false" # === Directory Features === - # - CUSTOM_DIRS=true - # - CREATE_CUSTOM_DIRS=true - # - DOWNLOAD_DIRS_INDEXABLE=false - # - CUSTOM_DIRS_EXCLUDE_REGEX=(^|/)[.@].*$ - # - CHOWN_DIRS=true + # CUSTOM_DIRS: "true" + # CREATE_CUSTOM_DIRS: "true" + # DOWNLOAD_DIRS_INDEXABLE: "false" + # CUSTOM_DIRS_EXCLUDE_REGEX: "(^|/)[.@].*$" + # CHOWN_DIRS: "true" # === Custom Environment File === - # - ENV_FILE=/path/to/custom.env + # ENV_FILE: /path/to/custom.env healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost:${PORT:-8081}/"] diff --git a/ui/src/app/app.html b/ui/src/app/app.html index baf96d7..78ff7b4 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -273,7 +273,8 @@ [(ngModel)]="format" (change)="formatChanged()" [disabled]="addInProgress || subscribeInProgress || downloads.loading" - ngbTooltip="Subtitle output format for captions mode"> + ngbTooltip="Subtitle output format for captions mode" + container="body"> @for (f of formatOptions; track f.id) { } @@ -291,13 +292,14 @@ (change)="subtitleLanguageChanged()" [disabled]="addInProgress || subscribeInProgress || downloads.loading" placeholder="e.g. en, es, zh-Hans" - ngbTooltip="Subtitle language (you can type any language code)"> - + ngbTooltip="Subtitle language (you can type any language code)" + container="body"> +