From 3e80b55b7870184727ffd621729e62a1a7a90070 Mon Sep 17 00:00:00 2001 From: Juan Villela Date: Fri, 5 Nov 2021 17:21:56 -0400 Subject: [PATCH] feat: Add youtubes bookmarking script. --- bookmark-viemos.ts | 4 +-- bookmark-youtubes.ts | 81 ++++++++++++++++++++++++++++++++++++++++++++ typings.d.ts | 62 +++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 bookmark-youtubes.ts diff --git a/bookmark-viemos.ts b/bookmark-viemos.ts index 2da8c5e..9d5fc44 100644 --- a/bookmark-viemos.ts +++ b/bookmark-viemos.ts @@ -54,11 +54,11 @@ const getVimeoDetails = async (url: string): Promise => { }; /** - * Get podcast details and upload to Airtable. + * Get Vimeo video details and upload to Airtable. * @function * @async * - * @param {string} url podcast url + * @param {string} url video url * @param {string[]} tags record tags * @returns {Promise} result of record upload */ diff --git a/bookmark-youtubes.ts b/bookmark-youtubes.ts new file mode 100644 index 0000000..0f26b81 --- /dev/null +++ b/bookmark-youtubes.ts @@ -0,0 +1,81 @@ +import "https://deno.land/x/dotenv@v3.1.0/load.ts"; + +import { airtableUpload } from "./airtable-upload.ts"; + +import { + BookmarkData, + BookmarkingResponse, + YouTubeAPIEndpoint, + YouTubeResponse, +} from "./typings.d.ts"; + +/** + * Convert video url to API ready endpoint. Extracts youtube ID. + * @function + * + * @param {string} url video url + * @returns {YouTubeAPIEndpoint} API endpoint to fetch video data + bookmarking ready url + */ +const cleanUrl = (url: string): YouTubeAPIEndpoint => { + const extractedID = url + .replace(/(https\:\/\/)(youtu.*)\.(be|com)\/(watch\?v=)?/g, "") + .replace("&feature=share", ""); + const endpoint = `https://youtube.googleapis.com/youtube/v3/videos?id=${extractedID}`; + const link = `https://youtu.be/${extractedID}`; + + return { endpoint, link }; +}; + +/** + * Get video details via YouTube API. + * Docs: https://developers.google.com/youtube/v3/docs/videos/list + * @function + * @async + * + * @param {string} url video url + * @returns {Promise} video title, creator, and url + */ +const getYouTubeDetails = async (url: string): Promise => { + try { + const { endpoint, link } = cleanUrl(url); + const request = await fetch( + `${endpoint}&key=${Deno.env.get("YOUTUBE_KEY")}` + ); + const response: YouTubeResponse = await request.json(); + const video = response.items[0].snippet; + + return { + title: video.title, + creator: video.channelTitle, + url: link, + }; + } catch (error) { + throw new Error(`Gettingg youtube details: \n ${error}`); + } +}; + +/** + * Get YouTube video details and upload to Airtable. + * @function + * @async + * + * @param {string} url video url + * @param {string[]} tags record tags + * @returns {Promise} result of record upload + */ +export const bookmarkYouTube = async ( + url: string, + tags: string[] +): Promise => { + try { + const youtTubeData = await getYouTubeDetails(url); + const airtableResp = await airtableUpload("Videos", { + ...youtTubeData, + tags, + }); + + return { success: true, message: airtableResp, source: "bookmarkYouTube" }; + } catch (error) { + return { success: false, message: error, source: "bookmarkYouTube" }; + } +}; diff --git a/typings.d.ts b/typings.d.ts index fecd941..35abe2e 100644 --- a/typings.d.ts +++ b/typings.d.ts @@ -32,6 +32,11 @@ export interface ParsingPatterns { [key: string]: ParsingService | RegExp[]; } +export interface YouTubeAPIEndpoint { + endpoint: string; + link: string; +} + export interface TwitterResponse { data: { author_id: string; @@ -989,6 +994,63 @@ export interface VimeoResponse { width: number; } +export interface YouTubeResponse { + kind: string; + etag: string; + items: { + kind: string; + etag: string; + id: string; + snippet: { + publishedAt: string; + channelId: string; + title: string; + description: string; + thumbnails: { + default: { + url: string; + width: number; + height: number; + }; + medium: { + url: string; + width: number; + height: number; + }; + high: { + url: string; + width: number; + height: number; + }; + standard: { + url: string; + width: number; + height: number; + }; + maxres: { + url: string; + width: number; + height: number; + }; + }; + channelTitle: string; + tags: string[]; + categoryId: string; + liveBroadcastContent: string; + defaultLanguage: string; + localized: { + title: string; + description: string; + }; + defaultAudioLanguage: string; + }; + }[]; + pageInfo: { + totalResults: number; + resultsPerPage: number; + }; +} + export interface RecordData { title?: string; tweet?: string;