Skip to content

Instantly share code, notes, and snippets.

@pnhoang
Created October 10, 2019 22:58
Show Gist options
  • Save pnhoang/06cfe4c59fa653dd3344c2b7e576a109 to your computer and use it in GitHub Desktop.
Save pnhoang/06cfe4c59fa653dd3344c2b7e576a109 to your computer and use it in GitHub Desktop.
How to build a CLI in Node.js using Oclif Framework

Single command vs Multiple command CLI

curl -h
curl http://google.com

heroku -h
heroku logs -a app-name
heroku 

Bootstrap a new CLI project with Oclif

$ node -v
$ npx -v

$ npx oclif single dl-youtube-videos

Go through the wizard in the terminal Choose Typescript

Open project

$ cd dl-youtube-videos
$ code .

Designing our CLI

$ dl-youtube-videos -h
$ dl-youtube-videos {csv_file}

Command arguments: https://oclif.io/docs/args

// add new .prettierrc and add {} to enable reformatting code on save.
// show the Prettier extension?


static args = [
    {
      name: "file",
      description:
        "The output CSV file downloaded from running web scraper chrome extension. Check this tutorial: {YouTubeLink}",
      required: true
    }
  ];


import { startDownloading } from "./main";


async run() {
    const { args } = this.parse(DownloadYoutubeVideos);

    if (args.file) {
      this.log(`Download Youtube Videos listed in this CSV file: ${args.file}`);
      startDownloading(args.file);
    }
  }


// main.ts

$ yarn add csv-parser fs


import * as csv from "csv-parser";
import * as fs from "fs";

interface IPlaylist {
  title: string;
  href: string;
}

const playlist: IPlaylist[] = [];

export const startDownloading = async filePath => {
  fs.createReadStream(filePath)
    .pipe(csv())
    .on("data", ({ link: title, "link-href": href }) =>
      playlist.push({
        title,
        href
      })
    )
    .on("end", () => {
      console.log(playlist)
    });
};


$ yarn add youtube-dl

import * as youtubedl from "youtube-dl";



const downloadVideo = href => {
  return new Promise((resolve, reject) => {
    const video = youtubedl(
      href,
      // Optional arguments passed to youtube-dl.
      [],
      // Additional options can be given for calling `child_process.execFile()`.
      { cwd: __dirname }
    );

    // Will be called when the download starts.
    video.on("info", function(info) {
      console.log("Download started");
      console.log("filename: " + info._filename);
      console.log("size: " + info.size);

      video.pipe(fs.createWriteStream(info._filename));
    });

    video.on("end", function() {
      resolve("finished downloading");
    });

    video.on("error", function error(err) {
      reject(`error downloading file ${href}`);
    });
  });
};


import * as Listr from "listr";


const createTasksList = async playlist => {
  const tasks = new Listr(
    playlist.map(item => ({
      title: item.title,
      task: () => downloadVideo(item.href)
    }))
  );

  await tasks.run();
};



.on("end", () => {
      createTasksList(playlist);
    });

Check out the full source code at: https://github.com/pnhoang/dl-youtube-videos

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment