sensible-cinema 0.14.6 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.6
1
+ 0.15.0
@@ -2,22 +2,21 @@
2
2
 
3
3
  puts 'Loading Sensible Cinema...'
4
4
 
5
- require 'rubygems'
5
+ require File.dirname(__FILE__) + "/../lib/add_any_bundled_gems_to_load_path.rb"
6
+ require 'sane'
6
7
 
7
- def add_any_bundled_gems
8
- Dir['vendor/cache/**/lib'].each{|lib_dir|
9
- $: << lib_dir
10
- }
8
+ for file in ['vlc_programmer']
9
+ require_relative '../lib/' + file
11
10
  end
12
11
 
13
- add_any_bundled_gems
14
-
15
- require 'sane' # File.write
16
12
  require 'ruby-wmi'
13
+ require 'tmpdir'
17
14
  require_relative '../lib/swing_helpers'
18
15
  require_relative '../lib/drive_info'
19
- load File.dirname(__FILE__) + '/sensible-cinema-cli'
20
- require 'tmpdir'
16
+
17
+ # add some common locations for vlc.exe to the path...
18
+ # lodo use the registry to pull out the 'right' path...
19
+ ENV['PATH'] = ENV['PATH'] + ";C:\\Program Files\\VideoLAN\\VLC"+ ";D:\\Program Files\\VideoLAN\\VLC" + ";E:\\Program Files\\VideoLAN\\VLC"
21
20
 
22
21
  module SensibleSwing
23
22
  class MainWindow < JFrame
@@ -35,6 +34,7 @@ module SensibleSwing
35
34
  jlabel.set_bounds(44,44,136,14)
36
35
  panel.add jlabel
37
36
  b = JButton.new( "Play Hulu, Youtube, or a DVD (Tracking)" ).on_clicked {
37
+ load File.dirname(__FILE__) + '/sensible-cinema-cli'
38
38
  Thread.new { go_sc([]) } # needs to be in its own thread becuase it blocks,
39
39
  # so "this thread" is the uni-swing thread, gets blocked, and the overlayer can't repaint
40
40
  self.close
@@ -119,7 +119,7 @@ module SensibleSwing
119
119
  if should_save_file
120
120
  fc = JFileChooser.new
121
121
  # LODO allow for spaces in the save to file
122
- fc.set_dialog_title "Pick where to save the file to"
122
+ fc.set_dialog_title "Pick where to save #{dvd_title || "the file"} edited to"
123
123
  save_to_file_name = ((dvd_title || "my dvd") + ' edited copy').gsub(' ', '_').gsub( /\W/, '') # no punctuation for now...
124
124
  fc.setSelectedFile(JFile.new(DriveInfo.get_drive_with_most_space_with_slash + save_to_file_name))
125
125
  save_to = fc.go
@@ -128,25 +128,28 @@ module SensibleSwing
128
128
  end
129
129
  dvd_title_track = descriptors["dvd_title_track"]
130
130
  bat_file_or_xspf = VLCProgrammer.convert_to_full_xspf(descriptors, save_to, drive, dvd_title_track, dvd_title)
131
+
132
+ temp_dir = Dir.tmpdir
133
+ temp_file = temp_dir + '/' + 'vlc.temp.xspf'
134
+ File.write(temp_file, bat_file_or_xspf)
135
+ puts bat_file_or_xspf
131
136
  if should_save_file
132
-
133
- popup = ModeLessDialog.new("Running edit list #{File.basename edit_list_path} against #{drive} (#{dvd_title})...\n" +
134
- "Copying to #{save_to}.\n" +
137
+ popup = ModeLessDialog.new( "Copying to #{save_to}.\n" +
138
+ "Running edit list #{File.basename edit_list_path} against \n" +
139
+ " #{drive} (#{dvd_title})...\n" +
135
140
  "This could take quite awhile, and will prompt you when it is done.\n" +
136
141
  "It may also cause small popup ballon dialogs to appear.\n" +
137
142
  "You can close this window and continue working while it runs.",
138
- "OK")
143
+ "OK")
139
144
 
140
145
  # this allows our popups to still be serviced while it is running
141
146
  Thread.new {
142
147
  run_copy_commands bat_file_or_xspf, save_to
148
+ popup.dispose
143
149
  }
144
150
  else
145
- temp_dir = Dir.tmpdir
146
- temp_file = temp_dir + '/' + 'vlc.temp.xspf'
147
- File.write(temp_file, bat_file_or_xspf)
148
151
  command = "vlc \"#{temp_file.to_filename}\" vlc://quit"
149
- p 'running playlist for DVD: ', command
152
+ puts 'running playlist for DVD: ', command
150
153
  system(command) # blocks the current window while running...
151
154
  p 'done playing DVD'
152
155
  end
@@ -169,14 +172,13 @@ module SensibleSwing
169
172
  }
170
173
  @progress_bar.visible=false
171
174
  @buttons.each{|b| b.set_enabled true}
172
- popup.dispose
173
175
  if success
174
176
  JOptionPane.showMessageDialog(nil, " Done--you may now watch file #{save_to}.ps in VLC player", "Done!", JOptionPane::INFORMATION_MESSAGE)
175
177
  show_file = "explorer /e,/select,\"#{File.expand_path(save_to).to_filename}.ps\""
176
178
  system show_file
177
179
  self.close
178
180
  else
179
- JOptionPane.showMessageDialog(nil, " Failed--please examine #{temp_file} and screen output and report back!", "Failed", JOptionPane::ERROR_MESSAGE)
181
+ JOptionPane.showMessageDialog(nil, " Failed--please examine screen output and report back!", "Failed", JOptionPane::ERROR_MESSAGE)
180
182
  end
181
183
  end
182
184
 
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
- require 'rubygems'
3
+ require File.dirname(__FILE__) + "/../lib/add_any_bundled_gems_to_load_path.rb"
4
4
  require 'sane'
5
5
 
6
- Thread.abort_on_exception=true #gah
6
+ Thread.abort_on_exception=true # gah
7
7
 
8
8
  for file in ['overlayer', 'keyboard_input', 'screen_tracker', 'mouse', 'file_chooser', 'ocr', 'vlc_programmer']
9
9
  require_relative '../lib/' + file
10
10
  end
11
11
 
12
-
13
12
  def go_sc(args)
14
13
 
15
14
  $VERBOSE = 1 if args.delete('-v')
@@ -96,7 +95,7 @@ def go_sc(args)
96
95
  Blanker.shutdown # lodo move this and the 'q' key to within overlayer
97
96
  OCR.serialize_cache_to_disk
98
97
  }
99
- puts "syntax from command line: #{player_description} #{edit_decision_list}"
98
+ puts "syntax from command line: \"#{player_description}\" \"#{edit_decision_list}\""
100
99
  key_input.handle_keystrokes_forever # blocking...
101
100
 
102
101
  end
@@ -0,0 +1,83 @@
1
+ Currently it takes as input a list of "skippable" scenes, and a player description.
2
+ It then tracks whichever player you are using, and mutes or blanks out the system appropriately,
3
+ during the scenes specified.
4
+
5
+ It works out of the box with the hulu and VLC players on windows. It isn't hard to
6
+ add new players, and probably wouldn't be too hard to add more operating systems etc.
7
+
8
+ == How to Use ==
9
+
10
+ Start playing your movie in its respective player, then startup sensible-cinema by running the file
11
+ run_sensible_cinema.bat
12
+
13
+ It prompts you for an EDL (Edit decision List) file (ex: bambi.txt),
14
+ and also for a player file (ex: hulu_full_screen.txt).
15
+
16
+ Sensible-cinema will now run in a console window, screen tracking the player to monitor its position,
17
+ and react'ing appropriately.
18
+
19
+ It is presumed that you'll then minimize sensible-cinema and proceed to enjoy the movie.
20
+
21
+ You'll know that it's working if, when you change the time of your player (ex: dragging it to a new spot
22
+ in the playback), the screen output in sensible-cinema's console should change to match the new time.
23
+
24
+ == How to install ==
25
+
26
+ You'll need to install imagemagick first: http://www.imagemagick.org/script/binary-releases.php
27
+
28
+ Next go and get the program: http://rogerdpack.t28.net
29
+
30
+ You can test that it's installed by running it (see above) and selecting the "example_edit_decision_list.txt", and
31
+ choosing the hulu player.
32
+
33
+ It will show you a few "demo" mutes and blank outs.
34
+
35
+ == FAQ ==
36
+
37
+ Q. Can I watch movies this way on my TV, not just on my computer?
38
+
39
+ A. Maybe. Or possible not yet, depending on your current hardware.
40
+ Currently you'll either need to attach your computer to your TV
41
+ (buy some long cables, or a new graphics card that matches your cables, etc.)
42
+ or get some computer that you can move closer to the TV to do the same
43
+ (ex: buy a used older laptop with s-video out, use that, or a laptop with DVI/HDMI would
44
+ work with an HDMI TV). If you're really aggressive you could run an ethernet cable from your computer, as well [1].
45
+ I'd be happy to do a linux port of sensible-cinema if anybody requests it, for use with
46
+ their dedicated TV computer.
47
+ There has also been some work toward getting ones computer to stream "live" to your existing wii/ps3/xbox.
48
+ Message me if you're interested in trying it out (testers wanted--plus I'll only work on it if there's demand)!
49
+
50
+ Q. What movies does this work with?
51
+
52
+ A. Any that you program it for :) (Assuming the computer player is compatible, which most probably are.)
53
+
54
+ Q. What movies are freely available to watch online?
55
+
56
+ A. Not many are available free (hulu, youtube have a few). Netflix has quite a few with its default subscription.
57
+ You can of course use it with any existing DVD, or rent or borrow DVD's and watch them using sensible-cinema.
58
+
59
+ Q. Why does my mouse bounce up and down while sensible-cinema is going?
60
+
61
+ A. This enables your player to keep its on-screen time tracker, which in turn allows sensible-cinema to track where
62
+ you're at. Message me if this bugs you too much and we'll see what we can do about it.
63
+
64
+ [1] http://ps3mediaserver.org/forum/viewtopic.php?f=6&t=5731#p34279
65
+
66
+ == Advanced Usage ==
67
+
68
+ You could specify the scene descriptions list and player list on the command-line, if you don't want to have
69
+ to pick them each time, like:
70
+
71
+ first install jruby, then
72
+
73
+ C:\> jruby -S gem install sensible-cinema (double check if that gives you the latest version)
74
+
75
+ C:\> jruby -S sensible-cinema edit_decision_list.yml player_description.yml
76
+
77
+ Also if you specify "test" for the scene descriptions file, it will pause 4s, take a snapshot of the player, then exit.
78
+ You can also specify -v or -t if you want to enable more verbose (chatty) output.
79
+
80
+ == Thanks ==
81
+
82
+ Thanks to Jarmo for the win32-screenshot gem, mini_magick gem authors, jruby guys, etc.
83
+ The combination made programming this actually something of a pleasure.
@@ -1,6 +1,7 @@
1
1
  raise 'must be using jruby to be able to run this...at least for now' unless RUBY_PLATFORM =~ /java/
2
2
 
3
3
  # reset the OCR cache...
4
+ require 'rubygems'
4
5
  require File.dirname(__FILE__) + '/../lib/ocr'
5
6
  OCR.clear_cache!
6
7
 
@@ -0,0 +1,14 @@
1
+
2
+ def add_any_bundled_gems_to_load_path
3
+ raise 'no vendor dir?' unless File.directory? 'vendor'
4
+ if File.directory? 'vendor/cache'
5
+ Dir['vendor/cache/**/lib'].each{|lib_dir|
6
+ $: << lib_dir
7
+ }
8
+ else
9
+ require 'rubygems'
10
+ # they'll need imagemagick installed, as well, currently
11
+ end
12
+ end
13
+
14
+ add_any_bundled_gems_to_load_path
@@ -0,0 +1,98 @@
1
+ if $0 == __FILE__
2
+ require 'rubygems'
3
+ require 'sane'
4
+ end
5
+
6
+ require_relative 'vlc_programmer'
7
+
8
+ class MencoderWrapper
9
+
10
+ class << self
11
+
12
+ def get_header this_drive
13
+ if File.exist?(@big_temp) && File.exist?(@big_temp + '.done')
14
+ ''
15
+ else
16
+ "call mencoder dvd:// -oac copy -lavcopts keyint=1 -ovc lavc -o #{@big_temp} -dvd-device #{this_drive} && echo got_file > #{@big_temp}.done\n"
17
+ end
18
+ end
19
+
20
+ def get_bat_commands these_mutes, this_drive, to_here_final_file, start_here = nil, end_here = nil
21
+ combined = VLCProgrammer.convert_incoming_to_split_sectors these_mutes
22
+
23
+ if start_here || end_here
24
+ raise 'need both' unless end_here && start_here
25
+ start_here = OverLayer.translate_string_to_seconds(start_here)
26
+ end_here = OverLayer.translate_string_to_seconds(end_here)
27
+ combined.select!{|start, endy, type| start > start_here && endy < end_here }
28
+ # it's relative now, since we rip from not the beginning
29
+ raise Exception.new('unable to find any edit decisions to do within that range') unless combined.length > 0
30
+ previous_end = start_here
31
+ else
32
+ previous_end = 0
33
+ end
34
+ @big_temp = to_here_final_file + ".fulli.tmp.avi"
35
+ out = get_header this_drive
36
+ @idx = 0
37
+ combined.each {|start, endy, type|
38
+ if start > previous_end
39
+ out += get_section previous_end, start, false, to_here_final_file
40
+ end
41
+ # type is either mute or :blank or :mute
42
+ if type == :blank
43
+ # do nothing... clip will be skipped
44
+ else
45
+ out += get_section start, endy, true, to_here_final_file
46
+ end
47
+ previous_end = endy
48
+ }
49
+ out += get_section previous_end, end_here || 1_000_000, false, to_here_final_file
50
+ partials = (1..@idx).map{|n| "#{to_here_final_file}.#{n}.avi"}
51
+
52
+ out += "del #{to_here_final_file}\n"
53
+ # ridiculous
54
+ out += "call mencoder #{partials.join(' ')} -o #{to_here_final_file}.smplayer_or_vlc.avi -ovc copy -oac copy\n"
55
+ # LODO only do this if they want to watch it on their computer, with something other than smplayer, or want to make it smaller, as it takes *forever* longer
56
+ # LODO the "insta play" mode, or the "faster rip" mode (related...)
57
+ # out += "call mencoder -oac lavc -ovc lavc -of mpeg -mpegopts format=dvd:tsaf -vf scale=720:480,harddup -srate 48000 -af lavcresample=48000 -lavcopts vcodec=mpeg2video:vrc_buf_size=1835:vrc_maxrate=9800:vbitrate=5000:keyint=18:vstrict=0:acodec=ac3:abitrate=192:aspect=16/9 -ofps 30000/1001 #{partials.join(' ')} -o #{to_here_final_file}\n"
58
+
59
+ out += "@rem del #{@big_temp}\n" # LODO
60
+ out += "@rem del " + partials.join(' ') + "\n"# LODO
61
+ out += "echo wrote to #{to_here_final_file}.smplayer_or_vlc.avi"
62
+ out
63
+ end
64
+
65
+ def get_section start, endy, should_mute, to_here_final_file
66
+ raise if start == endy # should never actually happen...
67
+ # delete 0.001 as per wiki's suggestion.
68
+ endy = endy - start - 0.001
69
+ # very decreased volume is like muting :)
70
+ # LODO can we copy more here? ntsc-dvd supposedly remuxes...
71
+ codecs = should_mute ? "-vcodec copy -acodec ac3 -vol 0 " : "-vcodec copy -acodec copy " # LODO not have the ac3...hmm...
72
+ # ffmpeg -i from_here.avi -vcodec copy -acodec copy -ss 1:00 -t 1:00 out.avi
73
+ partial_filename = to_here_final_file + '.' + (@idx += 1).to_s + '.avi'
74
+ "del #{partial_filename}\ncall ffmpeg -i #{@big_temp} #{codecs} -ss #{start} -t #{endy} #{partial_filename}\n"
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ if $0 == __FILE__
82
+ require 'rubygems'
83
+ require 'sane'
84
+ puts 'syntax: yaml_file_name d:\ output (00:15 00:25) (--run)'
85
+ a = YAML.load_file ARGV.shift
86
+ drive = ARGV.shift
87
+ raise 'wrong drive' unless File.exist?(drive + "AUDIO_TS")
88
+ execute = ARGV.delete('--run')
89
+ commands =MencoderWrapper.get_bat_commands(a, drive, *ARGV)
90
+ if ARGV.length > 2
91
+ write_to = 'range.bat'
92
+ else
93
+ write_to = 'all.bat'
94
+ end
95
+ File.write(write_to, commands)
96
+ print 'wrote ' + write_to
97
+ system(write_to) if execute
98
+ end
data/lib/ocr.rb CHANGED
@@ -6,7 +6,8 @@ ENV['PATH'] = im_path.to_filename + ';' + ENV['PATH']
6
6
 
7
7
  if RubyWhich.new.which('identify').length == 0 || RubyWhich.new.which('convert').length == 0
8
8
  # for gem users, so I don't have to bundle it :P
9
- raise 'appears you do not have imagemagick installed (or not in your path) -- please install it! http://www.imagemagick.org/script/binary-releases.php'
9
+ puts 'appears you do not have imagemagick installed (or not in your path) -- please install it first! http://www.imagemagick.org/script/binary-releases.php'
10
+ sleep 100
10
11
  end
11
12
 
12
13
  # helper for OCR'ing single digits that were screen captured
@@ -192,7 +192,7 @@ class OverLayer
192
192
  end
193
193
  end
194
194
  check_reload_yaml
195
- time + state + "(r [or ctrl+c to quit]): "
195
+ time + state + "(r [ctrl+c or q to quit]): "
196
196
  end
197
197
 
198
198
  def keyboard_input char
@@ -1,7 +1,7 @@
1
1
  require 'win32/screenshot'
2
2
  require 'sane'
3
3
  require 'yaml'
4
- require_relative 'ocr'
4
+ require File.dirname(__FILE__)+ '/ocr'
5
5
 
6
6
  class ScreenTracker
7
7
 
@@ -34,7 +34,7 @@ class ScreenTracker
34
34
  @height = height
35
35
  @x = x; @y = y; @x2 = x+width; @y2 = y+height; @callback = callback
36
36
  @max_x = max_x
37
- raise 'poor width or wrong window' if @x2 > max_x || @x2 == x
37
+ raise "poor width or wrong window #{@x2} #{max_x} #{x}" if @x2 > max_x || @x2 == x
38
38
  if @y2 > max_y || @y2 == y
39
39
  raise 'poor height or wrong window'
40
40
  end
@@ -1,21 +1,13 @@
1
1
  require_relative 'overlayer'
2
2
 
3
- # add some common locations for vlc.exe to the path...
4
- ENV['PATH'] = ENV['PATH'] + ";C:\\Program Files\\VideoLAN\\VLC"+ ";D:\\Program Files\\VideoLAN\\VLC" + ";E:\\Program Files\\VideoLAN\\VLC"
5
-
6
3
  class VLCProgrammer
7
4
 
8
5
  def self.to_english s
9
6
  @overlayer ||= OverLayer.allocate
10
7
  @overlayer.translate_time_to_human_readable s
11
8
  end
12
-
13
- def self.convert_to_full_xspf incoming, filename = nil, drive_with_slash = nil, dvd_title_track = nil, dvd_title_name = nil
14
9
 
15
- @drive = drive_with_slash || "e:\\"
16
- @filename_or_playlist_if_nil = filename
17
- @dvd_title_track = dvd_title_track || "1"
18
- @dvd_title_name = dvd_title_name
10
+ def self.convert_incoming_to_split_sectors incoming
19
11
  mutes = incoming["mutes"] || {}
20
12
  blanks = incoming["blank_outs"] || {}
21
13
  mutes = mutes.map{|k, v| [OverLayer.translate_string_to_seconds(k), OverLayer.translate_string_to_seconds(v), :mute]}
@@ -59,23 +51,39 @@ class VLCProgrammer
59
51
  raise 'unexpected'
60
52
  end
61
53
  }
54
+
55
+ combined.select{|start, endy, type|
56
+ (start != nil) && (endy > start) # ignore mutes wholly contained within blanks...
57
+ }
58
+ end
62
59
 
60
+ def self.convert_to_full_xspf incoming, filename = nil, drive_with_slash = nil, dvd_title_track = nil, dvd_title_name = nil
61
+
62
+ @drive = drive_with_slash || "e:\\"
63
+ @filename_or_playlist_if_nil = filename
64
+ @dvd_title_track = dvd_title_track || "1"
65
+ @dvd_title_name = dvd_title_name
66
+ combined = convert_incoming_to_split_sectors incoming
67
+
63
68
  out = get_header
64
69
 
65
70
  previous_end = 0
66
71
  idx = 0
67
72
 
68
73
  combined.each{|start, endy, type|
69
- next unless start
70
- next if endy <= start # ignore mutes wholly contained within blanks
71
-
74
+ # next unless start
75
+ #next if endy <= start
76
+ real_start = start # NB that this fails with VLC or mplayer, because of i-frame difficulties (google mencoder start time)
72
77
  if previous_end != start
73
- # play up to next "questionable section"
74
- out += get_section("#{@dvd_title_name} : #{to_english previous_end} to #{to_english start} (clean)", previous_end, start, idx += 1)
78
+ # play 'uncut' up to next "questionable section"
79
+ out += get_section("#{@dvd_title_name} : #{to_english previous_end} to #{to_english start} (clean)", previous_end, real_start, idx += 1)
80
+ else
81
+ # immediately let it do the next action
75
82
  end
83
+
76
84
  # now play through the muted section...
77
85
  if type == :mute
78
- out += get_section "#{@dvd_title_name} : #{to_english start}s to #{to_english endy}s muted", start, endy, idx += 1, true
86
+ out += get_section "#{@dvd_title_name} : #{to_english start}s to #{to_english endy}s muted", real_start, endy, idx += 1, true
79
87
  end
80
88
  previous_end = endy
81
89
  }
@@ -100,7 +108,7 @@ class VLCProgrammer
100
108
  end
101
109
 
102
110
  def self.get_section title, start, stop, idx, no_audio = false
103
- loc = "dvd://#{@drive}@#{@dvd_title_track}"
111
+ loc = "dvdsimple://#{@drive}@#{@dvd_title_track}"
104
112
  if @filename_or_playlist_if_nil == nil
105
113
  "<track>
106
114
  <title>#{title}</title>
@@ -125,9 +133,9 @@ class VLCProgrammer
125
133
  filename = @filename_or_playlist_if_nil
126
134
  files = (1..idx).map{|n| "#{filename}.ps.#{n}"}
127
135
  # concat
128
- line = 'type ' + files.join(' ')
129
- line += " > #{@filename_or_playlist_if_nil}.ps\n"
130
- line += "rm " + files.join(' ') + "\n"
136
+ line = 'copy /b ' + files.join('+')
137
+ line += " #{@filename_or_playlist_if_nil}.ps\n"
138
+ line += "@rem del " + files.join(' ') + "\n" # LODO!
131
139
  line += "echo Done--you may now watch file #{filename}.ps in VLC player"
132
140
  end
133
141