2018年12月

  • node.jsでzip圧縮

    https://logzitsu.tlog.work/puppeteer-%E3%81%A7%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89/

    上記記事を書いた時に気になったので調べてみました。

    archiver ってモジュールがあるみたいです。

    詳しくはこちらの記事を参照

    Scratchpad@usagi1975.com 
    https://www.usagi1975.com/10dec150717/

  • puppeteer でフォームからファイルのアップロード

    フォームからアップロード処理できたら便利だなー
    っと思ってたらちゃんとありました!

    以下は宅ふぁいる便に「image.zip」をアップロードするプログラムです。
    メール送信のところを使うと、「ファイルをアップロードしてメルアドに送信」というところまで自動化できちゃいます。
    consoleにアップロード先が表示されます。

    注意 : このコード宅ふぁいる便に迷惑かけないように配慮して使ってください

    const puppeteer = require('puppeteer');
    
    (async () => {
        const browser = await puppeteer.launch({ 
            args: ['--no-sandbox'],
            headless:false
         });
        const page = await browser.newPage();
        await page.goto('https://free.filesend.to/fileup_free2');
    
        const filePath = "image.zip";
        const input = await page.$('input[name="files[]"]');
        await input.uploadFile(filePath);
    
        //同意にチェック
        //利用規約ちゃんと読んでね。
        await page.click('#safefilechk');
    
        //送信ボタン(確認画面へ)
        await page.click('#uploadbtn > p.btn_fileup > a');
    
        await page.waitFor(2000);
        //確認送信ボタン
        await page.click('#uploadbtn > p > a');
    
        await page.waitFor(2000);
        var uploadinput  = await page.$("#input_dl_url");
        uploadurl = await page.evaluate(content => content.value, uploadinput);
        console.log(uploadurl);
        
        await browser.close();
    })();

    「xxx.uploadFile」の部分です
    最強かな?

  • 続:ラズパイで動画作成。node.jsで楽する

    https://logzitsu.tlog.work/%E5%BE%B9%E5%BA%95%E6%A4%9C%E8%A8%BC-%E3%83%A9%E3%82%BA%E3%83%91%E3%82%A4%E3%81%A7%E5%8B%95%E7%94%BB%E3%81%AF%E4%BD%9C%E3%82%8C%E3%82%8B%E3%81%8B%EF%BC%9F/

    前回の記事はこちら。

    シェルコマンドばっかなんですけど、できればnode.jsでまとめたいなぁ、という要望が出てきたのでまとめてみたいと思います。

    この記事って、誰かの役に立つことあるんだろうか。
    でも、記事にしないと勉強のモチベーションが保てないし、忘れやすいんですよね。

    ということで、これをみている奇特なかた、お付き合いください。

    Nodeからffmpegを使ってみる。

    http://tkybpp.hatenablog.com/entry/2016/04/25/163246

    この辺から使っていけばいいと思うのですが、最初に仕組み等もろもろを整頓しておこうと思います。

    事前に用意しておくもの

    • スライド画像数枚
    • それぞれに対応したテキスト

    意外と用意しておくものは少なくてすみますね。

    1. テキストから音声ファイルを作成する(複数)
    2. スライド画像を指定秒数の動画にする(複数)
    3. 動画と音声をくっつける(複数)
    4. 全部の動画をくっつける

    このような流れになります。
    テキストと音声ファイルをどう整理して関連付けていくかが問題ですね。

    ファイル名を決める

    とりあえず結合ファイル名と、結合前のリストのルールなど決めます。

    読み上げテキストファイル名
    voice_01.txt , voice_02.txt・・・
    音声ファイル名
    voice_01.wav , voice_02.wav・・・
    画像名
    frame_01.png , frame_02.png・・・・
    動画名(音無し)
    frame_01.mp4 , frame_01.mp4・・・
    動画名(音有り)
    video_01.mp4 , video_01.mp4・・・

    mylist.txt

    file video_01.mp4
    file video_02.mp4
    file video_03.mp4
    file video_04.mp4

    最終的なファイル名は
    output.mp4
    にしたいと思います。

    こうなりました

    voice.js

    var OpenJTalk = require('openjtalk');
    var fs = require('fs');
    var mei = new OpenJTalk();
    var list = [
        "voice_01",
        "voice_02",
        "voice_03"
    ]
    var i = 0;
    
    setInterval(loop,10000);
    
    function loop(){
        if(i < list.length){
        var text = fs.readFileSync("./" + list[i] + ".txt","utf-8");
        mei.talk(text,200);
        //process.exit(1);
        readdir("./",list[i]);
        i++
        }else{
            process.exit(1);
        }
      }
    
    function readdir(dir,file) {
        setTimeout(function(){
        fs.readdir(dir, function (err, files) {
          if (err) {
              throw err;
          }
          for(let i = 0; i < files.length; i++) {
            var result = files[i].match( /.wav$/ );
            if(result != null ){
                console.log(result.input);
                fs.copyFile(result.input, './tmp/' + file + '.wav', (err) => {
                    if (err) {
                        console.log(err.stack);
                    }
                    else {
                        console.log('Done.');
                    }
                });
            }
          }
        });
    },700);
      }

    makevideo.js

    const exec = require('child_process').exec;
    const fs = require('fs');
    var f_voice_txt = [
        'voice_01.txt',
        'voice_02.txt',
        'voice_03.txt'
    ];
    var time = "00:00:10";
    var f_voice_wav = [
        './tmp/voice_01.wav',
        './tmp/voice_02.wav',
        './tmp/voice_03.wav'
    ];
    var f_png = [
        'frame_01.png',
        'frame_02.png',
        'frame_03.png'
    ];
    var f_movie = [
        './tmp/frame_01.mp4',
        './tmp/frame_02.mp4',
        './tmp/frame_03.mp4'
    ];
    var f_movie_v = [
        'video_01.mp4',
        'video_02.mp4',
        'video_03.mp4'
    ];
    var output = "output.mp4";
    
    require('date-utils');
    var dt = new Date();
    var dtformatted = dt.toFormat("YYYYMMDDHH24MISS");
    
    //画像を動画にする
    for (let i = 0; i < f_png.length; i++) {
        exec('ffmpeg -f image2 -r 1 -loop 1 -t ' + time + ' -i ' + f_png[i] + ' ' + f_movie[i], (err, stdout, stderr) => {
        if (err) { console.log(err); }
        console.log(stdout);
        });
    };
    
    //動画と音をくっつける
    for (let i = 0; i < f_movie.length; i++) {
        exec('ffmpeg -i ' + f_movie[i] + ' -i ' + f_voice_wav[i] + '  -c:a aac -map 0:v:0 -map 1:a:0 ' + f_movie_v[i], (err, stdout, stderr) => {
        if (err) { console.log(err); }
        console.log(stdout);
        });
    };
    
    //動画の結合
    for (let i = 0; i < f_movie.length; i++) {
        f_movie_v[i] = "file " + f_movie_v[i];
    };
    var f_movie_v_j = f_movie_v.join("\n");
    fs.writeFileSync("./tmp/movie_list.txt", f_movie_v_j);
    console.log(f_movie_v_j);
    
    //動画の結合
    fs.mkdir(dtformatted, function (err) {});
    exec('ffmpeg -f concat -i ./tmp/movie_list.txt -c:v copy ' + "./" + dtformatted + "/" + output, (err, stdout, stderr) => {
    //process.exit(1);
    if (err) { console.log(err); }
    console.log(stdout);
    });

    音声は10秒ごとに発音、発音してる間にwavファイルをコピーするやり方にしました。
    あんまり美しくないけど、とりあえず動くからいいや。
    動画作成の方はシェルコマンドをファイル分やるように加工。
    ニッチすぎるけど公開しておきます。

    videoを作る方は、一発でちゃんと動いてるかよくわからない。
    もしかしたらちゃんとタイミングを設定してあげないとダメかも。

  • 4kのねこです

    ねこです

    よろしくおねがいします

    ねこはいます

    SCP-040-JP

    https://logzitsu.tlog.work/%E5%BE%B9%E5%BA%95%E6%A4%9C%E8%A8%BC-%E3%83%A9%E3%82%BA%E3%83%91%E3%82%A4%E3%81%A7%E5%8B%95%E7%94%BB%E3%81%AF%E4%BD%9C%E3%82%8C%E3%82%8B%E3%81%8B%EF%BC%9F/

    これで猫の動画を作って思い出したんで作ってみました。
    こえーんだけどって思った人は正解です。

    元ネタを調べてください。(収容違反)

    4kです

  • 徹底検証 : ラズパイで動画は作れるか?

    ラズパイで動画を作りたいことってありますよね?

    ないと思うけど、そんなこと思うことありますよね?

    ・・・ふと、思いついたことがあったので
    どれぐらいのことができるのか検証してみようと思いました。

    どうやって作る?

    GUIで作るのはかなりきついとおもいますので、シェルコマンドによって動画作成してみたいと思います。

    作る動画は、紙芝居的なものにナレーションをつけてみたいと思います。
    ナレーションは読み上げで作成を試します。
    スライドの画像素材は普通のPCで作って用意します。

    • 素材を結合しgifアニメーションの作成
    • 読み上げ音声データの作成
    • 音声とgifアニメを結合し動画データに

    こんなのを想定していますが、できるかなー?

    まずはMacで試す

    ラズパイでいきなり試すと辛いので、まず普通に試します。

    Gifアニメを作る

    movieというフォルダに次の画像を入れます。

    こんな感じの猫の素材を、frame_01.jpg・・・と連番でフォルダに入れます。

    imagemagickをインストールします。

    $ brew install imagemagick

    これで convert コマンドが使えるようになりました。

    $ convert -delay 500 0 frame_*.jpg movie.gif

    5秒切り替えのスライドショーができるはず。

    読み上げ音声を作ってみる

    どうやら、openjtalkという良いものがあるらしいので使わせてもらう。
    nodeで使いやすくしてある、node-openjtalkというものがあるのでこれを使う

    インストール

    $ npm install openjtalk

    読み上げ用のものらしく、音声ファイルがつど削除される仕様になっていた。
    それは困る。処理を途中で止めると音声ファイルは残るみたいなので使わせてもらう。

    適当な読み上げ用の テキストをファイルも作っておいてください。(text.txt)

    voice.js

    var OpenJTalk = require('openjtalk');
    var fs = require('fs');
    var mei = new OpenJTalk();
    var text = fs.readFileSync("./text.txt","utf-8");
    mei.talk(text,200);
    process.exit(1);

    file.js
    wavを探して、名前をvoice.wavに変えます。
    必要な複数の音声ファイルがあるところで実行すると危険です。消えます。
    法則性がないファイル名だったのでこんなことをしてます。
    いろいろ自動化したいという思いでこのプログラム作りましたが、普通は手動でリネームしてください。

    var fs = require('fs');
    
    readdir("./");
    function readdir(dir) {
        fs.readdir(dir, function (err, files) {
          if (err) {
              throw err;
          }
          console.log(files);
          for(let i = 0; i < files.length; i++) {
            var result = files[i].match( /.wav$/ );
            if(result != null ){
                console.log(result.input);
                fs.rename(result.input, 'voice.wav', function (err) {
                });
            }
          }
        });
      }
    

    音声と、gifアニメができました。

    結合して動画を作成する

    まずffmpegをインストールします。

    $ brew install ffmpeg

    結合するコマンド

    ffmpeg -i movie.gif -i voice.wav  -c:a aac -map 0:v:0 -map 1:a:0 output.mp4

    -i がソースとなるファイル名で、最後が出力されるファイル名。
    とりあえずできたっぽいです。
    quicktimeでは再生できませんでしたが、ブラウザでは再生されます。

    youtubeにアップしました。
    音声とgifアニメの長さが異なるのでシークバーがバグっとる
    ※サイコなので閲覧注意

    https://www.youtube.com/watch?v=hOTZsOYZcuM

    といった感じでとりあえず動画ができました(?)
    実際に使うには音声の秒数調節とかが難しそうだなぁ。

    ラズパイで試す

    ラズパイにubuntuが乗ってるのでapt-getとかでいろいろ入れてく。

    imagemagick

    $ sudo apt-get -y install imagemagick

    ffmpeg

    $ sudo add-apt-repository ppa:jonathonf/ffmpeg-4
    $ sudo apt update
    $ sudo apt-get install ffmpeg
    
    //入ったかチェック
    $ ffmpeg -version

    node-openjtalk

    $ npm install openjtalk

    pngのフレーム画像を入れて、gif作成

    $ convert -delay 500 frame_*.png movie.gif

    音声ファイル作成

    $ node voice.js
    $ node file.js

    合成

    ffmpeg -i movie.gif -i voice.wav  -c:a aac -map 0:v:0 -map 1:a:0 output.mp4

    こんな感じです。超稚拙ですが、処理は全然重くないし、工夫を加えていけば紙芝居的な画像は作れちゃいそうです。

    改善版!を考えてみる

    どうやらffmpegで動画が結合できるみたいので、紙芝居の結合はそっちに任せたいと思います。
    いめーじまじっく不要。画像をいきなり動画に変えます。
    そうなると

    • 画像を動画に変換(表示秒数の調整)
    • 音声をその画像に載せる(mp4化)
    • それを紙芝居分作成する。
    • 最後に結合する

    こんな流れですね。まずはとりあえずやってみよ。

    画像を動画にする

    $ ffmpeg -f image2 -r 1 -loop 1 -t 00:00:10 -i frame_01.jpg video01.mp4

    動画にした画像に音声を合成

    $ ffmpeg -i video01.mp4 -i voice.wav  -c:a aac -map 0:v:0 -map 1:a:0 v_01.mp4

    これをフレーム分、作ります。

    動画の後に動画をつなげる・・・な感じで結合する

    まず、結合する動画のファイル一覧をテキストファイルにします。

    maylist.txt

    file v_01.mp4
    file v_02.mp4
    file v_03.mp4

    結合コマンド

    ffmpeg -f concat -i mylist.txt  -c:v copy output.mp4

    できた動画はブラウザだと音声が繋がっておかしかったのですが、youtubeにアップしたら想定した感じになりました。

    じつは1080pです。
    macで作りましたが、全然処理が重たいとかないのでラズパイでも問題ないでしょう。

    ちょっと声が低い方が好みだなぁと思うのであとで調整します。
    あ、個人的な話でした。
    ちょっと記事が長くなってだるくなってきたのでわけます。

    この記事の結論

    全然できる!!!!!!11111

    https://logzitsu.tlog.work/%E7%B6%9A%EF%BC%9A%E3%83%A9%E3%82%BA%E3%83%91%E3%82%A4%E3%81%A7%E5%8B%95%E7%94%BB%E4%BD%9C%E6%88%90%E3%80%82node-js%E3%81%A7%E6%A5%BD%E3%81%99%E3%82%8B/
  • スマブラSPが待ちきれなかったのでamiiboを先に購入したよ

    きっかけはサイバーマンデー

    アマゾンのサイバーマンデーのニュースが耳に入ったので、ざっと確認していると…なんとスマブラのamiibo 63種セットが…!

    a
    https://amzn.to/2R48O7v

    こんなにいらねぇ…

    しかも、ファルコとかいねぇ。
    というわけで、欲しいキャラのみ購入することにしました。

    まずは前作で使い倒したソニック

    amiibo ソニック (大乱闘スマッシュブラザーズシリーズ)

    ソニックとの出会いは小さい頃に友達のお兄ちゃんのネオジオポケット。同じ横スクロールゲーのスーパーマリオブラザーズよりなんかすごくイケテルッ!!って思って虜になりました。

    それから月日は流れ、スマブラに参戦。もうソニックばっかり使ってました。

    かわいいからピクミン&オリマー

    amiibo ピクミン&オリマー (大乱闘スマッシュブラザーズシリーズ)M

    前作では2番手くらいで使ってました。私は人にやらせるほうが好きな性格なので、横Bのとりつかせるやつが地味に好きで。あとスマッシュのリーチの長さも魅力的。

    でも買った理由はかわいいから。

    ※ここに撮影画像各角度

    あとは持ってるSplatoon系amiiboで

    なんとSPはインクリングも参戦。Splatoon2用に買った子たちも使えるそうなので、一旦購入は上記2体にして、お気に入りのキャラが出てきたら買おうと思います。ポケモントレーナーとか買いそう。

    皆さんはどのキャラを使いますか??

  • fsでファイルを作成する

    単純ですがよく使うのでメモ。
    全機能の説明とかではなく、よく使うものをメモっています。

    ファイルを作成する場合

    const fs = require('fs');
    
    //ファイル名
    const filePath = "text.txt"
    const contents = "このテキストが保存したいでーす"
    
    fs.writeFileSync(filePath, contents);

    以上です。説明がいらんくらいシンプルですね。

    読み込む場合

    var fs = require('fs');
    
    var text = fs.readFileSync("./text.txt","utf-8");
    console.log(text);

    かんたんだぁー

    コピーしたい

    fs.copyFile("moto.txt", './saki/moto.txt');

    移動したい(リネーム)

    fs.renameSync("moto.txt", './saki/moto.txt');

    ディレクトリ作りたい

    fs.mkdir("tmp", function (err) {});
  • puppeteer すぐ使える取得例

    いろんな記事見ても、うーむわからん!取得できん!
    となって、うまく動かないことが多かったので、試行錯誤の末とりあえず動いた例をここにおいておきます。

    リストが並んでてそこから情報を取り出したい
    ということが多いと思いますが、そういったときにだいたい使えると思います。

    はてなブックマークの人気エントリーのリストから取り出す

    そんな例です。ブックマーク数、タイトル、URLを抜き出して配列にしています。
    コンソールに、結合してから出力しています。

    
    const puppeteer = require('puppeteer');
    
    (async () => {
        const browser = await puppeteer.launch({ 
            //executablePath: '/usr/bin/chromium-browser',
            args: ['--no-sandbox'],
            //見えるようにfalseにしたけど本番はtrueにしてね
            headless:false
        });
    
        //配列を作る
        var text = [];
        
        const page = await browser.newPage();
        await page.goto('http://b.hatena.ne.jp/hotentry/all',{ waitUntil: 'domcontentloaded' });
        //await page.waitFor(2000);
    
        //情報を取り出したい同列に並んでいる要素(liとかが多い)
        const items = await page.$$('#container > div.wrapper > div > div.entrylist-main > section > ul > li > div > div.entrylist-contents-main');
    
        //取り出したい内容のselectorをなんとかして選ぶ
        const inner1 = await page.$$('#container > div.wrapper > div > div.entrylist-main > section > ul > li > div > div.entrylist-contents-main > h3 > a');
        const inner2 = await page.$$('#container > div.wrapper > div > div.entrylist-main > section > ul > li > div > div.entrylist-contents-main > span > a');
        
        //要素分ループを回す
        for (let i = 0; i < items.length; i++) {
            //取得
            const text1 = await page.evaluate(content => content.innerText, inner1[i]);
            const text2 = await page.evaluate(content => content.innerText, inner2[i]);
            const text3 = await page.evaluate(content => content.href, inner2[i]);
            //配列に追加していく
            text.push( text1 + " 【" + text2 + "】\n" + text3 + "\n"); 
        }
    
        //配列を全て結合
        text = text.join("");
    
        //結合した配列を出力
        console.log(text);
    
        await browser.close();
    })();

    itemsが並んでる要素。
    innerが取り出したい中身です。上記の図でわかるでしょうか。

    こういうパターンもありますね。赤が「items」青が「inner」になります。

    セレクターをうまく取り出す方法

    セレクターがうまく指定できてないことが動かない原因であることが多いです。
    デベロッパーツールでセレクターを取得する方法です。

    chromeのデベロッパーツールを開き、その要素の上で右クリック。
    Copy > Copy selector と選ぶとその場所のセレクターがコピーできます。
    どこかにペーストしてください。

    #container > div.wrapper > div > div.entrylist-main > section > ul > li:nth-child(1) > div > div.entrylist-contents-main > span > a

    こんな感じになります。「何個め」を表す:nth-child はループさせるときに邪魔になるので

    #container > div.wrapper > div > div.entrylist-main > section > ul > li > div > div.entrylist-contents-main > span > a

    このように少し削除して上記のサンプルでは利用しています。

    まとめ

    スクレイピングは元サイトに迷惑をかけないようマナーを守ってやりましょう!
    訴えられた例もあるので注意してね。 https://ja.wikipedia.org/wiki/%E5%B2%A1%E5%B4%8E%E5%B8%82%E7%AB%8B%E4%B8%AD%E5%A4%AE%E5%9B%B3%E6%9B%B8%E9%A4%A8%E4%BA%8B%E4%BB%B6