LoginSignup
19
18

More than 5 years have passed since last update.

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

Posted at

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

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

19
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
18