Skip to content

Commit

Permalink
Update MP4 format string for iOS compatibility
Browse files Browse the repository at this point in the history
iOS has strict requirements for video files, requiring h264 or h265 video codec and aac audio codec in MP4 container. This update to the MP4 format string tries to get a fully compatible file first, followed by the right video codec and any M4A audio (audio is much faster to convert if needed), and then falls back to the original behaviour of getting the best available MP4 video and M4A audio.
  • Loading branch information
jgoguen committed May 21, 2024
1 parent 633cfb7 commit ebbe0ac
Showing 1 changed file with 33 additions and 14 deletions.
47 changes: 33 additions & 14 deletions app/dl_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

AUDIO_FORMATS = ("m4a", "mp3", "opus", "wav")


def get_format(format: str, quality: str) -> str:
"""
Returns format for download
Expand Down Expand Up @@ -37,7 +38,13 @@ def get_format(format: str, quality: str) -> str:
vres = f"[height<={quality}]" if quality != "best" else ""
vcombo = vres + vfmt

return f"bestvideo{vcombo}+bestaudio{afmt}/best{vcombo}"
# iOS has strict requirements for video files, requiring h264 or h265
# video codec and aac audio codec in MP4 container. This format string
# attempts to get the fully compatible formats first, then the h264/h265
# video codec with any M4A audio codec (because audio is faster to
# convert if needed), and falls back to getting the best available MP4
# file.
return f"bestvideo[vcodec~='^((he|a)vc|h26[45])']{vres}+bestaudio[acodec=aac]/bestvideo[vcodec~='^((he|a)vc|h26[45])']{vres}+bestaudio{afmt}/bestvideo{vcombo}+bestaudio{afmt}/best{vcombo}"

raise Exception(f"Unkown format {format}")

Expand All @@ -55,29 +62,41 @@ def get_opts(format: str, quality: str, ytdl_opts: dict) -> dict:
Returns:
ytdl_opts: Extra options
"""

opts = copy.deepcopy(ytdl_opts)

postprocessors = []

if format in AUDIO_FORMATS:
postprocessors.append({
"key": "FFmpegExtractAudio",
"preferredcodec": format,
"preferredquality": 0 if quality == "best" else quality,
})

#Audio formats without thumbnail
postprocessors.append(
{
"key": "FFmpegExtractAudio",
"preferredcodec": format,
"preferredquality": 0 if quality == "best" else quality,
}
)

# Audio formats without thumbnail
if format not in ("wav") and "writethumbnail" not in opts:
opts["writethumbnail"] = True
postprocessors.append({"key": "FFmpegThumbnailsConvertor", "format": "jpg", "when": "before_dl"})
postprocessors.append(
{
"key": "FFmpegThumbnailsConvertor",
"format": "jpg",
"when": "before_dl",
}
)
postprocessors.append({"key": "FFmpegMetadata"})
postprocessors.append({"key": "EmbedThumbnail"})

if format == "thumbnail":
opts["skip_download"] = True
opts["writethumbnail"] = True
postprocessors.append({"key": "FFmpegThumbnailsConvertor", "format": "jpg", "when": "before_dl"})

opts["postprocessors"] = postprocessors + (opts["postprocessors"] if "postprocessors" in opts else [])
postprocessors.append(
{"key": "FFmpegThumbnailsConvertor", "format": "jpg", "when": "before_dl"}
)

opts["postprocessors"] = postprocessors + (
opts["postprocessors"] if "postprocessors" in opts else []
)
return opts

0 comments on commit ebbe0ac

Please sign in to comment.