4zakiの日記

技術ブログです。

アフィリエイト広告を利用しています

node Puppeteerを使ってYoutubeの他人が作成した再生リストをコピー

Youtubeで他人が作成した再生リストを保存する機能があるが、
実際のところ保存というよりはお気に入りやブックマークに近い。

動画を追加したり消したりすることができない。

また再生リストを複製する機能も用意されていないので、自分で一つ一つ再生リストに保存していく方法しかない(多分ない)と思います。
f:id:H4zaki:20200323141828p:plain
なのでPuppeteerで再生リストを複製するスクリプトを書いてみました。

作業環境

  • windows10
  • Git-bash
  • node js:v13.7.0
  • Puppeteer:2.1.0
  • Chromium:80.0.3987.0(Developer Build) (64 ビット)

コード

const fs = require('fs');
const puppeteer = require('puppeteer');
const COOKIES_PATH = 'youtubecookie';

(async () => {

    const browser = await puppeteer.launch({
        headless: false
    })

    const page = await browser.newPage();

    //cookieが作られていない場合ログイン画面からログインし、cookieを保存
    if (fs.existsSync(COOKIES_PATH) == false) {
        await page.goto('https://accounts.google.com/signin/v2/identifier?service=youtube&uilel=3&passive=true&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26app%3Ddesktop%26hl%3Dja%26next%3D%252F%253Fgl%253DJP%2526hl%253Dja&hl=ja&ec=65620&flowName=GlifWebSignIn&flowEntry=ServiceLogin', {
            waitUntil: 'domcontentloaded'
        });
        await page.type('input[name="identifier"]', "ログインメールアドレス");
        await page.click('#identifierNext');
        await page.waitForNavigation({
            timeout: 60000,
            waitUntil: 'domcontentloaded'
        });
        await page.waitFor(1000);
        await page.type('input[name="password"]', "ログインパスワード");
        await page.click('#passwordNext');
        await page.waitForNavigation({
            timeout: 60000,
            waitUntil: 'domcontentloaded'
        });
        const afterCookies = await page.cookies();
        fs.writeFileSync(COOKIES_PATH, JSON.stringify(afterCookies));
    } else {  
        //cookieでログイン
        const cookies = JSON.parse(fs.readFileSync(COOKIES_PATH, 'utf-8'));
        for (let cookie of cookies) {
            await page.setCookie(cookie);
        }

        //コピー元プレイリスト(URL)
        await page.goto('https://www.youtube.com/playlist?list=PL590L5WQmH8dc-kEkuMX3n_EhpMvZxowe', {
            waitUntil: 'domcontentloaded'
        });

        //動画の数を数える
        let listSelector = "#contents > ytd-playlist-video-renderer";
        var list = await page.$$(listSelector);

        //保存処理
        for (let i = 1; i <= list.length; i++) {
            console.log(i + '/' + list.length);
            await page.click('.style-scope:nth-child(' + i + ') > #content > #menu > .style-scope > #button');
            await page.click('.style-scope:nth-child(1) > #items > .style-scope:nth-child(3)');
            await page.waitFor(4000);
            //コピー先(上から何番目か 後で見るもこみ nth-child(2)の場合2番目、nth-child(3)の場合3番目)
            await page.click('#playlists > .style-scope:nth-child(2)');
            //await page.waitFor(3000);
            await page.click('#close-button > button');
            await page.waitFor(1000);
        }
    }
    console.log('finish');
    await browser.close();
})();

コピー先の上から何番目かっていうのは、動画を再生リストに追加する操作をしたときに
f:id:H4zaki:20200323141934p:plain
このように表示表示されると思いますが、ここの上から数えて何番目かです。

あと新しくテスト用で作成したGoogleアカウントだとログインができなかったが、普段使いのアカウントならログインできました。

改善点とか妥協点とか

  • 少し読み込みが長いところとかをwaitForでごまかしてる。(1動画当たり5秒ほどかかる)
  • headlessをtrueにするとエラーを吐いたのでfarseにした。(Chromiumがウィンドウで起動する。メモリは230M前後使用していた。)
  • Youtubeのhtmlタグが特殊でよくわからなかったんで、チェックボックスなどの操作をクリックで操作している。
  • ↑のせいでコピー先とコピー元に同じ動画があると消えてしまう。

多分改良も改善もしないと思いますが、このコードをパクるならこのように不完全なものなので注意してください。

実際に動かしてみた動画

おわり