← YouTube (Data API) + Discord Webhook integrations

Send Message (Advanced) with Discord Webhook API on New Videos in Subscribed Channels from YouTube (Data API) API

Pipedream makes it easy to connect APIs for Discord Webhook, YouTube (Data API) and 900+ other apps remarkably fast.

Trigger workflow on
New Videos in Subscribed Channels from the YouTube (Data API) API
Next, do this
Send Message (Advanced) with the Discord Webhook API
No credit card required
Into to Pipedream
Watch us build a workflow
Watch us build a workflow
7 min
Watch now ➜
Trusted by 250,000+ developers from startups to Fortune 500 companies:
Trusted by 250,000+ developers from startups to Fortune 500 companies

Developers Pipedream

Getting Started

This integration creates a workflow with a YouTube (Data API) trigger and Discord Webhook action. When you configure and deploy the workflow, it will run on Pipedream's servers 24x7 for free.

  1. Select this integration
  2. Configure the New Videos in Subscribed Channels trigger
    1. Connect your YouTube (Data API) account
    2. Configure Polling interval
  3. Configure the Send Message (Advanced) action
    1. Connect your Discord Webhook account
    2. Optional- Configure Message
    3. Optional- Configure Thread ID
    4. Optional- Configure Username
    5. Optional- Configure Avatar URL
    6. Optional- Configure Include link to workflow
    7. Optional- Configure Embeds
  4. Deploy the workflow
  5. Send a test event to validate your setup
  6. Turn on the trigger

Details

This integration uses pre-built, open source components from Pipedream's GitHub repo. These components are developed by Pipedream and the community, and verified and maintained by Pipedream.

To contribute an update to an existing component or create a new component, create a PR on GitHub. If you're new to Pipedream component development, you can start with quickstarts for trigger span and action development, and then review the component API reference.

Trigger

Description:Emit new event for each new YouTube video posted to a subscribed channel.
Version:0.0.2
Key:youtube_data_api-new-videos-in-subscribed-channels

YouTube (Data API) Overview

There are lots of things you can build using the YouTube API! Here are just a
few examples:

  • A YouTube video player
  • A YouTube search engine
  • A tool to help you find popular videos on YouTube
  • A way to keep track of your favorite YouTube videos
  • A way to find new and interesting YouTube channels to watch

Trigger Code

import common from "../common.mjs";
import { toArray } from "../../utils.mjs";

/**
 * @typedef {import('@googleapis/youtube').youtube_v3.Schema$Subscription} Subscription
 * @typedef {import('@googleapis/youtube').youtube_v3.Schema$PlaylistItem} PlaylistItem
 * @typedef {import('@googleapis/youtube').youtube_v3.Schema$Channel} Channel
 */

/**
 * The number of channels to check for new videos whenever the event source is setup and deployed
 * for the first time.
 *
 * Note that the event source could check fewer channels if the authenticated user has fewer
 * subscriptions.
 */
const INITIAL_CHANNEL_COUNT = 10;

/**
 * The number of days ago to emit videos published after on deploy.
 */
const INITIAL_PUBLISHED_AFTER_DAYS_AGO = 7;

/**
 * Uses [YouTube API](https://developers.google.com/youtube/v3/docs) to get the authenticated user's
 * subscriptions with a `totalItemCount` for each. The user's subscriptions are used to get the
 * subscribed-to channels. Then the ID of the 'uploads' playlist in each channel is used to get
 * playlist items of recently uploaded videos. Process is taken roughly from [this stackoverflow
 * comment](https://bit.ly/3lp4uRS).
 *
 * Sequence: Subscriptions -> Channels -> PlaylistItems
 *
 * The YouTube API allows [listing channels](https://bit.ly/3Fh5Hm2) using a comma-separated list of
 * channel IDs, so channels are fetched using a single series of paginated requests. To list
 * playlist items [PlaylistItems](https://bit.ly/2WN7EW2), a separate request must be made for each
 * channel's 'uploads' playlist. To limit the number of requests, playlist items are fetched only
 * for channels (subscriptions) whose [totalItemCount](https://bit.ly/3ldUYAR) is greater than the
 * last recorded `totalItemCount`.
 */
export default {
  ...common,
  key: "youtube_data_api-new-videos-in-subscribed-channels",
  name: "New Videos in Subscribed Channels",
  description: "Emit new event for each new YouTube video posted to a subscribed channel.",
  version: "0.0.2",
  type: "source",
  dedupe: "unique",
  hooks: {
    ...common.hooks,
    async deploy() {
      const subscriptions = await this.getSubscriptions();
      this.setChannelData(subscriptions);

      // To emit events on the first run - set `totalItemCount` to 0 for first
      // `INITIAL_CHANNEL_COUNT` subscribed channels to check these channels for new uploads
      const channelData = this._getChannelData();
      subscriptions.slice(0, INITIAL_CHANNEL_COUNT).forEach((s) => {
        channelData[s.snippet.resourceId.channelId].totalItemCount = 0;
      });
      this._setChannelData(channelData);

      this._setPublishedAfter(
        this.youtubeDataApi.daysAgo(INITIAL_PUBLISHED_AFTER_DAYS_AGO).toISOString(),
      );
    },
  },
  methods: {
    ...common.methods,
    generateMeta(playlistItem) {
      const { snippet } = playlistItem;
      return {
        id: snippet.resourceId.videoId,
        summary: snippet.title,
        ts: Date.parse(snippet.publishedAt),
      };
    },
    /**
     * @typedef {{ totalItemCount: Number, lastPublishedAt: String }} ChannelDatum
     * @typedef {Object.<string, ChannelDatum>} ChannelData - A dict mapping channelId to an object
     * containing `lastPublishedAt` times and `totalItemCount` for each channel
     *
     * @returns {ChannelData} The channel data
     */
    _getChannelData() {
      return this.db.get("channelData");
    },
    /**
     * @param {ChannelData} channelData - The channelData to set
     */
    _setChannelData(channelData) {
      this.db.set("channelData", channelData);
    },
    /**
     * Sets current data for each channel using the user's current channel subscriptions and
     * 'uploads' playlists containing newly uploaded videos (playlistItems)
     *
     * @param {Subscription[]} subscriptions - The authenticated user's subscriptions
     * @param {PlaylistItem[]} [itemLists=[]] - A list of playlists with newly uploaded videos
     */
    setChannelData(subscriptions, itemLists = []) {
      const prevChannelData = this._getChannelData();
      // Create `channelData` dict from subscriptions
      let channelData = subscriptions.reduce((channels, s) => {
        const channelId = s.snippet.resourceId.channelId;
        channels[channelId] = {
          totalItemCount: s.contentDetails.totalItemCount,
          lastPublishedAt: (
            prevChannelData
              && prevChannelData[channelId]
              && prevChannelData[channelId].lastPublishedAt
          ),
        };
        return channels;
      }, {});
      // For each channel with new uploads, set the new `lastPublishedAt`
      itemLists.forEach((list) => {
        if (list.length > 0) {
          const channelId = list[0].snippet.videoOwnerChannelId;
          channelData[channelId].lastPublishedAt = list[0].snippet.publishedAt;
        }
      });
      this._setChannelData(channelData);
    },
    /**
     * Gets the full list of subscriptions for the current user
     *
     * @returns {Subscription[]} the list of subscriptions
     */
    async getSubscriptions() {
      // `part.snippet` contains the `resourceId.channelId`
      // `part.contentDetails` contains the `totalItemCount`
      return await this.youtubeDataApi.listAll(this.youtubeDataApi.getSubscriptions.bind(this), {
        part: "contentDetails,id,snippet",
        mine: true,
      });
    },
    /**
     * Gets a list of channels from a list of channel IDs
     *
     * @param {String[]} channelIds - A list of channel IDs
     * @returns {Channel[]} The list of subscriptions
     */
    async getChannels(channelIds) {
      return await this.youtubeDataApi.listAll(this.youtubeDataApi.getChannels.bind(this), {
        part: "contentDetails,id",
        id: channelIds.join(","),
      });
    },
    /**
     * Gets a list of playlist items in the 'uploads' playlist of a channel published after the last
     * processed video from that playlist
     *
     * @param {ChannelData} channelData - The recorded data for each channel to use in filtering
     * playlist items
     * @param {Object} Channel - The channel from which to get 'uploads' playlist items
     * @returns {PlaylistItem[]} The list of playlist items
     */
    async getPlaylistItems(channel, channelData, publishedAfter) {
      // Use `playlistId` of [uploads](https://bit.ly/3FuJiC3) playlist
      const playlistId = channel.contentDetails.relatedPlaylists.uploads;
      publishedAfter = channelData[channel.id].lastPublishedAt
        ? Date.parse(channelData[channel.id].lastPublishedAt)
        : Date.parse(publishedAfter);
      return await toArray(this.youtubeDataApi.paginateUntil(
        this.youtubeDataApi.getPlaylistItems.bind(this),
        {
          part: "contentDetails,id,snippet",
          playlistId,
        },
        null,
        (item) => (
          !item.snippet.publishedAt
          || Date.parse(item.snippet.publishedAt) <= publishedAfter
        ),
      ));
    },
  },
  async run() {
    let channelData = this._getChannelData();
    const publishedAfter = this._getPublishedAfter();

    // Get all subscriptions (each has a totalItemCount) for the current user
    const subscriptions = await this.getSubscriptions();

    // To avoid making an separate API request for each subscribed channel's uploads, include only
    // channels whose `totalItemCount` is greater than its previously recorded `totalItemCount`
    const updatedChannelIds = subscriptions
      .filter((s) => (
        channelData[s.snippet.resourceId.channelId]
        && s.contentDetails.totalItemCount >
          channelData[s.snippet.resourceId.channelId].totalItemCount
      ))
      .map((s) => s.snippet.resourceId.channelId);

    if (updatedChannelIds.length === 0) {
      return this.setChannelData(subscriptions);
    }

    const channels = await this.getChannels(updatedChannelIds);

    const uploadsPlaylists = await Promise.all(
      channels.map((channel) =>
        this.getPlaylistItems(channel, channelData, publishedAfter)),
    );

    this.setChannelData(subscriptions, uploadsPlaylists);
    this._setPublishedAfter(new Date().toISOString());

    const allItems = [].concat(...uploadsPlaylists);
    // Sort playlistItems in order of `publishedAt`
    allItems.sort((a, b) => Date.parse(a.snippet.publishedAt) - Date.parse(b.snippet.publishedAt));

    // Emit playlistItems
    allItems.forEach((item) => {
      this.emitEvent(item);
    });
  },
};

Trigger Configuration

This component may be configured based on the props defined in the component code. Pipedream automatically prompts for input values in the UI and CLI.
LabelPropTypeDescription
YouTube (Data API)youtubeDataApiappThis component uses the YouTube (Data API) app.
N/Adb$.service.dbThis component uses $.service.db to maintain state between component invocations.
Polling intervaltimer$.interface.timer

Pipedream will poll the YouTube API on this schedule

Trigger Authentication

YouTube (Data API) uses OAuth authentication. When you connect your YouTube (Data API) account, Pipedream will open a popup window where you can sign into YouTube (Data API) and grant Pipedream permission to connect to your account. Pipedream securely stores and automatically refreshes the OAuth tokens so you can easily authenticate any YouTube (Data API) API.

Pipedream requests the following authorization scopes when you connect your account:

emailprofilehttps://www.googleapis.com/auth/youtube.readonlyhttps://www.googleapis.com/auth/youtube.uploadhttps://www.googleapis.com/auth/youtube

About YouTube (Data API)

Online video platform

Action

Description:Send a simple or structured message (using embeds) to a Discord channel
Version:0.3.1
Key:discord_webhook-send-message-advanced

Discord Webhook Overview

With Discord's Webhook API, you can create applications that send messages to
Discord channels automatically. For example, you could create a bot that sends
a message to a channel every time a new blog post is published, or a message to
a channel when someone joins your Discord server.

Here are some ideas for what you could build using the Discord Webhook API:

  • A bot that sends a message to a channel when a new blog post is published
  • A bot that sends a message to a channel when someone joins your Discord
    server
  • A bot that sends a message to a channel when a new product is added to your
    online store

Action Code

import common from "../send-message-common.mjs";

export default {
  ...common,
  key: "discord_webhook-send-message-advanced",
  name: "Send Message (Advanced)",
  description: "Send a simple or structured message (using embeds) to a Discord channel",
  version: "0.3.1",
  type: "action",
  props: {
    ...common.props,
    message: {
      propDefinition: [
        common.props.discordWebhook,
        "message",
      ],
      optional: true,
    },
    embeds: {
      propDefinition: [
        common.props.discordWebhook,
        "embeds",
      ],
    },
  },
  async run({ $ }) {
    const {
      message,
      avatarURL,
      threadID,
      username,
      includeSentViaPipedream,
      embeds,
    } = this;

    if (!message && !embeds) {
      throw new Error("This action requires at least 1 message OR embeds object. Please enter one or the other above.");
    }

    try {
      // No interesting data is returned from Discord
      await this.discordWebhook.sendMessage({
        avatarURL,
        threadID,
        username,
        embeds,
        content: includeSentViaPipedream
          ? this.appendPipedreamText(message ?? "")
          : message,
      });
      $.export("$summary", "Message sent successfully");
    } catch (err) {
      const unsentMessage = this.getUserInputProps();
      $.export("unsent", unsentMessage);
      throw err;
    }
  },
};

Action Configuration

This component may be configured based on the props defined in the component code. Pipedream automatically prompts for input values in the UI.

LabelPropTypeDescription
Discord WebhookdiscordWebhookappThis component uses the Discord Webhook app.
Messagemessagestring

Enter a simple message up to 2000 characters. This is the most commonly used field. However, it's optional if you pass embed content.

Thread IDthreadIDstring

If provided, the message will be posted to this thread

Usernameusernamestring

Overrides the current username of the webhook

Avatar URLavatarURLstring

If used, it overrides the default avatar of the webhook. Note: Consecutive posts by the same username within 10 minutes of each other will not display updated avatar.

Include link to workflowincludeSentViaPipedreamboolean

Defaults to true, includes a link to this workflow at the end of your Discord message.

Embedsembedsany

Optionally pass an array of embed objects. E.g., {{ [{"description":"Use markdown including *Italic* **bold** __underline__ ~~strikeout~~ [hyperlink](https://google.com) `code`"}] }}. To pass data from another step, enter a reference using double curly brackets (e.g., {{steps.mydata.$return_value}}).
Tip: Construct the embeds array in a Node.js code step, return it, and then pass the return value to this step.

Action Authentication

Discord Webhook uses OAuth authentication. When you connect your Discord Webhook account, Pipedream will open a popup window where you can sign into Discord Webhook and grant Pipedream permission to connect to your account. Pipedream securely stores and automatically refreshes the OAuth tokens so you can easily authenticate any Discord Webhook API.

Pipedream requests the following authorization scopes when you connect your account:

webhook.incomingemailidentify

About Discord Webhook

Use this app to send messages to a channel using Discord's incoming webhooks

More Ways to Connect Discord Webhook + YouTube (Data API)

Send Message with Discord Webhook API on New Liked Videos from YouTube (Data API) API
YouTube (Data API) + Discord Webhook
 
Try it
Send Message with Discord Webhook API on New Subscriber from YouTube (Data API) API
YouTube (Data API) + Discord Webhook
 
Try it
Send Message with Discord Webhook API on New Subscription from YouTube (Data API) API
YouTube (Data API) + Discord Webhook
 
Try it
Send Message with Discord Webhook API on New Videos by Location from YouTube (Data API) API
YouTube (Data API) + Discord Webhook
 
Try it
Send Message with Discord Webhook API on New Videos by Search from YouTube (Data API) API
YouTube (Data API) + Discord Webhook
 
Try it
New Liked Videos from the YouTube (Data API) API

Emit new event for each new Youtube video liked by the authenticated user.

 
Try it
New Subscriber from the YouTube (Data API) API

Emit new event for each new Youtube subscriber to user Channel.

 
Try it
New Subscription from the YouTube (Data API) API

Emit new event for each new subscription from authenticated user.

 
Try it
New Videos from the YouTube (Data API) API

Emit new event for each new Youtube video the user posts.

 
Try it
New Videos by Location from the YouTube (Data API) API

Emit new event for each new YouTube video tied to a location.

 
Try it
Channel Statistics with the YouTube (Data API) API

Returns statistics from my YouTube Channel or by id. See the docs for more information

 
Try it
List Activities with the YouTube (Data API) API

Returns a list of channel activity events that match the request criteria. See the docs for more information

 
Try it
List Playlists with the YouTube (Data API) API

Returns a collection of playlists that match the API request parameters. See the docs for more information

 
Try it
List Videos with the YouTube (Data API) API

Returns a list of videos that match the API request parameters. See the docs for more information

 
Try it
Update Playlist with the YouTube (Data API) API

Modifies a playlist. For example, you could change a playlist's title, description, or privacy status. If you are submitting an update request, and your request does not specify a value for a property that already has a value, the property's existing value will be deleted. See the docs for more information

 
Try it