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,98 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<tv generator-info-name="www.ontv.dk/xmltv">
|
3
|
+
<channel id="www.ontv.dk/tv/1">
|
4
|
+
<display-name lang="dk">DR1 DK</display-name>
|
5
|
+
<icon src="http://ontv.dk/imgs/epg/logos/dr1_big.png"/>
|
6
|
+
</channel>
|
7
|
+
<channel id="www.ontv.dk/tv/2">
|
8
|
+
<display-name lang="dk">DR2 DK</display-name>
|
9
|
+
<icon src="http://ontv.dk/imgs/epg/logos/dr2_big.png"/>
|
10
|
+
</channel>
|
11
|
+
|
12
|
+
<programme channel="www.ontv.dk/tv/1" start="20120717060000 +0200" stop="20120717061000 +0200">
|
13
|
+
<title lang="dk">Noddy</title>
|
14
|
+
<sub-title lang="dk">Bare vær dig selv, Noddy.</sub-title>
|
15
|
+
<desc lang="dk">Tegnefilm.
|
16
|
+
Her 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!</desc>
|
17
|
+
<category lang="EN">kids</category>
|
18
|
+
<category lang="EN">serie</category>
|
19
|
+
<episode-num system="xmltv_ns"> .2/12. </episode-num>
|
20
|
+
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/1/11342497600.jpg&height=300&width=300"/>
|
21
|
+
</programme>
|
22
|
+
<programme channel="www.ontv.dk/tv/1" start="20120717061000 +0200" stop="20120717062000 +0200">
|
23
|
+
<title lang="dk">Sprutte-Patruljen</title>
|
24
|
+
<desc lang="dk">Spruttepatruljen og algesuppen.</desc>
|
25
|
+
<category lang="EN">kids</category>
|
26
|
+
<category lang="EN">serie</category>
|
27
|
+
</programme>
|
28
|
+
<programme channel="www.ontv.dk/tv/1" start="20120717062000 +0200" stop="20120717064000 +0200">
|
29
|
+
<title lang="dk">Kære Sebastian</title>
|
30
|
+
<sub-title lang="dk">Geparden skal have kød.</sub-title>
|
31
|
+
<desc lang="dk">Zoo's vildeste dyr. Sebastian Klein overfaldes og skubbes omkuld af en af zoo's vildeste dyr - gederne. Det er mad tid og de er sultne.</desc>
|
32
|
+
<category lang="EN">kids</category>
|
33
|
+
<category lang="EN">serie</category>
|
34
|
+
</programme>
|
35
|
+
<programme channel="www.ontv.dk/tv/1" start="20120717064000 +0200" stop="20120717065000 +0200">
|
36
|
+
<title lang="dk">Rasmus Klump på Nordpolen</title>
|
37
|
+
<desc lang="dk">Vi tager atter med Rasmus Klump og hans venner på eventyr.</desc>
|
38
|
+
<category lang="EN">kids</category>
|
39
|
+
</programme>
|
40
|
+
<programme channel="www.ontv.dk/tv/1" start="20120717065000 +0200" stop="20120717070000 +0200">
|
41
|
+
<title lang="dk">Olivia</title>
|
42
|
+
<sub-title lang="dk">Olivia på scenen.</sub-title>
|
43
|
+
<desc lang="dk">Animation med dansk tale.
|
44
|
+
Slå griseører og øjne ud, for modige Olivia er klar til at tage verden med storm! Den skæve, skøre og sjove gris Olivia er god til alt muligt, bl.a. at gøre folk rigtig trætte. Olivia kan nemlig som ingen anden prøve tøj fra skabet eller male billeder på væggen. Olivia har også startet sit eget band og sågar optrådt i cirkus.</desc>
|
45
|
+
<category lang="EN">kids</category>
|
46
|
+
<category lang="EN">serie</category>
|
47
|
+
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/1/11342500600.jpg&height=300&width=300"/>
|
48
|
+
</programme>
|
49
|
+
|
50
|
+
<programme channel="www.ontv.dk/tv/2" start="20120717080500 +0200" stop="20120717120000 +0200">
|
51
|
+
<title lang="dk">Morgenandagten på DR2</title>
|
52
|
+
<desc lang="dk">DR2 samsender med P2 Klassisk: Morgenandagten fra Københavns Domkirke. Den direkte radiotransmission af Morgenandagten ledsages af stemningsfulde billeder fra kirken samt kobbertryk af billedkunstneren Robert Jacobsen i anledning af 100-året for hans fødsel. Der er mulighed for at følge med i tekstlæsninger og salmesang, idet andagten tekstes.
|
53
|
+
www.dr.dk/tro</desc>
|
54
|
+
</programme>
|
55
|
+
<programme channel="www.ontv.dk/tv/2" start="20120717120000 +0200" stop="20120717123000 +0200">
|
56
|
+
<title lang="dk">Black Business</title>
|
57
|
+
<desc lang="dk">Hvis du har den rigtige idé, mod og eventyrlyst, kan du gøre store forretninger og tjene mange penge i Afrika. For Afrika er økonomisk set blandt de hurtigste voksende regioner i verden. Det sker lige nu, og det gælder om at komme først. Esben Sørensen er en rødhåret dansker på 30 år, der er vokset op delvist i Afrika og delvist i Danmark. Han har et pladeselskab, hvor han indtil nu kun har udgivet en enkelt plade - med sig selv. Drømmen er at Caravan Records bliver det største pladeselskab i Østafrika, og det er absolut ikke urealistisk, for Østafrika er fyldt med musikalsk talent, markedet er gigantisk og afrikanernes købekraft stiger.
|
58
|
+
Sendt første gang 25.01.11</desc>
|
59
|
+
<category lang="EN">documentary</category>
|
60
|
+
<category lang="EN">serie</category>
|
61
|
+
<episode-num system="xmltv_ns"> .1/4. </episode-num>
|
62
|
+
</programme>
|
63
|
+
<programme channel="www.ontv.dk/tv/2" start="20120717123000 +0200" stop="20120717130000 +0200">
|
64
|
+
<title lang="dk">Solens mad</title>
|
65
|
+
<desc lang="dk">Vi skal med på en rejse til madens og vinens Italien, vi skal på besøg hos nogle af de mange bønder i landet, som ikke går på kompromis med den gode smag og som tilhører den stadigt voksende bevægelse af producenter, der har en stor forståelse for, at miljø, klima og bæredygtighed er hjørnesten i alle produkter.
|
66
|
+
Overgødning og miljøgifte har ødelagt mange af Europas indsøer. I dag skal vi til Trasimeno-søen i Umbrien, den fjerdestørste indsø i Italien. Her lever fiskere og bønder side om side, begge parter meget bevidste om at bevare vandet og jorden her i landets grønne hjerte.
|
67
|
+
Nordvision fra Sverige.</desc>
|
68
|
+
<category lang="EN">entertainment</category>
|
69
|
+
<category lang="EN">serie</category>
|
70
|
+
<episode-num system="xmltv_ns"> .2/6. </episode-num>
|
71
|
+
</programme>
|
72
|
+
<programme channel="www.ontv.dk/tv/2" start="20120717130000 +0200" stop="20120717133000 +0200">
|
73
|
+
<title lang="dk">Øl i Frilandshaven</title>
|
74
|
+
<desc lang="dk">Sommerens bedste bryg bliver lavet over åben ild, og i udekøkkenet står menuen på kalvehaler i mørkt øl og jordbær i balsamico.
|
75
|
+
Sendt første gang 16.08.06</desc>
|
76
|
+
<category lang="EN">entertainment</category>
|
77
|
+
<credits>
|
78
|
+
<actor role="Organizer">Jørgen Skouboe</actor>
|
79
|
+
<actor role="Organizer">Anne Hjernøe</actor>
|
80
|
+
</credits>
|
81
|
+
</programme>
|
82
|
+
<programme channel="www.ontv.dk/tv/2" start="20120717133000 +0200" stop="20120717142500 +0200">
|
83
|
+
<title lang="dk">Intelligence - i hemmelig tjeneste</title>
|
84
|
+
<sub-title lang="dk">Italiensk actionserie fra 2009.</sub-title>
|
85
|
+
<desc lang="dk">Den tidligere jægersoldat Marco Tancredis hustru, Lidia, er blevet skudt. Marco er sikker på, at det var overlagt mord, og han er parat til at gøre hvad som helst for at finde ud af, hvem der står bag. Snart gør han en forbløffende opdagelse - nemlig at Lidia arbejdede for den italienske efterretningstjeneste. Men hvad arbejdede hun på? Og hvorfor skulle hun slås ihjel?</desc>
|
86
|
+
<category lang="EN">serie</category>
|
87
|
+
<episode-num system="xmltv_ns"> .1/12. </episode-num>
|
88
|
+
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/2/21342524600.jpg&height=300&width=300"/>
|
89
|
+
</programme>
|
90
|
+
<programme channel="www.ontv.dk/tv/2" start="20120717142500 +0200" stop="20120717152000 +0200">
|
91
|
+
<title lang="dk">Intelligence - i hemmelig tjeneste</title>
|
92
|
+
<sub-title lang="dk">Italiensk actionserie fra 2009.</sub-title>
|
93
|
+
<desc lang="dk">Den tidligere jægersoldat Marco Tancredis hustru er blevet myrdet. Marco har opdaget, at hun arbejdede for den italienske efterretningstjeneste, og nu har han selv ladet sig ansætte som efterretningsagent for at finde frem til sandheden om hendes død. Marcos jagt på svar bringer ham til Rumænien - og ansigt til ansigt med en kvinde, der er ude på sit eget private hævntogt.</desc>
|
94
|
+
<category lang="EN">serie</category>
|
95
|
+
<episode-num system="xmltv_ns"> .2/12. </episode-num>
|
96
|
+
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/2/21342527900.jpg&height=300&width=300"/>
|
97
|
+
</programme>
|
98
|
+
</tv>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::Ffmpeg do
|
4
|
+
it 'spawns an ffmpeg process for creating thumbnails' do
|
5
|
+
Process.should_receive(:spawn)
|
6
|
+
Process.should_receive(:detach)
|
7
|
+
|
8
|
+
SimplePvr::Ffmpeg.create_thumbnail_for('path/to/show')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'spawns an ffmpeg process for transcoding to WebM' do
|
12
|
+
File.should_receive(:exists?).with('path/to/show/stream.webm').and_return(false)
|
13
|
+
Process.should_receive(:spawn)
|
14
|
+
Process.should_receive(:detach)
|
15
|
+
|
16
|
+
SimplePvr::Ffmpeg.transcode_to_webm('path/to/show')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does not start ffmpeg for transcoding to WebM if WebM file already exists' do
|
20
|
+
File.should_receive(:exists?).with('path/to/show/stream.webm').and_return(true)
|
21
|
+
Process.should_not_receive(:spawn)
|
22
|
+
Process.should_not_receive(:detach)
|
23
|
+
|
24
|
+
SimplePvr::Ffmpeg.transcode_to_webm('path/to/show')
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::HDHomeRun do
|
4
|
+
before do
|
5
|
+
@pipe = double('pipe')
|
6
|
+
IO.should_receive(:popen).with('hdhomerun_config discover').and_yield(@pipe)
|
7
|
+
FileUtils.stub(:exists? => false)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'when initializing' do
|
11
|
+
it 'discovers a device when present' do
|
12
|
+
@pipe.stub(:read => 'hdhomerun device ABCDEF01 found at 10.0.0.4')
|
13
|
+
|
14
|
+
SimplePvr::HDHomeRun.new.device_id.should == 'ABCDEF01'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'raises an exception when no device is present' do
|
18
|
+
@pipe.stub(:read => 'no devices found')
|
19
|
+
|
20
|
+
expect { SimplePvr::HDHomeRun.new }.to raise_error(Exception, 'No device found: no devices found')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when initialized' do
|
25
|
+
before do
|
26
|
+
@pipe.stub(:read => 'hdhomerun device ABCDEF01 found at 10.0.0.4')
|
27
|
+
@hdhomerun = SimplePvr::HDHomeRun.new
|
28
|
+
@hdhomerun.stub(:tuner_control_file).with(0).and_return('tuner0.lock')
|
29
|
+
@hdhomerun.stub(:tuner_control_file).with(1).and_return('tuner1.lock')
|
30
|
+
@file = File.open(File.dirname(__FILE__) + '/../resources/channels.txt', 'r:UTF-8')
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
@file.close
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can do a channel scan' do
|
38
|
+
@hdhomerun.should_receive(:system).with('hdhomerun_config ABCDEF01 scan /tuner0 channels.txt')
|
39
|
+
SimplePvr::Model::Channel.should_receive(:clear)
|
40
|
+
File.should_receive(:open).with('channels.txt', 'r:UTF-8').and_yield(@file)
|
41
|
+
SimplePvr::Model::Channel.should_receive(:add).with('DR K', 282000000, 1098)
|
42
|
+
SimplePvr::Model::Channel.should_receive(:add).with('DR HD', 282000000, 1165)
|
43
|
+
|
44
|
+
@hdhomerun.scan_for_channels
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can start recording on one tuner' do
|
48
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/channel auto:282000000")
|
49
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/program 1098")
|
50
|
+
@hdhomerun.should_receive(:spawn) #... with a lot of arguments ...
|
51
|
+
FileUtils.should_receive(:touch).with('tuner0.lock')
|
52
|
+
|
53
|
+
@hdhomerun.start_recording(0, 282000000, 1098, 'test directory')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'can stop recording on one tuner' do
|
57
|
+
@hdhomerun.stub(:system)
|
58
|
+
@hdhomerun.stub(:spawn => 32)
|
59
|
+
|
60
|
+
FileUtils.should_receive(:touch).with('tuner0.lock')
|
61
|
+
FileUtils.should_receive(:rm).with('tuner0.lock')
|
62
|
+
Process.should_receive(:wait).with(32)
|
63
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/channel none")
|
64
|
+
|
65
|
+
@hdhomerun.start_recording(0, 282000000, 1098, 'test directory')
|
66
|
+
@hdhomerun.stop_recording(0)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'can start and stop recording on another tuner' do
|
70
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/channel auto:282000000")
|
71
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/program 1098")
|
72
|
+
@hdhomerun.stub(:spawn => 32)
|
73
|
+
FileUtils.should_receive(:touch).with('tuner1.lock')
|
74
|
+
FileUtils.should_receive(:rm).with('tuner1.lock')
|
75
|
+
Process.should_receive(:wait).with(32)
|
76
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/channel none")
|
77
|
+
|
78
|
+
@hdhomerun.start_recording(1, 282000000, 1098, 'test directory')
|
79
|
+
@hdhomerun.stop_recording(1)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::Model::Channel do
|
4
|
+
Channel, Programme = SimplePvr::Model::Channel, SimplePvr::Model::Programme
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
SimplePvr::Model::DatabaseInitializer.prepare_for_test
|
8
|
+
end
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
SimplePvr::Model::DatabaseInitializer.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'can insert channels' do
|
15
|
+
3.times {|i| Channel.add("Channel #{i}", 23000000, 1098) }
|
16
|
+
|
17
|
+
Channel.all.length.should == 3
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can clear channels' do
|
21
|
+
3.times {|i| Channel.add("Channel #{i}", 23000000, 1098) }
|
22
|
+
Channel.clear
|
23
|
+
|
24
|
+
Channel.all.length.should == 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'clears all programmes when clearing channels' do
|
28
|
+
channel = Channel.add("DR 1", 23000000, 1098)
|
29
|
+
3.times { Programme.add(channel, 'Title', 'Subtitle', 'Description', Time.local(2012, 7, 17, 20, 30), 50.minutes, nil) }
|
30
|
+
Channel.clear
|
31
|
+
|
32
|
+
Programme.all.length.should == 0
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'can find channels' do
|
36
|
+
Channel.add('Known channel', 23000000, 1098)
|
37
|
+
|
38
|
+
channel = Channel.with_name('Known channel')
|
39
|
+
channel.name.should == 'Known channel'
|
40
|
+
channel.frequency.should == 23000000
|
41
|
+
channel.channel_id.should == 1098
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'complains when asked for non-existing channel' do
|
45
|
+
expect { Channel.with_name('unknown') }.to raise_error "Unknown channel: 'unknown'"
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can fetch a channel along with the current and next 3 programmes' do
|
49
|
+
channel = Channel.create(name: 'Channel 1')
|
50
|
+
|
51
|
+
now = Time.now
|
52
|
+
ten_minutes = 10.minutes.to_i
|
53
|
+
current = Programme.create(channel: channel, title: 'Current programme on channel 1', subtitle: 'Current programme subtitle', start_time: now.advance(minutes: -3), duration: ten_minutes)
|
54
|
+
programme_2 = Programme.create(channel: channel, title: 'Next programme', subtitle: 'Next programme subtitle', start_time: now.advance(minutes: 7), duration: ten_minutes)
|
55
|
+
programme_3 = Programme.create(channel: channel, title: 'Programme 3', start_time: now.advance(minutes: 17), duration: ten_minutes)
|
56
|
+
programme_4 = Programme.create(channel: channel, title: 'Programme 4', start_time: now.advance(minutes: 27), duration: ten_minutes)
|
57
|
+
|
58
|
+
channel_and_programmes = Channel.with_current_programmes(channel.id)
|
59
|
+
|
60
|
+
channel_and_programmes[:channel].should == channel
|
61
|
+
channel_and_programmes[:current_programme].should == current
|
62
|
+
channel_and_programmes[:upcoming_programmes].should == [programme_2, programme_3, programme_4]
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'does not fetch programmes for hidden channels' do
|
66
|
+
channel = Channel.create(name: 'Channel 1')
|
67
|
+
channel.hidden = true
|
68
|
+
channel.save!
|
69
|
+
|
70
|
+
now = Time.now
|
71
|
+
ten_minutes = 10.minutes.to_i
|
72
|
+
current = Programme.create(channel: channel, title: 'Current programme on channel 1', subtitle: 'Current programme subtitle', start_time: now.advance(minutes: -3), duration: ten_minutes)
|
73
|
+
programme_2 = Programme.create(channel: channel, title: 'Next programme', subtitle: 'Next programme subtitle', start_time: now.advance(minutes: 7), duration: ten_minutes)
|
74
|
+
|
75
|
+
channel_and_programmes = Channel.with_current_programmes(channel.id)
|
76
|
+
|
77
|
+
channel_and_programmes[:channel].should == channel
|
78
|
+
channel_and_programmes[:current_programme].should be_nil
|
79
|
+
channel_and_programmes[:upcoming_programmes].should == []
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'can fetch all channels, along with the current and next 3 programmes' do
|
83
|
+
channel_1 = Channel.add('Channel 1', 23000000, 1098)
|
84
|
+
channel_2 = Channel.add('Channel 2', 23000000, 1098)
|
85
|
+
channel_3 = Channel.add('Channel 3', 23000000, 1098)
|
86
|
+
|
87
|
+
now = Time.now
|
88
|
+
ten_minutes = 10.minutes.to_i
|
89
|
+
current_on_channel_1 = Programme.create(channel: channel_1, title: 'Current programme on channel 1', subtitle: 'Current programme subtitle', start_time: now.advance(minutes: -3), duration: ten_minutes)
|
90
|
+
programme_2_on_channel_1 = Programme.create(channel: channel_1, title: 'Next programme', subtitle: 'Next programme subtitle', start_time: now.advance(minutes: 7), duration: ten_minutes)
|
91
|
+
programme_3_on_channel_1 = Programme.create(channel: channel_1, title: 'Programme 3', start_time: now.advance(minutes: 17), duration: ten_minutes)
|
92
|
+
programme_4_on_channel_1 = Programme.create(channel: channel_1, title: 'Programme 4', start_time: now.advance(minutes: 27), duration: ten_minutes)
|
93
|
+
programme_5_on_channel_1 = Programme.create(channel: channel_1, title: 'Programme 5', start_time: now.advance(minutes: 37), duration: ten_minutes)
|
94
|
+
|
95
|
+
programme_1_on_channel_2 = Programme.create(channel: channel_2, title: 'Next programme on channel 2', start_time: now.advance(minutes: 17), duration: ten_minutes)
|
96
|
+
|
97
|
+
old_programme_on_channel_3 = Programme.create(channel: channel_2, title: 'Obsolete programme', start_time: now.advance(minutes: -20), duration: ten_minutes)
|
98
|
+
|
99
|
+
channels = Channel.all_with_current_programmes
|
100
|
+
channels.length.should == 3
|
101
|
+
|
102
|
+
channels[0][:channel].should == channel_1
|
103
|
+
channels[0][:current_programme].should == current_on_channel_1
|
104
|
+
channels[0][:upcoming_programmes].should == [programme_2_on_channel_1, programme_3_on_channel_1, programme_4_on_channel_1]
|
105
|
+
|
106
|
+
channels[1][:channel].should == channel_2
|
107
|
+
channels[1][:current_programme].should be_nil
|
108
|
+
channels[1][:upcoming_programmes].should == [programme_1_on_channel_2]
|
109
|
+
|
110
|
+
channels[2][:channel].should == channel_3
|
111
|
+
channels[2][:current_programme].should be_nil
|
112
|
+
channels[2][:upcoming_programmes].should == []
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::Model::Programme do
|
4
|
+
Channel, Programme = SimplePvr::Model::Channel, SimplePvr::Model::Programme
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
SimplePvr::Model::DatabaseInitializer.prepare_for_test
|
8
|
+
end
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
SimplePvr::Model::DatabaseInitializer.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
@dr_1 = Channel.add('DR 1', 23000000, 1098)
|
16
|
+
@dr_2 = Channel.add('DR 2', 24000000, 1099)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'can insert programmes' do
|
20
|
+
3.times { Programme.add(@dr_1, 'Title', 'Subtitle', 'Description', Time.local(2012, 7, 17, 20, 30), 50.minutes, ' .4/12. ') }
|
21
|
+
|
22
|
+
Programme.all.length.should == 3
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can clear all programmes' do
|
26
|
+
3.times { Programme.add(@dr_1, 'Title', 'Subtitle', 'Description', Time.local(2012, 7, 17, 20, 30), 50.minutes, nil) }
|
27
|
+
Programme.clear
|
28
|
+
|
29
|
+
Programme.all.length.should == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can find all programmes with a certain title' do
|
33
|
+
Programme.add(@dr_2, 'Interesting', 'Second', 'Description', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
34
|
+
Programme.add(@dr_1, 'Interesting', 'First', 'Description', Time.local(2012, 7, 17, 20, 30), 50.minutes, nil)
|
35
|
+
Programme.add(@dr_1, 'Uninteresting', 'Subtitle', 'Description', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
36
|
+
|
37
|
+
programmes = Programme.with_title('Interesting')
|
38
|
+
programmes.length.should == 2
|
39
|
+
|
40
|
+
programmes[0].channel.should == @dr_1
|
41
|
+
programmes[0].title.should == 'Interesting'
|
42
|
+
programmes[0].subtitle.should == 'First'
|
43
|
+
programmes[0].description.should == 'Description'
|
44
|
+
programmes[0].start_time.should == Time.local(2012, 7, 17, 20, 30)
|
45
|
+
programmes[0].duration.should == 50.minutes
|
46
|
+
|
47
|
+
programmes[1].channel.should == @dr_2
|
48
|
+
programmes[1].title.should == 'Interesting'
|
49
|
+
programmes[1].subtitle.should == 'Second'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'can find all programmes with a certain title for a specific channel' do
|
53
|
+
Programme.add(@dr_1, 'Interesting', 'Second', 'Description', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
54
|
+
Programme.add(@dr_1, 'Interesting', 'First', 'Description', Time.local(2012, 7, 17, 20, 30), 50.minutes, nil)
|
55
|
+
Programme.add(@dr_2, 'Interesting', '...but on wrong channel...', 'Description', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
56
|
+
Programme.add(@dr_1, 'Uninteresting', 'Subtitle', 'Description', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
57
|
+
|
58
|
+
programmes = Programme.on_channel_with_title(@dr_1, 'Interesting')
|
59
|
+
programmes.length.should == 2
|
60
|
+
|
61
|
+
programmes[0].channel.should == @dr_1
|
62
|
+
programmes[0].title.should == 'Interesting'
|
63
|
+
programmes[0].subtitle.should == 'First'
|
64
|
+
|
65
|
+
programmes[1].channel.should == @dr_1
|
66
|
+
programmes[1].title.should == 'Interesting'
|
67
|
+
programmes[1].subtitle.should == 'Second'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'can find titles containing a certain string' do
|
71
|
+
Programme.add(@dr_1, 'First programme', '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
72
|
+
Programme.add(@dr_2, 'Second programme', '', '', Time.local(2012, 7, 17, 20, 30), 50.minutes, nil)
|
73
|
+
Programme.add(@dr_1, 'Uninteresting', '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
74
|
+
|
75
|
+
titles = Programme.titles_containing('programme')
|
76
|
+
titles.should == ['First programme', 'Second programme']
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'gives no duplicates as title search' do
|
80
|
+
Programme.add(@dr_1, 'First programme', '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
81
|
+
Programme.add(@dr_1, 'First programme', '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
82
|
+
Programme.add(@dr_2, 'Second programme', '', '', Time.local(2012, 7, 17, 20, 30), 50.minutes, nil)
|
83
|
+
|
84
|
+
titles = Programme.titles_containing('programme')
|
85
|
+
titles.should == ['First programme', 'Second programme']
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'finds at most 8 titles containing a certain string' do
|
89
|
+
20.times {|i| Programme.add(@dr_1, "Programme #{i}", '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil) }
|
90
|
+
|
91
|
+
titles = Programme.titles_containing('programme')
|
92
|
+
titles.length.should == 8
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'can find programmes with titles containing a certain string, ordered by start time' do
|
96
|
+
first_programme = Programme.add(@dr_1, 'First programme', '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
97
|
+
second_programme = Programme.add(@dr_2, 'Second programme', '', '', Time.local(2012, 7, 17, 20, 30), 50.minutes, nil)
|
98
|
+
Programme.add(@dr_1, 'Uninteresting', '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil)
|
99
|
+
|
100
|
+
titles = Programme.with_title_containing('programme')
|
101
|
+
titles.should == [second_programme, first_programme]
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'finds at most 20 programmes with titles containing a certain string' do
|
105
|
+
30.times {|i| Programme.add(@dr_1, "Programme #{i}", '', '', Time.local(2012, 7, 24, 20, 30), 50.minutes, nil) }
|
106
|
+
|
107
|
+
titles = Programme.with_title_containing('programme')
|
108
|
+
titles.length.should == 20
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::Model::Schedule do
|
4
|
+
Channel, Schedule = SimplePvr::Model::Channel, SimplePvr::Model::Schedule
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
SimplePvr::Model::DatabaseInitializer.prepare_for_test
|
8
|
+
end
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
SimplePvr::Model::DatabaseInitializer.clear
|
12
|
+
@dr_1 = Channel.add('DR 1', 23000000, 1098)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can save a schedule with a title' do
|
16
|
+
Schedule.add_specification(:title => 'Sports')
|
17
|
+
|
18
|
+
schedules = Schedule.all
|
19
|
+
schedules.length.should == 1
|
20
|
+
schedules[0].type.should == :specification
|
21
|
+
schedules[0].title.should == 'Sports'
|
22
|
+
schedules[0].channel.should be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can save a schedule with a title and a channel' do
|
26
|
+
Schedule.add_specification(:title => 'Sports', :channel => @dr_1)
|
27
|
+
|
28
|
+
schedules = Schedule.all
|
29
|
+
schedules.length.should == 1
|
30
|
+
schedules[0].type.should == :specification
|
31
|
+
schedules[0].title.should == 'Sports'
|
32
|
+
schedules[0].channel.name.should == 'DR 1'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'can save a schedule with a title and a channel and a start_time' do
|
36
|
+
start_time = Time.local(2012, 7, 10, 20, 50)
|
37
|
+
Schedule.add_specification(title: 'Sports', channel: @dr_1, start_time: start_time)
|
38
|
+
|
39
|
+
schedules = Schedule.all
|
40
|
+
schedules.length.should == 1
|
41
|
+
schedules[0].type.should == :specification
|
42
|
+
schedules[0].title.should == 'Sports'
|
43
|
+
schedules[0].channel.name.should == 'DR 1'
|
44
|
+
schedules[0].start_time.should == start_time
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::PvrInitializer do
|
4
|
+
before do
|
5
|
+
SimplePvr::Model::DatabaseInitializer.stub(:setup)
|
6
|
+
|
7
|
+
@scheduler = double('Scheduler')
|
8
|
+
SimplePvr::Scheduler.stub(new: @scheduler)
|
9
|
+
|
10
|
+
@hdhomerun = double('HDHomeRun')
|
11
|
+
SimplePvr::HDHomeRun.stub(new: @hdhomerun)
|
12
|
+
|
13
|
+
@recording_manager = double('RecordingManager')
|
14
|
+
SimplePvr::RecordingManager.stub(new: @recording_manager)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'starts the scheduler' do
|
18
|
+
SimplePvr::Model::Channel.stub(all: [1, 2, 3, 4, 5])
|
19
|
+
@scheduler.should_receive(:start)
|
20
|
+
|
21
|
+
SimplePvr::PvrInitializer.setup
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when scheduler is started' do
|
25
|
+
before do
|
26
|
+
@scheduler.stub(:start)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'runs a channel scan if channels are missing' do
|
30
|
+
SimplePvr::Model::Channel.stub(all: [])
|
31
|
+
@hdhomerun.should_receive(:scan_for_channels)
|
32
|
+
|
33
|
+
SimplePvr::PvrInitializer.setup
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does nothing if channels.txt is present' do
|
37
|
+
SimplePvr::Model::Channel.stub(all: [1])
|
38
|
+
|
39
|
+
SimplePvr::PvrInitializer.setup
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'initializes a HDHomeRun and RecordingManager instance' do
|
43
|
+
SimplePvr::Model::Channel.stub(all: [1])
|
44
|
+
|
45
|
+
SimplePvr::PvrInitializer.setup
|
46
|
+
SimplePvr::PvrInitializer.hdhomerun.should == @hdhomerun
|
47
|
+
SimplePvr::PvrInitializer.recording_manager.should == @recording_manager
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'simple_pvr'
|
2
|
+
|
3
|
+
describe SimplePvr::Recorder do
|
4
|
+
before do
|
5
|
+
@channel = SimplePvr::Model::Channel.new(frequency: 282000000, channel_id: 1098)
|
6
|
+
@recording = SimplePvr::Model::Recording.new(@channel, 'Star Trek', 'start time', 'duration')
|
7
|
+
|
8
|
+
@hdhomerun = double('HDHomeRun')
|
9
|
+
SimplePvr::PvrInitializer.stub(hdhomerun: @hdhomerun)
|
10
|
+
|
11
|
+
@recording_manager = double('RecordingManager')
|
12
|
+
@recording_manager.stub(:create_directory_for_recording).with(@recording).and_return('recording directory')
|
13
|
+
SimplePvr::PvrInitializer.stub(recording_manager: @recording_manager)
|
14
|
+
|
15
|
+
@recorder = SimplePvr::Recorder.new(1, @recording)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can start recording' do
|
19
|
+
@hdhomerun.should_receive(:start_recording).with(1, 282000000, 1098, 'recording directory')
|
20
|
+
|
21
|
+
@recorder.start!
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can stop recording as well, and creates a thumbnail' do
|
25
|
+
@hdhomerun.stub(:start_recording)
|
26
|
+
@hdhomerun.should_receive(:stop_recording).with(1)
|
27
|
+
SimplePvr::Ffmpeg.should_receive(:create_thumbnail_for).with('recording directory')
|
28
|
+
|
29
|
+
@recorder.start!
|
30
|
+
@recorder.stop!
|
31
|
+
end
|
32
|
+
end
|