【问题标题】:Is it possible to load google photos metadata into google sheets?是否可以将谷歌照片元数据加载到谷歌表格中?
【发布时间】:2019-07-13 23:43:25
【问题描述】:

我有一个项目,我扫描了早在 1900 年代的 10,000 张家庭照片,我将它们整理到 Google 照片中。我有一个电子表格,我在其中跟踪整个集合的正确日期和标题。我会一次组织一些,但最近发现了 google photos API。

我想使用Method: mediaItems.listMethod: mediaItems.search 之类的方法将照片中的数据导入电子表格进行管理。

这些示例的输出正是我要查找的内容,并且希望将其加载到电子表格中。

如果有办法再次从工作表更新回来,那就太棒了。

我找到了this article,但提供的代码对我不起作用。

我的工作表中现在有这个功能

function photoAPI() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length); 
  albums_sh.clear();
  var narray = []; 

  var api = "https://photoslibrary.googleapis.com/v1/albums";
  var headers = { "Authorization": "Bearer " +  ScriptApp.getOAuthToken() };
  var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };

  var param= "", nexttoken;
  do {
    if (nexttoken)
      param = "?pageToken=" + nexttoken; 
    var response = UrlFetchApp.fetch(api + param, options);
    var json = JSON.parse(response.getContentText());
    json.albums.forEach(function (album) {
      var data = [
        album.title,
        album.mediaItemsCount,
        album.productUrl
      ];
      narray.push(data);
    });
    nexttoken = json.nextPageToken;
  } while (nexttoken);
  albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}

当我在调试模式下运行它时,我收到以下错误

({error:{code:403, message:"Request has enough authentication scopes.", status:"PERMISSION_DENIED"}})

我知道这意味着我需要进行身份验证,但不知道如何实现。

我有一个来自 Google 照片 API 页面的 API 密钥和秘密。

编辑 我使用来自@Tanaike 的链接来弄清楚如何将范围添加到我的项目中。 我添加了这三个。

  • 电子表格.currentonly
  • 图片库
  • script.external_request

现在当我在调试模式下运行时,我收到一个 403 错误,表明我需要设置我的 API。错误摘要如下:

错误: 代码:403 照片库 API 之前未在项目 130931490217 中使用或已禁用。通过访问启用它 https://console.developers.google.com/apis/api/photoslibrary.googleapis.com/overview?project=130931490217 谷歌开发者控制台 API 激活 type.googleapis.com/google.rpc.Help “PERMISSION_DENIED”

但当我尝试访问列出的 URL 时,我只收到一条消息“加载失败”。

【问题讨论】:

  • 能否确认https://www.googleapis.com/auth/photoslibrary.readonlyhttps://www.googleapis.com/auth/photoslibrary的范围是否包含在您项目的appsscript.json的范围内?参考文献是thisthis
  • 您是否看过这里以查看是否有任何答案提供了有效的解决方案? stackoverflow.com/questions/51271207
  • @Tanaike 感谢您提供这些参考。我现在在appsscript.json 中为https://www.googleapis.com/auth/photoslibrary 设置了范围我现在有一个不同的问题,我将在主要问题中添加详细信息。
  • @RafaGuillermo 感谢您的链接。在 Tanaike 的帮助下,我认为您的建议可能是我接下来需要做的。不过,我仍然很困惑如何将这些连接在一起。我有 oauth 凭据,但另一个问题表明所需的凭据可能与我认为的来源不同。我也不知道在我当前的代码中将凭据放在哪里。这就是我所缺少的吗?
  • @Isaac Guerrero 感谢您的回复。如果您使用的是独立脚本或 2019 年 4 月 8 日之后创建的容器绑定脚本,我认为您的新问题的原因是 Cloud Platform Project 和 Google Apps Script Project 没有链接。请链接它们。参考是this

标签: google-apps-script google-sheets google-apis-explorer google-photos-api


【解决方案1】:

我的代码在上面的 cmets 中的 @Tanaike 的帮助下工作。我有两个问题。

1) 我需要在 appsscript.json 中指定 oauthScopes,它默认隐藏在 google 脚本中。可以通过转到菜单并选择 View > Show Manifest File 来显示它。

2) 我使用的默认 GCP 项目没有使用照片 API 的授权并且无法启用。我需要切换到我之前创建并启用了照片 API 的标准 GCP 项目。

这是我最初发布的函数,在我开始工作后带有额外的 cmets:

function photoAPI_ListAlbums() {
  // Modified from code by Stackoverflow user Frç Ju at https://stackoverflow.com/questions/54063937/0auth2-problem-to-get-my-google-photos-libraries-in-a-google-sheet-of-mine
  // which was originally Modified from http://ctrlq.org/code/20068-blogger-api-with-google-apps-script

  /*
  This function retrieves all albums from your personal google photos account and lists each one with the name of album, count of photos, and URL in a new sheet.

  Requires Oauth scopes. Add the below line to appsscript.json
  "oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]

  Also requires a standard GCP project with the appropriate Photo APIs enabled.
  https://developers.google.com/apps-script/guides/cloud-platform-projects
  */

  //Get the spreadsheet object
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  //Check for presence of target sheet, if it does not exist, create one.
  var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length); 
  //Make sure the target sheet is empty
  albums_sh.clear();
  var narray = []; 

  //Build the request string. Default page size is 20, max 50. set to max for speed.
  var api = "https://photoslibrary.googleapis.com/v1/albums?pageSize=50";
  var headers = { "Authorization": "Bearer " +  ScriptApp.getOAuthToken() };
  var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };

  var param= "", nexttoken;

  //Make the first row a title row
  var data = [
    "Title",
    "Item Count",
    "ID",
    "URL"
  ];
  narray.push(data);

  //Loop through JSON results until a nextPageToken is not returned indicating end of data
  do {
    //If there is a nextpagetoken, add it to the end of the request string
    if (nexttoken)
      param = "&pageToken=" + nexttoken; 

    //Get data and load it into a JSON object
    var response = UrlFetchApp.fetch(api + param, options);
    var json = JSON.parse(response.getContentText());

    //Loop through the JSON object adding desired data to the spreadsheet.
    json.albums.forEach(function (album) {
      var data = [
        "'"+album.title,  //The prepended apostrophe makes albums with a name such as "June 2007" to show up as that text rather than parse as a date in the sheet.
        album.mediaItemsCount,
        album.id,
        album.productUrl
      ];
      narray.push(data);
    });

    //Get the nextPageToken
    nexttoken = json.nextPageToken;
    //Continue if the nextPageToaken is not null
  } while (nexttoken);

  //Save all the data to the spreadsheet.
  albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}

这是我以相同风格创建的另一个函数,用于直接提取照片元数据。这就是我最初想要实现的目标。

function photoAPI_ListPhotos() {
  //Modified from above function photoAPI_ListAlbums

  /*
  This function retrieves all photos from your personal google photos account and lists each one with the Filename, Caption, Create time (formatted for Sheet), Width, Height, and URL in a new sheet.
  it will not include archived photos which can be confusing if you happen to have a large chunk of archived photos some pages may return only a next page token with no media items.

  Requires Oauth scopes. Add the below line to appsscript.json
  "oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]

  Also requires a standard GCP project with the appropriate Photo APIs enabled.
  https://developers.google.com/apps-script/guides/cloud-platform-projects
  */

  //Get the spreadsheet object
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  //Check for presence of target sheet, if it does not exist, create one.
  var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length); 
  //Make sure the target sheet is empty
  photos_sh.clear();
  var narray = []; 

  //Build the request string. Max page size is 100. set to max for speed.
  var api = "https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100";
  var headers = { "Authorization": "Bearer " +  ScriptApp.getOAuthToken() };
  var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };

  //This variable is used if you want to resume the scrape at some page other than the start. This is needed if you have more than 40,000 photos.
  //Uncomment the line below and add the next page token for where you want to start in the quotes.
  //var nexttoken="";

  var param= "", nexttoken;
  //Start counting how many pages have been processed.
  var pagecount=0;

  //Make the first row a title row
  var data = [
    "Filename",
    "description",
    "Create Time",
    "Width",
    "Height",
    "ID",
    "URL",
    "NextPage"
  ];
  narray.push(data);

  //Loop through JSON results until a nextPageToken is not returned indicating end of data
  do {
    //If there is a nextpagetoken, add it to the end of the request string
    if (nexttoken)
      param = "&pageToken=" + nexttoken; 

    //Get data and load it into a JSON object
    var response = UrlFetchApp.fetch(api + param, options);
    var json = JSON.parse(response.getContentText());

    //Check if there are mediaItems to process.
    if (typeof json.mediaItems === 'undefined') {
      //If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken

      //var data = ["","","","","","","",json.nextPageToken];
      //narray.push(data);
    } else {
      //Loop through the JSON object adding desired data to the spreadsheet.
      json.mediaItems.forEach(function (MediaItem) {

        //Check if the mediaitem has a description (caption) and make that cell blank if it is not present.
        if(typeof MediaItem.description === 'undefined') {
            var description = "";
          } else {
            var description = MediaItem.description;
          }

        //Format the create date as appropriate for spreadsheets.
        var d = new Date(MediaItem.mediaMetadata.creationTime);

        var data = [
          MediaItem.filename,
          "'"+description, //The prepended apostrophe makes captions that are dates or numbers save in the sheet as a string. 
          d,
          MediaItem.mediaMetadata.width,
          MediaItem.mediaMetadata.height,
          MediaItem.id,
          MediaItem.productUrl,
          json.nextPageToken
        ];
        narray.push(data);
      });
    }

    //Get the nextPageToken
    nexttoken = json.nextPageToken;    

    pagecount++;
    //Continue if the nextPageToaken is not null
    //Also stop if you reach 400 pages processed, this prevents the script from timing out. You will need to resume manually using the nexttoken variable above.
  } while (pagecount<400 && nexttoken);

    //Continue if the nextPageToaken is not null (This is commented out as an alternative and can be used if you have a small enough collection it will not time out.)
  //} while (nexttoken);

  //Save all the data to the spreadsheet.
  photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}

由于 ListPhotos 函数的局限性以及我的图书馆如此庞大的事实,我仍在开发第三个函数,以从特定相册中的所有照片中提取照片元数据。完成后,我将编辑此答案。

【讨论】:

  • 很好的解释,我只是在检查我们如何在清单文件中添加一个新的 google 项目,因为您提到您创建了一个新的 GCP 项目,但是您是否将它添加到清单文件中?
猜你喜欢
  • 2018-03-05
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-13
相关资源
最近更新 更多