#!/usr/bin/ruby # so my editor will like it... =begin Copyright 2010, Roger Pack This file is part of Sensible Cinema. Sensible Cinema is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Sensible Cinema is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Sensible Cinema. If not, see . =end print 'Loading Sensible Cinema...' require File.expand_path(File.dirname(__FILE__) + "/../lib/add_any_bundled_gems_to_load_path.rb") begin require 'sane' rescue LoadError p 'please run $ rake rebundle_copy_in_dependencies first, as we dislike needing locally installed rubygems' raise end raise 'need jruby' unless OS.java? raise 'need newer jruby!' unless RUBY_DESCRIPTION >= 'jruby 1.6.0' # may not need this since we still have to accomodate for so many oddities :P alias system_original system require 'fileutils' require_relative '../lib/mencoder_wrapper' require_relative '../lib/storage' require_relative '../lib/edl_parser' require_relative '../lib/mplayer_edl' require_relative '../lib/play_audio' require_relative '../lib/subtitle_profanity_finder' require_relative '../lib/convert_thirty_fps' require_relative '../lib/ruby_clip' require 'tmpdir' require_relative '../lib/swing_helpers' require_relative '../lib/drive_info' require 'whichr' require 'os' require 'ruby-wmi' unless OS.mac? vendor_cache = File.expand_path(File.dirname(__FILE__)) + '/../vendor/cache' if OS.windows? for name in ['.', 'mencoder', 'ffmpeg'] # put them all before the old path ENV['PATH'] = (vendor_cache + '/' + name).to_filename + ';' + ENV['PATH'] end installed_smplayer_folders = Dir['{c,d,e,f,g}:/program files*/MPlayer for Windows*'] # sometimes ends with UI? huh? for folder in installed_smplayer_folders ENV['PATH'] = ENV['PATH'] + ";#{folder.gsub('/', "\\")}" end else ENV['PATH'] = ENV['PATH'] + ':' + '/opt/local/bin' # add macports' bin in, just in case... end import 'javax.swing.ImageIcon' module SensibleSwing VERSION = File.read(File.dirname(__FILE__) + "/../VERSION").strip puts "v. " + VERSION UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) # sigh class MainWindow < JFrame def we_are_in_create_mode ARGV.index("--create-mode") end # currently advanced mode only *adds* buttons def new_jbutton title, tooltip = nil button = JButton.new title button.tool_tip = tooltip button.set_bounds(44, @starting_button_y, @button_width, 23) @panel.add button @buttons << button if block_given? # allow for new_jbutton("xx") do ... end [this works through some miraculous means LOL] button.on_clicked { yield } end increment_button_location button end def add_text_line line jlabel = JLabel.new line happy = Font.new("Tahoma", Font::PLAIN, 11) jlabel.setFont(happy) jlabel.set_bounds(44,@starting_button_y ,460,14) @panel.add jlabel increment_button_location 18 jlabel end def increment_button_location how_much = 30 @starting_button_y += how_much setSize @button_width+80, @starting_button_y + 50 end def force_accept_license_first require_blocking_license_accept_dialog 'Sensible Cinema', 'gplv3', 'http://www.gnu.org/licenses/gpl.html', 'Sensible Cinema license agreement', "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" 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"), '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' LocalStorage['main_license_accepted'] = VERSION end LocalStorage = Storage.new("sensible_cinema_storage") LocalStorage.set_default('multiple_half_screens', 4) def setup_upconvert_buttons @watch_file_upconvert = new_jbutton( "Watch a file upconverted (unedited)") do # ?? drive, volume, dvd_id = choose_dvd_drive_or_file true warn_if_no_upconvert_options_currently_selected filename_mpg = new_existing_file_selector_and_select_file( "pick moviefile (like moviename.mpg)") play_mplayer_edl [filename_mpg, nil] end @watch_file_upconvert = new_jbutton( "Watch a DVD upconverted (unedited)") do warn_if_no_upconvert_options_currently_selected play_dvd_smplayer_unedited false, false end @upconv_line = add_text_line '' change_upconvert_line_to_current @show_upconvert_options = new_jbutton( "Change upconvert options" ) do add_setup_upconvert_options end end def warn_if_no_upconvert_options_currently_selected if !LocalStorage[UpConvertEnglish] show_blocking_message_dialog "Warning: upconvert options have not been set yet--set upconvert options first and restart, if desired." end end def change_upconvert_line_to_current @upconv_line.set_text get_current_upconvert_as_phrase end UpConvertKey = 'upconvert_setting' UpConvertKeyExtra = 'upconvert_setting_extra' UpConvertEnglish = 'upconvert_english_name' def add_setup_upconvert_options none = new_jbutton("reset upconvert options to default (none)") none.tool_tip = "Having no upconvert options is reasonably good, might use directx for scaling, nice for slow cpu's" none.on_clicked { LocalStorage[UpConvertKey] = nil LocalStorage[UpConvertKeyExtra] = nil LocalStorage[UpConvertEnglish] = nil display_current_upconvert_setting } medium_dvd = new_jbutton("set upconvert options to DVD-style input") { LocalStorage[UpConvertKey] = "pullup,softskip,hqdn3d=0:1:4:4,scale=SCREEN_X:-10:0:0:2" # hqdn3d[=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp] LocalStorage[UpConvertKeyExtra] = "-sws 9 -ssf ls=75.0 -ssf cs=7.0" LocalStorage[UpConvertEnglish] = "DVD" display_current_upconvert_setting } high_compression = new_jbutton("set upconvert options for playing back highly compressed video") { # -autoq 6 -vf pp [?] LocalStorage[UpConvertEnglish] = "high compressed" LocalStorage[UpConvertKey] = "pullup,softskip,hqdn3d=0:1:4:4,pp=hb:y/vb:y,scale=SCREEN_X:-10:0:0:3" # ordering? LocalStorage[UpConvertKeyExtra] = "-sws 9 -ssf ls=75.0 -ssf cs=25.0" display_current_upconvert_setting # -Processing method: mplayer with accurate deblocking ?? } new_jbutton("set upconvert options to experimental DVD/HQ-style playback") { LocalStorage[UpConvertKey] = "pullup,softskip,hqdn3d=7:7:5,scale=SCREEN_X:-10:0:0:10" LocalStorage[UpConvertKeyExtra] = "-sws 9 -ssf ls=100.0 -ssf cs=75.0" LocalStorage[UpConvertEnglish] = "experimental DVD/HQ" display_current_upconvert_setting } slider = JSlider.new slider.setBorder(BorderFactory.createTitledBorder("Multiple factor of half screen widths (higher is better, but uses more cpu, default 4)")); slider.maximum=6# LODO non 6 here... slider.minimum=0 slider.setMajorTickSpacing(1) slider.setPaintTicks(true) slider.setPaintLabels(true) slider.snap_to_ticks=true slider.set_value LocalStorage['multiple_half_screens'] slider.add_change_listener { |event| if slider.value >= 2 LocalStorage['multiple_half_screens'] = slider.value else show_blocking_message_dialog "you can't set it lower than 2 that wouldn't make any sense, since that's screen width" end } slider.set_bounds(44, @starting_button_y, @button_width, 56) 2.times {increment_button_location} @panel.add(slider); #// content.add(slider, BorderLayout::SOUTH); end def display_current_upconvert_setting change_upconvert_line_to_current show_blocking_message_dialog get_current_upconvert_as_phrase end def get_current_upconvert_as_phrase settings = LocalStorage[UpConvertEnglish] "Upconvert options #{ settings ? "are set to #{settings}" : "not currently set"}" end def get_upconvert_vf_settings template = LocalStorage[UpConvertKey] if template # choose width of widest monitor (why would they display it on the other?) screen_x = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment.getScreenDevices.map{|gd| gd.display_mode.width}.max.to_i screen_multiple = LocalStorage['multiple_half_screens']/2 template.gsub('SCREEN_X', (screen_x*screen_multiple).to_i.to_s) # has to be an integer... else nil end end def get_upconvert_secondary_settings LocalStorage[UpConvertKeyExtra] end def setup_advanced_buttons add_text_line 'Realtime create Options:' @create_new_edl_for_current_dvd = new_jbutton("Create new Edit List for a DVD", "If your DVD doesn't have an EDL created for it, this will be your first step--create an EDL file for it.") @create_new_edl_for_current_dvd.on_clicked do create_brand_new_edl end @open_list = new_jbutton("Open/Edit a previously created EDL file", "If your DVD has a previously existing EDL for it, you can open it to edit it with this button.") @open_list.on_clicked { filename = new_existing_file_selector_and_select_file( "Pick any file to open in editor", EDL_DIR) open_file_to_edit_it filename } @create_dot_edl = new_jbutton( "Create an associatable moviefilename.edl file") @create_dot_edl.tool_tip = <<-EOL Create a moviefilename.edl file (corresponding to moviefilename.some_ext which already exists) XBMC/smplayer (smplayer can be used by WMC plugins, etc.) "automagically detect", if it exists, and automatically use it .edl to show that file edited played back. If you use smplayer, note that you'll need to download the "lord mulder mplayer" version (which includes an updated version of mplayer that fixes some bugs in EDL playback) EOL @create_dot_edl.on_clicked { choose_file_and_edl true } @play_smplayer = new_jbutton( "Watch full DVD unedited (realtime smplayer)") @play_smplayer.tool_tip = <<-EOL This will play the DVD unedited within smplayer. NB it will default to title 1, so updated your EDL file that matches this DVD with the proper title if this doesn't work for you i.e. if it just plays a single preview title or what not, and not the main title, you need to change this value. This is useful if you want to just kind of watch the movie to enjoy it, and look for scenes to cut out. You can use the built-in OSD (on-screen-display) to see what time frame the questionable scenes are at (type "o" to toggle it). However, just realize that the OSD is in 30 fps, and our time stamps are all in 29.97 fps, so you'll need to convert it (the convert timestamp button) to be able to use it in a file. EOL @play_smplayer.on_clicked { play_dvd_smplayer_unedited } @play_mplayer_raw = new_jbutton( "Watch full DVD unedited (realtime mplayer)") @play_mplayer_raw.tool_tip = <<-EOL This is also useful for comparing subtitle files to see if they have accurate timings. If you turn on subtitles (use the v button), then compare your srt file at say, the 1 hour mark, or 2 hour mark, with the subtitles that mplayer displays, it *should* match exactly with the output in the command line, like "V: 3600.0" should match your subtitle line "01:00:00,000 --> ..." EOL @play_mplayer_raw.on_clicked { play_dvd_smplayer_unedited true } @display_unique = new_jbutton( "Display information about current DVD (ID, etc.)" ) @display_unique.tool_tip = "This is useful to setup a DVD's 'unique ID' within an EDL for it. \nIf your EDL doesn't have a line like disk_unique_id => \"...\" then you will want to run this to be able to add that line in." @display_unique.on_clicked { drive, volume_name, dvd_id = choose_dvd_drive_or_file true # display it, allow them to copy and paste it out out = nil t = Thread.new {out= `mplayer dvdnav:// -dvd-device #{drive} -identify -frames 0 2>&1| grep LENGTH`} id_string = "\"disk_unique_id\" => \"#{dvd_id}\", # #{volume_name}" show_copy_pastable_string "#{drive} #{volume_name} for your copying+pasting pleasure (highlight, then ctrl+c to copy)\n This is USED eventually to identify a disk to match it to its EDL, later.", id_string t.join File.write EdlTempFile, id_string + "\n" + out open_file_to_edit_it EdlTempFile } @convert_seconds_to_ts = new_jbutton( "Convert 3600 <-> 1:00:00 style timestamps" ) @convert_seconds_to_ts.on_clicked { input = get_user_input("Enter \"from\" timestamps, like 3600 or 1:40:00:", "1:00:00.1 or 3600.1") if input =~ /:/ output = EdlParser.translate_string_to_seconds input else output = EdlParser.translate_time_to_human_readable input.to_f, true end show_copy_pastable_string("Converted:", output) } @convert_timestamp = new_jbutton( "Convert timestamp from DVD player time (30 fps) to EDL player time (29.97 fps)" ) @convert_timestamp.tool_tip=<<-EOL Our EDL's assume 29.97 fps (which is what a DVD is). Unfortunately most hardware/commercial DVD players assume that the DVD is 30 fps, which means that if you watch a movie with them, then use those timestamps for your EDL file, you will be off, at the end of a 2 hour film, by 8 seconds. So all your edits will be wrong. How to fix: convert your times from "DVD player" time to "EDL accurate" time by using this button. This is necessary for hardware DVD player timestamps, PowerDVD, Windows Media Player (playing a DVD), but not necessary for smplayer timestamps (or mplayer's "V: 3600" in the command line), which are already 29.97. smplayer's on-screen-display (the 'o' key) is accurate (and doesn't suffer from dvd_mplayer_splits) but is 30 fps, so timestamps would need to be converted. Dont use VLC for DVD timestamps at all--it can get up to 30s off! VLC playing back a file is usually pretty accurate to 29.97. In general, GUI's like VLC or smplayer are always a tidge off (maybe 0.3s) from the right timestamp, so take that into consideration. Mplayers "V: 3600" is usually right on, however. EOL @convert_timestamp.on_clicked { thirty_fps = get_user_input("Enter your DVD (30 fps) timestamp, I'll convert it to 29.97 (usable in EDL's):", "1:00:00.1") thirty_fps_in_seconds = EdlParser.translate_string_to_seconds thirty_fps twenty_nine_seven_fps = ConvertThirtyFps.from_thirty(thirty_fps_in_seconds) human_twenty_nine_seven = EdlParser.translate_time_to_human_readable twenty_nine_seven_fps, true show_copy_pastable_string("Sensible cinema usable value (29.97 fps) for #{thirty_fps} would be: ", human_twenty_nine_seven) } @parse_srt = new_jbutton("Scan a subtitle file (.srt) to detect profanity times automatically" ) @parse_srt.tool_tip = <<-EOL You can download a .srt file and use it to automatically search for profanities. Basically download it from opensubtitles.org (possibly from other sites, too), (enter dvd name in the search box, click a result, click one from the list with an English flag, then click 'Download(zip)') Once you download the zip, unzip it, and then compare the timestamps in it with those on the DVD (see the button "Watch DVD unedited (realtime mplayer)") EOL @parse_srt.on_clicked do filename = new_existing_file_selector_and_select_file("Pick srt file:") parsed = SubtitleProfanityFinder.edl_output filename, {}, 0, 0 # 0.35, 0.25 # PUT MOVIE SPECIFIC OFFSET SUBTITLE TIMINGS HERE LODO prompt :P File.write(EdlTempFile, "# add these into your mute section if they are offensive\n" + parsed) open_file_to_edit_it filename, true open_file_to_edit_it EdlTempFile end add_text_line 'Create Options that first create/use a local intermediary file:' @preview_section = new_jbutton( "Preview a certain time frame from fulli file (edited)" ) @preview_section.tool_tip = <<-EOL This allows you to preview an edit easily. It is the equivalent of saying \"watch this file edited from exactly minute x second y to minute z second q" Typically if you want to test an edit, you can start a few seconds before, and end a few seconds after it, to test it precisely. EOL @preview_section.on_clicked { do_copy_dvd_to_hard_drive_via_file true } @preview_section_unedited = new_jbutton("Preview a certain time frame from fulli file (unedited)" ) @preview_section.tool_tip = "Allows you to view a certain time frame unedited (ex: 10:00 to 10:05), so you can narrow down to pinpoint where questionable scenes are, etc. This is the only way to view a specific scene if there are not cuts within that scene yet." @preview_section_unedited.on_clicked { do_copy_dvd_to_hard_drive_via_file true, false, true } @rerun_preview = new_jbutton( "Re-run most recently watched preview time frame from fulli file" ) @rerun_preview.tool_tip = "This will re-run the preview that you most recently performed. Great for checking to see if you last edits were successful or not." @rerun_preview.on_clicked { repeat_last_copy_dvd_to_hard_drive } # Maybe this button should go too... @fast_preview = new_jbutton("fast preview all from fulli file (smplayer EDL)") @fast_preview.tool_tip = <<-EOL Plays smplayer on a file with an EDL. This gives you a rough estimate to see if your edits are accurate, and is really fast to seek, etc. This is useful because you can't use mplayer on a DVD for accurate timestamps if it has any timestamp splits in it [because some DVD's are buggy] EOL @fast_preview.on_clicked { success, wrote_to_here_fulli = do_copy_dvd_to_hard_drive_via_file false, true sleep 0.5 # lodo take out ??? background_thread.join if background_thread # let it write out the original fulli, if necessary [?] nice_file = wrote_to_here_fulli run_smplayer_blocking nice_file, "-edl \"#{EdlTempFile}\"", false } @reload = new_jbutton("reload bin/sensible-cinema code") do load $0 end end # advanced buttons def setup_normal_buttons @mplayer_edl = new_jbutton( "Watch DVD edited (realtime)") @mplayer_edl.tool_tip = "This will watch your DVD in realtime from your computer while skipping/muting questionable scenes." @mplayer_edl.on_clicked { play_mplayer_edl } @create = new_jbutton( "Create edited copy of video on Your Hard Drive" ) @create.tool_tip = <<-EOL This grabs from your DVD and creates a new file on your hard disk like dvd_name_edited.mpg for you to watch later. The file you create will contain the whole movie edited. It takes quite awhile maybe 2 hours. Sometimes the progress bar will look paused--it will continue eventually. EOL @create.on_clicked { do_copy_dvd_to_hard_drive_via_file false } # @watch_created_file = new_jbutton( "Watch the edited version of DVD file", false).on_clicked { # play_mplayer_edl # LODO # } @watch_file_edl = new_jbutton( "Watch movie file edited (realtime)" ) do choose_file_and_edl false end @open_help_file = new_jbutton("View Sensible Cinema Documentation") do show_in_explorer __DIR__ + "/../documentation" end end def choose_file_and_edl just_create_dot_edl_file_instead_of_play filename_mpg = new_existing_file_selector_and_select_file( "pick moviefile (like moviename.mpg)") edl_filename = new_existing_file_selector_and_select_file( "Pick an EDL file to use with it", EDL_DIR) assert_ownership_dialog if just_create_dot_edl_file_instead_of_play descriptors = EdlParser.parse_file edl_filename # LODO these timings...DRY up...plus is XBMC the same? what about on a slower computer? # NB these are just for the SxS EDL! #add_secs_end = 0 #add_secs_beginning = 0.5 # LODO are these accurate for file based, as well? slow cpu versus fast? # TODO do I want these added at file create time, not use time! edl_contents = MplayerEdl.convert_to_edl descriptors, add_secs_end = MplayerEndBuffer, MplayerBeginingBuffer, splits = [] output_file = filename_mpg.gsub(/\.[^\.]+$/, '') + '.edl' # sanitize... File.write(output_file, edl_contents) raise unless File.exist?(output_file) show_blocking_message_dialog("created #{output_file}") else play_mplayer_edl [filename_mpg, edl_filename] end end def we_are_in_upconvert_mode ARGV.index("--upconvert-mode") end def initialize super "Sensible-Cinema #{VERSION} (GPL)" if !(LocalStorage['main_license_accepted'] == VERSION) force_accept_license_first end setDefaultCloseOperation JFrame::EXIT_ON_CLOSE panel = JPanel.new @panel = panel @buttons = [] panel.set_layout nil add panel # why can't I just slap these down? panel? huh? @starting_button_y = 40 @button_width = 400 add_text_line "Welcome to Sensible Cinema!" @starting_button_y += 10 # kinder ugly... add_text_line " Rest mouse over buttons for \"help\" type descriptions (tooltips)." if LocalStorage[UpConvertEnglish] add_text_line " Upconvert settings are at:" + LocalStorage[UpConvertEnglish] else add_text_line "" end add_text_line "" if we_are_in_upconvert_mode setup_upconvert_buttons else setup_normal_buttons if we_are_in_create_mode setup_advanced_buttons add_text_line 'Contact:' end @upload = new_jbutton("Submit feedback/Upload/Request Help") # keep this one last! :) @upload.tool_tip = "We welcome all feedback!\nQuestion, comments, request help.\nAlso if you create a new EDL, please submit it back to us so that others can benefit from it later!" @upload.on_clicked { system_non_blocking("start mailto:sensible-cinema@googlegroups.com") system_non_blocking("start http://groups.google.com/group/sensible-cinema") } @progress_bar = JProgressBar.new(0, 100) @progress_bar.set_bounds(44,@starting_button_y,@button_width,23) @progress_bar.visible = false panel.add @progress_bar increment_button_location end @exit = new_jbutton("Exit", "Exits the application and kills any background processes that are running at all--don't exit unless you are done processing all the way!") @exit.on_clicked { Thread.new { self.close } # don't waste the time to close it :P puts 'Thank you for using Sensible Cinema. Come again!' java.lang.System.exit 0 } increment_button_location increment_button_location setIconImage(ImageIcon.new(__DIR__ + "/../vendor/monkey.png").getImage()) check_for_various_dependencies end def run_smplayer_non_blocking *args @background_thread = Thread.new { run_smplayer_blocking *args } end def run_smplayer_blocking play_this, extra_options, force_use_mplayer extra_options += " -mouse-movements #{get_upconvert_secondary_settings} " # just in case smplayer also needs -mouse-movements... :) extra_options += " -lavdopts threads=#{OS.cpu_count} " # just in case this helps [supposed to with H.264] :) if OS.mac? || force_use_mplayer @_run_smplayer_instructions ||= show_mplayer_instructions conf_file = File.expand_path './mplayer_input_conf' File.write conf_file, "MOUSE_BTN0_DBL vo_fullscreen\nMOUSE_BTN2 vo_fullscreen\nKP_ENTER dvdnav select\n" # dvdnav doesn't work here... if OS.windows? # direct3d for windows 7 old nvidia cards' sake [yipes] extra_options += " -vo direct3d " # more mouse, less speed [?] less mouse ?? extra_options += " -slang en " extra_options += " -font #{ENV['SystemRoot']}\\fonts\\ARIAL.TTF " # would need EXTRA ESCAPING FOR SMPLAYER USE conf_file = conf_file[2..-1] # strip off drive letter, which it doesn't like no sir end extra_options += " -fs " # full screen c = "mplayer #{extra_options} #{"-vf " + get_upconvert_vf_settings if get_upconvert_vf_settings} -input conf=\"#{conf_file}\" -mouse-movements \"#{play_this}\" " else if OS.windows? extra_options += " -vo direct3d " # more light nvidia...should be ok... end config_path = set_smplayer_opts extra_options c = "smplayer_portable \"#{play_this}\" -fullscreen -config-path \"#{File.dirname config_path}\" -close-at-end " end puts c system_blocking c end def set_smplayer_opts to_this, show_subs = false p 'set smplayer opts to this:' + to_this smplayer_prefs_file = File.expand_path("~/.smplayer/smplayer.ini") old_prefs = File.read(smplayer_prefs_file) rescue '' old_prefs = "[advanced]\nmplayer_additional_options=\nmplayer_additional_video_filters=\n[subtitles]\nautoload_sub=false\n[performance]\npriority=3" unless old_prefs.contain? 'mplayer_additional_options=' new_prefs = old_prefs.gsub(/mplayer_additional_options=.*/, "mplayer_additional_options=#{to_this}") new_prefs.gsub!(/autoload_sub=.*$/, "autoload_sub=#{show_subs.to_s}") upconvert_settings = get_upconvert_vf_settings if upconvert_settings.present? p 'put upconvert options in as vf', upconvert_settings else p 'not using upconvert' end new_prefs.gsub!(/mplayer_additional_video_filters=.*$/, "mplayer_additional_video_filters=\"#{upconvert_settings}\"") new_prefs.gsub!(/priority=.*$/, "priority=3") # normal priority...scary otherwise! lodo tell smplayer... FileUtils.mkdir_p File.dirname(smplayer_prefs_file) # case it doesn't exist' File.write(smplayer_prefs_file, new_prefs) smplayer_prefs_file end def create_brand_new_edl drive, volume, dvd_id = choose_dvd_drive_or_file true english_name = get_user_input("Enter a human readable DVD description for #{volume}", volume.gsub('_', ' ').downcase) input = <<-EOL # comments can go after a # on any line, for example this one. "name" => "#{english_name}", "mutes" => [ # an example line, uncomment the leading "#" to make it active # "0:00:01.0", "0:00:02.0", "profanity", "da..", ], "blank_outs" => [ # an example line, uncomment the leading "#" to make it active # "00:03:00.0" , "00:04:00.0", "violence", "of some sort", ], "volume_name" => "#{volume}", "disk_unique_id" => "#{dvd_id}", "dvd_title_track" => "1", # the show info button will tell you title length (typically longest is main feature) # "not edited out stuff" => "some violence", # "closing thoughts" => "still...", # "mplayer_dvd_splits" => ["59:59", "1:04:59"], # or [] if there are none. Additive currently. 12345.6 ok. In mplayer, the DVD timestamp "resets" to zero for some reason, so you need to specify when if you want to use mplayer DVD realtime playback, or use mencoder -edl to split your file. See http://goo.gl/yMfqX EOL # frame0 is the name of our window LOL tough to avoid that collision...hmm... filename = EDL_DIR + "\\edls_being_edited\\" + english_name.gsub(' ', '_') + '.txt' filename.downcase! File.write(filename, input) unless File.exist?(filename) # lodo let them choose name (?) open_file_to_edit_it filename end def system_blocking command, low_prio = false return true if command =~ /^@rem/ # jruby+MRI bug, I think... if low_prio out = IO.popen(command) # + " 2>&1" low_prio = 64 # from msdn if command =~ /(ffmpeg|mencoder)/ # XXXX not sure if there's a better way...because some *are* complex and have ampersands... # unfortunately have to check for nil because it could exit too early [?] exe_name = $1 + '.exe' begin p = proc{ ole = WMI::Win32_Process.find(:first, :conditions => {'Name' => exe_name}); sleep 1 unless ole; ole } piddy = p.call || p.call || p.call # we actually do need this to loop...guess we're too quick # but the first time through this still inexplicably fails... LODO if piddy # piddy.SetPriority low_prio # this call can seg fault at times...yikes...JRUBY-5422 pid = piddy.ProcessId # this doesn't seg fault, tho system_original("vendor\\setpriority -lowest #{pid}") # use the PID on the command line else # XXXX first one always fails [?] huh? p 'unable to find to set priority ' + exe_name end rescue Exception => e p 'warning, got exception trying to set priority [jruby prob? ...]', e end end print out.read # let it finish out.close $?.exitstatus == 0 # 0 means success else raise command + ' failed' unless system_original command end end def system_non_blocking command @background_thread = Thread.new { system_original command } end if respond_to?(:system) # make them choose which system call to use explicitly undef system end def download full_url, to_here require 'open-uri' writeOut = open(to_here, "wb") writeOut.write(open(full_url).read) writeOut.close end def play_dvd_smplayer_unedited use_mplayer_instead = false, show_instructions = true drive, dvd_volume_name, dvd_id, edl_path_maybe_nil, descriptors_maybe_nil = choose_dvd_or_file_and_edl_for_it false if descriptors_maybe_nil title_track_maybe_nil = get_title_track(descriptors_maybe_nil, false) end file_to_play = "dvdnav://#{title_track_maybe_nil}" options = " -osd-fractions 2 -nocache -dvd-device #{drive} " if show_instructions # want this even with smplayer sometimes I guess... @play_smplayer_warn ||= show_mplayer_instructions end run_smplayer_non_blocking file_to_play, options, use_mplayer_instead end EdlTempFile = Dir.tmpdir + '/mplayer.temp.edl' def show_mplayer_instructions show_non_blocking_message_dialog <<-EOL About to run mplayer. To control it, use spacebar : pause, double clicky/right click : toggle full screen, arrow keys (left, right, up down) to seek/scan / and * : inc/dec volume. '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). 'v' key: turn off subtitles. '.' key: step one frame. EOL end MplayerBeginingBuffer = 1.0 MplayerEndBuffer = 0.0 def play_mplayer_edl file_with_edl_path = nil extra_mplayer_commands = [] if file_with_edl_path drive_or_file, edl_path = file_with_edl_path dvd_id = NonDvd # fake it out...LODO else drive_or_file, dvd_volume_name, dvd_id, edl_path, descriptors = choose_dvd_or_file_and_edl_for_it end if dvd_id == NonDvd play_this = drive_or_file # play straight file... else using_dvd = true title_track = get_title_track(descriptors) extra_mplayer_commands << " -nocache -dvd-device #{drive_or_file} " play_this = "dvdnav://#{title_track}" end if edl_path descriptors = EdlParser.parse_file edl_path else descriptors = {} end if using_dvd p descriptors splits = descriptors['mplayer_dvd_splits'] if splits == nil 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 ).") splits = [] end splits.map!{|s| EdlParser.translate_string_to_seconds(s) } else splits = [] end edl_contents = nil begin edl_contents = MplayerEdl.convert_to_edl descriptors, add_secs_end = MplayerEndBuffer, MplayerBeginingBuffer, splits #, 600 TODOS # add a sec to mutes to accomodate for mplayer's oddness... rescue SyntaxError => e # overlaps et al show_blocking_message_dialog 'fail:' + e.to_s raise e end File.write(EdlTempFile, edl_contents) # -framedrop is for slow CPU's # same with -autosync to try and help it stay in sync... -mc 0.03 is to A/V correct 1s audio per 2s video # -hardframedrop might help but hurts just too much extra_mplayer_commands << "-framedrop " # ?? extra_mplayer_commands << " -mc 0.016 -autosync 15" # prefer english, attempt to disable subtitles, set edl... extra_mplayer_commands << "-alang en -nosub -noautosub -forcedsubsonly -sid 1000 -edl #{File.expand_path EdlTempFile}" # some maybe no longer necessary, but avoid subtitles by default... run_smplayer_non_blocking play_this, extra_mplayer_commands.join(' '), false end def assert_ownership_dialog message = "Do you certify you own the DVD this came of and have it in your possession?" title = "Verify ownership" returned = JOptionPane.showConfirmDialog self, message, title, JOptionPane::YES_NO_CANCEL_OPTION assert_confirmed_dialog returned, nil end def require_blocking_license_accept_dialog program, license_name, license_url_should_also_be_embedded_by_you_in_message, title = 'Confirm Acceptance of License Agreement', message = nil puts 'Please confirm license agreement in open window.' old = ['no', 'yes', 'ok'].map{|name| 'OptionPane.' + name + 'ButtonText'}.map{|name| [name, UIManager.get(name)]} UIManager.put("OptionPane.yesButtonText", 'Accept') UIManager.put("OptionPane.noButtonText", "View #{license_name}") # cancel button stays the same... message ||= "Sensible Cinema requires a separately installed program (#{program}). You can install this program separately to the vendor/cache directory, or Sensible Cinema can download it for you. By clicking accept, below, you are confirming that you have read and agree to be bound by the terms of its license (the #{license_name}), located at #{license_url_should_also_be_embedded_by_you_in_message}. Click 'View License' to view it. If you do not agree to these terms, click 'Cancel'. You also agree that this is a separate program, with its own distribution, license, ownership and copyright. You agree that you are responsible for the download and use of this program, within sensible cinema or otherwise." returned = JOptionPane.showConfirmDialog self, message, title, JOptionPane::YES_NO_CANCEL_OPTION assert_confirmed_dialog returned, license_url_should_also_be_embedded_by_you_in_message p 'confirmation of sensible cinema related license noted of: ' + license_name throw unless returned == 0 old.each{|name, old_setting| UIManager.put(name, old_setting)} end def assert_confirmed_dialog returned, license_url_should_also_be_embedded_by_you_in_message # 1 is view button was clicked # 0 is accept # 2 is cancel if returned == 1 if license_url_should_also_be_embedded_by_you_in_message system_non_blocking("start #{license_url_should_also_be_embedded_by_you_in_message}") puts "Please restart after reading license agreement, to be able to then accept it." end System.exit 1 end if returned == 2 p 'license not accepted...exiting' System.exit 1 end if returned == -1 p 'license exited early...exiting' System.exit 1 end end def print *args 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) end def download_7zip Dir.mkdir('./vendor/cache') unless File.directory? 'vendor/cache' # development may not have it created yet... [?] unless File.exist? 'vendor/cache/7za.exe' Dir.chdir('vendor/cache') do print 'downloading unzipper (400K) ...' download("http://downloads.sourceforge.net/project/sevenzip/7-Zip/9.20/7za920.zip", "7za920.zip") system_blocking("../unzip.exe -o 7za920.zip") # -o means "overwrite" without prompting end end end def download_zip_file_and_extract english_name, url, to_this download_7zip Dir.chdir('vendor/cache') do file_name = url.split('/')[-1] print "downloading #{english_name} ..." download(url, file_name) system_blocking("7za e #{file_name} -y -o#{to_this}") puts 'done ' + english_name # creates vendor/cache/mencoder/mencoder.exe... end end def check_for_exe windows_loc, unix_name # in windows, that exe *at that location* must exist... if OS.windows? File.exist?(windows_loc) else require 'lib/check_installed_mac.rb' if !CheckInstalledMac.check_for_installed(unix_name) exit 1 # it'll have already displayed a message... else true end end end def check_for_various_dependencies if we_are_in_create_mode ffmpeg_exe_loc = File.expand_path('vendor/cache/ffmpeg/ffmpeg.exe') if !check_for_exe(ffmpeg_exe_loc, 'ffmpeg') 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." download_zip_file_and_extract "ffmpeg (5MB)", "http://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-git-1929807-win32-shared.7z", "ffmpeg" end if !check_for_exe('vendor/cache/mencoder/mencoder.exe', 'mencoder') 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." download_zip_file_and_extract "Mplayer/mencoder (6MB)", "http://downloads.sourceforge.net/project/mplayer-win32/MPlayer%20and%20MEncoder/revision%2033574/MPlayer-rtm-svn-33574.7z", "mencoder" end end # runtime dependencies, at least today... if OS.mac? check_for_exe("mplayer", "mplayer") # mencoder and mplayer are separate for mac... [this checks for mac's mplayerx, too] else path = RubyWhich.new.which('smplayer_portable') if(path.length == 0) # this one has its own installer... show_blocking_message_dialog("It appears that you need to install a dependency: MPlayer for Windows (MPUI).\n Click ok to be directed to its download website, where you can download and install it (recommend: MPUI Full-package), then restart sensible cinema.", "Lacking dependency", JOptionPane::ERROR_MESSAGE) open_url_to_view_it_non_blocking "http://code.google.com/p/mulder/downloads/detail?name=MPUI.2011-06-09.Full-Package.exe" System.exit(1) end end end def open_url_to_view_it_non_blocking url if OS.windows? system_non_blocking(c = "start #{url.gsub('&', '^&')}") # LODO would launchy help/work here with the full url? p c else system_non_blocking "#{OS.open_file_command} \"#{url}\"" # LODO test end end def open_file_to_edit_it filename, start_minimized=false if OS.windows? if start_minimized system_non_blocking "start /min notepad \"#{filename}\"" else system_non_blocking "notepad \"#{filename}\"" end else system_non_blocking "#{OS.open_file_command} \"#{filename}\"" end end def single_edit_list_matches_dvd dvd_id matching = Dir[EDL_DIR + '/**/*.txt'].select{|file| begin parse_edl(file)["disk_unique_id"] == dvd_id rescue SyntaxError => e # ignore poorly formed edit lists for the auto choose phase... p 'warning, unable to parse a file:' + file + " " + e.to_s false end } if matching.length == 1 file = matching[0] p "selecting the one only matching EDL #{file} for this DVD ID: #{dvd_id}" file else nil end end EDL_DIR = File.expand_path(__DIR__ + "/../zamples/edit_decision_lists/dvds").to_filename def repeat_last_copy_dvd_to_hard_drive generate_and_run_bat_file *LocalStorage['last_params'] end def new_nonexisting_filechooser title = nil, default_dir = nil out = JFileChooser.new out.set_title title if default_dir out.set_current_directory JFile.new(default_dir) end out end def show_blocking_message_dialog(message, title = message.split("\n")[0], style= JOptionPane::INFORMATION_MESSAGE) JOptionPane.showMessageDialog(nil, message, title, style) true end # call dispose on this to close it if it hasn't been canceled yet... def show_non_blocking_message_dialog message, close_button_text = 'Close' # lodo NonBlockingDialog it can get to the top instead of being so buried... NonBlockingDialog.new(message, close_button_text) end include_class javax.swing.UIManager def get_user_input(message, default = '', cancel_ok = false) received = JOptionPane.showInputDialog(message, default) raise 'user canceled' unless received unless cancel_ok received end def show_copy_pastable_string(message, value) RubyClip.set_clipboard value get_user_input message + " (has been copied to clipboard)", value, true end def parse_edl path EdlParser.parse_file path end def get_freespace path JFile.new(File.dirname(path)).get_usable_space end def new_existing_file_selector_and_select_file title, dir=nil out = FileDialog.new(self, title, FileDialog::LOAD) out.set_title title dir ||= LocalStorage[caller.inspect] out.set_directory dir.to_filename if dir got = out.go raise 'cancelled choosing existing file method' unless got # I think we always want to raise... LocalStorage[caller.inspect] = File.dirname(got) got end def choose_dvd_or_file_and_edl_for_it force_choose_edl_file_if_no_easy_match = true drive_or_file, dvd_volume_name, dvd_id = choose_dvd_drive_or_file false unless @_edit_list_path # cache file selection... edit_list_path = single_edit_list_matches_dvd(dvd_id) if !edit_list_path && force_choose_edl_file_if_no_easy_match 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", EDL_DIR) raise 'cancelled choosing an EDL' unless edit_list_path end @_edit_list_path = edit_list_path end if @_edit_list_path # reload it every time just in case it has changed on disk descriptors = nil while(!descriptors) begin descriptors = parse_edl @_edit_list_path rescue SyntaxError => e puts e show_blocking_message_dialog("this file has an error--please fix then hit ok: \n" + @_edit_list_path + "\n " + e) end end end [drive_or_file, dvd_volume_name, dvd_id, @_edit_list_path, descriptors] end def get_title_track descriptors, use_default_of_one = true given = descriptors["dvd_title_track"] given ||= "1" if use_default_of_one given end def get_import_from_filename dvd_title, dvd_title_track @_get_import_from_filename ||= begin new_existing_file_selector_and_select_file "Please choose the file that is your ripped equivalent of #{dvd_title} (title track #{dvd_title_track}) (.mpg or .ts--see file documentation/how_to_get_files_from_dvd.txt)" end end def get_save_to_filename dvd_title @_get_save_to_filename ||= begin fc = new_nonexisting_filechooser "Pick where to save #{dvd_title} edited version to" save_to_file_name = dvd_title + ' edited version' save_to_file_name = save_to_file_name.gsub(' ', '_').gsub( /\W/, '') + ".avi" # no punctuation or spaces for now, to not complicate... fc.set_file(get_drive_with_most_space_with_slash + save_to_file_name) save_to = fc.go raise 'no spaces allowed yet' if save_to =~ / / begin a = File.open(File.dirname(save_to) + "/test_file_to_see_if_we_have_permission_to_write_to_this_folder", "w") a.close File.delete a.path rescue Errno::EACCES => e show_blocking_message_dialog "unable to write to that directory, please pick again: " + e.to_s raise 'pick again!' end freespace = get_freespace(save_to) if freespace < 8_000_000_000 show_blocking_message_dialog("Warning: there may not be enough space on the disk for #{save_to} (depending on DVD size, you may need around 10G free--you have #{freespace/1_000_000_000}GB free). Click OK to continue.") end save_to.gsub(/\.avi$/, '') end end def do_copy_dvd_to_hard_drive_via_file should_prompt_for_start_and_end_times, exit_early_if_fulli_exists = false, watch_unedited = false drive_or_file, dvd_volume_name, dvd_id, edit_list_path, descriptors = choose_dvd_or_file_and_edl_for_it descriptors = parse_edl(edit_list_path) if watch_unedited descriptors['mutes'] = descriptors['blank_outs'] = [] end # LODO allow for spaces in the save_to filename if should_prompt_for_start_and_end_times # only show this message once :) @show_block ||= show_blocking_message_dialog(<<-EOL, "Preview") Ok, let's preview just a portion of it. Note that you'll want to preview a section that wholly includes a deleted section in it. For example, if it mutes from second 1 to second 10, you'll want to play from 00:00 to 00:12 or what not. Also note that the first time you preview a section of a video, it will take a long time (like an hour) as it sets up the entire video for processing. Subsequent previews will be faster, though, as long as you use the same filename, as it won't have to re-set it up for processing. Also note that if you change your edit list, you'll need to close, and restart the video to be able to see it with your new settings. EOL old_start = LocalStorage['start_time'] start_time = get_user_input("At what point in the video would you like to start your preview? (like 01:00 for starting at 1 minute)", LocalStorage['start_time']) default_end = LocalStorage['end_time'] if start_time and start_time != old_start default_end = EdlParser.translate_string_to_seconds(start_time) + 10 default_end = EdlParser.translate_time_to_human_readable(default_end) end end_time = get_user_input("At what point in the video would you like to finish your preview? (like 02:00 for ending at the 2 minute mark)", default_end) unless start_time and end_time JOptionPane.showMessageDialog(nil, " Please choose start and end", "Failed", JOptionPane::ERROR_MESSAGE) return end LocalStorage['start_time'] = start_time LocalStorage['end_time'] = end_time end dvd_friendly_name = descriptors['name'] raise 'no dvd name in EDL?' unless dvd_friendly_name dvd_title_track = get_title_track(descriptors) if dvd_id == NonDvd file_from = drive_or_file else file_from = get_import_from_filename dvd_friendly_name, dvd_title_track # we don't even care about the drive letter anymore... end show_blocking_message_dialog("warning: file is not a .mpg or .ts file--it may not work properly all the way--if it's mkv and fails consider first converting to ts by using tsmuxer.") unless file_from =~ /\.(ts|mpg|mpeg)$/i save_to_edited = get_save_to_filename dvd_friendly_name fulli = MencoderWrapper.calculate_fulli_filename save_to_edited if exit_early_if_fulli_exists if fulli_dot_done_file_exists? save_to_edited return [true, fulli] end # make it create a dummy response file for us :) start_time = "00:00" end_time = "00:01" end should_run_mplayer = should_prompt_for_start_and_end_times || exit_early_if_fulli_exists require_deletion_entry = true unless watch_unedited generate_and_run_bat_file save_to_edited, edit_list_path, descriptors, file_from, dvd_friendly_name, start_time, end_time, dvd_title_track, should_run_mplayer, require_deletion_entry [false, fulli] # false means it's running in a background thread :P end def get_drive_with_most_space_with_slash DriveInfo.get_drive_with_most_space_with_slash end def fulli_dot_done_file_exists? save_to_edited fulli = MencoderWrapper.calculate_fulli_filename save_to_edited File.exist?(fulli + ".done") # stinky! end # to make it stubbable :) def get_mencoder_commands descriptors, file_from, save_to, start_time, end_time, dvd_title_track, require_deletion_entry delete_partials = true unless start_time # in case anybody wants to look really really close [?] MencoderWrapper.get_bat_commands descriptors, file_from, save_to, start_time, end_time, dvd_title_track, delete_partials, require_deletion_entry end def generate_and_run_bat_file save_to, edit_list_path, descriptors, file_from, dvd_title, start_time, end_time, dvd_title_track, run_mplayer, require_deletion_entry LocalStorage['last_params'] = [save_to, edit_list_path, descriptors, file_from, dvd_title, start_time, end_time, dvd_title_track, run_mplayer, require_deletion_entry] begin commands = get_mencoder_commands descriptors, file_from, save_to, start_time, end_time, dvd_title_track, require_deletion_entry rescue MencoderWrapper::TimingError => e show_blocking_message_dialog("Appears you chose a time frame with no deletion segment in it--please try again:" + e) return rescue Errno::EACCES => e show_blocking_message_dialog("Appears a file on the system is locked: perhaps you need to close down some instance of mplayer?" + e) return end temp_dir = Dir.tmpdir temp_file = temp_dir + '/vlc.temp.bat' File.write(temp_file, commands) popup_message = <<-EOL Applying #{File.basename edit_list_path} to #{file_from} (#{dvd_title}). Copying to #{save_to}. EOL if !fulli_dot_done_file_exists? save_to popup_message += "This could take quite awhile (several hours), and will prompt you with a chime noise when it is done.\n You can close this window and minimize sensible cinema and continue using your computer while it runs in the background.\n" end if !start_time # assume a full run.. popup_message += <<-EOL NB that the created file will be playable only with VLC (possibly also with smplayer), but probably not with windows media player. EOL end popup = show_non_blocking_message_dialog(popup_message, "OK") # allow our popups to still be serviced while it is running @background_thread = Thread.new { run_batch_file_commands commands, save_to, file_from, run_mplayer popup.dispose } # LODO warn if they will overwrite a file in the end... end attr_accessor :background_thread, :buttons def run_batch_file_commands batch_commands, save_to, file_from, run_mplayer_after_done @buttons.each{|b| b.set_enabled false} success = true lines = batch_commands.lines.to_a total_size = lines.length.to_f @progress_bar.visible=true @progress_bar.set_value(10) # start at 10% always, so they can see something. lines.each_with_index{|line, idx| if success puts "running #{line}" success = system_blocking(line, true) if !success puts "\n", 'line failed: ' + line + "\n" + ' see troubleshooting section in README.txt file! ignoring further processing commands...' end end @progress_bar.set_value(10 + idx/total_size*90) } @progress_bar.visible=false @buttons.each{|b| b.set_enabled true} if success saved_to = save_to + '.avi' if run_mplayer_after_done run_smplayer_non_blocking saved_to, '', false else if File.exist?(saved_to) && (File.size(saved_to).to_f/File.size(file_from) < 0.5) # less than 50% size is suspicious...indeed...check if exists for unit tests. show_blocking_message_dialog("Warning: file size differs by more than 50%--it's possible that transcoding failed somehow") end show_in_explorer saved_to PlayAudio.play(File.expand_path(File.dirname(__FILE__)) + "/../vendor/music.wav") msg = "Done--you may now watch file\n #{saved_to}\n in VLC player (or possibly smplayer)" puts msg show_blocking_message_dialog msg show_in_explorer saved_to # and again :) end else show_blocking_message_dialog("Failed--please examine console output and report back!\nAlso consult the documentation/troubleshooting file.", "Failed", JOptionPane::ERROR_MESSAGE) end end def show_in_explorer filename begin system_blocking "explorer /e,/select,\"#{File.expand_path(filename).to_filename}\"" # command returns immediately... rescue => why_does_this_happen_ignore_this_exception_it_probably_actually_succeeded end end def get_disk_chooser_window names GetDisk.new(self, names) end NonDvd = 'non dvd has no dvdid' # we need it for convenience, say you want to go through your indexed vids and convert them all? # returns e:\, volume_name, dvd_id # or full_path.mkv, filename, '' def choose_dvd_drive_or_file force_choose_only_dvd_drive opticals = DriveInfo.get_dvd_drives_as_openstruct if @saved_opticals == opticals && @_choose_dvd_drive_or_file # memoize...if disks haven't changed :) return @_choose_dvd_drive_or_file else @saved_opticals = opticals # save currently mounted disk list, so we know if we should re-select later... # is this ok for os x? end has_at_least_one_dvd_inserted = opticals.find{|d| d.VolumeName } if !has_at_least_one_dvd_inserted && force_choose_only_dvd_drive show_blocking_message_dialog 'insert a dvd first' raise 'no dvd found' end names = opticals.map{|d| d.Name + "\\" + " (" + (d.VolumeName || 'Insert DVD to use') + ")"} if !force_choose_only_dvd_drive && !has_at_least_one_dvd_inserted names += ['No DVD mounted so choose Local File (or insert DVD, re-try)'] # LODO cannot read it... used_local_file_option = true end count = 0 opticals.each{|d| count += 1 if d.VolumeName} if count == 1 && !used_local_file_option # just choose it if there's only one disk available.. p 'selecting only disk present in the various DVD drives' selected_idx = opticals.index{|d| d.VolumeName} unless selected_idx show_blocking_message_dialog "Please insert a disk first" raise 'inset disk' end else dialog = get_disk_chooser_window names dialog.setSize 200, 125 dialog.show selected_idx = dialog.selected_idx end if selected_idx if used_local_file_option raise unless selected_idx == 0 # it was our only option... filename = new_existing_file_selector_and_select_file("Select yer previously grabbed from DVD file") assert_ownership_dialog return [filename, File.basename(filename), NonDvd] else disk = opticals[selected_idx] out = show_non_blocking_message_dialog "calculating disk's unique id..." # useful, believe it or not dvd_id = DriveInfo.md5sum_disk(disk.MountPoint) out.dispose @_choose_dvd_drive_or_file = [disk.MountPoint, opticals[selected_idx].VolumeName, dvd_id] return @_choose_dvd_drive_or_file end else raise 'did not select a drive...' end end end class GetDisk < JDialog attr_reader :selected_idx def initialize parent, options_array super parent, true box = JComboBox.new box.add_action_listener do |e| idx = box.get_selected_index if idx != 0 # don't count choosing the first as a real entry @selected_idx = box.get_selected_index - 1 dispose end end box.add_item "Click to select DVD drive" # put something in index 0 options_array.each{|drive| box.add_item drive } add box pack # how do you get this arbitrary size? what the... end end end class String def present? length > 0 end end class NilClass def present? false end end class Object def present? true end end class Array def present? length > 0 end end if $0 == __FILE__ if ARGV.index('-h') || ARGV.index('--help') puts 'syntax: [--create-mode]' else a = SensibleSwing::MainWindow.new a.set_visible true puts 'Please use the Sensible Cinema GUI window popup...' end end # icon derived from: http://www.threes.com/index.php?option=com_content&view=article&id=1800:three-wise-monkeys&catid=82:mythology&Itemid=62