OmniAICreator/Qiita-1.07M huggingface dataset:
Go言語(プログラミング)入門メモ③-3

## switch文で条件に応じた処理を実行する
if文以外にも条件に応じて別の処理を実行する方法がある。今回はそのような用途で使うswitch文について学習する。

## switch文を使用した条件ごとの処理
変数osの判定を行う。変数の値がmacのときはMAC!!と返し、windowsのときはWiindows!と非常時する。また、それ以外の場合はDefault!!と返す処理を作成したいときは以下のように書く
~~~
os := "mac"
switch os {
    case "mac":
    fmt.Println("MAC!!")
    case "windows": 
    fmt.Println("Windows!!")
    default:
    fmt.Println("Default!!")
}
//MAC!!と出力される
~~~

defaultはどの条件に当てはまらなかった場合される処理だが、この記載は必須ではない。記載を行わず、どのcaseにも当てはまらない場合は何の処理も行われない。

## 変数宣言とswitch文をまとめて書く
if文同様変数への代入とswitch文はまとめて1行で記載することができる。
getOsNameという関数を作り、返り値として文字列のmacを返すとする。この関数の返り値を変数osに代入する場合を考える。
~~~
func getOsName() string {
    return "mac"
}

func main() {
    os := getOsName()
    switch os {
    case "mac":
    fmt.Println("MAC!!")
    case "windows": 
    fmt.Println("Windows!!")
    default:
    fmt.Println("Default!!")
    }
}
//MAC!!と出力される
~~~
switch文以降で変数osを使わない場合は、変数への代入とswitch文を1行で書く。以下コードでは、switch文の後ろで変数に値を代入し、;でつなげてswitch文が判断する変数(os)を記載している
~~~
func getOsName() string {
    return "mac"
}

func main() {
    switch os := getOsName(); os {
    case "mac":
    fmt.Println("MAC!!")
    case "windows": 
    fmt.Println("Windows!!")
    default:
    fmt.Println("Default!!")
    }
}
//MAC!!と出力される
~~~

## caseに条件式を書くパターン
timeパッケージを使用して、現在の時間を扱うtime.Nowという関数を使ってみる。
~~~
import (
    "fmt"
    "time"
)

func main () {
    t := time.Now()
    fmt.Println(t.Hour())
}

//実行したときの現在時間が表示される。
例として11が表示されることとする
~~~
今までは変数osをswitchの条件式として書いていたが、今回は書かない。
1つ目のケースとして、Hourで取得した時刻が12時よりも前だった場合Morningを出力し、2つ目のケースとして17時よりも後だったらAfternoonを出力するような処理を書こうとすると以下のようになる。
~~~
t := time.Now()
fmt.Println(t.Hour())

switch {
case t.Hour()< 12:
    fmt.Println("Morning")
case t.Hour()> 17:
    fmt.Println("Afternoon")
}
//
11
Morning
と表示される
~~~
switch文に条件式を書かずに、caseに判定条件を書く、というパターンもあるということを抑える。







## 学習に使用した教材
・『入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』M.A EduTech
https://www.udemy.com/course/golang-webgosql/?utm_medium=udemyads&utm_source=bene-msa&utm_campaign=responsive&utm_content=top-1&utm_term=general&msclkid=81e2f24a32cc185d275d953d60760226&couponCode=NEWYEARCAREERJP

・『シリコンバレー一流プログラマーが教える Goプロフェッショナル大全』酒井 潤 (著)
https://www.amazon.co.jp/%E3%82%B7%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%90%E3%83%AC%E3%83%BC%E4%B8%80%E6%B5%81%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%8C%E6%95%99%E3%81%88%E3%82%8B-Go%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E5%A4%A7%E5%85%A8-%E9%85%92%E4%BA%95-%E6%BD%A4/dp/4046070897


### 正月坊主にとりあえず1日はならず良かったです。
【勉強法】Geminiに知らない単語を聞くときのコツ

こんにちは。G検定が1/11が迫り震えている27歳です。

こちらの記事では、Geminiに聞くときのコツをお伝えできればと思います。
超わかりやすい返答を、簡単にゲットできる方法を編み出してしまったのです。

それは・・・「りんごを使って例えて」と聞くというものです！

ぼくが今勉強してるG検定って、
単語の意味を理解しているかが問われる試験なので
勉強してたら全然知らない専門用語が山のように出てくるわけです。

それをGeminiに分かりやすく丁寧に解説して！って言っても
その解説に出て来る単語がまたわからん…となっていました。

そこで、ふとりんごを使って例えてもらったらあら不思議。
めちゃくちゃわかりやすい解説が百発百中（ぼく調べ）で返ってくるようになりました。

下記はそんな分かりやすかった返答の一例です。
ぜひ皆さんも真似してやってみてください(^▽^)/

Q：群平均法とは？デンドログラムが利用されるクラスタリング手法って何のこと？
わかりやすくリンゴを使った例えも含めて解説して

A：群平均法とは？リンゴで例えて解説
群平均法とは？
群平均法は、階層的クラスタリングという手法の中で用いられる、クラスター間の距離を計算する方法の一つです。クラスター分析とは、データの集合を、似ているもの同士でグループ化（クラスタリング）する手法の総称で、群平均法はその中のひとつです。

リンゴで例えてみよう
例えば、リンゴを様々な種類（例えば、ふじ、紅玉、ジョナゴールドなど）で集めたとします。これらのリンゴを、似ている種類同士でグループ化したいとします。

群平均法では、以下の手順でグループ化を進めていきます。

初期状態: 各リンゴを一つのグループ（クラスター）とします。
距離の計算: 全てのリンゴ同士の距離（例えば、大きさ、色、甘さなどの特徴に基づいた距離）を計算します。
最も近いグループの結合: 距離が最も近い2つのグループを1つのグループにまとめます。
新しい距離の計算: まとめられた新しいグループと、残りのグループとの間の距離を再計算します。この際、群平均法では、新しいグループに含まれる全てのリンゴと、他のグループの全てのリンゴとの距離の平均を、新しいグループと他のグループ間の距離とします。
繰り返す: 3と4を、全てのグループが一つになるまで繰り返します。
この過程を図で表すと、樹状図のような形になります。この図をデンドログラムといいます。デンドログラムの枝分かれする部分を見ることで、どのリンゴがどのグループに属しているのか、また、どのグループ同士が似ているのかを視覚的に確認することができます。

デンドログラムとは？
デンドログラムは、階層的クラスタリングの結果を視覚的に表すための図です。木の枝のように、グループが段階的に分かれていく様子を表します。
![デンドログラムの例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3969586/7f9003c8-e5d3-a7d9-3c78-6b2ef9ddd02d.png)

x軸: 各データ点（今回の例ではリンゴ）
y軸: データ点間の距離
デンドログラムの枝分かれする高さを見ることで、どの段階でどのデータ点が一つのグループにまとめられたのかが分かります。

群平均法の特徴とメリット
安定性: 全てのデータ点間の距離を考慮するため、外れ値の影響を受けにくく、安定した結果が得られる傾向があります。
バランスの良さ: 最短距離法や最長距離法と比較して、クラスターの形成が極端になりにくく、バランスの取れた結果が得られやすいです。

まとめ
群平均法は、階層的クラスタリングの手法の一つで、デンドログラムを用いて視覚的に結果を表現することができます。全てのデータ点間の距離を考慮するため、安定した結果が得られやすく、様々な分野で利用されています。
IT未経験で基本情報に合格した方法

はじめまして。こんにちは。


今回は未経験でも2ヶ月半で基本情報技術者試験に合格した方法を紹介します。





# 私の基本情報
・it未経験で現在は他の業界で働いている
・it業界へ転職予定
・itパスポート取得済み
・勉強時間は、平日2〜３時間、休日5〜6時間
でも、時間はあまり気にせず勉強してました。



基本情報は一回落ちていて、1ヶ月後にもう一度受けて2回目で合格しました。

itパスポートは受けてなかったらもう少し勉強が大変だったと思うので、私は受けて良かったと思ってます。（ちなみに勉強時間は1ヶ月くらいでした）


試験の点数は恥ずかしいですが、
1回目：科目Aが645点、科目Bが565点
2回目：科目Aが665点、科目Bが705点です。



# 使用した教材
以下の4冊を使用しました。
（他にも買いましたが、私には合わず使いませんでした）


## ①基本情報技術者試験対策テキスト
![IMG_2540.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967855/c8b96a2a-0aae-418a-9f01-1e47e306d8be.jpeg)



## ②情報処理教科書出るとこだけ！基本情報技術者［科目B］
![IMG_2537.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967855/232603ed-6d8a-a799-e963-2e90cec145dd.jpeg)



## ③基本情報技術者【科目B】アルゴリズム✕擬似言語トレーニングブック
![IMG_2538.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967855/4a88a266-7bad-98b9-40b2-fbd40d3b1740.jpeg)



## ④情報処理教科書出るとこだけ！基本情報技術者［科目B］予想＋過去問題集
![IMG_2539.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967855/57e3f979-5ff4-f930-6151-81e82ce1cabb.jpeg)



## その他
youtubeの講義動画
過去問道場




# 科目Aの対策方法
・理解度6割くらいでいいので、①の教科書見ながらyoutubeの講義動画みて、全体像を把握します。

・それと同時進行で、過去問道場でとにかく数をこなします。最初は5年分くらいは絶対解いて、余裕がある方は10年分解くといいと思います。

### POINT
私は試験を2回受けただけですが、結構過去問で見たことある問題が出題されました。

ただ正解を選べたと満足するだけでなく、他の選択肢についても解説を読むと点数アップにつながると思います。

科目Aはとにかく知ったもん勝ちなので、数こなして教科書振り返って、苦手なところ復習するの繰り返し作業で仕上げました。

①の教科書は辞書みたいなので、隅から隅まで覚えようとはしませんでした。一度やったところにはマーカーを引き、苦手なところは付箋を貼ったりしてました。



# 科目Bの対策方法
科目Aと並行して、科目Bも勉強始めました。

初めは、③の参考書のみ購入してニュアンスで勉強してました。トレースもあまり行わず、セキュリティの問題については、いけるかな〜と思って対策していませんでした。

私が1回落ちた原因はここにあると思います。

科目Bに受かるならまず、②と④の参考書を購入して、しっかりと基本の部分を押さえることが大事です。

この本ではとにかくトレース！って感じなのですが、一行ずつ書くことが本当に大事だなと思いました。

本番でも休憩時間の10分間でひたすら横線を書いてました。（この方法がいいかはわかりません）

これらの本を2周してから、③の参考書を使うといろんな問題への対策ができると感じました。

③の参考書はやや難しく、本番で出たら珍しいかな、っていう問題もありますが、基本がわかっていたら十分対応できますし、実力がつく一冊だと思います。

# おわりに
以上が私の勉強法になります。

誰かの参考になれば嬉しいです。
質問があればお答えします。

では、お元気で。
HTTP ヘッダーのメモ

# HTTPヘッダーとは
HTTPリクエストやレスポンスでクライアントやサーバーが追加情報を渡すことができる。
HTTP ヘッダーは、大文字小文字を区別しないヘッダー名とそれに続くコロン (:) 値で構成されます。値の前にあるホワイトスペースは無視される

# Content-Typeヘッダー
HTTPリクエストやレスポンスのボディのメディアタイプ（MIMEタイプ）を指定するために使用する。
これにより、クライアントやサーバーはデータの形式を理解し、適切に処理することができます。
```http
Content-Type: text/html; charset=UTF-8
```


例えば、CSVファイルをダウンロードする場合、サーバーはレスポンスのContent-Typeヘッダーをtext/csvに設定することが一般的。
これにより、クライアントはレスポンスがCSV形式のデータであることを認識する。

:::note warn
#### Content-Typeヘッダーを指定しない場合
サーバーやクライアントはデータの形式を自動的に推測するが、誤った形式で解釈される可能性がある。
これはXSSなどに対する脆弱性になる。
:::

#### サーバーがレスポンスのContent-Typeヘッダーを指定しない場合
クライアントはデータの形式を正しく認識できない可能性がある。その結果、ブラウザや他のクライアントがデータを適切に処理できないという事態になる場合がある。

#### クライアントがリクエストのContent-Typeヘッダーを指定しない場合
サーバーはデフォルトの処理を行う。
GETリクエストの場合、通常はContent-Typeヘッダーを指定する必要ないが、POSTリクエストなどでデータを送信する場合は、データの形式を指定するためにContent-Typeヘッダーが必要。


# MIMEタイプ（メディアタイプ）
HTTPヘッダーで指定するデータの種類の情報のこと。ファイルで言う拡張子みたいなもの。
Content-TypeヘッダーやAcceptヘッダー、Content-Dispositionヘッダーなどで指定する。


# Acceptヘッダー
クライアントがサーバーに対して、どのメディアタイプのレスポンスを受け入れるかを指定する。
```http
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
```

### Accesptヘッダー以外のメディアタイプをサーバが指定した場合どうなる？

以下のような状態が発生する可能性がある。

#### 1.クライアントはエラーを報告するか、レスポンスを無視する。

#### 2.サーバーがHTTPステータスコード406 (Not Acceptable)を返す。
サーバーがクライアントの Accept ヘッダーに一致するメディアタイプを提供できない場合、サーバーはHTTPステータスコード406 (Not Acceptable) を返すことが一般的。このステータスコードは、サーバーがクライアントの要求に応じることができないことを示します。
【Node.js】CSVファイルをダウンロードするAPIを構築

### package.jsonを作成

```bash
npm init -y
```

### 必要なパッケージをimport

```bash
npm install express csv-writer

npm install --save-dev typescript @types/node @types/express ts-node
```

### tsconfig.jsonの作成

```json:tsconfig.json
{
	"compilerOptions": {
	  "target": "ES6",
	  "module": "commonjs",
	  "outDir": "./dist",
	  "rootDir": "./src",
	  "strict": true,
	  "esModuleInterop": true
	}
  }
```

### サーバーファイルを作成
```ts:src/index.ts
import express from "express";
import { createObjectCsvWriter } from "csv-writer";
import path from "path";
import fs from "fs";

const app = express();
const port = 3100;

app.get("/download-csv", async (req, res) => {
  try {
    const csvFilePath = path.join(__dirname, "output.csv");
    const csvWriter = createObjectCsvWriter({
      path: csvFilePath,
      header: [
        { id: "id", title: "ID" },
        { id: "name", title: "Name" },
        { id: "email", title: "Email" },
      ],
    });

    const records = [
      { id: 1, name: "John Doe", email: "john.doe@example.com" },
      { id: 2, name: "Jane Doe", email: "jane.doe@example.com" },
    ];

    await csvWriter.writeRecords(records);

    res.download(csvFilePath, "output.csv", (err) => {
      if (err) {
        console.error("Error downloading the file:", err);
        res.status(500).send("Error downloading the file");
      } else {
        fs.unlinkSync(csvFilePath); // ダウンロード後にファイルを削除
      }
    });
  } catch (error) {
    console.error("Error processing the request:", error);
    res.status(503).send("Service Unavailable");
  }
});

app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

```

### サーバ起動
```bash
npx ts-node src/index.ts
```
【Laravel】Webアプリ開発について②

今回は前回に引き続き、Webアプリの開発について記事を書いていこうと思います。


ルーティングとは？
LaravelのルーティングはHTTPリクエストのパスや内容に対応するコントローラに処理を渡します。

# リソースコントローラー（7つまとめて作成）について
7つのメソッドをまとめて作ることができます。
7つのURLとまとめて作るメソッドをつくります。


```
php artisan make:controller ContactFormController ̶--resource
```
「--resource」をつけることで、7つのメソッドをまとめて作れます。

下記パスで7つのメソッドがひな形で作成されます。
```
app/Http/Controllers
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929458/cdba7d90-9acf-119d-c443-df7857c316be.png)


次に、コントローラを指すリソースルートを登録します。

```routes/web.php
use App\Http\Controllers\ContactFormController;
```

「Route::～」SAFARIルートの7つまとめてってこどでリソースというメソッドがあります。
そこに、「contacts」フォルダを記載し、第2引数がコントローラーの名前ですので、「ContactFormController::class」を記載します。
```routes/web.php
Route::resource(`contacts`, ContactFormController::class);
```

web.phpに記載が終わったら、コマンドプロンプトに戻り、listのコマンドをたたきます。
結果にURLが7つ増えていることが確認できます。
```
■コマンドプロンプトにて
php artisan route:list
```

# リソースコントローラー（1行ずづバージョン）について
前では7つのリソースをまとめて作成しましたが、
1行ずづ記載をすることも可能です。
```routes/web.php
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 
```
⇒Routeゲットでたたくフォルダ名を記載し、
フォルダ名をたたき使うコントローラーを指定します。
コントローラーの中のメソッドを書きます。
※「name」をつけるとroot情報に名前をつけることができ、ビュー側でリンクなどを張るときに便利です。

web.phpに記載が終わったら、コマンドプロンプトに戻り、listのコマンドをたたきます。
結果にURLが1つ増えていることが確認できます。
```
■コマンドプロンプトにて
php artisan route:list
```

# 同じようなファイル名が続いた場合
下記だと見にくいためグループ化して、記載することができます。

下記はグループ化していない状態です。
```routes/web.php
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 
Route::get('contacts', [ ContactFormController::class, 'index'])->name('contacts.index'); 

```

グループ化した状態だと下記のコマンドとなります。
```routes/web.php
// グループ化してまとめるとシンプルに書ける 
Route::prefix('contacts') // 頭に contacts をつける 
    ->middleware(['auth']) // 認証 
    ->name('contacts.') // ルート名 
    ->controller(ContactFormController::class) // コントローラ指定(laravel9から) 
    ->group(function(){ // グループ化 
        Route::get('/', 'index')->name('index'); // 名前つきルート 
});
```

#まとめ
リソースコントローラーには2つの記載方法があります。
まとめて記載できる方法と一つずづ記載する方法があります。
現場入った際には、これらを使いわけるとよいですね！
闇バイトを撲滅する方法

# 概要
「抽選で〇名様に，100万円あげます！ 応募方法はいいねとリポストで…」
「ホワイト案件 日当10万 詳細はDMで」

１つ目の投稿を，あなたはきっと「明らかな詐欺じゃん」と笑い飛ばすことでしょう．(もちろんこの形の詐欺が流行ったということは騙される人も一定数いるのでしょうが．)

２つ目の投稿を，あなたはこう思うのではないでしょうか？
「きっと犯罪絡みだ．闇バイトに違いない．」

そして愚か者はこう思います．
「お，これ報酬いいな．話聞いてみよう．」

1つ目の投稿は "嘘の" うまい話だと思った．しかし２つ目の投稿は "真実の" 犯罪への勧誘だと思った．この信頼性の違いが闇バイト，ひいてはインターネット上の犯罪を撲滅する方法につながると考えます．



# 闇バイト勧誘の信頼性
## 市民vs犯罪者
1つめのような投稿は，市民に対する犯罪者の攻撃です．情報の受け手となる市民を騙して金を稼ごうとする犯罪者の攻撃です．

一方で闇バイト募集や，麻薬の密売などは，情報の受け手も犯罪者側です．市民の目をかいくぐって犯罪をしてやろうという仲間なのです．仲間は騙す必要がない．だから情報には信頼性があります．

## 報道による信頼性
1つ目の様な投稿は，様々なメディアや[警察](https://x.com/MPD_yokushi/status/1818189299738870212)に「詐欺であり，犯罪の共犯にされる危険性さえある」と "嘘であること" を信頼できる情報源が保証しています．

一方で闇バイトについてはどうでしょうか．闇バイトは連日，闇バイトによる犯罪被害がメディアに取り上げられています．つまり "本当の犯罪求人募集" であることを信頼できる情報源が保証しています．

つまり犯罪してでもお金が欲しい人にとって，こういった投稿は「犯罪ノウハウのある組織と繋がれて，かつ仲間もできる」信頼性の高い求人情報なのです．

## うまい話には裏がある．ならば…
常識的に，うまい話には裏があります．ただより高い物はありません．

でも闇バイトはそうでしょうか？ 犯罪を行うというリスクがあります．うまい話ではなく報酬に見合ったリスクがある話なのです．だから信頼できます．

# どうすればいいの？
「インターネット上の犯罪情報の信頼性を地に落とす」これが即応性，汎用性のある有力な方法だと思います．2つ目の投稿の信頼性を，1つめの投稿のレベルまで落とすのです．

AIを使って架空の犯罪情報をばらまきます．
・闇バイトに応募したのに待ち合わせに誰もこなかった
・麻薬を買ったのに何も届かなかった

こういった経験が犯罪者に積み重なったら，「闇バイト？ あれほとんど嘘でしょ．全部AIだよ」となるんじゃないでしょうか？ N倍の偽情報をばらまけば，犯罪契約が成立する確率は1/Nに．そしてそもそも母数が0になっていくでしょう．

なんで犯罪者ばっかり詐欺広告だしてるの？ こっちからも攻撃してやろうぜ

## 「麻薬 売買 本物 見分け方」検索
この検索結果にでてくる，まとめサイトさえも，AIで偽造してしまいましょう．そうすれば誰が犯罪組織と接触できるでしょうか？ やくざ公式Lineとかですか？
### 言論統制ではないか？
確かに，政府がやればそれは言論統制でしょう．しかしインターネットや検索エンジンの管理者でもない，いちユーザーがやれば，だれがそれを言論統制というでしょうか？

むしろそれらサービスの正常ではない使い方をする攻撃者，不正ユーザーの類でしょう．サービスの正常性を保つための必要悪，悪の犯罪者vs正義の犯罪者という感じでしょうか．


## 警察について
闇バイトに対する法改正や，警察の方々の並々ならぬ努力によって，いずれは闇バイトは撲滅されるでしょう．しかし犯罪組織は新たな犯罪を考えついて実行するのではないでしょうか．そのような場合でも，この方法はきっと有効だと思います．
### サイバーパトロールについて
きっと警察組織との協力なしにこれを行えば，サイバーパトロールは破綻してしまうでしょう．偽の犯罪情報に暗号を含めるなどしてサイバーパトロールを邪魔しないような施策が必要です．

# なるほど、完璧な作戦っスねーーっ
不可能だという点に目をつぶればよぉ～～．ジョジョ読んだことないけど引用させてもらいました．

この方法に必要なものは
・AIがSNSと連携して架空の犯罪広告を行い，応募者を匿名性の高いメッセージアプリに誘導し，待ち合わせを指示する．この一連のタスクを遂行できるシステム

・上記を開発，維持する経済力

そして何より，これはビジネスになりません．これらの点を鑑み，イーロンレベルじゃないと無理かな．ひろゆき…(金を)いれてもらうことはできるか？

# 最後に
もし既に同じことを考えていた人がいたらごめんなさい．思い付きで書いてるので先行研究とか調べてないです…
【やさしい解説】動画配信の仕組み：m3u8とHLSストリーミングを理解しよう

こんにちは！この記事では、動画配信でよく使われる「m3u8」と「HLSストリーミング」について、わかりやすく解説していきます。

## この記事で分かること

- 動画配信の仕組み（HLSストリーミング）の基本
- m3u8ファイルの役割と中身
- FFmpegを使った動画のストリーミング配信方法

## m3u8って何？なぜ必要なの？

YouTubeやNetflixなどの動画配信サービスを使っているとき、動画が途切れることなくスムーズに再生されることに気づいたことはありますか？

実は、動画をスムーズに配信するために、動画ファイルは小さな断片（セグメント）に分割されています。そして、それらの断片をどの順番で再生すればいいのか、どの品質の動画を再生すべきかという情報を記録しているのが「m3u8ファイル」です。

m3u8は「UTF-8でエンコードされたプレイリスト」という意味で、Appleが開発したHLS（HTTP Live Streaming）という技術で使用されています。

## m3u8ファイルの中身を見てみよう

実際のm3u8ファイルの中身を見てみましょう。テキストエディタで開くことができます：

```plaintext
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:10.0,
segment000.ts
#EXTINF:10.0,
segment001.ts
#EXTINF:10.0,
segment002.ts
```

各行の意味を解説します：

- `#EXTM3U`: このファイルがm3u8形式であることを示します
- `#EXT-X-VERSION`: HLSのバージョンを指定します
- `#EXT-X-TARGETDURATION`: 各動画断片の長さ（秒）を指定します
- `#EXTINF`: 次の動画断片の再生時間を示します
- `segment000.ts`: 実際の動画ファイルの名前です（.tsという形式で保存されています）

## 品質の自動切り替えの仕組み

スマートフォンで動画を見ているとき、電波の状態が悪くなると自動的に画質が下がった経験はありませんか？これも実はm3u8の機能の一つです。

動画配信では、同じ内容の動画を複数の品質で用意しています。そして、それらをまとめた「マスタープレイリスト」というm3u8ファイルを作成します：

```plaintext
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
high/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720
medium/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=854x480
low/playlist.m3u8
```

このファイルには、高画質・中画質・低画質それぞれの動画への参照が含まれています。プレーヤーは通信速度を見ながら、最適な品質の動画を自動的に選択します。

## FFmpegで動画をHLS形式に変換しよう

それでは、実際にFFmpegを使って動画をHLS形式に変換してみましょう。

### 基本的な変換コマンド

```bash
ffmpeg -i input.mp4 -c:v h264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 output.m3u8
```

このコマンドの意味を解説します：

- `-i input.mp4`: 入力ファイルを指定
- `-c:v h264`: 動画コーデックをH.264に指定
- `-c:a aac`: 音声コーデックをAACに指定
- `-f hls`: 出力フォーマットをHLSに指定
- `-hls_time 10`: 各セグメントの長さを10秒に設定
- `-hls_list_size 0`: すべてのセグメントをプレイリストに含める

### 複数の画質を用意する

より実践的な例として、複数の画質を同時に生成するコマンドを紹介します：

```bash
ffmpeg -i input.mp4 \
  -filter_complex \
  "[0:v]split=3[v1][v2][v3]; \
   [v1]copy[v1out]; \
   [v2]scale=w=1280:h=720[v2out]; \
   [v3]scale=w=854:h=480[v3out]" \
  -map "[v1out]" -c:v:0 h264 -b:v:0 5000k \
  -map "[v2out]" -c:v:1 h264 -b:v:1 3000k \
  -map "[v3out]" -c:v:2 h264 -b:v:2 1000k \
  -map a:0 -c:a aac -b:a 192k -ac 2 \
  -f hls \
  -var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" \
  -master_pl_name master.m3u8 \
  -hls_time 10 \
  -hls_list_size 0 \
  quality_%v/stream.m3u8
```

このコマンドは、入力動画から3つの異なる画質（1080p、720p、480p）のストリームを生成します。

## 配信の設定

変換した動画を配信するには、Webサーバー（Apache、Nginxなど）で以下の設定が必要です：

1. MIMEタイプの設定：
   - .m3u8ファイル: `application/vnd.apple.mpegurl`
   - .tsファイル: `video/mp2t`

2. CORSの設定（必要な場合）：
   ```nginx
   location ~ \.(m3u8|ts)$ {
       add_header 'Access-Control-Allow-Origin' '*';
   }
   ```

## まとめ

この記事では、動画配信の基本的な仕組みについて解説しました：

- m3u8ファイルは動画の再生リストを管理するテキストファイル
- 動画は小さな断片（セグメント）に分割して配信される
- 通信状況に応じて自動的に画質を切り替えることができる
- FFmpegを使えば、簡単にHLS形式の動画を作成できる

より詳しい技術情報については、[元記事](https://tech.jugoya.ai/blog/understanding-m3u8-and-ffmpeg)をご参照ください。

## 参考リンク

- [FFmpeg公式ドキュメント](https://ffmpeg.org/ffmpeg-formats.html)
- [Apple HLS仕様書](https://developer.apple.com/documentation/http_live_streaming)
- [Video.js HLSプレーヤー](https://videojs.com/)
Spring SecurityのRequestMatcherをつかったアクセス制御

# はじめに
脆弱性の対応などで、特定のrequest(あるヘッダーの値が不正など)を受けつないようにしたい、といったことがあります。Spring SecurityにはRequestMatcherという仕組みがあり、こういったケースに対応しやすいです。その実装例の記事です。

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/web/util/matcher/RequestMatcher.html

# 実装例

次のような方針で実装します。

* 不正なリクエストにマッチするRequestMatcherを実装する
* それをつかって、SecurityFilterChainで403を返すようにする

```java
@Component
public class IllegalHeaderMatcher implements RequestMatcher {

 @Override
 public boolean matches(HttpServletRequest request) {
     return request.getHeader("sample").contains("bad");
 }

}
```

```java
@Bean
SecurityFilterChain accessDeny(HttpSecurity http, IllegalHeaderMatcher illegalHeaderMatcher) {
  http.securityMatcher(illegalHeaderMatcher)
         .authorizedHttpRequest(authz -> authz.anyRequest().denyAll());
  return http.build();       
}
```

RequestMatcherは複数をand/orで組み合わせこともできるので、複雑な条件にも対応しやすく、不正なリクエストの条件をRequestMatcherの形で切り出しやすいです。
フリーのデータセットを使ってダッシュボードを作ってみた②

## はじめに

今回は、企業における生成AI活用の実態を分析できるダッシュボードを作ってみました。
Microsoft Work Trend Indexの調査データを参考にサンプルデータを作成し、ChatGPTなどの生成AIツールの企業内での活用状況を可視化してみました。

## 作成したダッシュボードの概要

このダッシュボードでは、以下の3つの観点から生成AI活用状況を確認できます

1. 各AIのユーザー数と最も多い用途
2. 用途別の利用時間と生産性向上度の関係性
3. 用途別利用者数ランキング

また、学習方法と利用者のレベル関係も確認できるようにしてあります。

作成したダッシュボードはこちら
https://public.tableau.com/app/profile/ryotaro.nunokawa/viz/AI_17361543945450/AI#1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/6447929c-7bb0-e717-f49e-fee80202a88f.png)


## データについて

今回使用したデータは、Microsoft Work Trend Indexの従業員調査データを参考に、サンプルデータを作成しました

主な項目
- 利用用途（文書作成・編集、プレゼン資料作成など）
- 各用途の利用時間
- 生産性向上度
- 利用者数
- 学習方法別の利用者数
- スキルレベル別の利用者数

## 💡作成時のTips

### ① 散布図での見やすさの工夫

散布図で用途別の利用時間と生産性向上度の関係を表現する際、平均値を基準線として追加し、4象限に分けて表示しました。これにより、各用途の相対的な位置づけが分かりやすくしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/d8a1dfbd-c580-b172-8627-504daa315956.png)

### ② ボタン型フィルタボタン
フィルタ操作用のワークシート作成とフィルタアクションを組み合わせることでユーザが行うフィルタ操作をより直感的にしています。

詳しい実装方法の解説は先人の記事を拝借させていただきます。
【Tableau】ボタン型フィルターによるデータ切り替えのやり方：https://qiita.com/taka___/items/c55da30887d5e0838b9e

Point: デフォルトではデータが表示されずダッシュボード上部のボタンをクリックすることで各データが表示されるようにしている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/e2fc6c66-e30c-6c1d-f90c-5f054b1dbfba.png)


そのほかには年月データのフィルタに本Tipsを適用して

年のボタン群　[2022] [2023] [2024]
月のボタン群　[1月] [2月] [3月] [4月] ...

のように配置するのおすすめです。

### ③ 「空白」オブジェクトを使って無駄な操作を防ぐ

本ダッシュボードは基本的にボタン型フィルタ以外でユーザ操作はなく、ツールヒントで追加の情報を見てもらうことも想定していません。

そこで今回は、ユーザ側でダッシュボードの各部分をクリックして意図しないフィルタ等を行ってしまうことを避けるために、浮動の「空白」オブジェクトをユーザ操作が必要な部分以外に配置することでこれを防ぐようにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/f989e4f6-05e5-16b3-5cb5-d755a8d4c124.png)

## 参考資料

- Microsoft Work Trend Index
https://www.microsoft.com/en-us/worklab/work-trend-index/ai-at-work-is-here-now-comes-the-hard-part
---

**Tags:** `#Tableau` `#データ可視化` `#生成AI` `#ChatGPT` `#ダッシュボード`
フリーのデータセットを使ってダッシュボードを作ってみた①

## 1. 概要
データ加工力とtableau力強化のためにフリーのデータセットを用いてダッシュボードを作ってみました。

今回は「今夜見る映画を探せるダッシュボード」というテーマつくってみました。
サブミッションとして計算フィールドを手動で作成しない（データ分割等自動で生成される計算フィールドはOK）という縛りも設けています。

データソースは[MovieLens](https://grouplens.org/datasets/movielens/)から取得できる「MovieLens 1M Dataset」を使用しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/fb5e76c1-ae1d-5358-7058-fab27abcf827.png)


ユーザーの映画評価データを提供するデータセットで、映画のタイトル、ジャンル、ユーザーの評価などが含まれています。ダウンロードしたデータそのままでは少し扱いにくいので今回は日本語化やマスタデータとのマッピング処理を行ったうえで利用しています。


できたダッシュボードはこちら
https://public.tableau.com/app/profile/ryotaro.nunokawa/viz/_17361543883450/sheet0#1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/1d1391d6-0720-602d-4699-0fe5752bc119.png)


---

## 2. 工夫したポイント
### 2.1：パラメータアクションを動的なタイトル変更
画面上部のグラフで特定の年齢層やジャンル、セルを選択すると、左下のグラフのタイトルがその条件に合わせたもの更新されるようパラメータアクションを用いて設定しています。
左下のグラフのタイトルにパラメータを埋め込んでおり、パラメータアクションによってこの数値が切り替わることで動的に変更することができるという仕組みです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/8862bccb-e6d2-94bf-28ae-fd87040f7445.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/cd09a695-c067-567f-af73-a96759eecbf9.png)


### 2.2：書式設定を活用して凡例をなくす
色を使ったカテゴライズをしたり、複数メジャーを扱った場合、基本的には凡例が必要となります。
ですが今回はあえて凡例を作らずに「ダッシュボードタイトルの中にグラフ内で扱っている値がなんであるかを示すとともに文字の色を変えグラフとリンクさせる」ことで凡例がなくても直感的に内容がわかるようなレイアウトにしてみました。
※カテゴライズの量が多くなってきたりするとこの手法はあまり使えないと思うので注意

　
（例）映画公開数の文字とグラフの色を黄色にすることで情報をリンクさせる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/f9aff953-804f-49f8-f238-5dd8b32f86a5.png)


### 2.3：URLアクションを活用して情報にリーチしやすくする
2.1の内容に関連しますがURLアクションで指定するURLにはアクション対象となるグラフ内の情報を埋め込むことができます。今回は映画タイトルを埋め込むことでクリックするとその映画をgoogle検索できるようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/0f8b39ef-d0c8-4684-b4d4-16209fb1b4f4.png)


### 2.4：メジャー集計値を条件に従って分類
「評価（＝人気）が高い 年齢×ジャンルから探してみる？」では平均評価が3.9点以上のものについて青くハイライトされるようになっています。
これについては様々な実現方法があるかと思いますが今回は、メジャー（評価の平均値）で色分けを行いカスタム分化と「詳細」オプションを駆使して実現しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932741/b015167d-724d-abac-2713-238df198f31a.png)

グラフ形式でハイライト表を選択したのちマーク欄の「色」＞「色の編集」と選択すると画像のような画面が出てきます。ここで画像のように設定を行うことで先ほどの平均評価が3.9点以上のものについて青くハイライトするという要件を実現しています。

そのほかパディングを調整することでフィルタ欄がどのグラフに関係しているのかを分かりやすくするといった細かな工夫はしていますが今回はここまでにしておきます。

続きはまたの機会に
計算フィールド周りの改善によるパフォーマンス向上

ダッシュボードパフォーマンスを考えるうえで1つのポイントとなってくる計算フィールド周りの最適化について自身の勉強もかねて記事にしてみました

ダッシュボードのパフォーマンス確認方法は以下を参考にしてください
https://help.tableau.com/current/pro/desktop/ja-jp/perf_record_create_desktop.htm

## パフォーマンス改善の際に気を付けるべき点

### ①ネストされた計算を減らす
より複雑な集計などを実現しようとして入れ子が増えすぎてしまうケース。FIXED計算や集計関数とIF文などを組み合わさっていることがよくある

クエリパイプラインを意識してなるべく全体の計算量が少なくなるように分解するとよい

🚫 **悪い例**：
`{FIXED [Category]:SUM({FIXED [Product]: SUM([Sales])})}`

✅ **改善例**：
`{FIXED [Category],[Product]: SUM([Sales])}`

**Point!**
* 不必要なネストを避ける
* シンプルな計算を心がける
* 同じ結果が得られるなら、よりシンプルな方法を選ぶ

### ②複雑な文字列操作を行わない
不要な文字の削除やデータの表記形式をそろえるために様々な文字列操作を実施しているパターン

そもそも文字列関数全般はパフォーマンスが悪いため、基本的にはデータソース側で対応するのがよい

🚫 **悪い例**：
`LEFT(RIGHT(REPLACE([Full Name], " ", "|"),FIND([Full Name], " ")),LEN([Full Name]))`

✅ **良い例**：
`REGEXP_EXTRACT([Full Name], '[^ ]+ ([^ ]+)', 1)`

**Point!**

* 正規表現を活用する
* データインタプリターやタブの[変換]>[分割]などデフォルトで備わっている機能を活用
* データソース側で事前に処理できないか検討する

### ③LOD式の使い所に注意する

LOD式（Level of Detail式）は便利だが、使い方次第でパフォーマンスに大きく影響するので表計算機能とうまく使い分ける

チューニング時にはいくつかの実装方法に対してパフォーマンスモニタリングを実施したうえで、どちらの実装が速いかを検討する必要がある

**使い分けのポイント**：

表計算を使うべき場合：
* ランキング計算
* 移動平均の計算
* 合計に対する割合の計算

LOD式を使うべき場合：

* クロス集計が必要な場合
* フィルターの影響を除外したい場合
* 異なる粒度の計算が必要な場合

### ④関数レベルでのパフォーマンスに気を付ける
* `NOW関数`より`TODAY関数`のほうが早い
NOW関数は時間まで必要な際に、日付まででよい場合はTODAY関数を使うとよい
* 集計関数では`MIN関数`&`MAX関数` > `SUM関数` > `AVG関数` > `COUNTD関数` の順で速度が速い（左のほうが早い）
* `IF関数`のネスト構造は避け`CASE関数`で代用できる場合はこちらを利用する
* `ELSE IF` より `ELSEIF`と記載する（空白を開けない）

### あとがき

データソース側でのフィルタリングやクレンジングを実施することが効果的なケースも多いですが、上記のような部分についても意識してダッシュボード作成をすることでより高いパフォーマンスを発揮できるので覚えておこうと思います。
Flutterでメール送信時にインデントや空白が+(プラス)に置き換わってしまう

## この記事の想定読者
Flutterのwebの`url_launcher`パッケージを使用してメーラーソフトを起動する
空白文字が「+」として扱われてしまっていて困っている

## イメージ画像

![スクリーンショット 2025-01-06 17.59.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844247/9ed98bd4-51fe-0247-5369-bcbeeded1f53.png)

## パッケージのインポート
```shell
$ flutter pub add url_launcher
```
- yaml
```yaml
dependencies:
  url_launcher: 
```
## コード
### 良い例
```dart
    final bodyStr = '''
    メール本文です。
    スペース　があっても+に置き換えられません。
    ''';
    final email = 'John.Doe@example.com';
    final title = Uri.encodeComponent('title');
    final body = Uri.encodeComponent(bodyStr);
    final mailtoUri =  Uri.parse('mailto:$email?subject=$title&body=$body');
    if (await canLaunchUrl(mailtoUri)) {
      launchUrl(mailtoUri);
    } else {
      throw "Cloud not launch $mailtoUri.";
    }
```
### 悪い例
```dart
  final title = 'title';
  final bodyStr = '''
    メール本文です。
    スペースがある場合+に置き換えられてしまいます。
    ''';
  final uri = Uri(
    scheme: 'mailto',
    path: 'John.Doe@example.com',
    queryParameters: {'subject': $title,'body':$bodyStr}
  );

  if (await canLaunch(scheme)) {
    await launch(scheme);
  } else {
    print('problem on launcher');
  }
```


## 参考
https://www.mechengjp.com/%E3%80%90flutter%E3%80%91%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%81%E4%BF%A1%E3%82%84url%E3%82%92%E9%96%8B%E3%81%8F%E6%A9%9F%E8%83%BD%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95-url_laun/#%E3%83%A1%E3%83%BC%E3%83%A9%E3%83%BC%E8%B5%B7%E5%8B%95

https://github.com/dart-lang/sdk/issues/43838

https://github.com/flutter/flutter/issues/73717#issuecomment-816039811
unityでQuest3をPCVRでコントローラと手が表示されない ※犯人はUltraLeap

## 0.スペック
win11,metasdk72,Quest3,unity2022

## 1.症状
PCVR用にQuest3にunityから有線Linkで表示時に手とコントローラが表示されない

## 2.犯人はUltraLeap
UltraLeapのアプリがOpenXRのアクセスを独占してた。
そしてソフトを落としてもアクセス開放をしていなかった。
UltraLeapの設定アプリを起動し直して設定からOpenXRをOFFする。
これで治った。1週間かかった。

## 3.補足
UltraLeapはおなじみLeapMotionを買収吸収した会社です。
もとはUltraHapticsで超音波集束装置（空中触覚）の開発販売をやってたところ。
これ、40万するんですよ。（今回の元凶）
40万するのにティッシュすら浮かべられないんですよ。
その40万の機材を無駄に持っててたまたまインストールしてあって
機材繋げてないし、何ならアプリ落としてたのにOpenXRのアクセス掴んだままだったんですよ。

:::note info
神引用：https://zenn.dev/rintaro_fms9th/articles/79aeb9016bbae8
神引用2：https://discussions.unity.com/t/xr-hands-not-showing-with-quest-2-with-link-cable-due-to-leap-motion/933620
:::

##おまけ
editorでは相変わらず動かない。動くようになったのはビルド済みの物だけ。
高度情報情報処理技術者試験のPMの結果

<h2>高度情報情報処理技術者試験の結果</h2>
不合格だった

午前Ⅱ得点	68.00点
午後Ⅰ得点	50点

前回のITストラテジストと同様に午後Ⅰで落ちていた

<h2>振り返り</h2>
今回の試験の振り返りを書いていく

<h2>午前Ⅱ</h2>
今回の試験はアクティブリコールを使用して試験に臨んだ

おかげで、午前問題は完璧だと思っていたけれど、新規問題や計算問題に足を取られてしまい、この午前Ⅱの結果となった

アクティブリコールを実施した割には、あんまり成果に直結してない感じがある。単語とかはちゃんと覚えられている実感はあるものの、点数が取れてない。テスト前の過去問道場では8割キープを毎日していたのに・・・

午前問題は通ってるのでとりあえず結果的にはOKだが、苦手な計算問題で落としてる感があるのでしっかり対策していきたい

あと、過去問道場で見たことあるような問題ばかり出ていた。で、問題と解答が結びついた状態で覚えてしまっていた。数をこなすことを重視していて、固定設定にしてしまっていた

次は、ランダム設定で過去問道場をやる

あと、ランニング中に過去問道場を解いていて、複雑な計算問題とかはパスしていたフシがあるのでランニング中以外でも勉強する時間を確保するようにしたい

<h2>午後Ⅰ</h2>
前回と同様午後Ⅰで落ちていた
テスト前に午後Ⅱ対策に注力していて午後Ⅰを網羅できていなかった準備不足はあるものの、ショックである

とはいえ、テスト勉強中に午後Ⅰは自分で採点していても半分くらいしか取れいない印象はあった。上ブレればなんとかみたいな感じだった

勉強時間の時間的制約があって、そこら辺を解消できずに試験に突入してしまったのが敗因だった

テスト勉強段階でいい感じになれば本番でもクリアできるはず。だが、それがなかなかできない

理由として、過去問道場では午前Ⅱは4択で確実に回答があるものの、午後問題に関しては回答がふわふわなので次に生かしずらい→過去問で回数重ねても結果が思うように出ない

他の人はどうやってクリアしているのかを研究する必要がある

あと、時間配分がギリギリなので速度を出せるようにしたい

どうやったらクリアできるか分からなさすぎるので、困っている・・・

<h2>午後Ⅱ</h2>
午後Ⅱに関しては、採点すらされていない

良かったのか悪かったのかすら分からないので、振り返りようがないが、試験前の準備がギリギリだったことが反省点である

<h2>次回までにやること</h2>
・アクティブリコールは継続する→効果はあると判断
・過去問道場はランダム出題にする
・計算問題から逃げない
・午後Ⅰ対策に力を入れる→他の人のクリア方法を調べて試行錯誤する
・本を読む→文章読解が苦手かも？文章に触れる習慣をつける
・週スパンで振り返りを行う→言語化することで自分の勉強の悪いところをあぶり出す

<h2>まとめ</h2>
次回はITストラテジストがある。そこで結果を出したい
午後Ⅰで２回連続負けているので、午後Ⅰ対策をめちゃくちゃ重視してやっていければと思う

せめて、午後Ⅰで落ちないようにしていきたい。ボトルネックである

すでに年越ししてるので、準備期間モあまりなさそうだが頑張っていきたい
Amazon RDS for MySQLのリザーブドインスタンスを購入してみた（9万円も浮いたよ）

タイトルの通りですが、本番環境で動かしているRDSのリザーブドインスタンスを購入してみました。

# リザーブドインスタンス（Reserved-Instances 以下RI）とは
公式ドキュメントや多くの記事が既に存在するので詳しくは割愛しますが、
要は、RDS使用料を先払いor予約することで割安に使えるサービスのことです。
しかも、面倒な設定は一切不要で、コンソール画面上からポチポチクリックするだけです。

# どのくらい安くなったか
現在、本番環境で使用しているインスタンスは、db.m6g.largeです。
計算して表にまとめてみました。1年設定です。

| 1ドル=160円 | 前払い      | 月額       | 合計       | 割引率  |
|------------:|-----------:|-----------:|-----------:|-------:|
| 通常        |        0円 |   24,411円 |  292,934円 |   0.0% |
| 前払い無し   |        0円 |   16,912円 |  202,944円 |  30.7% |
| 部分前払い   |   96,640円 |    8,048円 |  193,216円 |  34.0% |
| 全て前払い   |  189,440円 |        0円 |  189,440円 |  35.3% |


なんと、前払いしなくても3割引きで利用できてしまいます。
金額にして年間9万円です。
前払いすると、そこからさらに1～2万円お得になります。

先払いに関しては、利用者それぞれの資金繰りがあるので一概に言えませんが、
現在（2025年1月）の情勢を鑑みても、円安一辺倒のご時世、
為替リスクも小さい（むしろ先払いすべき？）のではないでしょうか。

詳しくは調べていませんが、RIを購入したあと、
RDSのインスタンスを変更したくなった場合、変更後のインスタンスに対しても、
AWSがよしなに計算して割引してくれるっぽいです。

RDSのインスタンスを止める予定がほぼ無い場合は積極的に利用していくべきソリューションだと思います。
Terraformで管理しているAWS SageMaker Notebookインスタンスを、JupyterLab 4へバージョンアップする

先日、AWSからこのようなメールが来ました。

> お客様の AWS アカウントに JupyterLab 1 (プラットフォーム識別子 notebook-al2-v1) または JupyterLab 3 (プラットフォーム識別子 notebook-al2-v2) で実行されている Amazon SageMaker ノートブックインスタンスが 1 つ以上あることをお知らせしております。これらのインスタンスのリストは、AWS ヘルスダッシュボードの「影響を受けるリソース」タブにあります。
> 
> 2025 年 6 月 30 日に、Amazon SageMakerは JupyterLab 1 および JupyterLab 3 プラットフォームで実行されているノートブックインスタンスの提供を非推奨にする予定です。この日付をもって、お客様は新しい JupyterLab 1 および JupyterLab 3 ノートブックインスタンスを作成することも、停止したインスタンスを再度起動することもできなくなります。
> 
> 既存の稼働中の JupyterLab 1 および JupyterLab 3 ノートブックインスタンスは引き続き機能する可能性がありますが、セキュリティアップデートや重大なバグ修正は受けられなくなります。これらのインスタンスのセキュリティを管理する責任はお客様にあります。既存の JupyterLab 1 または JupyterLab 3 ノートブックインスタンスで問題が発生した場合、Amazon SageMaker は、お客様がワークロードを新しい JupyterLab 4 ノートブックインスタンスに移行しない限り、それらの継続的な可用性を保証できません。
> 
> 安全でサポートされている環境を確保するため、2025 年 6 月 30 日よりも前にワークロードを新しい JupyterLab 4ノートブックインスタンス (プラットフォーム識別子 notebook-al2-v3) に移行することを強くお勧めします[1][2]。これにより、最新バージョンの Jupyter Notebooks、JupyterLab、その他の ML ライブラリを活用できるようになります。

SageMaker Notebookのバージョンを上げるように、という案内のようです。
対象のインスタンスはTerraformで管理しているので、Terraformでの修正方法をご紹介します。

# 手順
既存のTerraformコードでは、プラットフォームバージョンを指定していませんでした。
ドキュメントを確認したところ、`aws_sagemaker_notebook_instance`リソースの`platform_identifier`で指定できるようです。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sagemaker_notebook_instance.html#platform_identifier-1

`platform_identifier`に、メール本文に記載されている`notebook-al2-v3`を指定してあげれば良いようです。

```tf
resource "aws_sagemaker_notebook_instance" "notebook" {
  platform_identifier = "notebook-al2-v3"
}
```

ここでplanを実行すると、バリデーションエラーが発生してしまいました。
> Error: expected value of platform_identifier to match regular expression "^(notebook-al1-v1|notebook-al2-v1|notebook-al2-v2)$", got notebook-al2-v3

確認してみたところ、`notebook-al2-v3`という値は、AWSプロバイダーのバージョン5.81.0から対応しています。

https://github.com/hashicorp/terraform-provider-aws/commit/51978ac481e9128a2a8e00e45905c437fcc8b397#diff-743d2f187e3630d0f5d7abe7d00cfe9fb0368a648311af448e478dcbf0d31b76R127

`5.54.1`を使用していたため、AWSプロバイダーのバージョンも上げる必要がありました。

```diff
@@ -2,7 +2,7 @@ terraform {
   required_providers {
     aws = {
       source  = "hashicorp/aws"
-      version = "5.54.1"
+      version = "5.82.2"
     }
   }
 }

```

これでplanおよびapplyが実行可能になりました。

## 注意点
上記で新しいバージョンのNotebookを作成することはできますが、既存のリソースのバージョンを上げることはできません。
もし既存リソースの`platform_identifier`の値を変更した場合は、その**Notebookインスタンスが再生成**されます。
```tf
-/+ resource "aws_sagemaker_notebook_instance" "notebook" {
      ~ platform_identifier          = "notebook-al2-v2" -> "notebook-al2-v3" # forces replacement
    }
```

plan実行時に`forces replacement`と出てきますので、見落とさないようにご注意ください。

# まとめ
AWSプロバイダーの対応バージョンのリリースは2024/12と、記事作成時点でまだ1ヶ月程度です。
そのため、まだバージョンアップが済んでいない方も多いのではないでしょうか。
既存のNotebookインスタンスの非推奨化は2025/06/30で、時間的猶予はそんなにあるとは言えません。
SageMaker Notebookを使用中の方は、AWSプロバイダーのバージョンアップの検証が必要な場合もあると思いますので、早めに対応を進めることをお勧めします。
ReactとSupabaseで学習記録アプリを作成しました【React / Supabase / Firebase / Jest / GitHub Actions】

# はじめに
今回は、前回作成した学習記録アプリに機能を追加したり、GitHub ActionsでCI/CDを初めて実施した時の気づきなどを記事にしたのでよければ読んでいって下さい。

https://qiita.com/tktn/items/e420e6a2f6d6d72da5ad

![画面収録 2025-01-05 19.51.45.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775597/6b2f1637-a1dc-91a9-6cae-26476fcecd0e.gif)


# 何をやったのか
このプロジェクトでは、主に以下のことを行いました。

- Supabaseを使ってバックエンドのデータベースを設定
- 学習内容の削除機能を実装
- Firebaseでのdeploy
- Jestでtest
- GitHub ActionsでCI/CD
- GitHubのRepositoryで表示するREADMEの作成


# 大変だったところや工夫したところ
## 大変だったところ
- Supabaseの設定とReactの連携
- CI/CD


## 工夫したところ
- 今回は、ChatGPTやClaude、GitHub Copilotを前回より使用し、AIに頼るところはAIに頼りました


# 気づきなど
- test時の非同期処理の理解が深まり、他のプロジェクトでも気をつけないといけないなと思いました


# さいごに
まだまだ初歩的なReactとSupabaseを使用したアプリ作成ですが、Supabaseと連携させたり、CI/CDを経験できたのでかなりアプリ作成をしている実感を得ることができて楽しかったです。
これからも、自分でサービスをリリースできる程までスキルアップできるように精進していきたいと思います。


# JISOUのメンバー募集中！
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？
興味のある方は、ぜひホームページをのぞいてみてくださ！
▼▼▼

https://projisou.jp
Bearer認証とJWT認証

# Bearer認証とJWT認証
## Bearer認証
Bearer認証は一時的なトークンをヘッダーに設定し、サーバーサイドでそのトークンが有効かどうかを確認することで認証を行う。
トークンはユーザーIDと紐づけられており、サーバーはトークンを基にユーザー情報を都度取得する必要がある。

## JWT認証
JWT認証は署名付きトークンを使用し、署名の検証が成功すれば認証OKとなる仕組み。
トークン自体に `key: value` 形式でデータを含められるため、ユーザー情報を含むことが可能。
Bearer認証とは異なり、トークンとユーザーIDをサーバーで管理する必要がなく、ユーザー情報取得や認証処理にかかるリソースを軽減できる。

# JWTペイロードについて
JWTのペイロード部分はただのBase64エンコードされた文字列なのでXMLでもTSVでも好きな形式を指定しようと思えば可能ですが、標準ライブラリでパースできないのでヘッダ解析を自作する必要があります。
基本的にJSON（辞書型）運用を想定しているのでよっぽど事情が無い限りは `key: value` 形式で問題ないと思います

# リクエストサンプル
## Bearer認証のリクエスト

HTTPリクエスト例

```
GET /api/resource HTTP/1.1
Host: example.com
Authorization: Bearer 49368cec-87b5-40f1-9194-03a10d41c659
Content-Type: application/json
```

CURL例

```
curl -X GET https://example.com/api/resource \
-H "Authorization: Bearer 49368cec-87b5-40f1-9194-03a10d41c659" \
-H "Content-Type: application/json"

```

※認証情報はヘッダに設定されるため、GET以外のMETHODでも同様です

## JWT認証のリクエスト

ペイロード

```
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}
```

HTTPリクエスト例

```
GET /api/resource HTTP/1.1
Host: example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Content-Type: application/json
```

CURL例

```
curl -X GET https://example.com/api/resource \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" \
-H "Content-Type: application/json"
```

※認証情報はヘッダに設定されるため、GET以外のMETHODでも同様です

# シーケンス図

## Bearer認証
```mermaid
sequenceDiagram
    participant Client as クライアント
    participant Server as サーバ
    participant Storage as ユーザー情報ストレージ（DB/Cognito）

    Client->>Server: ID/PASS（初回認証）
    Server->>Storage: ユーザー情報確認
    Storage-->>Server: ユーザー情報（OK/NG）
    Server-->>Client: Bearerトークン発行

    Note over Client,Server: Bearerトークンを使用してリクエストを送信

    Client->>Server: Bearerトークンを送信
    Server->>Storage: トークンに紐づくユーザー情報を取得
    Storage-->>Server: ユーザー情報
    Server-->>Client: リソースへのアクセスを許可

```

## JWT認証
```mermaid
sequenceDiagram
    participant Client as クライアント
    participant Server as サーバ
    participant Storage as ユーザー情報ストレージ（DB/Cognito）

    Client->>Server: ID/PASS（初回認証）
    Server->>Storage: ユーザー情報確認
    Storage-->>Server: ユーザー情報（OK/NG）
    Server-->>Client: JWTトークン発行

    Note over Client,Server: JWTトークンを使用してリクエストを送信

    Client->>Server: JWTトークンを送信
    Server->>Server: JWTの署名を検証
    Server-->>Client: リソースへのアクセスを許可

```
FastAPIでリンク先の動画の保存を行うAPIを作成

# 概要
FastAPIを使用して動画リンクをリクエストボディに受け取りそのリンク先の動画を保存するというAPIを作成してみました

# 本題

## FastAPIでリクエストボディを受け取る方法
今回はAPI実行時にリクエストボディ受け取るようにしたいので、PydanticのBaseModelをインポートしました

データモデルとしては`get_yt`としてリクエストボディにstr型を受け取れるように定義しています
また、リクエストパスとしては`/yt_download`というパスを宣言し、get_ytで宣言したモデルに沿ったリクエストボディを受け取って処理するようにします

以下のサンプルコードを実行すると、リクエストボディに指定したurlがそのままレスポンスされます

```py
import uvicorn
from fastapi import FastAPI  # FastAPIを使うために必要
from pydantic import BaseModel  # リクエストbodyを定義するために必要

app = FastAPI()


# 今回実施したいことのモデルをここで定義
class get_yt(BaseModel):
    url: str


@app.post("/yt_download")
# 上で定義したモデルをAPI実行時に受け取るように設定
def download_yt_video(jsonbody: get_yt):
    download_link = jsonbody.url
    return download_link


if __name__ == "__main__":
    uvicorn.run("main:app", reload=True)
```

<details><summary>出力結果</summary>

```sh
curl -sX "POST" "http://localhost:8080/yt_download" \
-d "{\"url\": \"https://www.example.com\"}" -H 'Content-Type: application/json'
"https://www.example.com"
```

</details>

ということで無事にリクエストボディの中身を受け取ってAPIの処理内で扱えるようになりました


## URLから動画ファイルをダウンロードする方法
以下の記事などを参考にして、今回は`yt-dlp`を使用して実装しました
(1)https://qiita.com/kuro_8193/items/31706b620d69993afd90
(2)https://qiita.com/shinkai_/items/7175215d7433b4cf150c

ダウンロード処理も記載したコードが以下になります
先ほどリクエストボディで受け取ってurlの内容をそのまま返却していた部分をダウンロード処理に変更しました

```py
import uvicorn
import re
from fastapi import FastAPI  # FastAPIを使うために必要
from pydantic import BaseModel  # リクエストbodyを定義するために必要
from yt_dlp import YoutubeDL

URL_PATTERN = re.compile(r'http.*')
ytdlp_opts = {"format": "mp4"}
app = FastAPI()


class get_yt(BaseModel):
    url: str


@app.post("/yt_download")
def download_yt_video(jsonbody: get_yt):
    try:
        download_link = jsonbody.url
        with YoutubeDL(ytdlp_opts) as ydl:
            if re.match(URL_PATTERN, download_link):
                ydl.download(download_link)
            else:
                raise Exception
    except Exception as e:
        print(e)


if __name__ == "__main__":
    uvicorn.run("main:app", reload=True)
```

# おわりに
こんな感じで初めてのFastAPIによるAPI作成はこんな感じになりました
FastAPIの名前に違わず、APIの作成が素早く完了してしまいました
これはいろいろなAPI作ってしまう理由もわかります

また何かしら思い浮かび次第、記事にしていければと思いますのでまた見に来てください

(Dockerファイルなど一連のリソースを確認されたい方、試したい方は[github](https://github.com/ryo0819/tools/tree/main/video_downloader)の方もご覧ください)
(また、参考にした記事にもありますが、YouTubeなどの動画は当然 **「複製して配布」** はNGですので取り扱いにはご注意ください、一切の責任を負いません)
デプロイしたら画面が真っ白だった / Failed to load module script

firebaseを使っています。
Reactで作成したアプリをデプロイしたところ、
成功したものの画面が真っ白に

### エラー内容
```
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/jsx". Strict MIME type checking is enforced for module scripts per HTML spec
```

### 原因
JSX ファイルを直接読み込んでいる

## 発生時の状況
firebaseでHostingを設定時、
ディレクトリを聞かれたのだけどよく分からず...
```
? What do you want to use as your public directory? (public)
```
参考記事もchatGPTも「build」だよ、と教えてくたので設定するも新規でindex.htmlが作成され、画面の表示もデフォルトのよう

![Welcome-to-Firebase-Hosting-01-05-2025_11_17_PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927903/1fb4d8d0-4f58-6372-1320-411cb711cd22.png)

表示が期待通りではないので、
今までローカルで確認していたルートディレクトリの`index.html`をデプロイ対象にするため
`firebase.json`の`public`ディレクトリを変更したら画面が真っ白になったのでした

## 解決のヒント
まずJSXを読み込んでいるのが原因で、
`npm run build` で JSXをJavaScriptファイルに直して作成してくれるそう
そこで生成されたフォルダをデプロイすればエラーは解決しそう

私の場合は `dist` フォルダにjsファイルが作成されていることが確認できました
## 結論
### `Vite` でビルドしたらディレクトリ先は `dist` にする
firebase.json
```json
{
  "hosting": {
    "public": "dist"
  }
}

```

上記でエラーは解決して、期待した通りの表示が確認できました！

## そもそもなぜ `dist` フォルダだったのか
以下はchatGPTから流用
ーーーーーーー
もし `create-react-app` 以外のカスタムビルドツール（例えば `Parcel` や `Vite`）を使用している場合、ツールの設定によって `dist` フォルダが使われている場合があります。
ーーーーーーー
ということでした。
ちなみに私はViteで作成しました。
GoogleとBingの検索インデックスからURLを削除する

# 概要

ウェブサイトのページを`Google`や`Bing`の検索結果から削除したい場合、各検索エンジンが提供するウェブマスターツールを使用してインデックスから削除することができます。

本記事では、`Google Search Console` と `Microsoft Bing Webmaster Tools` を使用して、特定のURLをインデックスから削除する手順を詳しく解説します。

## Google

`Google`、`Yahoo`、`Firefox`、`Safari`など、Google検索エンジンを利用しているブラウザでは、[Google Search Console](https://search.google.com/search-console) を使用してURLを削除できます。

以下の手順に従って操作してください。

### 手順①：左メニューの「削除」をクリック

[Google Search Console](https://search.google.com/search-console)にログインし、左側のメニューから「削除」を選択します。

### 手順②：「新しいリクエスト」をクリック

「削除」ページで、送信されたリクエストの一覧が表示されます。右上にある「新しいリクエスト」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/f5b4a4e5-da4a-ab22-0a91-605c257eeecf.png)

### 手順③：削除したいページのURLを入力

表示されたフォームのURL入力欄に、削除したいページの完全なURLを入力します。

### 手順④：「このURLのみを削除」にチェックを入れる

- **このURLのみを削除** にチェックを入れると、指定したURLのみが削除されます。
- **このプレフィックスで始まるURLをすべて削除** にチェックを入れると、入力したURLの下層ページもすべて削除されます。

### 手順⑤：URLを入力し、希望の項目にチェック

URLを正確に入力し、削除オプションにチェックを入れたら「次へ」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/54a4fef8-1a6e-dd16-bcac-8a97e57de836.png)

### 手順⑥：削除リクエストを送信

削除するURLに誤りがないか確認し、「リクエストを送信」をクリックして完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/43138b9d-8965-8575-5aa7-d768f5cfa937.png)

## Bingの場合

`Microsoft Bing Webmaster Tools`を使用して、Bingの検索インデックスからURLを削除する手順は以下の通りです。

### 手順①：左メニューの「構成」をクリック

[Bing Webmaster Tools](https://www.bing.com/webmasters/about?cc=jp) にログインし、左側のメニューから「構成」を選択して、サブメニューを表示させます。

### 手順②：「URLのブロック」をクリック

表示されたサブメニューから「URLのブロック」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/1b25c10c-71b1-b1ff-449d-a1b928c6d04e.png)

### 手順③：削除したいURLを入力

「URLのブロック」ページで、削除したいURLを入力欄に入力します。

### 手順④：URLタイプにて「ページ」にチェックを入れる

- **ページ** にチェックを入れると、指定したページのみが削除されます。
- **ディレクトリ** にチェックを入れると、入力したURLの下層ページもすべて削除されます。

### 手順⑤：ブロックの種類にて「URLおよびキャッシュ」にチェック

- **URLおよびキャッシュ** にチェックを入れると、URLの削除とキャッシュのクリアが行われます。
- **キャッシュのみ** にチェックを入れると、URL自体は削除されずキャッシュのみがクリアされますのでご注意ください。

### 手順⑥：「送信」をクリック

入力内容を確認し、「送信」をクリックしてリクエストを完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/27a50c93-52dc-8fff-4733-092430a5e7c4.png)

## 注意事項

- 削除リクエストが完了しても、ページが再度クロールされインデックスされる可能性があります。
- 恒久的に削除したい場合は、ページ自体の削除や`robots.txt`の設定変更、`noindex`メタタグの追加などを検討してください。

## 参考記事

こちらの記事を参考にさせていただきました。

https://roundsquare.design/blog/post_17/
Flutterデスクトップアプリでコマンドを起動する方法

## 初めに

FlutterでWindowsのデスクトップアプリを作成するとき、コマンドプロンプトやPowerShellでコマンドを実行する場面があると思います。
本記事では、その方法をまとめました。

Flutterにデフォルトで含まれているdart:ioパッケージのProcess.runを使用します。

## 環境

- 開発ツール: Visual Studio Code (VSCode)
- Flutter バージョン: 3.24.3
- OS: Windows 11以上
※Windows 10での動作は未確認です。

## 本題

前提として、すでにFlutterのProjectが作成されているものとします。

### コマンドの実行例
Process.runを使ってコマンドを実行する簡単な例を以下に示します。

```dart
try {
    // 実行したいコマンド
    final result = await Process.run('cmd', ['/c', 'flutter --version']);
    
    // コマンドの実行結果を表示
    print('code: ${result.exitCode}');//実行結果の状態
    print('stdout: ${result.stdout}');//実行成功時に返ってくるテキスト
    print('stderr: ${result.stderr}');//実行失敗時に返ってくるテキスト
  } catch (e) {
    print('Error: $e');
  }
```

結果の確認
- result.exitCode: 実行結果の終了コード（0が正常終了）
- result.stdout: 実行成功時の出力
- result.stderr: 実行失敗時のエラーメッセージ
これらを利用して、結果に応じた機能を追加することができます。


### PowerShellを使う場合
PowerShellを使用したい場合は、Process.runの第一引数にpowershellを指定します。以下はPowerShellを使用して特定のコマンドを実行する例です。


```dart
 try {
      // usbipd の状態を確認
      final result = await Process.run(
        'powershell',
        [
          'Start-Process',
          '-FilePath',
          'usbipd',
          '-ArgumentList',
          "'bind','--busid','$port','-f'",
          '-Verb',
          'RunAs'
        ],
        runInShell: true,
      );

      print('Exit Code: ${result.exitCode}');
      print('stderr: ${result.stderr}');
      print('stdout: ${result.stdout}');
.
.
.省略
```
上記の例では、選択したUSBデバイスをWSLに共有するためにusbipdコマンドを実行しています。

第二引数に入れる値は、https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.4
のStart-Processのやり方を参照してください。

## 最後に

すべてのコマンドが期待通りに動作するわけではありませんが、Windowsの機能をコマンドで操作できるのはとても便利です。

PowerShellについては完全に理解していない部分もあるため、良い資料があればぜひ教えていただけると嬉しいです。

## 参考

https://api.flutter.dev/flutter/dart-io/Process-class.html

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.4
【初学者向け】JavaScript学習において避けては通れないスコープと非同期処理のお話

## はじめに
JavaScriptを学び始め、特に覚えておきたいスコープと非同期処理についてまとめました！
自分も本格的に学び始め3ヶ月程度ですので、JavaScript初心者の方はぜひ！

## 変数のスコープ
**JavaScriptでは、変数のスコープ（有効範囲）に注意することが非常に重要です**。
変数のスコープを理解することで、予期しない動作を避けることができます。
varは関数スコープを持ち、letとconstはブロックスコープを持ちます。
<details><summary>サンプルコード</summary>

```
function testVar() {
    if (true) {
        var x = "varは関数スコープです";
    }
    console.log(x); // ここではxにアクセスできます
}

function testLet() {
    if (true) {
        let y = "letはブロックスコープです";
    }
    console.log(y); // ここではyにアクセスできません - エラーになります
}

function testConst() {
    if (true) {
        const z = "constもブロックスコープです";
    }
    console.log(z); // ここではzにアクセスできません - エラーになります
}

testVar();  // 出力: varは関数スコープです
testLet();  // エラー: y is not defined
testConst(); // エラー: z is not defined
```
</details>

varは関数全体で有効ですが、 **letとconstはブロック内でのみ有効です。**
最近はvarを使うことがあまりないため、letとconstだけ理解すればOKです。

## 非同期処理の理解
JavaScriptは非同期処理が得意な言語です。
>※非同期処理とは、時間のかかる操作（例えば、ネットワークリクエストやファイルの読み書き）をバックグラウンドで実行しながら、他のコードを止めることなく実行し続ける手法です。これにより、アプリケーションのレスポンスが向上し、ユーザー体験が向上します。

特に、コールバック、プロミス（Promise）、async/awaitの使い方を理解することが重要です。

### コールバック関数

**コールバック関数は、書き方がシンプルです。** ですが、複数の非同期処理には向いてない印象です。
<details><summary>サンプルコード</summary>

```
function fetchData(callback) {
    setTimeout(() => {
        callback("データが取得されました");
    }, 2000); // 2秒後にコールバック関数が実行されます
}

fetchData((message) => {
    console.log(message); // ここに「データが取得されました」と表示されます
});
```
</details>



### プロミス（Promise）

**プロミスは、非同期処理の流れを簡潔に記述しやすいです。** エラーハンドリングも明確にしやすいことが主な特徴となってます。
<details><summary>サンプルコード</summary>

```
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("データが取得されました");
        }, 2000); // 2秒後にプロミスが解決されます
    });
}

fetchData().then((message) => {
    console.log(message); // ここに「データが取得されました」と表示されます
});
```

</details>



### asyncとawait

**async/awaitは、非同期処理を同期処理のように記述できる**ため、コードの読みやすさを重視する場合にも最適かも。

<details><summary>サンプルコード</summary>

```
async function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("データが取得されました");
        }, 2000); // 2秒後にプロミスが解決されます
    });
}

async function getData() {
    const message = await fetchData(); // fetchDataが解決されるのを待ちます
    console.log(message); // ここに「データが取得されました」と表示されます
}

getData();
```
</details>

## まとめ
今回は、JavaScriptでつまづきやすいスコープと非同期処理についてまとめました。
これらを理解しないと、JavaScriptでよく起きるエラーに遭遇することが増えるため、きっちり把握しておきましょう！
MocapForAllを使ったVRChatフルトラを爆速で起動する方法

# TL; DR
 - 身体への装着物が不要なカメラフルトラは気軽でいいぞ
 - カメラを固定できるなら毎回のキャリブレーションは不要
 - カメラが2台なくても有線で接続できるAndroidスマホが2台あれば爆速起動可(iPhoneの情報求む)

# 対象読者
 - フルトラを試してみたけど面倒でやめてしまった人
 - 眠っているWebカメラやAndroidスマホをモーキャプに転用したい人
 - 安価でモーキャプしたい人

# 構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520610/38cd3981-37ab-ad79-7d8a-80eb62a7c5ce.png)


# 準備

## 必要なモノの用意・セットアップ
 - WindowsPC
 - SteamVR
 - Steam版VRChat
 - VMT
     - SteamVRにインストールし、セットアップが完了していること
 - MocapForAll
 - カメラ2台
     - 必要に応じてUSB延長ケーブルも
     - 複数のカメラをUSBハブでまとめるのは通信量に耐えられないのでNG
     - 同じ機種2台はMocapForAllが反応しないことがあるので避ける
 
 (以下、カメラをAndroidスマホで代用したい人向け)
 - Androidスマホ
     - ロック解除にパスワードを求めないようにしておくとGood
 - Iriun Webcam (Androidアプリ)
 - Iriun Webcam Server (Windowsアプリ)
 - adb
     - Iriun Webcam Serverをインストールしていれば`C:\Program Files (x86)\Iriun Webcam\adb.exe`が利用可能
     - PATHを通しておくこと

## MocapForAllの各種設定
カメラ固定後、[公式マニュアル](https://akiya-research-institute.github.io/MocapForAll-Manual/ja/)を参考にカメラのキャリブレーション等の設定を終わらせる
一度VRChatで動作を確認しておくとよい
確認できたら最後に「全カメラ設定を保存」しておくこと(個別のパラメータ保存はしなくてもよい)

## adbでデバイスシリアルを取得
Androidスマホを使うとき限定
使用するスマホをPCにUSBで接続し、USBデバッグを許可する
PC上で`adb devices`を実行してデバイスのシリアル番号一覧を取得
出てきたシリアル番号を適当なテキストファイル(ここではdevices.txtとする)に保存
複数のデバイスを使用する場合は1行に1デバイスを記入

## batファイル作成
 - Androidスマホを使う場合
```bat
@echo off

for /f %%s in (devices.txt) do (

	rem power-on device
	call adb -s %%s shell input keyevent 26
	call adb -s %%s shell input keyevent 82

	rem start webcam
	call adb -s %%s shell am start -n com.jacksoftw.webcam/com.iriun.webcam.IntroActivity

)

start PATH\TO\MocapForAll.exe StartCapture LoadAllCameras
start steam://rungameid/438100

echo "press any key to exit"
pause

rem stop webcam
for /f %%s in (devices.txt) do (
	rem stop webcam
	call adb -s %%s shell am force-stop com.jacksoftw.webcam
	
	rem power-off device
	call adb -s %%s shell input keyevent 26
)
```
`PATH\TO\MocapForAll.exe`は個人の環境に合わせて置き換え

 - Androidスマホを使わない場合
```bat
@echo off
start PATH\TO\MocapForAll.exe StartCapture LoadAllCameras
start steam://rungameid/438100
```
`PATH\TO\MocapForAll.exe`は個人の環境に合わせて置き換え

# 実際の運用
上記batファイルを実行すれば起動できる
終わる際にはVRChat・MocapForAll・SteamVRを停止
Androidスマホ使用時はbat実行環境にキー入力してあげればカメラを停止してくれる(これをやらないとカメラが常時ONとなってしまい電力を浪費する)

# Tips
## Androidスマホの電池持ちが怖い
BIOSやWindowsの設定でシャットダウン中もUSBに通電するような設定にすると常時充電できる

# 参考にさせていただいた記事
batコマンドについて、下記記事を参考に作成しました。

https://qiita.com/plcherrim/items/67be34bab1fdf3fb87f9

構成図作成にあたり、下記記事を参考にしました。

https://qiita.com/borerere/items/99d73d7295840a28c473
git revert と git reset の違いと使い分け

![スクリーンショット 2025-01-06 16.28.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576061/8505084c-a3fd-cbd1-5458-c2873a1aca82.png)

# revertとresetの違い

revertとresetは英単語的に同じ意味に思う人が多くて、混乱してしまいますよね。
しかし`git reset`と`git revert`は全く別物なのです。

下記のように５つのコミットがあったとします。
- コミット5
- コミット4
- コミット3
- コミット2
- コミット1

コミット3をrevert/resetした結果を見比べてみましょう。

## revert

revertした場合、コミット3の変更内容全てが戻されます。

## reset

resetした場合、コミット3が行われた状態まで戻ります。