sensible-cinema 0.26.0 → 0.26.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitmodules +3 -0
- data/README +9 -6
- data/TODO +91 -41
- data/VERSION +1 -1
- data/bin/sensible-cinema +13 -1506
- data/bin/sensible-cinema-cli +156 -151
- data/change_log_with_feature_list.txt +17 -2
- data/developer_how_to_contribute_to_the_project.txt +3 -2
- data/documentation/upconversion.txt +4 -0
- data/history_and_related_works_list.txt +1 -0
- data/{play_with_inserted_scene.bat → legal/play_with_inserted_scene.bat} +0 -0
- data/{play_with_overlay.bat → legal/play_with_overlay.bat} +0 -0
- data/legal2 +2 -0
- data/lib/count_down_timer_jruby_swing.rb +3 -4
- data/lib/edl_parser.rb +345 -339
- data/lib/eight_three.rb +6 -1
- data/lib/gui/sensible-cinema-base.rb +671 -0
- data/lib/gui/sensible-cinema-create.rb +304 -0
- data/lib/{sensible-cinema-dependencies.rb → gui/sensible-cinema-dependencies.rb} +0 -0
- data/lib/gui/sensible-cinema-normal.rb +349 -0
- data/lib/gui/sensible-cinema-side-by-side.rb +27 -0
- data/lib/gui/sensible-cinema-upconvert.rb +254 -0
- data/lib/screen_tracker.rb +1 -0
- data/spec/edl_parser.spec.rb +6 -0
- data/spec/notes +107 -16
- data/spec/sensible_cinema_gui.spec.rb +63 -58
- data/todo.inventionzy.txt +12 -0
- data/todo.propaganda +1 -2
- data/todo.upconvert +3 -1
- data/upconvert_netflix/record_screen/record.bat +2 -0
- data/upconvert_netflix/record_screen/recording/1.png +0 -0
- data/upconvert_netflix/record_screen/recording/10.png +0 -0
- data/upconvert_netflix/record_screen/recording/2.png +0 -0
- data/upconvert_netflix/record_screen/recording/3.png +0 -0
- data/upconvert_netflix/record_screen/recording/4.png +0 -0
- data/upconvert_netflix/record_screen/recording/5.png +0 -0
- data/upconvert_netflix/record_screen/recording/6.png +0 -0
- data/upconvert_netflix/record_screen/recording/7.png +0 -0
- data/upconvert_netflix/record_screen/recording/8.png +0 -0
- data/upconvert_netflix/record_screen/recording/9.png +0 -0
- data/upconvert_netflix/record_screen/recording/d.png +0 -0
- data/www/content_editor.html +2 -2
- data/zamples/edit_decision_lists/dvds/edls_being_edited/cars_disney.txt +5 -7
- data/zamples/edit_decision_lists/dvds/innerspace.txt +77 -0
- data/zamples/edit_decision_lists/netflix_instant/avatar-last-air-bender-movie.txt +12 -0
- data/zamples/edit_decision_lists/netflix_instant/avatar-last-airbender-series.txt +1 -0
- data/zamples/players/netflix/netflix_firefox_non_maximized.txt +1 -1
- metadata +28 -11
- data/lib/file_chooser.rb +0 -46
- data/lib/swing_helpers.rb +0 -142
- data/www/monkey.png +0 -0
data/lib/eight_three.rb
CHANGED
@@ -17,7 +17,12 @@ DWORD WINAPI GetShortPathName(
|
|
17
17
|
def self.convert_path_to_8_3 path
|
18
18
|
out = FFI::MemoryPointer.new 256 # bytes
|
19
19
|
path_to_8_3(path, out, out.length)
|
20
|
-
out.get_string
|
20
|
+
out = out.get_string
|
21
|
+
if out.size == 0
|
22
|
+
raise 'unable to convert--probably file does not exist yet ' + path
|
23
|
+
else
|
24
|
+
out
|
25
|
+
end
|
21
26
|
end
|
22
27
|
|
23
28
|
end
|
@@ -0,0 +1,671 @@
|
|
1
|
+
#!/usr/bin/ruby # so my editor will like the file...
|
2
|
+
=begin
|
3
|
+
Copyright 2010, Roger Pack
|
4
|
+
This file is part of Sensible Cinema.
|
5
|
+
|
6
|
+
Sensible Cinema is free software: you can redistribute it and/or modify
|
7
|
+
it under the terms of the GNU General Public License as published by
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
9
|
+
(at your option) any later version.
|
10
|
+
|
11
|
+
Sensible Cinema is distributed in the hope that it will be useful,
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
GNU General Public License for more details.
|
15
|
+
|
16
|
+
You should have received a copy of the GNU General Public License
|
17
|
+
along with Sensible Cinema. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
=end
|
19
|
+
|
20
|
+
|
21
|
+
alias system_original system
|
22
|
+
require 'fileutils'
|
23
|
+
|
24
|
+
class String
|
25
|
+
def snake_case
|
26
|
+
self.gsub(/::/, '/').
|
27
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
28
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
29
|
+
tr("-", "_").
|
30
|
+
downcase
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require_relative '../jruby-swing-helpers/swing_helpers'
|
35
|
+
|
36
|
+
# attempt to load on demand...i.e. faster...
|
37
|
+
for kls in [:MencoderWrapper, :MplayerEdl, :PlayAudio, :SubtitleProfanityFinder, :ConvertThirtyFps, :RubyClip, :DriveInfo]
|
38
|
+
autoload kls, "./lib/#{kls.to_s.snake_case}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# a few I'll always need eventually...
|
42
|
+
require_relative '../storage'
|
43
|
+
require_relative '../edl_parser'
|
44
|
+
require 'tmpdir'
|
45
|
+
require 'whichr'
|
46
|
+
require 'os'
|
47
|
+
if OS.doze?
|
48
|
+
autoload :WMI, 'ruby-wmi'
|
49
|
+
autoload :EightThree, './lib/eight_three'
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
if OS.windows?
|
54
|
+
vendor_cache = File.expand_path(File.dirname(__FILE__)) + '/../../vendor/cache'
|
55
|
+
for name in ['.', 'mencoder', 'ffmpeg']
|
56
|
+
# put them all before the old path
|
57
|
+
ENV['PATH'] = (vendor_cache + '/' + name).to_filename + ';' + ENV['PATH']
|
58
|
+
end
|
59
|
+
|
60
|
+
installed_smplayer_folders = Dir['{c,d,e,f,g}:/program files*/MPlayer for Windows*'] # sometimes ends with UI? huh?
|
61
|
+
|
62
|
+
for folder in installed_smplayer_folders
|
63
|
+
ENV['PATH'] = ENV['PATH'] + ";#{folder.gsub('/', "\\")}"
|
64
|
+
end
|
65
|
+
|
66
|
+
else
|
67
|
+
ENV['PATH'] = ENV['PATH'] + ':' + '/opt/local/bin' # add macports' bin in, just in case...
|
68
|
+
end
|
69
|
+
|
70
|
+
import 'javax.swing.ImageIcon'
|
71
|
+
require_relative './sensible-cinema-dependencies'
|
72
|
+
|
73
|
+
module SensibleSwing
|
74
|
+
include SwingHelpers # various swing classes
|
75
|
+
JFrame
|
76
|
+
VERSION = File.read(File.dirname(__FILE__) + "/../../VERSION").strip
|
77
|
+
puts "v. " + VERSION
|
78
|
+
|
79
|
+
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) # <sigh>
|
80
|
+
|
81
|
+
class MainWindow < JFrame
|
82
|
+
include SwingHelpers # work-around?
|
83
|
+
|
84
|
+
def initialize be_visible = true
|
85
|
+
super "Sensible-Cinema #{VERSION} (GPL)"
|
86
|
+
force_accept_license_first
|
87
|
+
setDefaultCloseOperation JFrame::EXIT_ON_CLOSE # default is exit on close
|
88
|
+
@panel = JPanel.new
|
89
|
+
@buttons = []
|
90
|
+
@panel.set_layout nil
|
91
|
+
add @panel # why can't I just slap these down? panel? huh?
|
92
|
+
@starting_button_y = 40
|
93
|
+
@button_width = 400
|
94
|
+
|
95
|
+
add_text_line "Welcome to Sensible Cinema!"
|
96
|
+
@starting_button_y += 10 # kinder ugly...
|
97
|
+
add_text_line " Rest mouse over buttons for \"help\" type descriptions (tooltips)."
|
98
|
+
add_text_line ""
|
99
|
+
|
100
|
+
setIconImage(ImageIcon.new(__DIR__ + "/../vendor/profs.png").getImage())
|
101
|
+
check_for_various_dependencies
|
102
|
+
set_visible be_visible
|
103
|
+
end
|
104
|
+
|
105
|
+
def we_are_in_create_mode
|
106
|
+
ARGV.index("--create-mode")
|
107
|
+
end
|
108
|
+
|
109
|
+
def new_jbutton title, tooltip = nil
|
110
|
+
button = JButton.new title
|
111
|
+
button.tool_tip = tooltip
|
112
|
+
button.set_bounds(44, @starting_button_y, @button_width, 23)
|
113
|
+
@panel.add button
|
114
|
+
@buttons << button
|
115
|
+
if block_given? # allow for new_jbutton("xx") do ... end [this is possible through some miraculous means LOL]
|
116
|
+
button.on_clicked { yield }
|
117
|
+
end
|
118
|
+
increment_button_location
|
119
|
+
button
|
120
|
+
end
|
121
|
+
|
122
|
+
def add_text_line line
|
123
|
+
jlabel = JLabel.new line
|
124
|
+
happy = Font.new("Tahoma", Font::PLAIN, 11)
|
125
|
+
jlabel.setFont(happy)
|
126
|
+
jlabel.set_bounds(44,@starting_button_y ,460,14)
|
127
|
+
@panel.add jlabel
|
128
|
+
increment_button_location 18
|
129
|
+
jlabel
|
130
|
+
end
|
131
|
+
|
132
|
+
def increment_button_location how_much = 30
|
133
|
+
@starting_button_y += how_much
|
134
|
+
setSize @button_width+80, @starting_button_y + 50
|
135
|
+
end
|
136
|
+
|
137
|
+
def force_accept_license_first
|
138
|
+
if !(LocalStorage['main_license_accepted'] == VERSION)
|
139
|
+
require_blocking_license_accept_dialog 'Sensible Cinema', 'gplv3', 'http://www.gnu.org/licenses/gpl.html', 'Sensible Cinema license agreement',
|
140
|
+
"Sensible Cinema is distributed under the gplv3 (http://www.gnu.org/licenses/gpl.html).\nBY CLICKING \"accept\" YOU SIGNIFY THAT YOU HAVE READ, UNDERSTOOD AND AGREED TO ABIDE BY THE TERMS OF THIS AGREEMENT"
|
141
|
+
require_blocking_license_accept_dialog 'Sensible Cinema', 'is_it_legal_to_copy_dvds.txt file', File.expand_path(File.dirname(__FILE__) + "/../documentation/is_it_legal_to_copy_dvds.txt"),
|
142
|
+
'is_it_legal_to_copy_dvds.txt file', 'I acknowledge that I have read, understand, accept and agree to abide by the implications noted in the documentation/is_it_legal_to_copy_dvds.txt file'
|
143
|
+
LocalStorage['main_license_accepted'] = VERSION
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
LocalStorage = Storage.new("sensible_cinema_storage")
|
148
|
+
|
149
|
+
def when_thread_done(thread)
|
150
|
+
Thread.new {thread.join; yield }
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
# a window that when closed doesn't bring the whole app down
|
155
|
+
def new_child_window
|
156
|
+
child = MainWindow.new
|
157
|
+
child.setDefaultCloseOperation(JFrame::DISPOSE_ON_CLOSE)
|
158
|
+
child.parent=self # this should have failed in the PPL
|
159
|
+
# make both windows visible z-wise
|
160
|
+
x, y = self.get_location.x, self.get_location.y
|
161
|
+
child.set_location(x + 100, y + 100)
|
162
|
+
child
|
163
|
+
end
|
164
|
+
|
165
|
+
def run_smplayer_non_blocking *args
|
166
|
+
pp caller
|
167
|
+
@background_thread = Thread.new {
|
168
|
+
run_smplayer_blocking *args
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
def run_smplayer_blocking play_this, title_track_maybe_nil, passed_in_extra_options, force_use_mplayer, show_subs, start_full_screen
|
173
|
+
unless File.exist?(File.expand_path(play_this))
|
174
|
+
raise play_this + ' non existing?' # till these go away in mac :)
|
175
|
+
end
|
176
|
+
|
177
|
+
extra_options = ""
|
178
|
+
# -framedrop is for slow CPU's
|
179
|
+
# same with -autosync to try and help it stay in sync... -mc 0.03 is to A/V correct 1s audio per 2s video
|
180
|
+
# -hardframedrop might help but hurts just too much
|
181
|
+
extra_options << " -framedrop "
|
182
|
+
# ?? extra_mplayer_commands << "-mc 0.016" ??
|
183
|
+
extra_options << " -autosync 15 "
|
184
|
+
|
185
|
+
unless show_subs
|
186
|
+
# disable subtitles
|
187
|
+
extra_options << " -nosub -noautosub -forcedsubsonly -sid 1000 "
|
188
|
+
end
|
189
|
+
extra_options << " -alang en "
|
190
|
+
extra_options += " -slang en "
|
191
|
+
|
192
|
+
parent_parent = File.basename(File.dirname(play_this))
|
193
|
+
force_use_mplayer ||= OS.mac?
|
194
|
+
if parent_parent == 'VIDEO_TS'
|
195
|
+
# case d:\yo\VIDEO_TS\title0.vob
|
196
|
+
dvd_device_dir = normalize_path(File.dirname(play_this))
|
197
|
+
if force_use_mplayer
|
198
|
+
extra_options += " -dvd-device \"#{dvd_device_dir}/..\""
|
199
|
+
else
|
200
|
+
# smplayer
|
201
|
+
raise if dvd_device_dir =~ / / && OS.mac? # not accomodated <sniff>
|
202
|
+
extra_options += " -dvd-device #{dvd_device_dir}/.."
|
203
|
+
end
|
204
|
+
play_this = "dvdnav://#{title_track_maybe_nil}"
|
205
|
+
elsif File.exist?(play_this + '/VIDEO_TS')
|
206
|
+
# case d:\ where d:\VIDEO_TS exists [DVD mounted in drive] or mac's /Volumes/YO
|
207
|
+
raise if play_this =~ / / # unexpected
|
208
|
+
extra_options += " -nocache -dvd-device #{play_this}"
|
209
|
+
play_this = "dvdnav://#{title_track_maybe_nil}"
|
210
|
+
else
|
211
|
+
# case g:\video\filename.mpg
|
212
|
+
# leave it the same...
|
213
|
+
end
|
214
|
+
if play_this =~ /dvdnav/ && title_track_maybe_nil
|
215
|
+
extra_options << " -msglevel identify=4 " # prevent smplayer from using *forever* to look up info on DVD's with -identify ...
|
216
|
+
end
|
217
|
+
|
218
|
+
extra_options += " -mouse-movements #{get_upconvert_secondary_settings} " # just in case smplayer also needs -mouse-movements... :) LODO
|
219
|
+
extra_options += " -lavdopts threads=#{OS.cpu_count} " # just in case this helps [supposed to with h.264] # fast *crashes* doze...
|
220
|
+
if force_use_mplayer
|
221
|
+
show_mplayer_instructions_once
|
222
|
+
conf_file = File.expand_path './mplayer_input_conf'
|
223
|
+
File.write conf_file, "ENTER {dvdnav} dvdnav select\nMOUSE_BTN0 {dvdnav} dvdnav select\nMOUSE_BTN0_DBL vo_fullscreen\nMOUSE_BTN2 vo_fullscreen\nKP_ENTER dvdnav select\n" # that KP_ENTER doesn't actually work. Nor the MOUSE_BTN0 on windows. Weird.
|
224
|
+
extra_options += " -font #{File.expand_path('subfont.ttf')} "
|
225
|
+
extra_options += " -volume 100 " # why start low? mplayer why oh why LODO
|
226
|
+
if OS.windows?
|
227
|
+
# direct3d for windows 7 old nvidia cards' sake [yipes] and also dvdnav sake
|
228
|
+
extra_options += " -vo direct3d "
|
229
|
+
conf_file = conf_file[2..-1] # strip off drive letter, which it doesn't seem to like no sir
|
230
|
+
end
|
231
|
+
if start_full_screen
|
232
|
+
extra_options += " -fs "
|
233
|
+
upconv = get_upconvert_vf_settings
|
234
|
+
upconv = "-vf #{upconv}" if upconv.present?
|
235
|
+
else
|
236
|
+
upconv = ""
|
237
|
+
end
|
238
|
+
c = "mplayer #{extra_options} #{upconv} -input conf=\"#{conf_file}\" #{passed_in_extra_options} \"#{play_this}\" "
|
239
|
+
else
|
240
|
+
if OS.windows?
|
241
|
+
extra_options += " -vo direct3d " # more light nvidia...should be ok...
|
242
|
+
end
|
243
|
+
set_smplayer_opts extra_options + " " + passed_in_extra_options, get_upconvert_vf_settings, show_subs
|
244
|
+
c = "smplayer_portable \"#{play_this}\" -config-path \"#{File.dirname SMPlayerIniFile}\" "
|
245
|
+
c += " -fullscreen " if start_full_screen
|
246
|
+
if !we_are_in_create_mode
|
247
|
+
#c += " -close-at-end " # we're still too unstable, mate...
|
248
|
+
end
|
249
|
+
end
|
250
|
+
puts c
|
251
|
+
system_blocking c
|
252
|
+
end
|
253
|
+
|
254
|
+
SMPlayerIniFile = File.expand_path("~/.smplayer/smplayer.ini")
|
255
|
+
|
256
|
+
def set_smplayer_opts to_this, video_, show_subs = false
|
257
|
+
p 'set smplayer extra opts to this:' + to_this
|
258
|
+
old_prefs = File.read(SMPlayerIniFile) rescue ''
|
259
|
+
unless old_prefs.length > 0
|
260
|
+
# LODO double check the rest here...
|
261
|
+
old_prefs = "[advanced]\nmplayer_additional_options=\nmplayer_additional_video_filters=\n[subtitles]\nautoload_sub=false\n[performance]\npriority=3"
|
262
|
+
end
|
263
|
+
raise to_this if to_this =~ /"/ # unexpected, unfortunately... <smplayer bug>
|
264
|
+
assert new_prefs = old_prefs.gsub(/mplayer_additional_options=.*/, "mplayer_additional_options=#{to_this}")
|
265
|
+
assert new_prefs.gsub!(/autoload_sub=.*$/, "autoload_sub=#{show_subs.to_s}")
|
266
|
+
raise if get_upconvert_vf_settings =~ /"/
|
267
|
+
assert new_prefs.gsub!(/mplayer_additional_video_filters=.*$/, "mplayer_additional_video_filters=\"#{get_upconvert_vf_settings}\"")
|
268
|
+
new_prefs.gsub!(/priority=.*$/, "priority=3") # normal priority...scary otherwise! lodo tell smplayer...
|
269
|
+
# enable dvdnav navigation, just for kicks I guess.
|
270
|
+
new_prefs.gsub!(/use_dvdnav=.*$/, "use_dvdnav=true")
|
271
|
+
|
272
|
+
FileUtils.mkdir_p File.dirname(SMPlayerIniFile) # case it doesn't yet exist
|
273
|
+
File.write(SMPlayerIniFile, new_prefs)
|
274
|
+
new_prefs.each_line{|l| print l if l =~ /additional_video/} # debug
|
275
|
+
end
|
276
|
+
def system_blocking command, low_prio = false
|
277
|
+
return true if command =~ /^@rem/ # JRUBY-5890 bug
|
278
|
+
if low_prio
|
279
|
+
out = IO.popen(command) # + " 2>&1"
|
280
|
+
low_prio = 64 # from msdn
|
281
|
+
|
282
|
+
if command =~ /(ffmpeg|mencoder)/
|
283
|
+
# XXXX not sure if there's a better way...because some *are* complex and have ampersands...
|
284
|
+
# unfortunately have to check for nil because it could exit too early [?]
|
285
|
+
exe_name = $1 + '.exe'
|
286
|
+
begin
|
287
|
+
p = proc{ ole = WMI::Win32_Process.find(:first, :conditions => {'Name' => exe_name}); sleep 1 unless ole; ole }
|
288
|
+
piddy = p.call || p.call || p.call # we actually do need this to loop...guess we're too quick
|
289
|
+
# but the first time through this still inexplicably fails all 3...odd
|
290
|
+
piddys = WMI::Win32_Process.find(:all, :conditions => {'Name' => exe_name})
|
291
|
+
for piddy in piddys
|
292
|
+
# piddy.SetPriority low_prio # this call can seg fault at times...JRUBY-5422
|
293
|
+
pid = piddy.ProcessId # this doesn't seg fault, tho
|
294
|
+
system_original("vendor\\setpriority -lowest #{pid}") # uses PID for the command line
|
295
|
+
end
|
296
|
+
rescue Exception => e
|
297
|
+
p 'warning, got exception trying to set priority [jruby prob? ...]', e
|
298
|
+
end
|
299
|
+
end
|
300
|
+
print out.read # let it finish
|
301
|
+
out.close
|
302
|
+
$?.exitstatus == 0 # 0 means success
|
303
|
+
else
|
304
|
+
raise command + " failed env #{ENV['PATH']}" unless system_original command
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def system_non_blocking command
|
309
|
+
@background_thread = Thread.new { system_original command }
|
310
|
+
end
|
311
|
+
|
312
|
+
# make them choose which system call to use explicitly
|
313
|
+
undef system
|
314
|
+
|
315
|
+
def play_dvd_smplayer_unedited use_mplayer_instead, show_instructions, show_subs
|
316
|
+
drive_or_file, dvd_volume_name, dvd_id, edl_path_maybe_nil, descriptors_maybe_nil = choose_dvd_or_file_and_edl_for_it false
|
317
|
+
if descriptors_maybe_nil
|
318
|
+
title_track_maybe_nil = get_title_track(descriptors_maybe_nil, false)
|
319
|
+
end
|
320
|
+
if show_instructions
|
321
|
+
# want these even with smplayer sometimes I guess, if in power user mode anyway
|
322
|
+
show_mplayer_instructions_once
|
323
|
+
end
|
324
|
+
run_smplayer_non_blocking drive_or_file, title_track_maybe_nil, "-osd-fractions 2", use_mplayer_instead, show_subs, false
|
325
|
+
end
|
326
|
+
|
327
|
+
if OS.doze? # avoids spaces in filenames :)
|
328
|
+
EdlTempFile = EightThree.convert_path_to_8_3(Dir.tmpdir) + '\\mplayer.temp.edl'
|
329
|
+
else
|
330
|
+
raise if Dir.tmpdir =~ / / # that would be unexpected, and probably cause problems...
|
331
|
+
EdlTempFile = Dir.tmpdir + '/mplayer.temp.edl'
|
332
|
+
end
|
333
|
+
|
334
|
+
def show_mplayer_instructions_once
|
335
|
+
@_show_mplayer_instructions_once ||= show_non_blocking_message_dialog <<-EOL
|
336
|
+
About to run mplayer. To control it, use
|
337
|
+
spacebar : pause,
|
338
|
+
double clicky/right click : toggle full screen,
|
339
|
+
arrow keys (left, right, up down, pg up, pg dn) to seek/scan
|
340
|
+
/ and * : inc/dec volume.
|
341
|
+
'o' key: turn on on-screen-display timestamps (note: the OSD timestamps [upper left] are 30 fps so will need to be converted to use).
|
342
|
+
'v' key: turn off subtitles.
|
343
|
+
'.' key: step one frame.
|
344
|
+
# key: change audio language track
|
345
|
+
EOL
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
def choose_dvd_or_file_and_edl_for_it force_choose_edl_file_if_no_easy_match = true
|
350
|
+
drive_or_file, dvd_volume_name, dvd_id = choose_dvd_drive_or_file false
|
351
|
+
|
352
|
+
unless @_edit_list_path # cache file selection...
|
353
|
+
edit_list_path = EdlParser.single_edit_list_matches_dvd(dvd_id)
|
354
|
+
if !edit_list_path && force_choose_edl_file_if_no_easy_match
|
355
|
+
edit_list_path = new_existing_file_selector_and_select_file("Please pick a DVD Edit List File (none or more than one were found that seem to match #{dvd_volume_name})--may need to create one for it", EdlParser::EDL_DIR)
|
356
|
+
raise 'cancelled choosing an EDL' unless edit_list_path
|
357
|
+
end
|
358
|
+
@_edit_list_path = edit_list_path
|
359
|
+
end
|
360
|
+
p 'reloading'
|
361
|
+
if @_edit_list_path
|
362
|
+
# reload it every time just in case it has changed on disk
|
363
|
+
descriptors = nil
|
364
|
+
begin
|
365
|
+
descriptors = parse_edl @_edit_list_path
|
366
|
+
rescue SyntaxError => e
|
367
|
+
show_non_blocking_message_dialog("this file has an error--please fix then hit ok: \n" + @_edit_list_path + "\n " + e)
|
368
|
+
raise e
|
369
|
+
end
|
370
|
+
end
|
371
|
+
[drive_or_file, dvd_volume_name, dvd_id, @_edit_list_path, descriptors]
|
372
|
+
end
|
373
|
+
|
374
|
+
MplayerBeginingBuffer = 1.0
|
375
|
+
MplayerEndBuffer = 0.0
|
376
|
+
|
377
|
+
def play_mplayer_edl_non_blocking optional_file_with_edl_path = nil, extra_mplayer_commands_array = [], force_mplayer = false, start_full_screen = true
|
378
|
+
if optional_file_with_edl_path
|
379
|
+
drive_or_file, edl_path = optional_file_with_edl_path
|
380
|
+
dvd_id = NonDvd # fake it out...LODO a bit smelly
|
381
|
+
else
|
382
|
+
drive_or_file, dvd_volume_name, dvd_id, edl_path, descriptors = choose_dvd_or_file_and_edl_for_it
|
383
|
+
end
|
384
|
+
start_add_this_to_all_ts = 0
|
385
|
+
if edl_path # some don't care...
|
386
|
+
descriptors = EdlParser.parse_file edl_path
|
387
|
+
title_track = get_title_track(descriptors)
|
388
|
+
splits = descriptors['mplayer_dvd_splits']
|
389
|
+
end
|
390
|
+
|
391
|
+
if dvd_id == NonDvd && !(File.basename(File.dirname(drive_or_file)) == 'VIDEO_TS') # VOB's...always start at 0
|
392
|
+
# check if starts offset...
|
393
|
+
all = `ffmpeg -i "#{drive_or_file}" 2>&1`
|
394
|
+
# Duration: 01:35:49.59, start: 600.000000
|
395
|
+
all =~ /Duration.*start: ([\d\.]+)/
|
396
|
+
start = $1.to_f
|
397
|
+
if start > 1 # LODO huh? dvd's themselves start at 0.3 [sintel]?
|
398
|
+
show_non_blocking_message_dialog "Warning: file seems to start at an extra offset, adding it to the timestamps... #{start}
|
399
|
+
maybe not compatible with XBMC, if that's what you use, and you probably don't" # TODO test it XBMC...
|
400
|
+
start_add_this_to_all_ts = start
|
401
|
+
end
|
402
|
+
splits = []
|
403
|
+
else
|
404
|
+
if splits == nil
|
405
|
+
show_blocking_message_dialog("warning: edit list does not contain mplayer replay information [mplayer_dvd_splits] so edits past a certain time period might not won't work ( http://goo.gl/yMfqX ).")
|
406
|
+
splits = []
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
if edl_path
|
411
|
+
splits.map!{|s| EdlParser.translate_string_to_seconds(s) }
|
412
|
+
edl_contents = MplayerEdl.convert_to_edl descriptors, add_secs_end = MplayerEndBuffer, add_secs_begin = MplayerBeginingBuffer, splits, start_add_this_to_all_ts # add a sec to mutes to accomodate for mplayer's oddness..
|
413
|
+
File.write(EdlTempFile, edl_contents)
|
414
|
+
extra_mplayer_commands_array << "-edl #{File.expand_path EdlTempFile}"
|
415
|
+
end
|
416
|
+
|
417
|
+
run_smplayer_non_blocking drive_or_file, title_track, extra_mplayer_commands_array.join(' '), force_mplayer, false, start_full_screen
|
418
|
+
end
|
419
|
+
|
420
|
+
def assert_ownership_dialog
|
421
|
+
message = "Do you certify you own the DVD this came of and have it in your possession?"
|
422
|
+
title = "Verify ownership"
|
423
|
+
returned = JOptionPane.show_select_buttons_prompt(message, {})
|
424
|
+
assert_confirmed_dialog returned, nil
|
425
|
+
end
|
426
|
+
|
427
|
+
def require_blocking_license_accept_dialog program, license_name, license_url_should_also_be_embedded_by_you_in_message,
|
428
|
+
title = 'Confirm Acceptance of License Agreement', message = nil
|
429
|
+
puts 'Please confirm license agreement in open window.'
|
430
|
+
# cancel button stays the same...
|
431
|
+
|
432
|
+
message ||= "Sensible Cinema requires a separately installed program (#{program}), not yet installed.
|
433
|
+
You can install this program manually to the vendor/cache subdirectory, or Sensible Cinema can download it for you.
|
434
|
+
By clicking accept, below, you are confirming that you have read and agree to be bound by the
|
435
|
+
terms of its license (the #{license_name}), located at #{license_url_should_also_be_embedded_by_you_in_message}.
|
436
|
+
Click 'View License' to view it. If you do not agree to these terms, click 'Cancel'. You also agree that this is a
|
437
|
+
separate program, with its own distribution, license, ownership and copyright.
|
438
|
+
You agree that you are responsible for the download and use of this program, within sensible cinema or otherwise."
|
439
|
+
answer = JOptionPane.show_select_buttons_prompt message, :yes => 'Accept', :no => "View #{license_name}"
|
440
|
+
assert_confirmed_dialog answer, license_url_should_also_be_embedded_by_you_in_message
|
441
|
+
p 'confirmation of sensible cinema related license saved of: ' + license_name
|
442
|
+
throw unless returned == 0
|
443
|
+
|
444
|
+
end
|
445
|
+
|
446
|
+
def assert_confirmed_dialog answer, license_url_should_also_be_embedded_by_you_in_message
|
447
|
+
# :yes, :no, :cancel
|
448
|
+
# 1 is view button was clicked
|
449
|
+
# 0 is accept
|
450
|
+
# 2 is cancel
|
451
|
+
if returned == :no
|
452
|
+
if license_url_should_also_be_embedded_by_you_in_message
|
453
|
+
system_non_blocking("start #{license_url_should_also_be_embedded_by_you_in_message}")
|
454
|
+
puts "Please restart after reading license agreement, to be able to then accept it."
|
455
|
+
end
|
456
|
+
System.exit 0
|
457
|
+
elsif returned == :cancel
|
458
|
+
p 'license not accepted...exiting'
|
459
|
+
System.exit 1
|
460
|
+
elsif returned == :exited
|
461
|
+
p 'license exited early...exiting'
|
462
|
+
System.exit 1
|
463
|
+
elsif returned == :yes
|
464
|
+
# ok
|
465
|
+
else
|
466
|
+
raise 'unknown'
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
def print *args
|
471
|
+
Kernel.print *args # avoid bin\sensible-cinema.rb:83:in `system_blocking': cannot convert instance of class org.jruby.RubyString to class java.awt.Graphics (TypeError)
|
472
|
+
end
|
473
|
+
|
474
|
+
def download_7zip
|
475
|
+
Dir.mkdir('./vendor/cache') unless File.directory? 'vendor/cache' # development may not have it created yet... [?]
|
476
|
+
unless File.exist? 'vendor/cache/7za.exe'
|
477
|
+
Dir.chdir('vendor/cache') do
|
478
|
+
print 'downloading unzipper (7zip--400K) ...'
|
479
|
+
MainWindow.download("http://downloads.sourceforge.net/project/sevenzip/7-Zip/9.20/7za920.zip", "7za920.zip")
|
480
|
+
system_blocking("../unzip.exe -o 7za920.zip") # -o means "overwrite" without prompting
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
def download_zip_file_and_extract english_name, url, to_this
|
486
|
+
download_7zip
|
487
|
+
Dir.chdir('vendor/cache') do
|
488
|
+
file_name = url.split('/')[-1]
|
489
|
+
print "downloading #{english_name} ..."
|
490
|
+
MainWindow.download(url, file_name)
|
491
|
+
system_blocking("7za e #{file_name} -y -o#{to_this}")
|
492
|
+
puts 'done ' + english_name
|
493
|
+
# creates vendor/cache/mencoder/mencoder.exe...
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
def check_for_exe windows_full_loc, unix_name
|
498
|
+
# in windows, that exe *at that location* must exist...
|
499
|
+
if OS.windows?
|
500
|
+
File.exist?(windows_full_loc)
|
501
|
+
else
|
502
|
+
require 'lib/check_installed_mac.rb'
|
503
|
+
if !CheckInstalledMac.check_for_installed(unix_name)
|
504
|
+
exit 1 # it'll have already displayed a message...
|
505
|
+
else
|
506
|
+
true
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
def check_for_various_dependencies
|
512
|
+
if we_are_in_create_mode
|
513
|
+
if !check_for_exe('vendor/cache/mencoder/mencoder.exe', 'mencoder')
|
514
|
+
require_blocking_license_accept_dialog 'mplayer', 'gplv2', 'http://www.gnu.org/licenses/gpl-2.0.html', "Appears that you need to install a dependency: mplayer with mencoder."
|
515
|
+
download_zip_file_and_extract "Mplayer/mencoder (6MB)", "http://sourceforge.net/projects/mplayer-win32/files/MPlayer%20and%20MEncoder/revision%2033883/MPlayer-rtm-svn-33883.7z", "mencoder"
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
# runtime dependencies, at least as of today...
|
520
|
+
ffmpeg_exe_loc = File.expand_path('vendor/cache/ffmpeg/ffmpeg.exe')
|
521
|
+
if !check_for_exe(ffmpeg_exe_loc, 'ffmpeg')
|
522
|
+
require_blocking_license_accept_dialog 'ffmpeg', 'gplv2', 'http://www.gnu.org/licenses/gpl-2.0.html', "Appears that you need to install a dependency: ffmpeg."
|
523
|
+
download_zip_file_and_extract "ffmpeg (5MB)", "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-git-335bbe4-win32-static.7z", "ffmpeg"
|
524
|
+
end
|
525
|
+
if OS.mac?
|
526
|
+
check_for_exe("mplayer", "mplayer") # mencoder and mplayer are separate for mac... [this checks for mac's mplayerx, too]
|
527
|
+
else
|
528
|
+
path = RubyWhich.new.which('smplayer_portable')
|
529
|
+
if(path.length == 0)
|
530
|
+
# this one has its own installer...
|
531
|
+
show_blocking_message_dialog("It appears that you need to install a pre-requisite dependency: MPlayer for Windows (MPUI).
|
532
|
+
Click ok to be directed to its download website, where you can download and install it (recommend: MPUI....Full-Package.exe),
|
533
|
+
then restart sensible cinema. NB that it takes awhile to install. Sorry about that.",
|
534
|
+
"Lacking dependency", JOptionPane::ERROR_MESSAGE)
|
535
|
+
open_url_to_view_it_non_blocking "http://code.google.com/p/mulder/downloads/list?can=2&q=MPlayer&sort=-uploaded&colspec=Filename%20Summary%20Type%20Uploaded%20Size%20DownloadCount"
|
536
|
+
System.exit 0
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
def open_url_to_view_it_non_blocking url
|
542
|
+
if OS.windows?
|
543
|
+
system_non_blocking("start #{url.gsub('&', '^&')}") # LODO would launchy help/work here with the full url?
|
544
|
+
else
|
545
|
+
system_non_blocking "#{OS.open_file_command} \"#{url}\"" # LODO test
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
def open_file_to_edit_it filename, start_minimized=false
|
550
|
+
if OS.windows?
|
551
|
+
if start_minimized
|
552
|
+
system_non_blocking "start /min notepad \"#{filename}\""
|
553
|
+
else
|
554
|
+
system_non_blocking "notepad \"#{filename}\""
|
555
|
+
end
|
556
|
+
else
|
557
|
+
system_non_blocking "open -a TextEdit \"#{filename}\""
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def new_nonexisting_filechooser_and_go title = nil, default_dir = nil, default_file = nil
|
562
|
+
JFileChooser.new_nonexisting_filechooser_and_go title, default_dir, default_file
|
563
|
+
end
|
564
|
+
|
565
|
+
def show_blocking_message_dialog(message, title = message.split("\n")[0], style= JOptionPane::INFORMATION_MESSAGE)
|
566
|
+
SwingHelpers.show_blockiing_message_dialog message, title, style
|
567
|
+
end
|
568
|
+
|
569
|
+
# call dispose on this to close it if it hasn't been canceled yet...
|
570
|
+
def show_non_blocking_message_dialog message, close_button_text = 'Close'
|
571
|
+
# lodo NonBlockingDialog it can get to the top instead of being so buried...
|
572
|
+
NonBlockingDialog.new(message, close_button_text)
|
573
|
+
end
|
574
|
+
|
575
|
+
include_class javax.swing.UIManager
|
576
|
+
|
577
|
+
def get_user_input(message, default = '', cancel_ok = false)
|
578
|
+
SwingHelpers.get_user_input message, default, cancel_ok
|
579
|
+
end
|
580
|
+
|
581
|
+
def show_copy_pastable_string(message, value)
|
582
|
+
RubyClip.set_clipboard value
|
583
|
+
get_user_input message + " (has been copied to clipboard)", value, true
|
584
|
+
end
|
585
|
+
|
586
|
+
# also caches directory previously selected ...
|
587
|
+
def new_existing_file_selector_and_select_file title, dir = nil
|
588
|
+
dir ||= LocalStorage[caller.inspect] = File.dirname(got)
|
589
|
+
got = FileDialog.new_previously_existing_file_selector_and_go title, dir
|
590
|
+
LocalStorage[caller.inspect] = File.dirname(got)
|
591
|
+
got
|
592
|
+
end
|
593
|
+
|
594
|
+
def show_in_explorer filename
|
595
|
+
SwingHelpers.show_in_explorer filename
|
596
|
+
end
|
597
|
+
|
598
|
+
def get_disk_chooser_window names
|
599
|
+
GetDisk.new(self, names)
|
600
|
+
end
|
601
|
+
|
602
|
+
end
|
603
|
+
|
604
|
+
class GetDisk < JDialog
|
605
|
+
attr_reader :selected_idx
|
606
|
+
def initialize parent, options_array
|
607
|
+
super parent, true
|
608
|
+
|
609
|
+
box = JComboBox.new
|
610
|
+
box.add_action_listener do |e|
|
611
|
+
idx = box.get_selected_index
|
612
|
+
if idx != 0
|
613
|
+
# don't count choosing the first as a real entry
|
614
|
+
@selected_idx = box.get_selected_index - 1
|
615
|
+
dispose
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
box.add_item "Click to select DVD drive" # put something in index 0
|
620
|
+
options_array.each{|drive|
|
621
|
+
box.add_item drive
|
622
|
+
}
|
623
|
+
add box
|
624
|
+
pack # how do you get this arbitrary size? what the...
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
# LODO move to sane :) also remove the andand dep.
|
630
|
+
class String
|
631
|
+
def present?
|
632
|
+
length > 0
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
class NilClass
|
637
|
+
def present?
|
638
|
+
false
|
639
|
+
end
|
640
|
+
end
|
641
|
+
class Object
|
642
|
+
def present?
|
643
|
+
true
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
class Array
|
648
|
+
def present?
|
649
|
+
length > 0
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
|
654
|
+
class File
|
655
|
+
def self.get_root_dir this_path
|
656
|
+
this_path = File.expand_path this_path
|
657
|
+
if OS.doze?
|
658
|
+
this_path[0..2]
|
659
|
+
else
|
660
|
+
this_path.split('/')[0]
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
def self.strip_drive_windows this_complete_path
|
665
|
+
if OS.doze?
|
666
|
+
this_complete_path[2..-1]
|
667
|
+
else
|
668
|
+
this_complete_path
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|