From 8c24858f99d7d0b3c38d9201ec69d74b203bded8 Mon Sep 17 00:00:00 2001 From: ricardo Date: Tue, 11 Aug 2020 16:07:23 -0400 Subject: [PATCH] :zap: Improvements --- .../Google/YouTube/ChannelDescription.ts | 92 ++++++++-- .../Google/YouTube/PlaylistDescription.ts | 172 +++++++++++++++--- .../YouTube/VideoCategoryDescription.ts | 95 ++++++++++ .../nodes/Google/YouTube/VideoDescription.ts | 141 +++++++------- .../nodes/Google/YouTube/YouTube.node.ts | 171 ++++++++++++----- 5 files changed, 515 insertions(+), 156 deletions(-) create mode 100644 packages/nodes-base/nodes/Google/YouTube/VideoCategoryDescription.ts diff --git a/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts b/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts index 6db5d3a990..5d08941aeb 100644 --- a/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts +++ b/packages/nodes-base/nodes/Google/YouTube/ChannelDescription.ts @@ -15,6 +15,11 @@ export const channelOperations = [ }, }, options: [ + { + name: 'Get', + value: 'get', + description: 'Retrieve a channel', + }, { name: 'Get All', value: 'getAll', @@ -45,10 +50,6 @@ export const channelFields = [ name: 'part', type: 'multiOptions', options: [ - { - name: 'Audit Details', - value: 'auditDetails', - }, { name: 'Branding Settings', value: 'brandingSettings', @@ -186,13 +187,6 @@ export const channelFields = [ default: false, description: `Set this parameter's value to true to instruct the API to only return channels managed by the content owner that the onBehalfOfContentOwner parameter specifies`, }, - { - displayName: 'Mine', - name: 'mine', - type: 'boolean', - default: false, - description: `This parameter can only be used in a properly authorized request. Set this parameter's value to true to instruct the API to only return channels owned by the authenticated user.`, - }, ], }, { @@ -232,6 +226,82 @@ export const channelFields = [ ], }, /* -------------------------------------------------------------------------- */ + /* channel:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Channel ID', + name: 'channelId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + }, + { + displayName: 'Fields', + name: 'part', + type: 'multiOptions', + options: [ + { + name: 'Branding Settings', + value: 'brandingSettings', + }, + { + name: 'Content Details', + value: 'contentDetails', + }, + { + name: 'Content Owner Details', + value: 'contentOwnerDetails', + }, + { + name: 'ID', + value: 'id', + }, + { + name: 'Localizations', + value: 'localizations', + }, + { + name: 'Snippet', + value: 'snippet', + }, + { + name: 'Statistics', + value: 'statistics', + }, + { + name: 'Status', + value: 'status', + }, + { + name: 'Topic Details', + value: 'topicDetails', + }, + ], + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'channel', + ], + }, + }, + description: 'The fields parameter specifies a comma-separated list of one or more channel resource properties that the API response will include.', + default: '' + }, + /* -------------------------------------------------------------------------- */ /* channel:update */ /* -------------------------------------------------------------------------- */ { diff --git a/packages/nodes-base/nodes/Google/YouTube/PlaylistDescription.ts b/packages/nodes-base/nodes/Google/YouTube/PlaylistDescription.ts index 55b90fb63b..5cb58ee97b 100644 --- a/packages/nodes-base/nodes/Google/YouTube/PlaylistDescription.ts +++ b/packages/nodes-base/nodes/Google/YouTube/PlaylistDescription.ts @@ -25,6 +25,11 @@ export const playlistOperations = [ value: 'delete', description: 'Delete a playlist', }, + { + name: 'Get', + value: 'get', + description: 'Get a playlist', + }, { name: 'Get All', value: 'getAll', @@ -118,7 +123,10 @@ export const playlistFields = [ { displayName: 'Default Language', name: 'defaultLanguage', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLanguages', + }, default: '', description: `The language of the text in the playlist resource's title and description properties.`, }, @@ -141,6 +149,104 @@ export const playlistFields = [ ], }, /* -------------------------------------------------------------------------- */ + /* playlist:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Playlist ID', + name: 'playlistId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'playlist', + ], + }, + }, + default: '', + }, + { + displayName: 'Fields', + name: 'part', + type: 'multiOptions', + options: [ + { + name: 'Content Details', + value: 'contentDetails', + }, + { + name: 'ID', + value: 'id', + }, + { + name: 'Localizations', + value: 'localizations', + }, + { + name: 'Player', + value: 'player', + }, + { + name: 'Snippet', + value: 'snippet', + }, + { + name: 'Status', + value: 'status', + }, + ], + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'playlist', + ], + }, + }, + description: 'The fields parameter specifies a comma-separated list of one or more playlist resource properties that the API response will include.', + default: '' + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'playlist', + ], + }, + }, + options: [ + { + displayName: 'On Behalf Of Content Owner', + name: 'onBehalfOfContentOwner', + type: 'string', + default: '', + description: `The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify
+ a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value`, + }, + { + displayName: 'On Behalf Of Content Owner Channel', + name: 'onBehalfOfContentOwnerChannel', + type: 'string', + default: '', + description: `The onBehalfOfContentOwnerChannel parameter specifies the YouTube channel ID of the channel to which a video is being added`, + }, + ], + }, + /* -------------------------------------------------------------------------- */ /* playlist:delete */ /* -------------------------------------------------------------------------- */ { @@ -306,13 +412,6 @@ export const playlistFields = [ default: '', description: `The id parameter specifies a comma-separated list of the YouTube playlist ID(s) for the resource(s) that are being retrieved. In a playlist resource, the id property specifies the playlist's YouTube playlist ID.`, }, - { - displayName: 'Mine', - name: 'mine', - type: 'boolean', - default: false, - description: `Set this parameter's value to true to instruct the API to only return playlists owned by the authenticated user.`, - }, ], }, { @@ -371,6 +470,24 @@ export const playlistFields = [ default: '', description: `The playlist's title.`, }, + { + displayName: 'Title', + name: 'title', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'playlist', + ], + }, + }, + default: '', + description: `The playlist's title.`, + }, { displayName: 'Update Fields', name: 'updateFields', @@ -388,6 +505,16 @@ export const playlistFields = [ }, }, options: [ + { + displayName: 'Default Language', + name: 'defaultLanguage', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLanguages', + }, + default: '', + description: `The language of the text in the playlist resource's title and description properties.`, + }, { displayName: 'Description', name: 'description', @@ -395,6 +522,14 @@ export const playlistFields = [ default: '', description: `The playlist's description.`, }, + { + displayName: 'On Behalf Of Content Owner', + name: 'onBehalfOfContentOwner', + type: 'string', + default: '', + description: `The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify
+ a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value`, + }, { displayName: 'Privacy Status', name: 'privacyStatus', @@ -423,27 +558,6 @@ export const playlistFields = [ default: '', description: `Keyword tags associated with the playlist. Mulplie can be defined separated by comma`, }, - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - }, - { - displayName: 'Default Language', - name: 'defaultLanguage', - type: 'string', - default: '', - description: `The language of the text in the playlist resource's title and description properties.`, - }, - { - displayName: 'On Behalf Of Content Owner', - name: 'onBehalfOfContentOwner', - type: 'string', - default: '', - description: `The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify
- a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value`, - }, ], }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Google/YouTube/VideoCategoryDescription.ts b/packages/nodes-base/nodes/Google/YouTube/VideoCategoryDescription.ts new file mode 100644 index 0000000000..fcb308e1a7 --- /dev/null +++ b/packages/nodes-base/nodes/Google/YouTube/VideoCategoryDescription.ts @@ -0,0 +1,95 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const videoCategoryOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'videoCategory', + ], + }, + }, + options: [ + + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all video categories', + }, + ], + default: 'getAll', + description: 'The operation to perform.' + } +] as INodeProperties[]; + +export const videoCategoryFields = [ + /* -------------------------------------------------------------------------- */ + /* videoCategory:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Region Code', + name: 'regionCode', + type: 'options', + required: true, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'videoCategory', + ], + }, + }, + typeOptions: { + loadOptionsMethod: 'getCountriesCodes', + }, + default: '', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'videoCategory', + ], + }, + }, + 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: [ + 'videoCategory', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts b/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts index b7c4f9d294..8c9b9ea80b 100644 --- a/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts +++ b/packages/nodes-base/nodes/Google/YouTube/VideoDescription.ts @@ -73,8 +73,8 @@ export const videoFields = [ default: '', }, { - displayName: 'Country Code', - name: 'countryCode', + displayName: 'Region Code', + name: 'regionCode', type: 'options', typeOptions: { loadOptionsMethod: 'getCountriesCodes', @@ -98,7 +98,7 @@ export const videoFields = [ typeOptions: { loadOptionsMethod: 'getVideoCategories', loadOptionsDependsOn: [ - 'countryCode', + 'regionCode', ], }, displayOptions: { @@ -150,7 +150,10 @@ export const videoFields = [ { displayName: 'Default Language', name: 'defaultLanguage', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLanguages', + }, default: '', description: `The language of the text in the playlist resource's title and description properties.`, }, @@ -182,7 +185,7 @@ export const videoFields = [ value: 'youtube', }, ], - default: false, + default: '', description: `The video's license.`, }, { @@ -326,10 +329,6 @@ export const videoFields = [ name: 'Content Details', value: 'contentDetails', }, - { - name: 'Field Details', - value: 'fieldDetails', - }, { name: 'ID', value: 'id', @@ -346,10 +345,6 @@ export const videoFields = [ name: 'Player', value: 'player', }, - { - name: 'Processing Details', - value: 'processingDetails', - }, { name: 'Recording Details', value: 'recordingDetails', @@ -428,10 +423,6 @@ export const videoFields = [ name: 'Content Details', value: 'contentDetails', }, - { - name: 'File Details', - value: 'fileDetails', - }, { name: 'ID', value: 'id', @@ -448,10 +439,6 @@ export const videoFields = [ name: 'Player', value: 'player', }, - { - name: 'Processing Details', - value: 'processingDetails', - }, { name: 'Recording Details', value: 'recordingDetails', @@ -692,47 +679,64 @@ export const videoFields = [ }, default: '', }, - // { - // displayName: 'Country Code', - // name: 'countryCode', - // type: 'options', - // typeOptions: { - // loadOptionsMethod: 'getCountriesCodes', - // }, - // displayOptions: { - // show: { - // operation: [ - // 'update', - // ], - // resource: [ - // 'video', - // ], - // }, - // }, - // default: '', - // }, - // { - // displayName: 'Category ID', - // name: 'categoryId', - // type: 'options', - // typeOptions: { - // loadOptionsMethod: 'getVideoCategories', - // loadOptionsDependsOn: [ - // 'countryCode', - // ], - // }, - // displayOptions: { - // show: { - // operation: [ - // 'update', - // ], - // resource: [ - // 'video', - // ], - // }, - // }, - // default: '', - // }, + { + displayName: 'Title', + name: 'title', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'video', + ], + }, + }, + default: '', + }, + { + displayName: 'Region Code', + name: 'regionCode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCountriesCodes', + }, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'video', + ], + }, + }, + default: '', + }, + { + displayName: 'Category ID', + name: 'categoryId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getVideoCategories', + loadOptionsDependsOn: [ + 'regionCode', + ], + }, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'video', + ], + }, + }, + default: '', + }, { displayName: 'Update Fields', name: 'updateFields', @@ -753,7 +757,10 @@ export const videoFields = [ { displayName: 'Default Language', name: 'defaultLanguage', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLanguages', + }, default: '', description: `The language of the text in the playlist resource's title and description properties.`, }, @@ -785,7 +792,7 @@ export const videoFields = [ value: 'youtube', }, ], - default: false, + default: '', description: `The video's license.`, }, { @@ -851,12 +858,6 @@ export const videoFields = [ default: '', description: `Keyword tags associated with the playlist. Mulplie can be defined separated by comma`, }, - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - }, ], }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts b/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts index fe9c8d5309..0c09c391dd 100644 --- a/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts +++ b/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts @@ -32,6 +32,11 @@ import { videoFields, } from './VideoDescription'; +import { + videoCategoryOperations, + videoCategoryFields, +} from './VideoCategoryDescription'; + import { countriesCodes, } from './CountryCodes'; @@ -75,6 +80,10 @@ export class YouTube implements INodeType { name: 'Video', value: 'video', }, + { + name: 'Video Category', + value: 'videoCategory', + }, ], default: 'channel', description: 'The resource to operate on.' @@ -87,6 +96,9 @@ export class YouTube implements INodeType { ...videoOperations, ...videoFields, + + ...videoCategoryOperations, + ...videoCategoryFields, ], }; @@ -133,7 +145,8 @@ export class YouTube implements INodeType { async getVideoCategories( this: ILoadOptionsFunctions ): Promise { - const countryCode = this.getCurrentNodeParameter('countryCode') as string; + const countryCode = this.getCurrentNodeParameter('regionCode') as string; + const returnData: INodePropertyOptions[] = []; const qs: IDataObject = {}; qs.regionCode = countryCode; @@ -169,6 +182,25 @@ export class YouTube implements INodeType { const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { if (resource === 'channel') { + if (operation === 'get') { + //https://developers.google.com/youtube/v3/docs/channels/list + const part = this.getNodeParameter('part', i) as string[]; + const channelId = this.getNodeParameter('channelId', i) as string; + + qs.part = part.join(','); + + qs.id = channelId; + + responseData = await googleApiRequest.call( + this, + 'GET', + `/youtube/v3/channels`, + {}, + qs + ); + + responseData = responseData.items; + } //https://developers.google.com/youtube/v3/docs/channels/list if (operation === 'getAll') { const returnAll = this.getNodeParameter('returnAll', i) as boolean; @@ -178,27 +210,14 @@ export class YouTube implements INodeType { qs.part = part.join(','); - if (filters.categoryId) { - qs.categoryId = filters.categoryId as string; - } - if (filters.forUsername) { - qs.forUsername = filters.forUsername as string; - } - if (filters.id) { - qs.id = filters.id as string; - } - if (filters.managedByMe) { - qs.managedByMe = filters.managedByMe as boolean; - } - if (filters.mine) { - qs.mine = filters.mine as boolean; - } - if (options.h1) { - qs.h1 = options.h1 as string; - } - if (options.onBehalfOfContentOwner) { - qs.onBehalfOfContentOwner = options.onBehalfOfContentOwner as string; + Object.assign(qs, options, filters); + + qs.mine = true; + + if (qs.categoryId || qs.forUsername || qs.id || qs.managedByMe) { + delete qs.mine; } + if (returnAll) { responseData = await googleApiRequestAllItems.call( this, @@ -371,6 +390,28 @@ export class YouTube implements INodeType { } } if (resource === 'playlist') { + //https://developers.google.com/youtube/v3/docs/playlists/list + if (operation === 'get') { + const part = this.getNodeParameter('part', i) as string[]; + const playlistId = this.getNodeParameter('playlistId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.part = part.join(','); + + qs.id = playlistId; + + Object.assign(qs, options); + + responseData = await googleApiRequest.call( + this, + 'GET', + `/youtube/v3/playlists`, + {}, + qs + ); + + responseData = responseData.items; + } //https://developers.google.com/youtube/v3/docs/playlists/list if (operation === 'getAll') { const returnAll = this.getNodeParameter('returnAll', i) as boolean; @@ -382,6 +423,12 @@ export class YouTube implements INodeType { Object.assign(qs, options, filters); + qs.mine = true; + + if (qs.channelId || qs.id) { + delete qs.mine; + } + if (returnAll) { responseData = await googleApiRequestAllItems.call( this, @@ -450,14 +497,18 @@ export class YouTube implements INodeType { //https://developers.google.com/youtube/v3/docs/playlists/update if (operation === 'update') { const playlistId = this.getNodeParameter('playlistId', i) as string; + const title = this.getNodeParameter('title', i) as string; const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; - qs.part = 'snippet'; + qs.part = 'snippet, status'; const body: IDataObject = { id: playlistId, snippet: { - } + title, + }, + status: { + }, }; if (updateFields.tags) { @@ -465,9 +516,9 @@ export class YouTube implements INodeType { body.snippet.tags = (updateFields.tags as string).split(',') as string[]; } - if (updateFields.title) { + if (updateFields.privacyStatus) { //@ts-ignore - body.snippet.title = updateFields.title as string + body.status.privacyStatus = updateFields.privacyStatus as string; } if (updateFields.description) { @@ -529,6 +580,10 @@ export class YouTube implements INodeType { Object.assign(qs, options, filters); + if (qs.myRating) { + delete qs.chart; + } + if (returnAll) { responseData = await googleApiRequestAllItems.call( this, @@ -689,7 +744,8 @@ export class YouTube implements INodeType { //https://developers.google.com/youtube/v3/docs/playlists/update if (operation === 'update') { const id = this.getNodeParameter('videoId', i) as string; - //const categoryId = this.getNodeParameter('categoryId', i) as string; + const title = this.getNodeParameter('title', i) as string; + const categoryId = this.getNodeParameter('categoryId', i) as string; const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; qs.part = 'snippet, status, recordingDetails'; @@ -697,7 +753,8 @@ export class YouTube implements INodeType { const body = { id, snippet: { - //categoryId, + title, + categoryId, }, status: { }, @@ -707,59 +764,56 @@ export class YouTube implements INodeType { if (updateFields.description) { //@ts-ignore - data.snippet.description = updateFields.description as string; + body.snippet.description = updateFields.description as string; } if (updateFields.privacyStatus) { //@ts-ignore - data.status.privacyStatus = updateFields.privacyStatus as string; + body.status.privacyStatus = updateFields.privacyStatus as string; } if (updateFields.tags) { //@ts-ignore - data.snippet.tags = (updateFields.tags as string).split(',') as string[]; - } - - if (updateFields.description) { - //@ts-ignore - data.snippet.title = updateFields.title as string; + body.snippet.tags = (updateFields.tags as string).split(',') as string[]; } if (updateFields.embeddable) { //@ts-ignore - data.status.embeddable = updateFields.embeddable as boolean; + body.status.embeddable = updateFields.embeddable as boolean; } if (updateFields.publicStatsViewable) { //@ts-ignore - data.status.publicStatsViewable = updateFields.publicStatsViewable as boolean; + body.status.publicStatsViewable = updateFields.publicStatsViewable as boolean; } if (updateFields.publishAt) { //@ts-ignore - data.status.publishAt = updateFields.publishAt as string; - } - - if (updateFields.recordingDate) { - //@ts-ignore - data.recordingDetails.recordingDate = updateFields.recordingDate as string; + body.status.publishAt = updateFields.publishAt as string; } if (updateFields.selfDeclaredMadeForKids) { //@ts-ignore - data.status.selfDeclaredMadeForKids = updateFields.selfDeclaredMadeForKids as boolean; + body.status.selfDeclaredMadeForKids = updateFields.selfDeclaredMadeForKids as boolean; + } + + if (updateFields.recordingDate) { + //@ts-ignore + body.recordingDetails.recordingDate = updateFields.recordingDate as string; } if (updateFields.license) { //@ts-ignore - data.status.license = options.license as string; + body.status.license = updateFields.license as string; } if (updateFields.defaultLanguage) { //@ts-ignore - data.snippet.defaultLanguage = updateFields.defaultLanguage as string; + body.snippet.defaultLanguage = updateFields.defaultLanguage as string; } + console.log(body) + responseData = await googleApiRequest.call( this, 'PUT', @@ -810,6 +864,31 @@ export class YouTube implements INodeType { responseData = { success: true }; } } + if (resource === 'videoCategory') { + //https://developers.google.com/youtube/v3/docs/videoCategories/list + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const regionCode = this.getNodeParameter('regionCode', i) as string; + + qs.regionCode = regionCode; + + qs.part = 'snippet'; + + responseData = await googleApiRequest.call( + this, + 'GET', + `/youtube/v3/videoCategories`, + {}, + qs + ); + responseData = responseData.items; + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + } } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]);