Flet でビルドしたクライアントサイドウェブアプリをデプロイ

デスクトップ用に書いた Flet アプリのサイズや表示位置などをいじり、同じコード&操作感でウェブアプリとして使えるようにしました。今回のビルド方法はクライアントサイドで動くので (公式で言うところの、Static website) 、WordPress 等のホスティングサーバがあれば公開できるはずです。ついでに Google AdSense の広告も表示させています。

下準備

環境の構築方法などは以前の投稿に書いてあるので今回は省きます。以下の記事を参考に構築してください。

Python のフレームワーク Flet で macOS 用デスクトップアプリをビルド

サーバサイドでデプロイする場合

本記事はクライアントサイドなので、要するに HTML と JavaScript で動く公開方法です。サーバサイド、特に Apache ウェブサーバへデプロイする場合は以下の記事をご覧ください。Nginx ウェブサーバへのデプロイは公式サイトを見てもらうのが良いです。

Flet のウェブアプリを Apache ウェブサーバのリバースプロキシで動かす

ウェブアプリとしてビルドする

以前の macOS アプリビルドの記事の、ビルド実行 (手順 13) 直前まで終わっている事を前提に進めます。ウェブアプリに指定できる項目はあまりないようなので、簡単に以下のコマンドでビルドします。ビルドにかかる時間は、macOS 用にビルドした時と特に変わりないようです (本記事では、上記記事で紹介している fletpassgen を使用しています)。

flet build web

ローカル環境で動作確認

完成したファイル群は、build/web にまとめられています。まずは、ローカルのブラウザで動作することを確認しましょう。以下を実行し、ブラウザで http://localhost:8000 を開きます。一通りいじってみて問題無ければ、いよいよサーバへデプロイします。

python -m http.server --directory build/web
# 終了するときは Ctrl + C
Chrome で開いたところ

サーバにアップロードする前の作業

ディレクトリを指定

今回の例では、https://blog.peddals.com/fletpassgen にアクセスしたときにウェブアプリが開くようにします。そのために、index.html を一箇所書き換えます (ビルドするときにオプション --base-dir "/fletpassgen/" を追加していればこの手順は省けますが、ローカルでテストできません。なので、ビルド後に書き換えましょう)。お使いのエディタで build/web/index.html を開き、以下のように書き換えます。ディレクトリ名前後のスラッシュ (/) は必須です。

  <base href="/fletpassgen/">

フォルダを固める

フォルダ名自体も上記ディレクトリ名に変更し、一つのファイルに固めてアップロードしやすくします。以下実行後、fletpassgen.tar.gz が作られます。

cd build
mv web fletpassgen
tar cvzf fletpassgen.tar.gz fletpassgen

サーバにアップロード&その後の作業

固めたファイルをアップロード

ホスティングサーバへ fletpassgen.tar.gz をアップロードします。Terminal.app から実行する場合は以下のようなコマンドになります。指定するユーザ名、ホスト名、ディレクトリ名は適宜変更してください。

scp fletpassgen.tar.gz username@hostname:~/public_html

圧縮ファイルを展開

その後サーバでアップロードしたファイルを展開します。ssh が使える場合はこんな感じです。最後のコマンドは、不要になった圧縮ファイルを削除しています。

ssh username@hostname
cd ~/public_html
tar xvf fletpassgen.tar.gz
rm fletpassgen.tar.gz

Web ブラウザでアクセス

これまでの作業で、Flet のクライアントサイド (static) ウェブアプリがアップロードできました。実際にアップしたものがこちらからアクセスできます: https://blog.peddals.com/fletpassgen/

最初に数秒アイコンが表示され、その後ローカルでテストしたものと同じウェブアプリが表示されれば成功です。

うまくいかない時は

この方法でビルドした場合、ファイル容量が大きくなります。今回使ったサンプルで、展開後のトータルサイズは 28MB あります。なので、最初にアクセスしたときにはブラウザに必要なファイルを読み込むために多少の時間がかかります。まずはとにかく待ってみましょう。

いくら待ってもアイコンすら表示されない場合は、index.html 内のディレクトリ名の設定、実際に展開したディレクトリ名、ディレクトリやファイルのユーザ・グループ・アクセス権それぞれを見直しましょう。既存のファイルやディレクトリを参考に修正してみてください。

Tips と追加情報

デスクトップアプリと同じコードを使う

こぢんまりとしたデスクトップアプリは、ウィンドウサイズを指定し、リサイズを許可しない事で適当にレイアウトしてもどうにかなったりします (当初 fletwebapp がそうでした)。もちろん作るアプリ次第ですが、ウェブアプリにしたときにデザインが崩れないようにするには、page.window_width= で指定した幅を、ft.Containerwidth= プロパティでも指定するのが良いと思います。

サイズが大きいので注意

上にも書きましたが、こんな簡単なアプリ (Python コード単体で 約 3.9KB) でも、ビルドすると Python 自体や Flet、その他必要なファイルが追加されて約 28MB になってしまいます。デプロイの際には注意が必要です。

一度開けばネットワークが切れても動く

サーバサイドのデプロイではサーバとの接続が切れたときにも動作するように設計する必要がありますが、ブラウザにダウンロードするこの方法では、ネットが切れても動きます。用途によってはこの方法で十分でしょう。

Safari はコピーボタンが動かない

いつか修正されると思いますが、Chrome では動作する Copy ボタンが今回のデプロイ方法では動作しません。コード自体には、Safari だと Copy ボタンを表示しない設定を入れていますが、クライアントサイド (static website) デプロイではブラウザのエージェントを確認することができないためボタンを消すこともできませんでした。サーバサイドでのデプロイであれば、コピーはできないものの、ボタンが消えるハズです (そのうちテストします)。

おまけ: Google AdSense の広告を追加する

こちらのサイトを大いに参考にさせていただきました。ありがとうございます。

Flutterで作ったWebアプリでGoogleAdSenseの広告を表示する。

AdSense の HTML コードを取得

上記サイトを参考にコードを作成し、以下の部分をどこかに保存しておきます。

Google AdSence > 広告 > 広告ユニットごと > ディスプレイ広告 > 名前を付けて、作成

             data-ad-client="xxxxxxxx"
             data-ad-slot="yyyyyyyy"

index.html に style を追加

Flet ウェブアプリのディレクトリ (本記事では fletpassgen) 内にある index.html に以下 CSS の設定を追加します。追加場所は </style> タグのすぐ上あたりが良いでしょう。行番号は、Flet 0.19.0 の場合の参考としてください。

    footer{
        width: 100%;
        height: 100px;
        text-align: center;
        padding: 0;
        position: absolute;
        bottom: 0;
        z-index: 100;
    }

index.html に <footer></footer> ブロックを追加

最終行 </body></html> の上に、以下を追加します。ハイライトした data-ad-clientdata-ad-slot にはそれぞれ、先ほど AdSense からコピーした内容を貼り付けます。行番号に関しては同じく参考まで。

  <footer>
    <style>
    .example_responsive_1 { width: 320px; height: 100px; }
    @media(min-width: 500px) { .example_responsive_1 { width: 468px; height: 60px; } }
    @media(min-width: 800px) { .example_responsive_1 { width: 728px; height: 90px; } } 
    </style>
        <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
        <!-- Home_Page -->
        <ins class="adsbygoogle"
             style="display:inline-block"
             data-ad-client="AdSence でコピーした内容を貼る"
             data-ad-slot="AdSence でコピーした内容を貼る">
        </ins>
        <script>
    (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
  </footer>

変更内容を保存し、ウェブアプリの画面最下部に広告が表示されていれば成功です。

Image by Stable Diffusion

Date:
2024年1月29日 0:04:44

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
masterpiece, best quality, retro future, successful upload of application

Exclude from Image:

Seed:
3400661084

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

Python のフレームワーク Flet で macOS 用デスクトップアプリをビルド

2023年末にリリースされた Flet のバージョン 0.18.0 では、以前からあった pack より良いビルド方法 build が導入されました。自分も macOS 用にビルドしてみたのですが、なぜか一つの環境では Hello, Flet とだけウィンドウに表示されるアプリがビルドされ、いろいろ試すも解決できず。そしてつい数日前の 2024/1/16 に、主に flet build 関連の bug fix がなされたバージョン 0.19.0 がリリースされたので、macOS 用デスクトップアプリをビルドしてみました (flet 0.21.2 で Audio コントロールを使用する場合のビルド方法は別記事にまとめました)。

リリースのあった公式 discord: https://discord.com/channels/981374556059086931/981375816489402408

公式 GitHub の change log: https://github.com/flet-dev/flet/blob/main/CHANGELOG.md

公式 flet build の解説ページ (英語): https://flet.dev/docs/guides/python/packaging-app-for-distribution

まずはビルド前の Python スクリプトを簡単にご紹介

パスワード生成アプリを作りました。今回は Flet のビルドの紹介をメインとするので、アプリ自体の細かい説明は省きます。が、簡単に何ができるかというと、好きな文字数で、好きな特殊文字を含んだ、ある程度強度のあるパスワードを生成することができます。生成されたパスワードはコピーボタンでクリップボードにコピーできます。Safari が作ってくれる強力なパスワードが使えないサイトや、もっと強度を強くしたい、等の用途で活躍します。パスワード強度をチェックしてくれるサイト (bitwarden 等) で強度を調べると解析されるまで数世紀かかる等と結果が出て面白いです。

コードは GitHub に置いてあります: https://github.com/tokyohandsome/passgen.py/blob/main/fletpassgen.py

今回の Flet アプリ。Generate で生成し、Copy でクリップボードにコピー。文字数と特殊文字はキーボードから入力可能

環境

  • macOS: 14.2.1
  • 仮想環境 (pipenv): 2023.10.24
  • Python: 3.11.6
  • 追加モジュール: flet (バージョン: 0.19.0) 自動的に flet-core と flet-runtime も入っている
  • Rosetta
  • Xcode: 15.2
  • Git: 2.29.2
  • cocoapods: 1.14.3_1
  • Flutter: 3.16.7

手順

  1. 仮想環境を作る
  2. flet をインストール (バージョン 0.18.0 以上、今回は最新の 0.19.0)
  3. コードを書く、もしくは持ってくる
  4. 動作確認
  5. Flutter とその他必要なものを一式インストール
  6. アプリ名のフォルダを作り、以降はその中で作業
  7. assets フォルダを作り、アイコン (icon.png) を格納
  8. 手順 3. のコードを main.py とリネームしてコピー
  9. main.py の最後、メイン関数を呼び出す行を ft.app(main) だけにする
  10. requirements.txt に追加モジュールを書く
  11. ビルドテンプレートを GitHub からクローン
  12. ビルドテンプレートのコピーライト表記などを編集
  13. ビルド実行

部分的に細かく (手順 1~4)

仮想環境はお好きなのを使って良いと思います。Python は 3.8 以降、flet は特にバージョン指定せず pip install flet で良いでしょう。自分で書いた Flet アプリのコードが無ければ、上記のボクの GitHub からコピーしたものを適当な名前 (例: fletpassgen.py) で保存してください。python3 fletpassgen.py で動作することを確認したら、デスクトップアプリビルドに必要な諸々をインストールします。

Flutter とその他必要なものを一式インストール (手順 5)

ビルドには元祖である Flutter や開発言語の Dart とその他必要なもの一式をインストールする必要があります。基本的には一度済ませれば何度も行う必要はありません。以下は Apple Silicon (M1、M2、M3 シリーズ) の場合です。Flutter のサイトを参考に全て準備しましょう。

1. Rosetta: 以下を実行してインストール

sudo softwareupdate --install-rosetta --agree-to-license

2. Xcode 15: このページ右上の Download から Xcode 15 をダウンロードし、インストール

3. Cocoapods: 以下を実行してインストール

brew install cocoapods

4. Git: 以下を実行してインストール

brew install git

5. Flutter: ページ中頃のあたりの手順に従い、自分の CPU 用の Flutter SDK をダウンロードし、適当なフォルダ (例では ~/development/) に移動して .zip ファイルを展開、最後に環境変数 PATH にパスを追加 (以下の handsome を自分のユーザ名に変えて、~/.zshrc に追加し、source ~/.zshrc で読み込み)

export PATH="/Users/handsome/development/Flutter/flutter/bin:$PATH"

ビルドの準備 (手順 6~12)

Flet アプリとしての動作が確認できたら、最終的なアプリ名のフォルダを作り、中に入って準備を進めます。今回は fletpassgen という名前のアプリにします。

mkdir fletpassgen
cd fletpassgen
mkdir assets
open assets

最後の open assets で Finder でフォルダを開くので、アプリのアイコンに使用したい 512×512 ピクセルの画像ファイルを icon.png に従った名前で保存します (フォーマットは他に .bmp.jpg.webp が対応)。今回は使いませんが、その他アプリで使用する音声やテキストなどのファイルも assets フォルダに保存します。

アイコン画像について余談: 画像ファイルが無くても Flet のアイコンが使われるので、ビルドはできます。ボクは画像生成 AI 、Stable Diffusion の mac 用クライアントアプリ Mochi Diffusion を使ってアイコンを生成しました。参考までに本記事の最後にモデルやプロンプトを貼っておきます。

次に、Flet アプリのコードを main.py としてコピーしてきます。

cp ../fletpassgen.py main.py

Python コード最後の main 関数呼び出し部分が ft.app(main) じゃない場合は変更します。

#if __name__ == "__main__":ft.app(target=main) だったりしたら変更
ft.app(main)

requirements.txt には追加が必要なモジュールを記載するのですが、pip freeze > requirements.txt で書き出すと様々なエラーでビルドが止まり、ハマりました。今回のように標準のモジュールしか使用していない場合は、flet だけあれば大丈夫です。iOS や Android 用にビルドする場合も注意が必要なので、詳細は公式ページを見てください。

flet

コマンド + i でコピーライト表記が正しく表示されるように、テンプレートを公式の GitHub からクローンして cookiecutter.json の中身を書き換えます。以下例ではエディタに vi (vim) を使用していますが、お好みでどうぞ。

git clone https://github.com/flet-dev/flet-build-template
vi flet-build-template/cookiecutter.json

ハイライトした、7-9行目だけいじってます。

{
    "out_dir": "",
    "python_module_name": "main",
    "project_name": "",
    "project_description": "",
    "product_name": "{{ cookiecutter.project_name }}",
    "org_name": "com.peddals",
    "company_name": "Peddals.com",
    "copyright": "Copyright (c) 2024 Peddals.com",
    "sep": "/",
    "kotlin_dir": "{{ cookiecutter.org_name.replace('.', cookiecutter.sep) }}{{ cookiecutter.sep }}{{ cookiecutter.project_name }}{{ cookiecutter.sep }}",
    "hide_loading_animation": true,
    "team_id": "",
    "base_url": "/",
    "route_url_strategy": "path",
    "web_renderer": "canvaskit",
    "use_color_emoji": "false"
}

ビルド実行 (手順 13)

何もかも気にせずとりあえずビルドするなら、flet build macos でかまいません。バージョンを指定し、テンプレートをローカルに作ったものから読み込ませるなら、もうちょっと長く以下のように実行します (バージョン --build-version は指定しなければ 1.0.0 になるので、このサンプルではあえて 1.0.1 にしています)。(以下、Flet 作者の Feodor による説明を元に訂正 2024/01/25) ビルドテンプレートの指定は公式の説明に理解が及ばず、当初以下のように書いていましたが、--template の後に相対パス指定になります。修正前の方法でもビルドできますが、あるべき書き方にコマンドも修正しました。テンプレートの指定は相対パス (relative path) と公式ドキュメントにありますがうまくいかないので `pwd` を頭に付けて絶対パスとして指定しています。また、テンプレート指定のオプション自体が公式だと --template_dir (アンダースコア) になっていますが、0.18.0 と 0.19.0 では --template-dir (ハイフン) が正解でした。

flet build macos --build-version "1.0.1" --template flet-build-template

やや待って Success! が表示されれば完了です。build/macos/ の下に、アプリ fletpassgen.app が作られています。ボクの書いた fletpassgen.py を M1 mac mini でビルドすると、大体 3分 10秒くらいで完了しました。基本的に高効率コアが 6-8 割の使用量で推移し、所々パフォーマンス (高性能) コアのスパイクが見える感じです (他にもアプリが動いているので、はっきりとしたことはわかりませんけど)。

Creating Flutter bootstrap project...OK
Customizing app icons and splash images...OK
Generating app icons...OK
Packaging Python app...OK
Building macOS bundle...OK
Copying build to build/macos directory...OK
Success!
カラーだとこんな感じ

完成したアプリは Universal

アイコンはもちろん、バージョンとコピーライト表記も反映されている

ビルドされたアプリはアプリケーションフォルダにコピーしてダブルクリックで開けます。プライバシーとセキュリティで実行許可を与える必要はありません。Universal バイナリなので、試していませんが Intel mac でも動きそうです。アプリにした後の動作自体は CLI から実行したときと変わりありません。ウィンドウが開くときに若干もたつきがあり、Flet デフォルトのウィンドウが開いてから、指定したウィンドウにリサイズしてコンテンツを表示する、という動きも同じです。自分のコードの最適化か、Flet のアップデートで解決するかもしれません。

感想

実に良いです。これまで tkinter や、そのラッパー pysimplegui を使って GUI アプリを幾つか作りましたが、Flet は断然作りやすく、デザインはモダンで、今回紹介したようにビルドも簡単です。今後試したいこととしては、mac で Windows 用にビルドはできないようなのでParallels を使用したビルドを試したり、iOS やウェブ (サーバサイドでは無く、スタティック) 用にビルドすることも試したいと思います。一気にやれることが増え、やりたいことも増えてすごく楽しいですね。mac をお持ちの方は、ぜひお試しあれ (もちろん Windows や Linux の方も)。

Image by Stable Diffusion

Date:
2024年1月15日 23:05:04

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
masterpiece, best quality, retro future, cyber, disco computer, password generator

Exclude from Image:

Seed:
3224310018

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

(解決済み) macOS 「書類」以下のフォルダが Finder の移動メニューやターミナル.app から直接開けなかった

いつからかわからないんですが、mac の書類フォルダ (~/Documents) 以下のフォルダが Finder で直接開けなくなってしまっていました。原因はわからずじまいなのですが、解決できたので共有します。

きっかけ

現在の macOS のバージョンは Sonoma 14.2.1 です。実は以前から、ひょっとしたら Big Sur 11.0 あたりからターミナルで open ~/Documents/Python/hoge とかやってもウィンドウが開かないなと気になってはいたのですが、そんなに実害も無いしまあいいか、と放置していました。ところが今日、GitHub Desktop をいじっていた時に症状が現れました。同アプリで Show in Finder ボタンをクリックすると、本来開くべき書類フォルダ数階層下のフォルダでは無く、かわりに自分のホームフォルダがヘンな感じで開いたのです。これはやっぱりおかしい、解決しておかないと面倒なことになりそうだぞ、と言うことで調べ始めました。

↑これクリックで↓これが現れた (自分のホームディレクトリ)
本来は書類フォルダのもっと下の方にあるフォルダが開いてくれないとおかしい

症状

ターミナルアプリで ~/Documents フォルダ以下の様々なフォルダを open コマンドで開いても、同じく自分のホームしか開いてくれません。開いた Finder のウィンドウで書類フォルダをクリックすれば、内部のフォルダは全て開けます。ミュージック (~/Music) やダウンロード (~/Downloads) などの内部にあるフォルダも同様の手順でターミナルから開けます。書類フォルダの中にあるフォルダだけ、直接 Finder で開けないのです。Finder の「移動」メニューから「最近使ったフォルダ」で書類フォルダ以下のフォルダを指定したときも同じ動作です。右クリックから「新規タブで開く」を選んでも同じ。とにかく Finder が、書類フォルダ自体とその配下のフォルダを直接開くことができず、仕方なくホームフォルダを開いている感じでした。

どうやって解決したか

いろいろ試しましたが、最終的には Finder の表示方法をリストに変更することで解決したようです (元々は、カラム表示がダメだった雰囲気)。手順をもう少し細かく書くと、まず書類フォルダを開き、ウィンドウ上部にある表示からリストを選びます。

↑か↓

その後、アクションメニューから「表示オプションを表示」します。

コマンド + J でも OK

開いた小さいウィンドウの「常にリスト表示で開く」にチェックを入れ、一番下の「デフォルトとして使用」をクリックし、閉じます。これで、書類フォルダや配下のフォルダが Finder のリスト表示で開くようになりました。

勝利宣言

Finder は、小さな親切か大きなお世話かわかりませんが、あるウィンドウで表示方法を変更すると、次に新しく開いたウィンドウも表示方法が踏襲されたりします (条件はよくわからず)。なので、不具合を再現してみようと、上記設定をした後に表示方法をカラムにしたりギャラリーにしたり閉じたり開いたりを繰り返していたところ、最終的に不具合はぱったり発生しなくなりました。カラム表示でもサブフォルダが開くんです。よって、原因不明ながら、上記手順で解決、と言ってしまおうと思います。

他に試してダメだったこと (参考まで)

Image by Stable Diffusion

Date:
2024年1月3日 17:56:25

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
comicbook-style, gray hair guy looking for a missing folder in a book library

Exclude from Image:

Seed:
2520942867

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

mac でラズパイ起動用 SD カードや USB メモリをまるごとバックアップする簡単な Python スクリプト

ラズパイで使用している SSD が突然読み込めなくなったので、おおよそ1年前にバックアップしてあったディスクイメージを別の SSD にリストアし、諸々1年分を作り直しました。当然すごく面倒だったので、外付けメディアのバックアップを取る極シンプルな mac 用 Python スクリプトを書きました。ターゲットのデバイスを指定すると、バックアップの後イジェクトまでやってくれます。

いちおう、環境

  • macOS: Sonoma 14.2
  • Python: 3.11.6
  • 追加パッケージ等: 不要

普通にやるならこんな手順

mac に記憶デバイスを接続して一覧を表示、ターゲットとなるデバイス番号を確認、デバイスをアンマウントして、バックアップ開始、終了したらイジェクトして、記憶デバイスを物理的に取り外す、というのが一連の手順になります。Ubuntu がインストールされた 128GB SSD をバックアップしたときの例はこんな感じです ($ 以降が入力するコマンド)。

$ diskutil list external
... 省略 ...
/dev/disk4 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *128.0 GB   disk4
   1:             Windows_FAT_32 system-boot             268.4 MB   disk4s1
   2:                      Linux                         127.8 GB   disk4s2
... 省略 ...
$ sudo diskutil unmountDisk /dev/disk4
Password:
Unmount of all volumes on disk4 was successful
$ sudo dd if=/dev/rdisk4 of=RPi3BP_128GB_20231218-1.img bs=6M status=process
127999672320 bytes (128 GB, 119 GiB) copied, 2054 s, 62.3 MB/s
20350+1 records in
20350+1 records out
128035674112 bytes (128 GB, 119 GiB) copied, 2054.98 s, 62.3 MB/s
$ sudo diskutil eject /dev/disk4
Password:
Disk /dev/disk4 ejected

最初のコマンドは接続されている記憶デバイスの一覧を表示するのですが、external を付けて外付けディスクのみ表示させています。複数表示される場合は、表示される SIZE の値を頼りにするか、接続前に一度 diskutil list external を実行し、メディアを接続してから再度 diskutil list external で追加されたメディアの番号を確認します。

dd コマンドの説明:

  • if は、入力ファイルを指定。上の例の /dev/rdisk4 は、シーケンシャルにアクセスされるデバイスを指定しているので、/dev/disk4 とやるより速い
  • of は、出力ファイルを指定。上の例は、コマンドを実行したディレクトリに .img の拡張子を付けて書き出し
  • bs で、一度に処理するブロックサイズを指定。m1 mac mini (16GB RAM) でいくつか値を変えてテストした結果、6M (6メガバイト) が一番安定して速い速度で処理できた
  • status=process を付けると、実行中に進捗状況が表示される

mac 専用 Python スクリプト (とは言ってもほぼシステムコマンドを逐次実行)

import subprocess
import sys
import os
from datetime import datetime

if not os.getuid()==0:
    sys.exit(">> 管理者権限が必要です。'sudo python3 clibackup.py' の形式で再度実行し、パスワードを入力してください。")

subprocess.run(["diskutil", "list", "external"])
dnum = input(">> バックアップする記憶デバイスの番号を数字で入力してください('q' を入力すると終了)。\n>> (例: /dev/disk9 の場合は 9 を入力)\n>> /dev/disk?: ")
if dnum == 'q':
    print('終了しました。')
    sys.exit()
print(f">> ターゲット /dev/disk{dnum} をマウント解除します。")
subprocess.run(["diskutil", "umountDisk", "/dev/rdisk"+dnum])

timestamp = datetime.now().strftime("%Y%m%d%H%M")
#cmd = ["dd", "if=/dev/rdisk"+dnum, f"of=/Volumes/External HDD/SD_Card_Backup/backup_{timestamp}.img", "bs=6M", "status=progress"]
cmd = ["dd", "if=/dev/rdisk"+dnum, f"of=backup_{timestamp}.img", "bs=6M", "status=progress"]

print("\n>> 以下のコマンドを実行してバックアップします:")
for i in range(len(cmd)):
    print(cmd[i], end = " ")
print("\n")

process = subprocess.run(cmd)

print(f"\n>> バックアップが完了しました。デバイス /dev/disk{dnum} をイジェクトします。")
subprocess.run(["diskutil", "eject", "/dev/disk"+dnum])

保存先ディレクトリを指定する場合は、18行目の様に of= の後にパスを入れてください。ダブルクォーテーションでくくってあるので、スペースがあってもエスケープは不要です。作られるディスクイメージのファイル名は、年月日時分を含んだ backup_YYYYmmddHHMM.img になります。実行時の「分」までファイル名に含めているため、既存のファイルを上書きすることは無いでしょう。エラー処理の一切はしていないのであしからず。

使い方

sudo python3 clibackup.py で実行します (sudo を忘れると、メッセージを表示して終了します)。パスワードを入力すると、マウントされている外付け記憶デバイスの一覧が表示されるので、バックアップしたい USB ドライブなり SD カードなりのディスク番号を入力するとバックアップがスタートします。もし対象がわからない場合、一度接続していない状態で本スクリプトを実行し q で終了、その後メディアを接続して再度スクリプトを実行すると、前回は表示されていなかった /dev/disk があるはずなので、その番号を指定します。バックアップが完了すると自動でメディアをイジェクトするので、そのまま mac から取り外してかまいません。

実行例

Raspbian が入った 16GB の micro SD カード (SanDisk Ultra Class 10 A1 MicroSD HC I) を、mac mini の内蔵 SSD にバックアップしたときの実行例です。速度は 34MB/s、大体 8分ほどで完了しています。使用する記憶デバイスや接続の仕方次第で速度は結構違います (本記事頭で使用している SSD は、62.3MB/s)。自分は 2つのパーティションに分けた外付け 4TB HDD があるので、/dev/disk6~8 も表示されていますが、これらも環境によって変わります。

% sudo python clibackup.py 
Password:
/dev/disk4 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *15.9 GB    disk4
   1:             Windows_FAT_16 RECOVERY                1.7 GB     disk4s1
   2:                      Linux                         33.6 MB    disk4s5
   3:             Windows_FAT_32 boot                    72.4 MB    disk4s6
   4:                      Linux                         14.1 GB    disk4s7

/dev/disk6 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *4.0 TB     disk6
   1:                 Apple_APFS Container disk8         2.0 TB     disk6s1
   2:                 Apple_APFS Container disk7         2.0 TB     disk6s2

/dev/disk7 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +2.0 TB     disk7
                                 Physical Store disk6s2
   1:                APFS Volume External HDD            1.4 TB     disk7s1

/dev/disk8 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +2.0 TB     disk8
                                 Physical Store disk6s1
   1:                APFS Volume handsome's mac mini...  673.7 GB   disk8s1
   2:                APFS Volume Buffalo HDD             1.0 TB     disk8s2

>> バックアップする記憶デバイスの番号を数字で入力してください('q' を入力すると終了)。
>> (例: /dev/disk9 の場合は 9 を入力)
>> /dev/disk?: 4
>> ターゲット /dev/disk4 をマウント解除します。
Unmount of all volumes on disk4 was successful

>> 以下のコマンドを実行してバックアップします:
dd if=/dev/rdisk4 of=backup_202312231503.img bs=6M status=progress 

  15904800768 bytes (16 GB, 15 GiB) transferred 474.073s, 34 MB/s   
2532+1 records in
2532+1 records out
15931539456 bytes transferred in 474.873141 secs (33549043 bytes/sec)

>> バックアップが完了しました。デバイス /dev/disk4 をイジェクトします。
Disk /dev/disk4 ejected

記憶デバイスへの書き込み

スクリプトを書いていないので、手動でいくつかコマンドを打ってください。手順は本記事最初のサンプルと同様ですが、入出力が逆になります。dd コマンドでは、if= (入力ファイル) にディスクイメージ、of= (出力ファイル) に USB や SD カードなどの /dev/rdiskディスク番号 を指定します。出力先を間違うと取り返しがつかないことになり得るので、注意してください (サンプル通りに実行して大事なデータが消えてしまっても当方では責任とれません)。以下は、/dev/disk4 に SD カードがマウントされているときの例です。メディアを抜いた状態で diskutil list external を一度実行し、メディアをさしてから再度実行すれば、増えた /dev/disk が該当の記憶デバイスになります。

(記憶デバイスを接続していない状態で実行)
$ diskutil list external

(記憶デバイスを接続して再度実行)
$ diskutil list external
... 省略 ...
/dev/disk4 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *15.9 GB    disk4
   1:             Windows_FAT_16 RECOVERY                1.7 GB     disk4s1
   2:                      Linux                         33.6 MB    disk4s5
   3:             Windows_FAT_32 boot                    72.4 MB    disk4s6
   4:                      Linux                         14.1 GB    disk4s7
... 省略 ...
$ sudo diskutil unmountDisk /dev/disk4
Password:
Unmount of all volumes on disk4 was successful
$ sudo dd if=backup_202312231503.img of=/dev/rdisk4 bs=6M status=process

Image by Stable Diffusion

Date:
2023年12月23日 16:42:10

Model:
fruity-mix_split-einsum_compiled

Size:
512 x 512

Include in Image:
comicbook-style, cloned sheep standing side by side

Exclude from Image:

Seed:
3723203146

Steps:
25

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

Flet のウェブアプリを Apache ウェブサーバのリバースプロキシで動かす

Python でサクッとデスクトップアプリが作れる Flet ですが、同じコードに少しの変更をするだけでブラウザで動かせるようになります。今回の記事は、公式サイトにはない、Apache ウェブサーバで Flet アプリを動かす (セルフホスティングする) 方法です。

まず簡単に Flet とは

GUI やウェブフロントエンドの知識・経験がほとんど無くても、お手軽にデスクトップアプリやウェブアプリが作れる Python のフレームワークです。本家 (?) は Flutter という、Google さんが Dart という言語向けに開発しているモバイルアプリ用フレームワークです。それを Python から利用できるようにしたのが Flet と考えて良さそうです。実際、Flet 出書いたコードから吐き出されるエラーをネットで検索すると、Flutter に関するポストが多くヒットします。本記事では Flet も Flutter も深く触れませんので、詳細は他のサイトを漁ってください。

この記事で説明すること

キモは、Apache の TCP ポート指定を利用したリバースプロキシで Flet のウェブアプリを公開する、というところです。公開と言ってもボクの環境では、LAN にある mac からブラウザでアクセスできるようにするまでですが、パブリックに公開している Apache ウェブサーバでも同様の方法で公開できます。Flet の公式サイトでは、こちらの Self Hosting で NGINX ウェブサーバを使用した公開方法が紹介されていますが、それの Apache 版ということです。本記事のタイトルそのものズバリを説明しているサイトが見つからなかったので、まとめました。

環境

  • Ubuntu 20.04 LTS
  • Apache 2.4.41

ざっくりとした手順

  1. Ubuntu サーバに、Flet アプリの実行に必要なパッケージをインストール
  2. Python の仮想環境を作り、Flet をインストール
  3. Flet アプリを準備
  4. リバースプロキシに必要なモジュールを Apache で有効化
  5. Apache のコンフィグファイルを書く
  6. 自動的に起動する設定を書く

細かい手順

Ubuntu サーバに、Flet アプリの実行必要なパッケージをインストール

公式サイトの通り、Linux で Flet を動かすためには GStreamer のインストールが必要です。ここはサクッと入れてしまいましょう。

sudo apt-get update
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

Python の仮想環境を作り、Flet をインストール

ボクは pipenv を使っているので、こんな感じで仮想環境を作ります。手慣れた仮想環境とバージョンで、ここもサクッとどうぞ。サポートされている Python のバージョンは 3.8 以上です。

pipenv --python 3.11
pipenv shell
pip install flet

Flet アプリを準備

とりあえず、公式からそれっぽいものを持ってきましょう。インタラクションが確認できるので、ボクはこちらの Counter app を使用しました。とりあえず counter.py として保存し、最後の行を下のように編集します。

ft.app(target=main, view=None, port=8501)

簡単に説明すると、view=None でウィンドウやブラウザによる表示を行わず、ポート 8501 で待ち受けるよう指示をしています。ポートは同一サーバ上で重複が無ければ何でもかまいません。GUI とブラウザがインストールしてある環境であれば、python3 counter.py で実行すると、http://localhost:8501 にアクセスすればウェブアプリが開くと思います。次の手順以降で、外部にウェブアプリとして公開します。

リバースプロキシに必要なモジュールを Apache で有効化

Apache でリバースプロキシを行うには、いくつか必要なモジュールを追加する必要があります。Flet の場合、web socket も利用するため、wstunnel も必要です。以下は、モジュール追加、Apache の再起動、ステータス確認を実施しています。

sudo a2enmod proxy proxy_http proxy_wstunnel headers
sudo systemctl restart apache2
sudo systemctl status apache2

Apache のコンフィグファイルを書く

この例では、クライアント (PC 等のブラウザ) から flet.dev.peddals.com にアクセスすると Flet ウェブアプリが開く構成にしています。また、別記事に書いた様にこのドメインへの接続は HTTPS 接続になるので、Apache はポート 443 で待ち受け、内部的に 8501 ポートへリバースプロキシしています。このあたりはご自身の環境に合わせて指定してください。

13-14行目の wss:// の部分はひょっとしたら環境によっては必要ないかもしれません。

<VirtualHost *:443>
	ServerName flet.dev.peddals.com

	SSLEngine on
	SSLCertificateFile /etc/letsencrypt/live/dev.peddals.com/fullchain.pem	
	SSLCertificateKeyFile /etc/letsencrypt/live/dev.peddals.com/privkey.pem

	ProxyRequests Off
	ProxyPreserveHost On

	ProxyPass /ws ws://localhost:8501/ws
	ProxyPassReverse /ws ws://localhost:8501/ws
	ProxyPass /ws wss://localhost:8501/ws
	ProxyPassReverse /ws wss://localhost:8501/ws
	ProxyPass / http://localhost:8501/
	ProxyPassReverse / http://localhost:8501/

	ErrorLog ${APACHE_LOG_DIR}/flet.error.log
	CustomLog ${APACHE_LOG_DIR}/flet.log combined

</VirtualHost>

Apache に設定を読み込ませます。

sudo apachectl configtest
sudo systemctl reload apache2
sudo systemctl status apache2

この状態で一度 python3 counter.py で実行し、別のクライアント PC からサイトへアクセスし、動作するか確認してみましょう。wss:// の行を削除して読み込みが終わらない様でしたら追加してください。

自動的に起動する設定を書く

ここは公式のやり方を参考に編集します。自分の環境に合わせたものを貼っておきます。これを fletcounter.service として、counter.py と同じディレクトリに保存しています。

[Unit]
Description=Flet Counter Service
After=network.target

[Service]
User=handsome
Group=handsome
WorkingDirectory=/home/handsome/codes/flet
Environment="PATH=/home/handsome/.local/share/virtualenvs/flet-xuR7EMBP/bin/"
ExecStart=/home/handsome/.local/share/virtualenvs/flet-xuR7EMBP/bin/python3 /home/handsome/codes/flet/counter.py

[Install]
WantedBy=multi-user.target

いじる部分 (いじった内容) は以下の通りです:

  • Description= はご自由に
  • User=Group= には自分のユーザ名 (whoami)
  • WorkingDirectory= には、counter.py のあるディレクトリのパス
  • Environment="PATH= には、python3 のあるディレクトリのパス (which python3 の出力の bin/ まで)
  • ExecStart= の最初の引数は which python3 の出力全て、次の引数には counter.py のフルパス

そして最後にサービスとして起動、有効化します。これも公式のやり方に従います。シンボリックリンクの元ファイルは、上記のファイルを指定します。

cd /etc/systemd/system
sudo ln -s /home/handsome/codes/flet/fletcounter.service
sudo systemctl start fletcounter
sudo systemctl enable fletcounter
sudo systemctl status fletcounter

以上で設定はおしまいです。クライアント PC からアクセスし、カウンターが表示されれば OK です。可能であればサーバを再起動し、起動後にもカウンターが表示されることを確認しましょう。

ハマったところ

自分の環境で当初発生していた、読み込みが一生終わらない状態を解決するのにすごく時間がかかりました。原因は、ProxyPassProxyPassReverse にそれぞれ ws://http:// の両プロトコルだけしか指定していないことでした (公式の NGINX のリバースプロキシのコンフィグにも wss:// は無い) 。wss がウェブソケットのセキュア版 (http に対する https) と気づけなかったら諦めていたと思います。– なんて言いつつ、その後ラズパイの SSD が死に、リバースプロキシの設定をやり直したところ、wss:// の 2行が無くても問題なく動くようになっていました。ナゾ。

Image by Stable Diffusion

Date:
2023年11月25日 23:02:10

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, clolorful,
modern ladies working at post office classifying letters

Exclude from Image:

Seed:
4084494267

Steps:
23

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

LAN 内の開発用 Ubuntu に Let’s Encrypt の無料 SSL 証明書をインストール

HSTS preload の申請が通ったら、開発用サイトも HTTPS 接続必須となった (それは、そう)

会社で使っているドメインのセキュリティ向上のため HSTS preload の申請を行ったら良い感じでした。なので、個人所有のドメイン peddals.com も申請をしました。サブドメインを含め (includeSubDomains) 常に HTTPS で読み込む設定にしたため、クローズドな環境 (自宅 LAN 内の Raspberry Pi 3 model B) で http のみで運用していた開発サイトがアクセスできなくなりました。いくつか解決策を検討し、今回は Let’s Encrypt の無料 SSL 証明書を入れて対応してみたので、その手順を共有します。

開発環境と、その他必要だったもの

  • ラズパイ (Raspberry Pi 3 model B)
  • Ubuntu 20.04 LTS
  • Apache 2.4.41
  • ドメイン名
  • DNS サーバへのアクセス (TXT レコードの追加権限)

ざっくりとした手順

  1. Certbot をインストール
  2. certbot コマンドの実行 (DNS チャレンジ)
  3. 出力された値を DNS に TXT レコードとして記入
  4. Let’s Encrypt 証明書がインストールされる
  5. Apache で SSL を有効にする
  6. 開発サイトの Apache 設定ファイルのポートを 443 に変更し、SSL 証明書の内容を追記

細かい手順

証明書の取得 (CLI + DNS レコード追加)

まずは証明書のインストールに必要な certbot をインストールします (sudo bash で管理者権限を持ってから実行しています)。念のため、$# 以降が入力したコマンドで、それ以外は出力サンプルです。

$ sudo bash
# apt-get update
# apt-get install certbot

自分の場合、*.dev.peddals.com を対象にしたワイルドカード証明書をインストールしたいので、そのドメイン名を加えて以下コマンドで申請しました (別記事に書くかもしれませんが、mac では DNSmasq を動かしており、*.dev.peddals.com へのアクセスは全てラズパイに飛ばしています)。この例ではトップレベルドメインも含んでいますが、必要なわけではありません。

# certbot certonly --manual --preferred-challenges dns-01 -m mail@example.com -d '*.dev.peddals.com' -d peddals.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

今回はなぜかここで止まって進まなかったので一度 ctrl + C で止め、再度同じ certbot コマンドを実行しています。問題なく進んだので原因は調べていません、あしからず。

メールアドレスの共有 (上の質問) は強制では無かったと思いますが、IP アドレスの登録 (下の質問) は必須なので Y を入力しましょう。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for dev.peddals.com
dns-01 challenge for peddals.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.dev.peddals.com with the following value:

(ここにコードが表示される)

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

ここで一度止まるので、指示の通りに DNS に TXT レコードとして追加します。こんな感じです。

ホスト名にドメイン名が入らないように注意

追加したら CLI に戻ってエンターキーを押します。自分の例ではトップレベルドメインも -d で追加していたので、もう一度 DNS レコード記入の指示が出ました。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.peddals.com with the following value:

(ここにコードが表示される)

Before continuing, verify the record is deployed.
(This must be set up in addition to the previous challenges; do not remove,
replace, or undo the previous challenge tasks yet. Note that you might be
asked to create multiple distinct TXT records with the same name. This is
permitted by DNS standards.)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

同じ要領で DNS に追加し、エンターキーを押します。うまくいけば以下のように表示されて、証明書の取得が完了です。

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/dev.peddals.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/dev.peddals.com/privkey.pem
   Your cert will expire on 2024-02-10. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

秘密鍵や証明書の保存場所は IMPORTANT NOTES に書いてあります (忘れたら /etc/letsencrypt/live/ を見ましょう)。証明書の期限は 90日で、つど再取得の必要があります。再取得後も Apache の設定ファイルを書き直さなくて良いように、それぞれ実ファイルへのリンクになっています。

# ll /etc/letsencrypt/live/dev.peddals.com/
total 12
drwxr-xr-x 2 root root 4096 Nov 12 19:00 ./
drwx------ 3 root root 4096 Nov 12 19:00 ../
-rw-r--r-- 1 root root  692 Nov 12 19:00 README
lrwxrwxrwx 1 root root   39 Nov 12 19:00 cert.pem -> ../../archive/dev.peddals.com/cert1.pem
lrwxrwxrwx 1 root root   40 Nov 12 19:00 chain.pem -> ../../archive/dev.peddals.com/chain1.pem
lrwxrwxrwx 1 root root   44 Nov 12 19:00 fullchain.pem -> ../../archive/dev.peddals.com/fullchain1.pem
lrwxrwxrwx 1 root root   42 Nov 12 19:00 privkey.pem -> ../../archive/dev.peddals.com/privkey1.pem

Apache とサイトの設定ファイル

Apache で SSL が使えるようにします。最後の 2行で Apache を再起動 & 動いているか確認しています。

# a2enmod ssl
Considering dependency setenvif for ssl:
Module setenvif already enabled
Considering dependency mime for ssl:
Module mime already enabled
Considering dependency socache_shmcb for ssl:
Enabling module socache_shmcb.
Enabling module ssl.
See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates.
To activate the new configuration, you need to run:
  systemctl restart apache2
# systemctl restart apache2
# systemctl status apache2

サイトのコンフィグファイルに変更を加えます。ここでは変更、追加の部分のみ記載しています。先頭の TCP ポートを 443 に変更し、SSL の有効化、証明書と秘密鍵の指定を追加しています。最低限ブラウザでサイトが開くようにするための設定内容です。HSTS preload により全てのホストとの接続が HTTPS になるため、ポート 80 の設定ファイルに 443 へのリダイレクトを仕込むことも不要です。

<VirtualHost *:443>
	SSLEngine on
	SSLCertificateFile /etc/letsencrypt/live/dev.peddals.com/fullchain.pem	
	SSLCertificateKeyFile /etc/letsencrypt/live/dev.peddals.com/privkey.pem

設定ファイルに間違いが無いかテストし、Apache に設定を読み直させます。

# apachectl configtest
Syntax OK
# systemctl reload apache2

ブラウザで確認

最後に、ウェブブラウザで HTTPS で開ければ無事完成です。自動的に HTTPS 通信になるので、https:// を入力する必要はありません。

Safari でアクセス
もちろん Chrome でも開ける

Let’s Encrypt 証明書は 90日で期限が切れる

証明書の有効期限は 90日なので、なるべく切れる前に更新しましょう。期限切れまで 30日であれば更新できるようです。まぁ、開発用なのでとりあえずはつど手動で更新しましょうか。--dry-run オプションを付けてエラーが出ていなければ、オプション無しでもう一度実行して更新します。

# certbot renew --dry-run
# certbot renew

更新が必要ないときに実行すると、有効期限が切れる日付を表示してくれます。

# certbot renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/dev.peddals.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

The following certs are not due for renewal yet:
  /etc/letsencrypt/live/dev.peddals.com/fullchain.pem expires on 2024-02-10 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

自動化は crontab コマンドへの登録で実現できますが、今回は触れません。 → この方法でインストールした証明書は、crontabで自動化できません。以下にやり方を書きます。

証明書の更新方法

何度か書きたそうと思いつつ、2024年 7月末になってしまいました。久しぶりにラズパイで走っているウェブアプリにアクセスすると、いつまでたっても読み込まれません。証明書の状態 (Safari > URL の左のロックアイコンをクリック > [ 証明書を表示 ]) を見てみたら、こういう状況でした。月初に有効期限切れとなっていたわけです。

すでに「有効期限切れ」の状態。チェックマークと「この証明書は有効です」を見て安心してはいけない

ラズパイでcertbot renew --dry-runを実行するとこんな結果となりました。

# certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/dev.peddals.com-0001.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.')
Attempting to renew cert (dev.peddals.com-0001) from /etc/letsencrypt/renewal/dev.peddals.com-0001.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.'). Skipping.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/dev.peddals.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.')
Attempting to renew cert (dev.peddals.com) from /etc/letsencrypt/renewal/dev.peddals.com.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.'). Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/dev.peddals.com-0001/fullchain.pem (failure)
  /etc/letsencrypt/live/dev.peddals.com/fullchain.pem (failure)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/dev.peddals.com-0001/fullchain.pem (failure)
  /etc/letsencrypt/live/dev.peddals.com/fullchain.pem (failure)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2 renew failure(s), 0 parse failure(s)

上の例ではモノクロなのでわかりませんが、赤文字になっているところ (以下) を見ると、要するに認証スクリプトと一緒に実行しないとダメよ、ということです。

Attempting to renew cert (dev.peddals.com) from /etc/letsencrypt/renewal/dev.peddals.com.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.'). Skipping.
All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/dev.peddals.com-0001/fullchain.pem (failure)
/etc/letsencrypt/live/dev.peddals.com/fullchain.pem (failure)

というわけで、結局最初に入れた方法と同じコマンド+DNS TXT レコードの追加で対応することが必要となります。

# certbot certonly --manual --preferred-challenges dns-01 -m mail@example.com -d '*.dev.peddals.com'
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/dev.peddals.com-0001.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.')
Attempting to renew cert (dev.peddals.com-0001) from /etc/letsencrypt/renewal/dev.peddals.com-0001.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.'). Skipping.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/dev.peddals.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.')
Attempting to renew cert (dev.peddals.com) from /etc/letsencrypt/renewal/dev.peddals.com.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.'). Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/dev.peddals.com-0001/fullchain.pem (failure)
  /etc/letsencrypt/live/dev.peddals.com/fullchain.pem (failure)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/dev.peddals.com-0001/fullchain.pem (failure)
  /etc/letsencrypt/live/dev.peddals.com/fullchain.pem (failure)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2 renew failure(s), 0 parse failure(s)
root@ubuntu:/home/handsome# certbot certonly --manual --preferred-challenges dns-01 -m admin@peddals.com -d '*.dev.peddals.com'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for dev.peddals.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: 

Yで進める。

Please deploy a DNS TXT record under the name
_acme-challenge.dev.peddals.com with the following value:

(ここにコードが表示される)

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

以前追加済みの TXT レコードがドメインのパブリック DNS に残っていれば値を上記のコードに書き換え、削除済みであれば新規で TXT レコードを追加 (上記例のホストは _acme-challenge.dev.peddals.comがホスト名。コードを値として設定)。

DNS レコードの追加後、シェルに戻って Enter キー押下。やや待つとこんな感じの内容が表示されます。

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/dev.peddals.com-0001/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/dev.peddals.com-0001/privkey.pem
   Your cert will expire on 2024-10-22. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

んで、Apache を再起動。

# systemctl reload apache2

Safari で Cmd + Opt + R でページを新規読み込みすれば、無事表示されると思います。念のため証明書を確認してみましょう。

無事更新されました

ところで、HSTS の書き方・置き場所

本サイトはエックスサーバーに置いているので、ドキュメントルートに .htaccess を作り、そこに以下の一行を追加しています。includeSubDomains で全てのサブドメインを対象とし、preload でサイトへのアクセスは HTTPS に固定しています。

Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

まとめ

HSTS での設定以外にでも、開発環境に SSL 証明書をインストールしたい理由はいろいろあると思います。無料とはいえ本物の証明書が使えるので、この方法は試す価値大アリです。

Image by Stable Diffusion

Date:
2023年11月12日 17:59:44

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, let’s encrypt, apache, success

Exclude from Image:

Seed:
356871963

Steps:
30

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

StaSh の SSH を動かす方法 Pythonista 3 (ver. 3.4)

Pythonista 3.4 の StaSh で ssh を動かせたので、やったことをまとめます。

おことわり

Pythonista 3 のシェル環境 StaSh で ssh を使用するメリットについては触れません。ssh 接続をしている間は iPhone 本体が通常より熱を持つため、特にその必要が無い限り Termius 等の無料の SSH クライアント専用アプリを使用するのが良いと思います。本記事はむしろ、以前のバージョンの Pythonista 3 と StaSh で動いていたコマンドを、ver. 3.4 で動かす時の参考にしてもらえればと思います。また、再検証はしておらず、残しておいたメモを元に構成しているため、漏れや間違いがある可能性も否めません。

Pythonista 3.4 では、Python 2.7 のコードをそのまま実行することができなくなりました。StaSh に含まれるコマンドは全てが Python 3 に対応しているわけでは無く、ssh コマンド (ssh.py) もその一つです。以前の Pythonista (ver. 3.3 以前) では StaSh 自体を Python 2 環境で実行すれば ssh が実行可能でしたが、今後は Python 2 用コードは Python 3 に変換してあげる必要があります。Pythonista ではツールが用意されていて、スパナアイコンから Python 2 to 3 をタップすると自動変換してくれます。ただ、ssh コマンドは手直しが必要でした。

Python 2 のコードを開いた状態で Python 2 to 3 をタップすると変換してくれる

バージョン情報等

$ version
StaSh v0.8.0
Python 3.10.4 (CPython)
UI stash.system.shui.pythonista_ui
root: ~/Documents/site-packages/stash
core.py: 2023-05-05 18:00:40
SELFUPDATE_TARGET: master
Pythonista 3.4 (340012)
iOS 16.4.1 (64-bit iPhone10,2)
Platform iOS-16.4.1-iPhone10,2-64bit
BIN_PATH:
  ~/Documents/bin
  ~/Documents/stash_extensions/bin
  ~/Documents/site-packages/stash/bin

StaSh は、記事作成時に最新の dev バージョンです (StaSh インストール方法は別記事参照)

やったことひとまとめ

つど必要に応じて (変更箇所を反映させる等) Pythonista の再起動を実施しています。秘密鍵を使用した SSH の設定と、接続時の行数の指定例は過去記事をご参照下さい

  1. ~/Documents/site-packages/stash/bin/ssh.py を ~/Documents/stash_extensions/bin/ssh3.py としてコピー
  2. ssh3.py に対して「Python 2 to 3」を実行 (変更内容はそのまま)
  3. ssh3.py の関数 vk_tapped の内部 vk.name を全て int として処理するように書き換え (変更内容は下記)
  4. ~/Documents/site-packages/stash/system/shscreens.py に対して「Python 2 to 3」を実行 (変更内容はそのまま)
  5. shscreens.py の 541 行目と 576 行目それぞれにある /// に変更。変更後はこうなります: idx_line, idx_column = idx // (ncolumns + 1), idx % (ncolumns + 1)
  6. StaSh で ssh3 を実行

vk_tapped の変更後はこちら。Python 3 のコードに変換後の行数で、242~262 の部分になります。内容としては、vk.name を全て vk にし、'k_tab' 等となっていた部分を対応した数値に置き換えています。この変更により、StaSh のバーチャルキーボードで Tab や Up、CC 等をタップしたときに、SSH 接続先にも正しいキーコード (それぞれタブ、カーソル上、Control + C) が送られるようになります。

    def vk_tapped(self, vk):
        if vk == 7:
            self.send('\t')
        elif vk == 0:
            self.kc_pressed('C', CTRL_KEY_FLAG)
        elif vk == 1:
            self.kc_pressed('D', CTRL_KEY_FLAG)
        elif vk == 6:
            self.kc_pressed('U', CTRL_KEY_FLAG)
        elif vk == 9:
            self.kc_pressed('Z', CTRL_KEY_FLAG)
        elif vk == 2:
            self.kc_pressed('UIKeyInputUpArrow', 0)
        elif vk == 3:
            self.kc_pressed('UIKeyInputDownArrow', 0)

        elif vk == 10:
            if _stash.terminal.is_editing:
                _stash.terminal.end_editing()
            else:
                _stash.terminal.begin_editing()

出ていたエラーと解決のためのヒント

実際に ssh が動くようになるまでに行った手順としては、上のステップ 3 が最後になります。自分が最初にぶちあたったのは、ステップ 4の shscreens.py を Python 3 に変換した後で、エラーはこちらです:

system/shscreens.py", line 578, in load_pyte_screen
    c = pyte_screen.buffer[idx_line][idx_column]
TypeError: list indices must be integers or slices, not float

小数 (float) になっているのが問題と言うことなので、元の idx_line と idx_column を int() で整数にしてから呼び出されるようにしても解決せず、以下ページがヒント (とういか答え) となりました。

My guess would be that stash uses / division for line/column indices. On Python 3 / always produces a float. The fix is simple: replace it with flooring division //.

https://github.com/selectel/pyte/issues/123

ステップ 3 の変更を加える事になった原因のエラーはこちらです:

  File "stash_extensions/bin/ssh3.py", line 230, in vk_tapped
    if vk.name == 'k_tab':
AttributeError: 'int' object has no attribute 'name'

これは StaSh 自体のコードに関わる内容だと思うんですが、よくわかりません。ここをいじらなくても、SSH セッション自体は張れます。この関数の目的は、押された vk (=virtual key) に対応したキーコードを SSH 接続先に送るということです。なので、関数直下に print(vk) を差し込み、SSH 接続した状態で Tab やら CC やらを押して Console に表示される数字に name の値を置き換えていきました。美しい解決方法を探ったのですが諦めました。

さて、そもそも今回 ssh コマンドを動かしてやろうという動機付けになったのは、StaSh の説明の中にあった一文、特に色を付けた部分でした。

StaSh works with both Pythonista 2 and 3, though not all commands support python3. (訳: StaSh は Pythonista 2 でも 3 でも動きますが、全てのコマンドが python3 対応にはなっていません)

https://github.com/ywangd/stash

Image by Stable Diffusion

ここからは本文に関係ありません。今回のアイキャッチ画像は、Mac 用 Stable Diffusion クライアントの Mochi Diffusion で出力した画像に、後から文字を描き足したモノです。 Stable Diffusion を使った場合は、情報を記載するようにします。

Date:
2023年5月6日 22:31:24

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, a young man waring glasses, super happy

Exclude from Image:


Seed:
3826992198

Steps:
20

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

3年以上の時を経て、Pythonista 3 がバージョン 3.4 にアップデート (Python 3.10 サポート)

もう終わったのかと思っていたけど、、、うれしかった!

iPad で実行。Stash の version コマンドの出力。Python 3.10.4 になってます

Python 3 は 3.10.4 にアップデート。Python 2 は完全廃止

iOS で Python コードを書いて実行できるアプリ、Pythonista 3。3年以上の時を経て Python 3.10 対応となってアップデートされました。

Pythonista 3 ←App Storeへのリンク

詳しい変更内容は、以下のリリースノートを見てください。Google 日本語翻訳のリンクも貼っておきます。

https://omz-software.com/pythonista/docs-3.4/py3/ios/new.html

プラス要素だけでは無く、本バージョンでは Python 2.7 が無くなりました。なので、過去記事で書いた 2.x 環境用で動くスクリプトは、そのままでは実行できなくなりました。StaSh では ssh コマンドも動きません。

StaSh の話が続きますが、StaSh は有志の方々によって開発されている OSS のため、対応が追いついていません。やっとなんとかインストール・実行できるようになりましたが、エイリアスや環境変数を書き込める .stashrc ファイルや、複数のコマンドをセミコロンでつなげて一度に実行しようとしても、最初のコマンドしか実行されない状況です。

StaSh のインストール

StaSh をインストールして正しく動作させるには、一度 Pythonista 3 を削除することが推奨されています (StaSh 自体やインストール方法などは日々アップデートされているようなので、定期的に Github をチェックすることをお勧めします)。現在のところ、自分で試した感じでは dev バージョンをインストールするのが良さそうです。Console に以下をコピペして実行してください。

url = 'https://raw.githubusercontent.com/ywangd/stash/dev/getstash.py'; import requests as r; exec(r.get(url).text.replace('master', 'dev'))

その後、一度 Pythonista 3 アプリを終了後、This iPhone (Documents フォルダ) にある launch_stash.py を実行すると StaSh が立ち上がります。

Django のインストールと実行 (最新版は動かない)

Django は、試したところ version 4.0 ならインストールとテストページの表示までできました。バージョンを指定しないと 4.2.1 が入るのですが、そのバージョンでは openssl_md5 のシグナチャが見つからないというようなエラーで django-admin が動きません。

pip install django==4.0

ちなみに、StaSh に表示されるインストールログや、pip show Django を見ると 4.2.1 がインストールされているように表示されるのですが、Console で確認 ( import django してから print(django.__version__) ) すると 4.0 です。pip のバージョン表示はこれまで通り信用できないようです。

ともあれ無事インストールできたら、Pythonista 3 を終了して再度立ち上げ、プロジェクトを作ります。

django-admin startproject mysite

右にスワイプして This iPhone > mysite と進み、manage.py をタップしてコードを開きます。▷を長押しして Arguments に以下をコピペし、Run しましょう。noreload の左にあるのは、マイナス二つです。

runserver --noreload

赤文字の英語で「CommandError: You must set settings. ALLOWED_HOSTS if DEBUG is False.」と出た場合は、ここではとりあえず気にせず Pythonista 3 を終了・起動、そしてもう一度 manage.py を Run しましょう。iOS がネットワークアクセスの可否を聞いてきたら許可してください。

うまくいけば Console に諸々の注意事項と共に URL http://127.0.0.1:8000/ が表示されるはずです。こちらをタップするなり Safari にコピペするなりすると、はい、おめでとうございます、ロケットページが表示されます。 (このサイトには過去バージョンの Pythonista 3 の記事がいくつかあるので、参考に見てみてください)。

とりあえずのまとめ

最近 a-Shell という Unix/Linux ライクなシェル環境で Python 3.11 がいじれる iOS アプリを見つけていたのですが、Python がバックグラウンドで動き続けない (Django や Flask アプリは、ブラウザとアプリを行き来しないとページが更新できない) ことにがっかり (← いじっているうちになぜか解決しました)。そんな矢先に届いたのが Pythonista 3 のアップデートでした。個人的に新しめの Python ならではの機能はあまり使っていないのですが、これを機に知識のアップデートもしていこうと思っています。StaSh がキャッチアップするのにまだかかりそうではありますが、新たな発見があれば書きためていこうと思います。

Image by Stable Diffusion

ステップ数が少ないからかなり怖い画像になってますが、Mochi Diffusion の max 値である 50にすると、変にまとまってしまって自分のうれしい気持ちを表現しきれないので、最初に出力した画像をアイキャッチにしました。ゾッとした方、ごめんなさい。

Date:
2023年5月6日 14:35:09

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, people happy with a new release of software

Exclude from Image:


Seed:
3343127351

Steps:
20

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

ユニバーサルコントロールがいい感じ

Mac mini (M1) と MacBook Air (Intel 2019) を macOS 12.3.1 にし、iPad mini (第 5世代) を iPadOS 15.4.1 にアップデート。ユニバーサルコントロールを有効にして、ワンセットのマウスとキーボードで操作してみました。最初の感触としてはすごくいいです。3つの Apple デバイス+1つのモニタが、シームレスに操作できています。使い切れていない機器が再び使えるようになりそうですよ。

まずやることは、OS のアップデート、そしてユニバーサルコントロールの有効化

すでに macOS 12.3 と iOS 15.4 ではユニバーサルコントロールに対応していましたが、今回それぞれバグフィックスがされたバージョンをインストールしました。計測していませんが、macOS のアップデートは1時間くらい?iOS は 30分くらい?という感じです。どちらも再起動後、設定を有効にすれば使えるようになります。

そもそも完了しているとは思いますが、全てのデバイスが同じネットワーク内にいて、同じ Apple ID でサインインしている必要があります。その他の条件は Apple のページ (特にシステム条件) を確認してください。Mac と iPad それぞれの設定場所は以下の通りです:

macOS: アップルマーク > システム環境設定… > ディスプレイ > ユニバーサルコントロール … > 全部 (もしくは上から 2つ) にチェックを入れて、完了

接続された後、それぞれの位置を移動できます。左から、MBA、Mac Mini メイン、サブ、下に iPad mini。なんと、M1 搭載 Mac に標準の機能だけで 4画面!

iPadOS: 設定 > 一般 > AirPlay と Handoff > カーソルとキーボード (ベータ版) にチェック

iPad はここにチェックを入れるだけ

最初にできるのは横のつながりだけ?

Apple のデモを見ていたときには、あたかも置いてある方のデバイスのモニタにマウスカーソルが移動できるような雰囲気ではありましたが、さすがにそれぞれのデバイス同士で位置情報を交換しているわけでは無いです。ざっと試した感じでは、モニタの端にマウスカーソルを持って行ってから、さらに画面の外に持って行こうとすると、未接続のデバイスにカーソルが移動します。そうやって接続してから、Mac のディスプレイ設定で上のスクリーンショットのように気に入った場所に移動しましょう。下にも配置できます。iPad 画面の縦横も反映されるので、Sidecar より便利ですね。

MacBook Air (Intel) を生かせそうな予感

性能がかなり控えめで、たまーにリビングで Mac を使うときにしか稼働させることの無くなってしまった Intel CPU の MacBook Air (Retina, 13-inch, 2019) ですが、ユニバーサルコントロールのおかげで生き返りそうです。Mac mini と一緒に机で使うときは、3枚目のモニタとしてメールを開きっぱなしにしておけます。また、トラックパッドでページをめくれるので、リビングでは外付けモニタ的に iPad の Kindle で技術系書籍を開き、MBA でプログラムを書くなんて便利な使い方ができます。自分の用途では Sidecar より便利です (Sidecar は iPad を縦表示にできず、フルスクリーンでは使えない)。ただし、Kindle ではダブルクリックやドラッグで文字を選択することはできないようで、おそらく amazon 側の対応が必要な感じです。

ベータ版だからか、サポート外か?

ファイルのドラッグアンドドロップもできるということで、iPad のスクリーンショットをファイルに保存し、マウスで Mac のデスクトップに持ってこようとしましたができませんでした。できないどころか、Mac mini のディスプレイ設定から iPad mini が消え、MBA では見えているけど iPad mini の画面位置を移動できず、他のことをしていたらいつの間にか復活、という動作になりました。そもそも写真のドラッグアンドドロップは対応していないのかもしれません。使い続けていると、できそうでできないことが他にも見つかりそうです。少なくともデバイス間のテキストのコピペは問題なさそうです。

これは困る。iPad mini ではキーボード配列が日本語になってしまう

Mac mini M1 にインストールはできるものの、頻繁にクラッシュして使い物にならない Pythonista3 を iPad mini で起動していじってみました。使い慣れたキーボードで操作できるなんて最高です!と報告して本投稿を終了、と思ったのですが、なんだか様子がおかしい。どういうことかというと、キーボード配列が日本語 JIS 配列になってしまっていて、HHKB Pro 2 の英語キーボードではアンダースコアが入力できないのです。ググってみても、日本語キーボードが英語キーボードと認識されてしまうという情報は多いのですが、逆はみつかりませんでした。果たしてユニバーサルコントロールの問題なのか調べるために、iPad に USB-A コネクタを追加するアダプタでキーボードをつないだところ、結果は同じでした。日本語配列キーボードとして認識されてしまいます。

キーボード配列問題のワークアラウンド

本日確認できた唯一の解決方法は、iPad の使用言語を English にすることです。

iPad: 設定 > 一般 > 言語と地域 > iPad の使用言語 > English にして続ける

iPad のメニューが全て英語になるので、英語が苦手な方は注意

同じ言語と地域の画面の、言語を追加で英語を追加してもダメで、英語の優先順位を日本語より上にすると iPad の使用言語が English になり、上と同じ事に。なので、この記事を書いている現在では iPad 自体の使用言語を変えることがキーボードのレイアウトと合わせる唯一の方法のようです。いつかキーボードのレイアウト問題が解消するといいですね。

感想

Macbook + iPad のモバイル時の利便性や、M1 搭載 Mac でも擬似的にとはいえ 3画面 (以上) 使える使えるのはすごくいいですね。Mac 間では遅延や操作のしづらさもありません。無料の OS のアップデートだけで使えるので、複数の Mac や iPad を持っている方はいろいろ試してみたら良いと思います。ボクは以前購入した、iPad を Mac の外付けモニタにできる duet という 2,000円くらいしたアプリを削除する決心が付きました。最後にひとつ。どうやらユニバーサルコントロールで接続されていると、接続元 (?) がアクティブである間は接続先デバイスもアクティブであり続けるため、その分バッテリが減ってしまいます。モバイルで使用するときには、バッテリの使用状況に注意するようにしましょう。

Intel iMac から M1 Mac mini へ移行した記録 (読み物編)

娘のパソコンを iMac (Retina 4K, 21.5-inch, Late 2015) から Mac mini (M1, 2020) にしました。スペックや実際の移行方法など難しい話は別記事にします。こちらでは、読み物的な内容をまとめています。パソコン初心者 (自分の親や子供) にパソコンを買ってあげる、自分のパソコンの知識に自信は無いがなんか満足度が高いのが欲しい、昔から Mac っていいよなーって思っている、そんな方に読んでもらえるように書きました。

子供の iMac がくたびれてきた

小学生の娘は妻のお下がりの iMac を使っていたのですが、さすがに 6年も使っているとモニタの色味がおかしくなってきました。特に左右の端がピンクがかってきて、これは良くないな、と。娘が、新しいパソコンを買ってもらった友達と比べて自分のマイクラ動画はカクカクしている、とこぼすこともよく聞くように。さらに言えば、最近はなぜか Time Machine も上手にバックアップをとれなくなってきたし、Fusion Drive も単品 SSD と比較したらやっぱり遅い。そろそろ替え時かな、という思いが自分の中で大きくなってきました。

これまでは年代物で親のお古の Mac を 2台使わせていましたが、実際のところ一日の中で Mac を使う時間は家族で一番長いのです。主な用途のイラスト描きや YouTube 視聴は問題無いものの、マイクラにシェーダーやリソースパックを入れて Replay Mod で記録しながら QuickTime で録画するとか、編集した動画を書き出すとか、実は親よりもパソコンの性能を要求する使い方をしているのも事実。誕生日まで 3ヶ月ほどあるものの、安く手に入れられればそのタイミングで新しい Mac を買い与えることにしました。

Windows PC にしなかった理由

初期の動画は iMove を使っていた娘ですが、無料で高機能な DaVinci Resolve を少しかじり、現在はゆっくりムービーメーカー4 で YouTube 動画を作っています。同アプリは Windows 10 & 11 用のフリーウェアで、有料アプリの Paralles Desktop バージョン 16 に Windows 10 Home 環境を作り、その上で動作させています。他にもいくつか Windows 専用アプリを使用しているため、移行先も Windows が動くコンピュータである必要があります。娘の仲の良いお友達はみんな Windows PC ユーザです。親であるボクは完全 Windows 環境の会社の IT マネージャをやっているので、Windows PC という選択肢も無いわけでは無い。しかしそうしませんでした。いくつかある理由の一つは、娘が Windows OS の見た目をダサいと思っていて、macOS の方が良いと思っていたから。そしてもう一つは、娘以上にボクがそう思っているから。と、まぁダサいナウいの話は置いておいても、あまたある Windows PC の筐体やスペック、価格、サポート体制、評判などを調べ上げて最適なものを選定し、導入後は自宅でも Windows PC のサポートをしなければならなくなる、ということを考えると全く気分が乗らなかったからです。そんな自分を正当化するために、2週間ほどは Intel PC vs M1 Mac 的な YouTube を見まくりました。そして、性能、価格、発熱量、先進性などという要素を元に、Apple Silicon (M1 チップ) 搭載 Mac が最適であるという結論に至ったのです。Parallels Dekstop は早々に M1 チップ対応を謳っていましたし、逆に Windows で Mac 用アプリを動かすソリューションは知りません。

Parallels サイトより。確かに。

Mac mini M1 にした理由

Windows アプリを使用するための互換性から Intel Mac も考えなかったわけでは無いですが、Apple のラインナップからなくなることがわかっている製品を購入するよりも、これから各メーカさん・開発会社さん・OSS 開発者さん達による対応が進んでいくであろう M1 チップを搭載したコンピュータを子供に与えることに、何らかの意味があるように思えました。またちょうどこの頃 (2022年 2月)、Apple が近々発表する新製品の噂が各方面で記事にされていました。話半分で探ってみたところ、どうやらパワフルなものが中心になりそうで、Mac mini M1 がモデルチェンジしそうな感じもありません。歴代 Mac mini もモデルチェンジのが頻度が低かったですし、新機種登場による値下がりも期待薄です。また、娘は液晶タブレットを使ってイラストを描いているため、Macbook Air や Pro ではメインモニタが小さくて不便です (使っていた iMac は 21.5 インチ)。小学生なので持ち運んでドヤることもありません。こういった事から、本体は Mac mini M1 搭載モデルで決定しました。…といいつつ、当初は iMac M1 を第一候補として USB ポートのやりくりや設置場所の検討をしていました。娘が選ぶのは紫かな、水色かな、なんて思いながら必要な機器の金額を足していくと、どうしてもトータルが 27万円を超えることに。さすがにその金額はきびしいぞ、と。で、必要なハードウェア構成を考えてみると、キーボードとマウスは持っているし、使用中の iMac の 21.5インチ Retina 4K モニタは解像度を半分にしているので高解像度は必要ない。音は常にヘッドフォンで聴いているし、調べてみると Vtube 系アプリの顔認識には 720p のウェブカメラで十分らしい。さらに、会社の福利厚生の一環であるベネフィット・ステーションの Apple Store 割引は iMac M1 には使えないが、Mac mini M1 は対象ということが判明 (Apple Store に電話して聞きました)。ボクが使っている Dell の QHD 解像度モニタ なら 3万円台なので、トータルで 20万円かからない。ということもあり、Mac mini M1 となった次第です。

細かいことを知りたい人へ

もうちょっと細かいスペックも書いておくと、メモリは 16GB にしています。Parallels で Windows も走らせるので、16GB は必須です。SSD (ストレージ) は 1TB にしました。2TB は高すぎるし、動画を多く作る娘の用途では 512GB じゃ小さすぎます。安い外付けの SSD を追加するという使い方はある程度のコンピュータに関するリテラシや整理整頓スキルが要求されるので、現段階の娘には若干ハードルが高く、サポート (ボク自身の稼働) の頻度を下げるためにも 1TB としました。本体とは別になりますが、Time Machine によるバックアップ用に 2TB の外付け HDD を接続しています。コンピュータに詳しくない人がトラブルに遭う頻度と遭ってしまったときの損失を下げるための投資ですね。同じような理由で、キーボードとマウスも USB の有線接続です。原因不明の周辺機器認識しない問題に時間をとられないためには、有線接続の機器をお勧めします。

使用アプリの互換性を確認する

パソコンを新しくすると決めてから、娘が普段使ってるアプリの情報をもらいました。中身としては、ゲーム、イラスト関連、動画編集関連がほとんどで、Mac と Windows それぞれで使用しています。今の時代 Mac 専用のアプリは Apple 謹製のものくらいで、上でも書いた『ゆっくりムービーメーカー4』のような個人開発のアプリ (フリーウェア) や、大作ゲームはほぼ Windows 専用です。Mac 用アプリの動作状況を調べたところ、M1 チップに最適化されたもの、Intel 用に作られたものを M1 でエミュレーションして動作させるものの他、全く動かないものもあるということがわかりました。動かないものは 2つで、マイクラをリモートで一緒に遊ぶために使用していた VPN ソフトの Hamachi と、Mac から発せられる音とマイクから入力する音を同時に録音するために使用していた Sound Flower というツールでした。それぞれ代わりになるツールの存在が確認できたのでよしとし、実際に新しい Mac を購入してからインストールすることにします。

目的の再確認

今更ながら今回のプロジェクトの目的は、Mac 用マイクラ Java版をなめらかに動かすということにつきます。それ以外は親の都合やエゴと言って差し支えありません。で、肝心のマイクラ (正式名称: MINECRAFT JAVA EDITION) は M1 に対応しているのかというと…、未対応でした。確かに自分の Mac mini M1 で実行すると、Intel 用アプリとして動作しています。しかし調べてみると、MultiMC というランチャーを使い、Arm 用の Java を使用することで M1 ネイティブ動作させられることがわかりました。マイクラは Java という実行環境で動作するアプリのため、M1 用の Java で実行すれば良い、ということです。導入方法などの詳細は別記事にしようと思いますが、マイクラがぬるぬる動くということがわかったので、M1 搭載 Mac という選択肢は揺るぎないものになりました。その他のアプリ含め、動作状況は以下の通りです。

アプリ用途対応状況メモ
Minecraft Java Editionゲーム、建築、友達とマルチプレイ、YouTube 動画のネタ公式のままだと Intel のみ。MultiMC と macOS Arm 版 Java の利用でネイティブ有料。公式をダウンロードして遊んでも、全然大丈夫だが、M1 の恩恵は小さい
MultiMCマイクラを M1 ネイティブで動作させるM1 ネイティブ無料。これまで作ってきたマップや Mod、シェーダ (影 mod) も動く。設定はやや面倒
Robloxゲーム、友達と遊ぶ、チャット、動画のネタIntel無料。実際に試したが、十分遊べる
QuickTime Player画面収録 (録画)M1 ネイティブ無料。そりゃあ Apple 謹製ですから。macOS に同梱
OBS画面収録 (録画)まだ Intel無料。M1 対応版も開発中
Clip Studio Paint Pro (クリスタ)イラストM1 ネイティブ有料。amazon で購入
Live 2D Cubism Editor2D のイラストを Vtube アプリで動かせるようにするIntel無料版もありますが機能が限定的なため、我が家では学割を利用。小学生も在籍証明書を学校から発行してもらえば学割が利用できる
Parallels Desktop 16Windows を実行M1 ネイティブ (バージョン 16 以上)有料。ちょいちょいやってるセールがお勧め
Hamachiマイクラなどを友達と接続動作せず無料。VPN を簡単に構築できるソフト
ZeroTier同上M1 ネイティブ無料。オープンソース。Hamachi の置き換えに
Sound FlowerMac とマイクの同時録音のため動作せず無料。地声での実況系動画には必須だった
BlackHole同上M1 ネイティブ無料だがダウンロードには登録が必要。SoundFlower の置き換えに
Ladio CastMac とマイクの同時録音Intel無料。BlackHole と併せて使うことで、複数の音声入力を一つの出力先にまとめられる
Zoom友達との遠隔マルチプレイ中の音声チャット (映像も)M1 ネイティブ無料でも使える。1対1なら時間無制限。複数接続なら 1時間限定
Norton360アンチウィルス、統合セキュリティIntel有料。マイクラダウンロードサイトは怪しいところもあり、アンチウィルス系ソフトは必須
Wacom driver液晶タブレット Cintiq 13HD を使えるようにするM1 ネイティブ無料。妻が 1年ほど前に試したときは動きませんでしたが、今は大丈夫
Steamゲーム、Vtuber 的利用の FaceRigよくわからんSteam 自体は無料。FaceRig の必要性が無くたったため詳しく調べていない
KalidofaceLive 2D で作ったアバターをカメラから表情認識して動かすブラウザ依存?無料のウェブサービス。まばたきや顔の向きに合わせて動かすことができる
ffmpegマイクラの Replay Mod の録画をムービーとして書き出すM1 ネイティブ無料。Xcode と homebrew のインストールが必要

Windows 10 か Windows 11 か。Insider Preview しか選択肢は無いのか

Windows で絶対に動作させたいアプリは、ゆっくりムービーメーカー4とマイクラ統合版です。そのために動員する手持ちの有料アプリ (と OS) は、Parallels Desktop 16、Windows 10 Home、Minecraft Java 版、Minecraft Windows 統合版。できれば追加投資無しで、全てを使いたい。調べた結果は以下となりました。結論としては、Parallels Desktop は、バージョン 16を持っていればアップグレードの必要は無く、Windows は 11 Home の通常版をインストールでき、Windows 10 Home のキーでアクティベーションでき、マイクラ Java 版以外の使いたいアプリは動くことが確認できました。以下はそのまとめです。

アプリ用途対応状況メモ
Parallels Desktop 16Windows を実行M1 ネイティブ (バージョン 16 以上)ちょいちょいやってるセールがお勧め
Windows 10 HomeWindows アプリを実行マイクロソフトによる Arm 版 Windows 10 の提供が停止される←は Parallels のこちらを参照
Windows 11 HomeWindows アプリを実行Inside Preview だけじゃ無く、通常版も Parallels にインストールできる手持ちの Windows 10 Home のライセンスキーが使えるので追加投資不要
Minecraft Java 版Windows 専用 Mod を実行テストした限り、動かず…MultiMC で Arm 版 Windows 用 Java を使えばもしかして?
Minecraft 統合版Switch 版などとも遊べるArm 版が Microsoft Store からインストールして遊べた娘はマウスの動きが速すぎると言って、Windows とアプリ両方でマウスの感度を下げていました
ゆっくりムービーメーカー4YouTube 動画の作成Arm 版 Windows 11 で動いたArm 版 Win 10 での動作報告はいくつかありますが、Win 11 でも動作しました
UTAUボーカロイド的に音声合成で歌わせるArm 版 Windows 11 で動いた特に速くはなっていない感じ

Windows アプリの動作確認には M1 アプリ対応の確認よりも時間がかかりました。理由の一つは、日本のフリーウェアを M1 Mac + Parallels Desktop で動作させているような人の絶対数がそもそも少ないということ。情報が無いので、試してみるしか無い。それともう一つは、Arm 版 Windows 10 では x64 エミュレーションが行えなくなるような記事が見つかったことに起因します (ソースはこのあたり)。これはつまり、Arm 版 Windows 10 では Intel (x86 や x64) アプリが動かなくなるということです。マイクロソフトさんはその対応策として、Windows 11 へのアップグレードを推奨、ですって。さらにこちらの情報では Arm 版 Windows 10 の提供が停止されると言うことで、どのみち Windows 11 にする必要があるみたいでした。Windows エコシステムの良さは、古いアプリが新しい OS でもそのまま動くというところにあるとはいえ、Windows 11 の、しかも Arm 版でこれまでのアプリが動くのか。これは試してみないとわかりませんでした。結果は上の表の通り。マイクラ Java 版は Mac で使えるのでヨシということで娘には納得してもらいました。

アウトかグレーか半分か

ところで、Windows 11 と M1 Mac という点で調査を進めたところ面白いことがわかりました。おおっぴらにそのことに触れている大手メディア系サイトでは見つけられませんでしたが、通常版として使える Windows 11UUP dump というサイト手に入るのです。しかも Windows 10 の同じエディションのキーで本当の Windows としてアクティベーションできる。Parallels Desktop のインストール手順ページ含め大手のメディアでは Insider Preview 版 (要するに評価版) の Windows 11 をインストールできますよーとは表記していますが、評価版を娘に使わせ続けるのはトラブルがありそうで不安です。しかし通常版が使えるのであれば安心感は大きく向上します。UUP dump では海賊版の OS を配布しているわけでは無く、Microsoft 社が過去に公開したイメージをダウンロードし、インストール可能な ISO や VHDX 形式に変換するツールを提供しているので安心感があります。さらに、ボクと娘が持っている Parallels Desktop のバージョンは一つ前の 16 なのですが、16.5 以上であれば Windows 11 のインストールもできると。極めつけに、Yahoo ストアでは、Windows 10 や 11 のアクティベーションキーが数百円で買えることもわかりました。さすがに普通に amazon で買うと 1万円以上する OS が、メディア無しで文字列だけのアクティベーションキーまたはオンラインコードだけになったからと言って数百円になるというのはグレーかアウトか判断できません。不安な方は避けた方がいいと思います。ボクはというと、自己責任、怖いもの見たさ、ダメもと、人柱上等で 500円ほどの Windows 11 Home のキーを買ってみました。結果、Windows 11 のアクティベーションはできて、一ヶ月以上使えています。このあたりは覚悟のある方のみ試されたらいいと思います。

さて、やっと買う直前のフェーズに移行。とにかく安くしたい

使いたいアプリは M1 Mac でも、その上で動く Windows でも動作すると言うことが確認できたので、後は Mac 含めたハードウェアを安くという段階に入りました。Mac 以外に必須なのは、モニタ、キーボード、マウス、さらに我が家では web カメラとなります。特にこだわらない限り Mac 以外で高価なのはモニタですので、ここを安く抑えることがキーになります。だからといって値段だけで判断すると、品質や使い勝手で不満が出て、結局はより良いものを買い直すことになったりする危険もあるので、良い製品を安く買うということが重要になります。我が家では機能と価格のバランスが高い Dell モニタにすることにしました。ボクと同じモデルを探したのですが販売終了となっており、純粋な後継機はありません。QHD (解像度 2560×1440) で画面サイズは 23〜27インチ、USB Type-C 接続ができ、USB ハブとしても使える機種で探したところ、S2722DC が見つかりました。M1 Mac シリーズは USB ポートの少なさがネックなので、モニタがハブになっていくつかの USB 機器をつなげられるというのはコストカットになります。ボクの P2421DC と比較すると、解像度は同じなのに画面サイズが大きく、USB ハブとして使えるポートが 2つに減っていましたが、設置面積に問題は無く、評価を見るとP2421DC で問題のあった USB ハブ機能を使うためにケーブルの抜き差しが必要という問題も解消されているようで、こちらに決定です。Dell 製品は、納期に納得できれば直販サイトの価格が一番安いことが多いです (ビジネス向けしかり)。3月後半現在の価格は 46,536円になってしまっていますが、ボクは Dell 直販 20% オフの 34,110 円で購入し、納期は 2週間でした。同時期の amazon の価格は、2,000円割引が効いても 38,426円。プライムセールでも変化無しでした。ネットにはもっと安く買えたという情報もありますが、今は半導体不足による品不足や納期問題もあるようですし、35,000円程度で購入できれば十分いいところでしょう。間違って他のモデルを買ってしまわないよう、常にブラウザの左端のタブにページを開いておきました。

割引の効率

さて、最重要アイテムであり、最も高価でその分割引効果が一番大きいのはなんと言っても Mac mini M1 です。もちろんこれを買わなければ意味がありません。で、ベネフィット・ステーションの 3% 割引を使おうと思いつつ数日情報収集していたところ、とある方が YouTube で整備済製品の Mac mini を購入したとおっしゃっているのを聞きました。スペックはボクが選定した構成そのものです。これは盲点でした。amazon 含め、Apple Store 以外のショップではいわゆる吊るしモデルしか手に入らないのが普通です (「吊し、つるし」とはハンガーにつるされた洋服を買うように、自分好みにカスタマイズしない既製品を言います)。ボクはこれまで何年も、メモリやストレージのサイズを変更し、キーボードを US 配列にするというカスタマイズをしていたので Mac を整備済製品で入手するという考えに至りませんでしたが、確かに整備済製品は吊しとは違います (詳細は Apple 社のページ参照)。16GB メモリで 1TB の Mac mini M1 の筐体に傷が付いていたら、筐体だけ交換して販売されることもあるでしょう。というわけでその日から、日に数回 Mac整備済製品ページを覗きに行く日々が続きました。最初の頃は黒い Intel Mac mini や Intel Macbook Pro の大きな液晶モデルが現れては消えてという状況です。果たして娘の誕生日までに現れるのか、ま、最悪ベネフィット・ステーションもあるし、なんて思いながら 2週間ほど経ったある日の夕方、突然 iMac M1 が大量に現れました。多分全ての構成がそろっていて、カラーもいろいろ。もちろん 16GB メモリ 1TB SSD という構成のものもあり、パープルは無いけどブルーがある。じっくり見るとブルーの方が娘の好みに近い。キーボードは何が付いてるかわからないけど、どうせ今使っているものを使い続けるからどっちでもええわ。うーん、これ明日の朝には全部無くなってるかもしらん。今頃嗅覚の鋭い人たちや独立系メディアは祭りをやってるな。世の中がスペックやカラーに目移りしている間に絞り込みがほぼ終わっているオレがこのレースを制してやるぜ!

そして購入へ

整備済製品ページで iMac を見つけた時間に救われました。しらふの状態で十分冷静に検討することができたので。安くなっているとはいえ iMac M1 は 20万円を超えており、液晶タブレットを HDMI 接続するにはプラス 8千円ほどのアダプタも必要になります。キーボードなど、娘の用途には不要なものが多くその分高い。突然の出会いに胸が躍ったのは確かでしたが、見送ることにしました。我が家に向かい入れることはできない。その後 3日ほどかけて徐々に iMac は新しいユーザに買われて行きました。ひょっとしたら、いっせいに咲いた綺麗な花が徐々に枯れていくようだったかもしれません。しかしボクには確信めいた予感がありました。次に整備工場から出てくるのは Mac mini M1 に違いないという予感が…。

もったいぶりました

結果、狙ったとおりの Mac mini M1 16GB 1TB をポチれたのは、その数日後のことでした。確定するまで間違いが無いように何度もスペックを見直したのでずいぶん時間がかかりましたが、その間に売り切れることも無く、無事購入することができました。その流れで Dell のモニタを直販サイトでポチり、amazon ではお手頃な web カメラと念のための USB ハブをポチり、怒濤の勢いで購入作業を終わらせました。満足感と充実感。勝った感とやりきった感。Mac のリプレイスを決断してから 1ヶ月以上かかったと思いますが、機種の選定から購入までの一連のアクションがここに完結しました。

感想という体裁の余計な話

実際にモノが届いて接続したり、アプリのインストールや動作テストやデータの移行をしたり、そのつど娘の反応を見たり、というのがこの先続くわけですが、それら技術的にもまーまー細かい話は別の記事にまとめようと思います。なので、購入までの感想を書いてこちらの記事は終わりにします。

良かったこと

娘が親のおさがりじゃ無い現行の Mac を手に入れてからまだ 1ヶ月もたっていないのですが、忖度されないように極力さりげなく感想を聞いてみると「マイクラにキレイな影 Mod とリソースパック入れて録画してもぬるぬる動くようになったから最高」という反応が返ってきて安心しました。使う本人が最優先と思いながらも、実際は親の都合や趣味が多分に含まれたチョイスになっているわけで、娘が望んでいたことが実現できたのは素直にうれしい気持ちです。当初 Mac か Windows かで調査した時は、M1 がいくら速いと言ったって上位モデルの Intel/AMD CPU に性能はかなわないし、そもそもゲームが全然無いじゃないか、という趣旨の情報を多く目にしました。多くの Windows 専用アプリは Parallels で動くのですが、ハードウェアを酷使するゲームは動かないか、動いてもカクカクで遊べたものじゃ無いということです。ま、それはそうなんでしょうが、ファミコン世代・8 bit パソコン世代のボクにしてみれば、ゲームはゲーム機で遊ぶものであり、パソコンは何かを作る、実現するモノなんです。娘もゲームにはそれほど執着していないので問題ありませんでした。むしろ今後ゲームに傾倒することがあるとしても、Mac に向かっている間はゲームばかりにはならないということでもあり、クリエイティビティを伸ばしていきたい親としては好都合です。自分との違いも感じました。女の子だからなのかコンピュータのアーキテクチャやプログラミングに対する興味はおろか、カスタマイズして使いこなしたいという欲求も無く、最新の M1 チップ搭載!ということは娘には全く価値がないようです。でも、単に速いだけのパソコンというのとは違う、未来に期待できる M1 Mac を早い段階で与えられたことは、彼女のステキな未来に役立ってくれそうな、そんなワクワク感もボクに与えてくれています。ジョブズがこの世を去り、アイブは会社を去りましたが、M1 を始めとした Apple Silicon は、Apple 製品やコンピュータの世界を楽しいものにしてくれました。

結局どっちを勧めるか

お子さんをプロゲーマにさせてあげたい、ゲーム実況のスキルを伸ばしてあげたい、一緒に PC ゲームをしたい、という方は Windows の走る、Intel/AMD 搭載ゲーミング PC がいいと思います。もしそうではなく、上に書いた感想に共感できる方には、M1 を搭載したどれかの Mac をお勧めします。入門者向けには安いモデルでいいでしょう。まさに Mac mini は最適です。Macbook Air や Pro、iMac といった一体型は、液晶が壊れたら全て修理でその間は使えません。Mac mini なら本体さえ壊れなければ、その場しのぎの液晶モニタを買ってでも使い続けることができます。逆に、主な用途が変わればモニタだけ良いものに変えるだけで環境をアップグレードできるのです。モヤモヤするなら今日から整備済製品ページに足繁く通いましょう。

これも言っておきたい

最後に毒をひとつ。知らない情報や自分の想定を裏付ける・もしくは間違いだとわからせてくれる情報をブログや YouTube で入手しようと結構な時間を使いましたが、クソのような情報の多さに閉口しました。どこかから持ってきた情報を切り貼りして、スカスカの文章のあげくに「いかがでしたか」とまとめるフォーマットが多いですがその中には、ボクの記事をほぼ丸パクリしたものさえありました。日本の社会や人付き合いになじめず、ブログや YouTube で生活費を稼いで自立できたという人達には素直に良かったねという思いはあります。しかし、生活費を稼ぐためだけのペラッペラのクソ記事に自分の貴重な時間を使うことには納得できません。量産型クソブロガーや超薄型 YouTuber によってインターネットがウ○コ地帯になることはガマンなりません。少しでも多くのお父さんお母さんおばあちゃんおじいちゃんお姉ちゃんお兄ちゃんには、この世を、インターネットをより良いものにしていってくれる若い世代の育成にご尽力いただけたらと切に願うしだいでございます。また、日本語のソースだけでは偏りがあり、欲しいものにたどり着けない可能性が高いです。痛感しました。正しい判断をするには十分な情報が必要です。英語で、少なくとも読み書きができるスキルは必須であると思って間違いありません。TOEIC なら 750点程度です。苦手とか言っていたら世界から置いて行かれます。愛する人もしくは愛したい人がいるなら、とにかく英語スキルを磨いていきましょう。マイクラの mod や配布マップを導入するにも、英語は必須なのです。

ここまでお読みいただきありがとうございました。あなたの、そして未来を作る誰かのお役に立てたら幸いです。

© Peddals.com