【Ruby】APOD APIで毎日更新される宇宙写真を保存する

# はじめに
NASAのAPIでAPODというのがあって、これが毎日宇宙写真を更新していると知り、デスクトップの背景に使いたいと思ったのがきっかけでした。

https://apod.nasa.gov/apod/lib/about_apod.html

https://apod.nasa.gov/apod/archivepix.html

### 完成形
1. 画像を指定したディレクトリに保存する
2. 指定した日付の宇宙写真のタイトルと説明文を表示する

2の出力はこんな感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/530a5d46-001a-8e6a-3354-f8581e310cfe.png)

### 前提・gemなど
`Windows10`
Ruby `3.1.1`
gem `nasa_apod`, `open-uri`, `date`

# 手順
## 1. NASAのAPIキーを生成する
APIキーを生成します。名前とメールアドレスを登録すれば取得できます。
回数制限があるので注意してください。1つのIPアドレスあたり、1時間に30リクエスト、1日に50リクエストまでです。

https://api.nasa.gov/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/aba4341a-c761-2d5d-ede1-f0554c879fa6.png)

## 2. 必要なgemをインストールする
プログラムファイルと同じ階層にGemfileを作るか、`gem install`で必要なgemをインストールします。下はGemfileを作ってインストールする方法です。
```ruby:Gemfile
source "https://rubygems.org"
gem 'nasa_apod'
gem 'open-uri'
```
ターミナルで以下のコマンドを実行します。
```bash
$ bundle install
```
## 3. APODについて
まずはAPODから画像を取得するプログラムを書いていきます。
```ruby:app.rb
require 'nasa_apod'

def nasa
  client_nasa = NasaApod::Client.new(api_key: ENV['NASA_API_KEY'])
  result = client_nasa.search
  return result.url
end
```
`ENV['NASA_API_KEY']`の部分は、自分のAPIキーに変えてください。(.bash_profileを作って環境変数を設定することもできます)<br>
`client_nasa.search`に引数を与えると、任意の日付で取得することができます。引数なしの場合は今日の日付になります。
例: 2022年1月1日を調べる場合
```ruby
result = client_nasa.search(date: "2022-01-01") 
```
<br>

`return result.url`で画像のURLを返しています。`url`を変えることでタイトルや説明文なども返すことができます。よく使いそうなものをまとめました。
|  | 意味 | 値の例 |
|:-:|:-:|:-:|
| `date` | 画像の日付を返す | `"2022-05-14"`  |
| `title` | 画像のタイトルを返す | `"Ice Halos by Moonlight"` |
| `explanation` | 画像の説明文を返す | `"An almost full moon on April 15...(省略)"`　|
| `url` | 画像のURLを返す | `"https://apod.nasa.gov/apod/`<br>`image/2205/LunarHaloComplexLabels1024.jpg"`  |

## 4. 画像を保存する
画像を保存するために使うのは`open-uri`というgemです。使い方は以下の通りです。
```ruby
require 'open-uri'

url = "<画像のURL>"
file = "<パス>.jpg"
URI.open(file, 'wb') do |output|
  URI.open(url) do |data|
    output.puts(data.read)
    puts "ok"
  end
end
```
`url`には画像のURLを代入します。<br>
`file`は画像を保存するディレクトリのパスと保存するときの名前を入れてください。jpgで保存するので拡張子も忘れずにつけます。パスは絶対パスでも相対パスでもいいと思われます。パスを指定せずにファイル名だけ指定すると、プログラムファイルと同じ階層に保存されます。
例）`"/Users/images/cosmos.jpg"`
<br>
`URI.open`は、Rubyのバージョンが3未満の場合`open`だけにしてください。
`wb`でバイナリ形式で画像を保存します。
`w`は指定したファイル名のファイルがなければ新しくそのファイル名のファイルを作り、すでに同じファイル名のファイルがある場合には上書きします。
`b`はバイナリ形式で保存してくれます。試しにこれをなしにすると画像ファイルを開けませんでした。

### ソースコード
完成したコードはこちらです。
```ruby:app.rb
require 'nasa_apod'
require 'open-uri'
require 'date'

def nasa(today)
  client_nasa = NasaApod::Client.new(api_key: ENV['NASA_API_KEY'])
  result = client_nasa.search(date: "#{today}")
  return result.url, result.title
end

today = Date.today - 1   # NASA(US)との時差のため昨日の日付にする
data = nasa(today)
url = data[0]
title = data[1]
if url =~ /jpg/
  file = "<パス>#{today}.jpg"
  URI.open(file, 'wb') do |output|
    URI.open(url) do |data|
      output.puts(data.read)
      puts "#{today} ok"
    end
  end
else
  puts "not jpg"
  puts url
end
  
puts "#{title}"
```
#### 今日の日付だと画像が更新されていない場合があることに気づいた
おそらくですが、NASAはアメリカ(ワシントンD.C.)の時間で画像を更新しています。`search`は引数がないと今日の日付になると書きましたが、この今日の日付というのは日本時間でのことなので、そのままだとまだ写真が更新されていない可能性があり、エラーになってしまうことがあるようです。
そこで、昨日の日付の画像を取ってくるようにしました。昨日の日付は`date`のgemを使っています。`today = Date.today - 1`で昨日の日付を代入しています。

#### 宇宙写真が画像じゃないときがある
毎日更新される宇宙写真ですが、動画形式のことがあります。動画形式は保存しないでおこうと思ったので、if文で`url`に`jpg`が含まれていれば保存、含まれていなければURLだけ表示するようにしました。これで、ターミナルに出力されたURLにアクセスすると動画を見に行くことができます。
```ruby
if url =~ /jpg/
  # 画像を保存
else
  puts "not jpg"
  puts url
end
```

#### 毎日保存するのでファイル名は日付にする
毎日更新される画像を保存するので当然ながらファイル名も毎回変えなければなりません(上書きされてしまいます)。ファイル名は単純に画像の日付にしました。
```ruby
file = "<パス>#{today}.jpg"
```
### デスクトップの背景に設定する
Windowsの設定から、「個人用設定」、「背景」と進み、保存した画像を背景に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/70fb182f-106d-0113-89b7-d26966d046ea.png)
スライドショーにして、画像を保存したフォルダを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/69385919-58cc-d18a-554b-7461c71b57cc.png)
これで保存した宇宙写真がデスクトップの背景になりました！(設定した間隔で画像が切り替わります)


## 6. 指定した日付の宇宙写真のタイトルと説明文を表示するプログラムを作る
このプログラムは先ほどと同じ階層に別ファイルを作って書きました。これを作ったのは、保存した画像を見ていて「この画像何を写しているんだろう？」と疑問に思い、日付からタイトルと説明文を表示できればいいなと思ったことがきっかけでした。(画像ファイル名は日付になっているので日付はわかる)

### ソースコード
完成したコードはこちらです。
```ruby:explanation.rb
require 'nasa_apod'

def nasa(day)
  client_nasa = NasaApod::Client.new(api_key: ENV['NASA_API_KEY'])
  result = client_nasa.search(date: "#{day}")
  return result.title, result.explanation, result.url
end

print "Year: "
year = gets.to_i                   # 年を標準入力
print "Month: "
month = format("%02d", gets.to_i)  # 月を標準入力(1桁の場合も2桁で)
print "Date: "
date = format("%02d", gets.to_i)   # 日にちを標準入力(1桁の場合も2桁で)

day = "#{year}-#{month}-#{date}"   # "YY-MM-DD"の形にする
data = nasa(day)         
title = data[0]                    # 画像のタイトル
explanation = data[1]              # 画像の説明文
url = data[2]                      # 画像のURL

year = format("%02d", year % 100)  # 年を下2桁だけにする

puts title                         # 出力: 画像のタイトル
puts explanation                   # 出力: 画像の説明文
puts "https://apod.nasa.gov/apod/ap#{year}#{month}#{date}.html"  # 出力: URL
```
日付を標準入力で受け取り、タイトル、説明文、URLを出力します。
URLで指定するページは、画像だけでなく、タイトル、説明文、ライセンスなどすべてが載っているページです。

↓ 例

https://apod.nasa.gov/apod/ap220514.html

説明文は英文なので、webページを開いてgoogle翻訳したいがためのURLの出力です😁

## できなかったこと
毎日画像が更新されるので、決まった時間にプログラムを実行する定期実行をしようと思っていました。clockworkを使ったのですが、フォアグラウンドでは問題なく実行されたものの、バックグラウンド実行がうまくいかず断念しました。(もちろん2つの実行コマンドが違うことは既知ですが、できませんでした。なんでだろう...?)

# さいごに
以上の手順で宇宙写真をローカルフォルダに保存することができました。PCを開くたびに美しい宇宙写真が表示されてテンションが上がります！

↓ 完成プログラムはこちら

https://github.com/yuzu-ginger/nasa_cosmos_images

## 参考URL
https://docs.ruby-lang.org/ja/latest/library/open=2duri.html
https://api.nasa.gov/
https://apod.nasa.gov/apod/lib/about_apod.html