Showing posts sorted by relevance for query youtube_tools. Sort by date Show all posts
Showing posts sorted by relevance for query youtube_tools. Sort by date Show all posts

Friday, November 03, 2017

youtube_tools v2.0: Now with Playlist Management Support

Last month I finally got frustrated enough with YouTube's subscription management tools that I took matters into my own hands and wrote a command line tool to take over the job. Earlier this week I set my sights on a similar problem: playlist management.

I wanted a quick way to rename, delete and extract a YouTube playlist data. Extending my original youtube_tools wasn't that hard. And while I was at it, I added full support for paging.

Here's a few examples of the tool in action. Like most command line tools, uses are limited only by your imagination.

# Get a few playlists to work with
$ youtube_tools -a playlists | tail -5
PL755C43F40CCFC4A3:Songs To Make Shira Smile
PL1ED10A844DF6863C:Rock Music that Rocks
PL629D105C873357DE:Sweet Country Music
PL933A9F07113418D8:Funky Music
FLCsd3dwyF1qnzzmk7vrRbbQ:Favorites

# Rename one
$ youtube_tools -a playlist-rename \
   -i PL629D105C873357DE -n "Totally Sweet Country Music"
PL629D105C873357DE:Totally Sweet Country Music

# See how many entries are in a playlist
$ youtube_tools -a playlist -i PL629D105C873357DE | wc -l
26

# Any by Brooks & Dunn?
youtube_tools -a playlist -i PL629D105C873357DE | grep Brooks
UEw2MjlEMTA1Qzg3MzM1N0RFLjFEMzAyRkUyQjlEQzA3ODI=:nopBvlKfYgY:Brooks And Dunn Neon Moon
UEw2MjlEMTA1Qzg3MzM1N0RFLjFFNjE4RTg3MjUyNTNGOUE=:6gS6NucKt20:Brooks and Dunn - Play Something Country [ New Video + Lyrics + Tab + Download ]

# Generate a list of URLs to watch these videos
$ youtube_tools -a playlist -i PL629D105C873357DE | grep Brooks | 
    cut -d ':' -f 2 |
    while read x ; do echo "http://youtube.com/watch?v=$x";done
http://youtube.com/watch?v=nopBvlKfYgY
http://youtube.com/watch?v=6gS6NucKt20

# Print out the thumbnail images for each these videos
$ youtube_tools -a playlist -i PL629D105C873357DE -v 
  | jq -r '.items[] |  .snippet.thumbnails.high.url + ":" + .snippet.title '|
  grep Brooks
https://i.ytimg.com/vi/nopBvlKfYgY/hqdefault.jpg:Brooks And Dunn Neon Moon
https://i.ytimg.com/vi/6gS6NucKt20/hqdefault.jpg:Brooks and Dunn - Play Something Country [ New Video + Lyrics + Tab + Download ]

# Generate a trivial HTML page that contains all the videos
$echo "<html><body><h1>My Country Playlist</h1>"; 
 youtube_tools -a playlist -i PL629D105C873357DE | cut -d ':' -f 2 |
  while read x ; do
    echo "<iframe src='https://youtube.com/embed/$x' width='560' height='300'></iframe>";
  done;
  echo "</body></html>"
<html><body><h1>My Country Playlist</h1>
<iframe src='https://youtube.com/embed/q3zkkLckeyM' width='560' height='300'></iframe>
<iframe src='https://youtube.com/embed/IO7zCCJ2bXo' width='560' height='300'></iframe>
...

The source code for youtube_tools is found here. Feel free to grab it and hack away.

Wednesday, October 04, 2017

One Less Reason to Curse YouTube -- Managing Subscriptions from the Command Line

Every time I look over at the channels I'm subscribed to in YouTube I have this internal discussion:

  1. My gosh, how did that list of subscriptions get so big? I sure wish I had a way to trim it down without going through the tedious process of manually unusbscribing.
  2. I know, I'll write a command line script to manage my YouTube subscriptions!
  3. But that will take time ... let me just muscle through and manually unsubscribe from channels I don't want to follow any longer.
  4. (Goes through the process of unsubscribing from two channels)
  5. Ugh! This is painful. I give up.
  6. And repeat...

Today, however, I finally broke the cycle! I decided to write a tool that will let me list and delete subscriptions from the command line. This wasn't really a stretch, I've written code that works with the Blogger Data API, so I figured working with the YouTube Data API wouldn't be any more difficult.

Step 1, I created a small command line utility to grant me OAuth access to my YouTube account:

#!/bin/bash

##
## Authenticate with YouTube Data API
##
USAGE="`basename $0` {auth|refresh|token} ctx"
CTX_DIR=$HOME/.youtube_auth
CLIENT_ID=703261074341-e1vpmqq8kh2v9ke0mveve7dakpbobkft.apps.googleusercontent.com 
CLIENT_SECRET=uRhAcxywxYZF2bhbx7zVqeML

ctx=default

function usage {
  echo "Usage: `basename $0` [-h] [-c context] {init|token}"
  exit
}

function age {
  modified=`stat -c %X $1`
  now=`date +%s`
  expr $now - $modified
}

function refresh {
  refresh_token=`cat $CTX_DIR/$ctx.refresh_token`
  curl -si \
       -d client_id=$CLIENT_ID \
       -d client_secret=$CLIENT_SECRET \
       -d refresh_token=$refresh_token \
       -d grant_type=refresh_token \
       https://accounts.google.com/o/oauth2/token > $CTX_DIR/$ctx.refresh
  grep access_token $CTX_DIR/$ctx.refresh | sed -e 's/.*: "//' -e 's/",//' > $CTX_DIR/$ctx.access_token
}

while getopts :hc: opt ; do
  case $opt in
    c) ctx=$OPTARG ;;
    h) usage ;;
  esac
done
shift $(($OPTIND - 1))

cmd=$1 ; shift

mkdir -p $CTX_DIR
case $cmd in
  init)
    url=`curl -gsi \
         -d scope=https://www.googleapis.com/auth/youtube \
         -d redirect_uri=urn:ietf:wg:oauth:2.0:oob \
         -d response_type=code \
         -d access_type=offline \
         -d client_id=$CLIENT_ID \
         https://accounts.google.com/o/oauth2/v2/auth | \
      grep -i Location: | \
      sed 's/[lL]ocation: //'`
    echo $url
    echo -n "Code? "
    read code
    curl -s \
         -d client_id=$CLIENT_ID \
         -d client_secret=$CLIENT_SECRET \
         -d code=$code \
         -d grant_type=authorization_code \
         -d redirect_uri=urn:ietf:wg:oauth:2.0:oob \
         https://www.googleapis.com/oauth2/v4/token > $CTX_DIR/$ctx.init
    grep access_token $CTX_DIR/$ctx.init | sed -e 's/.*: "//' -e 's/",//' > $CTX_DIR/$ctx.access_token
    grep refresh_token $CTX_DIR/$ctx.init | sed -e 's/.*: "//' -e 's/"//' > $CTX_DIR/$ctx.refresh_token
    echo "Done"
    ;;
  token)
    if [ ! -f $CTX_DIR/$ctx.access_token ] ; then
      echo "Unknown context: $ctx. Try initing first."
      exit
    fi
    age=`age $CTX_DIR/$ctx.access_token`
    if [ $age -gt 3600 ] ; then
      refresh
    fi
    cat $CTX_DIR/$ctx.access_token
    ;;
  *)
    usage
esac

Surprisingly, I needed to use different API end points than I used for other curl based Google OAuth apps. I needed to generate the authentication code at https://accounts.google.com/o/oauth2/v2/auth and generate the auth token at https://www.googleapis.com/oauth2/v4/token.

Once I had an authentication token, life was good. I used the usual suspects to power this script. Mainly, curl to make the appropriate web requests and jq to format the resulting JSON in an intelligent way. You'll notice from the script below that if you pass -v to the script, you get the complete JSON output. Man those tools are awesome; if you don't know them well you're missing out.

Here's a sample run of my shiny new script:

$ youtube_tools  -a subscriptions|grep -i megan
ffVDX-wHSD6NaM3-kGgVug8jUOQIbwozBmWubSPnjtU:Megan Davies

# Nothing personal Megan, but we're breaking up:
$ youtube_tools -a subscription-delete \
 -i `youtube_tools  -a subscriptions |grep Megan | cut -d ':' -f 1`

Now that's how you manage YouTube subscriptions!

I'm not quite sure what else I'm going to do with this tool, but given how much I use YouTube for, I won't be surprised if I add to it.

Here's the code for the script, enjoy!

#!/bin/bash

##
## command line tools for working with YouTube.
## See: https://developers.google.com/youtube/v3/docs/
##
API_BASE=https://www.googleapis.com/youtube/v3
AUTH_TOKEN=`youtube_auth token`

if [ -z "$AUTH_TOKEN" ] ; then
  echo "`basename $0`: authentication not setup. Run: 'youtube_auth init'"
  exit 1
fi

function usage {
  echo "Usage: `basename $0` -a {subscriptions|subscription-delete} [-i ID] -v"
  exit
}

function filter {
  if [ -z "$VERBOSE" ] ; then
    jq "$@"
  else
    cat
  fi
}

while getopts ":a:i:v" opt ; do
  case $opt in
    a) ACTION=$OPTARG ;;
    v) VERBOSE=yes    ;;
    i) ID=$OPTARG     ;;
    \?) usage         ;;
  esac
done

function invoke {
  curl -s -H "Authorization: Bearer $AUTH_TOKEN" "$@"
}

case $ACTION in
  subscriptions)
    invoke -G $API_BASE/subscriptions \
           -d mine=true \
           -d part=snippet \
           -d maxResults=50 | 
      filter -r ' .items[] |  .id + ":" + .snippet.title'
    ;;
  subscription-delete)
    if [ -z "$ID" ] ; then
      echo "Deleting a subscription requires an ID (-i) value"
      usage
    fi
    invoke -X DELETE "$API_BASE/subscriptions?id=$ID"
    ;;
  *)
    usage
    ;;
esac