Search code examples
google-apps-scriptgoogle-apps

YouTube Apps Script API only runnable by accounts without a YouTube channel?


This is such a strange issue and there seems to be no documentation of it anywhere online.

Whenever I try to execute a script that requires authorization to access an API, the code will only continue executing after authorization if I choose to authorize it from a GMail account, but not a YouTube account.

The code literally does not execute after choosing to authorize it from a YouTube account. Even a Logger.Log() call on Line 1 of the function will not trigger unless the code is authorized from a GMail account only.

When authorizing from a YouTube account, it will continually loop and request authorization forever without continuing. When authorizing from a GMail account, the code doesn't work because it is requesting YouTube data.

The problem of course is that I'm trying to access Analytics data from my YouTube account, which the GMail account does not have.

Really hoping someone is able to provide insight into this. I've tested in multiple browsers, different YouTube accounts, GMails, etc. and the issue persists.

It also happens with any code that requests YouTube's Analytics API. Replicable with the sample code found here, pasted below: https://developers.google.com/youtube/analytics/v1/code_samples/apps-script#export_youtube_analytics_data_to_google_sheets

function spreadsheetAnalytics() {
  // Get the channel ID
  var myChannels = YouTube.Channels.list('id', {mine: true});
  var channel = myChannels.items[0];
  var channelId = channel.id;

  // Set the dates for our report
  var today = new Date();
  var oneMonthAgo = new Date();
  oneMonthAgo.setMonth(today.getMonth() - 1);
  var todayFormatted = Utilities.formatDate(today, 'UTC', 'yyyy-MM-dd')
  var oneMonthAgoFormatted = Utilities.formatDate(oneMonthAgo, 'UTC', 'yyyy-MM-dd');

  // The YouTubeAnalytics.Reports.query() function has four required parameters and one optional
  // parameter. The first parameter identifies the channel or content owner for which you are
  // retrieving data. The second and third parameters specify the start and end dates for the
  // report, respectively. The fourth parameter identifies the metrics that you are retrieving.
  // The fifth parameter is an object that contains any additional optional parameters
  // (dimensions, filters, sort, etc.) that you want to set.
  var analyticsResponse = YouTubeAnalytics.Reports.query(
    'channel==' + channelId,
    oneMonthAgoFormatted,
    todayFormatted,
    'views,likes,dislikes,shares',
    {
      dimensions: 'day',
      sort: '-day'
    });

  // Create a new Spreadsheet with rows and columns corresponding to our dates
  var ssName = 'YouTube channel report ' + oneMonthAgoFormatted + ' - ' + todayFormatted;
  var numRows = analyticsResponse.rows.length;
  var numCols = analyticsResponse.columnHeaders.length;

  // Add an extra row for column headers
  var ssNew = SpreadsheetApp.create(ssName, numRows + 1, numCols);

  // Get the first sheet
  var sheet = ssNew.getSheets()[0];

  // Get the range for the title columns
  // Remember, spreadsheets are 1-indexed, whereas arrays are 0-indexed
  var headersRange = sheet.getRange(1, 1, 1, numCols);
  var headers = [];

  // These column headers will correspond with the metrics requested
  // in the initial call: views, likes, dislikes, shares
  for(var i in analyticsResponse.columnHeaders) {
    var columnHeader = analyticsResponse.columnHeaders[i];
    var columnName = columnHeader.name;
    headers[i] = columnName;
  }
  // This takes a 2 dimensional array
  headersRange.setValues([headers]);

  // Bold and freeze the column names
  headersRange.setFontWeight('bold');
  sheet.setFrozenRows(1);

  // Get the data range and set the values
  var dataRange = sheet.getRange(2, 1, numRows, numCols);
  dataRange.setValues(analyticsResponse.rows);

  // Bold and freeze the dates
  var dateHeaders = sheet.getRange(1, 1, numRows, 1);
  dateHeaders.setFontWeight('bold');
  sheet.setFrozenColumns(1);

  // Include the headers in our range. The headers are used
  // to label the axes
  var range = sheet.getRange(1, 1, numRows, numCols);
  var chart = sheet.newChart()
                   .asColumnChart()
                   .setStacked()
                   .addRange(range)
                   .setPosition(4, 2, 10, 10)
                   .build();
  sheet.insertChart(chart);

}
youtube.gs

Solution

  • There is a related issue ticket which Google have marked as 'Wont't fix' which describes your problem:

    If you are trying to use the YouTube API to get data about a YouTube Channel that you manage using the Google+ association system, it will get you stuck in an infinite loop. Google+ pages can “own” a YouTube channel, and the authorization will not work when trying to use one of those channels

    Google do have a suggested workaround:

    use the OAuth2 library to authorize access to the G+ page, and then use UrlFetchApp instead of the YouTube Advanced service to make requests to the API

    As this is a problem I've also encountered I've published a detailed tutorial that uses the YouTube Data API. As the code you require also needs access via to the YouTube Analytics API there is some additional setup required detailed below and referencing the part in the tutorial:

    1. Add a YouTube Analytics library with id 1MWD64g7dq_ZhlN8HU_O6BRu5xNwywhp8V76utKowZEtcirEgO3t_JFFL (Part 1.4)
    2. Add additional scopes to getYouTubeService() for yt-analytics-monetary.readonly and yt-analytics.readonly (Part 2.2)

      // Set the scope and additional Google-specific parameters.
      .setScope(["https://www.googleapis.com/auth/youtube",
                "https://www.googleapis.com/auth/youtube.force-ssl",
                "https://www.googleapis.com/auth/youtube.readonly",
                "https://www.googleapis.com/auth/youtubepartner",
                "https://www.googleapis.com/auth/youtubepartner-channel-audit",
                "https://www.googleapis.com/auth/yt-analytics-monetary.readonly",
                "https://www.googleapis.com/auth/yt-analytics.readonly"])
      
    3. Enabling the YouTube Analytics API as well as the YouTube Data API (Part 3.4)

    There is also some minor modification in your code sample as to enable the library autocomplete e.g. YouTube.Channels.list() becomes YouTube.channelsList() and YouTubeAnalytics.Reports.query() becomes YouTubeAnalytics.reportsQuery()

    All these changes have been included in this example sheet. You'll still need to do all the setup of the console project outlined in the tutorial and in the auth.gs sheet (I've modified the example to run in @OnlyCurrentDoc to avoid app verification).

    Note: when you run logRedirectUri() you need to authenticate with your Google Drive account and when copy the authentication url in a new browser tab select the YouTube account you want data for.