electronでyoutubeの音楽動画を流しっぱなしにするものを作ってみました

[Musictonic](http://musictonic.com/)のように、ミュージシャンの名前で検索したら、それに関連したyoutube動画を延々と再生してくれるものがデスクトップアプリケーションであったらいいなと思って、electronの勉強も兼ねて作ってみました。

[https://github.com/makotot/tubehead](https://github.com/makotot/tubehead)が今回作ってみたものです。
![tubehead-screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/11465/14d1ceaf-ec13-9fb6-e80f-218a0c144f10.png)
検索ボックスから適当に検索すると、検索ワードに関連する動画の再生リストが出来て、それをループで再生するだけのアプリです。
あとは、10件まで検索履歴を保持するくらいしかやってません。

electronのバージョンは、0.36.0です。

## 利用しているライブラリ

- [React](https://facebook.github.io/react/)
        - Viewを富豪的に扱いたかったので、利用しました。
- [Flux](https://github.com/facebook/flux)
        - Fluxフレームワークが色々出てるみたいですが、今回はそこまでは追いつかなかったので、Facebookのfluxを使いました。
- [Lodash](https://lodash.com/)
        - 部分的に配列操作などで利用してます。
- [express](http://expressjs.com/en/)
        - electronのウィンドウで利用する静的ファイルをexpressで配信するようにしてます。
- [react-fontawesome](https://github.com/danawoodman/react-fontawesome)
        - アイコンで利用してます。


## メインプロセス

### アプリケーションサーバ

メインプロセス側で何かをやるということもないので、特にどうということも無いですが、
よく目にするメインプロセスのコードと若干違うのは、

```js
appIcon.window.loadURL('http://127.0.0.1:'+ port);
```
となってる部分で、expressでアプリケーションサーバを立ち上げて、そのURLを`loadURL`に渡しています。
[Quick Start](http://electron.atom.io/docs/latest/tutorial/quick-start/)のように

```js
mainWindow.loadURL('file://' + __dirname + '/index.html');
```
という感じだと、YouTubeのAPIで制限されているのか、ほとんどの動画が正常に読み込むことが出来ないようで、以下のようにexpressを使ってます。

```js
var express = require('express');
var webApp = express();
var port = 3838;

webApp.get('/', function (req, res) {
  res.sendFile(__dirname + '/dist/index.html');
});
webApp.use('/css', express.static(__dirname + '/dist/css'));
webApp.use('/js', express.static(__dirname + '/dist/js'));

var server = webApp.listen(port, function () {
  console.log('app listening at http://%s:%s', server.address().host, server.address().port);
});
```

### ショートカット

実際にパッケージを作ったあとに分かったんですが、検索ボックスで`Command+X`とか`Command+A`とかショートカットが全然使えなくて、開発中は全然問題なく使えてたのに何でだろうって思ったら、以下のようにアプリケーション側でキーバインドを設定しなければいけないということでした。

```js
  var template = [
    {
      label: 'Application',
      submenu: [
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: function () {
            app.quit();
          }
        }
      ]
    },
    {
      label: 'Edit',
      submenu: [
        {
          label: 'Copy',
          accelerator: 'CmdOrCtrl+C',
          selector: 'copy:'
        },
        {
          label: 'Cut',
          accelerator: 'CmdOrCtrl+X',
          selector: 'cut:'
        },
        {
          label: 'Paste',
          accelerator: 'CmdOrCtrl+V',
          selector: 'paste:'
        },
        {
          label: 'Select All',
          accelerator: 'CmdOrCtrl+A',
          selector: 'selectAll:'
        }
      ]
    }
  ]

  var menu = Menu.setApplicationMenu(Menu.buildFromTemplate(template))
```
[Electron - Enable copy and paste](http://www.pracucci.com/atom-electron-enable-copy-and-paste.html)


## レンダラプロセス

[Babel](https://babeljs.io/)、React、Fluxなどは殆ど触ったことがない状態で、折角だから使いたいと導入して始めたので結構ハマったりするかなーと不安もありつつ進めたんですが、一番厄介だったのは、そういうところよりも[YouTube API](https://developers.google.com/youtube/)でした。
再生リストをYouTubeのAPIから作れることを最初気づかずに、再生リストのようなものを作る方法を考えるのにだいぶ時間を無駄にしたりしました。

### browserify

ちょっと困ったのは、アプリ内のリンクを押下時に通常のウェブブラウザで開きたいってなったときに、[browserify](http://browserify.org/)を使っていると、

```js
require('shell')
```
のようなelectronのモジュールが`cannot find module 'shell'`となってしまって読み込めない点で、HTML側にインラインのscriptで

```js
const shell = require('electron').shell;
```

とすることで、解消しました。
[`window.require('remote')`とすることでも解決する](http://stackoverflow.com/questions/31233935/electron-es6-how-to-implement-require-remote-ipc-when-using-gulp-and-es6-modul)ようなので、グローバルの`require()`であればモジュールを問題なく読み込めるようです。


YouTube動画の読み込み失敗時に何も表示されないとか、アプリの機能をもうちょっと充実なり整理なりしたいとか、ReactやFluxについて理解が足りてなくてコードの整理が必要なとことか、不足してる点はあるものの、全体的にはES6 + React で楽しく作れたと思います。


## パッケージング + リリース

[NW.js/ElectronアプリをGitHub Releaseで公開する[自動デプロイ] | Web Scratch](http://efcl.info/2015/07/28/nwjs-electron-github-release/)を参考に、タグがついたコミットでtravis-ciからリリースするようにしました。

```yaml
after_success:
- test ${TRAVIS_TAG} && npm run package
```
travis-ci のテストが通ったら、npm scriptsの`package`を実行するようにしてます。
`package`の中身はこれで、

```json
    "package": "gulp package && zip tubehead-darwin-x64.zip -r ./tubehead-darwin-x64"
```

[Gulp](http://gulpjs.com/)の`package`タスクを実行した後に、`zip`コマンドでリリースするzipファイルを作ってます。`package`タスクは、[electron-packager](https://github.com/maxogden/electron-packager)でパッケージを作るだけです。

```js
gulp.task('package', ['build'], function (done) {
  packager({
    dir: './',
    out: './',
    name: 'tubehead',
    arch: 'x64',
    platform: 'darwin',
    version: '0.36.0',
    icon: './icons/tubehead.icns',
    overwrite: true,
    asar: true,
    prune: true,
    'app-version': require('./package.json').version,
    ignore: [
      'src',
      'node_modules/babel-eslint',
      'node_modules/babel-plugin-transform-object-assign',
      'node_modules/babel-preset-es2015',
      'node_modules/babel-preset-react',
      'node_modules/babelify',
      'node_modules/cssnano',
      'node_modules/del',
      'node_modules/electron-debug',
      'node_modules/electron-connect',
      'node_modules/eslint-config-makotot',
      'node_modules/eslint-plugin-react',
      'node_modules/gulp',
      'node_modules/gulp-babel',
      'node_modules/gulp-eslint',
      'node_modules/gulp-if',
      'node_modules/gulp-plumber',
      'node_modules/gulp-postcss',
      'node_modules/gulp-sass',
      'node_modules/gulp-watch',
      'node_modules/postcss-calc',
      'node_modules/postcss-reporter',
      'node_modules/rucksack-css',
      'node_modules/run-sequence',
      'node_modules/stylelint',
      'node_modules/stylelint-config-makotot',
      'node_modules/vinyl-buffer',
      'node_modules/vinyl-source-stream',
      'node_modules/watchify'
    ]
  }, function (err, path) {
    if (err) {
      console.error(err);
    }
    console.log(path);
    done();
  });
});
```


全然まとめられてないですが、こんな感じで作ってみました。


## 参考URL

- [Electron Documentation](http://electron.atom.io/docs/v0.36.0/)
- [sindresorhus/awesome-electron](https://github.com/sindresorhus/awesome-electron)
- [Electronアプリをプロダクトとして「正しく」リリースするために必要な3つのこと - ヌーラボ [Nulab Inc.]](https://nulab-inc.com/ja/blog/typetalk/3-points-for-releasing-production-electron-app/)
- [ぼくのかんがえたさいきょうのElectron  - Qiita](http://qiita.com/Quramy/items/90d61ff37ca1b95a7f6d)