add playlist-item resource

This commit is contained in:
ricardo 2020-08-15 22:36:11 -04:00
parent 091659df29
commit 54f103776e
5 changed files with 605 additions and 7 deletions

View file

@ -103,7 +103,7 @@ export const channelFields = [
}, },
}, },
description: 'The fields parameter specifies a comma-separated list of one or more channel resource properties that the API response will include.', description: 'The fields parameter specifies a comma-separated list of one or more channel resource properties that the API response will include.',
default: '' default: ['*'],
}, },
{ {
displayName: 'Return All', displayName: 'Return All',
@ -308,7 +308,7 @@ export const channelFields = [
}, },
}, },
description: 'The fields parameter specifies a comma-separated list of one or more channel resource properties that the API response will include.', description: 'The fields parameter specifies a comma-separated list of one or more channel resource properties that the API response will include.',
default: '' default: ['*'],
}, },
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* channel:update */ /* channel:update */

View file

@ -214,7 +214,7 @@ export const playlistFields = [
}, },
}, },
description: 'The fields parameter specifies a comma-separated list of one or more playlist resource properties that the API response will include.', description: 'The fields parameter specifies a comma-separated list of one or more playlist resource properties that the API response will include.',
default: '' default: ['*'],
}, },
{ {
displayName: 'Options', displayName: 'Options',
@ -346,7 +346,7 @@ export const playlistFields = [
}, },
}, },
description: 'The fields parameter specifies a comma-separated list of one or more playlist resource properties that the API response will include.', description: 'The fields parameter specifies a comma-separated list of one or more playlist resource properties that the API response will include.',
default: '' default: ['*'],
}, },
{ {
displayName: 'Return All', displayName: 'Return All',

View file

@ -0,0 +1,408 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const playlistItemOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'playlistItem',
],
},
},
options: [
{
name: 'Add',
value: 'add',
description: 'Add an item to a playlist',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a item from a playlist',
},
{
name: 'Get',
value: 'get',
description: `Get a playlist's item`,
},
{
name: 'Get All',
value: 'getAll',
description: 'Retrieve all playlist items',
},
],
default: 'add',
description: 'The operation to perform.'
}
] as INodeProperties[];
export const playlistItemFields = [
/* -------------------------------------------------------------------------- */
/* playlistItem:add */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist ID',
name: 'playlistId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getPlaylists',
},
required: true,
displayOptions: {
show: {
operation: [
'add',
],
resource: [
'playlistItem',
],
},
},
default: '',
},
{
displayName: 'Video ID',
name: 'videoId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: [
'add',
],
resource: [
'playlistItem',
],
},
},
default: '',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'add',
],
resource: [
'playlistItem',
],
},
},
options: [
{
displayName: 'Position',
name: 'position',
type: 'number',
typeOptions: {
minValue: 0,
},
default: '',
description: `The order in which the item appears in the playlist. The value uses a zero-based index, so the first item has a position of 0, the second item has a position of 1, and so forth.`,
},
{
displayName: 'Note',
name: 'note',
type: 'string',
default: '',
description: `A user-generated note for this item. The property value has a maximum length of 280 characters.`,
},
{
displayName: 'Start At',
name: 'startAt',
type: 'dateTime',
default: '',
description: `The time, measured in seconds from the start of the video, when the video should start playing.`,
},
{
displayName: 'End At',
name: 'endAt',
type: 'dateTime',
default: '',
description: `The time, measured in seconds from the start of the video, when the video should stop playing.`,
},
{
displayName: 'On Behalf Of Content Owner',
name: 'onBehalfOfContentOwner',
type: 'string',
default: '',
description: `The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify<br>
a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value`,
},
],
},
/* -------------------------------------------------------------------------- */
/* playlistItem:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist Item ID',
name: 'playlistItemId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'playlistItem',
],
},
},
default: '',
},
{
displayName: 'Fields',
name: 'part',
type: 'multiOptions',
options: [
{
name: '*',
value: '*',
},
{
name: 'Content Details',
value: 'contentDetails',
},
{
name: 'ID',
value: 'id',
},
{
name: 'Snippet',
value: 'snippet',
},
{
name: 'Status',
value: 'status',
},
],
required: true,
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'playlistItem',
],
},
},
description: 'The fields parameter specifies a comma-separated list of one or more playlistItem resource properties that the API response will include.',
default: ['*'],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'playlistItem',
],
},
},
options: [
{
displayName: 'On Behalf Of Content Owner',
name: 'onBehalfOfContentOwner',
type: 'string',
default: '',
description: `The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify<br>
a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value`,
},
],
},
/* -------------------------------------------------------------------------- */
/* playlistItem:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist Item ID',
name: 'playlistItemId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: [
'delete',
],
resource: [
'playlistItem',
],
},
},
default: '',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'delete',
],
resource: [
'playlistItem',
],
},
},
options: [
{
displayName: 'On Behalf Of Content Owner',
name: 'onBehalfOfContentOwner',
type: 'string',
default: '',
description: `The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify<br>
a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value`,
},
],
},
/* -------------------------------------------------------------------------- */
/* playlistItem:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist ID',
name: 'playlistId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getPlaylists',
},
required: true,
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'playlistItem',
],
},
},
default: ''
},
{
displayName: 'Fields',
name: 'part',
type: 'multiOptions',
options: [
{
name: '*',
value: '*',
},
{
name: 'Content Details',
value: 'contentDetails',
},
{
name: 'ID',
value: 'id',
},
{
name: 'Snippet',
value: 'snippet',
},
{
name: 'Status',
value: 'status',
},
],
required: true,
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'playlistItem',
],
},
},
description: 'The fields parameter specifies a comma-separated list of one or more playlistItem resource properties that the API response will include.',
default: ['*'],
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'playlistItem',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'playlistItem',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 50,
},
default: 25,
description: 'How many results to return.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'playlistItem',
],
},
},
options: [
{
displayName: 'On Behalf Of Content Owner',
name: 'onBehalfOfContentOwner',
type: 'string',
default: '',
description: `The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify<br>
a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value`,
},
],
},
] as INodeProperties[];

View file

@ -1,7 +1,6 @@
import { import {
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { id } from 'rhea';
export const videoOperations = [ export const videoOperations = [
{ {
@ -384,7 +383,7 @@ export const videoFields = [
}, },
}, },
description: 'The fields parameter specifies a comma-separated list of one or more video resource properties that the API response will include.', description: 'The fields parameter specifies a comma-separated list of one or more video resource properties that the API response will include.',
default: '' default: ['*'],
}, },
{ {
displayName: 'Options', displayName: 'Options',

View file

@ -27,6 +27,11 @@ import {
playlistFields, playlistFields,
} from './PlaylistDescription'; } from './PlaylistDescription';
import {
playlistItemOperations,
playlistItemFields,
} from './PlaylistItemDescription';
import { import {
videoOperations, videoOperations,
videoFields, videoFields,
@ -60,7 +65,7 @@ export class YouTube implements INodeType {
{ {
name: 'youTubeOAuth2Api', name: 'youTubeOAuth2Api',
required: true, required: true,
} },
], ],
properties: [ properties: [
{ {
@ -76,6 +81,10 @@ export class YouTube implements INodeType {
name: 'Playlist', name: 'Playlist',
value: 'playlist', value: 'playlist',
}, },
{
name: 'Playlist Item',
value: 'playlistItem',
},
{ {
name: 'Video', name: 'Video',
value: 'video', value: 'video',
@ -94,6 +103,9 @@ export class YouTube implements INodeType {
...playlistOperations, ...playlistOperations,
...playlistFields, ...playlistFields,
...playlistItemOperations,
...playlistItemFields,
...videoOperations, ...videoOperations,
...videoFields, ...videoFields,
@ -169,6 +181,33 @@ export class YouTube implements INodeType {
} }
return returnData; return returnData;
}, },
// Get all the playlists to display them to user so that he can
// select them easily
async getPlaylists(
this: ILoadOptionsFunctions
): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const qs: IDataObject = {};
qs.part = 'snippet';
qs.mine = true;
const playlists = await googleApiRequestAllItems.call(
this,
'items',
'GET',
'/youtube/v3/playlists',
{},
qs,
);
for (const playlist of playlists) {
const playlistName = playlist.snippet.title;
const playlistId = playlist.id;
returnData.push({
name: playlistName,
value: playlistId
});
}
return returnData;
},
} }
}; };
@ -616,6 +655,158 @@ export class YouTube implements INodeType {
responseData = { success: true }; responseData = { success: true };
} }
} }
if (resource === 'playlistItem') {
//https://developers.google.com/youtube/v3/docs/playlistItems/list
if (operation === 'get') {
let part = this.getNodeParameter('part', i) as string[];
const playlistItemId = this.getNodeParameter('playlistItemId', i) as string;
const options = this.getNodeParameter('options', i) as IDataObject;
if (part.includes('*')) {
part = [
'contentDetails',
'id',
'snippet',
'status',
];
}
qs.part = part.join(',');
qs.id = playlistItemId;
Object.assign(qs, options);
responseData = await googleApiRequest.call(
this,
'GET',
`/youtube/v3/playlistItems`,
{},
qs
);
responseData = responseData.items;
}
//https://developers.google.com/youtube/v3/docs/playlistItems/list
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
let part = this.getNodeParameter('part', i) as string[];
const options = this.getNodeParameter('options', i) as IDataObject;
const playlistId = this.getNodeParameter('playlistId', i) as string;
//const filters = this.getNodeParameter('filters', i) as IDataObject;
if (part.includes('*')) {
part = [
'contentDetails',
'id',
'snippet',
'status',
];
}
qs.playlistId = playlistId;
qs.part = part.join(',');
Object.assign(qs, options);
if (returnAll) {
responseData = await googleApiRequestAllItems.call(
this,
'items',
'GET',
`/youtube/v3/playlistItems`,
{},
qs
);
} else {
qs.maxResults = this.getNodeParameter('limit', i) as number;
responseData = await googleApiRequest.call(
this,
'GET',
`/youtube/v3/playlistItems`,
{},
qs
);
responseData = responseData.items;
}
}
//https://developers.google.com/youtube/v3/docs/playlistItems/insert
if (operation === 'add') {
const playlistId = this.getNodeParameter('playlistId', i) as string;
const videoId = this.getNodeParameter('videoId', i) as string;
const options = this.getNodeParameter('options', i) as IDataObject;
qs.part = 'snippet, contentDetails';
const body: IDataObject = {
snippet: {
playlistId,
resourceId: {
kind: 'youtube#video',
videoId: videoId,
},
},
contentDetails: {
},
};
if (options.position) {
//@ts-ignore
body.snippet.position = options.position as number;
}
if (options.note) {
//@ts-ignore
body.contentDetails.note = options.note as string;
}
if (options.startAt) {
//@ts-ignore
body.contentDetails.startAt = options.startAt as string;
}
if (options.endAt) {
//@ts-ignore
body.contentDetails.endAt = options.endAt as string;
}
if (options.onBehalfOfContentOwner) {
qs.onBehalfOfContentOwner = options.onBehalfOfContentOwner as string;
}
responseData = await googleApiRequest.call(
this,
'POST',
'/youtube/v3/playlistItems',
body,
qs
);
}
//https://developers.google.com/youtube/v3/docs/playlistItems/delete
if (operation === 'delete') {
const playlistItemId = this.getNodeParameter('playlistItemId', i) as string;
const options = this.getNodeParameter('options', i) as IDataObject;
const body: IDataObject = {
id: playlistItemId,
};
if (options.onBehalfOfContentOwner) {
qs.onBehalfOfContentOwner = options.onBehalfOfContentOwner as string;
}
responseData = await googleApiRequest.call(
this,
'DELETE',
'/youtube/v3/playlistItems',
body,
);
responseData = { success: true };
}
}
if (resource === 'video') { if (resource === 'video') {
//https://developers.google.com/youtube/v3/docs/search/list //https://developers.google.com/youtube/v3/docs/search/list
if (operation === 'getAll') { if (operation === 'getAll') {