4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は「 PostgreSQL Advent Calendar 2025 」の18日目です。

TL; DR

メリークリスマス!!!
日々データベースと格闘し、走される皆さん!お元気ですか!

たまにはデータベースに普段しないことをさせてみるのもいいじゃない?ってことで、
簡単なクリスマスムービーを流してもらいました。

こんな感じ 

※残念ながらキャプチャツールの都合上音が鳴らない
プレゼンテーション1.gif

使ったもの一覧

  • AlmaLinux9(VirtualBox+Vagrant)
  • PostgreSQL17.7
  • plpython
  • mpg123
  • alsa-util

音声協力

音源はオトロジック(CC BY 4.0)様からお借りしました。

ムービーの実装

ムービーの実装は先人の知恵を大いにお借りしました。
具体的には、以下のようなテキストファイルを用意して、plpgsqlプロシージャで
1画面22行ごとにSELECTして、RAISEから標準出力。これをループするという方法です。

表定義

postgres=# \d+ joke
                                                       Table "public.joke"
 Column |  Type   | Collation | Nullable |             Default              | Storage  | Compression | Stats target | Description
--------+---------+-----------+----------+----------------------------------+----------+-------------+--------------+-------------
 id     | integer |           | not null | generated by default as identity | plain    |             |              |
 frame  | text    |           |          |                                  | extended |             |              |
Indexes:
    "joke_pkey" PRIMARY KEY, btree (id)
Access method: heap

※id列は正直おまけです。LIMIT/OFFSETでSELECT条件を書くので、frame列だけでOKです。が、テキストファイル(AA)で記述すると、特殊記号が悪さするので、デバッグ用途で入れてます。

テキスト例

   +    ・   +         +    ・   +         +    ・   +      
       ゚。:。゚    +          ゚。:。゚    +          ゚。:。゚    +   
*   ・‥☆‥・       *   ・‥☆‥・       *   ・‥☆‥・       
    + 。゚▲゚。   *        + 。゚▲゚。   *        + 。゚▲゚。   *    
  +    ▲▲            +    ▲▲            +    ▲▲          
       ▲▲☆ +             ▲▲☆ +             ▲▲☆ +      
   * ▲☆彡▲    *     * ▲☆彡▲    *     * ▲☆彡▲    *  
十   ☆彡▲▲☆       十   ☆彡▲▲☆       十   ☆彡▲▲☆       
    彡▲▲☆∴▲  十      彡▲▲☆∴▲  十      彡▲▲☆∴▲  十  
   ▲▲☆∴▲▲☆        ▲▲☆∴▲▲☆        ▲▲☆∴▲▲☆     
  ▲☆∴▲▲☆彡▲      ▲☆∴▲▲☆彡▲      ▲☆∴▲▲☆彡▲    
`☆∴▲▲☆彡▲▲☆   `☆∴▲▲☆彡▲▲☆   `☆∴▲▲☆彡▲▲☆   
▲▲▲☆彡▲▲☆∴▲  ▲▲▲☆彡▲▲☆∴▲  ▲▲▲☆彡▲▲☆∴▲  
     __|_|__            __|_|__            __|_|__       
    ∥∥∥∥∥∥          ∥∥∥∥∥∥          ∥∥∥∥∥∥      
      ̄ ̄ ̄ ̄ ̄             ̄ ̄ ̄ ̄ ̄             ̄ ̄ ̄ ̄ ̄       
                                                ∩  ∩
                                                い_cノ  / ̄>O
                                              `c/・ ・っ (ニニニ)△△
                                              ("●" ) (・ω・`)[∥]
                                              O┳Oノ)=[ ̄てノ ̄ ̄]
                                              ◎┻し◎  ◎――◎=3
   +    ・   +         +    ・   +         +    ・   +      
       ゚。:。゚    +          ゚。:。゚    +          ゚。:。゚    +   
*   ・‥☆‥・       *   ・‥☆‥・       *   ・‥☆‥・       
    + 。゚▲゚。   *        + 。゚▲゚。   *        + 。゚▲゚。   *    
  +    ▲▲            +    ▲▲            +    ▲▲          
       ▲▲☆ +             ▲▲☆ +             ▲▲☆ +      
   * ▲☆彡▲    *     * ▲☆彡▲    *     * ▲☆彡▲    *  
十   ☆彡▲▲☆       十   ☆彡▲▲☆       十   ☆彡▲▲☆       
    彡▲▲☆∴▲  十      彡▲▲☆∴▲  十      彡▲▲☆∴▲  十  
   ▲▲☆∴▲▲☆        ▲▲☆∴▲▲☆        ▲▲☆∴▲▲☆     
  ▲☆∴▲▲☆彡▲      ▲☆∴▲▲☆彡▲      ▲☆∴▲▲☆彡▲    
`☆∴▲▲☆彡▲▲☆   `☆∴▲▲☆彡▲▲☆   `☆∴▲▲☆彡▲▲☆   
▲▲▲☆彡▲▲☆∴▲  ▲▲▲☆彡▲▲☆∴▲  ▲▲▲☆彡▲▲☆∴▲  
     __|_|__            __|_|__            __|_|__       
    ∥∥∥∥∥∥          ∥∥∥∥∥∥          ∥∥∥∥∥∥      
      ̄ ̄ ̄ ̄ ̄             ̄ ̄ ̄ ̄ ̄             ̄ ̄ ̄ ̄ ̄       
                                              ∩  ∩
                                              い_cノ  / ̄>O
                                            `c/・ ・っ (ニニニ)△△ 
                                            ("●" ) (・ω・`)[∥]=+
                                            O┳Oノ)=[ ̄てノ ̄ ̄] 
                                            ◎┻し◎  ◎――◎=3
                                            
 ・・・以下、繰り返し(今回は全部で858行のテキストファイルになりました)

プロシージャの定義

CREATE OR REPLACE PROCEDURE play_movie() 
AS $$
	DECLARE
		var_start integer :=0;
		var_cnt integer;
		var_end integer;
		var_height integer :=22;
		var_out text;
		var_interval integer :=1;
		scr_i integer := 0;
	BEGIN
		SELECT count(*) INTO var_cnt FROM joke;
                var_end:= var_cnt/22 -1 ;
		PERFORM '\pset format unaligned';
		FOR var_i IN var_start..var_end LOOP
			-- fetch data
			SELECT array_to_string(array(SELECT frame FROM joke LIMIT var_height OFFSET scr_i),CHR(10)) INTO var_out;
			-- clear display and write frame
			RAISE INFO '%', E'\x\033[H\033[2J' || var_out || chr(10);
			-- wait
			PERFORM pg_sleep(var_interval);			
			scr_i := scr_i + var_height;
		END LOOP;
		PERFORM '\pset format aligned';
	END;
$$ LANGUAGE plpgsql;

BGMの実装

plpythonを使ったファンクションを作成し、関数内でsubprocess.Popenを使ってOSコマンドであるmpg123経由で音声ファイルを鳴らします。なお、今回はmp3ファイルのためmpg123を使いますが、音声ファイルの形式に合わせてOSのツールを選んでください。

また、VirtualBoxを利用する場合は、仮想マシンの設定のところであらかじめオーディオ設定を正しくいじっておく必要があります。

OS上の音声カードの確認

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Intel [HDA Intel], device 0: STAC9221 A1 Analog [STAC9221 A1 Analog] Green Star 今回はコイツ
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: Intel [HDA Intel], device 1: STAC9221 A1 Digital [STAC9221 A1 Digital]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

plpythonの有効化

$ dnf -y install plostgresql17-plpython3-17.7-1PGDG.rhel9.x86_64.rpm 
postgres=# CREATE EXTENSION plpython3u;
CREATE EXTENSION

ファンクションの実装

CREATE OR REPLACE FUNCTION play_mp3_direct(file_path text)
RETURNS text
AS $$
import subprocess
import os
try:
    subprocess.Popen(['mpg123', '-a', 'hw:0,0', '-q', file_path]) --# ここでデバイスを指定
    return f"Attempting to play on hw:0,0: {file_path}"
except Exception as e:
    return str(e)
$$ LANGUAGE plpython3u;

ここまで出来たら、あとは各プログラムをpsqlで呼ぶだけです。

良いクリスマスを!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?