VRChatで動画プレイヤーが再生できない問題を簡単に解決したお話



# 概要
ファイルをダブルクリックして実行するだけで，ネットワークのIPv4の優先度を「最優先↔デフォルト設定」をトグルできるものを作った。

完全備忘録。
自分がやったことを書く日記的な。
知識を共有するならこんな長々とは書きませんたぶん。



# 経緯
VRChatでYoutubeを用いた動画プレイヤーサービスを利用していたが，Youtube側のセキュリティが変更になったのか，Youtubeにログインしていない状況でIPv6アドレスからアクセスするとボット判定を食らってしまい，動画プレイヤーサービスが利用できなくなってしまった。IPv4だとはじかれないらしいと聞いて，最初はPCのネットワークアダプタのIPv6アドレス自体をオフにしていたが，それだとIPv6のみにサービスを展開しているコンテンツにアクセスできなくなるし，セキュリティー的にもちょっと不安が残る。

最初は何も考えずにオフにしてしてたけど，これを話したら友人が，IPv4の優先度かえればえーやん，って色々調べて実装しようとしてたので私もそれにあやかった感じ。実装はオリジナル。

# そもそものネットワーク優先度の確認と変更


### 確認の方法

ネットワークの優先度はPowershell, command promptで確認ができる。
確認のコマンドは以下の通り。

```powershell
netsh interface ipv6 show prefixpolicies
```

これを実行すると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3960730/19e94636-872d-4137-b413-457b892f654e.png)

こんな感じの実行結果が出力される。

左から

- 優先順位
    - プレフィックスごとの優先順位。上から優先度が高い
- ラベル
    - しらん。なんかついてる。分からなくても問題無し
- プレフィックス
    - IPアドレスのネットワーク部のこと？よーわかってない。「::1/128」はIPv6，「::ffff:0:0/96」はIPv4，これだけわかってればOK
    - まあプレフィックスが通信プロトコル？方法を表していて，その優先度があるよ，という感じ？ふわふわ

### 変更の方法

ネットワークの優先度は分かったので，次はそのネットワークの優先度を変える。
変更のコマンドは以下の通り。

```powershell
netsh interface ipv6 set prefixpolicy prefix priority label
```

netsh interface ipv6 set prefixpolicy, で一区切り。

- ***prefix**で変更したいプレフィックスを指定*
- ***priority**で優先度を設定*
- ***label**でラベルを設定*

できる。

今回はIPv4（::ffff:0:0/96）の優先度をIPv6（::1/128）の優先度より高くしたいので実際にはこうなる

```powershell
netsh interface ipv6 set prefixpolicy ::ffff:0:0/96 60 0
```

※このコマンドの実行には管理者権限が必要

prefixでIPv4を指定して，priorityをIPv6より高い60に，ラベルは適当。
これを実行する。成功したらOKと返ってくる。


その後，先ほどと同様もう一度ネットワークの優先度を確認してみると…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3960730/7fc6cff8-2329-4eaf-93f1-723189cd691f.png)

IPv4のプレフィックスの優先度が，IPv6のプレフィックスの優先度より高くなってることがわかります。これでIPv6はオフにせずにIPv4で通信がされるようになった。

IPv6を最優先にしたい，ってときは

```powershell
rem もともとの値に変更（再起動不要）
netsh interface ipv6 set prefixpolicy ::ffff:0:0/96 35 4

rem 全部リセット（再起動必要）
netsh interface ipv6 reset
```

みたいな感じで打てばいいいいのではないでしょうか。

まあQiitaの記事とか見なさい。まとまってて分かりやすいから。

# もっと便利に

さっき言った方法を使えば，比較的簡単（少なくともIPv6をアダプターからオンオフするよりかは）にIPv4での通信を切り替えられる。

だけどめんどい！

いちいちWindows terminal管理者で開いて…コマンド打って…，必要なくなったらまた同じことを繰り返して…。

…

嫌なので，起動したら全部やってくれるようにします。

IPv4の通信の優先度は，常に最優先になってほしい，というわけではないので，ファイルを実行するだけでトグルができるように実装した。

最初は.batでウィンドウズのコマンドベースで作ろうとしたけど，構文分からんし，bash系シェルコマンドを使えなさそうだし，調べた感じ使うにしてもひと癖ありそうだった…。

ということで我らが味方Pythonでさくっと実装しました。

実装は以下の通り。

```python
import subprocess

# プレフィックスポリシークラス（1つずつ）
class PrefixPolicy:
    def __init__(self, priority, label, prefix):
        self.priority = priority
        self.label = label
        self.prefix = prefix

# プレフィックスポリシー集約クラス
class PrefixPolicyList:
    def __init__(self):
        self.policies = []

    def append(self, prefixpolicy):
        self.policies.append(prefixpolicy)

    def get_policy_by_prefix(self, prefix):
        for policy in self.policies:
            if prefix in policy.prefix:
                return policy

def main():

    # 現在のIPv6のプレフィックスポリシーを取得
    response = subprocess.check_output("netsh interface ipv6 show prefixpolicies")
    formatted_response = format_response(response)

    # プレフィックスポリシーリストを作成
    prefixpolicy_list = PrefixPolicyList()
    # プレフィックスポリシーリストにOSから取得したプレフィックスポリシーを追加
    for prefixpolicy in formatted_response:
        priority, label, prefix = prefixpolicy.split()
        prefixpolicy_list.append( PrefixPolicy(int(priority), label, prefix) )

    # IPv4とIPv6のプレフィックスポリシーを指定
    ipv4_prefix = "::ffff:0:0/96"
    ipv6_prefix = "::1/128"
    # IPv4とIPv6のプレフィックスポリシーを取得して表示
    ipv4_policy = prefixpolicy_list.get_policy_by_prefix(ipv4_prefix)
    ipv6_policy = prefixpolicy_list.get_policy_by_prefix(ipv6_prefix)

    # IPv4とIPv6のプレフィックスポリシーを比較して，IPv4が優先されるように設定
    if(ipv4_policy.priority > ipv6_policy.priority):
        subprocess.call("netsh interface ipv6 set prefixpolicy ::ffff:0:0/96 35 4")
        print('最優先：IPv6')
    elif(ipv4_policy.priority < ipv6_policy.priority):
        subprocess.call("netsh interface ipv6 set prefixpolicy ::ffff:0:0/96 60 0")
        print('最優先：IPv4')

# prefixpolycyのレスポンスをフォーマット
def format_response(response):
    return response.decode("utf-8", errors="replace").strip().replace("\r", "").split("\n")[4:]

if __name__ == "__main__":
    main()
```

なんか面倒になってきたので適当に書いていくよ。

## 最初のやつら

### import subprocess

subprocess は，python上でwindowsのpowershellなどで使えるシェルコマンドを利用するためのライブラリ。os.system()とかも使えるけど，こっちの方が自由度高めで使いやすいし，推奨されているらしい。

### PrefixPolicyクラス
プレフィックスポリシーを記述するオブジェクトを定義したクラス。
priority, label, prefixをインスタンス変数として持つ。

### PrefixPolicyListクラス
PrefixPolicyクラスのオブジェクトを集約する集約クラス。
集約するリストをインスタンス変数として持つ。

そのほかにはメソッドとして

- リストにPrefixPolicyを追加する君
- プレフィックスを引数とし，それに該当するPrefixPolicyを返却するゲッター

がある。

（もともとはprint関数とか定義してたけどテスト用でしかなかったので消した）

## main部

読ませる気のない説明。上から順番に適当に話してる。

最初にsubprocess.checkout()を使って，powershellコマンドを記述しネットワーク優先度を確認，実行結果を変数responseに格納してる。その後てきとーに結果をフォーマットしてる。

ちょっと補足
.checkout()は実行結果を返却してくれる。
.call()は実行結果は返却しない。（後々出てくる）
※これらの関数は.run()に統合されたが，めんどかったのでこっち使った。推奨は.run()

フォーマットされた結果を，PrefixPolicyオブジェクトとしてPrefixPolicyListオブジェクトに格納してる。これでデータがオブジェクトで扱えて便利だわね。

その後は，PrefixPolicyListオブジェクトのメソッドであるget_policy_by_prefix()を使って，プレフィックスを元にIPv4, 6のPrefixPolicyオブジェクトを取得してる。

このPrefixPolicyオブジェクトのインスタンス変数priorityを比較して，IPv4が優先でない場合は最優先に設定し，優先な場合は元に戻す（IPv6が最優先の状態）処理をしている。

設定はsubprocess.call()を用いてpowershellコマンドを記述することで実装。

終わり！

## 後ろ

フォーマットの関数（わざわざ作らなくてよかった）とメイン関数の実行処理があります。

# 実行

このままpythonスクリプトを実行すると，実行できるけど，管理者権限が必要な優先度の設定変更コマンドが実行できない！

以下はVS codeで実行した結果（上の方にプレフィックスポリシー並んでるけどデバック用print）

下に権限の昇格が必要，と書かれてる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3960730/c11c7dda-7677-49ca-80b2-7bd1b39138c8.png)

んーどうしようか…

いちいち右クリして管理者起動もめんどいし，そもそも.pyファイルはただのテキストファイルなので管理者で実行するメニューが出てこなかった。

かといってexeファイル化するのも面倒だしそこまでしっかり作りたくないしで…

ということでバッチファイルを管理者権限で実行して，その中でpythonスクリプトを実行することにした。

以下が実行のトリガーとなるバッチファイル。

```powershell
@echo off

rem admin reboot
net session >NUL 2>nul
if %errorlevel% neq 0 (
 @powershell start-process %~0 -verb runas
 exit
)

cd /d %~dp0
python toggle_ipv4_priority.py

timeout /t 5
```

`@echo off`は実行結果を出力しないようにするもの

`rem admin reboot`から `)` までは，バッチファイルによって開かれたターミナルが管理者権限であるかどうか確認して，そうでない場合は管理者として再起動するもの。これでいちいち右クリックしなくても管理者で起動できる（ユーザーアカウント制御の確認は出てくるけど…）。

`cd /d %~dp0`で実行ファイルのカレントディレクトリに移動

`python toggle_ipv4_priority.py` でpythonスクリプト（さっきのやつ）を実行

`timeout /t 5` は5秒待機するもの。これはpythonスクリプトで現在のIPの最優先設定を表示させているので，その確認猶予時間。

これで，このバッチファイルを起動して，ユーザーアカウント制御の確認もしたら，あとは勝手にIPv4を優先にしたりしなかったりしてくれる。

# 最後の面倒ポイント

もはや実行したときにユーザーアカウント制御のダイアログ出てきていちいち確認するのめんどくね？ってなったのでその回避方法を調べたら意外にも存在した。

もー解説面倒だしわざわざコード書いたとかではなくサイト見ただけなのでこちらを参照ください。

https://sakue.com/archives/1913

# めでたしめでたし
これで，デスクトップにあるショートカットを起動すれば，一発でIPv4とv6の優先度をトグル切り替えできるようになった。
うーれしー


※これ作った5日後くらいに気づいたんですけど，IPv6が最優先になってても動画プレイヤー見れるようになってました。んー作った意味なし！

# 参考とか

少しでも参考にしたサイトは大体貼ってます。
pythonの構文に関してのきじなｄ，蛇足な部分もありつつ，自分への備忘録なので残しておきます。

**IP優先度関係**

https://learn.microsoft.com/ja-jp/troubleshoot/windows-server/networking/configure-ipv6-in-windows

https://learn.microsoft.com/ja-jp/windows/win32/winsock/using-sio-address-list-sort

https://www.vwnet.jp/windows/w7/IPv4/IPv4PriorityUP.html

https://qiita.com/MeetMacaron/items/cdd8c80afcd02c0e9531

**python関係**

https://www.sejuku.net/blog/65485

https://qiita.com/HidKamiya/items/e192a55371a2961ca8a4#subprocessrun%E3%81%AB%E3%82%88%E3%82%8B%E7%BD%AE%E6%8F%9B

https://note.nkmk.me/python-list-str-select-replace/#in

https://note.nkmk.me/python-str-remove-strip/#replace

https://note.nkmk.me/python-rjust-center-ljust/

https://qiita.com/motoki1990/items/376fc1d1f3d59c960f5c

https://qiita.com/Tadataka_Takahashi/items/179d823f5b1dbda89f1f

https://qiita.com/sugarflower/items/caf8a39163363de96ca3

**バッチファイル関係**

https://zenn.dev/kkj/articles/c082a63a03ed05

https://qiita.com/hidemaru-jp/items/ddf3c7615831a40b55e5

https://qiita.com/kokosan60/items/a135f0e59e2790db36a3

https://www.bnote.net/windows/bat_tips_cd.html

https://note.com/mamezo_notes/n/nc5dd87dbb0f1

**ユーザーアカウント制御のスキップ**

https://sakue.com/archives/1913