Google Sheets - Youtube playlist

Started to play with YouTube data api, the goal was to retrieve some basic data about videos as well as managing play lists, but after a while realized that things may be achieved much easily right from Google Sheets

In my demo I have an spreadsheet with YouTube links and custom menu that will call sync function

Under the hood sync function will do its best to make things happen

Here is an sample code I have ended up with

/*
I will be called each time spread sheet is opened

Create YouTube \ Sync menu item that will trigger synchronization
Alternative approach will be to run in onEdit
Also we can schedule onSync run
This approach choose because it is not expected for list to change often
Also it will be changed manually and just to have an example of custom menu
*/
function onOpen() {
  SpreadsheetApp.getUi().createMenu("YouTube").addItem("Sync", "onSync").addToUi()
}

/*
Callback for YouTube \ Sync menu item

Extracted here because of alert popup which makes it impossible to run sync from editor
*/
function onSync() {
  var {items, removed, added, rearanged} = sync()
  var message = "YouTube sync completed\n\n"
  message += "- " + items.length + " items synced in playlist\n"

  if (removed) {
    message += "- " + removed + " items removed\n"
  }
  if (added) {
    message += "- " + removed + " items added\n"
  }
  if (rearanged) {
    message += "- " + rearanged + " items rearanged\n"
  }
  if (!removed && !added && !rearanged) {
    message += "- " + "nothing changed"
  }

  SpreadsheetApp.getUi().alert(message)
}

/*
Sync videos from Google Sheet to YouTube

How it works:
1. retrieve links from spread sheet
2. retrieve items from playlist
3. remove unwanted items from playlist
4. add missing items to playlist
5. rearange items
*/
function sync() {
  var removed = 0
  var added = 0
  var rearanged = 0
  console.log("retrieving videos from youtube playlist")
  var items = getVideosFromPlaylist()
  console.log(items.length + " videos retrieved from youtube")
  console.log("retrieving videos from spreadsheet")
  var videos = getVideosFromSheet()
  console.log(videos.length + " videos retrieved from spreadsheet")

  // remove unwanted items
  console.log("removing unwanted items")
  for(var item of items) {
    if(!videos.includes(item.videoId)) {
      console.log("removing " + item.videoId)
      YouTube.PlaylistItems.remove(item.id)
      removed = removed + 1
    }
  }
  console.log(removed + " items removed")

  // add missing videos
  console.log("adding missing videos")
  for(var videoId of videos) {
    if (!items.find(item => item.videoId == videoId)) {
      console.log("inserting " + item.videoId)
      YouTube.PlaylistItems.insert({
        snippet: {
          playlistId: getPlaylistId(),
          resourceId: {
            kind: "youtube#video",
            videoId: videoId
          }
        }
      }, ["snippet"])
      added = added + 1
    } else {
      console.log("skipping insert for " + item.videoId)
    }
  }
  console.log(added + " items added")

  // reset index
  console.log("retrieving videos from youtube playlist")
  items = getVideosFromPlaylist()
  console.log(items.length + " videos retrieved from youtube")
  console.log("rearanging items")
  for(var item of items) {
    var position = videos.indexOf(item.videoId)
    if (item.position != position) {
      console.log('reset index of ' + item.videoId + ' to ' + position)
      YouTube.PlaylistItems.update({
        id: item.id,
        snippet: {
          playlistId: getPlaylistId(),
          resourceId: {
            kind: "youtube#video",
            videoId: item.videoId,
            position: position
          }
        }
      }, ["snippet"])
      rearanged = rearanged + 1
    } else {
      console.log("skipping reorder for " + item.videoId)
    }
  }
  console.log(rearanged + " items rearanged")

  return {items, removed, added, rearanged}
}

/*
Retrive YouTube playlist items

Nothing special here, extracted only for readability
The code little bit longer than needed only because of paginated response
*/
function getVideosFromPlaylist() {
  var items = []
  var nextPageToken = ''
  while (nextPageToken != null) {
    var response = YouTube.PlaylistItems.list(["snippet"], {
      playlistId: getPlaylistId(),
      maxResults: 50,
      pageToken: nextPageToken
    })
    for(var item of response.items) {
      items.push({
        id: item.id,
        position: item.snippet.position,
        videoId: item.snippet.resourceId.videoId
      })
    }
    nextPageToken = response.nextPageToken
  }
  //console.log(items)
  //console.log(items.length)
  return items
}

/*
Retrieve YouTube links from spreadsheet

Things to fix/consider:
- getDataRange instead of getRange - so data retrieving will be more dynamic - aka even if I will move links to sibling column it should still work
- in sample I did not bother at all but it should use regular expression matching to catch links
*/
function getVideosFromSheet() {
  return SpreadsheetApp.getActive().getSheetByName("Sheet1").getRange(1,1,1000).getValues().map(x => x.shift()).filter(x => !!x).map(x => x.replace('https://www.youtube.com/watch?v=', ''))
}

/*
Nevermind
*/
function getPlaylistId() {
  return "PL2jJW0z9auIfNcigq0-yVYVLiD--febPX"
}

Notes:

  • for this to work app script should be attached to GCP project, otherwise Google wont authorize it
  • nothing special in code, just saving it for future
  • api reference is helpfull to understand which params are available
  • samples have some basic examples as well