DjangoでデータベースSQLite3にmp3を保存してみた

※ 当サイトではアフィリエイト広告を利用しています。リンクは広告リンクも含みます。

この記事は約9分で読めます。
広告

文字列などをSQLite3に保存するとdb.sqliteファイルに保存される。mp3も同じようにdb.sqliteに保存されるのか?と予想していましたが。mp3を実際に保存してみるとmanage.pyが置いてあるルートのディレクトリにmp3ファイルとして保存されました。ずんだもんのmp3を動的に保存するアプリを作って確認しました。
作ったアプリがこちら
ずんだもんに励ましてもらおう!+ずんだもんに天気を聞いてみよう!(お名前.comVPS(有料)にデプロイ)
ずんだもんに励ましてもらおう!+ずんだもんに天気を聞いてみよう!(Renderにデプロイ立ち上がるまで数分かかります)
Renderの無料プランにデプロイしているので立ち上がるまで数分かかります。
またデータベースのデータが毎回なくなるので、
最初にmp3を持ってきて保存するのに50秒かります。
使い方は下を見てください

Renderの無料プランについてはこちらの記事を参考にしてください。

ずんだもんのmp3についてはこちらの記事を参考にしてください。

アプリのベースにしたのは、こちらのDjangoのRestFrameworksとVue.jsで作ったアプリです。

広告

mp3を動的に保存してみた

テキストに対するずんだもんの声のmp3を取得しています。ずんだもんの声のmp3を動的に取得する方法参照

situmon='お疲れーっす。ずんだもんです。'
url=r'https://api.tts.quest/v1/voicevox/?text='+urllib.parse.quote(situmon)+r'&speaker=1'
res = requests.get(url)
time.sleep(5)
d=res.json()
song=d["mp3DownloadUrl"]

songにはmp3があるurlが入っています。ChatGptに、どうやって保存すればいいか聞いた結果。

response = urllib.request.urlopen(song)

#responseはhttp.client.HTTPResponse object
#http.client.HTTPResponse オブジェクトは、Python の http.client モジュールによって提供される HTTP レスポンスオブジェクトです。HTTP サーバから受け取った HTTP レスポンスを表します。このオブジェクトには、ステータスコード、ヘッダー、本文など、HTTP レスポンスに関する情報が含まれています。                                

# レスポンスオブジェクトからファイルオブジェクトを作成します。
from django.core.files.base import ContentFile
file_obj = ContentFile(response.read(), name='my_song.mp3')
   
# ファイルオブジェクトをモデルに保存します。
song = Song()
song.title='モアちゃん'
song.audio_file = file_obj
song.save()

保存するモデルmodels.py
class Song(models.Model):
    song_id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    audio_file = models.FileField()
保存されたものをDjango REST frameworkのApi画面で見てみると以下のようになっています。
"audio_file"には保存先が保存され、実際のmp3ファイルはルート(manage.pyがあるディレクトリ)に保存されています。
{
        "song_id": 1,
        "title": "モアちゃん",
        "audio_file": "https://zundahage.onrender.com/my_song_GH087BQ.mp3",
    },
目次へ

フロントから読み出して音を鳴らす

フロント側(Vue.js)

フロント側のJavascriptは以下の関数を実行すれば声が再生されます。

function AudioFunc(){
    const m = new Audio('例 http://127.0.0.1:8000/test/songs/audio/2');
    m.play();
}

Django側

サーバー側(Django側)では、以下のように音声ファイルをデータベースから取り出しreturnすればいい。

データベースから読み出す場合
from django.http import FileResponse
song = models.Song.objects.get(song_id=id) 
return FileResponse(song.audio_file.open())

HTTP レスポンスオブジェクトを使う場合(file_obj = ContentFile(response.read(), name='my_song.mp3')を実行しないとき
from django.http import HttpResponse
return HttpResponse(response.read(), content_type='audio/mp3')
フロント側でjsonでDjangoのRestFrameworksに以下のようにリクエストするとデータベースに保存した"audio_file": "https://zundahage.onrender.com/my_song_GH087BQ.mp3",などのjsonが返ってくるのでうまくいきません。
function AudioFunc(){
    const m = new Audio('http://127.0.0.1:8000/test/songs/audio/2/?format=json');
    m.play();
}
またconst m = new Audio('http://127.0.0.1:8000/test/songs/audio/2');にした場合も、Django側で上のreturn処理にいくようにしないとDjango REST frameworkのApi画面が表示されてしまいます。
その辺は、こちらを参考にしてください。
目次へ

混合コンテンツ

音声ファイルをXfreeに保存して、そこから読み出して音声を鳴らすようにしてみると

audio.src = 'http://ichige2.html.xdomain.jp/zundahage/my_song_D9QtZRK.mp3';
audio.play();

以下のように混合コンテンツでエラーになります。

Mixed Content: The page at 'https://zundahage.onrender.com/article' was loaded over HTTPS, but requested an insecure element 'http://ichige2.html.xdomain.jp/zundahage/my_song_D9QtZRK.mp3'. This request was automatically upgraded to HTTPS, 

混合コンテンツについては↓↓

Firebase Storageに保存したものを読み込めばHTTPSなのでエラーにならないのでそうしました。
Firebase Storageについては↓↓

「ずんだもんに励ましてもらおう!」の使い方

Railway版は毎月21日以降は使えません。Render版は立ち上がるまで数分かかります。

  1. 初期化-テキスト登録を押してください。(Render版のみ)
  2. 初期化-音声登録ボタンを押してください。(Render版のみ)
  3. 初期化するので50秒お待ちください。(Render版のみ)あらかじめ用意したテキストをWEB版VOICEVOX API(低速)でずんだもんの声に変換して保存してます。
    (処理完了は確認していません。タイマーでセットしています。定期的に声がします。)
  4. 本文: (article_body) に読んでもらいたい名前を入力して名前登録ボタンを押してください。
  5. ずんだもん励まして!ボタンを押すと名前を呼んだあと励ましの言葉をかけてもらえます。5回。
  6. そのほか、本文: (article_body) に入力して「オーム返し(22文字以内)」を押すと読み上げます。

ページをリロードするとデータベースに保存した読み上げテキストと実際に保存されている音声のテキストが表示されます。実際に登録されている音声の1が名前で、6~10が励ましの言葉です。
処理がうまくいかず2重に入る場合があります。
目次へ

天気予報も追加

テキスト入力→ChatGpt API→ずんだもん音声としたいところですが、ChatGpt APIはお金がかかるので気象庁の天気予報JSONファイルをWebAPI的に利用したサンプルアプリを参考に「ずんだもんに天気を聞いてみよう!」機能を追加しました。
天気の聞き方は漢字の県名(栃木より西)を含んだ質問文であれば答えられるようにしてみました。AIではなくプログラムで処理しています。
参考サイトではヘッドラインというjson項目があったようですが、今、やったらそこは空欄になっていました。なので天気予報の詳細からヘッドライン的なところを22文字抽出して声にしています。
ローカルではうまくいくのですがRender上だと前の返答が返ってくることがあります。

あとがき

SQLite3の場合はmanage.pyがあるところにmp3が保存されている。
Django側にリンク先をデータベースに保存し、mp3は別のサーバーに置こうと考えましたが
例えばxfree(混合コンテンツで無理)やブログで使っているお名前.com のサーバーに保存してリンクをデータベースに保存。しかし動的には無理だしcorsにも引っかかる。サーバー側のcors設定できない。ということでこの形になっています。
posgresなどを使う場合settings.pyのmediaで保存先を設定するみたいです。(実施確認してない)
それと今回1番苦労した点は、同期に関することです。フロントからDjangoへの処理がコードの見た目と違って追い越して実行されたり、DjangoからAPI取得する前に先に進んだり。フロント側の処理は、こちらのYoutubeを参考にさせていただき順番に実行できるようになりました。【ワンピースで覚えるJavaScript】第15回 非同期処理 Promise/async/await(プログラミング入門講座)
しかし、まだ安定した動作はしていません。

目次へ

イチゲをOFUSEで応援する(御質問でもOKです)Vプリカでのお支払いがおすすめです。
MENTAやってます(ichige)

コメント

タイトルとURLをコピーしました