import-youtube-music
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImport YouTube Music
导入YouTube音乐
Download the audio with yt-dlp, remux it into a real MP4 container, write the
tags and a square cover, then hand the file to Music.app.
使用yt-dlp下载音频,将其重新封装为标准MP4容器,写入标签和方形封面图,然后将文件导入Music.app。
Overview
概述
Two things decide whether this goes well.
- The library already has a convention. Read it off an existing track instead of inventing one.
- A file that plays fine locally can still be rejected by iCloud Sync Library. Verify through Music.app, not through the tagger.
能否顺利完成操作取决于两点:
- 音乐库已有既定的命名规范。应从现有曲目中读取规范,而非自行制定。
- 本地播放正常的文件仍可能被iCloud同步库拒绝。需通过Music.app验证,而非标签工具。
When to Use
使用场景
- The user wants a song from YouTube in their local Music.app library
- The user names an existing track and asks for "the same format"
- A track shows 0:00, no bitrate, or is marked ineligible for iCloud sync
- Fixing artist, album, or artwork on a track already imported
- NOT for: podcasts, keeping the video, anything that needs a real re-encode
- 用户希望将YouTube上的歌曲添加到本地Music.app库中
- 用户指定某首已有曲目并要求“相同格式”
- 曲目显示0:00时长、无比特率,或被标记为iCloud同步ineligible(不合格)
- 修复已导入曲目的艺术家、专辑或封面信息
- 不适用于:播客、保留视频内容、任何需要重新编码的场景
Requirements
环境要求
macOS, plus three tools that need no permanent install:
bash
uvx yt-dlp --version # downloader
uv run --with imageio-ffmpeg python -c \
'import imageio_ffmpeg; print(imageio_ffmpeg.get_ffmpeg_exe())' # static ffmpeg
sips --help # built into macOSThe last command prints a path; keep it in . ffmpeg is required, not
optional — see step 3. Work in a scratch directory until the file is final.
$FFmacOS系统,外加三款无需永久安装的工具:
bash
uvx yt-dlp --version # 下载工具
uv run --with imageio-ffmpeg python -c \
'import imageio_ffmpeg; print(imageio_ffmpeg.get_ffmpeg_exe())' # 静态ffmpeg
sips --help # macOS内置工具最后一条命令会输出路径;请将其保存到变量中。ffmpeg为必填项,不可省略——详见步骤3。在文件最终完成前,请在临时目录中操作。
$FF1. Identify the right video
1. 选择正确的视频
bash
uvx yt-dlp --flat-playlist \
--print "%(id)s | %(duration)s | %(channel)s | %(title)s" "ytsearch10:SONG NAME"Prefer the original upload by the credited artist. Duration is the tiebreaker:
hour-long loops, remixes, subtitle edits, and fan reuploads crowd the results and
none of them are the track being asked for.
When the user names an existing track as the template, find its source too —
a matching duration confirms which upload the library copy came from.
bash
uvx yt-dlp --flat-playlist \
--print "%(id)s | %(duration)s | %(channel)s | %(title)s" "ytsearch10:SONG NAME"优先选择官方艺术家上传的原版内容。时长是关键筛选条件:一小时循环版、混音版、字幕编辑版和粉丝重制版会占据大量搜索结果,但这些都不是用户需要的曲目。
当用户指定某首已有曲目作为模板时,也需查找其来源——匹配的时长可确认库中副本对应的是哪个上传版本。
2. Download
2. 下载音频
bash
uvx yt-dlp -f "bestaudio[ext=m4a]/bestaudio" \
--write-info-json --write-thumbnail -o "track.%(ext)s" "https://www.youtube.com/watch?v=ID"--write-info-jsonbash
uvx yt-dlp -f "bestaudio[ext=m4a]/bestaudio" \
--write-info-json --write-thumbnail -o "track.%(ext)s" "https://www.youtube.com/watch?v=ID"--write-info-json3. Remux, and never skip it
3. 重新封装,此步骤不可跳过
yt-dlp prints:
text
WARNING: writing DASH m4a. Only some players support this container.
Install ffmpeg to fix this automaticallyMusic.app is one of the players that does not support it. A DASH-fragmented
m4a imports and even plays, but Music reads no duration from it, so the track
shows 0:00, has no bitrate, and Sync Library marks it ineligible and
never uploads it. Rewrite the container:
bash
"$FF" -hide_banner -loglevel warning -y -i track.m4a \
-c:a copy -movflags +faststart -f mp4 "TITLE.m4a"
"$FF" -hide_banner -i "TITLE.m4a" 2>&1 | grep Duration # must print a real duration-c:a copyyt-dlp会输出如下警告:
text
WARNING: writing DASH m4a. Only some players support this container.
Install ffmpeg to fix this automaticallyMusic.app属于不支持该容器的播放器之一。DASH分片格式的m4a文件可以导入甚至播放,但Music无法读取其时长,因此曲目会显示0:00、无比特率,且同步库会标记其为ineligible(不合格),永远不会上传。需重新封装容器:
bash
"$FF" -hide_banner -loglevel warning -y -i track.m4a \
-c:a copy -movflags +faststart -f mp4 "TITLE.m4a"
"$FF" -hide_banner -i "TITLE.m4a" 2>&1 | grep Duration # 必须输出真实时长-c:a copy4. Derive the tags
4. 生成标签
| Atom | Field | How to derive |
|---|---|---|
| Title | Song name only — strip bracket tags and any artist suffix |
| Artist / Album Artist | The credited artist, not the uploader handle |
| Album | Song name for a single; keep a series tag, drop a format tag |
| Genre | From the music itself |
| Lyrics | The description, when it carries a lyrics section |
| Artwork | Square JPEG, 1024×1024 (step 5) |
Read the convention off the library before choosing values:
bash
ls -R ~/Music/Music/Media.localized/Music/
uv run --with mutagen mutagen-inspect "<an existing track>.m4a"The layout shows exactly how this user
tags things. Copy the shape — which atoms are set, whether lyrics are embedded,
what size the cover is. Do not copy the values: the artist on the reference
track is that song's artist, not this one's.
Music/<Album Artist>/<Album>/<Title>.m4aVideo titles often encode the artist rather than the metadata fields:
- — the name after the separator is the artist. It is not part of the title.
<TAG> Song Name / Artist Name - A bracket tag describing the video format (an official-video marker, a resolution) is noise: delete it. A tag naming a series belongs in the album.
- The channel is the uploader, often a remix account, a label, or a reuploader. It is the artist only when nothing else is credited.
Take the lyrics from the description mechanically rather than retyping them:
bash
jq -r '.description' track.info.json | awk 'f {print} /^Lyrics:|^歌詞/ {f=1}' > lyrics.txtMost videos have no lyrics section at all. When neither marker matches, the file
comes out empty — that is the normal case, not a failure. Leave the tag unset
rather than writing an empty one; step 6 already guards for it.
| 原子字段 | 对应信息 | 生成方式 |
|---|---|---|
| 标题 | 仅保留歌曲名称——移除括号标签和任何艺术家后缀 |
| 艺术家 / 专辑艺术家 | 采用官方标注的艺术家,而非上传者账号名 |
| 专辑 | 单曲使用歌曲名称;保留“系列”标签,移除“格式”标签 |
| 流派 | 根据音乐本身确定 |
| 歌词 | 当视频描述包含歌词部分时提取 |
| 封面图 | 方形JPEG,尺寸1024×1024(见步骤5) |
在选择标签值前,请先读取音乐库的规范:
bash
ls -R ~/Music/Music/Media.localized/Music/
uv run --with mutagen mutagen-inspect "<an existing track>.m4a"Music/<Album Artist>/<Album>/<Title>.m4a视频标题通常会包含艺术家信息,而非元数据字段:
- ——分隔符后的名称是艺术家,不属于标题的一部分。
<TAG> Song Name / Artist Name - 描述视频格式的括号标签(如官方视频标记、分辨率)属于冗余信息:需删除。标记“系列”的标签应归入专辑字段。
- 频道是上传者,通常是混音账号、唱片公司或重传者。只有当没有其他官方标注时,才将其作为艺术家。
通过脚本从描述中提取歌词,而非手动输入:
bash
jq -r '.description' track.info.json | awk 'f {print} /^Lyrics:|^歌詞/ {f=1}' > lyrics.txt大多数视频没有歌词部分。当匹配不到标记时,生成的文件会是空的——这是正常情况,而非失败。不要设置空标签,步骤6会自动处理这种情况。
5. Cover art
5. 制作封面图
Target a 1024×1024 JPEG. crops from the center.
sips -c H Wbash
curl -sL -o thumb.jpg "https://i.ytimg.com/vi/ID/maxresdefault.jpg" # 1280x720
cp thumb.jpg cover.jpg
sips -c 720 720 cover.jpg # center square
sips -z 1024 1024 cover.jpg
sips -s format jpeg -s formatOptions 95 --out cover.jpg cover.jpgLook at the result before embedding it. Render a preview with
and open it. A 16:9 thumbnail
center-crops well when the subject is centered and badly when the art is a wide
composition or carries text near the edges. If the crop mangles it, say so and
choose another source image instead of shipping it.
sips -Z 500 --out preview.jpg cover.jpg目标为1024×1024的JPEG图片。使用从中心裁剪。
sips -c H Wbash
curl -sL -o thumb.jpg "https://i.ytimg.com/vi/ID/maxresdefault.jpg" # 1280x720
cp thumb.jpg cover.jpg
sips -c 720 720 cover.jpg # 裁剪为中心方形
sips -z 1024 1024 cover.jpg
sips -s format jpeg -s formatOptions 95 --out cover.jpg cover.jpg嵌入前请查看结果。使用生成预览图并打开。16:9的缩略图在主体居中时裁剪效果较好,但当封面是宽幅构图或边缘有文字时,裁剪会破坏画面。如果裁剪效果不佳,请更换其他来源图片,不要直接使用。
sips -Z 500 --out preview.jpg cover.jpg6. Write the tags
6. 写入标签
bash
uv run --with mutagen python - "TITLE.m4a" <<'PY'
import pathlib, sys
from mutagen.mp4 import MP4, MP4Cover
path = sys.argv[1]
a = MP4(path)
a["\xa9nam"] = ["TITLE"]
a["\xa9ART"] = ["ARTIST"]
a["aART"] = ["ARTIST"]
a["\xa9alb"] = ["ALBUM"]
a["\xa9gen"] = ["GENRE"]
a["covr"] = [MP4Cover(pathlib.Path("cover.jpg").read_bytes(), imageformat=MP4Cover.FORMAT_JPEG)]
a.pop("\xa9too", None) # yt-dlp leaves "Google" herebash
uv run --with mutagen python - "TITLE.m4a" <<'PY'
import pathlib, sys
from mutagen.mp4 import MP4, MP4Cover
path = sys.argv[1]
a = MP4(path)
a["\xa9nam"] = ["TITLE"]
a["\xa9ART"] = ["ARTIST"]
a["aART"] = ["ARTIST"]
a["\xa9alb"] = ["ALBUM"]
a["\xa9gen"] = ["GENRE"]
a["covr"] = [MP4Cover(pathlib.Path("cover.jpg").read_bytes(), imageformat=MP4Cover.FORMAT_JPEG)]
a.pop("\xa9too", None) # yt-dlp会在此留下"Google"Only when step 4 produced something. Most descriptions carry no lyrics, and a
仅当步骤4生成了有效内容时才设置。大多数描述没有歌词,空文件会导致脚本崩溃,空内容会写入空标签。
missing file would crash the script while an empty one would write an empty tag.
—
lyrics = pathlib.Path("lyrics.txt")
text = lyrics.read_text(encoding="utf-8").strip() if lyrics.is_file() else ""
if text:
a["\xa9lyr"] = [text]
a.save()
for k, v in sorted(MP4(path).items()):
print(k, "=", f"{len(v[0])} bytes" if k == "covr" else v)
PY
Printing the tags back is what makes the write self-verifying.lyrics = pathlib.Path("lyrics.txt")
text = lyrics.read_text(encoding="utf-8").strip() if lyrics.is_file() else ""
if text:
a["\xa9lyr"] = [text]
a.save()
for k, v in sorted(MP4(path).items()):
print(k, "=", f"{len(v[0])} bytes" if k == "covr" else v)
PY
将标签回显出来可实现写入操作的自我验证。7. Import
7. 导入到Music.app
Hand the finished file to Music.app:
bash
osascript -e 'tell application "Music" to add POSIX file "/absolute/path/TITLE.m4a"'Music copies it into
.
~/Music/Music/Media.localized/Music/<Album Artist>/<Album>/<Title>.m4a将最终文件导入Music.app:
bash
osascript -e 'tell application "Music" to add POSIX file "/absolute/path/TITLE.m4a"'Music会将文件复制到路径下。
~/Music/Music/Media.localized/Music/<Album Artist>/<Album>/<Title>.m4a8. Verify through Music.app
8. 通过Music.app验证
This is the step that catches the container failure. A tag inspector reports the
correct duration even for a file Music cannot read, so tagger output is not
evidence. Ask Music:
bash
osascript -e 'tell application "Music"
set r to {}
repeat with t in (every file track of library playlist 1 whose album is "ALBUM")
set end of r to ((get name of t) & " | time=" & (get time of t) & " | bitrate=" & (get bit rate of t as text) & " | artwork=" & (count of artworks of t) & " | cloud=" & (get cloud status of t as text))
end repeat
return r
end tell'- must be a real
time, neverM:SSmissing value - must be a number
bit rate - settles on
cloud status,uploaded, ormatched.purchasedmeans Sync Library has not evaluated it yet, and settling can take well over five minutes while Music is in the background — a poll loop that times out onunknownis inconclusive, not a failure, so re-query later before concluding anything.unknownmeans the import failed; go back to step 3.ineligible
此步骤可检测容器是否存在问题。标签工具即使对于Music无法读取的文件,也会报告正确的时长,因此标签工具的输出不能作为验证依据。需通过Music确认:
bash
osascript -e 'tell application "Music"
set r to {}
repeat with t in (every file track of library playlist 1 whose album is "ALBUM")
set end of r to ((get name of t) & " | time=" & (get time of t) & " | bitrate=" & (get bit rate of t as text) & " | artwork=" & (count of artworks of t) & " | cloud=" & (get cloud status of t as text))
end repeat
return r
end tell'- 必须为真实的
time格式,绝不能是M:SSmissing value - 必须为数字
bit rate - 最终会变为
cloud status(已上传)、uploaded(已匹配)或matched(已购买)。purchased表示同步库尚未完成评估,当Music在后台运行时,评估可能需要超过5分钟——此时的轮询超时属于不确定状态,而非失败,需稍后重新查询。unknown表示导入失败;请返回步骤3重新操作。ineligible
Replacing a bad track
替换损坏的曲目
Editing the file inside the media folder does nothing: Music caches metadata in
its own database. Delete the entry, remove the file it orphans, then re-import.
bash
osascript -e 'tell application "Music"
repeat with t in (every file track of library playlist 1 whose album is "ALBUM")
delete t
end repeat
end tell'
rm -rf ~/Music/Music/Media.localized/Music/"ARTIST" # orphan; else you get "TITLE 1.m4a"For a metadata-only change, do not delete. Drive Music and it rewrites the
file's tags and relocates the folder for you:
bash
osascript -e 'tell application "Music"
repeat with t in (every file track of library playlist 1 whose album is "OLD ALBUM")
set artist of t to "NEW ARTIST"
set album artist of t to "NEW ARTIST"
set album of t to "NEW ALBUM"
end repeat
end tell'Select tracks with . The form raises error -1728.
every file track of library playlist 1 whose <property> is "…"some file track … whose database ID is N直接修改媒体文件夹中的文件无效:Music会在自身数据库中缓存元数据。需先删除库中的条目,移除孤立的文件,然后重新导入。
bash
osascript -e 'tell application "Music"
repeat with t in (every file track of library playlist 1 whose album is "ALBUM")
delete t
end repeat
end tell'
rm -rf ~/Music/Music/Media.localized/Music/"ARTIST" # 移除孤立文件;否则会生成"TITLE 1.m4a"如果仅需修改元数据,无需删除文件。直接通过Music修改,它会自动重写文件标签并调整文件夹位置:
bash
osascript -e 'tell application "Music"
repeat with t in (every file track of library playlist 1 whose album is "OLD ALBUM")
set artist of t to "NEW ARTIST"
set album artist of t to "NEW ARTIST"
set album of t to "NEW ALBUM"
end repeat
end tell'使用选择曲目。格式会触发错误-1728。
every file track of library playlist 1 whose <property> is "…"some file track … whose database ID is NCommon Mistakes
常见错误
| Mistake | Fix |
|---|---|
| Treating the DASH warning as cosmetic | It causes 0:00 and ineligible. The remux is mandatory. |
| Concluding "no ffmpeg here, so skip the remux" | Fetch a static build; nothing is installed system-wide. |
| Verifying duration with a tag inspector | It reads DASH fine. Only Music's |
Reporting success at | Poll again later; |
| Copying the artist off the reference track | That is that song's artist. Derive this one from its own credits. |
| Leaving a format tag in the album name | It describes the video, not the release. |
| Using the channel name as the artist | Look for |
| Picking a search hit without checking duration | Loops, remixes, and reuploads dominate the results. |
| Embedding a center crop unseen | Look at the image before it becomes the album art. |
Writing | Most descriptions have no lyrics. Set the tag only when there is text. |
| Re-importing without removing the orphan | Music creates |
Reaching for | Re-encodes for nothing; |
| 错误操作 | 修复方法 |
|---|---|
| 将DASH警告视为无关信息 | 该警告会导致时长显示0:00和同步不合格。重新封装步骤是必须的。 |
| 认为“没有ffmpeg就跳过重新封装” | 获取静态编译版本;无需全局安装。 |
| 使用标签工具验证时长 | 标签工具可以正常读取DASH格式。只有Music的 |
当 | 稍后重新查询; |
| 直接复制参考曲目的艺术家信息 | 参考曲目的艺术家属于该歌曲,需从当前曲目的官方标注中提取。 |
| 专辑名称中保留格式标签 | 格式标签描述的是视频,而非音乐发行版本。 |
| 将频道名称作为艺术家 | 优先从视频标题中查找 |
| 不检查时长就选择搜索结果 | 循环版、混音版和重制版会占据大量搜索结果。 |
| 未查看就嵌入中心裁剪的封面图 | 在设置为专辑封面前请先查看图片效果。 |
从空的 | 大多数描述没有歌词。仅当有内容时才设置该标签。 |
| 未移除孤立文件就重新导入 | Music会在旁边生成 |
使用 | 此操作会无意义地重新编码; |
Red Flags
危险信号
Stop and redo the import when any of these is true.
- The DASH warning appeared and ffmpeg never ran
- ,
time=missing value, orbit rate=missing valuecloud=ineligible - Success was reported citing only tag-inspector output
出现以下任一情况时,请停止操作并重新导入:
- 出现DASH警告但未运行ffmpeg
- 、
time=missing value或bit rate=missing valuecloud=ineligible - 仅依据标签工具的输出就报告成功