Daniel Wykerd fb68e6bcfe feat!: better cross runtime support (#97)
* refactor: remove dependancies

removes node-forge and uuid in favor of Web APIs

* refactor!: commonjs to es6

To aid with #93 I will make all my changes in TypeScript instead.
This is the first step into making that happen.

Used: https://github.com/wessberg/cjstoesm

* refactor!: NToken and Signature TS files

Bring this PR up to speed with #93

* feat: cross platform cache (WIP)

this is untested!
should remove idb as dependecy.

* feat: EventEmitter polyfill

* refactor: remove events

* feat: HTTPClient based on Fetch API (WIP)

* refactor!: parsers refactor (WIP)

Initial TS support for parsers as per #93

This adds several type safety checks to the parser which'll help to
ensure valid data is returned by the parser.

* refactor!: parsers refactor (WIP)

Bring more in line with the existing implementations & make less verbose

* refactor!: parser refactor

I was overcomplicating things, this is much simpler and compatible with
the existing JS API

* fix: some missed parsers while refactoring

* fix: better type inferance for parseResponse

* feat(TS): typesafe YTNode casts

* feat: more type safety in YTNode and Parser

* refactor: VideoInfo download with fetch & TS (WIP)

Again, this also does some work for #93

* fix: LiveChat in VideoInfo

* refactor!: more typesafety in parser

* refactor!: VideoInfo almost completed

* refactor!: player and session refactors

- Remove the Player class' dependance on Session.
- Add additional context to the Session.

* refactor!: move auth logic to Session (WIP)

* refactor: TS port for Actions and Innertube

My fingers hurt from typing out all those types :-P

* refactor: NavigationEndpoint TS

this is still a WIP and should be improved.
NavigationEndpoint should probably be refactored further.

* refactor!: VideoInfo compiles without errors

* chore: delete old player

* fix: import errors

It compiles and runs!!

* fix: Utils import fixes

* fix: several runtime errors

* fix: video streaming

* chore: remove console.log debugging

Whoops, forgot to remove these before I pushed the previous commit

* chore: remove old unused dependencies

* fix: typescript errors

Now emitting declarations and source maps

* refactor: TS feed

* chore: delete old Feed

* refactor: move streamToIterable into Utils

* refactor: AccountManager TS

* refactor: FilterableFeed to TS

* refactor: InteractionManager to TS

* refactor: PlaylistManager to TS

* refactor: TabbedFeed to TS

* refactor: Music to TS (WIP)

more work to be done, see TODO comments

* fix: getting the tests to pass (6/12)

YouTube.js Tests
    Search
      ✓ Should search on YouTube (1152 ms)
      ✕ Should search on YouTube Music (705 ms)
      ✕ Should retrieve YouTube search suggestions (722 ms)
      ✓ Should retrieve YouTube Music search suggestions (233 ms)
    Comments
      ✓ Should retrieve comments (585 ms)
      ✕ Should retrieve next batch of comments (221 ms)
      ✕ Should retrieve comment replies (1 ms)
    General
      ✕ Should retrieve playlist with YouTube (732 ms)
      ✓ Should retrieve home feed (838 ms)
      ✓ Should retrieve trending content (543 ms)
      ✓ Should retrieve video info (639 ms)
      ✕ Should download video (5 ms)

* fix: tests (7/12)

YouTube.js Tests
    Search
      ✓ Should search on YouTube (1984 ms)
      ✕ Should search on YouTube Music (1139 ms)
      ✕ Should retrieve YouTube search suggestions (1433 ms)
      ✓ Should retrieve YouTube Music search suggestions (529 ms)
    Comments
      ✓ Should retrieve comments (324 ms)
      ✓ Should retrieve next batch of comments (395 ms)
      ✕ Should retrieve comment replies
    General
      ✕ Should retrieve playlist with YouTube (653 ms)
      ✓ Should retrieve home feed (1085 ms)
      ✓ Should retrieve trending content (513 ms)
      ✓ Should retrieve video info (921 ms)
      ✕ Should download video (3 ms)

* fix: download tests (8/12)

YouTube.js Tests
    Search
      ✓ Should search on YouTube (1293 ms)
      ✕ Should search on YouTube Music (927 ms)
      ✕ Should retrieve YouTube search suggestions (1250 ms)
      ✓ Should retrieve YouTube Music search suggestions (258 ms)
    Comments
      ✓ Should retrieve comments (803 ms)
      ✓ Should retrieve next batch of comments (511 ms)
      ✕ Should retrieve comment replies
    General
      ✕ Should retrieve playlist with YouTube (528 ms)
      ✓ Should retrieve home feed (1047 ms)
      ✓ Should retrieve trending content (548 ms)
      ✓ Should retrieve video info (825 ms)
      ✓ Should download video (1779 ms)

* fix: tests (9/12)

YouTube.js Tests
    Search
      ✓ Should search on YouTube (1276 ms)
      ✕ Should search on YouTube Music (955 ms)
      ✓ Should retrieve YouTube search suggestions (661 ms)
      ✓ Should retrieve YouTube Music search suggestions (491 ms)
    Comments
      ✓ Should retrieve comments (624 ms)
      ✓ Should retrieve next batch of comments (353 ms)
      ✕ Should retrieve comment replies
    General
      ✕ Should retrieve playlist with YouTube (672 ms)
      ✓ Should retrieve home feed (1277 ms)
      ✓ Should retrieve trending content (999 ms)
      ✓ Should retrieve video info (1106 ms)
      ✓ Should download video (2514 ms)

* feat: key based type validation for parsers

* fix: comments tests pass (10/12)

YouTube.js Tests
    Search
      ✓ Should search on YouTube (938 ms)
      ✕ Should search on YouTube Music (850 ms)
      ✓ Should retrieve YouTube search suggestions (528 ms)
      ✓ Should retrieve YouTube Music search suggestions (224 ms)
    Comments
      ✓ Should retrieve comments (518 ms)
      ✓ Should retrieve next batch of comments (337 ms)
      ✓ Should retrieve comment replies (358 ms)
    General
      ✕ Should retrieve playlist with YouTube (466 ms)
      ✓ Should retrieve home feed (1051 ms)
      ✓ Should retrieve trending content (623 ms)
      ✓ Should retrieve video info (863 ms)
      ✓ Should download video (2656 ms)

* refactor: type safety checks removing @ts-ignore

* fix: playlist tests pass (11/12)

YouTube.js Tests
    Search
      ✓ Should search on YouTube (991 ms)
      ✕ Should search on YouTube Music (924 ms)
      ✓ Should retrieve YouTube search suggestions (606 ms)
      ✓ Should retrieve YouTube Music search suggestions (225 ms)
    Comments
      ✓ Should retrieve comments (393 ms)
      ✓ Should retrieve next batch of comments (284 ms)
      ✓ Should retrieve comment replies (252 ms)
    General
      ✓ Should retrieve playlist with YouTube (578 ms)
      ✓ Should retrieve home feed (1148 ms)
      ✓ Should retrieve trending content (541 ms)
      ✓ Should retrieve video info (799 ms)
      ✓ Should download video (1419 ms)

* fix: all tests pass for node 🎉

YouTube.js Tests
    Search
      ✓ Should search on YouTube (1053 ms)
      ✓ Should search on YouTube Music (761 ms)
      ✓ Should retrieve YouTube search suggestions (453 ms)
      ✓ Should retrieve YouTube Music search suggestions (221 ms)
    Comments
      ✓ Should retrieve comments (627 ms)
      ✓ Should retrieve next batch of comments (412 ms)
      ✓ Should retrieve comment replies (268 ms)
    General
      ✓ Should retrieve playlist with YouTube (565 ms)
      ✓ Should retrieve home feed (775 ms)
      ✓ Should retrieve trending content (498 ms)
      ✓ Should retrieve video info (875 ms)
      ✓ Should download video (1364 ms)

* build: working Deno bundle

Still need to test whether this bundle works in the browser

* docs: update deno example to download video

* refactor: MusicResponsiveListItem to TS

* docs: TSDoc for Parser helpers

* docs: Parser documentation for TS

* docs: add note about parseItem and parseArray

* test: remove browser tests since they're identical

* feat: browser support and proxy example

* fix: PlaylistManager TS after merge

* feat: in-browser video streaming

* refactor: cleanup the Dash example

* feat: allow custom fetch implementations

* feat: fetch debugger

* fix: OAuth login

* refactor: remove file extensions from imports

* refactor: build scripts

* fix: CustomEvent on node

* fix: LiveChat

* fix: linting

* fix: liniting in build-parser-json

* chore: update test workflow

* fix: NToken errors after lint fixes

* fix: codacy complaints

* docs: update to reflect changes

Definitly needs more work but its a start

* refactor: cleanup imports/exports

* fix: browser example

- Remove user-agent before making request.
- Fix cache on browsers

* fix: cache on node

* fix: stupid mistake

* refactor: Session#signIn to wait untill success

This also splits the 'auth' event up into 3 distinct events:
- 'auth' -> fired on success
- 'auth-pending' -> fired when pending authentication
- 'auth-error' -> fired when an error occurred

* refactor: freeze Constants

* refactor: cleanup HTTPClient Request

* refactor: debugFetch readability

* chore: lint

* refactor: replace jsdoc with tsdoc eslint plugin

remove @param annotations without descriptions

* fix: bunch of liniting warnings

* refactor: better inference on YTNode#is

As suggested by @MasterOfBob777

* fix: linting warnings

* revert: undici import

* refactor: rename `list_type` to `item_type`
2022-07-20 14:06:12 -03:00
2022-05-18 06:01:07 -03:00
2022-04-13 01:51:03 -03:00

YouTube.js

A full-featured wrapper around the Innertube API, which is what YouTube itself uses.

Report Bug · Request Feature

Tests Latest version Codefactor Monthly downloads Say thanks Discord

Note

:

We're currently in the process of rewriting some parts of the library to improve performance, maintainability and quality. While this might take a little while, most of this documentation will no longer be valid after v2 (see #65).


Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Contributing
  5. License
  6. Contact
  7. Disclaimer

About

Innertube is an API used across all YouTube clients, it was created to simplify the internal structure of the platform in a way that updates, tweaks, and experiments can be easily made. This library handles all the low-level communication with Innertube, providing a simple, fast, and efficient way to interact with YouTube programmatically.

And huge thanks to @gatecrasher777 for his research on the workings of the Innertube API!

If you have any questions or need help, feel free to contact us on our chat server here.

Features

  • Search videos, playlists, music, albums, artists, etc.
  • Subscribe, unsubscribe, like, dislike, post comments, replies, and etc.
  • Get subscriptions/home feed, notifications, watch history, and more.
  • Easily sign in to any Google Account.
  • Fetch live chat & live stats.
  • Manage account settings.
  • Manage playlists.
  • Download videos.

~ And more!

Getting Started

Prerequisites

  • NodeJS v14 or greater

    To verify things are set up properly, run this:

    node --version
    

Installation

  • NPM:
    npm install youtubei.js@latest
    
  • Yarn:
    yarn add youtubei.js@latest
    
  • Git (bleeding-edge version):
    npm install git+https://github.com/LuanRT/YouTube.js.git
    

Usage

Create an Innertube instance (or session):

// const Innertube = require('youtubei.js'); 
import Innertube from 'youtubei.js';
const youtube = await new Innertube({ gl: 'US' });

Options:

  • client: YOUTUBE | YTMUSIC

  • filters (WIP, youtube only):

    • upload_date: any | last_hour | today | this_week | this_month | this_year

    • type: any | video | channel | playlist | movie

    • duration: any | short | medium | long

    • sort_by: relevance | rating | upload_date | view_count

const search = await youtube.search('QUERY', { client: 'YOUTUBE' });
YouTube Output

{
  query: string,
  corrected_query: string,
  estimated_results: number,
  videos: [
    {
      id: string,
      url: string,
      title: string,
      description: string,
      metadata: {
        view_count: string,
        short_view_count_text: {
          simple_text: string,
          accessibility_label: string
        },
        thumbnails: object[],
        duration: {
          seconds: number,
          simple_text: string,
          accessibility_label: string
        },
        published: string,
        badges: string[],
        owner_badges: string[]
      }
    }
  ]
}

YTMusic Output

{
  query: string,
  corrected_query: string,
  results: {
    top_result: object[],  // <- Can be anything; video, playlist, artist etc..
    songs: [
      {
        id: string,
        title: string,
        artist: string,
        album: string,
        duration: string,
        thumbnails: object[]
      }
    ],
    videos: [
      {
        id: string,
        title: string,
        author: string,
        views: string,
        duration: string,
        thumbnails: object[]
      }
    ],
    albums: [
      {
        id: string,
        title: string,
        author: string,
        year: string,
        thumbnails: object[]
      }
    ],
    featured_playlists: [
      {
        id: string,
        title: string,
        author: string,
        channel_id: string,
        total_items:number
      }
    ],
    community_playlists: [
      {
        id: string,
        title: string,
        author: string,
        channel_id: string,
        total_items: number
      }
    ],
    artists: [
      {
        id: string,
        name: string,
        subscribers: string,
        thumbnails: object[]
      }
    ]
  }
}


Search suggestions:

const suggestions = await youtube.getSearchSuggestions('QUERY', { client: 'YOUTUBE' })
Output

{
  query: string,
  results: string[]
}

Video info:

const video = await youtube.getDetails('VIDEO_ID');
Output

{
  title: string,
  description: string,
  thumbnail: {
    url: string,
    width: number,
    height: number
  },
  metadata: {
    embed: {
      iframeUrl: string,
      flashUrl: string,
      width: number,
      height: number,
      flashSecureUrl: string
    },
    likes: {
      count: number, 
      short_count_text: string
    },
    view_count: number,
    average_rating: number,
    length_seconds: number,
    channel_id: string,
    channel_url: string,
    external_channel_id: string,
    allow_ratings: boolean,
    is_live_content: boolean,
    is_family_safe: boolean,
    is_unlisted: boolean,
    is_private: boolean,
    is_liked: boolean,
    is_disliked: boolean,
    is_subscribed: boolean,
    subscriber_count: string,
    current_notification_preference: string,
    publish_date_text: string,
    has_ypc_metadata: boolean,
    category: string,
    channel_name: string,
    publish_date: string,
    upload_date: string,
    keywords: string[]
  }
}

Comments:

Sorting options: TOP_COMMENTS | NEWEST_FIRST

const comments = await youtube.getComments('VIDEO_ID', 'TOP_COMMENTS');

Alternatively, you can use:

const video = await youtube.getDetails('VIDEO_ID');
const comments = await video.getComments(); 
Output

{
  page_count: number,
  comment_count: number,
  items: [
    {
      text: string,
      author: {
        name: string,
        thumbnails: [
          {
            url: string,
            width: number,
            height: number
          }
        ],
        channel_id: string
      },
      metadata: {
        published: string,
        is_liked: boolean,
        is_disliked: boolean,
        is_pinned: boolean,
        is_channel_owner: boolean,
        is_reply: boolean,
        like_count: number,
        reply_count: number,
        id: string
      }
    }
  ]
}

Reply to, like/dislike, translate and report a comment:

const top_comment = comments.items[0];

await top_comment.like();
await top_comment.dislike();
await top_comment.report();
await top_comment.reply('Nice comment!'); 

// Note: only ISO language codes are accepted
await top_comment.translate('ru');  

Comment replies:

const replies = await top_comment.getReplies();

Comments/replies continuation:

const continuation = await comments.getContinuation();
const replies_continuation = await replies.getContinuation();

Home feed:

const homefeed = await youtube.getHomeFeed();
Output

{
  videos: [
    {
      id: string,
      title: string,
      description: string,
      channel: {
        id: string,
        name: string,
        url: string
      },
      metadata: {
        view_count: string,
        short_view_count_text: {
          simple_text: string,
          accessibility_label: string
        },
        thumbnail: {
          url: string,
          width: number,
          height: number
        },
        moving_thumbnail: {
          url: string,
          width: number,
          height: number
        },
        published: string,
        badges: string[],
        owner_badges: string[]
      }
    }
  ]
}

Continuation:

const continuation = await homefeed.getContinuation();

Watch history:

const history = await youtube.getHistory();
Output

{
  items: [
    {
      date: string,
      videos: [
        {
          id: string,
          title: string,
          description: string,
          channel: {
            id: string,
            name: string,
            url: string
          },
          metadata: {
            view_count: string,
            short_view_count_text: {
              simple_text: string,
              accessibility_label: string
            },
            thumbnail: {
              url: string,
              width: number,
              height: number
            },
            moving_thumbnail: {
              url: string,
              width: number,
              height: number
            },
            published: string,
            badges: string[],
            owner_badges: string[]
          }
        }
      ]
    }
  ]
}

Continuation:

const continuation = await history.getContinuation();

Subscriptions feed:

const mysubsfeed = await youtube.getSubscriptionsFeed();
Output

{
  items: [
    {
      date: string,
      videos: [
        {
          id: string,
          title: string,
          description: string,
          channel: {
            id: string,
            name: string,
            url: string
          },
          metadata: {
            view_count: string,
            short_view_count_text: {
              simple_text: string,
              accessibility_label: string
            },
            thumbnail: {
              url: string,
              width: number,
              height: number
            },
            moving_thumbnail: {
              url: string,
              width: number,
              height: number
            },
            published: string,
            badges: string[],
            owner_badges: string[]
          }
        }
      ]
    }
  ]
}

Continuation:

const continuation = await mysubsfeed.getContinuation();
const trending = await youtube.getTrending();
Output

{
  now: {
    content: [
      {
        title: string,
        videos: object[]
      }
    ]
  },
  // Other categories require an additional call to fetch videos
  music: { getVideos: Promise.<Array.<object>> },
  gaming: { getVideos: Promise.<Array.<object>> },
  movies: { getVideos: Promise.<Array.<object>> }
}

Song lyrics:

const search = await youtube.search('Never give you up', { client: 'YTMUSIC' });
const lyrics = await youtube.getLyrics(search.results.songs[0].id); 

Notifications:

const notifications = await youtube.getNotifications();
Output

{
  items: [
    {
      title: string,
      sent_time: string,
      channel_name: string,
      channel_thumbnail: {
         url: string,
         width: number,
         height: number
      },
      video_thumbnail: { 
         url: string,
         width: number,
         height: number
      },
      video_url: string,
      read: boolean,
      notification_id: string
    }
  ]
}

Continuation:

const continuation = await notifications.getContinuation();

Unseen notifications count:

const unread_notis_count = await youtube.getUnseenNotificationsCount();

Get playlist:

Client: YOUTUBE | YTMUSIC

const playlist = await youtube.getPlaylist('PLAYLIST_ID', { client: 'YOUTUBE' });
YouTube Output

{
  title: string,
  description: string,
  total_items: string,
  last_updated: string,
  views: string,
  items: [
    {
      id: string,
      title: string,
      author: string,
      duration: {
        seconds: number,
        simple_text: string,
        accessibility_label: string
      },
      thumbnails: object[]
    }
  ]
}

YouTube Music Output

{
  title: string,
  description: string,
  total_items: number,
  duration: string,
  year: string,
  items: [
    {
      id: string,
      title: string,
      author: string,
      duration: {
        seconds: number,
        simple_text: string
      },
      thumbnails: object[]
    }
  ]
}

Interactions:


Don't forget that you must be signed in to use some of the following features!

  • Subscribe/Unsubscribe:

    await youtube.interact.subscribe('CHANNEL_ID');
    await youtube.interact.unsubscribe('CHANNEL_ID');
    
  • Like/Dislike:

    await youtube.interact.like('VIDEO_ID');
    await youtube.interact.dislike('VIDEO_ID');
    await youtube.interact.removeLike('VIDEO_ID');
    
  • Comment:

    await youtube.interact.comment('VIDEO_ID', 'Haha, nice video!');
    
  • Playlists:

    const videos = [
      'VIDEO_ID1',
      'VIDEO_ID2',
      'VIDEO_ID3'
      //...
    ];
    
    // Create and delete a playlist:
    await youtube.playlist.create('My Awesome Playlist', videos);
    await youtube.playlist.delete('PLAYLIST_ID');
    
    // Add and remove videos from a playlist:
    await youtube.playlist.addVideos('PLAYLIST_ID', videos);
    await youtube.playlist.removeVideos('PLAYLIST_ID', videos);
    
  • Translate (does not require sign in)

    await youtube.interact.translate('Hi mom!', 'ru');
    
  • Change notification preferences:

    Options: ALL | NONE | PERSONALIZED

    await youtube.interact.setNotificationPreferences('CHANNEL_ID', 'ALL'); 
    

Response schema:

{
  success: boolean, 
  status_code: number, 
  playlist_id?: string,
  translated_content?: string,
  data?: object
}

Account


  • Get account info:

    await youtube.account.getInfo();
    
    Output

    {
      name: string,
      email: string,
      channel_id: string,
      subscriber_count: string,
      photo: object[]
    }
    


  • Get basic channel analytics:

    await youtube.account.getAnalytics();
    
    Output

    {
      metrics: [
        {
          title: string,
          subtitle: string,
          metric_value: string,
          comparison_indicator: object,
          series_configuration: object
        }
      ],
      top_content: [
        {
          views: string,
          published: string,
          thumbnails: object[],
          duration: string,
          is_short: boolean
        }
      ]
    }
    


  • Get Time Watched stats:

    await session.account.getTimeWatched();
    
    Output

    [
      {
        title: string,
        time: string
      }
    ]
    


Channel:

  • Edit channel name:

    await youtube.account.channel.editName('My new awesome name');
    
  • Edit channel description:

    await youtube.account.channel.editDescription('An awesome description');
    

Notification settings:

Options: ON | OFF

  • Subscription notifications:

    await youtube.account.settings.notifications.setSubscriptions('ON'); 
    
  • Recommended content notifications:

    await youtube.account.settings.notifications.setRecommendedVideos('ON'); 
    
  • Channel activity notifications:

    await youtube.account.settings.notifications.setChannelActivity('ON'); 
    
  • Comment replies notifications:

    await youtube.account.settings.notifications.setCommentReplies('ON'); 
    
  • Channel mention notifications:

    await youtube.account.settings.notifications.setSharedContent('ON'); 
    

Privacy settings:

Options: ON | OFF

  • Subscriptions privacy:

    await youtube.account.settings.privacy.setSubscriptionsPrivate('ON'); 
    
  • Saved playlists privacy:

    await youtube.account.settings.privacy.setSavedPlaylistsPrivate('ON'); 
    

Live chats:


Currently, the library can retrieve the live chat, stats, and send messages.

Example:

import Innertube from 'youtubei.js';

const youtube = await new Innertube();

const search = await youtube.search('Lofi girl live');
const video = await youtube.getDetails(search.videos[0].id);
  
const livechat = video.getLivechat();

// Updated stats about the livestream
livechat.on('update-metadata', (data) => {
  console.info('Info:', data);
});
   
// Fired whenever there is a new message or other chat events
livechat.on('chat-update', (message) => {
  console.info(`- ${message.author.name}\n${message.text}\n\n`);
    
  if(message.text == '!info') {
    livechat.sendMessage('Hello! This message was sent from YouTube.js');
  }
});

Stop fetching the live chat:

livechat.stop();

Delete a message:

const msg = await livechat.sendMessage('Nice livestream!');
await msg.deleteMessage();

Downloading videos:


const options = {
  format?: string,
  quality?: string,
  type?: string,
  range?: { start: number, end: number }
};

const stream = youtube.download('VIDEO_ID', options);

Options:

  • format: mp4 | webm etc.. (note: only formats provided by YouTube are available)

  • quality: 144p, 240p, 360p, 480p, 720p, 1080p etc..

  • type: video | audio | videoandaudio

  • range: indicates which bytes should be downloaded

    • start: an integer indicating the beginning of the range
    • end: an integer indicating the end of the range

Cancel a download:

stream.cancel();

Example:

import fs from 'fs';
import Innertube from 'youtubei.js';

const youtube = await new Innertube();
const search = await youtube.search('Sound Coming From A Massive Black Hole - Anton Petrov');
  
const stream = youtube.download(search.videos[0].id, {
  format: 'mp4', // defaults to mp4
  quality: '720p', // falls back to 360p if a specific quality isn't available
  type: 'videoandaudio' 
});
  
stream.pipe(fs.createWriteStream(`./${search.videos[0].id}.mp4`));
 
stream.on('start', () => {
  console.info('[YOUTUBE.JS]', 'Starting now!');
});
  
stream.on('info', (info) => {
  console.info('[YOUTUBE.JS]', `Downloading ${info.video_details.title} by ${info.video_details.metadata.channel_name}`);
});
  
stream.on('progress', (info) => {
  process.stdout.clearLine();
  process.stdout.cursorTo(0);
  process.stdout.write(`[YOUTUBE.JS] Downloaded ${info.percentage}% (${info.downloaded_size}MB) of ${info.size}MB`);
});
  
stream.on('end', () => {
  process.stdout.clearLine();
  process.stdout.cursorTo(0);
  console.info('[YOUTUBE.JS]', 'Done!');
});
  
stream.on('error', (err) => console.error('[ERROR]', err)); 

Alternatively, you can get the deciphered streaming data and handle the download yourself:

await youtube.getStreamingData('VIDEO_ID', options);
Output

{
  selected_format: {
    itag: number,
    mimeType: string,
    bitrate: number,
    initRange: { start: string, end: string },
    indexRange: { start: string, end: string },
    lastModified: string,
    contentLength: string,
    quality: string,
    projectionType: string,
    averageBitrate: number,
    highReplication: boolean,
    audioQuality: string,
    approxDurationMs: string,
    audioSampleRate: string,
    audioChannels: number,
    loudnessDb: number,
    url: string,
    has_audio: boolean,
    has_video: boolean
  },
  formats: [
    {
      itag: number,
      mimeType: string,
      bitrate: number,
      initRange: { start: string, end: string },
      indexRange: { start: string, end: string },
      lastModified: string,
      contentLength: string,
      quality: string,
      projectionType: string,
      averageBitrate: number,
      highReplication: boolean,
      audioQuality: string,
      approxDurationMs: string,
      audioSampleRate: string,
      audioChannels: number,
      loudnessDb: number,
      url: string,
      has_audio: boolean,
      has_video: boolean
    }
  ]
}
  

Signing in:


When signing in to a Google account, you have two options:

  • OAuth 2.0; easy, simple & reliable.
  • Cookies; usually complicated to extract and unreliable.

OAuth:

import fs from 'fs';
import Innertube from 'youtubei.js';

const youtube = await new Innertube();

const creds_path = './yt_oauth_creds.json'; 
const creds = fs.existsSync(creds_path) ? JSON.parse(fs.readFileSync(creds_path).toString()) : {};

youtube.ev.on('auth', (data) => {
  switch (data.status) {
    case 'AUTHORIZATION_PENDING':
      console.info(`
        Hello! On your phone or computer,
        go to ${data.verification_url} and enter
        the code ${data.code}.
      `);
      break;
    case 'SUCCESS':
      fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
      console.info('Successfully signed in, enjoy!');
      break;
  }
});

youtube.ev.on('update-credentials', (data) => {
  fs.writeFileSync(creds_path, JSON.stringify(data.credentials));
  console.info('Credentials updated!', data);
});
  
await youtube.signIn(creds);
  
//...

Sign out:

const response = await youtube.signOut();
if (response.success) {
  console.log('You have successfully signed out');
}

Cookies:

import Innertube from 'youtubei.js';
const youtube = await new Innertube({ cookie: '...' }); 

Contributing

Contributions, issues and feature requests are welcome. Feel free to check issues page if you want to contribute.

Contributors

Contact

LuanRT - @lrt_nooneknows - luan.lrt4@gmail.com

Project Link: https://github.com/LuanRT/YouTube.js

Disclaimer

This project is not affiliated with, endorsed, or sponsored by YouTube or any of their affiliates or subsidiaries. All trademarks, logos and brand names are the property of their respective owners, and are used only to directly describe the services being provided, as such, any usage of trademarks to refer to such services is considered nominative use.

Should you have any questions or concerns please contact me directly via email.

License

Distributed under the MIT License.

(back to top)

Description
No description provided
Readme MIT 21 MiB
Languages
TypeScript 99%
JavaScript 1%