Help us understand the problem. What is going on with this article?

Raspberry PiでNode.jsを使ってYoutubeストリーミング

More than 3 years have passed since last update.

Raspberry Piを手に入れてまず試しにやってみるのは音楽をストリーミング再生させてみることではないでしょうか。(私だけ?)

コマンドラインを利用すれば簡単にストリーミングさせることができます。

コマンドラインからyoutubeを再生する - Qiita

こちらにあるように

ssh
$ youtube-dl 'https://www.youtube.com/watch?v=hoge' -o - | mplayer -

簡単にストリーミング再生出来ます。

Node.jsでコマンドライン操作

Node.jsのマニュアル & ドキュメンテーションを見ると

app.js
var spawn = require('child_process').spawn,
    ls    = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

ls.on('close', function (code) {
  console.log('child process exited with code ' + code);
});

こんな風に書いてあります。通常のコマンドはこれでいけるのですが、二つ以上のコマンドを組み合わせようとすると(パイプ)上手くいきません。

node.jsでパイプを使ったコマンドを実行するには

node.jsでパイプを使ったコマンドを実行するには - GitHubGist

こちらのGistを拝見したところ、sh -c コマンドをchild_processで実行して、実際に行いたいコマンドは引数に格納して実行すれば良い様です。

分かった様な、分からない様な。(分ってない)試しにapp.jsをこんな風に書いてみました。

app.js
var spawn = require('child_process').spawn;

var cmd = "youtube-dl 'https://www.youtube.com/watch?v=wArJc179HNk' -o - | mplayer - -novideo";

function shspawn(command) {
  return spawn('sh', ['-c', command]);
}

var child = shspawn(cmd);
var buf="";

child.stdout.on('data',function(data){
  buf=buf+data;
});
child.stderr.on('data',function (data){
  console.log('exec error: '+data);
});

child.on('close',function(code) {
  console.log("exit.");
});

1時間ほどのプレイリスト - Indie/Indie-Pop Compilation - May 2015 (55-Minute Playlist) ですが上手くストリーミング再生出来ました。

expressを使ってブラウザで操作する

expressを利用して幾つかのプレイリストを操作できる様にしてみました。

PonTube Pi 2016-03-02 23-29-46.png

app.js
var express = require('express');
var ejs = require("ejs");
var spawn = require('child_process').spawn;

var app = express();
app.engine('ejs',ejs.renderFile);

app.get('/', function(req, res){
    res.render('pontube.ejs',
        {title: 'PonTube Pi'});
});

app.get('/control', function (req, res) {
  console.log(req.query);
  var id = req.query.id;
  var watch =  "https://www.youtube.com/watch?v=" + id;

  var clear_cmd = "killall mplayer";
  //console.log("cmd = "+cmd);

  function shspawn(command) {
    return spawn('sh', ['-c', command]);
  }

  var child = shspawn(clear_cmd);
  var buf="";

  child.stdout.on('data',function(data){
    buf=buf+data;
  });
  child.stderr.on('data',function (data){
    //console.log('exec error: '+data);
  });

  child.on('close',function(code) {
     console.log( 'CLEAR. PLAY.' );
  });

  var play_cmd = "youtube-dl " + watch + " -o - | mplayer - -novideo";

  function shspawn(command) {
    return spawn('sh', ['-c', command]);
  }

  var child = shspawn(play_cmd);
  var buf="";

  child.stdout.on('data',function(data){
    buf=buf+data;
  });
  child.stderr.on('data',function (data){
    //console.log('exec error: '+data);
  });

  child.on('close',function(code) {
    console.log( 'EXIT.' );
  });


});

  app.get('/stop', function (req, res) {
    console.log(req.query);
    var id = req.query.id;

    var stop_cmd = "killall mplayer";
    //console.log("cmd = "+cmd);

    function shspawn(command) {
      return spawn('sh', ['-c', command]);
    }

    var child = shspawn(stop_cmd);
    var buf="";

    child.stdout.on('data',function(data){
      buf=buf+data;
    });
    child.stderr.on('data',function (data){
      //console.log('exec error: '+data);
    });

    child.on('close',function(code) {
    console.log( 'STOP.' );
    });

    });

var server = app.listen(3000, function () {
  var host = server.address().address
  var port = server.address().port
  console.log('This app listening at http://192.168.0.12:'+ port)
});

views/pontube.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta http-equiv="content-type"
        content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
    <title><%=title %></title>
     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <style>
    body{
      margin: 0 auto;
      max-width: 640px;
    }
     article{
       margin: 20px;
     }
    </style>
    </head>
<body>
    <header>
        <h1 class="text-center h2"><%=title %></h1>
    </header>
    <article>
    <div class="list-group">
      <button type="button" class="list-group-item" id="wArJc179HNk">Indie/Indie-Pop Compilation - May 2015 (55-Minute Playlist)</button>
      <button type="button" class="list-group-item" id="YYYJDalKI9E">Indie/Pop/Folk Compilation - January 2016 (1-Hour Playlist)</button>
      <button type="button" class="list-group-item" id="wiKgHFU8L9g">1.5 Hours of Rock n' Roll - Rock/Hard Rock Playlist</button>
      <button type="button" class="list-group-item" id="WVP3fUzQHcg">2 HOUR LONG Piano Music for Studying, Concentrating, and Focusing Playlist</button>
      <button type="button" class="list-group-item" id="ubZASS3uPrs">Relaxing Love Songs 80's & 90's Playlist</button>
    </div>
      <button class="btn btn-default" id="stop">stop</button>
    </article>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

    <script>
        $(".list-group-item").click(function() {
                var station = $(this).attr("id"); 
                $.get("http://192.168.0.12:3000/control", { id: station } );
            });
        $(".btn").click(function() {
                var stop = $(this).attr("id"); 
                $.get("http://192.168.0.12:3000/stop", { id: stop } );
            });

    </script>

</body>
</html>

コマンドライン操作も良いですが、手元のブラウザから操作出来ると愛着が湧いてきます。

DSC_0001.jpg

音声コマンドと組み合わせても面白いかもしれません。では。

PonDad
人工知能愛好家(Artificial Intelligence Hobbyist)
https://pondad.net
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away