About youtube-to-bookplayer
youtube-to-bookplayer is a skill that downloads YouTube audio, adds metadata, and transfers the file to BookPlayer on an iPhone over USB. Its staged workflow includes device checks and verifies the imported file.
Key features
- 01Download YouTube audio as M4A
- 02Edit audio metadata
- 03Transfer files to BookPlayer over USB
- 04Check device and app availability
- 05Verify the transferred file
Use cases
- 01Listen to a YouTube lecture in BookPlayer
- 02Move long-form YouTube audio to an iPhone
- 03Organize downloaded audio with title and artist metadata
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ terrylica/ cc-skills --skill youtube-to-bookplayerAfter installing, type/youtube-to-bookplayerin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/evolution-log.mdtool-reference.mdtroubleshooting.mdDetails
From the project READMEyoutube-to-bookplayer
Download audio from a YouTube video, tag metadata, and push to BookPlayer on iPhone via USB.
BookPlayer is an iOS audiobook player that resumes playback position — ideal for long-form YouTube content (lectures, audiobooks, podcasts). Files pushed to its /Documents/ directory are auto-imported on next app launch.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
Task Template
Execute phases 0–5 sequentially. Each phase has a [Preflight], [Ask], [Execute], or [Verify] tag indicating its nature. Do not skip phases.
Phase 0: Preflight [Preflight]
Check all required tools and device connectivity. Fail fast — do not proceed if any check fails.
# Tool availability
TOOLS_OK=true
for tool in yt-dlp ffmpeg exiftool; do
if command -v "$tool" &>/dev/null; then
echo "$tool: OK ($(command -v "$tool"))"
else
echo "$tool: MISSING"
TOOLS_OK=false
fi
done
# pymobiledevice3 (may only be available via uvx)
if command -v pymobiledevice3 &>/dev/null; then
echo "pymobiledevice3: OK ($(command -v pymobiledevice3))"
else
if uvx --python 3.14 --from pymobiledevice3 pymobiledevice3 --help &>/dev/null 2>&1; then
echo "pymobiledevice3: OK (via uvx)"
else
echo "pymobiledevice3: MISSING"
TOOLS_OK=false
fi
fi
echo "---"
[ "$TOOLS_OK" = true ] && echo "All tools OK" || echo "BLOCKED: Install missing tools (see table below)"
If tools are missing:
| Tool | Install Command |
|---|---|
yt-dlp | brew install yt-dlp |
ffmpeg | brew install ffmpeg |
exiftool | brew install exiftool |
pymobiledevice3 | uvx --python 3.14 --from pymobiledevice3 pymobiledevice3 --help |
Device check (only after tools pass):
# Check for connected iOS device
pymobiledevice3 usbmux list 2>/dev/null || uvx --python 3.14 --from pymobiledevice3 pymobiledevice3 usbmux list
# Check BookPlayer is installed
pymobiledevice3 apps list --no-color 2>/dev/null | grep -i "audiobookplayer\|bookplayer" || \
uvx --python 3.14 --from pymobiledevice3 pymobiledevice3 apps list --no-color 2>/dev/null | grep -i "audiobookplayer\|bookplayer"
If no device found: ask user to connect iPhone via USB, unlock it, and tap "Trust This Computer". If BookPlayer not found: ask user to install BookPlayer from the App Store.
Phase 1: Accept URL & Confirm [Ask]
If $ARGUMENTS[0] is provided, use it as the YouTube URL. Otherwise, use AskUserQuestion to ask for the URL.
Preview metadata before proceeding:
yt-dlp --dump-json --no-download "$URL" 2>/dev/null | python3 -c "
import json, sys
d = json.load(sys.stdin)
hrs, rem = divmod(int(d.get('duration', 0)), 3600)
mins, secs = divmod(rem, 60)
print(f\"Title: {d.get('title', 'Unknown')}\")
print(f\"Channel: {d.get('channel', 'Unknown')}\")
print(f\"Duration: {hrs}h {mins}m {secs}s\")
print(f\"Upload: {d.get('upload_date', 'Unknown')}\")
"
Use AskUserQuestion to confirm:
- Title, channel, duration look correct
- Whether to customize the metadata (title/artist/album) or use defaults from yt-dlp
Phase 2: Download Audio [Execute]
WORK_DIR=$(mktemp -d)
echo "Working directory: $WORK_DIR"
yt-dlp -x --audio-format m4a --audio-quality 0 --no-playlist \
-o "$WORK_DIR/%(title).100B.%(ext)s" \
"$URL"
# Show result
ls -lh "$WORK_DIR"/*.m4a
Notes:
--audio-quality 0= best available quality%(title).100Btruncates filename to 100 bytes (prevents filesystem issues)--no-playlistensures single video download even from playlist URLs- ffmpeg is auto-invoked by yt-dlp for M4A conversion
Phase 3: Tag Metadata [Execute]
Extract metadata from yt-dlp JSON and apply to the M4A file:
# Get the downloaded file path
M4A_FILE=$(ls "$WORK_DIR"/*.m4a | head -1)
# Apply metadata (use values confirmed in Phase 1, or yt-dlp defaults)
exiftool -overwrite_original \
-Title="$TITLE" \
-Artist="$ARTIST" \
-Album="YouTube Audio" \
"$M4A_FILE"
# Verify tags
exiftool -Title -Artist -Album "$M4A_FILE"
Variables (from Phase 1 confirmation):
$TITLE— Video title (or user-customized)$ARTIST— Channel name (or user-customized)- Album defaults to "YouTube Audio" unless user specifies otherwise
Phase 4: Push to BookPlayer [Execute]
CRITICAL: Use the Python API with
documents_only=True. The CLIpymobiledevice3 apps pushuses VendContainer mode and will not work with BookPlayer.
M4A_FILE=$(ls "$WORK_DIR"/*.m4a | head -1)
FILENAME=$(basename "$M4A_FILE")
uvx --python 3.14 --from pymobiledevice3 python3 << 'PYEOF'
import sys
from pathlib import Path
from pymobiledevice3.lockdown import create_using_usbmux
from pymobiledevice3.services.house_arrest import HouseArrestService
local_path = sys.argv[1] if len(sys.argv) > 1 else None
if not local_path:
# Find the m4a file from environment
import glob, os
work_dir = os.environ.get("WORK_DIR", "/tmp")
files = glob.glob(os.path.join(work_dir, "*.m4a"))
if not files:
print("ERROR: No .m4a file found in work directory")
sys.exit(1)
local_path = files[0]
file_path = Path(local_path)
filename = file_path.name
file_data = file_path.read_bytes()
size_mb = len(file_data) / (1024 * 1024)
print(f"Pushing: {filename} ({size_mb:.1f} MB)")
lockdown = create_using_usbmux()
service = HouseArrestService(
lockdown=lockdown,
bundle_id="com.tortugapower.audiobookplayer",
documents_only=True # CRITICAL: VendDocuments mode
)
service.set_file_contents(f"/Documents/{filename}", file_data)
print(f"SUCCESS: {filename} pushed to BookPlayer /Documents/")
PYEOF
Anti-pattern — DO NOT USE:
# WRONG: This uses VendContainer mode and fails silently on BookPlayer
pymobiledevice3 apps push com.tortugapower.audiobookplayer /path/to/file.m4a
Phase 5: Verify [Verify]
List BookPlayer's /Documents/ directory to confirm the file arrived:
uvx --python 3.14 --from pymobiledevice3 python3 << 'PYEOF'
from pymobiledevice3.lockdown import create_using_usbmux
from pymobiledevice3.services.house_arrest import HouseArrestService
lockdown = create_using_usbmux()
service = HouseArrestService(
lockdown=lockdown,
bundle_id="com.tortugapower.audiobookplayer",
documents_only=True
)
files = service.listdir("/Documents/")
print("BookPlayer /Documents/ contents:")
for f in sorted(files):
if f.startswith('.'):
continue
try:
info = service.stat(f"/Documents/{f}")
size_mb = info.get('st_size', 0) / (1024 * 1024)
print(f" {f} ({size_mb:.1f} MB)")
except Exception:
print(f" {f}")
PYEOF
Report to user:
- File name and size in BookPlayer
- Duration (from Phase 1 metadata)
- Remind: open BookPlayer on iPhone to see the new file (force-quit and reopen if it doesn't appear)
Cleanup:
# Remove temp working directory
rm -rf "$WORK_DIR"
echo "Cleaned up: $WORK_DIR"
Troubleshooting Quick Reference
| Problem | Quick Fix |
|---|---|
| No device found | Unlock iPhone, re-plug USB, tap "Trust" |
| File not in BookPlayer | You used the CLI — must use Python API with documents_only=True |
| Wrong metadata shown | Re-run Phase 3 with correct -Title/-Artist values |
Full troubleshooting: references/troubleshooting.md
References
- Tool Reference — yt-dlp flags, pymobiledevice3 API, exiftool tags
- Troubleshooting — Known issues, diagnostic commands
- Evolution Log — Origin and key discoveries
Post-Change Checklist
When modifying this skill, verify:
- Phase 0 preflight catches all missing tools with correct install commands
- Phase 4 uses Python API with
documents_only=True(never CLIapps push) - No hardcoded paths — uses
$HOME,mktemp,command -v,create_using_usbmux() - Python commands use
--python 3.14(per global policy) - Anti-pattern warning is preserved in Phase 4
Post-Execution Reflection
After this skill completes, reflect before closing the task:
- Locate yourself. — Find this SKILL.md's canonical path (Glob for this skill's name) before editing. All corrections target THIS file and its sibling references/ — never other documentation.
- What failed? — Fix the instruction that caused it. If it could recur, add it as an anti-pattern.
- What worked better than expected? — Promote it to recommended practice. Document why.
- What drifted? — Any script, reference, or external dependency that no longer matches reality gets fixed now.
- Log it. — Every change gets an evolution-log entry with trigger, fix, and evidence.