simple_pvr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +133 -0
- data/LICENSE.txt +13 -0
- data/README.md +169 -0
- data/Rakefile +16 -0
- data/bin/pvr_server +10 -0
- data/bin/pvr_xmltv +18 -0
- data/features/channel_overview.feature +48 -0
- data/features/programme_search.feature +20 -0
- data/features/scheduling.feature +112 -0
- data/features/step_definitions/pvr_steps.rb +104 -0
- data/features/step_definitions/web_steps.rb +219 -0
- data/features/support/env.rb +28 -0
- data/features/support/paths.rb +17 -0
- data/features/week_overview.feature +39 -0
- data/lib/simple_pvr/ffmpeg.rb +22 -0
- data/lib/simple_pvr/hdhomerun.rb +103 -0
- data/lib/simple_pvr/hdhomerun_save.sh +10 -0
- data/lib/simple_pvr/model/channel.rb +72 -0
- data/lib/simple_pvr/model/database_initializer.rb +36 -0
- data/lib/simple_pvr/model/programme.rb +58 -0
- data/lib/simple_pvr/model/recording.rb +45 -0
- data/lib/simple_pvr/model/schedule.rb +33 -0
- data/lib/simple_pvr/pvr_initializer.rb +47 -0
- data/lib/simple_pvr/pvr_logger.rb +14 -0
- data/lib/simple_pvr/recorder.rb +25 -0
- data/lib/simple_pvr/recording_manager.rb +101 -0
- data/lib/simple_pvr/recording_planner.rb +72 -0
- data/lib/simple_pvr/scheduler.rb +124 -0
- data/lib/simple_pvr/server/app_controller.rb +13 -0
- data/lib/simple_pvr/server/base_controller.rb +94 -0
- data/lib/simple_pvr/server/channels_controller.rb +68 -0
- data/lib/simple_pvr/server/config.ru +8 -0
- data/lib/simple_pvr/server/programmes_controller.rb +46 -0
- data/lib/simple_pvr/server/rack_maps.rb +7 -0
- data/lib/simple_pvr/server/schedules_controller.rb +71 -0
- data/lib/simple_pvr/server/shows_controller.rb +63 -0
- data/lib/simple_pvr/server/status_controller.rb +11 -0
- data/lib/simple_pvr/server/upcoming_recordings_controller.rb +18 -0
- data/lib/simple_pvr/version.rb +3 -0
- data/lib/simple_pvr/xmltv_reader.rb +83 -0
- data/lib/simple_pvr.rb +22 -0
- data/public/css/bootstrap-responsive.min.css +9 -0
- data/public/css/bootstrap.min.css +9 -0
- data/public/css/simplepvr.css +11 -0
- data/public/img/glyphicons-halflings-white.png +0 -0
- data/public/img/glyphicons-halflings.png +0 -0
- data/public/index.html +55 -0
- data/public/js/angular/angular-resource.min.js +10 -0
- data/public/js/angular/angular.min.js +157 -0
- data/public/js/app.js +145 -0
- data/public/js/bootstrap/bootstrap.min.js +6 -0
- data/public/js/controllers.js +156 -0
- data/public/js/services.js +27 -0
- data/public/partials/about.html +5 -0
- data/public/partials/channels.html +41 -0
- data/public/partials/programme.html +20 -0
- data/public/partials/programmeListing.html +18 -0
- data/public/partials/schedule.html +80 -0
- data/public/partials/schedules.html +44 -0
- data/public/partials/search.html +17 -0
- data/public/partials/show.html +21 -0
- data/public/partials/shows.html +7 -0
- data/public/partials/status.html +6 -0
- data/simple_pvr.gemspec +30 -0
- data/spec/resources/channels.txt +11 -0
- data/spec/resources/programs-without-icon.xmltv +95 -0
- data/spec/resources/programs.xmltv +98 -0
- data/spec/simple_pvr/ffmpeg_spec.rb +26 -0
- data/spec/simple_pvr/hdhomerun_spec.rb +82 -0
- data/spec/simple_pvr/model/channel_spec.rb +114 -0
- data/spec/simple_pvr/model/programme_spec.rb +110 -0
- data/spec/simple_pvr/model/schedule_spec.rb +47 -0
- data/spec/simple_pvr/pvr_initializer_spec.rb +50 -0
- data/spec/simple_pvr/recorder_spec.rb +32 -0
- data/spec/simple_pvr/recording_manager_spec.rb +158 -0
- data/spec/simple_pvr/recording_planner_spec.rb +104 -0
- data/spec/simple_pvr/scheduler_spec.rb +201 -0
- data/spec/simple_pvr/xmltv_reader_spec.rb +49 -0
- data/test/config/jsTestDriver-scenario.conf +10 -0
- data/test/config/jsTestDriver.conf +12 -0
- data/test/config/jstd-scenario-adapter-config.js +6 -0
- data/test/filtersSpec.js +97 -0
- data/test/lib/angular/angular-mocks.js +1719 -0
- data/test/lib/angular/angular-scenario.js +25937 -0
- data/test/lib/angular/jstd-scenario-adapter.js +185 -0
- data/test/lib/angular/version.txt +1 -0
- data/test/lib/jasmine/MIT.LICENSE +20 -0
- data/test/lib/jasmine/index.js +180 -0
- data/test/lib/jasmine/jasmine-html.js +190 -0
- data/test/lib/jasmine/jasmine.css +166 -0
- data/test/lib/jasmine/jasmine.js +2476 -0
- data/test/lib/jasmine/jasmine_favicon.png +0 -0
- data/test/lib/jasmine/version.txt +1 -0
- data/test/lib/jasmine-jstd-adapter/JasmineAdapter.js +196 -0
- data/test/lib/jasmine-jstd-adapter/version.txt +1 -0
- data/test/lib/jstestdriver/JsTestDriver.jar +0 -0
- data/test/lib/jstestdriver/version.txt +1 -0
- data/test/scripts/test-server.sh +14 -0
- data/test/scripts/test.sh +8 -0
- metadata +342 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
describe SimplePvr::RecordingManager do
|
5
|
+
before do
|
6
|
+
@recording_dir = File.dirname(__FILE__) + '/../resources/recordings'
|
7
|
+
FileUtils.rm_rf(@recording_dir) if Dir.exists?(@recording_dir)
|
8
|
+
FileUtils.mkdir_p(@recording_dir + "/series 1/1")
|
9
|
+
FileUtils.mkdir_p(@recording_dir + "/series 1/3")
|
10
|
+
FileUtils.mkdir_p(@recording_dir + "/Another series/10")
|
11
|
+
|
12
|
+
@manager = SimplePvr::RecordingManager.new(@recording_dir)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'knows which shows are recorded' do
|
16
|
+
@manager.shows.should == ['Another series', 'series 1']
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'can delete all episodes of a given show' do
|
20
|
+
@manager.delete_show('series 1')
|
21
|
+
File.exists?(@recording_dir + '/series 1').should be_false
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'knows which episodes of a given show exists' do
|
25
|
+
episodes = @manager.episodes_of('series 1')
|
26
|
+
|
27
|
+
episodes.length.should == 2
|
28
|
+
episodes[0].episode.should == '1'
|
29
|
+
episodes[1].episode.should == '3'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'reads metadata for recordings if present' do
|
33
|
+
start_time = Time.now
|
34
|
+
metadata = {
|
35
|
+
channel: 'Channel 4',
|
36
|
+
subtitle: 'A subtitle',
|
37
|
+
description: 'A description',
|
38
|
+
start_time: start_time,
|
39
|
+
duration: 10.minutes
|
40
|
+
}
|
41
|
+
File.open(@recording_dir + '/series 1/3/metadata.yml', 'w') {|f| f.write(metadata.to_yaml) }
|
42
|
+
|
43
|
+
episodes = @manager.episodes_of('series 1')
|
44
|
+
|
45
|
+
episodes.length.should == 2
|
46
|
+
|
47
|
+
episodes[0].show_name.should == 'series 1'
|
48
|
+
episodes[0].episode.should == '1'
|
49
|
+
|
50
|
+
episodes[1].show_name.should == 'series 1'
|
51
|
+
episodes[1].episode.should == '3'
|
52
|
+
episodes[1].channel.should == 'Channel 4'
|
53
|
+
episodes[1].subtitle.should == 'A subtitle'
|
54
|
+
episodes[1].description.should == 'A description'
|
55
|
+
episodes[1].start_time.should == start_time
|
56
|
+
episodes[1].duration == 10.minutes
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'knows when no thumbnail exists' do
|
60
|
+
episodes = @manager.episodes_of('series 1')
|
61
|
+
|
62
|
+
episodes[0].has_thumbnail.should == false
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'knows when thumbnail exists' do
|
66
|
+
FileUtils.touch(@recording_dir + "/series 1/1/thumbnail.png")
|
67
|
+
episodes = @manager.episodes_of('series 1')
|
68
|
+
|
69
|
+
episodes[0].has_thumbnail.should == true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'knows when no webm file exists' do
|
73
|
+
episodes = @manager.episodes_of('series 1')
|
74
|
+
|
75
|
+
episodes[0].has_webm.should == false
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'knows when a webm file exists' do
|
79
|
+
FileUtils.touch(@recording_dir + "/series 1/1/stream.webm")
|
80
|
+
episodes = @manager.episodes_of('series 1')
|
81
|
+
|
82
|
+
episodes[0].has_webm.should == true
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'can delete an episode of a given show' do
|
86
|
+
@manager.delete_show_episode('series 1', '3')
|
87
|
+
File.exists?(@recording_dir + '/series 1/3').should be_false
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when creating recording directories' do
|
91
|
+
before do
|
92
|
+
@start_time = Time.local(2012, 7, 23, 15, 30, 15)
|
93
|
+
@recording = SimplePvr::Model::Recording.new(double(name: 'Channel 4'), 'Star Trek', @start_time, 50.minutes)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'records to directory with number 1 if nothing exists' do
|
97
|
+
@manager.create_directory_for_recording(@recording)
|
98
|
+
|
99
|
+
File.exists?(@recording_dir + '/Star Trek/1').should be_true
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'removes some potentially harmful characters from directory name' do
|
103
|
+
@recording.show_name = "Some... harmful/irritating\\ characters in: '*title\""
|
104
|
+
@manager.create_directory_for_recording(@recording)
|
105
|
+
|
106
|
+
File.exists?(@recording_dir + '/Some harmfulirritating characters in title/1').should be_true
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'finds a directory name for titles which would otherwise get an empty directory name' do
|
110
|
+
@recording.show_name = '/.'
|
111
|
+
@manager.create_directory_for_recording(@recording)
|
112
|
+
|
113
|
+
File.exists?(@recording_dir + '/Unnamed/1').should be_true
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'finds next number in sequence for new directory' do
|
117
|
+
FileUtils.mkdir_p(@recording_dir + "/Star Trek/1")
|
118
|
+
FileUtils.mkdir_p(@recording_dir + "/Star Trek/2")
|
119
|
+
FileUtils.mkdir_p(@recording_dir + "/Star Trek/3")
|
120
|
+
@manager.create_directory_for_recording(@recording)
|
121
|
+
|
122
|
+
File.exists?(@recording_dir + '/Star Trek/4').should be_true
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'ignores random directories which are not sequence numbers' do
|
126
|
+
FileUtils.mkdir_p(@recording_dir + "/Star Trek/4")
|
127
|
+
FileUtils.mkdir_p(@recording_dir + "/Star Trek/random directory name")
|
128
|
+
@manager.create_directory_for_recording(@recording)
|
129
|
+
|
130
|
+
File.exists?(@recording_dir + '/Star Trek/5').should be_true
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'stores simple metadata if no programme information exists' do
|
134
|
+
@manager.create_directory_for_recording(@recording)
|
135
|
+
|
136
|
+
metadata = YAML.load_file(@recording_dir + '/Star Trek/1/metadata.yml')
|
137
|
+
metadata[:title].should == 'Star Trek'
|
138
|
+
metadata[:channel].should == 'Channel 4'
|
139
|
+
metadata[:start_time].should == @start_time
|
140
|
+
metadata[:duration].should == 50.minutes
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'stores extensive metadata if programme information exists' do
|
144
|
+
start_time = Time.local(2012, 7, 23, 15, 30, 15)
|
145
|
+
recording = SimplePvr::Model::Recording.new(double(name: 'Channel 4'), 'Extensive Metadata', start_time, 50.minutes)
|
146
|
+
recording.programme = SimplePvr::Model::Programme.new(subtitle: 'A subtitle', description: "A description,\nspanning several lines")
|
147
|
+
@manager.create_directory_for_recording(recording)
|
148
|
+
|
149
|
+
metadata = YAML.load_file(@recording_dir + '/Extensive Metadata/1/metadata.yml')
|
150
|
+
metadata[:title].should == 'Extensive Metadata'
|
151
|
+
metadata[:channel].should == 'Channel 4'
|
152
|
+
metadata[:start_time].should == start_time
|
153
|
+
metadata[:duration].should == 50.minutes
|
154
|
+
metadata[:subtitle].should == 'A subtitle'
|
155
|
+
metadata[:description].should == "A description,\nspanning several lines"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::RecordingPlanner do
|
4
|
+
before do
|
5
|
+
SimplePvr::Model::DatabaseInitializer.prepare_for_test
|
6
|
+
SimplePvr::Model::DatabaseInitializer.clear
|
7
|
+
@dr_1 = SimplePvr::Model::Channel.create(name: 'DR 1')
|
8
|
+
@dr_k = SimplePvr::Model::Channel.create(name: 'DR K')
|
9
|
+
@start_time_1, @start_time_2 = Time.local(2012, 7, 10, 20, 50), Time.local(2012, 7, 17, 20, 50)
|
10
|
+
@programme_title = 'Borgias'
|
11
|
+
@programme_duration = 60.minutes.to_i
|
12
|
+
|
13
|
+
@scheduler = double('Scheduler')
|
14
|
+
SimplePvr::PvrInitializer.stub(scheduler: @scheduler)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'resets the recordings when no schedules exist' do
|
18
|
+
SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_k, start_time: @start_time_1, duration: @programme_duration)
|
19
|
+
SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_k, start_time: @start_time_2, duration: @programme_duration)
|
20
|
+
SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @start_time_2, duration: @programme_duration)
|
21
|
+
SimplePvr::Model::Programme.create(title: @programme_title + "-", channel: @dr_k, start_time: @start_time_2, duration: @programme_duration)
|
22
|
+
|
23
|
+
@scheduler.should_receive(:recordings=).with([])
|
24
|
+
|
25
|
+
SimplePvr::RecordingPlanner.read
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'can set up schedules from channel and programme title' do
|
29
|
+
SimplePvr::Model::Schedule.create(type: :specification, title: 'Borgias', channel: @dr_k)
|
30
|
+
@programme_1 = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_k, start_time: @start_time_1, duration: @programme_duration)
|
31
|
+
@programme_2 = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_k, start_time: @start_time_2, duration: @programme_duration)
|
32
|
+
SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @start_time_2, duration: @programme_duration)
|
33
|
+
SimplePvr::Model::Programme.create(title: @programme_title + "-", channel: @dr_k, start_time: @start_time_2, duration: @programme_duration)
|
34
|
+
|
35
|
+
@scheduler.should_receive(:recordings=).with([
|
36
|
+
SimplePvr::Model::Recording.new(@dr_k, 'Borgias', Time.local(2012, 7, 10, 20, 48), 67.minutes, @programme_1),
|
37
|
+
SimplePvr::Model::Recording.new(@dr_k, 'Borgias', Time.local(2012, 7, 17, 20, 48), 67.minutes, @programme_2)
|
38
|
+
])
|
39
|
+
|
40
|
+
SimplePvr::RecordingPlanner.read
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'can set up schedules from channel, programme title, and start time' do
|
44
|
+
SimplePvr::Model::Schedule.create(type: :specification, title: 'Borgias', channel: @dr_k, start_time: @start_time_2)
|
45
|
+
SimplePvr::Model::Programme.create(title: @programme_title, channel:@dr_k, start_time: @start_time_1, duration: @programme_duration)
|
46
|
+
@programme_to_be_recorded = SimplePvr::Model::Programme.create(title: @programme_title, channel:@dr_k, start_time: @start_time_2, duration: @programme_duration)
|
47
|
+
|
48
|
+
@scheduler.should_receive(:recordings=).with([
|
49
|
+
SimplePvr::Model::Recording.new(@dr_k, @programme_title, @start_time_2.advance(minutes: -2), 2.minutes + @programme_duration + 5.minutes, @programme_to_be_recorded)
|
50
|
+
])
|
51
|
+
|
52
|
+
SimplePvr::RecordingPlanner.read
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'can set up schedules from programme title only' do
|
56
|
+
SimplePvr::Model::Schedule.create(type: :specification, title: 'Borgias')
|
57
|
+
@programme_1 = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @start_time_1, duration: @programme_duration)
|
58
|
+
@programme_2 = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_k, start_time: @start_time_2, duration: @programme_duration)
|
59
|
+
SimplePvr::Model::Programme.create(title: @programme_title + '-', channel: @dr_k, start_time: @start_time_2, duration: @programme_duration)
|
60
|
+
|
61
|
+
@scheduler.should_receive(:recordings=).with([
|
62
|
+
SimplePvr::Model::Recording.new(@dr_1, 'Borgias', Time.local(2012, 7, 10, 20, 48), 67.minutes, @programme_1),
|
63
|
+
SimplePvr::Model::Recording.new(@dr_k, 'Borgias', Time.local(2012, 7, 17, 20, 48), 67.minutes, @programme_2)
|
64
|
+
])
|
65
|
+
|
66
|
+
SimplePvr::RecordingPlanner.read
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'can set up schedules for specific days of the week' do
|
70
|
+
@monday = Time.local(2012, 12, 10, 20, 50)
|
71
|
+
@tuesday, @wednesday, @thursday, @friday, @saturday, @sunday = @monday + 1.day, @monday + 2.days, @monday + 3.days, @monday + 4.days, @monday + 5.days, @monday + 6.days
|
72
|
+
@monday_programme = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @monday, duration: @programme_duration)
|
73
|
+
@tuesday_programme = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @tuesday, duration: @programme_duration)
|
74
|
+
@wednesday_programme = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @wednesday, duration: @programme_duration)
|
75
|
+
@thursday_programme = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @thursday, duration: @programme_duration)
|
76
|
+
@friday_programme = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @friday, duration: @programme_duration)
|
77
|
+
@saturday_programme = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @saturday, duration: @programme_duration)
|
78
|
+
@sunday_programme = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @sunday, duration: @programme_duration)
|
79
|
+
|
80
|
+
SimplePvr::Model::Schedule.create(type: :specification, title: @programme_title, filter_by_weekday: true, monday: false, tuesday: true, wednesday: true, thursday: false, friday: false, saturday: true, sunday: true)
|
81
|
+
|
82
|
+
@scheduler.should_receive(:recordings=).with([
|
83
|
+
SimplePvr::Model::Recording.new(@dr_1, @programme_title, @tuesday - 2.minutes, 67.minutes, @tuesday_programme),
|
84
|
+
SimplePvr::Model::Recording.new(@dr_1, @programme_title, @wednesday - 2.minutes, 67.minutes, @wednesday_programme),
|
85
|
+
SimplePvr::Model::Recording.new(@dr_1, @programme_title, @saturday - 2.minutes, 67.minutes, @saturday_programme),
|
86
|
+
SimplePvr::Model::Recording.new(@dr_1, @programme_title, @sunday - 2.minutes, 67.minutes, @sunday_programme),
|
87
|
+
])
|
88
|
+
|
89
|
+
SimplePvr::RecordingPlanner.read
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'ignores programmes for which exceptions exist' do
|
93
|
+
SimplePvr::Model::Schedule.create(type: :specification, title: 'Borgias')
|
94
|
+
SimplePvr::Model::Schedule.create(type: :exception, title: 'Borgias', channel: @dr_1, start_time: @start_time_1)
|
95
|
+
SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_1, start_time: @start_time_1, duration: @programme_duration)
|
96
|
+
@programme_to_be_recorded = SimplePvr::Model::Programme.create(title: @programme_title, channel: @dr_k, start_time: @start_time_2, duration: @programme_duration)
|
97
|
+
|
98
|
+
@scheduler.should_receive(:recordings=).with([
|
99
|
+
SimplePvr::Model::Recording.new(@dr_k, 'Borgias', Time.local(2012, 7, 17, 20, 48), 67.minutes, @programme_to_be_recorded)
|
100
|
+
])
|
101
|
+
|
102
|
+
SimplePvr::RecordingPlanner.read
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::Scheduler do
|
4
|
+
before do
|
5
|
+
@channel = double('Channel DR K', name: 'DR K', frequency: 282000000, channel_id: 1098)
|
6
|
+
@channel_dr1 = double('Channel DR 1', name: 'DR 1', frequency: 290000000, channel_id: 1099)
|
7
|
+
|
8
|
+
@scheduler = SimplePvr::Scheduler.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'leaves recordings that are in the future' do
|
12
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
13
|
+
Time.stub(:now => start_time.advance(hours: -1))
|
14
|
+
|
15
|
+
@scheduler.recordings = [SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)]
|
16
|
+
@scheduler.process
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'marks conflicting future recordings' do
|
20
|
+
first_start_time = Time.now.advance(days: 1)
|
21
|
+
second_start_time = Time.now.advance(days: 1, minutes: 10)
|
22
|
+
third_start_time = Time.now.advance(days: 1, minutes: 20)
|
23
|
+
fourth_start_time = Time.now.advance(days: 2)
|
24
|
+
first_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', first_start_time, 60.minutes)
|
25
|
+
second_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', second_start_time, 60.minutes)
|
26
|
+
third_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', third_start_time, 60.minutes)
|
27
|
+
fourth_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', fourth_start_time, 60.minutes)
|
28
|
+
|
29
|
+
@scheduler.recordings = [first_recording, third_recording, second_recording]
|
30
|
+
|
31
|
+
first_recording.should_not be_conflicting
|
32
|
+
second_recording.should_not be_conflicting
|
33
|
+
third_recording.should be_conflicting
|
34
|
+
fourth_recording.should_not be_conflicting
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'starts recordings at start time' do
|
38
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
39
|
+
starting_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)
|
40
|
+
Time.stub(:now => start_time)
|
41
|
+
SimplePvr::Recorder.stub(:new).with(0, starting_recording).and_return(@recorder = double('Recorder'))
|
42
|
+
@recorder.should_receive(:start!)
|
43
|
+
|
44
|
+
@scheduler.recordings = [starting_recording]
|
45
|
+
@scheduler.process
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'starts recordings that are in progress' do
|
49
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
50
|
+
recording_in_progress = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)
|
51
|
+
Time.stub(:now => start_time.advance(minutes: 30))
|
52
|
+
SimplePvr::Recorder.stub(:new).with(0, recording_in_progress).and_return(@recorder = double('Recorder'))
|
53
|
+
@recorder.should_receive(:start!)
|
54
|
+
|
55
|
+
@scheduler.recordings = [recording_in_progress]
|
56
|
+
@scheduler.process
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'can start two recordings at once' do
|
60
|
+
first_start_time = Time.local(2012, 7, 15, 19, 45, 30)
|
61
|
+
second_start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
62
|
+
recording_in_progress = SimplePvr::Model::Recording.new(@channel, 'Borgia', first_start_time, 60.minutes)
|
63
|
+
starting_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', second_start_time, 60.minutes)
|
64
|
+
Time.stub(:now => second_start_time)
|
65
|
+
SimplePvr::Recorder.stub(:new).with(0, recording_in_progress).and_return(@recorder0 = double('Recorder'))
|
66
|
+
SimplePvr::Recorder.stub(:new).with(1, starting_recording).and_return(@recorder1 = double('Recorder'))
|
67
|
+
@recorder0.should_receive(:start!)
|
68
|
+
@recorder1.should_receive(:start!)
|
69
|
+
|
70
|
+
@scheduler.recordings = [recording_in_progress, starting_recording]
|
71
|
+
@scheduler.process
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'rejects third recording at once, and marks the third recording as conflicting' do
|
75
|
+
first_start_time = Time.local(2012, 7, 15, 19, 45, 30)
|
76
|
+
second_start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
77
|
+
third_start_time = Time.local(2012, 7, 15, 20, 20, 0)
|
78
|
+
recording_in_progress = SimplePvr::Model::Recording.new(@channel, 'Borgia', first_start_time, 60.minutes)
|
79
|
+
starting_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', second_start_time, 60.minutes)
|
80
|
+
rejected_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', third_start_time, 60.minutes)
|
81
|
+
Time.stub(:now => second_start_time)
|
82
|
+
SimplePvr::Recorder.stub(:new).with(0, recording_in_progress).and_return(@recorder0 = double('Recorder'))
|
83
|
+
SimplePvr::Recorder.stub(:new).with(1, starting_recording).and_return(@recorder1 = double('Recorder'))
|
84
|
+
@recorder0.should_receive(:start!)
|
85
|
+
@recorder1.should_receive(:start!)
|
86
|
+
|
87
|
+
@scheduler.recordings = [recording_in_progress, starting_recording, rejected_recording]
|
88
|
+
@scheduler.process
|
89
|
+
|
90
|
+
recording_in_progress.should_not be_conflicting
|
91
|
+
starting_recording.should_not be_conflicting
|
92
|
+
rejected_recording.should be_conflicting
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'ends recordings at end time' do
|
96
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
97
|
+
ending_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)
|
98
|
+
SimplePvr::Recorder.stub(:new).with(0, ending_recording).and_return(@recorder = double('Recorder'))
|
99
|
+
@recorder.should_receive(:start!)
|
100
|
+
Time.stub(:now => start_time)
|
101
|
+
|
102
|
+
@scheduler.recordings = [ending_recording]
|
103
|
+
@scheduler.process
|
104
|
+
|
105
|
+
@recorder.should_receive(:stop!)
|
106
|
+
Time.stub(:now => start_time.advance(hours: 1, minutes: 1))
|
107
|
+
|
108
|
+
@scheduler.process
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'skips recordings that have passed' do
|
112
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
113
|
+
passed_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time - 65.minutes, 60.minutes)
|
114
|
+
Time.stub(:now => start_time)
|
115
|
+
|
116
|
+
@scheduler.recordings = [passed_recording]
|
117
|
+
@scheduler.process
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'knows which programmes are being recorded' do
|
121
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
122
|
+
Time.stub(:now => start_time.advance(hours: -1))
|
123
|
+
scheduled_programme = double(id: 2)
|
124
|
+
unscheduled_programme = double(id: 3)
|
125
|
+
|
126
|
+
@scheduler.recordings = [SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes, scheduled_programme)]
|
127
|
+
@scheduler.is_scheduled?(scheduled_programme).should be_true
|
128
|
+
@scheduler.is_scheduled?(unscheduled_programme).should be_false
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'gives idle status when nothing is recording' do
|
132
|
+
@scheduler.recordings = []
|
133
|
+
@scheduler.process
|
134
|
+
|
135
|
+
@scheduler.status_text.should == 'Idle'
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'gives recording status when recording' do
|
139
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
140
|
+
Time.stub(:now => start_time.advance(minutes: 30))
|
141
|
+
SimplePvr::Recorder.stub(new: (@recorder = double('Recorder')))
|
142
|
+
@recorder.stub(:start!)
|
143
|
+
|
144
|
+
@scheduler.recordings = [SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)]
|
145
|
+
@scheduler.process
|
146
|
+
|
147
|
+
@scheduler.status_text.should == "Recording 'Borgia' on channel 'DR K'"
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'when updating existing recordings' do
|
151
|
+
it 'leaves running recording if new recording is equal' do
|
152
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
153
|
+
continuing_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)
|
154
|
+
Time.stub(:now => start_time)
|
155
|
+
SimplePvr::Recorder.stub(:new).with(0, continuing_recording).and_return(@recorder = double('Recorder'))
|
156
|
+
@recorder.should_receive(:start!)
|
157
|
+
|
158
|
+
@scheduler.recordings = [continuing_recording]
|
159
|
+
@scheduler.process
|
160
|
+
|
161
|
+
@scheduler.recordings = [SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)]
|
162
|
+
@scheduler.process
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'stops existing recording if not present in new recording list' do
|
166
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
167
|
+
stopping_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)
|
168
|
+
upcoming_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time + 10.minutes, 60.minutes)
|
169
|
+
Time.stub(:now => start_time)
|
170
|
+
SimplePvr::Recorder.stub(:new).with(0, stopping_recording).and_return(@recorder = double('Recorder'))
|
171
|
+
@recorder.should_receive(:start!)
|
172
|
+
|
173
|
+
@scheduler.recordings = [stopping_recording]
|
174
|
+
@scheduler.process
|
175
|
+
|
176
|
+
@recorder.should_receive(:stop!)
|
177
|
+
|
178
|
+
@scheduler.recordings = [upcoming_recording]
|
179
|
+
@scheduler.process
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'stops existing recording and starts new recording if new recording list has other current recording' do
|
183
|
+
start_time = Time.local(2012, 7, 15, 20, 15, 30)
|
184
|
+
stopping_recording = SimplePvr::Model::Recording.new(@channel, 'Borgia', start_time, 60.minutes)
|
185
|
+
starting_recording = SimplePvr::Model::Recording.new(@channel_dr1, 'Sports', start_time, 60.minutes)
|
186
|
+
Time.stub(:now => start_time)
|
187
|
+
SimplePvr::Recorder.stub(:new).with(0, stopping_recording).and_return(@old_recorder = double('Recorder'))
|
188
|
+
SimplePvr::Recorder.stub(:new).with(0, starting_recording).and_return(@new_recorder = double('New recorder'))
|
189
|
+
@old_recorder.should_receive(:start!)
|
190
|
+
|
191
|
+
@scheduler.recordings = [stopping_recording]
|
192
|
+
@scheduler.process
|
193
|
+
|
194
|
+
@old_recorder.should_receive(:stop!)
|
195
|
+
@new_recorder.should_receive(:start!)
|
196
|
+
|
197
|
+
@scheduler.recordings = [starting_recording]
|
198
|
+
@scheduler.process
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#encoding: UTF-8
|
2
|
+
require 'simple_pvr'
|
3
|
+
|
4
|
+
describe SimplePvr::XmltvReader do
|
5
|
+
before do
|
6
|
+
@dr_1 = double(name: 'DR 1')
|
7
|
+
@dr_1.stub(:icon_url=)
|
8
|
+
|
9
|
+
SimplePvr::Model::Channel.stub(all: [@dr_1])
|
10
|
+
SimplePvr::Model::Programme.stub(:transaction).and_yield
|
11
|
+
SimplePvr::Model::Programme.stub(:destroy)
|
12
|
+
@xmltv_reader = SimplePvr::XmltvReader.new({'www.ontv.dk/tv/1' => 'DR 1'})
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'populates programme information through the DAO' do
|
16
|
+
SimplePvr::Model::Programme.stub(:add)
|
17
|
+
SimplePvr::Model::Programme.should_receive(:add).with(
|
18
|
+
@dr_1,
|
19
|
+
'Noddy',
|
20
|
+
'Bare vær dig selv, Noddy.',
|
21
|
+
"Tegnefilm.\nHer kommer Noddy - så kom ud og leg! Den lille dreng af træ har altid travlt med at køre sine venner rundt i Legebyen - og du kan altid høre, når han er på vej!",
|
22
|
+
Time.new(2012, 7, 17, 6, 0, 0, "+02:00"),
|
23
|
+
10.minutes,
|
24
|
+
' .2/12. ')
|
25
|
+
|
26
|
+
@xmltv_reader.read(File.new(File.dirname(__FILE__) + '/../resources/programs.xmltv'))
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'ignores programmes for channels with no mapping' do
|
30
|
+
# There are two channels in the XMLTV file, but only one with a mapping
|
31
|
+
SimplePvr::Model::Programme.should_receive(:add).exactly(5).times
|
32
|
+
|
33
|
+
@xmltv_reader.read(File.new(File.dirname(__FILE__) + '/../resources/programs.xmltv'))
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'adds channel icons to existing channels' do
|
37
|
+
SimplePvr::Model::Programme.stub(:add) # necessary to deal with the way Programme setup its relation to Channel
|
38
|
+
@dr_1.should_receive(:icon_url=).with("http://ontv.dk/imgs/epg/logos/dr1_big.png")
|
39
|
+
|
40
|
+
@xmltv_reader.read(File.new(File.dirname(__FILE__) + '/../resources/programs.xmltv'))
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'has no problem with xml with no channel icons' do
|
44
|
+
SimplePvr::Model::Programme.stub(:add) # necessary to deal with the way Programme setup its relation to Channel
|
45
|
+
@dr_1.should_not_receive(:icon_url=)
|
46
|
+
|
47
|
+
@xmltv_reader.read(File.new(File.dirname(__FILE__) + '/../resources/programs-without-icon.xmltv'))
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
server: http://localhost:9877
|
2
|
+
|
3
|
+
load:
|
4
|
+
- test/lib/angular/angular-scenario.js
|
5
|
+
- test/config/jstd-scenario-adapter-config.js
|
6
|
+
- test/lib/angular/jstd-scenario-adapter.js
|
7
|
+
- test/e2e/scenarios.js
|
8
|
+
|
9
|
+
proxy:
|
10
|
+
- {matcher: "*", server: "http://localhost:8000"}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
server: http://localhost:9876
|
2
|
+
|
3
|
+
load:
|
4
|
+
- test/lib/jasmine/jasmine.js
|
5
|
+
- test/lib/jasmine-jstd-adapter/JasmineAdapter.js
|
6
|
+
- public/js/angular/angular.min.js
|
7
|
+
- public/js/angular/angular-resource.min.js
|
8
|
+
- test/lib/angular/angular-mocks.js
|
9
|
+
- public/js/*.js
|
10
|
+
- test/*.js
|
11
|
+
|
12
|
+
exclude:
|
data/test/filtersSpec.js
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
describe('filters', function() {
|
4
|
+
beforeEach(module('simplePvr'));
|
5
|
+
|
6
|
+
describe('chunk', function() {
|
7
|
+
it('should let non-Array input pass through', inject(function(chunkFilter) {
|
8
|
+
var input = 'this is not an array';
|
9
|
+
expect(chunkFilter(input)).toBe(input);
|
10
|
+
}));
|
11
|
+
|
12
|
+
it('should let empty arrays pass through', inject(function(chunkFilter) {
|
13
|
+
expect(chunkFilter([], 3)).toEqual([]);
|
14
|
+
}));
|
15
|
+
|
16
|
+
it('should chunk up arrays and give them hashKeys corresponding to the chunk numbers', inject(function(chunkFilter) {
|
17
|
+
var input = [1, 2, 3, 4, 5, 6, 7];
|
18
|
+
var firstChunk = [1, 2, 3];
|
19
|
+
var secondChunk = [4, 5, 6];
|
20
|
+
var thirdChunk = [7];
|
21
|
+
|
22
|
+
firstChunk.$$hashKey = 0;
|
23
|
+
secondChunk.$$hashKey = 1;
|
24
|
+
thirdChunk.$$hashKey = 2;
|
25
|
+
|
26
|
+
expect(chunkFilter(input, 3)).toEqual([firstChunk, secondChunk, thirdChunk]);
|
27
|
+
}));
|
28
|
+
|
29
|
+
it('should create equal outputs given equal inputs', inject(function(chunkFilter) {
|
30
|
+
var output1 = chunkFilter([1, 2, 3, 4], 3);
|
31
|
+
var output2 = chunkFilter([1, 2, 3, 4], 3);
|
32
|
+
expect(angular.equals(output1, output2)).toBeTruthy();
|
33
|
+
}));
|
34
|
+
|
35
|
+
it('should create non-equal outputs given non-equal inputs', inject(function(chunkFilter) {
|
36
|
+
var output1 = chunkFilter([1, 2, 3, 4], 3);
|
37
|
+
var output2 = chunkFilter([4, 3, 2, 1], 3);
|
38
|
+
expect(angular.equals(output1, output2)).toBeFalsy();
|
39
|
+
}));
|
40
|
+
});
|
41
|
+
|
42
|
+
describe('weekdayFilter', function() {
|
43
|
+
it('should be blank when there is no weekday filtering', inject(function(filteredWeekdaysFilter) {
|
44
|
+
var scheduleWithNoWeekdayFiltering = { filter_by_weekday: false };
|
45
|
+
expect(filteredWeekdaysFilter(scheduleWithNoWeekdayFiltering)).toEqual('');
|
46
|
+
}));
|
47
|
+
|
48
|
+
it('should name a single weekday if only one is selected', inject(function(filteredWeekdaysFilter) {
|
49
|
+
var scheduleWithOneWeekdayFiltering = {
|
50
|
+
filter_by_weekday: true,
|
51
|
+
monday: false,
|
52
|
+
tuesday: false,
|
53
|
+
wednesday: true,
|
54
|
+
thursday: false,
|
55
|
+
friday: false,
|
56
|
+
saturday: false,
|
57
|
+
sunday: false
|
58
|
+
};
|
59
|
+
expect(filteredWeekdaysFilter(scheduleWithOneWeekdayFiltering)).toEqual('(Wednesdays)');
|
60
|
+
}));
|
61
|
+
|
62
|
+
it('should name two weekdays if exactly two are selected', inject(function(filteredWeekdaysFilter) {
|
63
|
+
var scheduleWithTwoWeekdayFilterings = {
|
64
|
+
filter_by_weekday: true,
|
65
|
+
monday: true,
|
66
|
+
tuesday: false,
|
67
|
+
wednesday: true,
|
68
|
+
thursday: false,
|
69
|
+
friday: false,
|
70
|
+
saturday: false,
|
71
|
+
sunday: false
|
72
|
+
};
|
73
|
+
expect(filteredWeekdaysFilter(scheduleWithTwoWeekdayFilterings)).toEqual('(Mondays and Wednesdays)');
|
74
|
+
}));
|
75
|
+
|
76
|
+
it('should list all weekdays for three and more allowed weekdays', inject(function(filteredWeekdaysFilter) {
|
77
|
+
var scheduleWithSeveralWeekdayFilterings = {
|
78
|
+
filter_by_weekday: true,
|
79
|
+
monday: true,
|
80
|
+
tuesday: false,
|
81
|
+
wednesday: true,
|
82
|
+
thursday: false,
|
83
|
+
friday: true,
|
84
|
+
saturday: false,
|
85
|
+
sunday: false
|
86
|
+
};
|
87
|
+
expect(filteredWeekdaysFilter(scheduleWithSeveralWeekdayFilterings)).toEqual('(Mondays, Wednesdays, and Fridays)');
|
88
|
+
scheduleWithSeveralWeekdayFilterings.saturday = true;
|
89
|
+
expect(filteredWeekdaysFilter(scheduleWithSeveralWeekdayFilterings)).toEqual('(Mondays, Wednesdays, Fridays, and Saturdays)');
|
90
|
+
scheduleWithSeveralWeekdayFilterings.tuesday = true;
|
91
|
+
scheduleWithSeveralWeekdayFilterings.thursday = true;
|
92
|
+
scheduleWithSeveralWeekdayFilterings.saturday = true;
|
93
|
+
scheduleWithSeveralWeekdayFilterings.sunday = true;
|
94
|
+
expect(filteredWeekdaysFilter(scheduleWithSeveralWeekdayFilterings)).toEqual('(Mondays, Tuesdays, Wednesdays, Thursdays, Fridays, Saturdays, and Sundays)')
|
95
|
+
}));
|
96
|
+
});
|
97
|
+
});
|