hilight.js

2018年6月10日日曜日

5ちゃんにニコ動のURLを貼る

5ちゃんにニコ動のURLをそのまま貼り付けても書き込みに失敗してしまう。
リンクを有効にしたまま貼り付けるには、一文字だけでもHTMLとかで使用される、文字コードに置き換えてやればいいらしい。

この表を元に、niconicoのnを以下に置き換える

n

http://www.nicovideo.jp/watch/sm32587390 に適用するとこうなる。

http://www.nicovideo.jp/watch/sm32587390

今更だけど、よく忘れて毎回調べるのがめんどかったので。

2018年5月4日金曜日

[c++]visual stadio 2012だけ 空shared_ptr構築の挙動が違う

visual studio 2012(以降vs2012)で、空のshared_ptrを生成するとuser_countで得られる値が、cpprefjpで確認した挙動と違ったので、確認してみるとvs2012だけ挙動が違ってたっぽい。

挙動を確認したコード

#include <iostream>
#include <memory>

template <class T, class P>
void print( T&& str, P&& p ) {
    std::cout << str << ":" << p.use_count() << " ";
    if ( p ) {
        std::cout << "exist" << std::endl;
    } else {
        std::cout << "null" << std::endl;
    }
}

int main()
{
    std::shared_ptr<int> p1;
    print( "p1", p1 );

    std::shared_ptr<int> p2( nullptr );
    print( "p2", p2 );

    std::shared_ptr<int> p3( nullptr, std::default_delete<int>() );
    print( "p3", p3 );

    std::shared_ptr<int> p4( nullptr, std::default_delete<int>(), std::allocator<void>() );
    print( "p4", p4 );

    std::shared_ptr<int> p5 = nullptr;
    print( "p5", p5 );
            
    std::shared_ptr<int> p6( new int );
    p6 = nullptr;
    print( "p6", p6 );

    return 0;
}

これを実行すると、cpprefjpの通りなら、デリータやアロケータを渡しているp3,p4だけ、use_countが1になるはず。

しかし、vs2012の結果はこれ。

Wandboxで、gccとclangを使ってコンパイルすると、ちゃんとリファレンス通りの結果になる。
gcc 4.7.3
clang 3.2
※コンパイラのバージョンはC++11に対応したできるだけ古いバージョンで比較

vs2015以降は、gccやclangと同じ結果になった。vs2013は知らない。
vs2012で空のshared_ptrを作りたい場合は、デフォルトコンストラクタを使おうってことだけど、もうvs2012自体使うことないか。

おまけ


shared_ptr クラス - MSDN - Microsoft visual studio 2015には、

null ポインターを使用して初期化される shared_ptr オブジェクトにはコントロール ブロックが存在し、空ではありません。

空の shared_ptr オブジェクトは、リソースを一切所有せず、コントロール ブロックも持ちません。

引数なし: 結果として生成されるオブジェクトは、空の shared_ptr オブジェクトまたは空の weak_ptr オブジェクトです。

デフォルトコンストラクタから生成されるのは、コントロールブロックもない空shared_ptrオブジェクトで、nullptrを渡した場合は、コントロールブロックが作られるので空オブジェクトじゃないと言ってるが、実装はこうなっていた。

constexpr shared_ptr() _NOEXCEPT
{    // construct empty shared_ptr
}

constexpr shared_ptr(nullptr_t) _NOEXCEPT
{    // construct empty shared_ptr
}

どう見ても両方共空shared_ptrオブジェクト生成しそうだが。
vs2012も同じ文章だったので、vs2012の実装に合わせて書いてそのままということなのだろうか。

2018年4月22日日曜日

[neovim]表示が一部壊れる

環境


  • ubuntu 16.04.4 LTS + LXDE
  • LXTerminal 0.2.0(LXDE入れた時にデフォルト設定されたターミナル)

現象


コマンド入力ラインの左側に変な記号が表示されて、入力中のコマンドが見えない。



解決


原因は、使用しているターミナル(LXTerminal)との相性が悪いらしい。
というわけで、ubuntuのデフォルトターミナルのgnome-terminalでneovimを開くようにしたら表示が壊れなくなった。

元々は、gnome-terminalを使っていたのだが、ubuntuデフォルトのデスクトップ環境が重かったので、LXDEを入れたら、デフォルトのターミナルまで変わってしまい、こんな状態になった。
現在、ctrl+alt+tでデフォルトターミナル(LXTerminal)を開いてから、そのターミナルでgnome-terminalを開いて、gnome-terminalでneovimを開くというめんどくさいことになっている。
というわけで、デフォルトターミナルを変更できないかと色々やってたら、ctrl+alt+tでデフォルトターミナルすら開かなくなってしまった。ぐぬぅ。

2018年4月21日土曜日

neovimに手を出した

これまでatomで記事を書いていたが、自分のPCでは100行くらい書くとガックガクに重くなってきつかった。(markdownプレビューも動かしていたのもあるが)
SublimeText3にも手を出してみたが、自分の環境だと日本語入力プラグインがうまく動いてくれずギブアップ。
というわけで、ターミナル上で動くなら軽いのではないかという根拠のない理由で、neovimに手を出してみた。

環境


  • ubuntu 16.04.4 LTS + LXDE
  • LXTerminal 0.2.0(LXDE入れた時にデフォルト設定されたターミナル)

インストール


neovimは、公式リポジトリにはないみたいなので、非公式リポジトリからaptでインストールするためのppaというソフトウェアを追加する。
追加すると、非公式リポジトリを追加するための add-apt-repository というコマンドが使用できるようになるので、それでneovimのリポジトリを追加する。

ppaを含んでいるパッケージをインストール。

sudo apt install software-properties-common

add-apt-repositoryが使えるようになったので、neovimのリポジトリを登録。

sudo add-apt-repository ppa:neovim-ppa/unstable

あとはいつも通り、一覧を更新してインストール。

sudo apt update
sudo apt install neovim

参考にしたサイトとかだと、ここからさらにドバーっと設定をしていくが、いきなり全部設定してもよくわからない。
というわけで、デフォルト状態で使っていて、必要になったものを順次対応していくことにした。


操作


nvim

とするとターミナル上にneovimが起動する。

ファイルを開きたい場合は

nvim sample.txt

ってやっとけばしばらくは困らなそう。

neovimはvim同様複数モードを切り替えて使用するエディターだが、とりあえずノーマルモードと挿入モードを覚えておけば最初のうちはなんとかなりそう。

モード 概要
ノーマルモード 基点になるモード。ショートカットキーで色々するモードらしい
挿入モード 文章を編集するモード。いわゆる普通のテキストエディター

あとは以下のショートカットを覚えておけば、文章書くだけならなんとかなる。
ちなみに、<C-a>のような記載は、Ctrl+aということらしい。
neovimとかvim系のサイトを見るとよく出てくる。

ショートカット 内容
i 挿入モードに移行
<C-[> ノーマルモードに戻る

あとは、ファイルを開いたり保存したりしたいのでそのためのコマンドがあればいい。
コマンドは、ノーマルモード時に:を入力すると、コマンド入力モードになり、一番下のラインにコマンドを入力できる。

コマンド 内容
:e 引数にファイルパスを指定してファイルを開く。例):e sample.txt
:w ファイルを上書き保存する。
:q ファイルを閉じる
:q! ファイルを保存せずに閉じる

:eは、:e ./ とかでディレクトリを開こうとすると、ディレクトリビューワーが表示されて、そこからファイルを選択できるようになる。

パスを入力するよりこっちのが楽。

とりあえず、これだけで文章書くのはなんとかなりそう。
1回windows上でvimを使って見た時は、モードとかコマンドとかややこしくてこんなの使えるかと思ったが、 linux使うようになったらターミナルが身近になったせいか、なんか使っていけそうな気がする。

2018年2月21日水曜日

[discord bot]チャンネルの作成と削除

discordで、サーバーのメンバーに自由にチャンネルを作成する権限を解放したかったが、削除の権限も一緒に解放されてしまうため、うっかり違うチャンネルを削除してしまったとかが起こりそうで、なかなか権限解放に踏み切れなかった。
ということで、botを介してチャンネルの作成削除を行うことで、自分が作成したチャンネルじゃないと削除できないようにしてみた。

事前にチャンネルの作成、削除を行える役職を作成しておいて、それを対象のbotに割り当てる必要がある。

環境


  • ubuntu 16.04.3 LTS + LXDE
  • javascript
  • node.js 9.4.0
  • discord.js 11.3.0

チャンネルの作成


チャンネルの作成は、guildオブジェクト(1つのサーバーを表すオブジェクトらしい)が持っているcreateChannelメソッドで行うことができる。

client.on("message", (message) =>
{
  message.guild.createChannel( 'sample', 'text' );
}

とりあえずこれだけで、sampleという名前のテキストチャンネルが作成される。ボイスチャンネルを作りたい場合は、第二引数を'voice'にする。

また、特定のカテゴリの下にチャンネルを作りたい場合は、チャンネルを作った後に、そのチャンネルに親チャンネルを指定してあげればいい。

client.on("message", (message) =>
{
  message.guild.createChannel( 'sample', 'text' )
    .then( (ch) => {
      // カテゴリもチャンネルの一種なので、channelsの中に入っている
      let parent = message.guild.channels.find( 'name', 'Text Channels' );
      if ( parent ) {
        ch.setParent( parent );
      }
    })
    .catch( (err) => { console.log( err ); } );
}

ちなみに、カテゴリ名に実際は小文字が使われていても、チャンネルリスト上では全部大文字で表示される。findメソッドで検索する時には、ちゃんと実際のカテゴリ名で検索しないと失敗する。

コマンドでチャンネル名を指定して作成する処理は、以下のような形にしてみた。うちのサーバーはカテゴリを使ってないので、単純にチャンネルを作成するだけになっている。

client.on("message", (message) =>
{
  // コマンドとチャンネル名指定の引数にわける
  let arg = message.content.split( /\s+/ );
  const cmd = arg.shift();
  const ch_name = arg[0];
 
  if ( cmd === '!ch' )
  {
    // 既に同名のチャンネルが存在していないかチェック
    // 同名チャンネルも作成できるが、消すときに困るので同名は弾く
    if ( !message.guild.channels.exists( 'name', ch_name ) )
    {
      message.guild.createChannel( ch_name, 'text' )
        .then( (ch) => {
          ch.send( message.member.displayName + 'が作成しました' );
        })
        .catch( (err) => { console.log( err ); } );
    }
  }
}

チャンネルの作成に成功したら、作成したチャンネルで作成者の名前を表示するようになっている。
thenとかcatchは、createChannelメソッドが返してくるpromisオブジェクトのメソッドで、非同期処理を扱うものらしい。
とりあえず、createChannelが終わるとthenの中身が実行される。失敗した場合はcatchの方が呼ばれる。


チャンネルの削除


チャンネルの削除は、channelオブジェクトのdeleteメソッドを呼ぶだけで削除される。

client.on("message", (message) =>
{
  let ch = message.guild.channels.find( 'name', 'sample' );
  if ( ch ) {
    ch.delete();
  }
}

コマンドで削除チャンネルを指定するのは、チャンネル作成と同じなので省略。


チャンネル作成者のみ削除を行えるようにする


作成したチャンネルのIDと作成者のIDを記録しておいて、削除の時に参照するようにする。
また、botを再起動しても大丈夫なように、チャンネル作成ログをJSONファイルとして出力するようにする。
以下がそのコード。

"use strict"
 
// JSONファイル出力関数
const write_json = ( filename, obj ) =>
{
  fs.writeFile( filename, JSON.stringify( obj, null, '\t' ), (e) => {
    if ( e ) {
      console.log( e );
      throw e;
    }
  });
}
 
const Discord = require( 'discord.js' );
const fs = require( 'fs' );
const client = new Discord.Client();
const token = "xxxxxxxx"; // トークンに置き換え
 
const ch_log_filename = 'ch_log.json';
 
let ch_log ={};
// ch_log.jsonが存在していれば読み込み、無ければchannels配列を作成
try {
  const str = fs.readFileSync( ch_log_filename, 'utf8' );
  ch_log = JSON.parse( str );
}
catch ( err ) {
  ch_log.channels = new Array();
}
 
 
client.on("ready", () => {
  console.log("im ready test bot");
});
 
 
client.on("message", (message) =>
{
  let arg = message.content.split( /\s+/ );
  const cmd = arg.shift();
  const ch_name = arg[0];
 
  if ( cmd === '!ch' )
  {
    if ( !message.guild.channels.exists( 'name', ch_name  ) )
    {
      message.guild.createChannel( ch_name, 'text' )
        .then( (ch) => {
          ch.send( message.member.displayName + 'が作成しました' );
 
          let obj = {
            ch_id: ch.id,
            user_id: message.member.id,
          }
 
          // チャンネルIDとユーザーIDを追加してJSONファイルに出力
          ch_log.channels.push( obj );
          write_json( ch_log_filename, ch_log );
        })
        .catch( (err) => { console.log( err ); });
    }
    else {
      message.channel.send( '同名のチャンネルが既に存在しています' );
    }
  }
 
  if ( cmd === '!dch' )
  {
    let channel = message.guild.channels.find( 'name', ch_name );
 
    if ( channel )
    {
      const index = ch_log.channels.findIndex( (obj)=>{
        return obj.ch_id === channel.id;
      });
 
      // 削除しようとしているユーザーが作成者かチェック
      if ( message.member.id === ch_log.channels[index].user_id )
      {
        channel.delete()
          .then( (ch) => {
            // 削除したチャンネルじゃなければ削除メッセージを送信
            if ( ch.id !== message.channel.id ) {
              message.channel.send( ch_name + 'チャンネルを削除しました' );
            }
 
            // 削除したチャンネルのログを削除してJSONファイルに出力
            ch_log.channels.splice( index, 1 );
            write_json( ch_log_filename, ch_log );
          })
          .catch( (err) => { console.log( err ); } );
      }
      else {
        message.channel.send( ch_name + 'チャンネルを削除する権限がありません' );
      }
    }
    else {
      message.channel.send( ch_name + 'チャンネルは存在しません' );
    }
  }
});
 
client.login(token);

作成

削除


そろそろコードブロック表示ちゃんと対応しないと厳しいな…。

2018年2月13日火曜日

discordのbotをNAS上で常時動かす

discordでbotを動かせるようになったのはいいが、botは自分のPC上で動いているため、PCを落とすともちろんbotも落ちてしまう。
丁度、常時動いているNASがあったので、NAS上でbotを動かしてみた。


環境


  • ubuntu 16.04.3 LTS + LXDE
  • QNAP NAS TS-231+ バージョン4.3.4.0435


とりあえずNAS上で直接動かしてみる(失敗)


QNAP QTSのAppCenterからNode.jsをインストール。
その後、NASにSSHでリモート接続(ここのmacのやり方を参考)し、npmでdiscord.jsをインストールすることで、とりあえずbotが動かせる環境はできた。

しかし、botを動かしたままでNASからログアウトしようとすると、NAS上の端末がbotに占有されているので、botを止めないとログアウト処理ができない。 ログインしたままにしてみるが、PCを落とすともちろんSSH接続の切れて、なぜかNAS上で動いてるbotも停止する。SSH接続が切れると、NAS上で動かしていた端末も終了してしまうのだろうか。

そういう場合、screenを使って仮想端末を作り、その仮想端末上でbotを動かしたあとにデタッチすることで、botを動かしたままNASからログアウトできるらしいが、NASにはscreenが入っていない。
screenをインストールしようとするも、aptみたいなパッケージマネージャも入っていない。
opkgというパッケージマネージャに辿り着き、インストールしてみるもうんともすんとも言わない。
パッケージマネージャがなくてもscreenをインストールする方法もあるかもしれないが、分からなかったので他の方法に逃げることにした。


仮想環境でbotを動かす(成功)


QNAPのNASには、ContainerStationという仮想環境を立ち上げるアプリがあり、NAS上で別のOSを動かすことができるらしい。
ということで、その仮想環境上でbotを動かしたままにして、NASからログアウトできるかやってみる。

まずは、AppCenterでContainerStationをインストール。
ContainerStationを起動し、コンテンツ作成からUbuntuイメージをインストールする。


Ubuntuイメージは、推奨の中には3つほどあるが(イメージ検索するともっと出てくる)、UbuntuのバージョンをPC側と合わせたかったので、dockerのものを選択した。

インストールボタンを押すと、設定画面が出てくるがデフォルトのままでも問題なさそう。
NASのスペックが低いので、一応CPUリミットとメモリー制限を半分にしておいた。

あとは、詳細設定の共有フォルダで、NAS上のフォルダに仮想環境からマウントされるようにしておくと、NAS上のファイルに仮想環境からアクセスできて便利。

あとは、作成ボタンを押して少し待つと仮想環境が立ち上がる。

仮想環境にアクセスする

PCからNASにSSH接続し、

docker ps

とすると、現在起動しているdockerコンテナ(仮想環境)が表示される。
そこに表示されているCONTAINER IDを使用して

docker attach <CONTAINER ID>

とすることで、dockerコンテナにアクセスできる。

仮想環境内でbotを動かす環境を構築する

user作った方がいいみたいだけど、NASは外部に公開してないので、これ以降の仮想環境内ではルートで作業してしまっている。

まずはパッケージ一覧の更新と、パッケージの更新をしておく。

apt update
apt upgrade

次に、node.js,npm,discord.jsをインストールする。

apt install nodejs nps
npm install discord.js --save

dockerイメージは、タイムゾーンが協定世界時(UTC)になっているようなので、botで時間を扱う時用に日本(JST)に変更しておく。
timedatectlはなぜか入ってないので、/etc/localtimeのシンボリックリンクを変更する方法で変更しようとすると、タイムゾーンデータベースもなぜか入ってないのでインストールする。

apt install tzdata

それからタイムゾーンを日本に変更。

ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime


botを動かしたままにしてNASからログアウトする

自分の場合は、botのjsファイルをNAS上のjsフォルダにおいてたので、マウント先に移動してbotを起動。

cd /mnt/nas/js
node bot.js

起動を確認したら ctrl+p ctrl+q の順に押すことで、dockerコンテナをデタッチしてNAS上の端末に戻る。
あとはその端末で

exit

とするとNASからログアウトできる。

これで、botをNAS上で常時動かせるように出来た。

2018年2月7日水曜日

discordのbotからイカリング2にアクセスする

splatoon2の連動サービスであるイカリング2。アプリ以外からでも情報を読めるように出来るということで、discordのbot上から情報を取得して表示してみた。

環境


  • ubuntu 16.04.3 LTS + LXDE
  • javascript
  • node.js 9.4.0
  • npm 5.6.0
  • discord.js 11.3.0
  • ipad iOS 11.2.2


前提


イカリング2にアプリ以外からアクセスできるようになるまでは、
イカリング2をPCブラウザで見れる方法があるらしいのでやってみたを参照。

イカリング2から情報を取得するのは、
[Python]イカリング2のJsonを取得してみたを参照。

上記を参照しながら、ひっかかったところだけまとめていく。


pipのインストールでひっかかる


参照サイトのように

sudo easy_install pip

と打っても、easy_installなんてコマンドはないと言われる。
easy_installは、setuptoolsというパッケージをインストールすることで使えるようになるらしいが、既にメンテナンスされておらず、代わりにdistributeというのを使った方がいいらしい。
が、distributeについてはよくわからなかったので、普通にaptでインストールすることにした。

sudo apt install python-pip3

pipはpythonのパッケージ管理ツールで、python2用とpython3用で別々に存在するらしい。
ここでは、新しい方をインストールしておこうということでpython3用のpip3を選択した。
そのため、コマンドを打つ時は、pipではなくpip3になる。
ちなみにpython2用のpipも共存できる。


mitmproxyのインストールでひっかかる


mitmproxyインストール中にエラーで中断してしまった。
どうも以下のパッケージがインストールされていないといけないらしい。

  • python-dev
  • libffi-dev
  • libssl-dev
  • libxml2-dev
  • libxslt1-dev
  • libjpeg8-dev
  • zlib1g-dev

自分の場合は、sslなんちゃらが見つからないよと言われていたので、libssl-devをインストールしたら通るようになった。
めんどくさい場合は、全部インストールしておけば問題ないだろう。

sudo apt install python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev



mitmproxyの起動でひっかかる


参照サイト の通りに、ポート8080を指定したら、既に使われていると出て起動できなかった。
調べてみるとSublimeText3がそのポートを使用していたので終了させた。
すると今度はchromeがポート8080を使い出して起動できない。
結局、他の使われていないポートを使用することで起動した。
ここではポート8081を使うようにする。

mitmproxy -p 8081

iOS端末の方でも、HTTPプロキシの設定でポートを8081にする必要がある。


PCのIPが分からなくてひっかかる


linux ip 確認 でググッてたどり着いたのが

ip route

だったが、IPらしきものが複数表示されて、どれがPCのIPかぜんぜんわからん。 ip route 見方 でググっても経路どうこうと専門的な説明ばかりで、結局PCのIPと言われた場合はどのIPを指すのかぜんぜんわからん。

実際に試した感じでは、下の画像の○で囲んだ部分が、PCのIPと言われた時に参照するIPのようだ。

一番上の頭にdefaultがついているのがルーターを指し、一番下の行が自分のPC、真ん中の169.254.0.0というのは無視していいもののようだ。
ちなみにwifi搭載機で有線接続を行っていると、ルーターと自分のPCのところに、有線分の行が追加される。

といわけでIPが分かったので、iOS端末のHTTPプロキシ設定でサーバーのところに、このIPを書き込む。


iOS端末がネットに繋がらなくてひっかかる


http://mitm.itにアクセスして証明書のインストールまでは出来たが、iOS端末でNintendoSwitchOnlineアプリを開くと、接続が不安定ですと言われて先に進めなくなった。
(そもそもhttp://mitm.itにアクセスできない場合は、mitmproxyの起動か、iOS端末のHTTPプロキシの設定に問題がある)

証明書をインストールした後に、iOS端末の[設定]-[一般]-[証明書信頼度設定]から、インストールしたmitmproxyをONにする必要があった。
これでイカリング2にアクセスできるようになり、mitmproxy上で色々情報が見れるようになった。


参考サイトのサンプルコードがpythonでひっかかる


ここからは、[Python]イカリング2のJsonを取得してみたを参照しながら作業を進めるが、タイトルにもあるようにpythonを使用している。

というわけで調べた結果、javascriptでjsonを取得する場合、node.jsのモジュールであるrequestを使うのがよさそうだ。
他にもいろいろ方法があるが、discord.jsもnode.jsのモジュールなので、同じnode.js上で動くものを選んだ。
というわけで、requestモジュールをインストールする。

npm install request --save

これを使用して、jsonの取得処理を書くとこうなる。

let req = require( 'request' );
let j = req.jar();
 
const url = 'https://app.splatoon2.nintendo.net/api/onlineshop/merchandises';
const cookie = 'iksm_session=xxxxx';  // xxxxxを取得したcookieの値に置き換え
j.setCookie( cookie, url );
 
const options = {
  url: url,
  method: 'GET',
  jar: j,
  headers: {
    'content-type': 'application/json',
  },
  json: true
}
 
req( options, ( err, res, obj ) => {
  console.log( JSON.stringify( obj, null, '\t' ) );
});

上の例では、ゲソタウンから取得したJSONをコンソール上に表示する。

取得したjsonを利用して、botでゲソタウンの先頭のギアを表示してみる。

"use strict"
 
const Discord = require("discord.js");
const client = new Discord.Client();
const token = "xxxxx";  // xxxxxをbotのトークンに置き換え
 
client.on("ready", () => {
    console.log("im ready test bot");
});
 
 
client.on("message", (message) =>
{
    if ( message.isMentioned(client.user) )
    {
        let req = require( 'request' );
        let j = req.jar();
 
        const url = 'https://app.splatoon2.nintendo.net/api/onlineshop/merchandises';
        const cookie = 'iksm_session=xxxxx';  // xxxxxを取得したcookieの値に置き換え
        j.setCookie( cookie, url );
 
        const options = {
            url: url,
              method: 'GET',
              jar: j,
              headers: {
                  'content-type': 'application/json',
              },
              json: true
        }
 
        req( options, ( err, res, obj ) =>
        {
            const gear = obj.merchandises[0];
 
            const desc = 'ブランド:' + gear.gear.brand.name + '\n' +
                        'ギアパワー:' + gear.skill.name + '\n' +
                        'サブギアパワー:' + gear.gear.brand.frequent_skill.name;
 
            message.channel.send( desc, {
                'embed': {
                    title: gear.gear.name,
                    'image': {
                        'url': 'https://app.splatoon2.nintendo.net' + gear.gear.image,
                    }
                }
            } );
        });
    }
 
});
 
client.login(token);

こんな感じになる

2018年2月2日金曜日

discordのbotにカスタム絵文字を使わせる

前提

ここでのdiscord botは、javascriptでdiscord.jsを使用して作成。
javascriptもdiscord botもさわり初めて2〜3日なので、その程度の知識。

botでカスタム絵文字が使えない

discordのbotに絵文字を使わせる場合、標準の絵文字だと

:boom:

とか書くだけで


のように表示される。
しかし、これがカスタム絵文字になると :custom_test: と書いても

のようにそのまま表示されてしまった。

解決策

ここを参考に解決。英語が読める場合は、参照サイトの方が詳しく書いてます。

テキストチャットをする際は、カスタム絵文字も :custom_test: と書くと表示されていたけど、実はこれは省略された形らしく、botにカスタム絵文字を使わせる場合は本来の書き方をしないといけないらしい。

本来の形は以下の通り

<:custom_test:012345678910111213>

数字部分はカスタム絵文字に割り振られたID(値は適当)。
これをこのまま書いても表示されるようになるが、このIDがdiscord内のどこに表記されているかわからなかったので、"<:custom_test:012345678910111213>"という文字列を取得するようにした。

以下、botにメンションを送ったら、botがカスタム絵文字を表示するコード。

"use strict"
 
const Discord = require("discord.js");
const client = new Discord.Client();
const token = "xxxxxxxxxx"; // botのトークンに置き換える
 
client.on("ready", () => {
    console.log("im ready test bot");
});
 
 
client.on("message", (message) =>
{
    if ( message.isMentioned(client.user) )
    {
        // emojisコレクションから"custom_test"という名前のカスタム絵文字を検索
        // "name"は名前を検索するってことか?
        const emoji = client.emojis.find( "name", "custom_test" );
 
        // emojiオブジェクトのtoString()メソッドで"<:custom_test:012345678910111213>"が取得できる
        message.channel.send( emoji.toString() );
    }
 
});
 
client.login(token);

結果

絵文字が見つからなかった場合、emojiにはnullが入ってるので、実際に使う場合はちゃんとnullチェックしないといけない。


余談

参考にしたサイト だと

  const emoji = client.emojis.find( "name", "custom_test" );
  message.channel.send( '${emoji}' );

でいけるみたいに書いてたが、うちの環境ではそのまま ${emoji} と表示されてしまい、解決できなかったのでtoString()を使った。

2018年1月24日水曜日

動画確認用プレイヤー

動画編集時に録っておいた動画を確認するのに時間がかかるのでよさ気なプレイヤーがないか探してみた。
(windows media playerは倍速再生ができない)

VLCMPC-BEが候補に上がった。
どちらも4倍速再生まで行えるが、うちの環境だと、VLCでの4倍速再生はカクカクしたり、画像がかなり乱れていた。設定の問題かもしれないが、MPC-BEではデフォルトでスムーズに再生されていたので、MPC-BEを使うことにした。

ちなみに、MPC-BEにはシークバーにカーソルを乗せると、その場面をプレビューしてくれるシークプレビュー機能がついていて、これも確認作業には便利だ。
デフォルトではプレビュー表示されないが、[表示]-[プレイヤー]-[インターフェース]にある、[サーチでプレビュー機能を使用]にチェックをいれ、プレイヤーを再起動すると表示されるようになる。

2018年1月21日日曜日

アカネオブザワイルド act.02




前回、プレビューがうまく動かなくて編集が大変だったが、原因は単純に負荷が高すぎたせいだった。
動画素材は、game capture HDから出力されたものをそのまま使っていたのだが、フルHDのH.264圧縮だったらしくデコード負荷がえらいことになっていたようだ。

動画データを非圧縮のデータに変換することで、デコード負荷を減らすことができるらしいが、非圧縮だと数秒の動画でもギガ単位の大きさになったので、最終的にLagarithコーデックで可逆圧縮にした。非可逆圧縮より可逆圧縮の方が負荷が低いらしい。

変換作業手順

  1. Lagarithコーデックをインストール
  2. game capture HDから出力されたMP4(H.264非可逆圧縮)ファイルを、AviUtlにドラッグ&ドロップし、出力したい区間を指定
  3. [ファイル]-[AVI出力]の[ビデオ出力]ボタンを押し、Lagarith Lossless Codecを選択
  4. [保存]ボタンを押して、AVI(可逆圧縮)ファイルが出力される

変換したAVIファイルをゆっくりームービーメーカーの編集時に使うことで、プレビューでの確認がだいぶまともになった。タイミング合わせもプレビューでの確認で十分できそうだ。
ちなみに、変換する際に解像度もフルHDからハーフHDに落としておいた。

基本的に動画の編集は重いものなので、作業中の負荷も工夫して減らしていかないとすぐ重くなってしまうらしい。
例えば、オブジェクトがいっぱいで重い部分を一旦中間ファイルとして出力して、それを素材として使うと、オブジェクトが動画ファイル1つになるので軽くなるというのは、よく使われる方法らしい。

動画内容はうーんって感じだが、続けていかないと向上しないだろうし、とりあえず1000再生目指して続けていってみようと思う。
視聴者でしかなかった時は、1000再生は少ないと思ってたけど、自分でやってみると遠いなぁ。



ゼルダの伝説
ブレスオブザワイルド

2018年1月18日木曜日

PS Now無料体験申し込んだら自動更新されてた

ゲームストリーミングサービスである、PlayStation Nowの7日間無料体験というのに申し込んだら、デフォルトが自動更新になってて、7日過ぎたあとに無駄に月額2500円が支払われていた。

申し込み画面では自動更新の設定はなかった気がするが、定額系サービスの無料体験はどこも自動更新されるようになってたりするので気をつけなければならなかった。
自動更新解除は、PS4の場合[設定]-[アカウント管理]-[アカウント情報]-[PlayStasionの定額サービス]から行える。

そもそも、モンハンワールドの体験版をやるためにPS+の無料体験に申し込もうとして間違えたのだが、自動更新をOFFにして、使用を停止していたPS+も、無料体験を申し込んだ時点で自動更新がONにされていたみたいで、無駄に1ヶ月分支払われていた。

申し込み画面でちゃんとそこらへんのことを書いていたのかもしれないので、ちゃんと確認しないとなと思った。

せっかくなのでPS Nowを体験してみた。

  • wifi環境ではやはり厳しい。しっかりしたwifi環境用意できたら問題ないかもしれないが、有線で繋いだ方がはやい。
  • コントローラーからの入力ラグはどうしてもあるが、シビアな操作を求めるアクションでなければとりあえずプレイできる。でもカーソル動くのがワンテンポ遅れるのはストレス。
  • 解像度が落とされるので、グラが魅力のゲームにも向かなそう。

思ったよりは悪くなかったけど、1年で23600円(3ヶ月利用権x4の場合)と考えると躊躇するな…

2018年1月15日月曜日

初めて編集した動画を投稿した



初めてなのもあるけど作るのにはめちゃくちゃ時間がかかった。
編集5時間、エンコード5時間で、1分に満たない動画に10時間もかかってしまった。

まず編集にはゆっくりムービーメーカー3を使わせてもらったけど、編集中の動画プレビューがなかなかうまく動いてくれない。再生する度に、ムービーが再生してくれなかったり、VOICELOIDの音声が再生されなかったり、タイミングがずれてたりする。
そのため、ゆっくりムービーメーカー3上では大まかにデータを配置し、タイミングをしっかり合わせたい部分は、一旦動画をエンコードしてからタイミングを確認して、また編集してエンコードしてを繰り返した。

1分弱の動画なので、この手順でもなんとかなったが、長い動画を作る場合には気が遠くなりそうだ。
タイミング合わせがキレッキレの動画はたくさん上がっているので、なにか方法はあるのだろう。
単純にPCのスペックだろうか。次の動画を作る時はなんとかしたい。

エンコードでは、【AviUtl+x264guiEx】ニコニコ新仕様用 自己流エンコード設定を参考に高画質で出力されるように設定したらエラーが出まくった。 エラーの内容は、メモリが足りなかったり、メモリ不正アクセスだったり、さっきは読み込めてたムービーファイルや音声ファイルが対応してないフォーマットだとか言われたりで、同じ設定で出力しようとしても違うエラーを吐くこともあって困った。

おすすめされていた以下の3つのフィルターを切るとエラーがだいぶ大人しくなった。

  • NL-Means-Light for GPU
  • SharpenResize
  • WarpSharpMT

あとは、264guiExの設定をタイミング合わせ時の設定(ニコ動-エコノミー回避プリセット)に戻し、設定を1つ変えてはエンコードしてを繰り返し、できるだけ画質をあげていった。
結果的には、ビットレート計算機で計算した値よりちょっと大きい値を、目標映像ビットレートに設定するくらいの設定で落ち着いた。


その他ひっかかったこと

  • 立ち絵のファイル名につける連番は、0埋めする場合2桁まで
    xxx_01.pngはいいが、xxx_001.pngは認識されない でも、xxx_100.pngはOK

  • 使用する立ち絵は800x800のサイズに収まっていないと、AviUtl上で画像が切れて表示される キャラ素材スクリプトとかいうのがそういう仕様になってるかららしい ゆっくりムービーメーカー上では普通に表示されていたので、編集作業終盤まで気づかなかった

2018年1月13日土曜日

コードブロック表示テスト

test

記事はatomを使ってmarkdownで書いたものを、markdownのプレビューから[HTMLをコピー]でコピーしてbloggerに貼り付けたけど、コードブロックがプレビュー通りに表示されなかった。

いろいろ調べるとコードブロックを表示してくれるための仕掛けをbloggerに仕込まないといけないらしい。
悪戦苦闘したがなかなかうまくいかなかったので、
hassiweb's programming Bloggerの記事をAtom/Markdownで作成するための準備
の”コード記述用のCSSを作成"にあるcssをお借りして、とりあえず単純にコードブロックが分かるようにだけした。

高機能なものは必要になったらまたやってみよう…

2018年1月11日木曜日

ubuntuでgame capture HDを使えるようにした

 ubuntuを使い始めて数日の初心者が、ubuntuでgame capture HDからnintendo switchの動画をキャプチャするための手順を、ひっかかった部分も混じえてまとめてみました。

  • windows pcかmac持ってる場合は、公式でドライバが用意されてるのでそっち使った方が楽です。  
  • linuxに慣れてる人は、github tolga9009/elgato-gchd を見た方が早いです。
  • この方法はgame capture HD用であり、game capture HD60では使えません。(github tolga9009/elgato-gchd にHD60対応中みたいなこと書いてあるけど、現時点でしばらく更新されてない)

環境

  • ubuntu 16.04 RTS
  • game capture HD
  • nintendo switch

大まかな流れ

  1. 依存するソフトウェアのインストール
  2. 公式ドライバからファームウェア抽出
  3. linux用ドライバの作成と起動
  4. キャプチャ用ソフトウェアのインストールと設定

基本的に、Streaming on Linux using Elgato Game Capture HD and OBS Studio をグーグル翻訳したものに従って進めていきます。

依存するソフトウェアのインストール

まずは、ctrl+alt+Tで端末を起動します。
そしたら下記のコマンドを入力すると必要なものがインストールされます。

sudo apt install git dmg2img hfsprogs libusb-dev clang make build-essential cmake

installの後ろが全て必要なソフトウェア。参考にしているサイトではcmakeが入っていませんが、ドライバを作成する工程で必要になるので含めています。

さらにドライバ作成時には、libusbの1.0.20以上のバージョン(libusb-devに含まれているらしい)が必要になりますが、自分の場合はなぜか1.0.0でした。
1.0.20以上のバージョンでないと、ドライバを作成する工程でひっかかるので、以下のコマンドでバージョンを確認してください。

apt show libusb

Versionの項目が1.0.20以上であれば、公式ドライバからファームウェアの抽出の項目に飛んでください。

libusb1.0.20以上のバージョンのインストール

aptではバージョンを更新出来なかったので、ソースコードを取得してパッケージを作成し、そのパッケージをインストールするという手順で更新しました。

参考にしたページ
 クロの思考ノート 意外に簡単!Debパッケージを自作する方法
 クロの思考ノート Linuxにソフトウェアをインストールするには?

github.com/libusb/libusb/releasesからlibusb-1.0.21.tar.bz2を、~/Downloadsフォルダにダウンロードします。
とりあえず最新の安定版を落としました。

パッケージを作成するのに必要なソフトウェアをインストールします。

sudo apt install checkinstall

ダウンロードしたソースコードを展開、ビルドします。

cd Downloads
tar -jxf libusb-1.0.21.tar.bz2
cd libusb-1.0.21
./configure
make

ビルドしたものからDebパッケージを作成します。

sudo checkinstall --install=no

この後、端末上でいくつか設定の入力を求められますが、基本的に全部そのままEnterでOKです。

作成したDebパッケージをインストールするためのソフトウェアをインストールします。

sudo apt install gdebi

作成したlibusbのDebパッケージをインストールします。

sudo gdebi libusb_1.0.21-1_amd64.deb

ファイル名のamd64の部分が環境によって違うかもしれないので、libusb_まで入力したらtabキーで補完するのがいいです。  

これでlibusbが1.0.20以上のバージョンになったと思うので、もう一度バージョン確認コマンドで確認してみてください。

公式ドライバからファームウェアの抽出

参考サイトでは、macドライバがうんたらとか書いていますが、作業は全てubuntu上で行います。

まずは、ドライバ作成をするためのフォルダを作成します。

mkdir -v ~/elgato-gchd

次にドライバ作成に必要なものをgithubからダウンロードしておきます。

git clone https://github.com/tolga9009/elgato-gchd.git

これで、さっき作成したフォルダに必要なものがダウンロードされます。
フォルダは、このgit cloneをすると勝手に作られるかもしれませんが、自分はよくわからなかったので先にフォルダ作っておきました。

elgato公式から、ファームウェアを抽出するためのmac版ドライバをダウンロードします。

wget http://files.elgato.com/gamecapture/gchdm_203_970.dmg

~/Downloadsフォルダにダウンロードされたと思うので、そこに移動して解凍します。

cd ~/Downloads
dmg2img gchdm_203_970.dmg -o gchdm_203_970.dmg.img

解凍したイメージファイルにマウントします。

mkdir -v /tmp/dmg
sudo mount -o loop -t hfsplus gchdm_203_970.dmg.img /tmp/dmg

マウントはまだよくわかってませんが、こうすることでイメージファイル内にアクセスして必要なファイルを取ってこれるようです。

ファームウェア用のフォルダを作成します。

sudo mkdir -v /usr/local/lib/firmware/gchd

/tmp/dmg/Game Capture HD.app/Contents/Resources/Firmware/Beddo/から/usr/local/lib/firmware/gchd/に以下の4つのファイルをコピーします。

  • mb86h57_h58_enc_h.bin
  • mb86h57_h58_idle.bin
  • mb86m01_assp_nsec_enc_h.bin
  • mb86m01_assp_nsec_idle.bin

下2つのファイルは参考サイトにはありませんが、ドライバ起動時に見つからないとエラーが出たのでコピーしておきます。

sudo cp /tmp/dmg/Game Capture HD.app/Contents/Resources/Firmware/Beddo/mb86h57_h58_idle.bin /usr/local/lib/firmware/gchd/
sudo cp /tmp/dmg/Game Capture HD.app/Contents/Resources/Firmware/Beddo/mb86h57_h58_enc_h.bin /usr/local/lib/firmware/gchd/
sudo cp /tmp/dmg/Game Capture HD.app/Contents/Resources/Firmware/Beddo/mb86m01_assp_nsec_enc_h.bin /usr/local/lib/firmware/gchd/
sudo cp /tmp/dmg/Game Capture HD.app/Contents/Resources/Firmware/Beddo/mb86m01_assp_nsec_idle.bin /usr/local/lib/firmware/gchd/

さらに

  • mb86h57_h58_enc_h.bin
  • mb86h57_h58_idle.bin

の2つは~/elgato-gchd/firmware/にもコピーします。

こちらはファイルマネージャーからGUIでコピペしてもいいし、以下のコマンドでコピーしてもいいです。

sudo cp /tmp/dmg/Game Capture HD.app/Contents/Resources/Firmware/Beddo/mb86h57_h58_idle.bin $HOME/elgato-gchd/firmware/
sudo cp /tmp/dmg/Game Capture HD.app/Contents/Resources/Firmware/Beddo/mb86h57_h58_enc_h.bin $HOME/elgato-gchd/firmware/

$HOMEの部分はtabキーで補完すると、その環境の/home/(username)に変換されます。

tmpフォルダにファイルマネージャーからアクセスする場合は、コンピューターから辿るか、移動の場所を入力からアドレス入力でいけます。 (自分はホームとルートについてわかっておらず、ずっとホームを探してました。)

linux用ドライバの作成と起動

ドライバ作成用に作っておいたフォルダに移動します。

cd ~/elgato-gchd

ドイラバをビルドするためのフォルダを作り、そこに移動します。

mkdir -v build
cd build

ドライバをビルドします。

cmake ..
make

これにてドライバの作成は完了です。

ドライバを起動させるには、事前にgame capture HDをPCに接続しておきます。
次に、ドライバの実行ファイルがあるフォルダに移動してドライバを起動します。

cd ~/elgato-gchd/build/src/
sudo ./gchd -c rgb

switchを対象にした場合、デフォルト設定で起動すると画面が紫っぽくなったため、-cオプションでrgbを指定しました。

ドライバ起動中は、端末を開いたままにする必要があります。今開いている端末は専有してしまうので、ドライバ起動中にコマンドを打ちたい場合は別ウィンドウで新たに端末を起動する必要があります。

ドライバを停止したい場合は、ドライバが動作している端末でctrl+Cを押して停止させます。

キャプチャ用ソフトウェアのインストールと設定

OBS Studioというソフトウェアで、動画を録画できるようです。
下記のコマンドでインストールします。

sudo add-apt-repository ppa:obsproject/obs-studio
sudo apt update && sudo apt install obs-studio

インストール後、OBSを開いたらソースリストの下にある+ボタンをクリックし、メディアソースを選択します。
開いたウィンドウに任意の名前を入力して[OK]をクリックします。
次に開いたウィンドウのローカルファイルチェックボックスにチェックを入れます。(他のチェックボックスはそのままでOK)
ローカルファイルの右にあるブラウズボタンを押し、/tmp/elgato-gchd.tsファイルを選択(コンピューターから辿っていけます)して[開く]をクリックし、次に[OK]をクリックします。
OBSのメイン画面に戻ってちょっとするとプレビューウィンドウに動画が表示されます。


自分の環境では、PCが貧弱すぎたせいで動画がカクカクで結局使い物になりませんでした。
ああああああああああああああああああああああああああああああああああああああああああああああああああ