fix: Fixed suffix when quality is 'best'
This commit is contained in:
parent
de11f5c638
commit
8723ab0afe
3 changed files with 132 additions and 45 deletions
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
|
|
@ -58,8 +58,6 @@ jobs:
|
||||||
needs: quality-checks
|
needs: quality-checks
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
|
||||||
ghcr_repo: ${{ steps.ghcr-repo.outputs.ghcr_repo }}
|
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Get current date
|
name: Get current date
|
||||||
|
|
@ -175,11 +173,12 @@ jobs:
|
||||||
id: release_body
|
id: release_body
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_REPO: ${{ secrets.DOCKERHUB_REPOSITORY }}
|
DOCKERHUB_REPO: ${{ secrets.DOCKERHUB_REPOSITORY }}
|
||||||
GHCR_REPO: ${{ needs.dockerhub-build-push.outputs.ghcr_repo }}
|
GHCR_REPO_RAW: ghcr.io/${{ github.repository }}
|
||||||
DATE: ${{ steps.date.outputs.date }}
|
DATE: ${{ steps.date.outputs.date }}
|
||||||
HAS_COMMITS: ${{ steps.commits.outputs.has_commits }}
|
HAS_COMMITS: ${{ steps.commits.outputs.has_commits }}
|
||||||
COMMITS: ${{ steps.commits.outputs.commits }}
|
COMMITS: ${{ steps.commits.outputs.commits }}
|
||||||
run: |
|
run: |
|
||||||
|
GHCR_REPO=$(echo "$GHCR_REPO_RAW" | tr '[:upper:]' '[:lower:]')
|
||||||
{
|
{
|
||||||
echo '## Docker Images'
|
echo '## Docker Images'
|
||||||
echo ''
|
echo ''
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,10 @@ sys.modules.setdefault("yt_dlp.utils", fake_utils)
|
||||||
|
|
||||||
from ytdl import (
|
from ytdl import (
|
||||||
DownloadInfo,
|
DownloadInfo,
|
||||||
|
_codec_suffix_value,
|
||||||
_compact_persisted_entry,
|
_compact_persisted_entry,
|
||||||
_convert_srt_to_txt_file,
|
_convert_srt_to_txt_file,
|
||||||
|
_quality_suffix_value,
|
||||||
_resolve_outtmpl_fields,
|
_resolve_outtmpl_fields,
|
||||||
_sanitize_entry_for_pickle,
|
_sanitize_entry_for_pickle,
|
||||||
_sanitize_path_component,
|
_sanitize_path_component,
|
||||||
|
|
@ -181,6 +183,74 @@ Second line
|
||||||
self.assertIn("Second line", content)
|
self.assertIn("Second line", content)
|
||||||
|
|
||||||
|
|
||||||
|
class QualitySuffixValueTests(unittest.TestCase):
|
||||||
|
def _download_info(self, **kwargs):
|
||||||
|
info = DownloadInfo(
|
||||||
|
id="id1",
|
||||||
|
title="t",
|
||||||
|
url="http://example.com/v",
|
||||||
|
quality=kwargs.pop("quality", "best"),
|
||||||
|
download_type=kwargs.pop("download_type", "video"),
|
||||||
|
codec=kwargs.pop("codec", "auto"),
|
||||||
|
format=kwargs.pop("format", "any"),
|
||||||
|
folder="",
|
||||||
|
custom_name_prefix="",
|
||||||
|
error=None,
|
||||||
|
entry=None,
|
||||||
|
playlist_item_limit=0,
|
||||||
|
split_by_chapters=False,
|
||||||
|
chapter_template="",
|
||||||
|
)
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(info, key, value)
|
||||||
|
return info
|
||||||
|
|
||||||
|
def test_video_best_uses_actual_height_template(self):
|
||||||
|
self.assertEqual(_quality_suffix_value(self._download_info()), "%(height)sp")
|
||||||
|
|
||||||
|
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):
|
||||||
|
info = self._download_info(download_type="audio", quality="best")
|
||||||
|
self.assertEqual(_quality_suffix_value(info), "best")
|
||||||
|
|
||||||
|
|
||||||
|
class CodecSuffixValueTests(unittest.TestCase):
|
||||||
|
def _download_info(self, **kwargs):
|
||||||
|
return DownloadInfo(
|
||||||
|
id="id1",
|
||||||
|
title="t",
|
||||||
|
url="http://example.com/v",
|
||||||
|
quality="best",
|
||||||
|
download_type="video",
|
||||||
|
codec=kwargs.pop("codec", "auto"),
|
||||||
|
format="any",
|
||||||
|
folder="",
|
||||||
|
custom_name_prefix="",
|
||||||
|
error=None,
|
||||||
|
entry=kwargs.pop("entry", None),
|
||||||
|
playlist_item_limit=0,
|
||||||
|
split_by_chapters=False,
|
||||||
|
chapter_template="",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_explicit_codec_passes_through(self):
|
||||||
|
self.assertEqual(_codec_suffix_value(self._download_info(codec="h265")), "h265")
|
||||||
|
|
||||||
|
def test_auto_codec_uses_entry_vcodec(self):
|
||||||
|
info = self._download_info(entry={"vcodec": "avc1.640028"})
|
||||||
|
self.assertEqual(_codec_suffix_value(info), "h264")
|
||||||
|
|
||||||
|
def test_auto_codec_uses_requested_formats(self):
|
||||||
|
info = self._download_info(entry={"requested_formats": [{"vcodec": "vp09.00.51.08"}]})
|
||||||
|
self.assertEqual(_codec_suffix_value(info), "vp9")
|
||||||
|
|
||||||
|
def test_unknown_auto_codec_is_sanitized(self):
|
||||||
|
info = self._download_info(entry={"vcodec": "some codec/name"})
|
||||||
|
self.assertEqual(_codec_suffix_value(info), "some_codec_name")
|
||||||
|
|
||||||
|
|
||||||
class DownloadInfoSetstateTests(unittest.TestCase):
|
class DownloadInfoSetstateTests(unittest.TestCase):
|
||||||
def _base_state(self, **kwargs):
|
def _base_state(self, **kwargs):
|
||||||
base = {
|
base = {
|
||||||
|
|
|
||||||
102
app/ytdl.py
102
app/ytdl.py
|
|
@ -374,6 +374,60 @@ def _download_info_from_record(record: dict[str, Any]) -> DownloadInfo:
|
||||||
info.error = None
|
info.error = None
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
def _quality_suffix_value(dl: DownloadInfo) -> str:
|
||||||
|
quality = str(getattr(dl, 'quality', '') or '')
|
||||||
|
if getattr(dl, 'download_type', '') == 'video':
|
||||||
|
if quality == 'best':
|
||||||
|
return '%(height)sp'
|
||||||
|
if re.fullmatch(r'\d+', quality):
|
||||||
|
return f'{quality}p'
|
||||||
|
return quality
|
||||||
|
|
||||||
|
|
||||||
|
def _codec_suffix_value(dl: DownloadInfo) -> str:
|
||||||
|
codec_value = str(getattr(dl, 'codec', '') or '')
|
||||||
|
if codec_value.lower() != 'auto':
|
||||||
|
return codec_value
|
||||||
|
|
||||||
|
entry = getattr(dl, 'entry', None) or {}
|
||||||
|
resolved = None
|
||||||
|
if isinstance(entry, dict):
|
||||||
|
vc = entry.get('vcodec')
|
||||||
|
ac = entry.get('acodec')
|
||||||
|
if vc:
|
||||||
|
resolved = vc
|
||||||
|
elif ac:
|
||||||
|
resolved = ac
|
||||||
|
else:
|
||||||
|
for fld in ('requested_formats', 'formats'):
|
||||||
|
lst = entry.get(fld) or []
|
||||||
|
if isinstance(lst, list):
|
||||||
|
for it in lst:
|
||||||
|
if isinstance(it, dict):
|
||||||
|
if it.get('vcodec'):
|
||||||
|
resolved = it.get('vcodec')
|
||||||
|
break
|
||||||
|
if it.get('acodec'):
|
||||||
|
resolved = it.get('acodec')
|
||||||
|
break
|
||||||
|
if resolved:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not resolved:
|
||||||
|
return codec_value
|
||||||
|
|
||||||
|
resolved_value = str(resolved).lower()
|
||||||
|
if 'avc' in resolved_value or 'h264' in resolved_value:
|
||||||
|
return 'h264'
|
||||||
|
if 'hevc' in resolved_value or 'h265' in resolved_value or 'h.265' in resolved_value:
|
||||||
|
return 'h265'
|
||||||
|
if 'av01' in resolved_value or 'av1' in resolved_value:
|
||||||
|
return 'av1'
|
||||||
|
if 'vp9' in resolved_value or 'vp09' in resolved_value:
|
||||||
|
return 'vp9'
|
||||||
|
return re.sub(r'[^0-9a-zA-Z]+', '_', resolved_value)
|
||||||
|
|
||||||
class Download:
|
class Download:
|
||||||
manager = None
|
manager = None
|
||||||
|
|
||||||
|
|
@ -922,48 +976,12 @@ class DownloadQueue:
|
||||||
suffix = None
|
suffix = None
|
||||||
if not getattr(dl, 'custom_name_prefix', None):
|
if not getattr(dl, 'custom_name_prefix', None):
|
||||||
parts = []
|
parts = []
|
||||||
if getattr(dl, 'quality', ''):
|
quality_suffix = _quality_suffix_value(dl)
|
||||||
parts.append(str(dl.quality))
|
if quality_suffix:
|
||||||
codec_value = getattr(dl, 'codec', '')
|
parts.append(quality_suffix)
|
||||||
if str(codec_value).lower() == 'auto':
|
codec_suffix = _codec_suffix_value(dl)
|
||||||
entry = getattr(dl, 'entry', None) or {}
|
if codec_suffix:
|
||||||
resolved = None
|
parts.append(codec_suffix)
|
||||||
if isinstance(entry, dict):
|
|
||||||
vc = entry.get('vcodec')
|
|
||||||
ac = entry.get('acodec')
|
|
||||||
if vc:
|
|
||||||
resolved = vc
|
|
||||||
elif ac:
|
|
||||||
resolved = ac
|
|
||||||
else:
|
|
||||||
for fld in ('requested_formats', 'formats'):
|
|
||||||
lst = entry.get(fld) or []
|
|
||||||
if isinstance(lst, list):
|
|
||||||
for it in lst:
|
|
||||||
if isinstance(it, dict):
|
|
||||||
if it.get('vcodec'):
|
|
||||||
resolved = it.get('vcodec')
|
|
||||||
break
|
|
||||||
if it.get('acodec'):
|
|
||||||
resolved = it.get('acodec')
|
|
||||||
break
|
|
||||||
if resolved:
|
|
||||||
break
|
|
||||||
if resolved:
|
|
||||||
s = str(resolved).lower()
|
|
||||||
if 'avc' in s or 'h264' in s:
|
|
||||||
codec_value = 'h264'
|
|
||||||
elif 'hevc' in s or 'h265' in s or 'h.265' in s:
|
|
||||||
codec_value = 'h265'
|
|
||||||
elif 'av01' in s or 'av1' in s:
|
|
||||||
codec_value = 'av1'
|
|
||||||
elif 'vp9' in s or 'vp09' in s:
|
|
||||||
codec_value = 'vp9'
|
|
||||||
else:
|
|
||||||
codec_value = re.sub(r'[^0-9a-zA-Z]+', '_', s)
|
|
||||||
|
|
||||||
if codec_value:
|
|
||||||
parts.append(str(codec_value))
|
|
||||||
if parts:
|
if parts:
|
||||||
suffix = '_'.join(_sanitize_path_component(p) for p in parts if p)
|
suffix = '_'.join(_sanitize_path_component(p) for p in parts if p)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue