#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'em-websocket' require 'set' require 'webrick' require 'json' require 'uri' # WebSocket ポート WS_PORT = 8888 # HTTP ポート HTTP_PORT = 8889 # クライアント集合 clients = Set.new # 現在選択中の曲URL(簡易的にグローバル管理) selected_url = nil # === HTTPサーバー(音楽ファイルと JSON を提供)=== http_server = WEBrick::HTTPServer.new( Port: HTTP_PORT, BindAddress: '0.0.0.0', # ← これを追加! DocumentRoot: './public', AccessLog: [], Logger: WEBrick::Log.new("/dev/null") ) http_server.mount_proc '/music_list.json' do |req, res| files = Dir.glob("public/music/*.{mp3,wav,ogg}") .map { |f| File.basename(f) } .sort_by { |f| f[/^\d+/].to_i } # ← 番号の順に並べるよ! res['Content-Type'] = 'application/json' res['Access-Control-Allow-Origin'] = '*' res.body = { files: files }.to_json end http_server.mount_proc '/music/' do |req, res| path = "./public/music/#{File.basename(req.path)}" if File.exist?(path) res['Content-Type'] = 'audio/mpeg' res['Access-Control-Allow-Origin'] = '*' res.body = File.open(path, 'rb') { |f| f.read } else res.status = 404 res.body = 'Not Found' end end # HTTP サーバーをバックグラウンドで開始 Thread.new { http_server.start } puts "WebSocket server starting on port #{WS_PORT}..." puts "HTTP server running on http://localhost:#{HTTP_PORT}/" # Ctrl+C で両サーバーを終了 trap('INT') do puts "\n[INTERRUPT] シャットダウン中..." http_server.shutdown EM.stop if EM.reactor_running? end # WebSocket サーバー EM.run do EM::WebSocket.start(host: "0.0.0.0", port: WS_PORT) do |ws_conn| ws_conn.onopen do clients << ws_conn ws_conn.send({ type: "info", message: "うっす! WebSocket 接続完了" }.to_json) puts "[OPEN] クライアント接続: #{clients.size} 名" end ws_conn.onmessage do |msg| puts "[RECV] #{msg}" begin data = JSON.parse(msg) case data["type"] when "select" selected_url = "http://localhost:#{HTTP_PORT}/music/#{URI.encode_www_form_component(data["file"])}" clients.each { |c| c.send({ type: "select", url: selected_url }.to_json) } when "play" if selected_url clients.each { |c| c.send({ type: "play", url: selected_url }.to_json) } end when "stop" clients.each { |c| c.send({ type: "stop" }.to_json) } when "reset" # 🔽 リセット処理をここに追加 clients.each { |c| c.send({ type: "announce", queue: [] }.to_json) } when "raise" clients.each { |c| c.send({ type: "raise", name: data["name"] }.to_json) } else # 未知のactionはそのまま送信 clients.each { |c| c.send(msg) unless c == ws_conn } end rescue JSON::ParserError # JSON解析できないメッセージはそのまま転送(または無視) clients.each { |c| c.send(msg) unless c == ws_conn } end end ws_conn.onclose do clients.delete(ws_conn) puts "[CLOSE] 切断: 残り #{clients.size} 名" end end end