first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,561 @@
import type { INodeProperties } from 'n8n-workflow';
export const channelOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['channel'],
},
},
options: [
{
name: 'Get',
value: 'get',
description: 'Retrieve a channel',
action: 'Get a channel',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Retrieve many channels',
action: 'Get many channels',
},
{
name: 'Update',
value: 'update',
description: 'Update a channel',
action: 'Update a channel',
},
{
name: 'Upload Banner',
value: 'uploadBanner',
description: 'Upload a channel banner',
action: 'Upload a channel banner',
},
],
default: 'getAll',
},
];
export const channelFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* channel:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Fields',
name: 'part',
type: 'multiOptions',
options: [
{
name: '*',
value: '*',
},
{
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: ['getAll'],
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: ['*'],
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['channel'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['channel'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 50,
},
default: 25,
description: 'Max number of results to return',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['getAll'],
resource: ['channel'],
},
},
options: [
{
displayName: 'Category ID',
name: 'categoryId',
type: 'string',
default: '',
description:
'The categoryId parameter specifies a YouTube guide category, thereby requesting YouTube channels associated with that category',
},
{
displayName: 'For Username',
name: 'forUsername',
type: 'string',
default: '',
description:
'The forUsername parameter specifies a YouTube username, thereby requesting the channel associated with that username',
},
{
displayName: 'ID',
name: 'id',
type: 'string',
default: '',
description:
"The ID parameter specifies a comma-separated list of the YouTube channel ID(s) for the resource(s) that are being retrieved. In a channel resource, the ID property specifies the channel's YouTube channel ID.",
},
{
displayName: 'Managed By Me',
name: 'managedByMe',
type: 'boolean',
default: false,
description:
'Whether to instruct the API to only return channels managed by the content owner that the onBehalfOfContentOwner parameter specifies',
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['getAll'],
resource: ['channel'],
},
},
options: [
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Language Code',
name: 'h1',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLanguages',
},
default: '',
description:
'The hl parameter instructs the API to retrieve localized resource metadata for a specific application language that the YouTube website supports. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
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",
},
],
},
/* -------------------------------------------------------------------------- */
/* channel:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel ID',
name: 'channelId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['get'],
resource: ['channel'],
},
},
description: 'ID of the channel',
default: '',
},
{
displayName: 'Fields',
name: 'part',
type: 'multiOptions',
options: [
{
name: '*',
value: '*',
},
{
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 */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel ID',
name: 'channelId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['update'],
resource: ['channel'],
},
},
default: '',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['update'],
resource: ['channel'],
},
},
options: [
{
displayName: 'Branding Settings',
name: 'brandingSettingsUi',
type: 'fixedCollection',
default: {},
description: 'Encapsulates information about the branding of the channel',
placeholder: 'Add Branding Settings',
typeOptions: {
multipleValues: false,
},
options: [
{
name: 'channelSettingsValues',
displayName: 'Channel Settings',
values: [
{
displayName: 'Channel',
name: 'channel',
type: 'collection',
default: {},
placeholder: 'Add Channel Settings',
typeOptions: {
multipleValues: false,
},
options: [
{
displayName: 'Country',
name: 'country',
type: 'string',
default: '',
description:
'The country with which the channel is associated. Update this property to set the value of the snippet.country property.',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description:
"The channel description, which appears in the channel information box on your channel page. The property's value has a maximum length of 1000 characters.",
},
{
displayName: 'Default Language',
name: 'defaultLanguage',
type: 'string',
default: '',
description:
'The content tab that users should display by default when viewers arrive at your channel page',
},
{
displayName: 'Default Tab',
name: 'defaultTab',
type: 'string',
default:
'The content tab that users should display by default when viewers arrive at your channel page.',
},
{
displayName: 'Featured Channels Title',
name: 'featuredChannelsTitle',
type: 'string',
default: '',
description:
'The title that displays above the featured channels module. The title has a maximum length of 30 characters.',
},
{
displayName: 'Featured Channels Urls',
name: 'featuredChannelsUrls',
type: 'string',
typeOptions: {
multipleValues: true,
},
description:
'A list of up to 100 channels that you would like to link to from the featured channels module. The property value is a list of YouTube channel ID values, each of which uniquely identifies a channel.',
default: [],
},
{
displayName: 'Keywords',
name: 'keywords',
type: 'string',
placeholder: 'tech,news',
description:
'Keywords associated with your channel. The value is a space-separated list of strings.',
default: '',
},
{
displayName: 'Moderate Comments',
name: 'moderateComments',
type: 'boolean',
description:
'Whether user-submitted comments left on the channel page need to be approved by the channel owner to be publicly visible',
default: false,
},
{
displayName: 'Profile Color',
name: 'profileColor',
// eslint-disable-next-line n8n-nodes-base/node-param-color-type-unused
type: 'string',
default: '',
description: "A prominent color that complements the channel's content",
},
{
displayName: 'Show Related Channels',
name: 'showRelatedChannels',
type: 'boolean',
description:
'Whether YouTube should show an algorithmically generated list of related channels on your channel page',
default: false,
},
{
displayName: 'Show Browse View',
name: 'showBrowseView',
type: 'boolean',
description:
'Whether the channel page should display content in a browse or feed view',
default: false,
},
{
displayName: 'Tracking Analytics AccountId',
name: 'trackingAnalyticsAccountId',
type: 'string',
description:
'The ID for a Google Analytics account that you want to use to track and measure traffic to your channel',
default: '',
},
{
displayName: 'Unsubscribed Trailer',
name: 'unsubscribedTrailer',
type: 'string',
description:
"The video that should play in the featured video module in the channel page's browse view for unsubscribed viewers",
default: '',
},
],
},
],
description: 'The channel object encapsulates branding properties of the channel page',
},
{
name: 'imageSettingsValues',
displayName: 'Image Settings',
values: [
{
displayName: 'Image',
name: 'image',
type: 'collection',
default: {},
placeholder: 'Add Channel Settings',
description:
"The image object encapsulates information about images that display on the channel's channel page or video watch pages",
typeOptions: {
multipleValues: false,
},
options: [
{
displayName: 'Banner External Url',
name: 'bannerExternalUrl',
type: 'string',
default: '',
},
{
displayName: 'Tracking Image Url',
name: 'trackingImageUrl',
type: 'string',
default: '',
},
{
displayName: 'Watch Icon Image Url',
name: 'watchIconImageUrl',
type: 'string',
default: '',
},
],
},
],
},
{
name: 'statusValue',
displayName: 'Status',
values: [
{
displayName: 'Status',
name: 'status',
type: 'collection',
default: {},
placeholder: 'Add Status',
typeOptions: {
multipleValues: false,
},
options: [
{
displayName: 'Self Declared Made For Kids',
name: 'selfDeclaredMadeForKids',
type: 'boolean',
default: false,
},
],
},
],
},
],
},
{
displayName: 'On Behalf Of Content Owner',
name: 'onBehalfOfContentOwner',
type: 'string',
default: '',
},
],
},
/* -------------------------------------------------------------------------- */
/* channel:uploadBanner */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel ID',
name: 'channelId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['uploadBanner'],
resource: ['channel'],
},
},
description: 'ID of the channel',
default: '',
},
{
displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
required: true,
hint: 'The name of the input binary field containing the file to be uploaded',
displayOptions: {
show: {
operation: ['uploadBanner'],
resource: ['channel'],
},
},
default: 'data',
},
];
@@ -0,0 +1,65 @@
import type {
IExecuteFunctions,
ILoadOptionsFunctions,
IDataObject,
JsonObject,
IHttpRequestMethods,
IRequestOptions,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
export async function googleApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions,
method: IHttpRequestMethods,
resource: string,
body: any = {},
qs: IDataObject = {},
uri?: string,
option: IDataObject = {},
): Promise<any> {
let options: IRequestOptions = {
headers: {
'Content-Type': 'application/json',
},
method,
body,
qs,
uri: uri || `https://www.googleapis.com${resource}`,
json: true,
};
try {
options = Object.assign({}, options, option);
if (Object.keys(body as IDataObject).length === 0) {
delete options.body;
}
return await this.helpers.requestOAuth2.call(this, 'youTubeOAuth2Api', options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function googleApiRequestAllItems(
this: IExecuteFunctions | ILoadOptionsFunctions,
propertyName: string,
method: IHttpRequestMethods,
endpoint: string,
body: any = {},
query: IDataObject = {},
): Promise<any> {
const returnData: IDataObject[] = [];
let responseData;
query.maxResults = 100;
do {
responseData = await googleApiRequest.call(this, method, endpoint, body, query);
query.pageToken = responseData.nextPageToken;
returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]);
} while (responseData.nextPageToken !== undefined && responseData.nextPageToken !== '');
return returnData;
}
@@ -0,0 +1,519 @@
import type { INodeProperties } from 'n8n-workflow';
export const playlistOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['playlist'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a playlist',
action: 'Create a playlist',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a playlist',
action: 'Delete a playlist',
},
{
name: 'Get',
value: 'get',
description: 'Get a playlist',
action: 'Get a playlist',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Retrieve many playlists',
action: 'Get many playlists',
},
{
name: 'Update',
value: 'update',
description: 'Update a playlist',
action: 'Update a playlist',
},
],
default: 'getAll',
},
];
export const playlistFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* playlist:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Title',
name: 'title',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['create'],
resource: ['playlist'],
},
},
default: '',
description: "The playlist's title",
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['create'],
resource: ['playlist'],
},
},
options: [
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: "The playlist's description",
},
{
displayName: 'Privacy Status',
name: 'privacyStatus',
type: 'options',
options: [
{
name: 'Private',
value: 'private',
},
{
name: 'Public',
value: 'public',
},
{
name: 'Unlisted',
value: 'unlisted',
},
],
default: '',
description: "The playlist's privacy status",
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
default: '',
description:
'Keyword tags associated with the playlist. Mulplie can be defined separated by comma.',
},
{
displayName: 'Default Language Name or ID',
name: 'defaultLanguage',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLanguages',
},
default: '',
description:
'The language of the text in the playlist resource\'s title and description properties. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
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. This parameter is required when a request specifies a value for the onBehalfOfContentOwner parameter, and it can only be used in conjunction with that parameter.',
},
{
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",
},
],
},
/* -------------------------------------------------------------------------- */
/* 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: '*',
value: '*',
},
{
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 */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist ID',
name: 'playlistId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['delete'],
resource: ['playlist'],
},
},
default: '',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['delete'],
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",
},
],
},
/* -------------------------------------------------------------------------- */
/* playlist:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Fields',
name: 'part',
type: 'multiOptions',
options: [
{
name: '*',
value: '*',
},
{
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: ['getAll'],
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: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['playlist'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['playlist'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 50,
},
default: 25,
description: 'Max number of results to return',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['getAll'],
resource: ['playlist'],
},
},
options: [
{
displayName: 'Channel ID',
name: 'channelId',
type: 'string',
default: '',
description:
"This value indicates that the API should only return the specified channel's playlists",
},
{
displayName: 'ID',
name: 'id',
type: 'string',
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: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['getAll'],
resource: ['playlist'],
},
},
options: [
{
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. This parameter is required when a request specifies a value for the onBehalfOfContentOwner parameter, and it can only be used in conjunction with that parameter.',
},
{
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",
},
],
},
/* -------------------------------------------------------------------------- */
/* playlist:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist ID',
name: 'playlistId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['update'],
resource: ['playlist'],
},
},
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',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['update'],
resource: ['playlist'],
},
},
options: [
{
displayName: 'Default Language Name or ID',
name: 'defaultLanguage',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLanguages',
},
default: '',
description:
'The language of the text in the playlist resource\'s title and description properties. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
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',
type: 'options',
options: [
{
name: 'Private',
value: 'private',
},
{
name: 'Public',
value: 'public',
},
{
name: 'Unlisted',
value: 'unlisted',
},
],
default: '',
description: "The playlist's privacy status",
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
default: '',
description:
'Keyword tags associated with the playlist. Mulplie can be defined separated by comma.',
},
],
},
];
@@ -0,0 +1,364 @@
import type { INodeProperties } from 'n8n-workflow';
export const playlistItemOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['playlistItem'],
},
},
options: [
{
name: 'Add',
value: 'add',
description: 'Add an item to a playlist',
action: 'Add a playlist item',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a item from a playlist',
action: 'Delete a playlist item',
},
{
name: 'Get',
value: 'get',
description: "Get a playlist's item",
action: 'Get a playlist item',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Retrieve many playlist items',
action: 'Get many playlist items',
},
],
default: 'add',
},
];
export const playlistItemFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* playlistItem:add */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist Name or ID',
name: 'playlistId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
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: '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: '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: '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: '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: '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',
},
],
},
/* -------------------------------------------------------------------------- */
/* 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 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 a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value",
},
],
},
/* -------------------------------------------------------------------------- */
/* playlistItem:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Playlist Name or ID',
name: 'playlistId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
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: 'Whether to return all results 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: 'Max number of 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 a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value",
},
],
},
];
@@ -0,0 +1,80 @@
import type { INodeProperties } from 'n8n-workflow';
export const videoCategoryOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['videoCategory'],
},
},
options: [
{
name: 'Get Many',
value: 'getAll',
description: 'Retrieve many video categories',
action: 'Get many video categories',
},
],
default: 'getAll',
},
];
export const videoCategoryFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* videoCategory:getAll */
/* -------------------------------------------------------------------------- */
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Region Code',
name: 'regionCode',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
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: 'Whether to return all results 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: 50,
},
default: 25,
description: 'Max number of results to return',
},
];
@@ -0,0 +1,826 @@
import type { INodeProperties } from 'n8n-workflow';
export const videoOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['video'],
},
},
options: [
{
name: 'Delete',
value: 'delete',
description: 'Delete a video',
action: 'Delete a video',
},
{
name: 'Get',
value: 'get',
description: 'Get a video',
action: 'Get a video',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Retrieve many videos',
action: 'Get many videos',
},
{
name: 'Rate',
value: 'rate',
description: 'Rate a video',
action: 'Rate a video',
},
{
name: 'Update',
value: 'update',
description: 'Update a video',
action: 'Update a video',
},
{
name: 'Upload',
value: 'upload',
description: 'Upload a video',
action: 'Upload a video',
},
],
default: 'getAll',
},
];
export const videoFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* video:upload */
/* -------------------------------------------------------------------------- */
{
displayName: 'Title',
name: 'title',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['upload'],
resource: ['video'],
},
},
default: '',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Region Code',
name: 'regionCode',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getCountriesCodes',
},
displayOptions: {
show: {
operation: ['upload'],
resource: ['video'],
},
},
default: '',
},
{
displayName: 'Category Name or ID',
name: 'categoryId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getVideoCategories',
loadOptionsDependsOn: ['regionCode'],
},
displayOptions: {
show: {
operation: ['upload'],
resource: ['video'],
},
},
default: '',
},
{
displayName: 'Input Binary Field',
name: 'binaryProperty',
type: 'string',
required: true,
hint: 'The name of the input binary field containing the file to be uploaded',
displayOptions: {
show: {
operation: ['upload'],
resource: ['video'],
},
},
default: 'data',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['upload'],
resource: ['video'],
},
},
options: [
{
displayName: 'Default Language Name or ID',
name: 'defaultLanguage',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLanguages',
},
default: '',
description:
'The language of the text in the playlist resource\'s title and description properties. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: "The playlist's description",
},
{
displayName: 'Embeddable',
name: 'embeddable',
type: 'boolean',
default: false,
description: 'Whether the video can be embedded on another website',
},
{
displayName: 'License',
name: 'license',
type: 'options',
options: [
{
name: 'Creative Common',
value: 'creativeCommon',
},
{
name: 'Youtube',
value: 'youtube',
},
],
default: '',
description: "The video's license",
},
{
displayName: 'Notify Subscribers',
name: 'notifySubscribers',
type: 'boolean',
default: false,
description:
"Whether YouTube should send a notification about the new video to users who subscribe to the video's channel",
},
{
displayName: 'Privacy Status',
name: 'privacyStatus',
type: 'options',
options: [
{
name: 'Private',
value: 'private',
},
{
name: 'Public',
value: 'public',
},
{
name: 'Unlisted',
value: 'unlisted',
},
],
default: '',
description: "The playlist's privacy status",
},
{
displayName: 'Public Stats Viewable',
name: 'publicStatsViewable',
type: 'boolean',
default: true,
description:
"Whether the extended video statistics on the video's watch page are publicly viewable",
},
{
displayName: 'Publish At',
name: 'publishAt',
type: 'dateTime',
default: '',
description:
'If you set a value for this property, you must also set the status.privacyStatus property to private',
},
{
displayName: 'Recording Date',
name: 'recordingDate',
type: 'dateTime',
default: '',
description: 'The date and time when the video was recorded',
},
{
displayName: 'Self Declared Made For Kids',
name: 'selfDeclaredMadeForKids',
type: 'boolean',
default: false,
description:
'Whether the video is designated as child-directed, and it contains the current "made for kids" status of the video',
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
default: '',
description:
'Keyword tags associated with the playlist. Mulplie can be defined separated by comma.',
},
],
},
/* -------------------------------------------------------------------------- */
/* video:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Video ID',
name: 'videoId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['delete'],
resource: ['video'],
},
},
description: 'ID of the video',
default: '',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['delete'],
resource: ['video'],
},
},
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",
},
],
},
/* -------------------------------------------------------------------------- */
/* video:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Video ID',
name: 'videoId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['get'],
resource: ['video'],
},
},
default: '',
},
{
displayName: 'Fields',
name: 'part',
type: 'multiOptions',
options: [
{
name: '*',
value: '*',
},
{
name: 'Content Details',
value: 'contentDetails',
},
{
name: 'ID',
value: 'id',
},
{
name: 'Live Streaming Details',
value: 'liveStreamingDetails',
},
{
name: 'Localizations',
value: 'localizations',
},
{
name: 'Player',
value: 'player',
},
{
name: 'Recording Details',
value: 'recordingDetails',
},
{
name: 'Snippet',
value: 'snippet',
},
{
name: 'Statistics',
value: 'statistics',
},
{
name: 'Status',
value: 'status',
},
{
name: 'Topic Details',
value: 'topicDetails',
},
],
required: true,
displayOptions: {
show: {
operation: ['get'],
resource: ['video'],
},
},
description:
'The fields parameter specifies a comma-separated list of one or more video resource properties that the API response will include',
default: ['*'],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['get'],
resource: ['video'],
},
},
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",
},
],
},
/* -------------------------------------------------------------------------- */
/* video:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['video'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['video'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 50,
},
default: 25,
description: 'Max number of results to return',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['getAll'],
resource: ['video'],
},
},
options: [
{
displayName: 'Channel ID',
name: 'channelId',
type: 'string',
default: '',
description:
'The channelId parameter indicates that the API response should only contain resources created by the channel',
},
{
displayName: 'For Developer',
name: 'forDeveloper',
type: 'boolean',
default: false,
description:
"Whether to restrict the search to only retrieve videos uploaded via the developer's application or website",
},
{
displayName: 'Published After',
name: 'publishedAfter',
type: 'dateTime',
default: '',
description:
'The publishedAfter parameter indicates that the API response should only contain resources created at or after the specified time',
},
{
displayName: 'Published Before',
name: 'publishedBefore',
type: 'dateTime',
default: '',
description:
'The publishedBefore parameter indicates that the API response should only contain resources created before or at the specified time',
},
{
displayName: 'Query',
name: 'q',
type: 'string',
default: '',
description: 'The q parameter specifies the query term to search for',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Region Code',
name: 'regionCode',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCountriesCodes',
},
default: '',
description:
'The regionCode parameter instructs the API to select a video chart available in the specified region. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Related To Video ID',
name: 'relatedToVideoId',
type: 'string',
default: '',
description:
'The relatedToVideoId parameter retrieves a list of videos that are related to the video that the parameter value identifies',
},
{
displayName: 'Video Category ID',
name: 'videoCategoryId',
type: 'string',
default: '',
description:
'The videoCategoryId parameter identifies the video category for which the chart should be retrieved',
},
{
displayName: 'Video Syndicated',
name: 'videoSyndicated',
type: 'boolean',
default: false,
description:
'Whether to restrict a search to only videos that can be played outside youtube.com',
},
{
displayName: 'Video Type',
name: 'videoType',
type: 'options',
options: [
{
name: 'Any',
value: 'any',
},
{
name: 'Episode',
value: 'episode',
},
{
name: 'Movie',
value: 'movie',
},
],
default: '',
description:
'The videoType parameter lets you restrict a search to a particular type of videos',
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['getAll'],
resource: ['video'],
},
},
options: [
{
displayName: 'Order',
name: 'order',
type: 'options',
options: [
{
name: 'Date',
value: 'date',
},
{
name: 'Relevance',
value: 'relevance',
},
],
default: 'relevance',
},
{
displayName: 'Safe Search',
name: 'safeSearch',
type: 'options',
options: [
{
name: 'Moderate',
value: 'moderate',
description:
'YouTube will filter some content from search results and, at the least, will filter content that is restricted in your locale',
},
{
name: 'None',
value: 'none',
description: 'YouTube will not filter the search result set',
},
{
name: 'Strict',
value: 'strict',
description:
'YouTube will try to exclude all restricted content from the search result set',
},
],
default: '',
},
],
},
/* -------------------------------------------------------------------------- */
/* video:rate */
/* -------------------------------------------------------------------------- */
{
displayName: 'Video ID',
name: 'videoId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['rate'],
resource: ['video'],
},
},
default: '',
},
{
displayName: 'Rating',
name: 'rating',
type: 'options',
displayOptions: {
show: {
operation: ['rate'],
resource: ['video'],
},
},
options: [
{
name: 'Dislike',
value: 'dislike',
description: 'Records that the authenticated user disliked the video',
},
{
name: 'Like',
value: 'like',
description: 'Records that the authenticated user liked the video',
},
{
name: 'None',
value: 'none',
description:
'Removes any rating that the authenticated user had previously set for the video',
},
],
default: '',
},
/* -------------------------------------------------------------------------- */
/* video:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Video ID',
name: 'videoId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['update'],
resource: ['video'],
},
},
default: '',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['update'],
resource: ['video'],
},
},
default: '',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Region Code',
name: 'regionCode',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getCountriesCodes',
},
displayOptions: {
show: {
operation: ['update'],
resource: ['video'],
},
},
default: '',
},
{
displayName: 'Category Name or ID',
name: 'categoryId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getVideoCategories',
loadOptionsDependsOn: ['regionCode'],
},
displayOptions: {
show: {
operation: ['update'],
resource: ['video'],
},
},
default: '',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['update'],
resource: ['video'],
},
},
options: [
{
displayName: 'Default Language Name or ID',
name: 'defaultLanguage',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLanguages',
},
default: '',
description:
'The language of the text in the playlist resource\'s title and description properties. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: "The playlist's description",
},
{
displayName: 'Embeddable',
name: 'embeddable',
type: 'boolean',
default: false,
description: 'Whether the video can be embedded on another website',
},
{
displayName: 'License',
name: 'license',
type: 'options',
options: [
{
name: 'Creative Common',
value: 'creativeCommon',
},
{
name: 'Youtube',
value: 'youtube',
},
],
default: '',
description: "The video's license",
},
{
displayName: 'Notify Subscribers',
name: 'notifySubscribers',
type: 'boolean',
default: false,
description:
"Whether YouTube should send a notification about the new video to users who subscribe to the video's channel",
},
{
displayName: 'Privacy Status',
name: 'privacyStatus',
type: 'options',
options: [
{
name: 'Private',
value: 'private',
},
{
name: 'Public',
value: 'public',
},
{
name: 'Unlisted',
value: 'unlistef',
},
],
default: '',
description: "The playlist's privacy status",
},
{
displayName: 'Public Stats Viewable',
name: 'publicStatsViewable',
type: 'boolean',
default: true,
description:
"Whether the extended video statistics on the video's watch page are publicly viewable",
},
{
displayName: 'Publish At',
name: 'publishAt',
type: 'dateTime',
default: '',
description:
'If you set a value for this property, you must also set the status.privacyStatus property to private',
},
{
displayName: 'Recording Date',
name: 'recordingDate',
type: 'dateTime',
default: '',
description: 'The date and time when the video was recorded',
},
{
displayName: 'Self Declared Made For Kids',
name: 'selfDeclaredMadeForKids',
type: 'boolean',
default: false,
description:
'Whether the video is designated as child-directed, and it contains the current "made for kids" status of the video',
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
default: '',
description:
'Keyword tags associated with the playlist. Mulplie can be defined separated by comma.',
},
],
},
];
@@ -0,0 +1,25 @@
{
"node": "n8n-nodes-base.youTube",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Marketing"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.youtube/"
}
],
"generic": [
{
"label": "Using Automation to Boost Productivity in the Workplace",
"icon": "💪",
"url": "https://n8n.io/blog/using-automation-to-boost-productivity-in-the-workplace/"
}
]
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,193 @@
{
"type": "object",
"properties": {
"brandingSettings": {
"type": "object",
"properties": {
"channel": {
"type": "object",
"properties": {
"country": {
"type": "string"
},
"description": {
"type": "string"
},
"keywords": {
"type": "string"
},
"title": {
"type": "string"
},
"unsubscribedTrailer": {
"type": "string"
}
}
},
"image": {
"type": "object",
"properties": {
"bannerExternalUrl": {
"type": "string"
}
}
}
}
},
"contentDetails": {
"type": "object",
"properties": {
"relatedPlaylists": {
"type": "object",
"properties": {
"likes": {
"type": "string"
},
"uploads": {
"type": "string"
}
}
}
}
},
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"snippet": {
"type": "object",
"properties": {
"country": {
"type": "string"
},
"customUrl": {
"type": "string"
},
"description": {
"type": "string"
},
"localized": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"publishedAt": {
"type": "string"
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
}
}
},
"statistics": {
"type": "object",
"properties": {
"hiddenSubscriberCount": {
"type": "boolean"
},
"subscriberCount": {
"type": "string"
},
"videoCount": {
"type": "string"
},
"viewCount": {
"type": "string"
}
}
},
"status": {
"type": "object",
"properties": {
"isLinked": {
"type": "boolean"
},
"longUploadsStatus": {
"type": "string"
},
"madeForKids": {
"type": "boolean"
},
"privacyStatus": {
"type": "string"
}
}
},
"topicDetails": {
"type": "object",
"properties": {
"topicCategories": {
"type": "array",
"items": {
"type": "string"
}
},
"topicIds": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"version": 3
}
@@ -0,0 +1,190 @@
{
"type": "object",
"properties": {
"brandingSettings": {
"type": "object",
"properties": {
"channel": {
"type": "object",
"properties": {
"country": {
"type": "string"
},
"description": {
"type": "string"
},
"keywords": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"image": {
"type": "object",
"properties": {
"bannerExternalUrl": {
"type": "string"
}
}
}
}
},
"contentDetails": {
"type": "object",
"properties": {
"relatedPlaylists": {
"type": "object",
"properties": {
"likes": {
"type": "string"
},
"uploads": {
"type": "string"
}
}
}
}
},
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"snippet": {
"type": "object",
"properties": {
"country": {
"type": "string"
},
"customUrl": {
"type": "string"
},
"description": {
"type": "string"
},
"localized": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"publishedAt": {
"type": "string"
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
}
}
},
"statistics": {
"type": "object",
"properties": {
"hiddenSubscriberCount": {
"type": "boolean"
},
"subscriberCount": {
"type": "string"
},
"videoCount": {
"type": "string"
},
"viewCount": {
"type": "string"
}
}
},
"status": {
"type": "object",
"properties": {
"isChannelMonetizationEnabled": {
"type": "boolean"
},
"isLinked": {
"type": "boolean"
},
"longUploadsStatus": {
"type": "string"
},
"privacyStatus": {
"type": "string"
}
}
},
"topicDetails": {
"type": "object",
"properties": {
"topicCategories": {
"type": "array",
"items": {
"type": "string"
}
},
"topicIds": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"version": 3
}
@@ -0,0 +1,145 @@
{
"type": "object",
"properties": {
"contentDetails": {
"type": "object",
"properties": {
"itemCount": {
"type": "integer"
}
}
},
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"player": {
"type": "object",
"properties": {
"embedHtml": {
"type": "string"
}
}
},
"snippet": {
"type": "object",
"properties": {
"channelId": {
"type": "string"
},
"channelTitle": {
"type": "string"
},
"description": {
"type": "string"
},
"localized": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"publishedAt": {
"type": "string"
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"maxres": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"standard": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
}
}
},
"status": {
"type": "object",
"properties": {
"privacyStatus": {
"type": "string"
}
}
}
},
"version": 1
}
@@ -0,0 +1,145 @@
{
"type": "object",
"properties": {
"contentDetails": {
"type": "object",
"properties": {
"itemCount": {
"type": "integer"
}
}
},
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"player": {
"type": "object",
"properties": {
"embedHtml": {
"type": "string"
}
}
},
"snippet": {
"type": "object",
"properties": {
"channelId": {
"type": "string"
},
"channelTitle": {
"type": "string"
},
"description": {
"type": "string"
},
"localized": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"publishedAt": {
"type": "string"
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"maxres": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"standard": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
}
}
},
"status": {
"type": "object",
"properties": {
"privacyStatus": {
"type": "string"
}
}
}
},
"version": 1
}
@@ -0,0 +1,116 @@
{
"type": "object",
"properties": {
"contentDetails": {
"type": "object",
"properties": {
"videoId": {
"type": "string"
},
"videoPublishedAt": {
"type": "string"
}
}
},
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"snippet": {
"type": "object",
"properties": {
"channelId": {
"type": "string"
},
"channelTitle": {
"type": "string"
},
"description": {
"type": "string"
},
"playlistId": {
"type": "string"
},
"position": {
"type": "integer"
},
"publishedAt": {
"type": "string"
},
"resourceId": {
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"videoId": {
"type": "string"
}
}
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
},
"videoOwnerChannelId": {
"type": "string"
},
"videoOwnerChannelTitle": {
"type": "string"
}
}
}
},
"version": 1
}
@@ -0,0 +1,152 @@
{
"type": "object",
"properties": {
"contentDetails": {
"type": "object",
"properties": {
"videoId": {
"type": "string"
},
"videoPublishedAt": {
"type": "string"
}
}
},
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"snippet": {
"type": "object",
"properties": {
"channelId": {
"type": "string"
},
"channelTitle": {
"type": "string"
},
"description": {
"type": "string"
},
"playlistId": {
"type": "string"
},
"position": {
"type": "integer"
},
"publishedAt": {
"type": "string"
},
"resourceId": {
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"videoId": {
"type": "string"
}
}
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"maxres": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"standard": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
},
"videoOwnerChannelId": {
"type": "string"
},
"videoOwnerChannelTitle": {
"type": "string"
}
}
},
"status": {
"type": "object",
"properties": {
"privacyStatus": {
"type": "string"
}
}
}
},
"version": 1
}
@@ -0,0 +1,231 @@
{
"type": "object",
"properties": {
"contentDetails": {
"type": "object",
"properties": {
"caption": {
"type": "string"
},
"definition": {
"type": "string"
},
"dimension": {
"type": "string"
},
"duration": {
"type": "string"
},
"licensedContent": {
"type": "boolean"
},
"projection": {
"type": "string"
}
}
},
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"localizations": {
"type": "object",
"properties": {
"es": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
}
}
},
"player": {
"type": "object",
"properties": {
"embedHtml": {
"type": "string"
}
}
},
"snippet": {
"type": "object",
"properties": {
"categoryId": {
"type": "string"
},
"channelId": {
"type": "string"
},
"channelTitle": {
"type": "string"
},
"defaultAudioLanguage": {
"type": "string"
},
"defaultLanguage": {
"type": "string"
},
"description": {
"type": "string"
},
"liveBroadcastContent": {
"type": "string"
},
"localized": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"publishedAt": {
"type": "string"
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"maxres": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"standard": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
}
}
},
"statistics": {
"type": "object",
"properties": {
"commentCount": {
"type": "string"
},
"favoriteCount": {
"type": "string"
},
"likeCount": {
"type": "string"
},
"viewCount": {
"type": "string"
}
}
},
"status": {
"type": "object",
"properties": {
"embeddable": {
"type": "boolean"
},
"license": {
"type": "string"
},
"madeForKids": {
"type": "boolean"
},
"privacyStatus": {
"type": "string"
},
"publicStatsViewable": {
"type": "boolean"
},
"uploadStatus": {
"type": "string"
}
}
},
"topicDetails": {
"type": "object",
"properties": {
"topicCategories": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"version": 5
}
@@ -0,0 +1,96 @@
{
"type": "object",
"properties": {
"etag": {
"type": "string"
},
"id": {
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"videoId": {
"type": "string"
}
}
},
"kind": {
"type": "string"
},
"snippet": {
"type": "object",
"properties": {
"channelId": {
"type": "string"
},
"channelTitle": {
"type": "string"
},
"description": {
"type": "string"
},
"liveBroadcastContent": {
"type": "string"
},
"publishedAt": {
"type": "string"
},
"publishTime": {
"type": "string"
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
}
}
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,153 @@
{
"type": "object",
"properties": {
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"snippet": {
"type": "object",
"properties": {
"categoryId": {
"type": "string"
},
"channelId": {
"type": "string"
},
"channelTitle": {
"type": "string"
},
"defaultAudioLanguage": {
"type": "string"
},
"description": {
"type": "string"
},
"liveBroadcastContent": {
"type": "string"
},
"localized": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"publishedAt": {
"type": "string"
},
"thumbnails": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"high": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"maxres": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"medium": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
},
"standard": {
"type": "object",
"properties": {
"height": {
"type": "integer"
},
"url": {
"type": "string"
},
"width": {
"type": "integer"
}
}
}
}
},
"title": {
"type": "string"
}
}
},
"status": {
"type": "object",
"properties": {
"embeddable": {
"type": "boolean"
},
"license": {
"type": "string"
},
"privacyStatus": {
"type": "string"
},
"publicStatsViewable": {
"type": "boolean"
},
"selfDeclaredMadeForKids": {
"type": "boolean"
},
"uploadStatus": {
"type": "string"
}
}
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"uploadId": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,29 @@
{
"type": "object",
"properties": {
"etag": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"snippet": {
"type": "object",
"properties": {
"assignable": {
"type": "boolean"
},
"channelId": {
"type": "string"
},
"title": {
"type": "string"
}
}
}
},
"version": 1
}
@@ -0,0 +1,38 @@
import { DateTime } from 'luxon';
import { NodeOperationError, type IExecuteFunctions } from 'n8n-workflow';
import { validateAndSetDate } from '../../GenericFunctions';
const mockContext = {
getNode: jest.fn().mockReturnValue('Youtube'),
} as unknown as IExecuteFunctions;
describe('validateAndSetDate', () => {
const timezone = 'America/New_York';
let filter: { [key: string]: string };
beforeEach(() => {
filter = {};
});
it('should convert a valid ISO date and set it with the specified timezone', () => {
filter.publishedAfter = '2023-10-05T10:00:00.000Z';
validateAndSetDate(filter, 'publishedAfter', timezone, mockContext);
expect(filter.publishedAfter).toBe(
DateTime.fromISO('2023-10-05T10:00:00.000Z').setZone(timezone).toISO(),
);
});
it('should throw NodeOperationError for an invalid date', () => {
filter.publishedAfter = 'invalid-date';
expect(() => validateAndSetDate(filter, 'publishedAfter', timezone, mockContext)).toThrow(
NodeOperationError,
);
expect(() => validateAndSetDate(filter, 'publishedAfter', timezone, mockContext)).toThrow(
`The value "${filter.publishedAfter}" is not a valid DateTime.`,
);
});
});
@@ -0,0 +1,182 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import categories from './fixtures/categories.json';
import channels from './fixtures/channels.json';
import playlistItems from './fixtures/playlistItems.json';
import playlists from './fixtures/playlists.json';
describe('Test YouTube Node', () => {
const credentials = {
youTubeOAuth2Api: {
scope: '',
oauthTokenData: {
access_token: 'ACCESSTOKEN',
},
},
};
const youtubeNock = nock('https://www.googleapis.com/youtube');
beforeAll(() => {
jest
.useFakeTimers({ doNotFake: ['setImmediate', 'nextTick'] })
.setSystemTime(new Date('2024-12-16 12:34:56.789Z'));
});
describe('Channel', () => {
beforeAll(() => {
youtubeNock
.get('/v3/channels')
.query({
part: 'brandingSettings,contentDetails,contentOwnerDetails,id,localizations,snippet,statistics,status,topicDetails',
mine: 'true',
maxResults: '25',
})
.reply(200, { items: channels });
youtubeNock
.get('/v3/channels')
.query({
part: 'brandingSettings,contentDetails,contentOwnerDetails,id,localizations,snippet,statistics,status,topicDetails',
id: 'UCabc123def456ghi789jkl',
})
.reply(200, { items: [channels[0]] });
youtubeNock
.put('/v3/channels', (body) => {
return (
body.id === 'UCabc123def456ghi789jkl' &&
body.brandingSettings?.channel?.description === 'Test'
);
})
.query({ part: 'brandingSettings' })
.reply(200, {
kind: 'youtube#channel',
etag: 'someEtag',
id: 'UCabc123def456ghi789jkl',
brandingSettings: {
channel: {
description: 'Test',
},
image: {},
},
});
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['channels.workflow.json'],
});
});
describe('Playlist', () => {
beforeAll(() => {
youtubeNock
.post('/v3/playlists', {
snippet: { title: 'Playlist 1', privacyStatus: 'public', defaultLanguage: 'en' },
})
.query({ part: 'snippet' })
.reply(200, playlists[0]);
youtubeNock
.put('/v3/playlists', {
id: 'playlist_id_1',
snippet: { title: 'Test Updated', description: 'This description is updated' },
status: { privacyStatus: 'private' },
})
.query({ part: 'snippet, status' })
.reply(200, {
...playlists[0],
snippet: {
...playlists[0].snippet,
title: 'The title is updated',
description: 'The description is updated',
localized: {
...playlists[0].snippet.localized,
title: 'The title is updated',
description: 'The description is updated',
},
},
});
youtubeNock
.get('/v3/playlists')
.query({
part: 'contentDetails,id,localizations,player,snippet,status',
id: 'playlist_id_1',
})
.reply(200, { items: [playlists[0]] });
youtubeNock
.get('/v3/playlists')
.query({
part: 'contentDetails,id,localizations,player,snippet,status',
mine: true,
maxResults: 25,
})
.reply(200, { items: playlists });
youtubeNock.delete('/v3/playlists', { id: 'playlist_id_1' }).reply(200, { success: true });
});
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['playlists.workflow.json'],
});
});
describe('Video Categories', () => {
beforeAll(() => {
youtubeNock
.get('/v3/videoCategories')
.query({
part: 'snippet',
regionCode: 'GB',
})
.reply(200, { items: categories });
});
afterAll(() => youtubeNock.done());
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['videoCategories.workflow.json'],
});
});
describe('Playlist Item', () => {
beforeAll(() => {
youtubeNock
.get('/v3/playlistItems')
.query({
part: 'contentDetails,id,snippet,status',
id: 'fakePlaylistItemId1',
})
.reply(200, { items: playlistItems[0] });
youtubeNock
.get('/v3/playlistItems')
.query({
playlistId: 'PLXXXXFAKEPLAYLISTID01',
part: 'contentDetails,id,snippet,status',
maxResults: 3,
})
.reply(200, { items: playlistItems });
youtubeNock
.post('/v3/playlistItems', {
snippet: {
playlistId: 'PLXXXXFAKEPLAYLISTID01',
resourceId: { kind: 'youtube#video', videoId: 'FAKEVIDID1' },
},
contentDetails: {},
})
.query({ part: 'snippet, contentDetails' })
.reply(200, playlistItems[0]);
youtubeNock
.delete('/v3/playlistItems', (body) => {
return body.id === 'UExWUDRtV2RxbGFhNWlwZEJRWXZVaFgyNk9RTENJRlV2cS41NkI0NEY2RDEwNTU3Q0M2';
})
.reply(200, {});
});
afterAll(() => youtubeNock.done());
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['playlistItems.workflow.json'],
});
});
});
@@ -0,0 +1,446 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [320, 1980],
"id": "ca093a4e-81cb-445a-980c-dcdacbb1cc36",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"filters": {},
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [540, 1780],
"id": "a36477d2-f98b-48b9-a392-0f615527a7e6",
"name": "YouTube",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {
"operation": "get",
"channelId": "UCabc123def456ghi789jkl"
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [540, 1980],
"id": "afcfc3bb-a16a-4890-a4b3-692f843b9103",
"name": "YouTube1",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {
"operation": "update",
"channelId": "UCabc123def456ghi789jkl",
"updateFields": {
"brandingSettingsUi": {
"channelSettingsValues": {
"channel": {
"description": "Test"
}
}
}
}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [540, 2180],
"id": "3a5f1c72-7dd1-4a76-958f-b9ecf7f8afbe",
"name": "YouTube2",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [760, 1780],
"id": "0ef3bfeb-6772-4eae-b4f6-5a8a05678861",
"name": "Channel Get All"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [760, 1980],
"id": "9af6133d-2a77-4245-83a7-1fb137997f3a",
"name": "Channel Get"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [760, 2180],
"id": "0d316d95-7e49-4497-a31c-9e56b1a7fda3",
"name": "Channel Update"
}
],
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "YouTube",
"type": "main",
"index": 0
},
{
"node": "YouTube2",
"type": "main",
"index": 0
},
{
"node": "YouTube1",
"type": "main",
"index": 0
}
]
]
},
"YouTube": {
"main": [
[
{
"node": "Channel Get All",
"type": "main",
"index": 0
}
]
]
},
"YouTube1": {
"main": [
[
{
"node": "Channel Get",
"type": "main",
"index": 0
}
]
]
},
"YouTube2": {
"main": [
[
{
"node": "Channel Update",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"Channel Get All": [
{
"json": {
"kind": "youtube#channel",
"etag": "etag_123456",
"id": "UCabc123def456ghi789jkl",
"snippet": {
"title": "Tech Insights",
"description": "Latest tech reviews and industry insights.",
"customUrl": "@techinsights",
"publishedAt": "2015-06-15T09:30:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Tech Insights",
"description": "Latest tech reviews and industry insights."
}
},
"statistics": {
"viewCount": "1250000",
"subscriberCount": "45000",
"hiddenSubscriberCount": false,
"videoCount": "320"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
}
},
{
"json": {
"kind": "youtube#channel",
"etag": "etag_654321",
"id": "UCxyz987uvw654rst321mno",
"snippet": {
"title": "Daily Vlogs",
"description": "Sharing daily experiences and adventures.",
"customUrl": "@dailyvlogs",
"publishedAt": "2018-03-10T14:20:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_vlog1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_vlog1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_vlog1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Daily Vlogs",
"description": "Sharing daily experiences and adventures."
}
},
"statistics": {
"viewCount": "530000",
"subscriberCount": "12000",
"hiddenSubscriberCount": false,
"videoCount": "540"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
}
},
{
"json": {
"kind": "youtube#channel",
"etag": "etag_987654",
"id": "UCopq135stu864vwx579yzb",
"snippet": {
"title": "Fitness Pro",
"description": "Workout tips and health advice.",
"customUrl": "@fitnesspro",
"publishedAt": "2017-08-22T07:45:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_fitness1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_fitness1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_fitness1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Fitness Pro",
"description": "Workout tips and health advice."
}
},
"statistics": {
"viewCount": "850000",
"subscriberCount": "30000",
"hiddenSubscriberCount": false,
"videoCount": "215"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
}
},
{
"json": {
"kind": "youtube#channel",
"etag": "etag_246810",
"id": "UCghi123stu456vwx789yzc",
"snippet": {
"title": "Gamer's Den",
"description": "Game walkthroughs, reviews, and tips.",
"customUrl": "@gamersden",
"publishedAt": "2016-12-05T12:10:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_gamer1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_gamer1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_gamer1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Gamer's Den",
"description": "Game walkthroughs, reviews, and tips."
}
},
"statistics": {
"viewCount": "1950000",
"subscriberCount": "80000",
"hiddenSubscriberCount": false,
"videoCount": "640"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
}
},
{
"json": {
"kind": "youtube#channel",
"etag": "etag_135790",
"id": "UCjkl987stu654vwx321mno",
"snippet": {
"title": "Cooking Mastery",
"description": "Delicious recipes and cooking tips.",
"customUrl": "@cookingmastery",
"publishedAt": "2019-04-17T16:55:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_cooking1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_cooking1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_cooking1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Cooking Mastery",
"description": "Delicious recipes and cooking tips."
}
},
"statistics": {
"viewCount": "650000",
"subscriberCount": "25000",
"hiddenSubscriberCount": false,
"videoCount": "190"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
}
}
],
"Channel Get": [
{
"json": {
"kind": "youtube#channel",
"etag": "etag_123456",
"id": "UCabc123def456ghi789jkl",
"snippet": {
"title": "Tech Insights",
"description": "Latest tech reviews and industry insights.",
"customUrl": "@techinsights",
"publishedAt": "2015-06-15T09:30:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Tech Insights",
"description": "Latest tech reviews and industry insights."
}
},
"statistics": {
"viewCount": "1250000",
"subscriberCount": "45000",
"hiddenSubscriberCount": false,
"videoCount": "320"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
}
}
],
"Channel Update": [
{
"json": {
"kind": "youtube#channel",
"etag": "someEtag",
"id": "UCabc123def456ghi789jkl",
"brandingSettings": {
"channel": {
"description": "Test"
},
"image": {}
}
}
}
]
},
"meta": {
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
}
}
@@ -0,0 +1,32 @@
[
{
"kind": "youtube#videoCategory",
"etag": "grPOPYEUUZN3ltuDUGEWlrTR90U",
"id": "1",
"snippet": {
"title": "Film & Animation",
"assignable": true,
"channelId": "UCFAKE1234567890TEST"
}
},
{
"kind": "youtube#videoCategory",
"etag": "Q0xgUf8BFM8rW3W0R9wNq809xyA",
"id": "2",
"snippet": {
"title": "Autos & Vehicles",
"assignable": true,
"channelId": "UCFAKE1234567890TEST"
}
},
{
"kind": "youtube#videoCategory",
"etag": "qnpwjh5QlWM5hrnZCvHisquztC4",
"id": "10",
"snippet": {
"title": "Music",
"assignable": true,
"channelId": "UCFAKE1234567890TEST"
}
}
]
@@ -0,0 +1,217 @@
[
{
"kind": "youtube#channel",
"etag": "etag_123456",
"id": "UCabc123def456ghi789jkl",
"snippet": {
"title": "Tech Insights",
"description": "Latest tech reviews and industry insights.",
"customUrl": "@techinsights",
"publishedAt": "2015-06-15T09:30:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_tech1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Tech Insights",
"description": "Latest tech reviews and industry insights."
}
},
"statistics": {
"viewCount": "1250000",
"subscriberCount": "45000",
"hiddenSubscriberCount": false,
"videoCount": "320"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
},
{
"kind": "youtube#channel",
"etag": "etag_654321",
"id": "UCxyz987uvw654rst321mno",
"snippet": {
"title": "Daily Vlogs",
"description": "Sharing daily experiences and adventures.",
"customUrl": "@dailyvlogs",
"publishedAt": "2018-03-10T14:20:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_vlog1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_vlog1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_vlog1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Daily Vlogs",
"description": "Sharing daily experiences and adventures."
}
},
"statistics": {
"viewCount": "530000",
"subscriberCount": "12000",
"hiddenSubscriberCount": false,
"videoCount": "540"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
},
{
"kind": "youtube#channel",
"etag": "etag_987654",
"id": "UCopq135stu864vwx579yzb",
"snippet": {
"title": "Fitness Pro",
"description": "Workout tips and health advice.",
"customUrl": "@fitnesspro",
"publishedAt": "2017-08-22T07:45:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_fitness1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_fitness1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_fitness1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Fitness Pro",
"description": "Workout tips and health advice."
}
},
"statistics": {
"viewCount": "850000",
"subscriberCount": "30000",
"hiddenSubscriberCount": false,
"videoCount": "215"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
},
{
"kind": "youtube#channel",
"etag": "etag_246810",
"id": "UCghi123stu456vwx789yzc",
"snippet": {
"title": "Gamer's Den",
"description": "Game walkthroughs, reviews, and tips.",
"customUrl": "@gamersden",
"publishedAt": "2016-12-05T12:10:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_gamer1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_gamer1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_gamer1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Gamer's Den",
"description": "Game walkthroughs, reviews, and tips."
}
},
"statistics": {
"viewCount": "1950000",
"subscriberCount": "80000",
"hiddenSubscriberCount": false,
"videoCount": "640"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
},
{
"kind": "youtube#channel",
"etag": "etag_135790",
"id": "UCjkl987stu654vwx321mno",
"snippet": {
"title": "Cooking Mastery",
"description": "Delicious recipes and cooking tips.",
"customUrl": "@cookingmastery",
"publishedAt": "2019-04-17T16:55:00Z",
"thumbnails": {
"default": {
"url": "https://yt3.ggpht.com/ytc/AIdro_cooking1=s88-c-k-c0x00ffffff-no-rj",
"width": 88,
"height": 88
},
"medium": {
"url": "https://yt3.ggpht.com/ytc/AIdro_cooking1=s240-c-k-c0x00ffffff-no-rj",
"width": 240,
"height": 240
},
"high": {
"url": "https://yt3.ggpht.com/ytc/AIdro_cooking1=s800-c-k-c0x00ffffff-no-rj",
"width": 800,
"height": 800
}
},
"localized": {
"title": "Cooking Mastery",
"description": "Delicious recipes and cooking tips."
}
},
"statistics": {
"viewCount": "650000",
"subscriberCount": "25000",
"hiddenSubscriberCount": false,
"videoCount": "190"
},
"status": {
"privacyStatus": "public",
"isLinked": true,
"longUploadsStatus": "eligible"
}
}
]
@@ -0,0 +1,104 @@
[
{
"kind": "youtube#playlistItem",
"etag": "etag1",
"id": "fakePlaylistItemId1",
"snippet": {
"publishedAt": "2023-02-12T11:15:49Z",
"channelId": "UCXXXXCHANNELID01",
"title": "Video Title 1",
"description": "This is a fake video description for testing.",
"thumbnails": {
"default": { "url": "https://example.com/thumb1.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/thumb1.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/thumb1.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/thumb1.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/thumb1.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Fake Channel 1",
"playlistId": "PLXXXXFAKEPLAYLISTID01",
"position": 0,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID1"
},
"videoOwnerChannelTitle": "FakeOwner1",
"videoOwnerChannelId": "UCOWNERCHANNELID01"
},
"contentDetails": {
"videoId": "FAKEVIDID1",
"videoPublishedAt": "2023-01-13T00:00:09Z"
},
"status": {
"privacyStatus": "public"
}
},
{
"kind": "youtube#playlistItem",
"etag": "etag2",
"id": "fakePlaylistItemId2",
"snippet": {
"publishedAt": "2023-02-12T11:16:16Z",
"channelId": "UCXXXXCHANNELID02",
"title": "Video Title 2",
"description": "Another test video description.",
"thumbnails": {
"default": { "url": "https://example.com/thumb2.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/thumb2.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/thumb2.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/thumb2.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/thumb2.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Fake Channel 2",
"playlistId": "PLXXXXFAKEPLAYLISTID02",
"position": 1,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID2"
},
"videoOwnerChannelTitle": "FakeOwner2",
"videoOwnerChannelId": "UCOWNERCHANNELID02"
},
"contentDetails": {
"videoId": "FAKEVIDID2",
"videoPublishedAt": "2016-06-23T11:41:12Z"
},
"status": {
"privacyStatus": "public"
}
},
{
"kind": "youtube#playlistItem",
"etag": "etag3",
"id": "fakePlaylistItemId3",
"snippet": {
"publishedAt": "2023-02-12T11:18:13Z",
"channelId": "UCXXXXCHANNELID03",
"title": "Video Title 3",
"description": "Yet another placeholder video description.",
"thumbnails": {
"default": { "url": "https://example.com/thumb3.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/thumb3.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/thumb3.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/thumb3.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/thumb3.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Fake Channel 3",
"playlistId": "PLXXXXFAKEPLAYLISTID03",
"position": 2,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID3"
},
"videoOwnerChannelTitle": "FakeOwner3",
"videoOwnerChannelId": "UCOWNERCHANNELID03"
},
"contentDetails": {
"videoId": "FAKEVIDID3",
"videoPublishedAt": "2017-02-18T17:18:00Z"
},
"status": {
"privacyStatus": "public"
}
}
]
@@ -0,0 +1,59 @@
[
{
"kind": "youtube#playlist",
"etag": "etag_placeholder_1",
"id": "playlist_id_1",
"snippet": {
"publishedAt": "2024-11-22T12:40:13Z",
"channelId": "channel_id_placeholder",
"title": "Playlist 1",
"description": "Description for Playlist 1.",
"thumbnails": {
"default": { "url": "https://example.com/default.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/mqdefault.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/hqdefault.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/sddefault.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/maxresdefault.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Channel Placeholder",
"defaultLanguage": "en",
"localized": {
"title": "Playlist 1",
"description": "Description for Playlist 1."
}
},
"status": { "privacyStatus": "public" },
"contentDetails": { "itemCount": 16 },
"player": {
"embedHtml": "<iframe width=\"640\" height=\"360\" src=\"https://example.com/embed/playlist1\" frameborder=\"0\" allowfullscreen></iframe>"
}
},
{
"kind": "youtube#playlist",
"etag": "etag_placeholder_2",
"id": "playlist_id_2",
"snippet": {
"publishedAt": "2024-08-30T18:23:41Z",
"channelId": "channel_id_placeholder",
"title": "Playlist 2",
"description": "Description for Playlist 2.",
"thumbnails": {
"default": { "url": "https://example.com/default.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/mqdefault.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/hqdefault.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/sddefault.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/maxresdefault.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Channel Placeholder",
"localized": {
"title": "Playlist 2",
"description": "Description for Playlist 2."
}
},
"status": { "privacyStatus": "public" },
"contentDetails": { "itemCount": 18 },
"player": {
"embedHtml": "<iframe width=\"640\" height=\"360\" src=\"https://example.com/embed/playlist2\" frameborder=\"0\" allowfullscreen></iframe>"
}
}
]
@@ -0,0 +1,412 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 200],
"id": "31e68280-caac-4297-bb03-ca6d548b459e",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "playlistItem",
"operation": "getAll",
"playlistId": "PLXXXXFAKEPLAYLISTID01",
"limit": 3,
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [220, -100],
"id": "790afd1a-f8b5-4bc1-b839-9658ee112890",
"name": "YouTube",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [440, -100],
"id": "84ef4be3-8c98-449f-ba1e-b08d1492435e",
"name": "Playlist Item Get All"
},
{
"parameters": {
"resource": "playlistItem",
"operation": "get",
"playlistItemId": "fakePlaylistItemId1",
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [220, 100],
"id": "e3c6f615-7fc1-48cd-a365-25ba425eb62a",
"name": "YouTube1",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [440, 100],
"id": "2bf72f15-3ca8-4ceb-a558-0fc7426ef564",
"name": "Playlist Item Get"
},
{
"parameters": {
"resource": "playlistItem",
"playlistId": "PLXXXXFAKEPLAYLISTID01",
"videoId": "FAKEVIDID1",
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [220, 300],
"id": "1a84eae4-fd43-445c-8c2b-c5846c1fa427",
"name": "YouTube2",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [440, 300],
"id": "91cef9b3-843d-4ae9-b946-f2bc705cc528",
"name": "Playlist Item Add"
},
{
"parameters": {
"resource": "playlistItem",
"operation": "delete",
"playlistItemId": "UExWUDRtV2RxbGFhNWlwZEJRWXZVaFgyNk9RTENJRlV2cS41NkI0NEY2RDEwNTU3Q0M2",
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [220, 500],
"id": "829b494e-2a4e-4ec5-a3e2-2dabeed9ea0b",
"name": "YouTube3",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [440, 500],
"id": "3195a72c-b8bd-4d85-ba37-d24df23b1761",
"name": "Playlist Item Delete"
}
],
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "YouTube",
"type": "main",
"index": 0
},
{
"node": "YouTube1",
"type": "main",
"index": 0
},
{
"node": "YouTube3",
"type": "main",
"index": 0
},
{
"node": "YouTube2",
"type": "main",
"index": 0
}
]
]
},
"YouTube": {
"main": [
[
{
"node": "Playlist Item Get All",
"type": "main",
"index": 0
}
]
]
},
"YouTube1": {
"main": [
[
{
"node": "Playlist Item Get",
"type": "main",
"index": 0
}
]
]
},
"YouTube2": {
"main": [
[
{
"node": "Playlist Item Add",
"type": "main",
"index": 0
}
]
]
},
"YouTube3": {
"main": [
[
{
"node": "Playlist Item Delete",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"Playlist Item Get All": [
{
"json": {
"kind": "youtube#playlistItem",
"etag": "etag1",
"id": "fakePlaylistItemId1",
"snippet": {
"publishedAt": "2023-02-12T11:15:49Z",
"channelId": "UCXXXXCHANNELID01",
"title": "Video Title 1",
"description": "This is a fake video description for testing.",
"thumbnails": {
"default": { "url": "https://example.com/thumb1.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/thumb1.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/thumb1.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/thumb1.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/thumb1.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Fake Channel 1",
"playlistId": "PLXXXXFAKEPLAYLISTID01",
"position": 0,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID1"
},
"videoOwnerChannelTitle": "FakeOwner1",
"videoOwnerChannelId": "UCOWNERCHANNELID01"
},
"contentDetails": {
"videoId": "FAKEVIDID1",
"videoPublishedAt": "2023-01-13T00:00:09Z"
},
"status": {
"privacyStatus": "public"
}
}
},
{
"json": {
"kind": "youtube#playlistItem",
"etag": "etag2",
"id": "fakePlaylistItemId2",
"snippet": {
"publishedAt": "2023-02-12T11:16:16Z",
"channelId": "UCXXXXCHANNELID02",
"title": "Video Title 2",
"description": "Another test video description.",
"thumbnails": {
"default": { "url": "https://example.com/thumb2.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/thumb2.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/thumb2.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/thumb2.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/thumb2.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Fake Channel 2",
"playlistId": "PLXXXXFAKEPLAYLISTID02",
"position": 1,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID2"
},
"videoOwnerChannelTitle": "FakeOwner2",
"videoOwnerChannelId": "UCOWNERCHANNELID02"
},
"contentDetails": {
"videoId": "FAKEVIDID2",
"videoPublishedAt": "2016-06-23T11:41:12Z"
},
"status": {
"privacyStatus": "public"
}
}
},
{
"json": {
"kind": "youtube#playlistItem",
"etag": "etag3",
"id": "fakePlaylistItemId3",
"snippet": {
"publishedAt": "2023-02-12T11:18:13Z",
"channelId": "UCXXXXCHANNELID03",
"title": "Video Title 3",
"description": "Yet another placeholder video description.",
"thumbnails": {
"default": { "url": "https://example.com/thumb3.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/thumb3.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/thumb3.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/thumb3.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/thumb3.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Fake Channel 3",
"playlistId": "PLXXXXFAKEPLAYLISTID03",
"position": 2,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID3"
},
"videoOwnerChannelTitle": "FakeOwner3",
"videoOwnerChannelId": "UCOWNERCHANNELID03"
},
"contentDetails": {
"videoId": "FAKEVIDID3",
"videoPublishedAt": "2017-02-18T17:18:00Z"
},
"status": {
"privacyStatus": "public"
}
}
}
],
"Playlist Item Get": [
{
"json": {
"kind": "youtube#playlistItem",
"etag": "etag1",
"id": "fakePlaylistItemId1",
"snippet": {
"publishedAt": "2023-02-12T11:15:49Z",
"channelId": "UCXXXXCHANNELID01",
"title": "Video Title 1",
"description": "This is a fake video description for testing.",
"thumbnails": {
"default": {
"url": "https://example.com/thumb1.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://example.com/thumb1.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://example.com/thumb1.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://example.com/thumb1.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://example.com/thumb1.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "Fake Channel 1",
"playlistId": "PLXXXXFAKEPLAYLISTID01",
"position": 0,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID1"
},
"videoOwnerChannelTitle": "FakeOwner1",
"videoOwnerChannelId": "UCOWNERCHANNELID01"
},
"contentDetails": {
"videoId": "FAKEVIDID1",
"videoPublishedAt": "2023-01-13T00:00:09Z"
},
"status": {
"privacyStatus": "public"
}
}
}
],
"Playlist Item Add": [
{
"json": {
"kind": "youtube#playlistItem",
"etag": "etag1",
"id": "fakePlaylistItemId1",
"snippet": {
"publishedAt": "2023-02-12T11:15:49Z",
"channelId": "UCXXXXCHANNELID01",
"title": "Video Title 1",
"description": "This is a fake video description for testing.",
"thumbnails": {
"default": { "url": "https://example.com/thumb1.jpg", "width": 120, "height": 90 },
"medium": { "url": "https://example.com/thumb1.jpg", "width": 320, "height": 180 },
"high": { "url": "https://example.com/thumb1.jpg", "width": 480, "height": 360 },
"standard": { "url": "https://example.com/thumb1.jpg", "width": 640, "height": 480 },
"maxres": { "url": "https://example.com/thumb1.jpg", "width": 1280, "height": 720 }
},
"channelTitle": "Fake Channel 1",
"playlistId": "PLXXXXFAKEPLAYLISTID01",
"position": 0,
"resourceId": {
"kind": "youtube#video",
"videoId": "FAKEVIDID1"
},
"videoOwnerChannelTitle": "FakeOwner1",
"videoOwnerChannelId": "UCOWNERCHANNELID01"
},
"contentDetails": {
"videoId": "FAKEVIDID1",
"videoPublishedAt": "2023-01-13T00:00:09Z"
},
"status": {
"privacyStatus": "public"
}
}
}
],
"Playlist Item Delete": [
{
"json": {
"success": true
}
}
]
},
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
}
}
@@ -0,0 +1,536 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [580, 1460],
"id": "974c3af2-835d-47c5-b3ee-879daeb475dc",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "playlist",
"operation": "create",
"title": "Playlist 1",
"options": {
"description": "Description for Playlist 1.",
"privacyStatus": "public",
"defaultLanguage": "en"
}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [800, 1060],
"id": "df81d20e-4c27-4839-873c-3a90b1d9356c",
"name": "YouTube",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {
"resource": "playlist",
"operation": "update",
"playlistId": "playlist_id_1",
"title": "Test Updated",
"updateFields": {
"description": "This description is updated",
"privacyStatus": "private"
}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [800, 1260],
"id": "ab9110d9-552d-4e7e-bc8f-78763a2a6395",
"name": "YouTube1",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {
"resource": "playlist",
"operation": "get",
"playlistId": "playlist_id_1",
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [800, 1460],
"id": "e3939be4-1319-421e-96ca-282fea9f0a9e",
"name": "YouTube2",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {
"resource": "playlist",
"filters": {},
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [800, 1660],
"id": "4ea4cd62-6f2f-4e71-ae5e-6ef50718956b",
"name": "YouTube3",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {
"resource": "playlist",
"operation": "delete",
"playlistId": "playlist_id_1",
"options": {}
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [800, 1860],
"id": "8bf6c082-bd4a-45d1-8625-46062fe7aeed",
"name": "YouTube4",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1020, 1060],
"id": "fe18eabc-0dc5-4ac8-8fe3-9a72558811cc",
"name": "Playlist Create"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1020, 1260],
"id": "be72d8a0-a83e-46e7-94f0-13e11c93e965",
"name": "Playlist Update"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1020, 1460],
"id": "6555e151-2214-46bf-8ca5-1a03a929236e",
"name": "Playlist Get"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1020, 1660],
"id": "f9ac55b1-ff4e-4f98-8c75-05c3f456682c",
"name": "Playlist Get All"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1020, 1860],
"id": "f2091eb1-cc41-468d-9c3b-6217a9124c85",
"name": "Playlist Delete"
}
],
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "YouTube1",
"type": "main",
"index": 0
},
{
"node": "YouTube",
"type": "main",
"index": 0
},
{
"node": "YouTube2",
"type": "main",
"index": 0
},
{
"node": "YouTube3",
"type": "main",
"index": 0
},
{
"node": "YouTube4",
"type": "main",
"index": 0
}
]
]
},
"YouTube": {
"main": [
[
{
"node": "Playlist Create",
"type": "main",
"index": 0
}
]
]
},
"YouTube1": {
"main": [
[
{
"node": "Playlist Update",
"type": "main",
"index": 0
}
]
]
},
"YouTube2": {
"main": [
[
{
"node": "Playlist Get",
"type": "main",
"index": 0
}
]
]
},
"YouTube3": {
"main": [
[
{
"node": "Playlist Get All",
"type": "main",
"index": 0
}
]
]
},
"YouTube4": {
"main": [
[
{
"node": "Playlist Delete",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"Playlist Create": [
{
"json": {
"kind": "youtube#playlist",
"etag": "etag_placeholder_1",
"id": "playlist_id_1",
"snippet": {
"publishedAt": "2024-11-22T12:40:13Z",
"channelId": "channel_id_placeholder",
"title": "Playlist 1",
"description": "Description for Playlist 1.",
"thumbnails": {
"default": {
"url": "https://example.com/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://example.com/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://example.com/hqdefault.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://example.com/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://example.com/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "Channel Placeholder",
"defaultLanguage": "en",
"localized": {
"title": "Playlist 1",
"description": "Description for Playlist 1."
}
},
"status": {
"privacyStatus": "public"
},
"contentDetails": {
"itemCount": 16
},
"player": {
"embedHtml": "<iframe width=\"640\" height=\"360\" src=\"https://example.com/embed/playlist1\" frameborder=\"0\" allowfullscreen></iframe>"
}
}
}
],
"Playlist Update": [
{
"json": {
"kind": "youtube#playlist",
"etag": "etag_placeholder_1",
"id": "playlist_id_1",
"snippet": {
"publishedAt": "2024-11-22T12:40:13Z",
"channelId": "channel_id_placeholder",
"title": "The title is updated",
"description": "The description is updated",
"thumbnails": {
"default": {
"url": "https://example.com/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://example.com/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://example.com/hqdefault.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://example.com/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://example.com/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "Channel Placeholder",
"defaultLanguage": "en",
"localized": {
"title": "The title is updated",
"description": "The description is updated"
}
},
"status": {
"privacyStatus": "public"
},
"contentDetails": {
"itemCount": 16
},
"player": {
"embedHtml": "<iframe width=\"640\" height=\"360\" src=\"https://example.com/embed/playlist1\" frameborder=\"0\" allowfullscreen></iframe>"
}
}
}
],
"Playlist Get": [
{
"json": {
"kind": "youtube#playlist",
"etag": "etag_placeholder_1",
"id": "playlist_id_1",
"snippet": {
"publishedAt": "2024-11-22T12:40:13Z",
"channelId": "channel_id_placeholder",
"title": "Playlist 1",
"description": "Description for Playlist 1.",
"thumbnails": {
"default": {
"url": "https://example.com/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://example.com/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://example.com/hqdefault.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://example.com/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://example.com/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "Channel Placeholder",
"defaultLanguage": "en",
"localized": {
"title": "Playlist 1",
"description": "Description for Playlist 1."
}
},
"status": {
"privacyStatus": "public"
},
"contentDetails": {
"itemCount": 16
},
"player": {
"embedHtml": "<iframe width=\"640\" height=\"360\" src=\"https://example.com/embed/playlist1\" frameborder=\"0\" allowfullscreen></iframe>"
}
}
}
],
"Playlist Get All": [
{
"json": {
"kind": "youtube#playlist",
"etag": "etag_placeholder_1",
"id": "playlist_id_1",
"snippet": {
"publishedAt": "2024-11-22T12:40:13Z",
"channelId": "channel_id_placeholder",
"title": "Playlist 1",
"description": "Description for Playlist 1.",
"thumbnails": {
"default": {
"url": "https://example.com/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://example.com/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://example.com/hqdefault.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://example.com/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://example.com/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "Channel Placeholder",
"defaultLanguage": "en",
"localized": {
"title": "Playlist 1",
"description": "Description for Playlist 1."
}
},
"status": {
"privacyStatus": "public"
},
"contentDetails": {
"itemCount": 16
},
"player": {
"embedHtml": "<iframe width=\"640\" height=\"360\" src=\"https://example.com/embed/playlist1\" frameborder=\"0\" allowfullscreen></iframe>"
}
}
},
{
"json": {
"kind": "youtube#playlist",
"etag": "etag_placeholder_2",
"id": "playlist_id_2",
"snippet": {
"publishedAt": "2024-08-30T18:23:41Z",
"channelId": "channel_id_placeholder",
"title": "Playlist 2",
"description": "Description for Playlist 2.",
"thumbnails": {
"default": {
"url": "https://example.com/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://example.com/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://example.com/hqdefault.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://example.com/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://example.com/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "Channel Placeholder",
"localized": {
"title": "Playlist 2",
"description": "Description for Playlist 2."
}
},
"status": {
"privacyStatus": "public"
},
"contentDetails": {
"itemCount": 18
},
"player": {
"embedHtml": "<iframe width=\"640\" height=\"360\" src=\"https://example.com/embed/playlist2\" frameborder=\"0\" allowfullscreen></iframe>"
}
}
}
],
"Playlist Delete": [
{
"json": {
"success": true
}
}
]
},
"meta": {
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
}
}
@@ -0,0 +1,117 @@
import type { MockProxy } from 'jest-mock-extended';
import { mock } from 'jest-mock-extended';
import type { IExecuteFunctions } from 'n8n-workflow';
import { Readable } from 'stream';
import * as genericFunctions from '../../GenericFunctions';
import { YouTube } from '../../YouTube.node';
jest.mock('../../GenericFunctions', () => {
const originalModule = jest.requireActual('../../GenericFunctions');
return {
...originalModule,
googleApiRequest: jest.fn(async function (method: string) {
if (method === 'POST') {
return {
headers: { location: 'https://www.youtube.com/watch?v=1234' },
body: { id: '1234' },
};
}
return {};
}),
};
});
const httpRequestMock = jest.fn(() => ({ id: '5678' }));
describe('Test YouTube, video => upload', () => {
let youTube: YouTube;
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
let fromSpy: jest.SpyInstance;
beforeEach(() => {
fromSpy = jest.spyOn(Readable, 'from');
youTube = new YouTube();
mockExecuteFunctions = mock<IExecuteFunctions>();
const buffer = Buffer.alloc(2 * 1024 * 1024, 'a');
mockExecuteFunctions.helpers = {
constructExecutionMetaData: jest.fn(() => []),
returnJsonArray: jest.fn(() => []),
assertBinaryData: jest.fn(() => ({ data: buffer.toString('base64'), mimeType: 'video/mp4' })),
httpRequest: httpRequestMock,
} as any;
});
afterEach(() => {
fromSpy.mockRestore();
jest.clearAllMocks();
});
it('should create readable from buffer', async () => {
const items = [{ json: { data: 'test' } }];
mockExecuteFunctions.getInputData.mockReturnValue(items);
mockExecuteFunctions.getNodeParameter.mockImplementation((key: string) => {
switch (key) {
case 'resource':
return 'video';
case 'operation':
return 'upload';
case 'title':
return 'test';
case 'categoryId':
return '11';
case 'options':
return {};
case 'binaryProperty':
return 'data';
default:
}
});
await youTube.execute.call(mockExecuteFunctions);
expect(genericFunctions.googleApiRequest).toHaveBeenCalledTimes(1);
expect(genericFunctions.googleApiRequest).toHaveBeenCalledWith(
'POST',
'/upload/youtube/v3/videos',
{
recordingDetails: { recordingDate: undefined },
snippet: {
categoryId: '11',
defaultLanguage: undefined,
description: undefined,
tags: undefined,
title: 'test',
},
status: {
embeddable: undefined,
license: undefined,
privacyStatus: undefined,
publicStatsViewable: undefined,
publishAt: undefined,
selfDeclaredMadeForKids: undefined,
},
},
{
notifySubscribers: false,
part: 'snippet,status,recordingDetails',
uploadType: 'resumable',
},
undefined,
{
headers: { 'X-Upload-Content-Length': 2097152, 'X-Upload-Content-Type': 'video/mp4' },
json: true,
resolveWithFullResponse: true,
},
);
expect(httpRequestMock).toHaveBeenCalledWith({
body: expect.any(Object),
headers: { 'Content-Length': 2097152, 'Content-Range': 'bytes 0-2097151/2097152' },
method: 'PUT',
url: 'https://www.youtube.com/watch?v=1234',
});
expect(fromSpy).toHaveBeenCalledTimes(1);
});
});
@@ -0,0 +1,106 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "31e68280-caac-4297-bb03-ca6d548b459e",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "videoCategory",
"regionCode": "GB",
"limit": 3
},
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [220, 0],
"id": "790afd1a-f8b5-4bc1-b839-9658ee112890",
"name": "YouTube",
"credentials": {
"youTubeOAuth2Api": {
"id": "62",
"name": "YouTube OAuth2 creds"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [440, 0],
"id": "84ef4be3-8c98-449f-ba1e-b08d1492435e",
"name": "Video Category Get All"
}
],
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "YouTube",
"type": "main",
"index": 0
}
]
]
},
"YouTube": {
"main": [
[
{
"node": "Video Category Get All",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"Video Category Get All": [
{
"json": {
"kind": "youtube#videoCategory",
"etag": "grPOPYEUUZN3ltuDUGEWlrTR90U",
"id": "1",
"snippet": {
"title": "Film & Animation",
"assignable": true,
"channelId": "UCFAKE1234567890TEST"
}
}
},
{
"json": {
"kind": "youtube#videoCategory",
"etag": "Q0xgUf8BFM8rW3W0R9wNq809xyA",
"id": "2",
"snippet": {
"title": "Autos & Vehicles",
"assignable": true,
"channelId": "UCFAKE1234567890TEST"
}
}
},
{
"json": {
"kind": "youtube#videoCategory",
"etag": "qnpwjh5QlWM5hrnZCvHisquztC4",
"id": "10",
"snippet": {
"title": "Music",
"assignable": true,
"channelId": "UCFAKE1234567890TEST"
}
}
}
]
},
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB