simple_pvr 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +1 -4
  5. data/Gemfile.lock +72 -61
  6. data/README.md +51 -33
  7. data/bin/pvr_server +14 -6
  8. data/changelog.txt +11 -0
  9. data/development_server +14 -6
  10. data/features/channel_overview.feature +1 -1
  11. data/features/recordings.feature +30 -0
  12. data/features/step_definitions/pvr_steps.rb +34 -0
  13. data/features/support/env.rb +1 -2
  14. data/features/support/paths.rb +2 -0
  15. data/lib/simple_pvr.rb +2 -0
  16. data/lib/simple_pvr/model/database_initializer.rb +8 -0
  17. data/lib/simple_pvr/model/programme.rb +12 -2
  18. data/lib/simple_pvr/model/programme_actor.rb +14 -0
  19. data/lib/simple_pvr/model/programme_category.rb +14 -0
  20. data/lib/simple_pvr/model/programme_director.rb +13 -0
  21. data/lib/simple_pvr/model/programme_presenter.rb +13 -0
  22. data/lib/simple_pvr/model/recording.rb +12 -0
  23. data/lib/simple_pvr/programme_icon_fetcher.rb +15 -0
  24. data/lib/simple_pvr/pvr_initializer.rb +4 -4
  25. data/lib/simple_pvr/recorder.rb +4 -3
  26. data/lib/simple_pvr/recording_manager.rb +44 -24
  27. data/lib/simple_pvr/recording_planner.rb +3 -3
  28. data/lib/simple_pvr/scheduler.rb +12 -4
  29. data/lib/simple_pvr/server/base_controller.rb +12 -13
  30. data/lib/simple_pvr/server/channels_controller.rb +5 -5
  31. data/lib/simple_pvr/server/programmes_controller.rb +2 -2
  32. data/lib/simple_pvr/server/schedules_controller.rb +4 -4
  33. data/lib/simple_pvr/server/secured_controller.rb +71 -0
  34. data/lib/simple_pvr/server/shows_controller.rb +13 -8
  35. data/lib/simple_pvr/server/status_controller.rb +1 -1
  36. data/lib/simple_pvr/server/upcoming_recordings_controller.rb +1 -1
  37. data/lib/simple_pvr/version.rb +1 -1
  38. data/lib/simple_pvr/xmltv_reader.rb +37 -4
  39. data/public/css/typeahead.js-bootstrap.css +83 -0
  40. data/public/index.html +45 -37
  41. data/public/js/angular/http-auth-interceptor.js +122 -0
  42. data/public/js/app.js +4 -37
  43. data/public/js/controllers.js +22 -14
  44. data/public/js/directives.js +102 -0
  45. data/public/js/filters.js +0 -31
  46. data/public/js/services.js +64 -0
  47. data/public/js/typeahead/typeahead.min.js +7 -0
  48. data/public/partials/about.html +15 -13
  49. data/public/partials/channels.html +41 -36
  50. data/public/partials/programme.html +20 -4
  51. data/public/partials/programmeListing.html +14 -13
  52. data/public/partials/schedule.html +91 -86
  53. data/public/partials/schedules.html +28 -16
  54. data/public/partials/search.html +10 -11
  55. data/public/partials/show.html +26 -9
  56. data/public/partials/shows.html +5 -6
  57. data/public/partials/status.html +1 -1
  58. data/public/templates/loginDialog.html +30 -0
  59. data/public/templates/logoutLink.html +1 -0
  60. data/public/templates/titleSearch.html +5 -3
  61. data/simple_pvr.gemspec +6 -4
  62. data/spec/resources/dummyImage.png +1 -0
  63. data/spec/resources/programmes-with-categories.xmltv +27 -0
  64. data/spec/resources/programmes-with-credits.xmltv +24 -0
  65. data/spec/resources/programmes-with-icons.xmltv +25 -0
  66. data/spec/resources/programmes-with-presenters.xmltv +19 -0
  67. data/spec/resources/{programs-without-icon.xmltv → programmes-without-icon.xmltv} +0 -0
  68. data/spec/resources/{programs.xmltv → programmes.xmltv} +0 -4
  69. data/spec/simple_pvr/ffmpeg_spec.rb +24 -22
  70. data/spec/simple_pvr/hdhomerun_spec.rb +69 -67
  71. data/spec/simple_pvr/model/channel_spec.rb +101 -101
  72. data/spec/simple_pvr/model/programme_spec.rb +104 -104
  73. data/spec/simple_pvr/model/schedule_spec.rb +74 -74
  74. data/spec/simple_pvr/programme_icon_fetcher_spec.rb +25 -0
  75. data/spec/simple_pvr/pvr_initializer_spec.rb +40 -38
  76. data/spec/simple_pvr/recorder_spec.rb +37 -26
  77. data/spec/simple_pvr/recording_manager_spec.rb +160 -133
  78. data/spec/simple_pvr/recording_planner_spec.rb +213 -211
  79. data/spec/simple_pvr/scheduler_spec.rb +189 -172
  80. data/spec/simple_pvr/server/secured_controller_spec.rb +118 -0
  81. data/spec/simple_pvr/xmltv_reader_spec.rb +89 -41
  82. data/test/karma.conf.js +7 -4
  83. data/test/unit/filtersSpec.js +0 -36
  84. metadata +79 -63
  85. data/public/css/bootstrap-responsive.min.css +0 -9
  86. data/public/css/bootstrap.min.css +0 -9
  87. data/public/img/glyphicons-halflings-white.png +0 -0
  88. data/public/img/glyphicons-halflings.png +0 -0
  89. data/public/js/angular/angular-resource.min.js +0 -10
  90. data/public/js/angular/angular.min.js +0 -162
  91. data/public/js/bootstrap/bootstrap.min.js +0 -6
  92. data/public/js/jquery/jquery.min.js +0 -5
  93. data/test/lib/angular/angular-mocks.js +0 -1768
@@ -0,0 +1,25 @@
1
+ require 'simple_pvr'
2
+
3
+ module SimplePvr
4
+ describe ProgrammeIconFetcher 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)
9
+ end
10
+
11
+ it 'fetches image and stores it in specified location' do
12
+ destination_file = "#{@recording_dir}/dummyImage.png"
13
+
14
+ # We're using open-uri, which unfortunately does not understand file://, which would be perfect for
15
+ # this unit test. However, URLs starting with / are apparently interpreted as normal files.
16
+ source_url = "#{File.dirname(__FILE__)}/../resources/dummyImage.png"
17
+
18
+ Thread.should_receive(:new).and_yield
19
+
20
+ ProgrammeIconFetcher.fetch(source_url, destination_file)
21
+
22
+ File.read(destination_file).should == 'This is not really a PNG file...'
23
+ end
24
+ end
25
+ end
@@ -1,50 +1,52 @@
1
1
  require 'simple_pvr'
2
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
3
+ module SimplePvr
4
+ describe PvrInitializer do
25
5
  before do
26
- @scheduler.stub(:start)
6
+ Model::DatabaseInitializer.stub(:setup)
7
+
8
+ @scheduler = double('Scheduler')
9
+ Scheduler.stub(new: @scheduler)
10
+
11
+ @hdhomerun = double('HDHomeRun')
12
+ HDHomeRun.stub(new: @hdhomerun)
13
+
14
+ @recording_manager = double('RecordingManager')
15
+ RecordingManager.stub(new: @recording_manager)
27
16
  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
17
 
33
- SimplePvr::PvrInitializer.setup
34
- end
18
+ it 'starts the scheduler' do
19
+ Model::Channel.stub(all: [1, 2, 3, 4, 5])
20
+ @scheduler.should_receive(:start)
35
21
 
36
- it 'does nothing if channels.txt is present' do
37
- SimplePvr::Model::Channel.stub(all: [1])
38
-
39
- SimplePvr::PvrInitializer.setup
22
+ PvrInitializer.setup
40
23
  end
24
+
25
+ context 'when scheduler is started' do
26
+ before do
27
+ @scheduler.stub(:start)
28
+ end
29
+
30
+ it 'runs a channel scan if channels are missing' do
31
+ Model::Channel.stub(all: [])
32
+ @hdhomerun.should_receive(:scan_for_channels)
33
+
34
+ PvrInitializer.setup
35
+ end
36
+
37
+ it 'does nothing if channels.txt is present' do
38
+ Model::Channel.stub(all: [1])
41
39
 
42
- it 'initializes a HDHomeRun and RecordingManager instance' do
43
- SimplePvr::Model::Channel.stub(all: [1])
40
+ PvrInitializer.setup
41
+ end
44
42
 
45
- SimplePvr::PvrInitializer.setup
46
- SimplePvr::PvrInitializer.hdhomerun.should == @hdhomerun
47
- SimplePvr::PvrInitializer.recording_manager.should == @recording_manager
43
+ it 'initializes a HDHomeRun and RecordingManager instance' do
44
+ Model::Channel.stub(all: [1])
45
+
46
+ PvrInitializer.setup
47
+ PvrInitializer.hdhomerun.should == @hdhomerun
48
+ PvrInitializer.recording_manager.should == @recording_manager
49
+ end
48
50
  end
49
51
  end
50
52
  end
@@ -1,32 +1,43 @@
1
1
  require 'simple_pvr'
2
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
3
+ module SimplePvr
4
+ describe Recorder do
5
+ before do
6
+ @programme = Model::Programme.new
7
+ @channel = Model::Channel.new(frequency: 282000000, channel_id: 1098)
8
+ @recording = Model::Recording.new(@channel, 'Star Trek', 'start time', 'duration', @programme)
23
9
 
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')
10
+ @hdhomerun = double('HDHomeRun')
11
+ PvrInitializer.stub(hdhomerun: @hdhomerun)
28
12
 
29
- @recorder.start!
30
- @recorder.stop!
13
+ @recording_manager = double('RecordingManager')
14
+ @recording_manager.stub(:create_directory_for_recording).with(@recording).and_return('recording directory')
15
+ PvrInitializer.stub(recording_manager: @recording_manager)
16
+
17
+ @recorder = Recorder.new(1, @recording)
18
+ end
19
+
20
+ it 'can start recording' do
21
+ @hdhomerun.should_receive(:start_recording).with(1, 282000000, 1098, 'recording directory')
22
+
23
+ @recorder.start!
24
+ end
25
+
26
+ it 'starts fetching programme icon when recording starts' do
27
+ @programme.icon_url = 'http://tv-images.com/myImage.png'
28
+ ProgrammeIconFetcher.should_receive(:fetch).with('http://tv-images.com/myImage.png', 'recording directory/icon')
29
+ @hdhomerun.should_receive(:start_recording)
30
+
31
+ @recorder.start!
32
+ end
33
+
34
+ it 'can stop recording as well, and creates a thumbnail' do
35
+ @hdhomerun.stub(:start_recording)
36
+ @hdhomerun.should_receive(:stop_recording).with(1)
37
+ Ffmpeg.should_receive(:create_thumbnail_for).with('recording directory')
38
+
39
+ @recorder.start!
40
+ @recorder.stop!
41
+ end
31
42
  end
32
43
  end
@@ -1,158 +1,185 @@
1
1
  require 'simple_pvr'
2
2
  require 'yaml'
3
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')
4
+ module SimplePvr
5
+ describe RecordingManager do
6
+ before do
7
+ @recording_dir = File.dirname(__FILE__) + '/../resources/recordings'
8
+ FileUtils.rm_rf(@recording_dir) if Dir.exists?(@recording_dir)
9
+ FileUtils.mkdir_p(@recording_dir + "/series 1/1")
10
+ FileUtils.mkdir_p(@recording_dir + "/series 1/3")
11
+ FileUtils.mkdir_p(@recording_dir + "/Another series/10")
12
+
13
+ @manager = RecordingManager.new(@recording_dir)
14
+ end
26
15
 
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) }
16
+ it 'knows which shows are recorded' do
17
+ @manager.shows.size.should == 2
18
+ @manager.shows.should include('Another series', 'series 1')
19
+ end
42
20
 
43
- episodes = @manager.episodes_of('series 1')
21
+ it 'can delete all recordings of a given show' do
22
+ @manager.delete_show('series 1')
23
+ File.exists?(@recording_dir + '/series 1').should be_false
24
+ end
44
25
 
45
- episodes.length.should == 2
46
-
47
- episodes[0].show_name.should == 'series 1'
48
- episodes[0].episode.should == '1'
26
+ it 'knows which recordings of a given show exists' do
27
+ recordings = @manager.recordings_of('series 1')
28
+
29
+ recordings.length.should == 2
30
+ recordings[0].id.should == '1'
31
+ recordings[1].id.should == '3'
32
+ end
49
33
 
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
34
+ it 'reads metadata for recordings if present' do
35
+ start_time = Time.now
36
+ metadata = {
37
+ channel: 'Channel 4',
38
+ subtitle: 'A subtitle',
39
+ description: 'A description',
40
+ start_time: start_time,
41
+ duration: 10.minutes
42
+ }
43
+ File.open(@recording_dir + '/series 1/3/metadata.yml', 'w') {|f| f.write(metadata.to_yaml) }
44
+
45
+ recordings = @manager.recordings_of('series 1')
46
+
47
+ recordings.length.should == 2
89
48
 
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)
49
+ recordings[0].id.should == '1'
50
+ recordings[0].show_name.should == 'series 1'
51
+
52
+ recordings[1].id.should == '3'
53
+ recordings[1].show_name.should == 'series 1'
54
+ recordings[1].channel.should == 'Channel 4'
55
+ recordings[1].subtitle.should == 'A subtitle'
56
+ recordings[1].description.should == 'A description'
57
+ recordings[1].start_time.to_i.should == start_time.to_i
58
+ recordings[1].duration == 10.minutes
94
59
  end
95
60
 
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
61
+ it 'knows when no icon exists' do
62
+ recordings = @manager.recordings_of('series 1')
63
+
64
+ recordings[0].has_icon.should be_false
100
65
  end
101
66
 
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)
67
+ it 'knows when icon exists' do
68
+ FileUtils.touch(@recording_dir + "/series 1/1/icon")
69
+ recordings = @manager.recordings_of('series 1')
70
+
71
+ recordings[0].has_icon.should be_true
72
+ end
105
73
 
106
- File.exists?(@recording_dir + '/Some harmfulirritating characters in title/1').should be_true
74
+ it 'knows when no thumbnail exists' do
75
+ recordings = @manager.recordings_of('series 1')
76
+
77
+ recordings[0].has_thumbnail.should be_false
107
78
  end
108
79
 
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
80
+ it 'knows when thumbnail exists' do
81
+ FileUtils.touch(@recording_dir + "/series 1/1/thumbnail.png")
82
+ recordings = @manager.recordings_of('series 1')
83
+
84
+ recordings[0].has_thumbnail.should be_true
114
85
  end
115
86
 
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
87
+ it 'knows when no webm file exists' do
88
+ recordings = @manager.recordings_of('series 1')
89
+
90
+ recordings[0].has_webm.should be_false
123
91
  end
124
92
 
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
93
+ it 'knows when a webm file exists' do
94
+ FileUtils.touch(@recording_dir + "/series 1/1/stream.webm")
95
+ recordings = @manager.recordings_of('series 1')
96
+
97
+ recordings[0].has_webm.should be_true
131
98
  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
99
+
100
+ it 'can delete a recording of a given show' do
101
+ @manager.delete_show_recording('series 1', '3')
102
+ File.exists?(@recording_dir + '/series 1/3').should be_false
141
103
  end
142
104
 
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"
105
+ context 'when creating recording directories' do
106
+ before do
107
+ @start_time = Time.local(2012, 7, 23, 15, 30, 15)
108
+ @start_time_millis = @start_time.to_i
109
+ @recording = Model::Recording.new(double(name: 'Channel 4'), 'Star Trek', @start_time, 50.minutes)
110
+ end
111
+
112
+ it 'records to directory with record start time if it does not already exist' do
113
+ @manager.create_directory_for_recording(@recording)
114
+
115
+ File.exists?("#{@recording_dir}/Star Trek/#{@start_time_millis}").should be_true
116
+ end
117
+
118
+ it 'appends sequence number to start time if time stamp is already used' do
119
+ FileUtils.mkdir_p("#{@recording_dir}/Star Trek/#{@start_time_millis}")
120
+ @manager.create_directory_for_recording(@recording)
121
+
122
+ File.exists?("#{@recording_dir}/Star Trek/#{@start_time_millis}-1").should be_true
123
+ end
124
+
125
+ it 'finds next number in sequence for new directory if time stamp is already used' do
126
+ FileUtils.mkdir_p("#{@recording_dir}/Star Trek/#{@start_time_millis}")
127
+ FileUtils.mkdir_p("#{@recording_dir}/Star Trek/#{@start_time_millis}-1")
128
+ FileUtils.mkdir_p("#{@recording_dir}/Star Trek/#{@start_time_millis}-2")
129
+ @manager.create_directory_for_recording(@recording)
130
+
131
+ File.exists?("#{@recording_dir}/Star Trek/#{@start_time_millis}-3").should be_true
132
+ end
133
+
134
+ it 'removes some potentially harmful characters from directory name' do
135
+ @recording.show_name = "Some... harmful/irritating\\ characters in: '*title\""
136
+ @manager.create_directory_for_recording(@recording)
137
+
138
+ File.exists?("#{@recording_dir}/Some harmfulirritating characters in title/#{@start_time_millis}").should be_true
139
+ end
140
+
141
+ it 'finds a directory name for titles which would otherwise get an empty directory name' do
142
+ @recording.show_name = '/.'
143
+ @manager.create_directory_for_recording(@recording)
144
+
145
+ File.exists?("#{@recording_dir}/Unnamed/#{@start_time_millis}").should be_true
146
+ end
147
+
148
+ it 'stores simple metadata if no programme information exists' do
149
+ @manager.create_directory_for_recording(@recording)
150
+
151
+ metadata = YAML.load_file("#{@recording_dir}/Star Trek/#{@start_time_millis}/metadata.yml")
152
+ metadata[:title].should == 'Star Trek'
153
+ metadata[:channel].should == 'Channel 4'
154
+ metadata[:start_time].should == @start_time
155
+ metadata[:duration].should == 50.minutes
156
+ end
157
+
158
+ it 'stores extensive metadata if programme information exists' do
159
+ start_time = Time.local(2012, 7, 23, 15, 30, 15)
160
+ start_time_millis = start_time.to_i
161
+ recording = Model::Recording.new(double(name: 'Channel 4'), 'Extensive Metadata', start_time, 50.minutes)
162
+ recording.programme = Model::Programme.new(subtitle: 'A subtitle', description: "A description,\nspanning several lines")
163
+ recording.programme.categories.new(name: 'series')
164
+ recording.programme.categories.new(name: 'children')
165
+ recording.programme.directors.new(name: 'The director')
166
+ recording.programme.presenters.new(name: 'The presenter')
167
+ recording.programme.actors.new(role_name: 'First role', actor_name: 'First actor')
168
+ recording.programme.actors.new(role_name: 'Second role', actor_name: 'Second actor')
169
+ @manager.create_directory_for_recording(recording)
170
+
171
+ metadata = YAML.load_file("#{@recording_dir}/Extensive Metadata/#{start_time_millis}/metadata.yml")
172
+ metadata[:title].should == 'Extensive Metadata'
173
+ metadata[:channel].should == 'Channel 4'
174
+ metadata[:start_time].should == start_time
175
+ metadata[:duration].should == 50.minutes
176
+ metadata[:subtitle].should == 'A subtitle'
177
+ metadata[:description].should == "A description,\nspanning several lines"
178
+ metadata[:categories].should == ['series', 'children']
179
+ metadata[:directors].should == ['The director']
180
+ metadata[:presenters].should == ['The presenter']
181
+ metadata[:actors].should == [{role_name: 'First role', actor_name: 'First actor'}, {role_name: 'Second role', actor_name: 'Second actor'}]
182
+ end
156
183
  end
157
184
  end
158
185
  end