さんだすメモ

さんだすメモ

どんなにしょうもないこともいつかは役に立つはず

wsltty をビルドする

はじめに

自分用のメモより。基本的には README.md を見てインストールする。

install Cygwin64

  • パッケージは gitmakegcc-coregcc-g++unzipzoopatchlcab を選択する。

install Alpine WSL

  • Microsoft store より
  • 一度立ち上げてインストールしておく

download source

mkdir -p $HOME/src
cd $HOME/src
git config --global user.email "<email>"
git config --global user.name "<username>"
git clone https://github.com/mintty/wsltty.git

mintty のカスタマイズ

  • cd <wsltty>
  • make mintty-get
  • <wsltty>/makefile にて build: 内の mintty-get を削除
    • mintty が上書きされるのを防ぐため

デフォーカス時に透明度を更新しないようにする

<wsltty>/<mintty>/src/winmain.c にて、when WM_ACTIVATE: 内の win_update_transparency(cfg.opaque_when_focused);コメントアウトする。

mintty-build

cd <wsltty>
make mintty-build

mintty-build で失敗するとき

make mintty-build
# gcc -c -MMD -MP -std=gnu99 -include std.h -Wall -Wextra -Wundef -Werror -Wtrampolines -fomit-frame-pointer -O2  -DTARGET=x86_64-pc-cygwin -DVERSION_SUFFIX="– wsltty 3.5.0.2" -DWSLTTY_VERSION="3.5.0.2" -DNDEBUG -fstack-check winmain.c -o ../bin/cygwin64/winmain.o
# windres -c 65001 --preprocessor 'gcc -E -xc -DRC_INVOKED -MMD -MP -DTARGET=x86_64-pc-cygwin -DVERSION_SUFFIX="– wsltty 3.5.0.2" -DWSLTTY_VERSION="3.5.0.2" -DNDEBUG' res.rc ../bin/cygwin64/res.o
# /bin/sh: gcc -E -xc -DRC_INVOKED -MMD -MP -DTARGET=x86_64-pc-cygwin -DVERSION_SUFFIX=–: command not found
# windres: preprocessing failed.

windres の仕様か何かで詰まった。適当に修正する。

vim <wsltty>/<mintty>/src/Makefile
# --preprocessor=$(CC)
# --preprocessor-arg=-E
# ...
# --preprocessor-arg=$(addprefix --preprocessor-arg=,$(DEPOPT))
# ...
make mintty-build

build and install

cd <wsltty>
make build
make install

wsl2 の Ubuntu-20.04 で xrdp を起動してデスクトップを表示するバッチファイルを作った

はじめに

手作業で毎回立ち上げるのが面倒な(気がする)ので、バッチファイルを作ってみた。

wsl -d <distribution> -u <wsl_user> -- sudo /etc/init.d/xrdp restart; sleep 3
mstsc <file_name>.rdp

xrdp start して 3 秒待ってリモートデスクトップ接続するだけ。sleep が必要なことになかなか気づかなかった。

追記:WSLG が来たので必要じゃなくなった。

やったことなど

インストールなどは省略。

/etc/init.d/xrdp をパスワードなしで実行できるようにする

sudo visudo -f /etc/sudoers.d/xrdp で設定ファイルを作成・編集して、

%xrdp ALL=(root) NOPASSWD: /etc/sudoers.d/xrdp

とした。グループ xrdp に所属しているユーザーは、sudo/etc/init.d/xrdp を実行するときにパスワードが求められなくなる。

sleep 3

sleep 3 を書かずにやると、xrdp のサービスが停止し、接続できなかった。 xrdp-sesman のログを見たところ、端末が閉じたといった内容の警告が出ていたので、sleep を入れて無理やり解消した。3 秒間にしたのは、そうしているスクリプトをどこかで見たような気がしたから。

リモートデスクトップ接続のプロファイルを保存

<file_name>.rdp を作り、これを読み込むようにすると楽になる。

おわりに

自動起動にはいくつか方法があるみたいだけど、あんまり環境を変えるのもなあと思い、こんな対処をした。systemd を待つかどうか、地味に悩む……。

また、/etc/rc*.d のあたりの設定に、現状の一番自然な解の可能性を感じて調べていたが、勉強不足のまま解決してしまった。

GCコンのプロトコルについて

コントローラーを作ろうと思って色々調べたところ、GCコンのプロトコルについて新しくわかったことがあるので、その周辺をまとめました。間違いなどありましたら指摘してくださると助かります。

参考にしたサイト

コマンドは以下のサイトを参照した上で調べました。論理値の定義など、必要なことがほとんど載っています。

  1. Nintendo Gamecube Controller Protocol

  2. GameCube to Nintendo 64 Controller Converter

  3. Yet Another Gamecube Documentation

リクエストとコマンド

普通のGCコントローラーのDOL-003を使ってテストしました。

0x00: Identify

コントローラーは2バイトのデバイスIDと1バイトのステータスを返します。GCコンのIDは0x0900です。3.を参考にしつつ調べると、ステータスの8ビットの意味は次の表のようだとかんがえまs。

bit(s) description
7 bit 7, byte 0 of joy-channel = ERRSTAT? (0)
6 bit 6, byte 0 of joy-channel = ERRLATCH? (0)
5 is-not-calibrated
4 unused? (0)
3 is-rumbling
2-0 joy-channel mode of analog values

5ビット目はコントローラーのニュートラルポジションがセットされたかどうかを示します。そして、ホストが一度0x41または0x42のコマンドを送ると、5ビット目は0になります。 As described below, this is same as bit 5, byte 7 of joy-channel. So I assumed bit 7 and bit 6 are same as that of byte 0 of joy-channel (I couldn't test them).

0x40, byte B, byte C: request joy-channel, make rumble or not

The controller returns 8 bytes of joy-channel. The meanings are as follows, along with 1., 3..

bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
byte 0 ERRSTAT (0) ERRLATCH (0) is-not-calibrated Start Y X B A
byte 1 unused? (1) L R Z D-Up D-Down D-Right D-Left

And analog values follow.

bit 7-0
byte 2 Joystick X value (8 bit)
byte 3 Joystick Y value (8 bit)
byte 4-7 depends on the mode of analog values

joy-channel mode of analog values depends on byte B. The meanings are as follows. Only lower 3 bits of byte B are used.

bit 2 bit 1 bit 0 C-Stick X, Y L, R
0 0 0 8 bits 4 bits
0 0 1 4 bits 8 bits
0 1 0 4 bits 4 bits
0 1 1 8 bits 8 bits
1 0 0 8 bits 0 bits
X X X 8 bits 4 bits

The values are put in byte 4-7 in this order, and if there are extra frames, they are filled by 0.

Bit 0 of byte C shows whether to make the controller rumble or not. Other bits seem to be unused but I think they should be set to 0.

0x41: get original positions of the analog values

The controller returns 10 bytes. Byte 0-1 are the button state that is almost same format as above one. In special case that the host send 0x41 while Z button is pushed, ERRLATCH is set to 1 (I don't know why). Byte 2-7 are original positions of the analog values. Joystick X, Y, C-Stick X, Y, L, R are placed in this order, and the size of values are always 8 bits. Byte 8-9 are set to 0 in all case I tried. Note that if the controller haven't set original positions, it sets them and returns.

0x42 (, 0x00, 0x00): set original positions of the analog values

The controller sets orginal values. When the controller recieved 1 byte of 0x42, it returns nothing. On the other hand when recieved 3 bytes of 0x42, 0x00, 0x00, it returns 10 bytes of original positions (This is the same behavior as recieving 0x41 for the first time).

最後に

コントローラーを作るときに何が必須の条件になるのかを調べる余裕はありませんでしたが、実際の受け答えがわかれば設計がスムーズにできると思います。これが役に立てば幸いです。

    TS

開始時間を指定して録画する。等倍なのがよくない。
認証用にselenium-webdriverを利用。ログから探したいのでchromedriverを使う。

手順

起動

require 'selenium-webdriver'

log_prefs = { "performance" => "ALL" }
caps = Selenium::WebDriver::Remote::Capabilities.chrome("loggingPrefs" => log_prefs)

driver = Selenium::WebDriver.for :chrome, desired_capabilities: caps

# タイムアウトの時間設定
driver.manage.timeouts.page_load = 30

これでログが確認できるらしい。何をやっているかは調べてない。

ログイン

省略

再生

# ページのurl
live_url = "?????"

# ページにアクセス
driver.get(live_url)

# 設定ボタン
setting_button = driver.find_element(:xpath, "//button[contains(@class, 'setting-button')]")

# 設定が閉じていたら開く
if setting_button.attribute("aria-pressed") == "false"
  driver.action.move_to(setting_button).click().perform()
end

# 画質を選択する
video_quality_select_box = Selenium::WebDriver::Support::Select.new(driver.find_element(:xpath, "//select[contains(@name, 'videoQualitySelectBox')]"))
video_quality_select_box.select_by(:index, 1)


# 設定が開いていたら閉じる
puts setting_button.attribute("aria-pressed")
if setting_button.attribute("aria-pressed") == "true"
  driver.action.move_to(setting_button).click().perform()
end

# ここまでのログを処理しておく
driver.manage.logs.get(:performance)

# 再生位置を指定
time_text_box = driver.find_element(:xpath, "//input[contains(@class,'time-text-box')]")
time_text_box.send_keys(start_time, :enter)

# 再生中止
begin
  play_button = driver.find_element(:xpath, "//button[contains(@class,'play-button')]")
  Selenium::WebDriver::Wait.new(:timeout => 3).until {
    play_button.attribute("data-toggle-state") == "true"
  }
  driver.action.move_to(play_button).click().perform()
rescue
end

ログを確認

# m3u8のurlを取得
url_m3u8 = ""
driver.manage.logs.get(:performance).each do |log_entry|
  hash_message = JSON.parse(log_entry.message)
  if hash_message["message"]["method"] == "Network.requestWillBeSent"
    url = hash_message.dig("message", "params", "request", "url")
    if url.match(/master\.m3u8/)
      url_m3u8 = url
      puts url_m3u8
    end
  end
end

command = "ffmpeg -i \"#{url_m3u8}\" -bsf:a aac_adtstoasc -movflags faststart #{file_path}"

補足など

ページを開いておけばurlのみで大丈夫みたいだったため、この形にした。
ffmpegの開始が遅れるせいか、開始時間の指定から5秒遅れて始まることに気をつける。終了時間も指定しないとよくないかも。開始時間を指定するのは全体の時間短縮のためで、微調整は後でする。
ブラウザが作れると後々便利そうだけど……。

  らぶ画像

DOWNLOADの携帯用ページを順に見ていく(その他は省略)。練習用にselenium-driverを利用した。

準備

  • selenium-webdriver
  • webdriver-user-agent
  • geckodriver をインストールする

手順

ログインする

require 'selenium-webdriver'
require 'webdriver-user-agent'

# User-AgentをiPhoneにして使う
driver = Webdriver::UserAgent.driver(:browser => :firefox, :agent => :iphone)

# タイムアウトの時間設定
driver.manage.timeouts.page_load = 30

# ログインページを開く
driver.navigate.to "https://.........com/login_......html"

# フォームに入力
input_mailtel = driver.find_element(:xpath, "//input[contains(@name,'userno')]")
input_mailtel.send_keys(userno)

# フォームに入力
input_password = driver.find_element(:xpath, "//input[contains(@name,'password')]")
input_password.send_keys(password)

# クリックして送る
login_button = driver.find_element(:xpath, "//input[contains(@value,'ログイン')]")
login_button.click()

順番に画像を見る

# DOWNLOADのページ
driver.get("https://.........com/contents/......./sp/download/index.html")

# 各月のページのリンクを探す
driver.find_elementss(:xpath, "//section[contains(@class,'maincontents')]//li//a").each do |element|

  # 各月のページのurlを持っておく
  url = element.attribute('href')

  # 新しいウィンドウを開く
  driver.execute_script("window.open()")

  # 新しいウィンドウのハンドルが得られるまで待つ
  Selenium::WebDriver::Wait.new(:timeout => 10).until {
    driver.window_handles.length == 2
  }

  # 元と新規のハンドルを持っておく
  window_handles = driver.window_handles

  # フォーカスを移動
  driver.switch_to.window(window_handles[1])

  # ロードを待つ時間を設定:不要?
  driver.manage.timeouts.page_load = 30

  # 詳細のページに移動
  driver.get(url)

  # iPhone 6やAndroid用の画像があるので、それぞれのリンクを得る
  driver.find_elements(:xpath, "//section[contains(@class,'maincontents')]//li//a").each do |element_wp|
    # 何らかの処理
  end

  # 新しいウィンドウを閉じる
  driver.close

  # 元のウィンドウにフォーカスを戻す
  driver.switch_to.window(window_handles[0])
end

補足

driver.action.key_down(:control).click(element).key_up(:control).performで新しいタブで開けるが、ロードの待ち方がわからなかったので、新規ウィンドウを開くようにした。ウィンドウをくっつけたいとは思うけど必要ないので放置。
iPhone 6 Plusが一番高画質っぽい。

    動画

準備

ブラウザを操作できるようにします。

  • selenium-webdriverをインストールする
  • geckodriverをダウンロードしてpathを設定する(Firefoxの場合に必要みたい)

手順

ログインする

require 'selenium-webdriver'

# Firefoxを使う
driver = Selenium::Webdriver.for :firefox

# ロード完了まで少なくとも30秒は読み込みをする
driver.manage.timeouts.page_load = 30

# ログインページを開く
driver.navigate.to("https://account.---------.jp/login")

# フォームに入力
input_mailtel = driver.find_element(:id, 'input__mailtel')
input_mailtel.send_keys mail_tel

# フォームに入力
input_password = driver.find_element(:id, 'input__password')
input_password.send_keys password

# クリックして送る
login_button = driver.find_element(:id, 'login__submit')
login_button.click()

動画ページ

require 'selenium-webdriver'
require 'json'

# 動画ページにアクセス
driver.get("https://www.nicovideo.jp/watch/#{video_id}")

# Cookieを取得
all_cookies = driver.manage.all_cookies

# ffmpeg用にCookieを書き直す
# 各Cookieを区切るのには\nを使う
cookies = all_cookies.map { |hash_cookie|
  "#{hash_cookie[:name]}=#{hash_cookie[:value]};path=#{hash_cookie[:path]};domain=#{hash_cookie[:domain]}"
}.join("\n")

# ソースから動画の情報を抜き出す
js_initial_watch_data = driver.find_element(:id, 'js-initial-watch-data')
hash_apidata = JSON.parse(js_initial_watch_data.attribute('data-api-data'))

# 各種情報を取り出す
url = hash_apidata['video']['smileInfo']['url']
title = hash_apidata['video']['originalTitle']
date = hash_apidata['video']['postedDateTime']
filepath = "?????"

ffmpegを使う

system "ffmpeg -cookies \"#{cookies}\" -i #{url} -c copy \"#{filepath}\""

補足

hash_apidata['video']['smileInfo']['isSlowLine']trueのときはかなり遅くなります。falseにしたいなら深夜の時間を選ぶか会員になればok。

最後に

ブラウザで動画のページを開いておき、さらにCookieを持っていればsmileInfoのurlから再生できることがわかったので、ブラウザを制御できるselenium-webdriverを使いました。

net/httpでリクエストを送り、ストリーミングで受け取る方法もあります。ただし、制限をかけてくるので体感1.5倍~2倍程度の速さになり、随分時間がかかります。ページを開いたときやシークしたときは制限されないので、何かしら工夫したら改善しそうですが、わからないことがたくさんあるので諦めました。

ブラウザを操作できるのはすごく便利なんですが、リクエストなどの小回りは効かないので響は無理かなと思います。

2019/3/8 Wake Up, Girls! FINAL LIVE ~想い出のパレード~ 感想

自分と向き合うときに誤魔化しを嫌うかやたんの姿勢がとても好きです。表面的に理解して妥協しがちだけど、そこからも模索を続ける姿に学ばされることがあります。

まだまだ幼稚な自分にとって、彼女たちが背負って教えてくれたものはとても大きいです。

重い事柄に対しては、自分を守ることを優先して目を反らしがちになります。ずっと逃げていた中、自分を導くコンテンツが「そのひとつだといいな」と歌っていました。僕が土地に赴くことにほんの少しの意味があったかもしれない。この姿勢が提示されたという意味で、TUNAGOは自分にとってある種の救いになっています。

至らなすぎる。

これからは人生の第二章になります。

声優ユニットWake Up, Girls!が過去にあったという記憶が残る生活はまさに「第二章」です。まだ回収していないものが石油ほどありますし、各メンバーの活躍も楽しみです。活躍が目に見える世界で、純粋に応援したい人を応援できる幸せを噛みしめています。楽しむことが応援に繋がるとは、なんて素敵な推し事なんだ……。

同窓会が待ち遠しいです。

↓↓供養する場所がない


すべての人への感謝を伝えるところ。

決して悪口を言わないところ。

メンバーの喧嘩を見て泣いてしまったところ。

そうして大人になっていったところ。

どこか似ていて、かつ理想の先にいる存在だと思っている。

そんな姿に近づきたいと思っているし、彼女に魅力を感じている"同志"がいるのが嬉しい。

自分の立ち回りの上手さは100分の1以下だけど、そういう生き方を求めることに自分を見出したい。