sleeproom 0.4.3 → 0.9.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,69 +1,71 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "terminal-table"
3
4
 
4
5
  module SleepRoom
5
6
  module Record
6
7
  class Tasks
7
8
  # @return [void]
8
- def self.start
9
- Async do |_task|
10
- count = 0
11
- write_status = WriteStatus.new
12
- SleepRoom.reload_config
13
- if SleepRoom.running?
14
- SleepRoom.error("PID #{SleepRoom.load_pid} Process is already running.")
15
- exit
16
- else
17
- SleepRoom.write_config_file(:status, [])
18
- end
19
- SleepRoom.create_pid(Process.pid)
20
- lists = SleepRoom.load_config(:record)
21
- lists.each do |group, list|
22
- SleepRoom.info("Empty list.") if list.empty?
23
- list.each do |room|
24
- record = SleepRoom::Record::Showroom.new(room: room["room"], group: group, queue: write_status)
25
- record.record
26
- count += 1
9
+ def self.start(verbose: false)
10
+ Async do |task|
11
+ begin
12
+ running_task_count = 0
13
+ write_status = WriteStatus.new
14
+ write_status.run
15
+ if SleepRoom.running?
16
+ SleepRoom.error("PID #{SleepRoom.load_pid} Process is already running.")
17
+ exit
18
+ else
19
+ SleepRoom.write_config_file(:status, [])
27
20
  end
28
- rescue
29
- SleepRoom.error("Cannot parse Recording list.")
21
+ SleepRoom.create_pid(Process.pid)
22
+ lists = SleepRoom.load_config(:record)
23
+ lists.each do |group, list|
24
+ begin
25
+ SleepRoom.info("Empty list.") if list.empty?
26
+ list.each do |room|
27
+ Async do
28
+ running_task_count += 1
29
+ record = SleepRoom::Record::Showroom.new(room: room["room"], group: group, queue: write_status)
30
+ record.record
31
+ end
32
+ end
33
+ rescue StandardError
34
+ SleepRoom.error("Cannot parse Recording list.")
35
+ end
36
+ end
37
+ SleepRoom.info("共启动 #{running_task_count} 个任务.")
38
+ if configatron.web.use
39
+ Async do
40
+ host = configatron.web.server.to_s
41
+ port = configatron.web.port.to_i
42
+ Rack::Handler.get(:falcon).run(Web::App, Host: host, Port: port)
43
+ SleepRoom.info("Web server running. http://#{host}:#{port}/")
44
+ end
45
+ end
46
+ task.children.each(&:wait)
47
+ rescue StandardError => e
48
+ puts e.full_message
30
49
  end
31
- write_status.run
32
- SleepRoom.info("共启动 #{count} 个任务.")
33
- wait
34
- rescue => e
35
- puts e.full_message
36
50
  end
37
- rescue Exception
51
+ rescue Exception => e
52
+ puts e.full_message if verbose
38
53
  SleepRoom.create_pid(nil) unless SleepRoom.running?
39
54
  puts "Exit..."
40
55
  end
41
56
 
42
57
  # @return [void]
43
58
  def self.stop
44
- SleepRoom.reload_config
45
59
  raise "未实现"
46
60
  end
47
61
 
48
62
  # @return [void]
49
63
  def self.status
50
64
  Async do
51
- SleepRoom.reload_config
52
- status = SleepRoom.load_status
65
+ status = SleepRoom.load_status.sort_by { |hash| hash[:group] }
53
66
  pid = SleepRoom.load_config(:pid)
54
67
  if !SleepRoom.running?(pid) || status.empty? || pid.nil?
55
- lists = SleepRoom.load_config(:record)
56
68
  SleepRoom.info("No tasks running.")
57
- lists.each do |group, list|
58
- next if list.empty?
59
- rows = []
60
- title = group
61
- headings = list[0].keys
62
- list.each do |hash|
63
- rows.push(hash.values)
64
- end
65
- puts Terminal::Table.new(title: "[Recording list] Group: #{title}",:rows => rows, headings: headings)
66
- end
67
69
  else
68
70
  rows = []
69
71
  headings = status[0].keys
@@ -71,7 +73,7 @@ module SleepRoom
71
73
  rows.push(
72
74
  hash.values.map do |s|
73
75
  if s.is_a?(Hash)
74
- "#{(s[:last_ack].is_a?(Time) ? "[ACK]" + s[:last_ack].strftime("%H:%M:%S").to_s : "nil")}"
76
+ ((s[:last_ack].is_a?(Time) ? "[ACK]" + s[:last_ack].strftime("%H:%M:%S").to_s : "nil")).to_s
75
77
  elsif s.is_a?(Time)
76
78
  s.strftime("%H:%M:%S")
77
79
  else
@@ -80,18 +82,34 @@ module SleepRoom
80
82
  end
81
83
  )
82
84
  end
83
- puts Terminal::Table.new(title: "Status [PID #{pid}] (#{status.count})",:rows => rows, headings: headings)
85
+ puts Terminal::Table.new(title: "Status [PID #{pid}] (#{status.count})", rows: rows, headings: headings)
84
86
  end
85
87
  end
86
88
  end
87
89
 
90
+ def self.lists
91
+ lists = SleepRoom.load_config(:record)
92
+ puts "[Record list]"
93
+ lists.each do |group, list|
94
+ next if list.empty?
95
+
96
+ rows = []
97
+ title = group
98
+ headings = list[0].keys
99
+ list.each do |hash|
100
+ rows.push(hash.values)
101
+ end
102
+ puts Terminal::Table.new(title: title, rows: rows, headings: headings)
103
+ end
104
+ end
105
+
88
106
  def self.add(room, group)
89
107
  Async do
90
108
  group = "default" if group.empty?
91
109
  old_record = SleepRoom.load_config(:record)
92
110
  name = API::RoomAPI.new(room).room_name
93
- input_record = {"room" => room, "name" => name}
94
- if !old_record[group].nil? && new_record = old_record[group].find{|h| h = input_record if h["room"] == room}
111
+ input_record = { "room" => room, "name" => name }
112
+ if !old_record[group].nil? && old_record[group].find { |h| h = input_record if h["room"] == room }
95
113
  SleepRoom.error("Room #{room} already exists.")
96
114
  else
97
115
  old_record[group] = [] if old_record[group].nil?
@@ -105,19 +123,10 @@ module SleepRoom
105
123
 
106
124
  def self.remove(room)
107
125
  old_record = SleepRoom.load_config(:record)
108
- new_record = old_record.each {|k, v| v.delete_if { |h| h["room"] == room }}
126
+ new_record = old_record.each { |_k, v| v.delete_if { |h| h["room"] == room } }
109
127
  SleepRoom.write_config_file(:record, new_record)
110
128
  SleepRoom.info("Remove success.")
111
129
  end
112
-
113
- private
114
- def self.wait
115
- Async do |task|
116
- while true
117
- task.sleep 1
118
- end
119
- end
120
- end
121
130
  end
122
131
  end
123
132
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roda"
4
+ module SleepRoom
5
+ module Web
6
+ class App < Roda
7
+ plugin :default_headers,
8
+ "Content-Type" => "application/json",
9
+ "X-Frame-Options" => "deny",
10
+ "X-Content-Type-Options" => "nosniff",
11
+ "X-XSS-Protection" => "1; mode=block"
12
+ plugin :content_security_policy do |csp|
13
+ csp.default_src :none
14
+ csp.style_src :self
15
+ csp.form_action :self
16
+ csp.script_src :self
17
+ csp.connect_src :self
18
+ csp.base_uri :none
19
+ csp.frame_ancestors :none
20
+ end
21
+ plugin :public
22
+ plugin :multi_route
23
+ plugin :not_found do
24
+ end
25
+ plugin :error_handler do |e|
26
+ $stderr.print "#{e.class}: #{e.message}\n"
27
+ warn e.backtrace
28
+ next exception_page(e, assets: true) if ENV["RACK_ENV"] == "development"
29
+ end
30
+
31
+ route "status" do |r|
32
+ SleepRoom.load_status.sort_by { |hash| hash[:group] }.to_json
33
+ end
34
+
35
+ route "lists" do |r|
36
+ r.get do
37
+ SleepRoom.load_config(:record).to_json
38
+ end
39
+
40
+ r.post do
41
+ end
42
+ end
43
+ route do |r|
44
+ r.public
45
+ r.multi_route
46
+
47
+ r.root do
48
+ "SleepRoom Web API"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,86 +1,101 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sleeproom/async/websocket"
2
4
  require "async/http/endpoint"
3
5
  require "async/websocket/client"
4
6
  require "json"
5
7
 
6
8
  module SleepRoom
7
- module Record
8
- class WebSocket
9
- attr_accessor :queue
10
- def initialize(room:, broadcast_key:, url:)
11
- @room = room
12
- @url = "wss://" + url
13
- @broadcast_key = broadcast_key
14
- @running = false
15
- @queue = Async::Queue.new
16
- end
9
+ module Record
10
+ class WebSocket
11
+ attr_accessor :last_ack
12
+ def initialize(room:, broadcast_key:, url:)
13
+ @room = room
14
+ @url = "wss://" + url
15
+ @broadcast_key = broadcast_key
16
+ @running = false
17
+ @last_ack = nil
18
+ end
17
19
 
18
- def connect(task: Async::Task.current)
19
- url = @url
20
- endpoint = Async::HTTP::Endpoint.parse(url)
21
- Async::WebSocket::Client.connect(endpoint, handler: WebSocketConnection) do |connection|
22
- @connection = connection
23
- @running = true
24
- connection.write("SUB\t#{@broadcast_key}")
20
+ def connect(task: Async::Task.current)
21
+ url = @url
22
+ endpoint = Async::HTTP::Endpoint.parse(url)
23
+ Async::WebSocket::Client.connect(endpoint, handler: WebSocketConnection) do |connection|
24
+ begin
25
+ @connection = connection
26
+ @running = true
27
+ connection.write("SUB\t#{@broadcast_key}")
28
+ connection.flush
29
+ log("Connect to websocket server.")
30
+ yield :status, { event: :connect, time: Time.now }
31
+
32
+ ping_task = task.async do |sub|
33
+ while @running
34
+ sub.sleep 60
35
+ connection.write("PING\tshowroom")
25
36
  connection.flush
26
- log("Connect to websocket server.")
27
- @queue.enqueue({event: :connect, time: Time.now})
28
-
29
- ping_task = task.async do |sub|
30
- while @running
31
- sub.sleep 60
32
- @queue.enqueue({event: :ping, time: Time.now})
33
- connection.write("PING\tshowroom")
34
- connection.flush
35
- end
36
- end
37
+ end
38
+ end
37
39
 
38
- status_task = task.async do |sub|
39
- while true
40
- sub.sleep 1
41
- if @running == false
42
- connection.close
43
- end
44
- end
45
- end
40
+ status_task = task.async do |sub|
41
+ loop do
42
+ sub.sleep 1
43
+ connection.close if @running == false
44
+ end
45
+ end
46
46
 
47
- while message = connection.read
48
- if message == "ACK\tshowroom"
49
- @queue.enqueue({event: :ack, time: Time.now}) if message == "ACK\tshowroom"
50
- end
51
- if message.start_with?("MSG")
52
- begin
53
- yield JSON.parse(message.split("\t")[2])
54
- rescue => e
55
- SleepRoom.error(e.message)
56
- end
47
+ reconnect_task = task.async do |t|
48
+ loop do
49
+ t.sleep 10
50
+ if !@last_ack.nil? && Time.now.to_i - @last_ack.to_i > 65
51
+ begin
52
+ yield :status, { event: :close, time: Time.now }
53
+ connection.close
54
+ rescue
57
55
  end
58
56
  end
57
+ end
58
+ end
59
+ while message = connection.read
60
+ if message == "ACK\tshowroom"
61
+ @last_ack = Time.now
62
+ yield :status, { event: :ack, time: Time.now } if message == "ACK\tshowroom"
63
+ end
64
+ next unless message.start_with?("MSG")
65
+
66
+ begin
67
+ yield :websocket, JSON.parse(message.split("\t")[2])
59
68
  rescue => e
60
69
  SleepRoom.error(e.message)
61
- ensure
62
- ping_task&.stop
63
- connection.close
64
- log("WebSocket closed.")
65
70
  end
71
+ end
72
+ rescue => e
73
+ yield :status, { event: :error, error: e }
74
+ SleepRoom.error(e.message)
75
+ ensure
76
+ ping_task&.stop
77
+ status_task&.stop
78
+ connection.close
79
+ reconnect_task&.stop
80
+ log("WebSocket closed.")
66
81
  end
67
-
68
- def running?
69
- @running
70
- end
82
+ end
83
+ end
71
84
 
72
- def running=(bool)
73
- @running = bool
74
- end
85
+ def running?
86
+ @running
87
+ end
75
88
 
76
- def stop
77
- @running = false
78
- @connection.close
79
- end
89
+ attr_writer :running
80
90
 
81
- def log(str)
82
- SleepRoom.info("[#{@room}] #{str}")
83
- end
91
+ def stop
92
+ @running = false
93
+ @connection.close
94
+ end
95
+
96
+ def log(str)
97
+ SleepRoom.info("[#{@room}] #{str}")
84
98
  end
85
- end
86
- end
99
+ end
100
+ end
101
+ end
@@ -15,9 +15,11 @@ module SleepRoom
15
15
  status[:update] = Time.now
16
16
  old_status = SleepRoom.load_config(:status)
17
17
  room = status[:room]
18
- if old_status.find { |h| h[:room] == room }
18
+ if tmp_status = old_status.find { |h| h[:room] == room }
19
19
  new_status = old_status.delete_if { |h| h[:room] == room }
20
- new_status.push(status)
20
+ unless tmp_status[:status] == :downloading && status[:status] == :waiting
21
+ new_status.push(tmp_status.merge!(status))
22
+ end
21
23
  else
22
24
  new_status = old_status.push(status)
23
25
  end
@@ -31,6 +33,31 @@ module SleepRoom
31
33
  @queue.enqueue(status)
32
34
  end
33
35
  end
36
+
37
+ def downloading(room:, url:, pid:, start_time:)
38
+ add(
39
+ {
40
+ room: room,
41
+ live: true,
42
+ status: :downloading,
43
+ streaming_url: url,
44
+ pid: pid,
45
+ start_time: start_time
46
+ }
47
+ )
48
+ end
49
+
50
+ def waiting(room:, group:, room_name:)
51
+ add(
52
+ {
53
+ room: room,
54
+ live: false,
55
+ group: group,
56
+ name: room_name,
57
+ status: :waiting,
58
+ }
59
+ )
60
+ end
34
61
  end
35
62
  end
36
63
  end
@@ -3,6 +3,7 @@
3
3
  require "configatron"
4
4
  require "colorize"
5
5
  require "fileutils"
6
+ require "tmpdir"
6
7
  require "yaml"
7
8
  require "logger"
8
9
 
@@ -64,6 +65,7 @@ module SleepRoom
64
65
  def self.create_config_file(config, settings)
65
66
  path = config_path(config)
66
67
  return false if File.exist?(path)
68
+
67
69
  mkdir(File.dirname(path)) unless Dir.exist?(File.dirname(path))
68
70
  write_config_file(config, settings)
69
71
  end
@@ -105,7 +107,7 @@ module SleepRoom
105
107
  default_save_name: "%ROOMNAME%-%TIME%.ts",
106
108
  minyami: {
107
109
  threads: 8,
108
- retries: 999,
110
+ retries: 999
109
111
  },
110
112
  logger: {
111
113
  console: true,
@@ -122,9 +124,9 @@ module SleepRoom
122
124
  mkdir(config_dir) unless Dir.exist?(config_dir)
123
125
 
124
126
  mkdir("#{config_dir}/tmp") unless Dir.exist?("#{config_dir}/tmp")
125
-
127
+
126
128
  init_base
127
-
129
+
128
130
  record = {
129
131
  "default" => []
130
132
  }
@@ -150,6 +152,7 @@ module SleepRoom
150
152
  def self.settings
151
153
  configatron
152
154
  end
155
+
153
156
  def self.load_status
154
157
  SleepRoom.load_config(:status)
155
158
  rescue Error
@@ -164,11 +167,11 @@ module SleepRoom
164
167
  retry
165
168
  end
166
169
 
167
- def self.running?(pid=nil)
170
+ def self.running?(pid = nil)
168
171
  pid = SleepRoom.load_config(:pid) if pid.nil?
169
172
  Process.kill(0, pid)
170
173
  true
171
- rescue
174
+ rescue StandardError
172
175
  false
173
176
  end
174
177
 
@@ -183,7 +186,7 @@ module SleepRoom
183
186
  SleepRoom.create_config_file(:status, status)
184
187
  end
185
188
 
186
- def self.create_record(record = {default: []})
189
+ def self.create_record(record = { default: [] })
187
190
  SleepRoom.create_config_file(:record, record)
188
191
  end
189
192
 
@@ -192,6 +195,34 @@ module SleepRoom
192
195
  SleepRoom.write_config_file(:pid, pid)
193
196
  end
194
197
 
198
+ def self.find_tmp_directory(output, call_time)
199
+ regex = /Proccessing (.*) finished./
200
+ output = "#{configatron.save_path}/#{output}"
201
+ log = "#{output}.out"
202
+ if media_name = File.readlines(log).select { |line| line =~ regex }.last.match(regex)[1]
203
+ directories = Dir["#{Dir.tmpdir}/minyami_#{call_time.to_i / 10}*"]
204
+ directories.each do |path|
205
+ if Dir.glob("#{path}/*.ts").select{ |e| e.include?(media_name)}
206
+ next unless Dir.glob("#{path}/*.ts").last.include?(media_name)
207
+
208
+ return path
209
+ end
210
+ end
211
+ end
212
+ return false
213
+ rescue => e
214
+ puts e.full_message
215
+ SleepRoom.error("寻找失败.")
216
+ end
217
+
218
+ def self.move_ts_to_archive(tmp, path, name)
219
+ FileUtils.cp_r(tmp, path)
220
+ FileUtils.mv(File.join(path, File.basename(tmp)), File.join(path, name))
221
+ rescue => e
222
+ puts e.full_message
223
+ SleepRoom.error("复制失败.")
224
+ end
225
+
195
226
  # @param string [String]
196
227
  # @return [nil]
197
228
  def self.info(string)