JavaScript >> Javascript 文檔 >  >> Node.js

如何在 Node.js 中獲取 YouTube 視頻的時長

如何使用 YouTube API 獲取視頻的元數據並解析時長字符串以分別獲取小時、分鐘和秒。

開始使用

對於本教程,我們將使用 CheatCode Node.js Boilerplate 為我們的工作提供一個起點。首先,讓我們克隆一個副本:

終端

git clone https://github.com/cheatcode/nodejs-server-boilerplate.git

接下來,安裝依賴項:

終端

cd nodejs-server-boilerplate && npm install

安裝後,添加 node-fetch 我們將用於向 YouTube API 發送請求的依賴項:

終端

npm i node-fetch

安裝好之後,啟動開發服務器:

終端

npm run dev

運行後,我們就可以跳入代碼了。

連接端點以獲取持續時間

在我們開始獲取持續時間之前,我們將使用 Express 設置一個 HTTP 端點,我們可以使用它來調用我們的獲取代碼。

/api/index.js

import graphql from "./graphql/server";
import getYoutubeVideoDuration from "../lib/getYoutubeVideoDuration";

export default (app) => {
  graphql(app);
  app.use("/youtube/duration/:videoId", async (req, res) => {
    const duration = await getYoutubeVideoDuration(req?.params?.videoId);
    res.set("Content-Type", "application/json");
    res.send(JSON.stringify(duration, null, 2));
  });
};

在我們用於本教程的樣板中,一個 Express 應用程序已經在 /index.js 中為我們初始化 在應用程序的根目錄。在該文件中,導入多個函數並通過 Express app 實例。在這個文件中,我們定義了負責定義 API 相關路由的函數之一。

默認情況下,樣板支持 GraphQL API,該 API 已在此處導入並稱為移交 Express app 實例。這裡的重點是組織;沒什麼技術。此時您需要了解的是 app 作為我們在這裡定義的函數的參數傳入的是 app 當我們調用 express() 時返回的實例 express 導出的函數 .

這裡重要的部分是我們如何使用 那個app 實例。為了更輕鬆地獲取視頻時長,我們通過 app.use() 定義了一條新路由 Express 導出的方法。在這裡,我們期望 URL http://localhost:5001/youtube/duration/:videoId 返回我們一個或多個對象的數組,詳細說明一個或多個視頻的持續時間。這裡,:videoId 將被一個或多個 YouTube 視頻 ID(例如 http://localhost:5001/youtube/duration/RjzC1Dgh17Ahttp://localhost:5001/youtube/duration/RjzC1Dgh17A,KgzQuE1pR1w,VH8RoWfklg4 )。

在函數的回調中,我們可以看到我們正在調用一個我們將定義下一個 getYoutubeVideoDuration() 的函數 ,將預期的 :videoId 傳遞給它 通過 req?.params?.videoId 從我們的 URL ? 問號只是說“如果 req 存在並且 params 存在於 req , 和 videoId 存在於 req.params ,返回 videoId 在這裡。”同樣,videoId 將是一個包含一個或多個 YouTube 視頻 ID 的字符串(如果有多個,我們希望它們以逗號分隔)。

當我們調用這個函數時,我們需要放置一個 await 關鍵字,並確保添加 async 我們路由的回調函數的關鍵字。這是必需的。如果我們省略 async 關鍵字,當我們運行這個關於 await 的代碼時,我們會得到一個錯誤 作為保留關鍵字。這裡,await 是說“當你到達這行代碼時,等到它返回的 JavaScript Promise 被解析,或者,等到這段代碼完成後再評估這一行之後的行。”

接下來,為了響應請求,我們先設置Content-Type application/json 的標頭 使用 res.set() Express 提供的方法,最後,通過 res.send() 用我們找到的持續時間數組響應請求 .這裡,JSON.stringify(duration, null, 2) 部分只是“美化”我們返回的字符串,因此它在瀏覽器中被隔開而不是混合在一起(有助於可讀性)。

現在我們已經設置了基本的腳手架,為了使這項工作,讓我們來看看 getYoutubeVideoDuration 我們在文件頂部導入的函數。

從 YouTube API 獲取視頻的元數據

有兩件事要做。首先,我們需要向 YouTube 的 API 發出請求,以獲取視頻的元數據——這將包括視頻的時長——其次,我們需要從元數據中解析時長,以便在我們的應用程序(假設)。

現在讓我們將請求連接到 API 並取回元數據:

/lib/getYoutubeVideoDuration.js

import fetch from "node-fetch";
import { URL, URLSearchParams } from "url";
import settings from "./settings";

const getDuration = (durationString = "") => {
  // We'll handle conversion of the duration string for each video here...
};

export default async (youtubeVideoId = '') => {
  const url = new URL("https://www.googleapis.com/youtube/v3/videos");
  url.search = new URLSearchParams({
    key: settings?.youtube?.apiKey,
    part: "contentDetails",
    id: youtubeVideoId,
  }).toString();

  return fetch(url)
    .then(async (response) => {
      const data = await response.json();
      const videos = data?.items || [];
      return videos.map((video) => {
        return {
          id: video?.id,
          duration: getDuration(video?.contentDetails?.duration),
        };
      });
    })
    .catch((error) => {
      console.warn(error);
    });
};

為了使我們的工作更容易一些,我們在此處輸出與 YouTube API 通信所需的所有代碼。首先,從這個文件中,我們導出一個函數,該函數接受預期的 youtubeVideoId 字符串(我們在這裡使用單數形式,但這並沒有改變我們可以傳遞帶有逗號分隔列表的字符串)。

接下來,使用 URL 從原生 Node.js url 導入的構造函數 包——原生意味著你不需要安裝任何額外的東西——我們創建一個新的 url 對象,傳入 YouTube API 的基本 URL(特別是 v3 視頻端點)。

使用我們的 url 對象(我們從 new URL() ),接下來,為了將數據傳遞給 YouTube,我們需要使用查詢參數(而不是 POST 正文)。為了減少傳遞這些查詢參數的錯誤率,我們使用 URLSearchParams 構造函數也是從 Node.js url 導入的 包裹。向它傳遞一個我們想要序列化(轉換)為查詢字符串的對象,例如 ?key=someAPIKey&part=contentDetails&id=someVideoId .在這裡,我們分配 url.search 到這裡 search 屬性是 url 使用的名稱 庫來引用 URL 對像上的查詢參數(查詢參數的原始意圖的技術工件,用於幫助向搜索操作添加上下文)。

專注於什麼 我們傳遞的參數,我們關心三個:

  1. key 其中包含我們的 YouTube API 密鑰(如果您還沒有其中之一,但在此處了解如何生成一個 - 請確保獲取 API 密鑰版本,而不是 OAuth2 版本)。
  2. part 它描述了我們希望 YouTube API 返回的可用數據的哪一部分來響應我們的請求。
  3. id 這是我們要為其獲取數據的一個或多個 Youtube 視頻 ID 的字符串。

值得注意的是,key 我們在這裡使用的是我們正在使用的樣板中內置的設置約定。這為我們提供了一種特定於環境的方式來在我們的應用程序中安全地存儲配置數據。 settings 頂部導入的值來自 /lib/settings.js 文件,其中包含決定從我們應用程序的根目錄加載哪個設置文件的代碼。它使用 process.env.NODE_ENV 的當前值來執行此操作 .

對於本教程,因為我們在 development 環境,我們將加載 settings-development.json 文件位於我們應用程序的根目錄。如果我們部署到 production 環境,我們會加載 settings-production.json .快速瀏覽一下那個文件,讓我們看看我們的 Youtube API 密鑰需要去哪裡:

/settings-development.json

{
  "authentication": {
    "token": "abcdefghijklmnopqrstuvwxyz1234567890"
  },
  ...
  "youtube": {
    "apiKey": "Your key goes here..."
  }
}

按字母順序,我們添加一個屬性 youtube 到帶有嵌套 apiKey 的主設置對象 屬性,其值設置為我們從 YouTube 檢索到的 API 密鑰。當我們調用 settings?.youtube?.apiKey 時返回我們的代碼 ,這是我們引用的值。

/lib/getYoutubeVideoDuration.js

import fetch from "node-fetch";
import { URL, URLSearchParams } from "url";
import settings from "./settings";

const getDuration = (durationString = "") => {
  // We'll handle conversion of the duration string for each video here...
};

export default async (youtubeVideoId = '') => {
  const url = new URL("https://www.googleapis.com/youtube/v3/videos");
  url.search = new URLSearchParams({
    key: settings?.youtube?.apiKey,
    part: "contentDetails",
    id: youtubeVideoId,
  }).toString();

  return fetch(url)
    .then(async (response) => {
      const data = await response.json();
      const videos = data?.items || [];
      return videos.map((video) => {
        return {
          id: video?.id,
          duration: getDuration(video?.contentDetails?.duration),
        };
      });
    })
    .catch((error) => {
      console.warn(error);
    });
};

完成所有配置後,我們就可以從 YouTube 獲取視頻元數據了。使用 fetch 我們從 node-fetch 頂部導入的函數 我們之前安裝的包(這只是瀏覽器 fetch() 的節點友好實現 方法),我們傳入我們的 url 對象,附加一個 .then().catch() 最後回調,這意味著我們預計我們對 fetch() 的調用 將返回一個 JavaScript Promise。

.catch() 回調,如果出現問題,我們只需使用 console.warn() 將錯誤註銷到我們的服務器控制台 (如果適用,您可能希望將其交給您的日誌記錄工具)。

我們這里關心的部分,.then() 回調,是所有動作發生的地方。首先,我們取 response 我們期望傳遞給 .then() 的參數 回調,調用它的 .json() 方法和使用 await 關鍵字——記得添加 async 回調函數的關鍵字以避免語法錯誤。

這裡,response.json()fetch() 的函數 為我們提供了允許我們將獲得的 HTTP 響應對象轉換回我們選擇的格式(在我們調用的 API 的限制內)的方法。在這種情況下,我們希望 YouTube 發回給我們的數據位於 JSON 中 格式,所以我們使用 .json() 此處的方法將原始響應轉換為 JSON 數據。

用那個 data 對象,接下來,我們希望 YouTube 添加一個 items 該對象的屬性,其中包含一個或多個對象的數組,描述我們通過 id 傳遞的視頻 ID url 中的查詢參數 .

現在是有趣的部分。使用我們的 videos 列表 (一個或多個),我們希望將該數據格式化為在我們的應用程序中更有用的東西。默認情況下,YouTube 會格式化 duration 存儲在視頻的 contentDetails 下的時間戳 對像作為一個類似於 PT1H23M15S 的字符串 它描述了一個視頻時長為 1 小時 23 分 15 秒的視頻。

照原樣,這個字符串不是很有幫助,所以我們想把它轉換成我們可以在代碼中實際使用的東西。為此,在下一節中,我們將裝配那個 getDuration() 我們在這裡調用的方法。

在我們這樣做之前,很明顯,一旦我們檢索到這個格式化的持續時間值,因為我們正在返回對 videos.map() 的調用 回到我們的 .then() 回調和也是 返回我們對 fetch() 的調用 從我們的函數中,我們期望映射的 videos 數組是我們從這個文件中導出的函數返回的值(最終會返回給我們的 res.send() 在`/api/index.js)。

解析 YouTube API 返回的時長字符串

讓我們隔離那個 getDuration() 我們在文件頂部指定的函數,並介紹它是如何工作的。

/lib/getYoutubeVideoDuration.js

const getDuration = (durationString = "") => {
  const duration = { hours: 0, minutes: 0, seconds: 0 };
  const durationParts = durationString
    .replace("PT", "")
    .replace("H", ":")
    .replace("M", ":")
    .replace("S", "")
    .split(":");

  if (durationParts.length === 3) {
    duration["hours"] = durationParts[0];
    duration["minutes"] = durationParts[1];
    duration["seconds"] = durationParts[2];
  }

  if (durationParts.length === 2) {
    duration["minutes"] = durationParts[0];
    duration["seconds"] = durationParts[1];
  }

  if (durationParts.length === 1) {
    duration["seconds"] = durationParts[0];
  }

  return {
    ...duration,
    string: `${duration.hours}h${duration.minutes}m${duration.seconds}s`,
  };
};

我們這裡的目標是取回一個具有四個屬性的對象:

  1. hours 描述多少小時(0 或更多)視頻播放。
  2. minutes 描述多少分鐘(0 或更多)視頻播放。
  3. seconds 描述視頻播放了多少秒。
  4. 將上述三個值連接在一起的字符串,我們可以(假設地)顯示在應用的 UI 中。

為此,首先,我們初始化一個名為 duration 的對象 其中將包含 hours , minutes , 和 seconds 對於我們的視頻。在這裡,我們在對像上設置這些屬性並將它們默認為 0 .

接下來,請記住我們的持續時間字符串類似於:PT1H23M15S .它也可以看起來像 PT23M15SPT15S 如果長度少於一小時或少於一分鐘。為了處理這些不同的情況,這裡我們採用 durationString 我們已經傳入並首先刪除 PT 部分使用 .replace() 然後交換 HM : 的零件 符號,最後,刪除 S 價值。

在這個鏈的末端,我們調用一個 .split(): 我們剛剛添加到字符串中以將小時、分鐘和秒拆分為一個數組的字符。所以很明顯,轉換流程是這樣的:

// 1
PT1H23M15S

// 2
1H23M15S

// 3
1:23:15S

// 4
1:23:15

// 5
['1', '23', '15']

有了這些durationParts 我們可以開始朝著更容易使用持續時間值的方向發展。更具體地說,我們需要做的工作是確定 hours , minutes , 和 seconds duration 上的屬性 我們在函數頂部定義的對象需要設置為(如果有的話)。

我們在這裡使用的技巧是測試 durationParts 的長度 大批。如果它包含 3 項目,我們知道它有小時、分鐘和秒。如果它包含 2 項目,我們知道它有分和秒。如果它有 1 項目,我們知道它有秒數。

對於每種情況,我們添加一個 if 語句,在其中我們覆蓋 duration 上的適當值 durationParts 中相應的持續時間部分對應的對象 大批。所以,在這裡,如果我們有 3 項目,我們設置 duration.hours 到數組中的第一項,duration.minutes 到數組中的第二個項目,以及 duration.seconds 到數組中的第三項(如果此處的 0、1、2 令人困惑,請記住 JavaScript 數組是從零開始的,這意味著數組中的第一項位於零位置)。

我們對其他兩種情況重複這種模式,只覆蓋我們期望大於零的值(2 的分鐘和秒 項目數組和 1 只需幾秒鐘 項目數組)。

使用我們的 duration 最終,在我們的 getDuration() 底部構建了對象 函數我們返回一個對象,使用 JavaScript ... 擴展運算符“解包”我們的 duration 對象屬性到該新對像上並添加一個額外的 string 連接我們的 duration 的屬性 對象的值放在一個字符串中。

而已!現在,我們準備試一試這個東西。

測試獲取持續時間

為了測試這一點,讓我們在瀏覽器中加載我們在教程開頭定義的 HTTP 端點,並傳遞一些 Youtube 視頻 ID:

驚人的!嘗試使用任何 YouTube 視頻 ID 來獲取持續時間對象。

總結

在本教程中,我們學習瞭如何在 Express 中連接 HTTP 端點,以幫助我們調用一個函數,該函數通過 YouTube API 發送對 YouTube 視頻元數據的 GET 請求。我們學習瞭如何使用 node-fetch 幫助我們執行請求以及如何編寫一個函數來幫助我們解析從 API 返回的 YouTube 時長字符串。


Tutorial JavaScript 教程
  1. 天才之路:初學者#7

  2. javascript 中是否有空合併 (Elvis) 運算符或安全導航運算符?

  3. 使用 Grommet、PropelAuth 和 Next.js 設置響應式 B2B 項目

  4. 用 JS、Python 和 Java 學習算法#10:金字塔

  5. 如何在 JavaScript 中處理“未定義”

  6. 重構一個小的 Next 應用程序以使用 Hooks

  7. 帶有 React 的數字時鐘

  1. 獲取錯誤 .replace 不是函數

  2. 將 addListener 用於稍後將創建的元素

  3. 使用 Stimulus 2.0(測試版)複製到剪貼板按鈕

  4. Angular 8 的主要功能:有什麼新功能?

  5. 決定在內部構建和購買 UI 組件庫之間的 8 個關鍵因素

  6. 理解 React 中的 setState/useState

  7. 使用 React Native 創建人臉識別應用

  1. 在 Nodejs / MongoDB /Passport /JWT 中構建 REST API

  2. Angular 13 PDF 教程 – 使用 JSPDF 在 Angular 中導出 PDF

  3. 用 JavaScript 構建聊天應用程序

  4. 看原型