kaede 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/kaede.svg)](http://badge.fury.io/rb/kaede)
|
2
3
|
[![Build Status](https://api.travis-ci.org/eagletmt/kaede.svg)](https://travis-ci.org/eagletmt/kaede)
|
3
4
|
[![Coverage Status](https://coveralls.io/repos/eagletmt/kaede/badge.png)](https://coveralls.io/r/eagletmt/kaede)
|
4
5
|
[![Code Climate](https://codeclimate.com/github/eagletmt/kaede.png)](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
|