diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..e586953 --- /dev/null +++ b/Gemfile @@ -0,0 +1,9 @@ +source 'http://rubygems.org' +gem 'rubygems-update' +gem 'sinatra' +gem 'sinatra-reloader' +gem 'sinatra-contrib' +gem 'sinatra-websocket' +gem 'bundler' +gem 'activesupport' +gem 'warden' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..5def4db --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,71 @@ +GEM + remote: http://rubygems.org/ + specs: + activesupport (5.2.3) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + backports (3.15.0) + concurrent-ruby (1.1.5) + daemons (1.3.1) + em-websocket (0.3.8) + addressable (>= 2.1.1) + eventmachine (>= 0.12.9) + eventmachine (1.2.7) + i18n (1.6.0) + concurrent-ruby (~> 1.0) + minitest (5.11.3) + multi_json (1.13.1) + mustermann (1.0.3) + public_suffix (3.1.0) + rack (2.0.7) + rack-protection (2.0.5) + rack + rubygems-update (3.0.4) + sinatra (2.0.5) + mustermann (~> 1.0) + rack (~> 2.0) + rack-protection (= 2.0.5) + tilt (~> 2.0) + sinatra-contrib (2.0.5) + backports (>= 2.8.2) + multi_json + mustermann (~> 1.0) + rack-protection (= 2.0.5) + sinatra (= 2.0.5) + tilt (>= 1.3, < 3) + sinatra-reloader (1.0) + sinatra-contrib + sinatra-websocket (0.3.1) + em-websocket (~> 0.3.6) + eventmachine + thin (>= 1.3.1, < 2.0.0) + thin (1.7.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thread_safe (0.3.6) + tilt (2.0.9) + tzinfo (1.2.5) + thread_safe (~> 0.1) + warden (1.2.8) + rack (>= 2.0.6) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport + bundler + rubygems-update + sinatra + sinatra-contrib + sinatra-reloader + sinatra-websocket + warden + +BUNDLED WITH + 2.0.2 diff --git a/app.rb b/app.rb new file mode 100644 index 0000000..57319cc --- /dev/null +++ b/app.rb @@ -0,0 +1,99 @@ +require 'bundler/setup' +Bundler.require +require 'sinatra/reloader' if development? +require 'sinatra-websocket' +require 'active_support' +require 'active_support/core_ext' +require 'logger' + +enable :sessions + +configure do #どの環境でも起動時に1回だけ実行される + set :flags => (1..6).to_a.map{|i| [i, 0]}.to_h #どのチームが押したか管理するためのハッシュ +end + +set :server, 'thin' +set :sockets, [] + +logger = Logger.new(STDOUT) + +get '/' do + if session[:team].blank? #チームがsetされていないときに選択画面に遷移する + erb :select + else + pushed=Array.new + settings.flags.sort_by{|key,value| value}.each do |key, value| + if value==0 + next + else + pushed.push("
  • チーム#{key}
  • ") + end + end + @pushed=pushed.join("\n") + erb :index + end +end + +get '/websocket' do + if request.websocket? #WebSocket通信かどうかを確認 + request.websocket do |ws| + ws.onopen do #最初に開いたときの処理 + settings.sockets << ws + end + ws.onmessage do |btn| #ボタン or resetが押された時の処理 + logger.debug(btn) + if session[:team].present? && btn=="Stop!" + if session[:team]>=1 && session[:team]<=6 && settings.flags[session[:team]]==0 + settings.flags[session[:team]] = settings.flags.values.inject(:+) + 1 + t_name="チーム#{session[:team]}" + logger.debug(settings.flags) + settings.sockets.each do |s| + s.send(t_name) + end + end + elsif session[:admin].present? && btn=="Delete!" #controllerでresetを押された時の処理 + if session[:admin]==8804912 + settings.sockets.each do |s| + settings.flags=(1..6).to_a.map{|i| [i, 0]}.to_h + logger.debug(settings.flags) + s.send("Delete!") + end + end + end + end + ws.onclose do #ブラウザ閉じたりとかしてWebSocket通信が切れた時の処理 + settings.sockets.delete(ws) + end + end + end +end + +post '/setteam' do #チーム決定ボタンを押した時の処理 + t_num=params[:t_num].to_i + if t_num>=1 && t_num<=6 + session[:team]=t_num + redirect "/" + else + session.clear + erb :error + end +end + +get '/resetteam' do #チーム再選択ボタンを押した時の処理 + session.clear #チーム番号を保持しているセッションを消す + redirect "/" +end + +get '/controller' do #出題者が操作するページ + session[:admin]=8804912 + pushed=Array.new + settings.flags.sort_by{|key,value| value}.each do |key, value| + if value==0 + next + else + pushed.push("
  • チーム#{key}
  • ") + end + end + @pushed=pushed.join("\n") + erb :controller +end diff --git a/public/css/main.css b/public/css/main.css new file mode 100644 index 0000000..0e3c8f3 --- /dev/null +++ b/public/css/main.css @@ -0,0 +1,26 @@ +div.pushbutton { + text-align: center; +} + +button#push { + width: 90%; + height: 80vh; + font-size: 1.4em; + font-weight: bold; + padding: 10px 30px; + color: #fff; + border-style: none; + background: #00f; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +button#push:hover { + opacity: 0.8; +} + +button#push:disabled { +color: #000; +background:#ff0; +} diff --git a/public/js/client.js b/public/js/client.js new file mode 100644 index 0000000..566a101 --- /dev/null +++ b/public/js/client.js @@ -0,0 +1,28 @@ +window.addEventListener('load', () => { + let pushedbox = document.getElementById('pushedteam'); + let form = document.getElementById('form'); + //let sendMsg = document.getElementById('send-msg'); + let ws = new WebSocket('ws://' + window.location.host + '/websocket'); + + ws.onopen = () => console.log('connection opened'); + ws.onclose = () => console.log('connection closed'); + ws.onmessage = m => { + if (m.data=="Delete!") { + while (pushedbox.firstChild) pushedbox.removeChild(pushedbox.firstChild); + document.getElementById('push').disabled = false; + } else { + let li = document.createElement('li'); + li.textContent = m.data; + pushedbox.append(li); + }; + }; + + //sendMsg.addEventListener('click', () => sendMsg.value = ''); + + form.addEventListener('submit', e => { + document.getElementById('push').disabled = true; + ws.send("Stop!"); // + //sendMsg.value = ''; //sendMsgを空にして初期状態に + e.preventDefault(); //form送信して送信先のページに遷移する動作をキャンセル + }); +}); diff --git a/public/js/controller.js b/public/js/controller.js new file mode 100644 index 0000000..feca3b7 --- /dev/null +++ b/public/js/controller.js @@ -0,0 +1,29 @@ +window.addEventListener('load', () => { + let pushedbox = document.getElementById('pushedteam'); + let ws = new WebSocket('ws://' + window.location.host + '/websocket'); + + ws.onopen = () => console.log('connection opened'); + ws.onclose = () => console.log('connection closed'); + ws.onmessage = m => { + if (m.data=="Delete!") { + while (pushedbox.firstChild) pushedbox.removeChild(pushedbox.firstChild); + } else { + let li = document.createElement('li'); + li.textContent = m.data; + pushedbox.append(li); + document.getElementById("hayaoshi").pause(); + document.getElementById("hayaoshi").play(); + playFlag = false; + audioObj.pause(); + pButton.innerHTML = 'PLAY'; + }; + }; + + var resetButton = document.getElementById('reset'); + + resetButton.addEventListener('click', function(){ + while (pushedbox.firstChild) pushedbox.removeChild(pushedbox.firstChild); + ws.send("Delete!"); + }); + +}); diff --git a/public/js/player.js b/public/js/player.js new file mode 100644 index 0000000..63dcf56 --- /dev/null +++ b/public/js/player.js @@ -0,0 +1,65 @@ +// 再生するファイルリスト +var fileList = [ +{ name : 'Cat_Life', url : 'cat_life.mp3' }, +{ name : 'Ray_at_midsummer', url : 'Ray_at_midsummer.mp3' } +]; +// Audioオブジェクト +var audioObj = new Audio(); +var playFlag = "select"; +// 再生するプレイリストを表示 +var playList = ''; +document.getElementById('playListArea').innerHTML = playList; +// 再生開始 +function start_music(url, name){ + playFlag = false; + audioObj.pause(); // 以前の音楽を停止させる + audioObj = new Audio(url); + audioObj.addEventListener('loadedmetadata', function() { + var sec = '0' + Math.floor(audioObj.currentTime % 60); // 秒数 + var min = '0' + Math.floor(audioObj.currentTime / 60); // 分数 + sec = sec.substr(sec.length-2, 2); + min = min.substr(min.length-2, 2); + var totalSec = '0' + Math.floor(audioObj.duration % 60); // 秒数 + var totalMin = '0' + Math.floor(audioObj.duration / 60); // 分数 + totalSec = totalSec.substr(totalSec.length-2, 2); + totalMin = totalMin.substr(totalMin.length-2, 2); + crtTime.innerHTML = min+":"+sec+' ['+ totalMin+':'+totalSec +']'; + }, true); + + audioObj.addEventListener('timeupdate', function(){ + var sec = '0' + Math.floor(audioObj.currentTime % 60); // 秒数 + var min = '0' + Math.floor(audioObj.currentTime / 60); // 分数 + sec = sec.substr(sec.length-2, 2); + min = min.substr(min.length-2, 2); + var totalSec = '0' + Math.floor(audioObj.duration % 60); // 秒数 + var totalMin = '0' + Math.floor(audioObj.duration / 60); // 分数 + totalSec = totalSec.substr(totalSec.length-2, 2); + totalMin = totalMin.substr(totalMin.length-2, 2); + crtTime.innerHTML = min+":"+sec+' ['+ totalMin+':'+totalSec +']'; + }, true); + + document.getElementById('name').innerHTML=name; + pButton.innerHTML = 'START'; +} +// イベント設定 +var crtTime = document.getElementById('ctime'); +var pButton = document.getElementById('playButton'); +// 再生ボタンのイベントを設定 +pButton.addEventListener('click', function(){ + if (playFlag == false){ + playFlag = true; // 再生フラグ:再生中にする + audioObj.play(); + pButton.innerHTML = 'STOP'; + } else if (playFlag == true){ + playFlag = false; // 再生フラグ:停止中にする + audioObj.pause(); + pButton.innerHTML = 'PLAY'; + } else { + alert("曲をセットしてね!"); + } +}, true); diff --git a/public/music/Ray_at_midsummer.mp3 b/public/music/Ray_at_midsummer.mp3 new file mode 100644 index 0000000..68bdffa --- /dev/null +++ b/public/music/Ray_at_midsummer.mp3 Binary files differ diff --git a/public/music/cat_life.mp3 b/public/music/cat_life.mp3 new file mode 100644 index 0000000..a581144 --- /dev/null +++ b/public/music/cat_life.mp3 Binary files differ diff --git a/public/music/hayaoshi.mp3 b/public/music/hayaoshi.mp3 new file mode 100755 index 0000000..552eebf --- /dev/null +++ b/public/music/hayaoshi.mp3 Binary files differ diff --git a/views/controller.erb b/views/controller.erb new file mode 100644 index 0000000..afa6b98 --- /dev/null +++ b/views/controller.erb @@ -0,0 +1,26 @@ + + + + + + + イントロクイズ2019 + + + +
    +
    + +
    曲名
    +
    0:00
    +
    + + + +
      + <%=@pushed%> +
    + + diff --git a/views/error.erb b/views/error.erb new file mode 100644 index 0000000..9dd6764 --- /dev/null +++ b/views/error.erb @@ -0,0 +1,13 @@ + + + + + + イントロクイズ2019 + + +

    エラーが発生しました

    +

    もう一度お試しください

    +

    最初のページに戻る

    + + diff --git a/views/index.erb b/views/index.erb new file mode 100644 index 0000000..920be33 --- /dev/null +++ b/views/index.erb @@ -0,0 +1,21 @@ + + + + + + + + イントロクイズ2019 + + +
    +
    + +
    +
    +

    チームを選択し直す

    +
      + <%=@pushed%> +
    + + diff --git a/views/select.erb b/views/select.erb new file mode 100644 index 0000000..9c77fb4 --- /dev/null +++ b/views/select.erb @@ -0,0 +1,22 @@ + + + + + + イントロクイズ2019 + + +

    チーム選択

    +
    + + +
    + +