Nintendo Switchの画像をひとつのディレクトリに集結させたい

おうち時間の増加によってさらなる売上を伸ばしているらしい[ニンテンドースイッチ](https://www.nintendo.co.jp/hardware/switch/)。

ゲーム画面のスクリーンショットをワンタッチで撮影することができるボタンを備えていて、撮影した写真をスマホに送ったり、直接SNSに投稿したりすることができます。

# 前提

## 画像のディレクトリ構成

画像は本体またはSDカードに保存されますが、SDカードをPCに差し込んで中を除くと以下のようなディレクトリ構成になっていることがわかります。
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59926/7e6b0552-7058-364d-88ab-856133c730de.png)
**年 / 月 / 日 / 年月日時間_xxxxxxxxx.jpg**

### メリット

- 画像が撮影日ごとになっているのは単純にわかりやすい
- 「ああ、この時こんなソフトで遊んでたなあ」と思い出に更けやすい

### デメリット

- 探したい画像がある場合に一覧で探せない（探しづらい）
- いちいち日付ディレクトリまでくだるのがめんどくさい

## 対象はSwitch二台分

僕はSwitchを二台持っているので、今回はそれぞれの画像ルートディレクトリを下記のような構成で配置しておきます。
![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59926/191a8ff1-a689-bdb1-b3c9-d9dfc78384bc.png)


# やりたいこと

下記のように、ひとつのディレクトリに全画像を集結させ、一覧性を高めたい！
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59926/7aad609f-ac43-7f0b-207a-e38bbff8712e.png)

## 方法

たぶんshellでもできるけど、なんとなくPythonを使ってみたい。
（Pythonならでは、じゃないのでPythonサイコー！とはなりません）

## ソースコード

初心者過ぎてコードを正しく解説できる自信はないし、何より「もっとこう書けるよ？」的な感じのご意見もあると思うのでさっさと結論を貼ります。

```python
import os
import glob
import shutil
from pathlib import Path

# ベースディレクトリ
base_dir = "/Users/hogehoge/Desktop/Game/Album"

# なければ出力ディレクトリを作成
output_dir = "/Users/hogehoge/Desktop/output"
Path(output_dir).mkdir(exist_ok=True)

# ファイルコピー関数
def func(input_dir):
    # **/*.jpgにより、サブディレクトリまで探索
    for i, img_path in enumerate(Path(input_dir).glob("**/*.jpg")):

        # 保存ファイルパス <output_dir>/<元のファイル名>.jpg
        save_path = Path(output_dir) / img_path.name

        # ファイルをコピー(img_path => save_path)
        shutil.copyfile(img_path, save_path)

        # なんとなく枚数カウント
        cnt =+ i
    print(cnt + 1) # 最後に枚数出力

if __name__ == '__main__':
    # ベース配下のディレクトリの絶対パスを取得(recursive = サブディレクトリまで見るかどうか）
    paths = map(os.path.abspath, glob.glob(base_dir + '/*', recursive=False))

    # ディレクトリだけにフィルタリングして、listで返す
    paths = list(filter(os.path.isdir, paths))

    # 1つずつ処理する
    for i, p in enumerate(paths):
        print('[%s / %s] Start %s' % (i + 1, len(paths), p))
        func(p)
```
## ちょこっとだけ解説（備忘録）

### Path("hoge/fugafuga")
```python
Path(output_dir).mkdir(exist_ok=True)
```
これにより与えられた文字列をディレクトリパスとして扱うことができるらしいです。

### \_\_name\_\_
```python
if __name__ == '__main__':
```
プログラミングでよくある冒頭のおまじない的な感じです。（importされた時の挙動制御みたいな意味合いがあるらしいです）

### ディレクトリのリストに対する操作諸々

```python
paths = map(os.path.abspath, glob.glob(base_dir + '/*', recursive=False))
paths = list(filter(os.path.isdir, paths))
```

最初の`map`関数で、`base_dir/*`にあるファイルやディレクトリの絶対パス(abspath)を取得して、次の`filter`関数で、`paths`の中からディレクトリだけ(isdir)を取得します。（返却はlist型）

今回は`base_dir`配下に、**Switch**と**Switch_lite**という二つのフォルダがあるので、最終的な`paths`は以下のようになりました（整形済み）。

```
[
  '/Users/hogehoge/Desktop/Game/Album/Switch_lite',
  '/Users/hogehoge/Desktop/Game/Album/Switch'
]
```

### enumerate(paths)

forループの中で使うとインデックス番号を取得できるらしいです。（以下の場合、iがインデックス番号）

```python
for i, p in enumerate(paths):
```

### glob

```python
glob.glob(base_dir + '/*', recursive=False)

Path(input_dir).glob("**/*.jpg")
```

同じ`glob`関数ですが、前者は`base_dir/*`配下のファイルやディレクトリ、後者は`input_dir`配下の`*.jpg`ファイルを再帰的に取得します。

ちなみに、前者は後者のような書き方でも大丈夫そうです。ただ、どっちがいいかはわからないです。

```python
Path(base_dir).glob("*")
```

### shutil.copyfile
```python
shutil.copyfile(img_path, save_path)
```
`img_path`を`save_path`にコピーするらしいです。`shutil`は読み方、エスエイチユーティルでいいのかな。

---

というわけで趣味の延長ですが、Nintendo Switchで撮影した画面キャプチャをもともとのディレクトリ構造からひとつのディレクトリに集結させるPythonを書いてみた話でした。