3.7 KiB
YouTube Workflows
End-to-end workflows for managing YouTube content using the yutu CLI.
Publishing Video Pipeline
Videos go through a staging queue before publishing. The "Unlisted" playlist acts as an inbox.
1. Upload (stage to queue)
Upload a video as unlisted. Use the --description field to describe the video content for AI context (what happens, tone, subjects) — this will be rewritten for SEO before publishing.
CHANNEL_ID=$(yutu channel list --for mine --output json | jq -r '.[0].id')
VIDEO_ID=$(yutu video insert --file video.mp4 \
--title 'Draft title' \
--description 'A husky finds an octopus in shallow water. Every time it gets close, the octopus sprays water at it. Funny and playful tone.' \
--categoryId 22 --privacy unlisted --yes \
--output json | jq -r '.id')
yutu playlistItem insert --kind video \
--playlistId UNLISTED_PLAYLIST_ID --channelId "$CHANNEL_ID" \
--kVideoId "$VIDEO_ID" --yes
2. Review (AI reads description, generates SEO fields)
Read the staged video's description to understand its content, then generate optimized title, description, tags, and thumbnail.
yutu video list --ids VIDEO_ID --parts id,snippet,status --output json
The AI uses the description as a content brief to generate:
- SEO title (curiosity gap + keywords, under 60 chars)
- SEO description (keywords, timestamps, CTAs, hashtags)
- Tags (broad + long-tail mix)
- Thumbnail guidance
3. Publish (update fields + make public + move to target playlist)
Overwrite all fields with SEO-optimized versions, change privacy to public, add to a themed playlist, and remove from the "Unlisted" queue.
CHANNEL_ID=$(yutu channel list --for mine --output json | jq -r '.[0].id')
# Update video metadata and publish
yutu video update --id VIDEO_ID \
--title 'Husky vs Octopus Water Fight!' \
--description 'Watch this hilarious husky get sprayed by an octopus...' \
--tags 'husky,octopus,funny animals,pets' \
--privacy public --yes
# Set thumbnail
yutu thumbnail set --file thumbnail.jpg --videoId VIDEO_ID
# Add to target playlist
yutu playlistItem insert --kind video \
--playlistId TARGET_PLAYLIST_ID --channelId "$CHANNEL_ID" \
--kVideoId VIDEO_ID --yes
# Remove from "Unlisted" queue (find the playlistItem ID first)
ITEM_ID=$(yutu playlistItem list --playlistId UNLISTED_PLAYLIST_ID --maxResults 0 --output json \
| jq -r '.[] | select(.snippet.resourceId.videoId == "VIDEO_ID") | .id')
yutu playlistItem delete --ids "$ITEM_ID" --yes
Find Unlisted/Private Videos
The search API only returns public videos. To find unlisted or private videos, use the uploads playlist which contains ALL uploads regardless of privacy status.
# 1. Get the uploads playlist ID (replace UC→UU in channel ID)
UPLOADS=$(yutu channel list --for mine --parts id,contentDetails --output json \
| jq -r '.[0].contentDetails.relatedPlaylists.uploads')
# 2. List all videos in the uploads playlist
yutu playlistItem list --playlistId "$UPLOADS" --maxResults 0 --output json > uploads.json
# 3. Extract video IDs and query privacy status in batches of 50
cat uploads.json | jq -r '.[].snippet.resourceId.videoId' | \
xargs -n50 | while read BATCH; do
yutu video list --ids "$(echo $BATCH | tr ' ' ',')" \
--parts id,snippet,status --output json
done | jq '.[] | select(.status.privacyStatus == "unlisted") | {id, title: .snippet.title, published: .snippet.publishedAt}'
Key points:
search listonly returns public videos.- The uploads playlist ID follows the pattern: channel
UCxxx→ playlistUUxxx. video list --idsaccepts at most 50 IDs per call — batch accordingly.- The
statuspart containsprivacyStatus(public/unlisted/private).