sensible-cinema 0.14.5 → 0.14.6

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.14.6 ==
2
+
3
+ Auto-select EDL depending on which disk they have.
4
+
1
5
  == 0.14.5 ==
2
6
 
3
7
  Make it distributable more easily.
data/Rakefile CHANGED
@@ -93,8 +93,6 @@ task 'create_distro_dir' do
93
93
  raise 'need bundle_dependencies first' unless File.directory? 'vendor/cache'
94
94
  require 'fileutils'
95
95
  spec = eval File.read('sensible-cinema.gemspec')
96
- require 'ruby-debug'
97
- #debugger
98
96
  dir_out = spec.name + "-" + spec.version.version + '/sensible-cinema'
99
97
  FileUtils.rm_rf dir_out + '/..' # in case it exists
100
98
  existing = Dir['*']
data/TODO CHANGED
@@ -9,17 +9,16 @@
9
9
 
10
10
  == slightly lower than that even ==
11
11
 
12
+
12
13
  == slightly lower than that ==
13
14
 
14
- auto-select EDL
15
- can even tell them a button "play movie x" or what not, has been detected
16
-
15
+ pass unit tests jruby
17
16
  can parse IMDB (require they put in the URL by hand fer now)
18
17
  add stuffs to imdb hmmm
19
18
  IMDB easy mapping
20
19
  unit test the GUI
21
20
  can have collaborators re-selling EL's
22
- good license for their stuff like "public domain" or what not :P
21
+ good license for their stuff like "public domain" or what not :P Can't be a hypocrite now, can I?
23
22
  80/20
24
23
  can "watch as it is being ripped" (concat, watching it as it grows faster than you watch...)
25
24
  a "big download" of the whole thing (so that people can actually use it without pain...batch file even would probably work splendidly...
@@ -28,16 +27,16 @@
28
27
  easily
29
28
  automagically... (call into iso blah blah blah?)
30
29
  can lop off sections to save space (?)
31
- can burn the extra sections, too?
30
+ can burn the extras from the DVD, too?
32
31
  easy editor for this style of edit
33
32
  split the two projects loosely, then cleanly (project, distribution)
34
33
  partner (Jon, Karlie if interested...give beta)
35
34
  survey/beta release
36
- a certified clean list (?)
37
- better playlist names, like with the DVD title in there :)
38
35
 
39
36
  == DVD monetary backlog (unordered) ==
40
37
 
38
+ use DVD navigator ?
39
+ http://msdn.microsoft.com/en-us/library/dd388584(v=VS.85).aspx
41
40
  some way to choose between x,y,z when auto-select sees several
42
41
  careful phrasing "if you can't afford it..."
43
42
  authorize instead of paypal?
@@ -60,6 +59,7 @@ mplayer EDL's
60
59
 
61
60
  == random backlog ... note: just plow forward, to "grab" available ideas...except that for now, just what *I* plan on needing for myself (filters for what I need/want). ==
62
61
 
62
+ a "certified clean" list (?)
63
63
  screencast of things helpful (use, creation...).
64
64
  tune up the levels...
65
65
  prototypes of the experimental stuff (overlay, track time while recording -> map file, specifiable levels, more?)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.5
1
+ 0.14.6
@@ -15,6 +15,7 @@ add_any_bundled_gems
15
15
  require 'sane' # File.write
16
16
  require 'ruby-wmi'
17
17
  require_relative '../lib/swing_helpers'
18
+ require_relative '../lib/drive_info'
18
19
  load File.dirname(__FILE__) + '/sensible-cinema-cli'
19
20
  require 'tmpdir'
20
21
 
@@ -80,7 +81,7 @@ module SensibleSwing
80
81
  end
81
82
 
82
83
  def vlc_currently_installed?
83
- if !system("vlc --qt-start-minimized vlc://quit")
84
+ if !system("vlc --qt-start-minimized vlc://quit 2>NUL")
84
85
  JOptionPane.showMessageDialog(nil, "Appears you do not yet have VLC Media Player installed. Click ok to download it.", "VLC Player not found", JOptionPane::ERROR_MESSAGE)
85
86
  system("start http://www.videolan.org/")
86
87
  false
@@ -89,65 +90,84 @@ module SensibleSwing
89
90
  end
90
91
  end
91
92
 
92
- def do_copy_dvd_to_hard_drive should_save_file
93
+ def single_edit_list_matches_dvd volume_name
94
+ matching = Dir[EDL_DIR + '/*.txt'].select{|file|
95
+ YAML.load_file(file)["dvd_drive_label"] == volume_name
96
+ }
97
+ if matching.length == 1
98
+ file = matching[0]
99
+ p "selecting only matching file #{file}"
100
+ return file
101
+ end
102
+ nil
103
+ end
104
+
105
+ EDL_DIR = __dir__ + "/../zamples/edit_decision_lists/dvds"
106
+
107
+ def do_copy_dvd_to_hard_drive should_save_file
93
108
  return unless vlc_currently_installed?
94
- drive = choose_dvd_drive
95
- fc = JFileChooser.new
96
- fc.setFileSelectionMode(nil)
97
- fc.set_dialog_title "Please pick a DVD Edit List File"
98
- fc.set_current_directory(JFile.new( __dir__ + "/../zamples/edit_decision_lists/dvds"))
99
- edit_list_path = fc.go
109
+ drive, volume_name = choose_dvd_drive
110
+ if !(edit_list_path = single_edit_list_matches_dvd(volume_name))
111
+ fc = JFileChooser.new
112
+ fc.setFileSelectionMode(nil)
113
+ fc.set_dialog_title "Please pick a DVD Edit List File"
114
+ fc.set_current_directory(JFile.new EDL_DIR)
115
+ edit_list_path = fc.go
116
+ end
100
117
  descriptors = YAML.load_file edit_list_path
101
-
118
+ dvd_title = descriptors['title']
102
119
  if should_save_file
103
120
  fc = JFileChooser.new
104
- # LODO allow for spaces in save to file
121
+ # LODO allow for spaces in the save to file
105
122
  fc.set_dialog_title "Pick where to save the file to"
106
- save_to_file_name = ((descriptors["title"] || "my dvd") + ' edited copy').gsub(' ', '_').gsub( /\W/, '') # no punctuation for now...
107
- fc.setSelectedFile(JFile.new(get_drive_with_most_space_with_slash + save_to_file_name))
123
+ save_to_file_name = ((dvd_title || "my dvd") + ' edited copy').gsub(' ', '_').gsub( /\W/, '') # no punctuation for now...
124
+ fc.setSelectedFile(JFile.new(DriveInfo.get_drive_with_most_space_with_slash + save_to_file_name))
108
125
  save_to = fc.go
109
126
  else
110
127
  save_to = nil
111
128
  end
112
- # LODO don't pop up those annoying windows
113
129
  dvd_title_track = descriptors["dvd_title_track"]
114
- bat_file_or_xspf = VLCProgrammer.convert_to_full_xspf(descriptors, save_to, drive, dvd_title_track)
115
- temp_dir = Dir.tmpdir
130
+ bat_file_or_xspf = VLCProgrammer.convert_to_full_xspf(descriptors, save_to, drive, dvd_title_track, dvd_title)
116
131
  if should_save_file
117
- # allow our popups to still be serviced while it is running
132
+
133
+ popup = ModeLessDialog.new("Running edit list #{File.basename edit_list_path} against #{drive} (#{dvd_title})...\n" +
134
+ "Copying to #{save_to}.\n" +
135
+ "This could take quite awhile, and will prompt you when it is done.\n" +
136
+ "It may also cause small popup ballon dialogs to appear.\n" +
137
+ "You can close this window and continue working while it runs.",
138
+ "OK")
139
+
140
+ # this allows our popups to still be serviced while it is running
118
141
  Thread.new {
119
142
  run_copy_commands bat_file_or_xspf, save_to
120
143
  }
121
144
  else
122
- temp_file = temp_dir + '/' + 'vlc.xspf'
145
+ temp_dir = Dir.tmpdir
146
+ temp_file = temp_dir + '/' + 'vlc.temp.xspf'
123
147
  File.write(temp_file, bat_file_or_xspf)
124
148
  command = "vlc \"#{temp_file.to_filename}\" vlc://quit"
125
- p 'running playlist: ', command
149
+ p 'running playlist for DVD: ', command
126
150
  system(command) # blocks the current window while running...
127
- p 'done running playlist'
151
+ p 'done playing DVD'
128
152
  end
129
- # TODO warn if they don't have VLC installed, with instructions...
130
153
  # LODO warn if they will overwrite a file in the end
131
154
  end
132
155
 
133
- def run_copy_commands batch_file, save_to
134
- popup = ModeLessDialog.new("Running the encoding in the background...\n" +
135
- "This could take quite awhile, and will prompt you when it is done.\n" +
136
- "It may also cause small popup ballon dialogs to appear.")
137
- p 'saving to ' + save_to + '.ps'
156
+ def run_copy_commands batch_commands, save_to
157
+ p 'saving to ' + save_to + '.ps' # lodo cleanup the .ps here...
138
158
  @buttons.each{|b| b.set_enabled false}
139
159
  success = true
140
- lines = batch_file.lines.to_a
160
+ lines = batch_commands.lines.to_a
141
161
  total_size = lines.length.to_f
142
162
  @progress_bar.visible=true
143
- @progress_bar.set_value(0)
163
+ @progress_bar.set_value(10) # start at 10% always, so they can see something.
144
164
  lines.each_with_index{|l, idx|
145
165
  if success
146
166
  success = system(l) unless ARGV.find{|a| a == '--test'} # this doesn't seem to cancel out early for some reason..
147
- @progress_bar.set_value(idx/total_size*100)
167
+ @progress_bar.set_value(10 + idx/total_size*90)
148
168
  end
149
- }
150
- @progress_var.visible=false
169
+ }
170
+ @progress_bar.visible=false
151
171
  @buttons.each{|b| b.set_enabled true}
152
172
  popup.dispose
153
173
  if success
@@ -160,42 +180,33 @@ module SensibleSwing
160
180
  end
161
181
  end
162
182
 
183
+ # returns e:\, win32ole instance
163
184
  def choose_dvd_drive
164
- opticals = get_dvd_drives
165
- opticals.map!{|d| d.Name + "\\" + " (" + (d.VolumeName || 'empty') + ")"}
185
+ opticals = DriveInfo.get_dvd_drives_as_win32ole
186
+ names = opticals.map{|d| d.Name + "\\" + " (" + (d.VolumeName || 'empty') + ")"}
166
187
 
167
188
  if opticals.length != 1
168
- dialog = GetDisk.new(self, opticals)
189
+ dialog = GetDisk.new(self, names)
169
190
  dialog.setSize 200,125
170
191
  dialog.show
171
- selected = dialog.selected
192
+ selected_idx = dialog.selected_idx
172
193
  else
173
- selected = opticals[0]
174
- end
175
- if selected
176
- return selected[0..2]
194
+ selected_idx = 0
195
+ p 'selecting only disk drive ' + names[0]
196
+ end
197
+ if selected_idx
198
+ disk = opticals[selected_idx]
199
+ return names[selected_idx][0..2], opticals[selected_idx].VolumeName
177
200
  else
178
- puts 'did not select drive'
201
+ puts 'did not select a drive'
179
202
  java.lang.System.exit 1
180
203
  end
181
- end
182
-
183
- def get_drive_with_most_space_with_slash
184
- disks = WMI::Win32_LogicalDisk.find(:all)
185
- out = disks.sort_by{|d| d.FreeSpace.to_i}[-1].Name + "\\"
186
- out
187
- end
188
-
189
- private
190
- def get_dvd_drives
191
- disks = WMI::Win32_LogicalDisk.find(:all)
192
- disks.select{|d| d.Description =~ /CD-ROM/} # hope this works...
193
204
  end
194
205
 
195
206
  end
196
207
 
197
208
  class GetDisk < JDialog
198
- attr_reader :selected
209
+ attr_reader :selected_idx
199
210
  def initialize parent, options_array
200
211
  super parent, true
201
212
 
@@ -203,8 +214,9 @@ module SensibleSwing
203
214
  box.add_action_listener do |e|
204
215
  idx = box.get_selected_index
205
216
  if idx != 0
206
- @selected = box.get_selected_item
207
- dispose
217
+ # don't count choosing the first as a real entry
218
+ @selected_idx = box.get_selected_index - 1
219
+ dispose
208
220
  end
209
221
  end
210
222
 
@@ -220,7 +232,7 @@ module SensibleSwing
220
232
 
221
233
  end
222
234
 
223
-
224
- SensibleSwing::MainWindow.new.set_visible true
225
-
226
- puts 'Please use the Sensible Cinema GUI window popup'
235
+ if $0 == __FILE__
236
+ SensibleSwing::MainWindow.new.set_visible true
237
+ puts 'Please use the Sensible Cinema GUI window popup...'
238
+ end
@@ -0,0 +1,27 @@
1
+ require 'digest/md5'
2
+ require 'ruby-wmi'
3
+
4
+ class DriveInfo
5
+
6
+ def self.md5sum_disk(dir)
7
+ digest = Digest::MD5.new()
8
+
9
+ Dir[dir + "VIDEO_TS/*.IFO"].sort.each{|f|
10
+ digest << File.binread(f)
11
+ }
12
+ digest.hexdigest
13
+ end
14
+
15
+ def self.get_dvd_drives_as_win32ole
16
+ disks = WMI::Win32_LogicalDisk.find(:all)
17
+ disks.select{|d| d.Description =~ /CD-ROM/} # hope this works...
18
+ end
19
+
20
+ def self.get_drive_with_most_space_with_slash
21
+ disks = WMI::Win32_LogicalDisk.find(:all)
22
+ most_space = disks.sort_by{|d| d.FreeSpace.to_i}[-1]
23
+ most_space.Name + "\\"
24
+ end
25
+
26
+ end
27
+
@@ -35,18 +35,19 @@ module SensibleSwing
35
35
  def go
36
36
  success = show_open_dialog nil
37
37
  unless success == Java::javax::swing::JFileChooser::APPROVE_OPTION
38
- raise 'did not choose one' # don't kill background proc...
38
+ java.lang.System.exit 1 # kills background proc...but we shouldn't let them do stuff while a background proc is running, anyway
39
39
  end
40
40
  get_selected_file.get_absolute_path
41
41
  end
42
42
  end
43
- #showMessageDialog JOptionPane
43
+
44
44
  class ModeLessDialog < JDialog
45
45
  def initialize title_and_display_text, close_button_text= 'Close'
46
46
  super nil
47
- set_title title_and_display_text.split("\n")[0]
47
+ lines = title_and_display_text.split("\n")
48
+ set_title lines[0]
48
49
  get_content_pane.set_layout nil
49
- title_and_display_text.split("\n").each_with_index{|line, idx|
50
+ lines.each_with_index{|line, idx|
50
51
  jlabel = JLabel.new line
51
52
  jlabel.set_bounds(10, 15*idx, 400, 24)
52
53
  get_content_pane.add jlabel
@@ -54,9 +55,9 @@ module SensibleSwing
54
55
  close = JButton.new( close_button_text ).on_clicked {
55
56
  self.dispose
56
57
  }
57
- close.set_bounds(125,50,70,25)
58
+ close.set_bounds(125,30+15*lines.length,70,25)
58
59
  get_content_pane.add close
59
- set_size 400,125
60
+ set_size 400, 100+15*lines.length
60
61
  set_visible true
61
62
  setDefaultCloseOperation JFrame::DISPOSE_ON_CLOSE
62
63
  setLocationRelativeTo nil # center it on the screen
@@ -65,6 +66,7 @@ module SensibleSwing
65
66
  end
66
67
 
67
68
  require 'os'
69
+
68
70
  class String
69
71
  def to_filename
70
72
  if OS.windows?
@@ -10,10 +10,12 @@ class VLCProgrammer
10
10
  @overlayer.translate_time_to_human_readable s
11
11
  end
12
12
 
13
- def self.convert_to_full_xspf incoming, filename = nil, drive_with_slash = nil, dvd_title_track = nil
13
+ def self.convert_to_full_xspf incoming, filename = nil, drive_with_slash = nil, dvd_title_track = nil, dvd_title_name = nil
14
+
14
15
  @drive = drive_with_slash || "e:\\"
15
16
  @filename_or_playlist_if_nil = filename
16
17
  @dvd_title_track = dvd_title_track || "1"
18
+ @dvd_title_name = dvd_title_name
17
19
  mutes = incoming["mutes"] || {}
18
20
  blanks = incoming["blank_outs"] || {}
19
21
  mutes = mutes.map{|k, v| [OverLayer.translate_string_to_seconds(k), OverLayer.translate_string_to_seconds(v), :mute]}
@@ -69,11 +71,11 @@ class VLCProgrammer
69
71
 
70
72
  if previous_end != start
71
73
  # play up to next "questionable section"
72
- out += get_section("#{to_english previous_end} to #{to_english start} (clean)", previous_end, start, idx += 1)
74
+ out += get_section("#{@dvd_title_name} : #{to_english previous_end} to #{to_english start} (clean)", previous_end, start, idx += 1)
73
75
  end
74
76
  # now play through the muted section...
75
77
  if type == :mute
76
- out += get_section "#{to_english start}s to #{to_english endy}s muted", start, endy, idx += 1, true
78
+ out += get_section "#{@dvd_title_name} : #{to_english start}s to #{to_english endy}s muted", start, endy, idx += 1, true
77
79
  end
78
80
  previous_end = endy
79
81
  }
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{sensible-cinema}
8
- s.version = "0.14.5"
8
+ s.version = "0.14.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Roger Pack"]
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
31
31
  "ext/mkrf_conf.rb",
32
32
  "gplv3.txt",
33
33
  "lib/blanker.rb",
34
+ "lib/drive_info.rb",
34
35
  "lib/fake_blanker.rb",
35
36
  "lib/file_chooser.rb",
36
37
  "lib/keyboard_input.rb",
@@ -48,6 +49,7 @@ Gem::Specification.new do |s|
48
49
  "spec/blanker.spec.rb",
49
50
  "spec/common.rb",
50
51
  "spec/convert_image.rb",
52
+ "spec/drive_info.spec.rb",
51
53
  "spec/images/black.bmp",
52
54
  "spec/images/colon.bmp",
53
55
  "spec/images/hulu_0.bmp",
@@ -101,6 +103,7 @@ Gem::Specification.new do |s|
101
103
  "spec/ocr.spec.rb",
102
104
  "spec/overlayer.spec.rb",
103
105
  "spec/screen_tracker.spec.rb",
106
+ "spec/sensible_cinema_gui.spec.rb",
104
107
  "spec/silence.wav",
105
108
  "spec/swing_helpers.spec.rb",
106
109
  "spec/test_yaml.yml",
@@ -139,12 +142,14 @@ Gem::Specification.new do |s|
139
142
  "spec/blanker.spec.rb",
140
143
  "spec/common.rb",
141
144
  "spec/convert_image.rb",
145
+ "spec/drive_info.spec.rb",
142
146
  "spec/keyboard_input.spec.rb",
143
147
  "spec/mouse.spec.rb",
144
148
  "spec/muter.spec.rb",
145
149
  "spec/ocr.spec.rb",
146
150
  "spec/overlayer.spec.rb",
147
151
  "spec/screen_tracker.spec.rb",
152
+ "spec/sensible_cinema_gui.spec.rb",
148
153
  "spec/swing_helpers.spec.rb",
149
154
  "spec/vlc_programmer.spec.rb"
150
155
  ]
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/common')
2
+ require_relative '../lib/drive_info'
3
+
4
+ describe 'dvd_drive_info' do
5
+ it 'should be able to get an md5sum from the dvd' do
6
+ FileUtils.mkdir_p 'VIDEO_TS'
7
+ Dir.chdir 'VIDEO_TS' do
8
+ File.binwrite("VTS_01_0.IFO", "b")
9
+ File.binwrite("VIDEO_TS.IFO", "a")
10
+ end
11
+ DriveInfo.md5sum_disk(".\\").should == Digest::MD5.hexdigest("ab")
12
+ end
13
+
14
+ it "should be able to do it for real drive" do
15
+ DriveInfo.get_dvd_drives_as_win32ole.each{|d|
16
+ DriveInfo.md5sum_disk(d.Name + "/").length.should be > 0 if d.VolumeName
17
+ }
18
+ end
19
+
20
+ it "should return a drive with most space" do
21
+ DriveInfo.get_drive_with_most_space_with_slash[1..-1].should == ":\\"
22
+ end
23
+
24
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/common')
2
+ load '../bin/sensible-cinema'
3
+
4
+ module SensibleSwing
5
+ describe MainWindow do
6
+
7
+ it "should auto-select an EDL if the title matches" do
8
+ MainWindow.new.single_edit_list_matches_dvd("BOBS_BIG_PLAN").should_not be nil
9
+ end
10
+
11
+ it "should prompt if two match" do
12
+ MainWindow.const_set(:EDL_DIR, 'temp')
13
+ FileUtils.rm_rf 'temp'
14
+ Dir.mkdir 'temp'
15
+ MainWindow.new.single_edit_list_matches_dvd("BOBS_BIG_PLAN").should be nil
16
+ Dir.chdir 'temp' do
17
+ File.binwrite('a.txt', "dvd_drive_label: BOBS_BIG_PLAN")
18
+ File.binwrite('b.txt', "dvd_drive_label: BOBS_BIG_PLAN")
19
+ end
20
+ MainWindow.new.single_edit_list_matches_dvd("BOBS_BIG_PLAN").should be nil
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -1,12 +1,13 @@
1
1
  mutes:
2
- "1" : "1:06" # theme song is a bit raucous
2
+ "1" : "1:07" # theme song is a bit loud at times.
3
3
  "48:46" : "49:08" # theme song again
4
- "29:14" : "30:46" # theme song yikes.
4
+ "29:14" : "30:46" # theme song again
5
5
  blank_outs:
6
6
  # none
7
7
 
8
8
  title: Bob's Big Plan
9
+ dvd_drive_label: BOBS_BIG_PLAN
9
10
  source: DVD (vlc)
10
11
  dvd_title_track: 1
11
12
 
12
- other_notes: could use more differentiation, as I don't dislike *all* the music.
13
+ other_notes: could use more differentiation during the song, as I don't dislike *all* the music.
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 14
8
- - 5
9
- version: 0.14.5
8
+ - 6
9
+ version: 0.14.6
10
10
  platform: ruby
11
11
  authors:
12
12
  - Roger Pack
@@ -179,6 +179,7 @@ files:
179
179
  - ext/mkrf_conf.rb
180
180
  - gplv3.txt
181
181
  - lib/blanker.rb
182
+ - lib/drive_info.rb
182
183
  - lib/fake_blanker.rb
183
184
  - lib/file_chooser.rb
184
185
  - lib/keyboard_input.rb
@@ -196,6 +197,7 @@ files:
196
197
  - spec/blanker.spec.rb
197
198
  - spec/common.rb
198
199
  - spec/convert_image.rb
200
+ - spec/drive_info.spec.rb
199
201
  - spec/images/black.bmp
200
202
  - spec/images/colon.bmp
201
203
  - spec/images/hulu_0.bmp
@@ -249,6 +251,7 @@ files:
249
251
  - spec/ocr.spec.rb
250
252
  - spec/overlayer.spec.rb
251
253
  - spec/screen_tracker.spec.rb
254
+ - spec/sensible_cinema_gui.spec.rb
252
255
  - spec/silence.wav
253
256
  - spec/swing_helpers.spec.rb
254
257
  - spec/test_yaml.yml
@@ -313,11 +316,13 @@ test_files:
313
316
  - spec/blanker.spec.rb
314
317
  - spec/common.rb
315
318
  - spec/convert_image.rb
319
+ - spec/drive_info.spec.rb
316
320
  - spec/keyboard_input.spec.rb
317
321
  - spec/mouse.spec.rb
318
322
  - spec/muter.spec.rb
319
323
  - spec/ocr.spec.rb
320
324
  - spec/overlayer.spec.rb
321
325
  - spec/screen_tracker.spec.rb
326
+ - spec/sensible_cinema_gui.spec.rb
322
327
  - spec/swing_helpers.spec.rb
323
328
  - spec/vlc_programmer.spec.rb