- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试向 youtube 分析 API 发出请求。我在形成请求时遇到一些困难,因此它们被接受。我正在使用 Google API Node.js 客户端
https://github.com/google/google-api-nodejs-client
我的代码如下
import { Meteor } from 'meteor/meteor';
import google from 'googleapis';
import KEY_FILE from './keyFile.json';
import { CHANNEL_ID } from './channelId.js';
//api's
const analytics = google.youtubeAnalytics('v1');
//fetch youtube analytics
export function youtubeAnalytics(start, end){
//initalise request data
const startDate = `${start.getFullYear()}-${('0'+(start.getMonth()+1)).slice(-2)}-${('0'+(start.getDate())).slice(-2)}`;
const endDate = `${end.getFullYear()}-${('0'+(end.getMonth()+1)).slice(-2)}-${('0'+(end.getDate())).slice(-2)}`;
const scopes = [
'https://www.googleapis.com/auth/youtube',
'https://www.googleapis.com/auth/youtube.readonly',
'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',
'https://www.googleapis.com/auth/yt-analytics-monetary.readonly'
];
//generate authorisation token
var AUTH = new google.auth.JWT(
KEY_FILE.client_email,
null,
KEY_FILE.private_key,
scopes,
null
);
//authorize request
AUTH.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
}
//create request
const analyticsRequest = {
auth: AUTH,
'start-date': startDate,
'end-date': endDate,
ids: `channel==${CHANNEL_ID}`,
metrics: 'views',
};
//make request
analytics.reports.query(analyticsRequest, function (err, data) {
if (err) {
console.error('Error: ' + err);
return false;
}
if (data) {
console.log(data);
return data;
}
});
});
return false;
}
Meteor.methods({youtubeAnalytics});
我不断收到以下错误
Error: Error: Invalid query. Query did not conform to the expectations.
我认为这与我的请求对象有关
const analyticsRequest = {
auth: AUTH,
'start-date': startDate,
'end-date': endDate,
ids: `channel==${CHANNEL_ID}`,
metrics: 'views',
};
但我发现的所有示例都表明此请求对象应该有效。我已经尽可能地简化了它。我最初的请求(我真正想要的)如下。
const analyticsRequest = {
auth: AUTH,
'start-date': startDate,
'end-date': endDate,
ids: `channel==${CHANNEL_ID}`,
metrics: 'views',
dimensions: 'video',
sort: '-views',
'max-results': '200'
}
之后,我需要执行另一个请求来获取使用不同 API 端点的列出视频的所有相关信息。
//api's
const youtube = google.youtube('v3');
/*
do processing of analytics data to create batchRequest
which is a string of comma separated video ids
*/
videoRequest = {
auth: AUTH,
part: 'id,snippet',
id: batchRequest;
}
youtubeApiData.search.list(videosRequest, function (err, data) {
if (err) {
console.error('Error: ' + err);
return false;
}
if (data) {
console.log(data);
return data;
}
});
总结一下
我需要向各种 google api 发出请求,但在形成请求时遇到问题,因此它们被接受(我还没有通过 youtube 分析的第一个请求)。
有人能指出我正确的方向吗?
最佳答案
想通了。无法为此 API 执行服务帐户请求。我通过 account-google 设置帐户验证
https://guide.meteor.com/accounts.html
使用帐户合并将其合并到基本帐户
https://atmospherejs.com/splendido/accounts-meld
这为我提供了附加到用户的访问 token ,然后进行了 OAuth 2.0 调用。 Facebook 也做了类似的事情。
我仍然遇到的一个问题是“视频的获取保留”部分效率非常低。每个视频调用一次电话。 google api 有可用的批量请求,但我似乎无法找到使用“googleapis”包的正确示例。如果有人有一个例子,将不胜感激。
这是新代码。
import { Meteor } from 'meteor/meteor';
import google from 'googleapis';
import { CHANNEL_ID, USER_ID, KEY_FILE, API_KEY, CLIENT_ID, CLIENT_SECRET } from './keys.js';
//api's
const fetchAnalytics = google.youtubeAnalytics('v1');
const fetchYoutube = google.youtube('v3');
const OAuth2 = google.auth.OAuth2;
export function youtubeAnalytics(start, end){
//requires login to generate authorisation
const user = Meteor.user();
if(user && user.services && user.services.google && user.services.google.accessToken){
//-------------------- general setup -------------------- //
//convert async functions to sync functions
const fetchYoutubeSync = Meteor.wrapAsync(fetchYoutube.search.list);
const fetchAnalyticsSync = Meteor.wrapAsync(fetchAnalytics.reports.query);
// set up default values
const analytics = {views: [], retention: []};
const videos = [];
const startDate = `${start.getFullYear()}-${('0'+(start.getMonth()+1)).slice(-2)}-${('0'+(start.getDate())).slice(-2)}`;
const endDate = `${end.getFullYear()}-${('0'+(end.getMonth()+1)).slice(-2)}-${('0'+(end.getDate())).slice(-2)}`;
const startDateLong = `${start.getFullYear()}-${('0'+(start.getMonth()+1)).slice(-2)}-${('0'+(start.getDate())).slice(-2)}T00:00:00.0+00:00`;
const endDateLong = `${end.getFullYear()}-${('0'+(end.getMonth()+1)).slice(-2)}-${('0'+(end.getDate())).slice(-2)}T00:00:00.0+00:00`;
// generate authorisation tokens
const oauth2Client = new OAuth2(CLIENT_ID, CLIENT_SECRET,'');
oauth2Client.setCredentials({access_token: user.services.google.accessToken});
google.options({
auth: oauth2Client
});
//-------------------- fetch videos between dates -------------------- //
let fetch = true;
let next = '';
while (fetch) {
//create video request
let videosRequest = {
auth: oauth2Client,
part: 'id,snippet',
type: 'video',
channelId: CHANNEL_ID,
publishedAfter: startDateLong,
publishedBefore: endDateLong,
maxResults: 50,
pageToken: next,
}
//make video request
let response = fetchYoutubeSync(videosRequest);
//store video request
videos.push.apply(videos, response.items);
//check if more data is available
if(response.nextPageToken){ next = response.nextPageToken; }
else{ fetch = false;}
}
//-------------------- create batch request objects -------------------- //
if(videos.length > 0){
//process videos
let batchRequestViews = '';
let batchRequestRetention = [];
let first = true;
for(let i = 0; i < videos.length; i += 1){
let id = false;
if(videos[i].id.kind === 'youtube#video'){ id = videos[i].id.videoId;}
if(id){
//views processing
if(first){ first = false; batchRequestViews = id;}
else{ batchRequestViews = `${batchRequestViews},${id}`}
//retention processing
let request = {
auth: oauth2Client,
'start-date': startDate,
'end-date': endDate,
ids: `channel==${CHANNEL_ID}`,
metrics: 'audienceWatchRatio',
dimensions: 'elapsedVideoTimeRatio',
filters: `video==${id}`
};
batchRequestRetention.push(request);
}
}
//-------------------- fetch views for videos -------------------- //
// create views request
const analyticsRequestViews = {
auth: oauth2Client,
'start-date': startDate,
'end-date': endDate,
ids: `channel==${CHANNEL_ID}`,
metrics: 'views',
dimensions: 'video',
filters: `video==${batchRequestViews}`
};
// make and store views request
analytics.views = fetchAnalyticsSync(analyticsRequestViews).rows;
//-------------------- fetch retention for videos -------------------- //
//make retention batch request
if(batchRequestRetention && batchRequestRetention.length > 0){
for(let i = 0; i < batchRequestRetention.length; i += 1){
//fetch retention request
let request = batchRequestRetention[i];
//make retention request
let response = fetchAnalyticsSync(request);
//store response
analytics.retention.push({id: request.filters.substring(7), response});
}
}
//-------------------- return results -------------------- //
return {videos, analytics};
}
}
return false;
}
Meteor.methods({youtubeAnalytics});
关于angularjs - YouTube 分析 - 创建请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43037261/
是否有在非 AngularJS 页面内初始化 AngularJS 应用程序的最佳实践方法?我正在向现有网页添加新功能,需要传入一个参数。具体来说,有一组选项卡,一个新选项卡将启动一个 Angular
找不到这两者之间的区别。 保留其中任何一个来引导我的 Angular 应用程序是否有意义? angular.bootstrap(document,['myApp']); 或者 angularAMD.b
我试图理解 Packpub 的书附带的示例 AngularJS 应用程序。 app.js文件在 client/src/app 下定义文件夹,它的模块定义看起来像 angular.module('app
Angularjs 具有用于表单验证和显示错误消息的强大基础设施。但是,我处于必须在特定场景中向用户显示警告消息的情况。这是我的简单表格的图表 该表单在两个字段上都应用了必需和模式验证。除了此验证之外
我在重试功能正常工作时遇到了一些麻烦,希望能获得一些帮助。我有一个要调用的$ resource,直到出现成功情况或超过最大重试次数为止。 我似乎遇到的问题是,在我的重试函数中,我正在调用另一个prom
我目前正在开发一个 AngularJS 应用程序,我遇到了以下障碍。 当用户提交时,我们有一个 login 页面,我们调用一个 web api 并对用户进行身份验证,我们目前正在使用 claims 身
当范围更新时,指令的属性不会改变,它们仍然保持初始值。我在这里缺少什么? HTML works great works: {{foo}} Javascript (基于首页上的 A
我正在使用 Zurb 的 Foundation 框架修改应用程序以实现响应性和 AngularJS。存在数据显示在带有 ... 的表中的错误有 是根据 Foundation 的响应规则隐藏/显示的。不
在过去的三天里,我一直在搜寻互联网,试图弄清楚当angular注意到div的宽度发生变化时如何使指令运行。 我不断看到相同的示例,说明如何实现此目标,但是它们对我不起作用,我也不知道为什么。 我回到一
我正在使用以下代码尝试汇总 在 Angular ,这在整个作品中,但是小于 0.5 的数字四舍五入为 0。我想 向上取整 每个数字到下一个整数。例如 0.02 应四舍五入为 1 {{((data.Vi
我目前正在尝试以一种能够适当扩展到企业级别的方式来组织我的 Angular 应用程序。但是我发现似乎过度依赖框架内的命名约定,并且试图避免命名冲突是一个真正的问题。 例如,当定义任何 constant
我正在阅读 AngularJS 基础知识,并且喜欢在我的页面中使用它的绑定(bind)功能。所以我可以定义可以在 View 中显示的数据,可以对数据进行更改,以便在 View 中更改它而无需担心。 在
在父 Controller 范围内,我定义了 selectedItem设置为“x”。然后在子范围内,我定义了selectedItem使用 ngModel:
关闭。这个问题需要更多 focused .它目前不接受答案。 想改进这个问题?更新问题,使其仅关注一个问题 editing this post . 5年前关闭。 Improve this questi
如果2个条件为真,我试图将一个特定的类应用于li元素,因此我编写了以下代码,但似乎无法正常工作 ng-class="{current:isActive('/'), loginStatus: false
请看看朋克。 http://plnkr.co/edit/DuTFYbLVbPkCIvRznYjG?p=preview ng-pattern regEx不适用于输入文本字段。 仅在需要验证的情况下才能正
我正在为iOS + Android构建AngularJS(1.x)和Ionic/Cordova移动应用程序。我想在登录页面上添加/创建“深层链接”,以便在我向新用户发送“确认您的电子邮件”电子邮件时,
angularjs 中服务(或工厂)的生命周期是什么,何时重新初始化? 最佳答案 当 Angular 启动时,它会将服务的构造函数附加到关联的模块上。这种情况发生一次。 angular .modu
我对 Angular 很陌生,所以希望我知道的足够多,可以问什么似乎是合理的设计问题。 我正在通过 Angular 绘制一些数据,并且正在使用 $resource。在将 Angular 引入项目之前,
我需要在我的 angular-breeze 应用程序中使用国家/地区下拉菜单,我尝试了以下操作: https://github.com/banafederico/angularjs-country-s
我是一名优秀的程序员,十分优秀!