kaede 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/README.md +4 -1
- data/kaede.gemspec +5 -3
- data/kaede.service.sample +1 -1
- data/lib/kaede/cli.rb +13 -4
- data/lib/kaede/config.rb +3 -3
- data/lib/kaede/database.rb +79 -90
- data/lib/kaede/dbus/main.rb +73 -0
- data/lib/kaede/dbus/program.rb +6 -66
- data/lib/kaede/dbus/properties.rb +96 -0
- data/lib/kaede/notifier.rb +16 -0
- data/lib/kaede/recorder.rb +48 -2
- data/lib/kaede/scheduler.rb +35 -15
- data/lib/kaede/version.rb +1 -1
- data/spec/kaede/recorder_spec.rb +31 -1
- data/spec/kaede/scheduler_spec.rb +14 -19
- data/spec/kaede/updater_spec.rb +5 -1
- data/spec/spec_helper.rb +34 -3
- metadata +41 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0698a5193297bca2036eaaaf8bab6af7751653c9
|
4
|
+
data.tar.gz: 79fd8fab1adfe6bd0e31406daed1af3438881e90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d3760d621577b1afb34a8c610cbeeb9f8e9dcc8c7be6e7a8eb81b7b370a02a6077f7715dcf57bff2887a33f001b300f6161ff93d2b9dc3fc7b6588f2bf8b123
|
7
|
+
data.tar.gz: a6b336df58a7627d596ea0aac04fcb79ab222acbae432d05a9a72e8549673e06cf362eb2dcfed8fec22f58a4323addc4855a827b91d463305a47e33aa85bca0d
|
data/.travis.yml
CHANGED
@@ -2,13 +2,18 @@ language: ruby
|
|
2
2
|
rvm:
|
3
3
|
- 1.9.3
|
4
4
|
- 2.0.0
|
5
|
-
- 2.1.
|
5
|
+
- 2.1.2
|
6
6
|
- ruby-head
|
7
|
+
env:
|
8
|
+
- DB=sqlite
|
9
|
+
- DB=postgres
|
7
10
|
before_install:
|
8
11
|
- export NOKOGIRI_USE_SYSTEM_LIBRARIES=1
|
9
12
|
before_script:
|
10
13
|
- bundle exec bin/kaede dbus-policy $USER > kaede.conf
|
11
14
|
- sudo mv kaede.conf /etc/dbus-1/system.d/kaede.conf
|
15
|
+
- psql -c "CREATE ROLE kaede WITH LOGIN" -U postgres
|
16
|
+
- psql -c "CREATE DATABASE kaede_test WITH OWNER = kaede ENCODING = 'UTF-8' TEMPLATE = template0" -U postgres
|
12
17
|
matrix:
|
13
18
|
allow_failures:
|
14
19
|
- rvm: ruby-head
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Kaede
|
2
|
+
[](http://badge.fury.io/rb/kaede)
|
2
3
|
[](https://travis-ci.org/eagletmt/kaede)
|
3
4
|
[](https://coveralls.io/r/eagletmt/kaede)
|
4
5
|
[](https://codeclimate.com/github/eagletmt/kaede)
|
@@ -21,7 +22,7 @@ Or install it yourself as:
|
|
21
22
|
|
22
23
|
## Usage
|
23
24
|
### Requirements
|
24
|
-
-
|
25
|
+
- SQLite3 or PostgreSQL
|
25
26
|
- redis
|
26
27
|
- dbus
|
27
28
|
- recpt1
|
@@ -39,6 +40,8 @@ sudo mv kaede.conf /etc/dbus-1/system.d/kaede.conf
|
|
39
40
|
|
40
41
|
cp kaede.rb.sample kaede.rb
|
41
42
|
vim kaede.rb
|
43
|
+
gem install pg # gem install sqlite3
|
44
|
+
kaede db-prepare
|
42
45
|
|
43
46
|
cp kaede.service.sample kaede.service
|
44
47
|
vim kaede.service
|
data/kaede.gemspec
CHANGED
@@ -20,17 +20,19 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler"
|
22
22
|
spec.add_development_dependency "coveralls"
|
23
|
+
spec.add_development_dependency "pg"
|
23
24
|
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "rspec", "
|
25
|
+
spec.add_development_dependency "rspec", ">= 3.0.0"
|
25
26
|
spec.add_development_dependency "simplecov"
|
27
|
+
spec.add_development_dependency "sqlite3"
|
26
28
|
spec.add_development_dependency "timecop"
|
27
29
|
spec.add_development_dependency "vcr"
|
28
30
|
spec.add_development_dependency "webmock"
|
29
|
-
spec.add_dependency "ruby-dbus"
|
30
31
|
spec.add_dependency "nokogiri"
|
31
32
|
spec.add_dependency "redis"
|
33
|
+
spec.add_dependency "ruby-dbus"
|
34
|
+
spec.add_dependency "sequel"
|
32
35
|
spec.add_dependency "sleepy_penguin"
|
33
|
-
spec.add_dependency "sqlite3"
|
34
36
|
spec.add_dependency "thor"
|
35
37
|
spec.add_dependency "twitter"
|
36
38
|
end
|
data/kaede.service.sample
CHANGED
@@ -5,7 +5,7 @@ Description=Kaede Recorder
|
|
5
5
|
WorkingDirectory=/home/eagletmt/work/kaede
|
6
6
|
User=eagletmt
|
7
7
|
ExecStart=/usr/bin/bundle exec bin/kaede scheduler -c kaede.rb
|
8
|
-
ExecReload=/usr/bin/
|
8
|
+
ExecReload=/usr/bin/dbus-send --print-reply --system --dest=cc.wanko.kaede1 /cc/wanko/kaede1/scheduler cc.wanko.kaede1.Scheduler.Reload
|
9
9
|
ExecStop=/usr/bin/dbus-send --print-reply --system --dest=cc.wanko.kaede1 /cc/wanko/kaede1/scheduler cc.wanko.kaede1.Scheduler.Stop
|
10
10
|
Restart=always
|
11
11
|
KillMode=none
|
data/lib/kaede/cli.rb
CHANGED
@@ -16,7 +16,7 @@ module Kaede
|
|
16
16
|
require 'kaede/scheduler'
|
17
17
|
load_config
|
18
18
|
|
19
|
-
db = Kaede::Database.new(Kaede.config.
|
19
|
+
db = Kaede::Database.new(Kaede.config.database_url)
|
20
20
|
Kaede::Scheduler.setup(db)
|
21
21
|
Kaede::Scheduler.start
|
22
22
|
end
|
@@ -37,7 +37,7 @@ module Kaede
|
|
37
37
|
require 'kaede/channel'
|
38
38
|
load_config
|
39
39
|
|
40
|
-
db = Kaede::Database.new(Kaede.config.
|
40
|
+
db = Kaede::Database.new(Kaede.config.database_url)
|
41
41
|
db.add_channel(Channel.new(nil, name, options[:recorder], options[:syoboi]))
|
42
42
|
end
|
43
43
|
|
@@ -46,7 +46,7 @@ module Kaede
|
|
46
46
|
require 'kaede/database'
|
47
47
|
load_config
|
48
48
|
|
49
|
-
db = Kaede::Database.new(Kaede.config.
|
49
|
+
db = Kaede::Database.new(Kaede.config.database_url)
|
50
50
|
db.add_tracking_title(tid.to_i)
|
51
51
|
end
|
52
52
|
|
@@ -57,7 +57,7 @@ module Kaede
|
|
57
57
|
require 'kaede/updater'
|
58
58
|
load_config
|
59
59
|
|
60
|
-
db = Kaede::Database.new(Kaede.config.
|
60
|
+
db = Kaede::Database.new(Kaede.config.database_url)
|
61
61
|
syobocal = Kaede::SyoboiCalendar.new
|
62
62
|
Kaede::Updater.new(db, syobocal).update
|
63
63
|
end
|
@@ -69,6 +69,15 @@ module Kaede
|
|
69
69
|
puts DBus::Generator.new.generate_policy(user)
|
70
70
|
end
|
71
71
|
|
72
|
+
desc 'db-prepare', 'Create tables'
|
73
|
+
def db_prepare
|
74
|
+
require 'kaede/database'
|
75
|
+
load_config
|
76
|
+
|
77
|
+
db = Kaede::Database.new(Kaede.config.database_url)
|
78
|
+
db.prepare_tables
|
79
|
+
end
|
80
|
+
|
72
81
|
private
|
73
82
|
|
74
83
|
def load_config
|
data/lib/kaede/config.rb
CHANGED
@@ -3,9 +3,9 @@ require 'redis'
|
|
3
3
|
|
4
4
|
module Kaede
|
5
5
|
class Config
|
6
|
-
attr_accessor :redis, :redis_queue, :twitter, :twitter_target
|
6
|
+
attr_accessor :database_url, :redis, :redis_queue, :twitter, :twitter_target
|
7
7
|
|
8
|
-
path_attrs = [:b25, :recpt1, :assdumper, :clean_ts, :statvfs, :
|
8
|
+
path_attrs = [:b25, :recpt1, :assdumper, :clean_ts, :statvfs, :record_dir, :cache_dir, :cabinet_dir]
|
9
9
|
attr_reader *path_attrs
|
10
10
|
path_attrs.each do |attr|
|
11
11
|
define_method("#{attr}=") do |arg|
|
@@ -20,7 +20,7 @@ module Kaede
|
|
20
20
|
self.clean_ts = '/usr/bin/clean-ts'
|
21
21
|
self.statvfs = '/usr/bin/statvfs'
|
22
22
|
basedir = Pathname.new(ENV['HOME']).join('kaede')
|
23
|
-
self.
|
23
|
+
self.database_url = "sqlite://#{basedir.join('kaede.db')}"
|
24
24
|
self.record_dir = basedir.join('records')
|
25
25
|
self.cache_dir = basedir.join('cache')
|
26
26
|
self.cabinet_dir = basedir.join('cabinet')
|
data/lib/kaede/database.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'forwardable'
|
2
|
-
require '
|
2
|
+
require 'sequel'
|
3
3
|
require 'kaede/channel'
|
4
4
|
require 'kaede/program'
|
5
5
|
|
@@ -9,74 +9,56 @@ module Kaede
|
|
9
9
|
def_delegators :@db, :transaction
|
10
10
|
|
11
11
|
def initialize(path)
|
12
|
-
@db =
|
13
|
-
@db.send(:set_boolean_pragma, 'foreign_keys', true)
|
14
|
-
prepare_tables
|
12
|
+
@db = Sequel.connect(path)
|
15
13
|
end
|
16
14
|
|
17
15
|
def prepare_tables
|
18
|
-
@db.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
tid integer NOT NULL UNIQUE,
|
47
|
-
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
|
48
|
-
);
|
49
|
-
SQL
|
50
|
-
end
|
51
|
-
private :prepare_tables
|
52
|
-
|
53
|
-
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
|
54
|
-
|
55
|
-
def to_db_datetime(time)
|
56
|
-
time.utc.strftime(DATETIME_FORMAT)
|
57
|
-
end
|
58
|
-
|
59
|
-
def from_db_datetime(str)
|
60
|
-
Time.parse("#{str} UTC").localtime
|
61
|
-
end
|
62
|
-
|
63
|
-
def current_timestamp
|
64
|
-
to_db_datetime(Time.now)
|
16
|
+
@db.create_table?(:channels) do
|
17
|
+
primary_key :id
|
18
|
+
String :name, size: 255, null: false, unique: true
|
19
|
+
Integer :for_recorder, null: false, unique: true
|
20
|
+
Integer :for_syoboi, null: false, unique: true
|
21
|
+
end
|
22
|
+
@db.create_table?(:programs) do
|
23
|
+
primary_key :pid
|
24
|
+
Integer :tid, null: false
|
25
|
+
DateTime :start_time, null: false
|
26
|
+
DateTime :end_time, null: false
|
27
|
+
foreign_key :channel_id, :channels
|
28
|
+
String :count, size: 16, null: false
|
29
|
+
Integer :start_offset, null: false
|
30
|
+
String :subtitle, size: 255
|
31
|
+
String :title, size: 255
|
32
|
+
String :comment, size: 255
|
33
|
+
end
|
34
|
+
@db.create_table?(:jobs) do
|
35
|
+
foreign_key :pid, :programs, primary_key: true
|
36
|
+
DateTime :enqueued_at, null: false
|
37
|
+
DateTime :finished_at
|
38
|
+
DateTime :created_at, null: false
|
39
|
+
end
|
40
|
+
@db.create_table?(:tracking_titles) do
|
41
|
+
Integer :tid, primary_key: true
|
42
|
+
DateTime :created_at, null: false
|
43
|
+
end
|
65
44
|
end
|
66
|
-
private :current_timestamp
|
67
45
|
|
68
46
|
def get_jobs
|
69
|
-
@db.
|
70
|
-
{ pid: pid, enqueued_at: from_db_datetime(enqueued_at) }
|
71
|
-
end
|
47
|
+
@db.from(:jobs).select(:pid, :enqueued_at).where(finished_at: nil).where(Sequel.qualify(:jobs, :enqueued_at) >= Time.now).order(:enqueued_at).to_a
|
72
48
|
end
|
73
49
|
|
74
50
|
def update_job(pid, enqueued_at)
|
75
|
-
@db.
|
51
|
+
@db.transaction do
|
52
|
+
if @db.from(:jobs).where(pid: pid).select(1).first
|
53
|
+
@db.from(:jobs).where(pid: pid).update(enqueued_at: enqueued_at)
|
54
|
+
else
|
55
|
+
@db.from(:jobs).insert(pid: pid, enqueued_at: enqueued_at, created_at: Time.now)
|
56
|
+
end
|
57
|
+
end
|
76
58
|
end
|
77
59
|
|
78
60
|
def delete_job(pid)
|
79
|
-
@db.
|
61
|
+
@db.from(:jobs).where(pid: pid).delete
|
80
62
|
end
|
81
63
|
|
82
64
|
def get_program(pid)
|
@@ -84,62 +66,69 @@ CREATE TABLE IF NOT EXISTS tracking_titles (
|
|
84
66
|
end
|
85
67
|
|
86
68
|
def get_programs(pids)
|
87
|
-
rows = @db.execute(<<-SQL)
|
88
|
-
SELECT pid, tid, start_time, end_time, channels.name, for_syoboi, for_recorder, count, start_offset, subtitle, title, comment
|
89
|
-
FROM programs
|
90
|
-
INNER JOIN channels ON programs.channel_id = channels.id
|
91
|
-
WHERE programs.pid IN (#{pids.join(', ')})
|
92
|
-
SQL
|
93
69
|
programs = {}
|
94
|
-
|
95
|
-
program = Program.new(
|
96
|
-
|
97
|
-
|
70
|
+
@db.from(:programs).inner_join(:channels, [[channel_id: :id]]).where(pid: pids).each do |row|
|
71
|
+
program = Program.new(
|
72
|
+
row[:pid],
|
73
|
+
row[:tid],
|
74
|
+
row[:start_time],
|
75
|
+
row[:end_time],
|
76
|
+
row[:name],
|
77
|
+
row[:for_syoboi],
|
78
|
+
row[:for_recorder],
|
79
|
+
row[:count],
|
80
|
+
row[:start_offset],
|
81
|
+
row[:subtitle],
|
82
|
+
row[:title],
|
83
|
+
row[:comment],
|
84
|
+
)
|
98
85
|
programs[program.pid] = program
|
99
86
|
end
|
100
87
|
programs
|
101
88
|
end
|
102
89
|
|
103
90
|
def mark_finished(pid)
|
104
|
-
@db.
|
91
|
+
@db.from(:jobs).where(pid: pid).update(finished_at: Time.now)
|
105
92
|
end
|
106
93
|
|
107
94
|
def get_channels
|
108
|
-
@db.
|
109
|
-
Channel.new(
|
95
|
+
@db.from(:channels).map do |row|
|
96
|
+
Channel.new(row[:id], row[:name], row[:for_recorder], row[:for_syoboi])
|
110
97
|
end
|
111
98
|
end
|
112
99
|
|
113
100
|
def add_channel(channel)
|
114
|
-
@db.
|
101
|
+
@db.from(:channels).insert(name: channel.name, for_recorder: channel.for_recorder, for_syoboi: channel.for_syoboi)
|
115
102
|
end
|
116
103
|
|
117
104
|
def update_program(program, channel)
|
118
|
-
|
119
|
-
program.
|
120
|
-
program.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
program.
|
125
|
-
program.
|
126
|
-
program.
|
127
|
-
program.
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
105
|
+
attributes = {
|
106
|
+
tid: program.tid,
|
107
|
+
start_time: program.start_time,
|
108
|
+
end_time: program.end_time,
|
109
|
+
channel_id: channel.id,
|
110
|
+
count: program.count,
|
111
|
+
start_offset: program.start_offset,
|
112
|
+
subtitle: program.subtitle,
|
113
|
+
title: program.title,
|
114
|
+
comment: program.comment,
|
115
|
+
}
|
116
|
+
@db.transaction do
|
117
|
+
if @db.from(:programs).where(pid: program.pid).select(1).first
|
118
|
+
@db.from(:programs).where(pid: program.pid).update(attributes)
|
119
|
+
else
|
120
|
+
@db.from(:programs).insert(attributes.merge(pid: program.pid))
|
121
|
+
end
|
122
|
+
end
|
134
123
|
end
|
135
124
|
|
136
125
|
def add_tracking_title(tid)
|
137
|
-
@db.
|
126
|
+
@db.from(:tracking_titles).insert(tid: tid, created_at: Time.now)
|
138
127
|
end
|
139
128
|
|
140
129
|
def get_tracking_titles
|
141
|
-
@db.
|
142
|
-
row[
|
130
|
+
@db.from(:tracking_titles).select(:tid).map do |row|
|
131
|
+
row[:tid]
|
143
132
|
end
|
144
133
|
end
|
145
134
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Based on DBus::Main in ruby-dbus gem.
|
2
|
+
# Stop the main loop as quick as possible.
|
3
|
+
require 'sleepy_penguin'
|
4
|
+
|
5
|
+
module Kaede
|
6
|
+
module DBus
|
7
|
+
class Main
|
8
|
+
def initialize
|
9
|
+
@buses = Hash.new
|
10
|
+
@quit_event = SleepyPenguin::EventFD.new(0, :SEMAPHORE)
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(bus)
|
14
|
+
@buses[bus.message_queue.socket] = bus
|
15
|
+
end
|
16
|
+
|
17
|
+
def quit
|
18
|
+
@quit_event.incr(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
def loop
|
22
|
+
flush_buffers
|
23
|
+
|
24
|
+
epoll = prepare_epoll
|
25
|
+
begin
|
26
|
+
while !@buses.empty?
|
27
|
+
epoll.wait do |events, io|
|
28
|
+
if io == @quit_event
|
29
|
+
io.value
|
30
|
+
return
|
31
|
+
end
|
32
|
+
handle_socket(io)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
ensure
|
36
|
+
epoll.close
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_socket(socket)
|
41
|
+
bus = @buses[socket]
|
42
|
+
begin
|
43
|
+
bus.message_queue.buffer_from_socket_nonblock
|
44
|
+
rescue EOFError, SystemCallError
|
45
|
+
@buses.delete(socket) # this bus died
|
46
|
+
return
|
47
|
+
end
|
48
|
+
while message = bus.message_queue.message_from_buffer_nonblock
|
49
|
+
bus.process(message)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def flush_buffers
|
54
|
+
# before blocking, empty the buffers
|
55
|
+
# https://bugzilla.novell.com/show_bug.cgi?id=537401
|
56
|
+
@buses.each_value do |b|
|
57
|
+
while m = b.message_queue.message_from_buffer_nonblock
|
58
|
+
b.process(m)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def prepare_epoll
|
64
|
+
SleepyPenguin::Epoll.new.tap do |epoll|
|
65
|
+
epoll.add(@quit_event, [:IN])
|
66
|
+
@buses.each_key do |socket|
|
67
|
+
epoll.add(socket, [:IN])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/kaede/dbus/program.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'dbus'
|
2
2
|
require 'nokogiri'
|
3
3
|
require 'time'
|
4
|
+
require 'kaede/dbus/properties'
|
4
5
|
|
5
6
|
module Kaede
|
6
7
|
module DBus
|
@@ -16,6 +17,10 @@ module Kaede
|
|
16
17
|
@enqueued_at = enqueued_at
|
17
18
|
end
|
18
19
|
|
20
|
+
include Properties
|
21
|
+
properties_for PROGRAM_INTERFACE, :properties
|
22
|
+
define_properties
|
23
|
+
|
19
24
|
def to_xml
|
20
25
|
Nokogiri::XML::Builder.new do |xml|
|
21
26
|
xml.doc.create_internal_subset(
|
@@ -30,64 +35,11 @@ module Kaede
|
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
33
|
-
xml
|
34
|
-
xml.method_(name: 'Get') do
|
35
|
-
xml.arg(name: 'interface', direction: 'in', type: 's')
|
36
|
-
xml.arg(name: 'property', direction: 'in', type: 's')
|
37
|
-
xml.arg(name: 'value', direction: 'out', type: 'v')
|
38
|
-
end
|
39
|
-
|
40
|
-
xml.method_(name: 'GetAll') do
|
41
|
-
xml.arg(name: 'interface', direction: 'in', type: 's')
|
42
|
-
xml.arg(name: 'properties', direction: 'out', type: 'a{sv}')
|
43
|
-
end
|
44
|
-
|
45
|
-
xml.method_(name: 'Set') do
|
46
|
-
xml.arg(name: 'interface', direction: 'in', type: 's')
|
47
|
-
xml.arg(name: 'property', direction: 'in', type: 's')
|
48
|
-
xml.arg(name: 'value', direction: 'in', type: 'v')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
xml.interface(name: PROGRAM_INTERFACE) do
|
53
|
-
properties.each_key do |key|
|
54
|
-
xml.property(name: key, type: 's', access: 'read')
|
55
|
-
end
|
56
|
-
end
|
38
|
+
xml_for_properties(xml)
|
57
39
|
end
|
58
40
|
end.to_xml
|
59
41
|
end
|
60
42
|
|
61
|
-
dbus_interface PROPERTY_INTERFACE do
|
62
|
-
dbus_method :Get, 'in interface:s, in property:s, out value:v' do |iface, prop|
|
63
|
-
case iface
|
64
|
-
when PROGRAM_INTERFACE
|
65
|
-
if properties.has_key?(prop)
|
66
|
-
[properties[prop]]
|
67
|
-
else
|
68
|
-
raise_unknown_property!
|
69
|
-
end
|
70
|
-
else
|
71
|
-
raise_unknown_property!
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
dbus_method :GetAll, 'in interface:s, out properties:a{sv}' do |iface|
|
76
|
-
case iface
|
77
|
-
when PROGRAM_INTERFACE
|
78
|
-
[properties]
|
79
|
-
when PROGRAM_INTERFACE
|
80
|
-
[{}]
|
81
|
-
else
|
82
|
-
unknown_interface!
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
dbus_method :Set, 'in interface:s, in property:s, in value:v' do |iface, prop, val|
|
87
|
-
raise_access_denied!
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
43
|
dbus_interface PROGRAM_INTERFACE do
|
92
44
|
end
|
93
45
|
|
@@ -108,18 +60,6 @@ module Kaede
|
|
108
60
|
'EnqueuedAt' => @enqueued_at.iso8601,
|
109
61
|
}
|
110
62
|
end
|
111
|
-
|
112
|
-
def raise_unknown_interface!
|
113
|
-
raise ::DBus.error('org.freedesktop.DBus.Error.UnknownInterface')
|
114
|
-
end
|
115
|
-
|
116
|
-
def raise_unknown_property!
|
117
|
-
raise ::DBus.error('org.freedesktop.DBus.Error.UnknownProperty')
|
118
|
-
end
|
119
|
-
|
120
|
-
def raise_access_denied!
|
121
|
-
raise ::DBus.error('org.freedesktop.DBus.Error.AccessDenied')
|
122
|
-
end
|
123
63
|
end
|
124
64
|
end
|
125
65
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Kaede
|
2
|
+
module DBus
|
3
|
+
module Properties
|
4
|
+
PROPERTY_INTERFACE = 'org.freedesktop.DBus.Properties'
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def properties_method
|
12
|
+
@properties_method ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def properties_for(iface, method_sym)
|
16
|
+
self.properties_method[iface] = method_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
def define_properties
|
20
|
+
dbus_interface PROPERTY_INTERFACE do
|
21
|
+
dbus_method :Get, 'in interface:s, in property:s, out value:v' do |iface, prop|
|
22
|
+
get_property(iface, prop)
|
23
|
+
end
|
24
|
+
|
25
|
+
dbus_method :GetAll, 'in interface:s, out properties:a{sv}' do |iface|
|
26
|
+
get_properties(iface)
|
27
|
+
end
|
28
|
+
|
29
|
+
dbus_method :Set, 'in interface:s, in property:s, in value:v' do |iface, prop, val|
|
30
|
+
raise_access_denied!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def xml_for_dbus_properties(xml)
|
37
|
+
xml.interface(name: PROPERTY_INTERFACE) do
|
38
|
+
xml.method_(name: 'GetAll') do
|
39
|
+
xml.arg(name: 'interface', direction: 'in', type: 's')
|
40
|
+
xml.arg(name: 'properties', direction: 'out', type: 'a{sv}')
|
41
|
+
end
|
42
|
+
|
43
|
+
helper = lambda do |verb, dir|
|
44
|
+
xml.method_(name: verb) do
|
45
|
+
xml.arg(name: 'interface', direction: 'in', type: 's')
|
46
|
+
xml.arg(name: 'property', direction: 'in', type: 's')
|
47
|
+
xml.arg(name: 'value', direction: dir, type: 'v')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
helper.call('Get', 'out')
|
51
|
+
helper.call('Set', 'in')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def xml_for_properties(xml)
|
56
|
+
xml_for_dbus_properties(xml)
|
57
|
+
self.class.properties_method.each do |iface, method_sym|
|
58
|
+
xml.interface(name: iface) do
|
59
|
+
send(method_sym).each_key do |key|
|
60
|
+
xml.property(name: key, type: 's', access: 'read')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_property(iface, prop)
|
67
|
+
props = get_properties(iface).first
|
68
|
+
if props.has_key?(prop)
|
69
|
+
[props[prop]]
|
70
|
+
else
|
71
|
+
raise_unknown_property!
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_properties(iface)
|
76
|
+
if sym = self.class.properties_method[iface]
|
77
|
+
[send(sym)]
|
78
|
+
else
|
79
|
+
unknown_interface!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def raise_unknown_interface!
|
84
|
+
raise ::DBus.error('org.freedesktop.DBus.Error.UnknownInterface')
|
85
|
+
end
|
86
|
+
|
87
|
+
def raise_unknown_property!
|
88
|
+
raise ::DBus.error('org.freedesktop.DBus.Error.UnknownProperty')
|
89
|
+
end
|
90
|
+
|
91
|
+
def raise_access_denied!
|
92
|
+
raise ::DBus.error('org.freedesktop.DBus.Error.AccessDenied')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/kaede/notifier.rb
CHANGED
@@ -31,6 +31,22 @@ module Kaede
|
|
31
31
|
tweet(msg)
|
32
32
|
end
|
33
33
|
|
34
|
+
def notify_duration_error(program, got_duration)
|
35
|
+
msg = sprintf('%s (PID:%d) の長さが%g秒しか無いようだが……', format_title(program), program.pid, got_duration)
|
36
|
+
if @twitter_target
|
37
|
+
msg = "@#{@twitter_target} #{msg}"
|
38
|
+
end
|
39
|
+
tweet(msg)
|
40
|
+
end
|
41
|
+
|
42
|
+
def notify_redo_error(program)
|
43
|
+
msg = "再実行にも失敗した…… (PID:#{program.pid})"
|
44
|
+
if @twitter_target
|
45
|
+
msg = "@#{@twitter_target} #{msg}"
|
46
|
+
end
|
47
|
+
tweet(msg)
|
48
|
+
end
|
49
|
+
|
34
50
|
def format_title(program)
|
35
51
|
buf = "#{program.channel_name}で「#{program.title}"
|
36
52
|
if program.count
|
data/lib/kaede/recorder.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'date'
|
2
|
-
require 'open3'
|
3
2
|
require 'fileutils'
|
3
|
+
require 'json'
|
4
|
+
require 'open3'
|
4
5
|
|
5
6
|
module Kaede
|
6
7
|
class Recorder
|
@@ -90,7 +91,7 @@ module Kaede
|
|
90
91
|
@ass_pipe_r.close
|
91
92
|
end
|
92
93
|
|
93
|
-
BUFSIZ = 188 *
|
94
|
+
BUFSIZ = 188 * 1024
|
94
95
|
|
95
96
|
def spawn_repeater
|
96
97
|
@repeater_thread = Thread.start do
|
@@ -122,6 +123,9 @@ module Kaede
|
|
122
123
|
|
123
124
|
def after_record(program)
|
124
125
|
@notifier.notify_after_record(program)
|
126
|
+
unless verify_duration(program, cache_path(program))
|
127
|
+
redo_ts_process(program)
|
128
|
+
end
|
125
129
|
move_ass_to_cabinet(program)
|
126
130
|
clean_ts(program)
|
127
131
|
enqueue_to_redis(program)
|
@@ -143,8 +147,50 @@ module Kaede
|
|
143
147
|
end
|
144
148
|
end
|
145
149
|
|
150
|
+
def redo_ts_process(program)
|
151
|
+
unless system(Kaede.config.b25.to_s, '-v0', '-s1', '-m1', record_path(program).to_s, cache_path(program).to_s)
|
152
|
+
@notifier.notify_redo_error(program)
|
153
|
+
return false
|
154
|
+
end
|
155
|
+
unless system(Kaede.config.assdumper.to_s, record_path(program).to_s, out: cache_ass_path(program).to_s)
|
156
|
+
@notifier.notify_redo_error(program)
|
157
|
+
return false
|
158
|
+
end
|
159
|
+
unless verify_duration(program, cache_path(program))
|
160
|
+
@notifier.notify_redo_error(program)
|
161
|
+
return false
|
162
|
+
end
|
163
|
+
true
|
164
|
+
end
|
165
|
+
|
146
166
|
def enqueue_to_redis(program)
|
147
167
|
Kaede.config.redis.rpush(Kaede.config.redis_queue, program.formatted_fname)
|
148
168
|
end
|
169
|
+
|
170
|
+
ALLOWED_DURATION_ERROR = 20
|
171
|
+
|
172
|
+
def verify_duration(program, path)
|
173
|
+
expected_duration = calculate_duration(program)
|
174
|
+
json = ffprobe(path)
|
175
|
+
got_duration = json['duration'].to_f
|
176
|
+
if (got_duration - expected_duration).abs < ALLOWED_DURATION_ERROR
|
177
|
+
true
|
178
|
+
else
|
179
|
+
@notifier.notify_duration_error(program, got_duration)
|
180
|
+
false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class FFprobeError < StandardError
|
185
|
+
end
|
186
|
+
|
187
|
+
def ffprobe(path)
|
188
|
+
outbuf, errbuf, status = Open3.capture3('ffprobe', '-show_format', '-print_format', 'json', path.to_s)
|
189
|
+
if status.success?
|
190
|
+
JSON.parse(outbuf)['format']
|
191
|
+
else
|
192
|
+
raise FFprobeError.new("ffprobe exited with #{status.exitstatus}: #{errbuf}")
|
193
|
+
end
|
194
|
+
end
|
149
195
|
end
|
150
196
|
end
|
data/lib/kaede/scheduler.rb
CHANGED
@@ -2,6 +2,7 @@ require 'dbus'
|
|
2
2
|
require 'thread'
|
3
3
|
require 'sleepy_penguin'
|
4
4
|
require 'kaede/dbus'
|
5
|
+
require 'kaede/dbus/main'
|
5
6
|
require 'kaede/dbus/program'
|
6
7
|
require 'kaede/dbus/scheduler'
|
7
8
|
require 'kaede/notifier'
|
@@ -12,7 +13,7 @@ module Kaede
|
|
12
13
|
|
13
14
|
def setup(db)
|
14
15
|
@db = db
|
15
|
-
|
16
|
+
prepare_events
|
16
17
|
$stdout.sync = true
|
17
18
|
$stderr.sync = true
|
18
19
|
@recorder_queue = Queue.new
|
@@ -23,11 +24,13 @@ module Kaede
|
|
23
24
|
|
24
25
|
POISON = Object.new
|
25
26
|
|
26
|
-
def
|
27
|
+
def prepare_events
|
27
28
|
@reload_event = SleepyPenguin::EventFD.new(0, :SEMAPHORE)
|
28
|
-
|
29
29
|
@stop_event = SleepyPenguin::EventFD.new(0, :SEMAPHORE)
|
30
|
-
|
30
|
+
end
|
31
|
+
|
32
|
+
def fire_stop
|
33
|
+
@stop_event.incr(1)
|
31
34
|
end
|
32
35
|
|
33
36
|
def start_recorder_waiter
|
@@ -50,18 +53,29 @@ module Kaede
|
|
50
53
|
@recorder_waiter.join
|
51
54
|
end
|
52
55
|
|
53
|
-
def
|
54
|
-
epoll = SleepyPenguin::Epoll.new
|
55
|
-
epoll.add(@reload_event, [:IN])
|
56
|
-
epoll.add(@stop_event, [:IN])
|
57
|
-
|
56
|
+
def prepare_timerfds
|
58
57
|
@timerfds = {}
|
59
58
|
@db.get_jobs.each do |job|
|
60
59
|
tfd = SleepyPenguin::TimerFD.new(:REALTIME)
|
61
60
|
tfd.settime(:ABSTIME, 0, job[:enqueued_at].to_i)
|
62
|
-
epoll.add(tfd, [:IN])
|
63
61
|
@timerfds[tfd.fileno] = [tfd, job[:pid]]
|
64
62
|
end
|
63
|
+
@timerfds
|
64
|
+
end
|
65
|
+
|
66
|
+
def prepare_epoll
|
67
|
+
prepare_timerfds
|
68
|
+
SleepyPenguin::Epoll.new.tap do |epoll|
|
69
|
+
epoll.add(@reload_event, [:IN])
|
70
|
+
epoll.add(@stop_event, [:IN])
|
71
|
+
@timerfds.each_value do |tfd, _|
|
72
|
+
epoll.add(tfd, [:IN])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def start_epoll
|
78
|
+
epoll = prepare_epoll
|
65
79
|
puts "Loaded #{@timerfds.size} schedules"
|
66
80
|
start_dbus
|
67
81
|
|
@@ -99,7 +113,13 @@ module Kaede
|
|
99
113
|
def start_dbus
|
100
114
|
bus = ::DBus.system_bus
|
101
115
|
service = bus.request_service(DBus::DESTINATION)
|
116
|
+
dbus_export_programs(service)
|
117
|
+
service.export(DBus::Scheduler.new(@reload_event, @stop_event))
|
118
|
+
|
119
|
+
@dbus_thread = start_dbus_loop(bus)
|
120
|
+
end
|
102
121
|
|
122
|
+
def dbus_export_programs(service)
|
103
123
|
programs = @db.get_programs(@timerfds.values.map { |_, pid| pid })
|
104
124
|
@timerfds.each_value do |tfd, pid|
|
105
125
|
_, value = tfd.gettime
|
@@ -116,16 +136,16 @@ module Kaede
|
|
116
136
|
end
|
117
137
|
end
|
118
138
|
end
|
139
|
+
end
|
119
140
|
|
120
|
-
|
121
|
-
|
122
|
-
@dbus_main = ::DBus::Main.new
|
141
|
+
def start_dbus_loop(bus)
|
142
|
+
@dbus_main = DBus::Main.new
|
123
143
|
@dbus_main << bus
|
124
|
-
|
144
|
+
Thread.start do
|
125
145
|
max_retries = 10
|
126
146
|
retries = 0
|
127
147
|
begin
|
128
|
-
@dbus_main.
|
148
|
+
@dbus_main.loop
|
129
149
|
rescue ::DBus::Connection::NameRequestError => e
|
130
150
|
puts "#{e.class}: #{e.message}"
|
131
151
|
if retries < max_retries
|
data/lib/kaede/version.rb
CHANGED
data/spec/kaede/recorder_spec.rb
CHANGED
@@ -8,7 +8,7 @@ require 'kaede/updater'
|
|
8
8
|
describe Kaede::Recorder do
|
9
9
|
let(:notifier) { double('Notifier') }
|
10
10
|
let(:recorder) { described_class.new(notifier) }
|
11
|
-
let(:db) { Kaede::Database.new(
|
11
|
+
let(:db) { Kaede::Database.new(DatabaseHelper.database_url) }
|
12
12
|
let(:job) { db.get_jobs.first }
|
13
13
|
let(:program) { db.get_program(job[:pid]) }
|
14
14
|
let(:duration) { 30 }
|
@@ -24,12 +24,15 @@ describe Kaede::Recorder do
|
|
24
24
|
let(:cabinet_ass_path) { recorder.cabinet_ass_path(program) }
|
25
25
|
|
26
26
|
before do
|
27
|
+
db.prepare_tables
|
27
28
|
db.add_channel(Kaede::Channel.new(nil, 'MX', 9, 19))
|
28
29
|
channel = db.get_channels.first
|
29
30
|
program = Kaede::Program.new(1234, 5678, Time.now, Time.now + duration, nil, 19, 9, '6', 0, 'sub', 'title', 'comment')
|
30
31
|
db.update_program(program, channel)
|
31
32
|
db.update_job(program.pid, Time.now + 5)
|
32
33
|
|
34
|
+
allow(recorder).to receive(:verify_duration)
|
35
|
+
|
33
36
|
Kaede.configure do |config|
|
34
37
|
config.redis = double('redis')
|
35
38
|
tools = @topdir.join('tools')
|
@@ -72,6 +75,7 @@ describe Kaede::Recorder do
|
|
72
75
|
|
73
76
|
allow(Kaede.config.redis).to receive(:rpush)
|
74
77
|
allow(notifier).to receive(:notify_after_record).with(program)
|
78
|
+
allow(recorder).to receive(:verify_duration).and_return(true)
|
75
79
|
end
|
76
80
|
|
77
81
|
it 'calls Notifier#notify_after_record' do
|
@@ -173,4 +177,30 @@ describe Kaede::Recorder do
|
|
173
177
|
end
|
174
178
|
end
|
175
179
|
end
|
180
|
+
|
181
|
+
describe '#verify_duration' do
|
182
|
+
let(:duration) { 1800 }
|
183
|
+
let(:got_duration) { 1789.21 }
|
184
|
+
|
185
|
+
before do
|
186
|
+
# Unstub verify_duration
|
187
|
+
allow(recorder).to receive(:verify_duration).and_call_original
|
188
|
+
|
189
|
+
allow(recorder).to receive(:ffprobe).and_return('duration' => got_duration)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'verifies the duration of recorded MPEG2-TS' do
|
193
|
+
expect(recorder.verify_duration(program, double('path to MPEG2-TS'))).to eq(true)
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'when the duration is too short' do
|
197
|
+
let(:got_duration) { 1750.45 }
|
198
|
+
|
199
|
+
it 'notifies the verification error' do
|
200
|
+
expect(notifier).to receive(:notify_duration_error)
|
201
|
+
|
202
|
+
expect(recorder.verify_duration(program, double('path to MPEG2-TS'))).to eq(false)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
176
206
|
end
|
@@ -6,10 +6,14 @@ require 'kaede'
|
|
6
6
|
require 'kaede/database'
|
7
7
|
require 'kaede/recorder'
|
8
8
|
require 'kaede/scheduler'
|
9
|
+
require 'kaede/dbus'
|
9
10
|
|
10
11
|
describe Kaede::Scheduler do
|
11
|
-
let(:
|
12
|
-
|
12
|
+
let(:db) { Kaede::Database.new(DatabaseHelper.database_url) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
db.prepare_tables
|
16
|
+
end
|
13
17
|
|
14
18
|
describe '.start' do
|
15
19
|
let(:program) { Kaede::Program.new(1234, 5678, Time.now, Time.now + 30, nil, 19, 9, '5.5', 0, 'sub', 'title', '') }
|
@@ -22,32 +26,23 @@ describe Kaede::Scheduler do
|
|
22
26
|
end
|
23
27
|
|
24
28
|
it 'works' do
|
25
|
-
|
29
|
+
q = Queue.new
|
26
30
|
allow_any_instance_of(Kaede::Recorder).to receive(:record) { |recorder, db, pid|
|
27
|
-
|
28
|
-
puts "Record #{program.pid}"
|
31
|
+
q.push(pid)
|
29
32
|
}
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
described_class.setup(db)
|
33
|
+
described_class.setup(db)
|
34
|
+
expect(db.get_jobs.size).to eq(1)
|
35
|
+
thread = Thread.start do
|
34
36
|
described_class.start
|
35
37
|
end
|
36
|
-
w.close
|
37
38
|
|
38
|
-
expect(db.get_jobs.size).to eq(1)
|
39
39
|
begin
|
40
40
|
Timeout.timeout(10) do
|
41
|
-
|
42
|
-
if s =~ /\ARecord (\d+)\z/
|
43
|
-
expect($1.to_i).to eq(program.pid)
|
44
|
-
break
|
45
|
-
end
|
46
|
-
end
|
41
|
+
expect(q.pop).to eq(program.pid)
|
47
42
|
end
|
48
43
|
ensure
|
49
|
-
|
50
|
-
|
44
|
+
described_class.fire_stop
|
45
|
+
thread.join
|
51
46
|
end
|
52
47
|
expect(db.get_jobs.size).to eq(0)
|
53
48
|
end
|
data/spec/kaede/updater_spec.rb
CHANGED
@@ -5,10 +5,14 @@ require 'kaede/syoboi_calendar'
|
|
5
5
|
require 'kaede/updater'
|
6
6
|
|
7
7
|
describe Kaede::Updater do
|
8
|
-
let(:db) { Kaede::Database.new(
|
8
|
+
let(:db) { Kaede::Database.new(DatabaseHelper.database_url) }
|
9
9
|
let(:syobocal) { Kaede::SyoboiCalendar.new }
|
10
10
|
let(:updater) { described_class.new(db, syobocal) }
|
11
11
|
|
12
|
+
before do
|
13
|
+
db.prepare_tables
|
14
|
+
end
|
15
|
+
|
12
16
|
describe '#update' do
|
13
17
|
let(:channel) { Kaede::Channel.new(nil, 'MX', 9, 19) }
|
14
18
|
let(:tracking_tid) { 3225 }
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'coveralls'
|
2
|
+
require 'sequel'
|
2
3
|
require 'simplecov'
|
3
4
|
require 'timecop'
|
4
5
|
require 'tmpdir'
|
@@ -12,9 +13,6 @@ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
|
12
13
|
SimpleCov.start do
|
13
14
|
add_filter Bundler.bundle_path.to_s
|
14
15
|
add_filter File.dirname(__FILE__)
|
15
|
-
|
16
|
-
# Kaede::Scheduler is tested in another process.
|
17
|
-
add_filter 'lib/kaede/scheduler.rb'
|
18
16
|
end
|
19
17
|
|
20
18
|
VCR.configure do |config|
|
@@ -41,6 +39,13 @@ RSpec.configure do |config|
|
|
41
39
|
@topdir = Pathname.new(__FILE__).parent
|
42
40
|
end
|
43
41
|
|
42
|
+
config.before :suite do
|
43
|
+
DatabaseHelper.clean
|
44
|
+
end
|
45
|
+
config.after :each do
|
46
|
+
DatabaseHelper.clean
|
47
|
+
end
|
48
|
+
|
44
49
|
config.around :each do |example|
|
45
50
|
Dir.mktmpdir('kaede') do |dir|
|
46
51
|
@tmpdir = Pathname.new(dir)
|
@@ -48,3 +53,29 @@ RSpec.configure do |config|
|
|
48
53
|
end
|
49
54
|
end
|
50
55
|
end
|
56
|
+
|
57
|
+
module DatabaseHelper
|
58
|
+
module_function
|
59
|
+
|
60
|
+
def database_url
|
61
|
+
if ENV['DB'] == 'postgres'
|
62
|
+
"postgres://localhost/kaede_test?user=kaede"
|
63
|
+
else
|
64
|
+
'sqlite://kaede.db'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def clean
|
69
|
+
db = Sequel.connect(database_url)
|
70
|
+
[
|
71
|
+
:tracking_titles,
|
72
|
+
:jobs,
|
73
|
+
:programs,
|
74
|
+
:channels,
|
75
|
+
].each do |table|
|
76
|
+
if db.table_exists?(table)
|
77
|
+
db.from(table).delete
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kaede
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kohei Suzuki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pg
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,16 +70,16 @@ dependencies:
|
|
56
70
|
name: rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- - "
|
73
|
+
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: 3.0.0
|
75
|
+
version: 3.0.0
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- - "
|
80
|
+
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: 3.0.0
|
82
|
+
version: 3.0.0
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: simplecov
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: timecop
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,7 +151,7 @@ dependencies:
|
|
123
151
|
- !ruby/object:Gem::Version
|
124
152
|
version: '0'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
154
|
+
name: nokogiri
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
128
156
|
requirements:
|
129
157
|
- - ">="
|
@@ -137,7 +165,7 @@ dependencies:
|
|
137
165
|
- !ruby/object:Gem::Version
|
138
166
|
version: '0'
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
168
|
+
name: redis
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
142
170
|
requirements:
|
143
171
|
- - ">="
|
@@ -151,7 +179,7 @@ dependencies:
|
|
151
179
|
- !ruby/object:Gem::Version
|
152
180
|
version: '0'
|
153
181
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
182
|
+
name: ruby-dbus
|
155
183
|
requirement: !ruby/object:Gem::Requirement
|
156
184
|
requirements:
|
157
185
|
- - ">="
|
@@ -165,7 +193,7 @@ dependencies:
|
|
165
193
|
- !ruby/object:Gem::Version
|
166
194
|
version: '0'
|
167
195
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
196
|
+
name: sequel
|
169
197
|
requirement: !ruby/object:Gem::Requirement
|
170
198
|
requirements:
|
171
199
|
- - ">="
|
@@ -179,7 +207,7 @@ dependencies:
|
|
179
207
|
- !ruby/object:Gem::Version
|
180
208
|
version: '0'
|
181
209
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
210
|
+
name: sleepy_penguin
|
183
211
|
requirement: !ruby/object:Gem::Requirement
|
184
212
|
requirements:
|
185
213
|
- - ">="
|
@@ -247,7 +275,9 @@ files:
|
|
247
275
|
- lib/kaede/database.rb
|
248
276
|
- lib/kaede/dbus.rb
|
249
277
|
- lib/kaede/dbus/generator.rb
|
278
|
+
- lib/kaede/dbus/main.rb
|
250
279
|
- lib/kaede/dbus/program.rb
|
280
|
+
- lib/kaede/dbus/properties.rb
|
251
281
|
- lib/kaede/dbus/scheduler.rb
|
252
282
|
- lib/kaede/notifier.rb
|
253
283
|
- lib/kaede/program.rb
|