sensible-cinema 0.28.1 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. data/{README → README.TXT} +35 -14
  2. data/Rakefile +49 -41
  3. data/TODO +337 -250
  4. data/VERSION +1 -1
  5. data/bin/sensible-cinema +15 -14
  6. data/bin/sensible-cinema-cli +1 -1
  7. data/business_ideas.txt +8 -0
  8. data/change_log_with_feature_list.txt +198 -1
  9. data/documentation/is_it_legal_to_copy_dvds.txt +44 -30
  10. data/goc.bat +1 -1
  11. data/gpl-2.0.txt +339 -0
  12. data/history_and_related_works_list.txt +37 -33
  13. data/{cone.png → inventionzy_files/cone.png} +0 -0
  14. data/legal_draft +23 -0
  15. data/lib/add_any_bundled_gems_to_load_path.rb +4 -3
  16. data/lib/check_installed_mac.rb +10 -10
  17. data/lib/count_down_timer_jruby_swing.rb +4 -4
  18. data/lib/edl_parser.rb +155 -142
  19. data/lib/extract/dumpstream.bat +1 -0
  20. data/lib/extract/get_init.bat +3 -0
  21. data/lib/extract/pause_early.rb +13 -0
  22. data/lib/gui/base.rb +707 -0
  23. data/lib/gui/create-file.rb +244 -0
  24. data/lib/gui/create.rb +565 -0
  25. data/lib/gui/dependencies.rb +177 -0
  26. data/lib/gui/normal.rb +190 -0
  27. data/lib/gui/{sensible-cinema-upconvert.rb → upconvert.rb} +33 -14
  28. data/lib/keyboard_input.rb +0 -2
  29. data/lib/mencoder_wrapper.rb +8 -3
  30. data/lib/movie_hasher.rb +27 -0
  31. data/lib/mplayer_edl.rb +1 -0
  32. data/lib/subtitle_profanity_finder.rb +141 -103
  33. data/lib/vlc_programmer.rb +1 -1
  34. data/lib/zoom_player_max_edl.rb +50 -0
  35. data/{developer_how_to_contribute_to_the_project.txt → notes_for_potential_developers.txt} +18 -11
  36. data/roadmap_possibilities +12 -0
  37. data/spec/arse.srt +6 -1
  38. data/spec/dragon.srt +3 -0
  39. data/spec/edl_parser.spec.rb +57 -52
  40. data/spec/mencoder_wrapper.spec.rb +26 -19
  41. data/spec/mplayer_edl.spec.rb +40 -10
  42. data/spec/notes +3043 -9
  43. data/spec/sensible_cinema_gui.spec.rb +8 -3
  44. data/spec/subtitle_profanity_finder.spec.rb +91 -36
  45. data/spec/zoom_player_max_edl.spec.rb +29 -0
  46. data/template_bats/README_DISTRO.TXT +3 -1
  47. data/template_bats/RUN SENSIBLE CINEMA CLICK HERE WINDOWS.bat +25 -0
  48. data/template_bats/mac/RUN SENSIBLE CINEMA CLICK HERE.command +2 -1
  49. data/template_bats/mac/advanced--create or edit sensible cinema edit list files.command +2 -1
  50. data/template_bats/mac/advanced--run-upconverting-video-player.command +2 -1
  51. data/template_bats/pc/advanced--create or edit sensible cinema edit list files.bat +2 -0
  52. data/template_bats/pc/advanced--run-upconverting-video-player.bat +2 -0
  53. data/todo.inventionzy.txt +69 -2
  54. data/todo.propaganda +34 -10
  55. data/todo.upconvert +8 -1
  56. data/{upconvert_netflix/latest2 → upconvert_from_screen}/go_upscaling.bat +0 -0
  57. data/{also_see_rogerdpack_youtube_account_and_sourceforge_files_movies_account → upconvert_from_screen/go_upscaling_works} +0 -0
  58. data/{upconvert_netflix → upconvert_from_screen/old}/latest2/combine_video.avs +0 -0
  59. data/{upconvert_netflix → upconvert_from_screen/old}/latest2/go_no_upscaling.bat +0 -0
  60. data/upconvert_from_screen/old/latest2/go_upscaling.bat +12 -0
  61. data/{upconvert_netflix → upconvert_from_screen/old}/latest2/push2.GRF +0 -0
  62. data/upconvert_from_screen/old/latest2/push3.grf +0 -0
  63. data/{upconvert_netflix → upconvert_from_screen/old}/latest2/setup_capture_coords.rb +0 -0
  64. data/{upconvert_netflix → upconvert_from_screen/old}/latest2/setup_directshow_filter_params.rb +0 -0
  65. data/{upconvert_netflix → upconvert_from_screen/old}/latest2/upconvert_from_screen_me2.avs +1 -1
  66. data/{upconvert_netflix → upconvert_from_screen}/old/latest_now_possibly_oudated/push2.GRF +0 -0
  67. data/{upconvert_netflix → upconvert_from_screen}/old/latest_now_possibly_oudated/upconvert_from_screen_me2.avs +0 -0
  68. data/{upconvert_netflix → upconvert_from_screen}/old/position_window.png +0 -0
  69. data/{upconvert_netflix → upconvert_from_screen}/old/push_source_desktop.GRF +0 -0
  70. data/{upconvert_netflix → upconvert_from_screen/old}/record_screen/record.bat +0 -0
  71. data/{upconvert_netflix → upconvert_from_screen}/old/upconvert_from_screen.avs +0 -0
  72. data/{upconvert_netflix → upconvert_from_screen}/old/upconvert_from_screen_me.avs +0 -0
  73. data/{upconvert_netflix → upconvert_from_screen}/old/upconvert_from_screen_me2.avs +0 -0
  74. data/{upconvert_netflix → upconvert_from_screen}/old/upconvert_from_screen_me_push_source.avs +0 -0
  75. data/{upconvert_netflix → upconvert_from_screen}/old/uscreen.GRF +0 -0
  76. data/{upconvert_netflix → upconvert_from_screen}/old/uscreen_me.GRF +0 -0
  77. data/upconvert_from_screen/push3.grf +0 -0
  78. data/upconvert_from_screen/upconvert_from_screen_me2.avs +19 -0
  79. data/vendor/mplayer_patches/apply.bat +12 -0
  80. data/vendor/{rdp-win32screenshot-0.0.9/RDP_BRANCH_HAS_MASTER_WITH_CORRECT_VERSION_NUMBER → mplayer_patches/configure_from_betterlogic} +0 -0
  81. data/vendor/mplayer_patches/libdvdnav/2905259c3b45529b3d8dedba572b6e4f67a2d8f4.diff +19 -0
  82. data/vendor/mplayer_patches/libdvdnav/83f1c9256f500285e46f1e44bcc74ffce90159db.diff +16 -0
  83. data/vendor/mplayer_patches/libdvdnav/eb91fb74680d30322461a1b9e425918ad4e2b2df.diff +21 -0
  84. data/vendor/mplayer_patches/libdvdnav/jump_to_time.diff +654 -0
  85. data/vendor/mplayer_patches/libdvdnav/non_strict.diff +13 -0
  86. data/vendor/mplayer_patches/mplayer_edl.diff +354 -0
  87. data/vendor/mplayer_patches/port_dir/PortIndex.quick +2 -0
  88. data/vendor/mplayer_patches/port_dir/how_to +13 -0
  89. data/vendor/mplayer_patches/port_dir/multimedia/mplayer-edl/Portfile +300 -0
  90. data/vendor/mplayer_patches/port_dir/multimedia/mplayer-edl/files/configure.x11.patch +20 -0
  91. data/vendor/mplayer_patches/port_dir/multimedia/mplayer-edl/files/llvm-gcc-workaround.patch +11 -0
  92. data/vendor/mplayer_patches/port_dir/multimedia/rdp-projects/Portfile +43 -0
  93. data/vendor/{rdp-win32screenshot-0.0.9/RDP_VERSION_IS_IN_RDP_BRANCH → mplayer_patches/updated_lib_too} +0 -0
  94. data/vendor/{readme.txt → readme.txt.setpriority} +0 -0
  95. data/www/content_editor.html +28 -28
  96. data/www/index.html +6 -1
  97. data/www/upconverting_movie_player.html +13 -3
  98. data/www/youtube_edl/.htaccess +44 -0
  99. data/{inventionzy_files/control_youtube.html → www/youtube_edl/control_youtube.rhtml} +39 -33
  100. data/www/youtube_edl/dispatch.fcgi +25 -0
  101. data/www/youtube_edl/render_edited.rb +25 -0
  102. data/{inventionzy_files/serve.rb → www/youtube_edl/server_this_dir.rb} +0 -0
  103. data/www/youtube_edl/todo +23 -0
  104. data/www/zoomplayer_max.png +0 -0
  105. data/zamples/edit_decision_lists/dvds/Harry Potter 2 chamber of secrets.txt +10 -5
  106. data/zamples/edit_decision_lists/dvds/aladdin.txt +30 -0
  107. data/zamples/edit_decision_lists/dvds/bambi.txt +1 -1
  108. data/zamples/edit_decision_lists/dvds/big_buck_bunny_dvd.txt +2 -0
  109. data/zamples/edit_decision_lists/dvds/bobs_big_plan.txt +2 -2
  110. data/zamples/edit_decision_lists/dvds/cars_2.txt +26 -0
  111. data/zamples/edit_decision_lists/dvds/cars_disney.txt +1 -1
  112. data/zamples/edit_decision_lists/dvds/condor_man_widescreen.txt +22 -0
  113. data/zamples/edit_decision_lists/dvds/court_jester.txt +10 -13
  114. data/zamples/edit_decision_lists/dvds/edls_being_edited/making_marriage_work.txt +15 -0
  115. data/zamples/edit_decision_lists/dvds/edls_being_edited/national_treasure.txt +23 -0
  116. data/zamples/edit_decision_lists/dvds/edls_being_edited/ratatouille.txt +35 -0
  117. data/zamples/edit_decision_lists/dvds/edls_being_edited/the_explorers.txt +49 -0
  118. data/zamples/edit_decision_lists/dvds/finding_neverland.txt +32 -0
  119. data/zamples/edit_decision_lists/dvds/flight_of_the_navigator.txt +22 -16
  120. data/zamples/edit_decision_lists/dvds/harry_potter_3_prisoner_of_azkaban.txt +21 -4
  121. data/zamples/edit_decision_lists/dvds/hitchhiker's_guide_to_the_galaxy.txt +1 -1
  122. data/zamples/edit_decision_lists/dvds/{edls_being_edited/king_of_kings.txt → king_of_kings.txt} +5 -5
  123. data/zamples/edit_decision_lists/dvds/nanny_mcphee.txt +31 -0
  124. data/zamples/edit_decision_lists/dvds/pack_jackson_wedding_2007-03-03.txt +9 -2
  125. data/zamples/edit_decision_lists/dvds/remember_the_titans.txt +35 -0
  126. data/zamples/edit_decision_lists/dvds/sintel_open_source_blender_ntsc_dvd.txt +8 -4
  127. data/zamples/edit_decision_lists/dvds/speed_racer.txt +1 -1
  128. data/zamples/edit_decision_lists/dvds/tangled.txt +21 -0
  129. data/zamples/edit_decision_lists/dvds/tron_legacy.txt +4 -1
  130. data/zamples/edit_decision_lists/files/conference_music_video.txt +10 -0
  131. data/zamples/edit_decision_lists/notes_on_movies_without_edls_yet/tron.txt +10 -0
  132. data/zamples/edit_decision_lists/old_not_yet_updated/example_edit_decision_list.txt +1 -1
  133. metadata +139 -117
  134. data/conclusions +0 -6
  135. data/documentation/how_to_create_your_own_delete_lists.txt +0 -69
  136. data/lib/gui/sensible-cinema-base.rb +0 -620
  137. data/lib/gui/sensible-cinema-create.rb +0 -331
  138. data/lib/gui/sensible-cinema-dependencies.rb +0 -28
  139. data/lib/gui/sensible-cinema-normal.rb +0 -351
  140. data/lib/gui/sensible-cinema-side-by-side.rb +0 -27
  141. data/lib/shutdown_kill.rb +0 -32
  142. data/lib/storage.rb +0 -105
  143. data/ocr.todo_if_need_speed +0 -6
  144. data/upconvert_netflix/record_screen/recording/1.png +0 -0
  145. data/upconvert_netflix/record_screen/recording/10.png +0 -0
  146. data/upconvert_netflix/record_screen/recording/2.png +0 -0
  147. data/upconvert_netflix/record_screen/recording/3.png +0 -0
  148. data/upconvert_netflix/record_screen/recording/4.png +0 -0
  149. data/upconvert_netflix/record_screen/recording/5.png +0 -0
  150. data/upconvert_netflix/record_screen/recording/6.png +0 -0
  151. data/upconvert_netflix/record_screen/recording/7.png +0 -0
  152. data/upconvert_netflix/record_screen/recording/8.png +0 -0
  153. data/upconvert_netflix/record_screen/recording/9.png +0 -0
  154. data/upconvert_netflix/record_screen/recording/d.png +0 -0
  155. data/vendor/monkey.png +0 -0
  156. data/vendor/rdp-win32screenshot-0.0.9/.document +0 -5
  157. data/vendor/rdp-win32screenshot-0.0.9/History.rdoc +0 -53
  158. data/vendor/rdp-win32screenshot-0.0.9/LICENSE +0 -20
  159. data/vendor/rdp-win32screenshot-0.0.9/README.rdoc +0 -95
  160. data/vendor/rdp-win32screenshot-0.0.9/Rakefile +0 -63
  161. data/vendor/rdp-win32screenshot-0.0.9/VERSION +0 -1
  162. data/vendor/rdp-win32screenshot-0.0.9/enumerated +0 -55576
  163. data/vendor/rdp-win32screenshot-0.0.9/lib/aero_offscreen_srccopy_captureblt_doesnt_have_everything.bmp.gz +0 -0
  164. data/vendor/rdp-win32screenshot-0.0.9/lib/enumerate.rb +0 -6
  165. data/vendor/rdp-win32screenshot-0.0.9/lib/firefox.bmp +0 -0
  166. data/vendor/rdp-win32screenshot-0.0.9/lib/normal.bmp +0 -0
  167. data/vendor/rdp-win32screenshot-0.0.9/lib/normal2.bmp +0 -0
  168. data/vendor/rdp-win32screenshot-0.0.9/lib/notes +0 -5
  169. data/vendor/rdp-win32screenshot-0.0.9/lib/snapshot.rb +0 -10
  170. data/vendor/rdp-win32screenshot-0.0.9/lib/srccopy.bmp +0 -0
  171. data/vendor/rdp-win32screenshot-0.0.9/lib/srccopyplus.bmp +0 -0
  172. data/vendor/rdp-win32screenshot-0.0.9/lib/srccopyplusdirectx.bmp +0 -0
  173. data/vendor/rdp-win32screenshot-0.0.9/lib/vc.bmp +0 -0
  174. data/vendor/rdp-win32screenshot-0.0.9/lib/win32/screenshot.rb +0 -95
  175. data/vendor/rdp-win32screenshot-0.0.9/lib/win32/screenshot/bitmap_maker.rb +0 -200
  176. data/vendor/rdp-win32screenshot-0.0.9/lib/win32/util.rb +0 -93
  177. data/vendor/rdp-win32screenshot-0.0.9/spec/spec.opts +0 -1
  178. data/vendor/rdp-win32screenshot-0.0.9/spec/spec_helper.rb +0 -85
  179. data/vendor/rdp-win32screenshot-0.0.9/spec/win32_screenshot_spec.rb +0 -194
  180. data/vendor/rdp-win32screenshot-0.0.9/spec/win32_screenshot_util_spec.rb +0 -75
  181. data/vendor/rdp-win32screenshot-0.0.9/win32screenshot.gemspec +0 -69
  182. data/vendor/tsmuxer_1.10.6/licence.txt +0 -42
  183. data/zamples/edit_decision_lists/dvds/making_marriage_work.txt +0 -15
  184. data/zamples/edit_decision_lists/notes/tron.txt +0 -4
  185. data/zamples/edit_decision_lists/old_not_yet_updated/dvds/making marriage work.txt +0 -9
@@ -0,0 +1 @@
1
+ mplayer -dumpstream dvd://1 -dvd-device d: -dumpfile dvdout.mpg
@@ -0,0 +1,3 @@
1
+ @rem with track 1, DVD drive d:\
2
+ mplayer -benchmark -endpos 10 dvdnav://1/d: -vo null -nosound 2>&1 > output2.txt
3
+ @rem then examine output2.txt
@@ -0,0 +1,13 @@
1
+ start = 33.26
2
+ #play_this = "g:\\Video\\Sintel_NTSC\\Sintel_NTSC-2.m4v"
3
+ play_this = "dvdnav://1/d:"
4
+ #play_this = "dvd://1/d:"
5
+ #play_this = "title00.ts"
6
+ #play_this = "dvdout.mpg"
7
+ in_out = IO.popen("mplayer -vo direct3d -osdlevel 2 #{play_this} -ss #{start} -slave -font c:\\windows\\fonts\\arial.ttf", 'w')
8
+ in_out.puts "pause"
9
+ loop {
10
+ p 'accepting from console'
11
+ incoming = STDIN.gets
12
+ in_out.puts incoming.strip
13
+ }
@@ -0,0 +1,707 @@
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
+ alias system_original system
21
+ require 'fileutils'
22
+
23
+ class String
24
+ def snake_case
25
+ self.gsub(/::/, '/').
26
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
27
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
28
+ tr("-", "_").
29
+ downcase
30
+ end
31
+ def quotify
32
+ '"' + self + '"'
33
+ end
34
+ end
35
+
36
+ # a few I'll always need no matter what...
37
+ require_relative '../jruby-swing-helpers/swing_helpers'
38
+ require_relative '../jruby-swing-helpers/storage'
39
+ require_relative '../edl_parser'
40
+ require 'tmpdir'
41
+ require 'whichr'
42
+ require 'os'
43
+
44
+ if OS.doze?
45
+ autoload :WMI, 'ruby-wmi'
46
+ autoload :EightThree, './lib/eight_three'
47
+ end
48
+
49
+ # attempt to load on demand...i.e. faster...
50
+ for kls in [:MencoderWrapper, :MplayerEdl, :PlayAudio, :SubtitleProfanityFinder, :ConvertThirtyFps]
51
+ autoload kls, "./lib/#{kls.to_s.snake_case}"
52
+ end
53
+
54
+ for kls in [:PlayAudio, :RubyClip, :DriveInfo]
55
+ autoload kls, "./lib/jruby-swing-helpers/#{kls.to_s.snake_case}"
56
+ end
57
+
58
+ if OS.windows?
59
+ vendor_cache = File.expand_path(File.dirname(__FILE__)) + '/../../vendor/cache'
60
+ for name in ['.', 'mencoder', 'ffmpeg', 'mplayer_edl']
61
+ # put them all before the old path
62
+ ENV['PATH'] = (vendor_cache + '/' + name).to_filename + ';' + ENV['PATH']
63
+ end
64
+
65
+ installed_smplayer_folders = Dir['{c,d,e,f,g}:/program files*/smplayer']
66
+
67
+ for folder in installed_smplayer_folders
68
+ ENV['PATH'] = ENV['PATH'] + ";#{folder.gsub('/', "\\")}"
69
+ end
70
+
71
+ else
72
+ # handled in check_mac_installed.rb file
73
+
74
+ end
75
+
76
+ import 'javax.swing.ImageIcon'
77
+
78
+ module SensibleSwing
79
+ include SwingHelpers # various swing classes
80
+ JFrame
81
+ VERSION = File.read(File.dirname(__FILE__) + "/../../VERSION").strip
82
+ puts "v. " + VERSION
83
+
84
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) # <sigh>
85
+
86
+ class MainWindow < JFrame
87
+ include SwingHelpers # work-around?
88
+
89
+ def initialize start_visible = true, args = ARGV # lodo not optionals
90
+ super "Sensible-Cinema #{VERSION} (GPL)"
91
+ @args = args
92
+ force_accept_license_first # in other file :P
93
+ setDefaultCloseOperation JFrame::EXIT_ON_CLOSE # closes the whole app when they hit X ...
94
+ @panel = JPanel.new
95
+ @buttons = []
96
+ @panel.set_layout nil
97
+ add @panel # why can't I just slap these down? panel? huh?
98
+ @starting_button_y = 40
99
+ @button_width = 400
100
+
101
+ add_text_line "Welcome to Sensible Cinema!"
102
+ @starting_button_y += 10 # kinder ugly...
103
+ add_text_line " Rest mouse over buttons for \"help\" type descriptions (tooltips)."
104
+ @current_dvds_line1 = add_text_line "Checking present DVD's..."
105
+ @current_dvds_line2 = add_text_line ""
106
+ @callbacks_for_dvd_edl_present = []
107
+ DriveInfo.create_looping_drive_cacher
108
+ DriveInfo.add_notify_on_changed_disks { update_currently_inserted_dvd_list }
109
+
110
+ setIconImage(ImageIcon.new(__DIR__ + "/../vendor/profs.png").getImage())
111
+ check_for_various_dependencies
112
+ set_visible start_visible
113
+ end
114
+
115
+ def add_callback_for_dvd_edl_present &block
116
+ raise unless block
117
+ @callbacks_for_dvd_edl_present << block
118
+ update_currently_inserted_dvd_list # updates them :P
119
+ end
120
+
121
+ def update_currently_inserted_dvd_list
122
+ present_discs = []
123
+ DriveInfo.get_dvd_drives_as_openstruct.each{|disk|
124
+ if disk.VolumeName
125
+ dvd_id = DriveInfo.md5sum_disk(disk.MountPoint)
126
+ edit_list_path_if_present = EdlParser.single_edit_list_matches_dvd(dvd_id, true)
127
+ if edit_list_path_if_present
128
+ human_name = parse_edl(edit_list_path_if_present)['name']
129
+ human_name ||= ''
130
+ end
131
+ present_discs << [human_name, disk.VolumeName, edit_list_path_if_present]
132
+ end
133
+ }
134
+ found_one = false
135
+ present_discs.map!{|human_name, volume_name, has_edl|
136
+ if human_name
137
+ found_one = true
138
+ "DVD: #{human_name} has an Edit List available! (#{volume_name})"
139
+ else
140
+ "DVD: (#{volume_name}) has NO Edit List available!"
141
+ end
142
+ }
143
+ if present_discs.length > 0
144
+ @current_dvds_line1.text= ' ' + present_discs[0]
145
+ @current_dvds_line2.text= ' ' + present_discs[1..2].join(" ")
146
+ else
147
+ @current_dvds_line1.text= ' No DVD discs currently inserted.'
148
+ @current_dvds_line2.text = ''
149
+ end
150
+ @callbacks_for_dvd_edl_present.each{|c| c.call(present_discs.length > 0, found_one)}
151
+ end
152
+
153
+ def get_title_track_string descriptors, use_default_of_one = true
154
+ given = descriptors["dvd_title_track"]
155
+ given ||= "1" if use_default_of_one
156
+ given
157
+ end
158
+
159
+ def get_srt_filename descriptors, edl_filename
160
+ path = descriptors['subtitles_to_display_relative_path'] if descriptors
161
+ if path
162
+ path = File.expand_path(File.dirname(edl_filename) + '/' + path)
163
+ raise 'nonexisting srt file must be relative to the edl file...' + path unless File.exist? path
164
+ end
165
+ path
166
+ end
167
+
168
+ def we_are_in_create_mode
169
+ @args.index("--create-mode")
170
+ end
171
+
172
+ def we_are_in_developer_mode?
173
+ @args.detect{|a| a == '--developer-mode'}
174
+ end
175
+
176
+ def new_jbutton title, tooltip = nil
177
+ button = JButton.new title
178
+ button.tool_tip = tooltip
179
+ button.set_bounds(44, @starting_button_y, @button_width, 23)
180
+ @panel.add button
181
+ @buttons << button
182
+ if block_given? # allow for new_jbutton("xx") do ... end [this is possible through some miraculous means LOL]
183
+ button.on_clicked { yield }
184
+ end
185
+ increment_button_location
186
+ button
187
+ end
188
+
189
+ def add_text_line line
190
+ jlabel = JLabel.new line
191
+ happy = Font.new("Tahoma", Font::PLAIN, 11)
192
+ jlabel.setFont(happy)
193
+ jlabel.set_bounds(44,@starting_button_y ,460,14)
194
+ @panel.add jlabel
195
+ increment_button_location 18
196
+ jlabel
197
+ end
198
+
199
+ def increment_button_location how_much = 30
200
+ @starting_button_y += how_much
201
+ setSize @button_width+80, @starting_button_y + 50
202
+ end
203
+
204
+ LocalStorage = Storage.new("sensible_cinema_storage_#{VERSION}")
205
+
206
+ def when_thread_done(thread)
207
+ Thread.new {thread.join; yield }
208
+ end
209
+
210
+
211
+ # a window that when closed doesn't bring the whole app down
212
+ def new_child_window
213
+ child = MainWindow.new true, []
214
+ child.setDefaultCloseOperation(JFrame::DISPOSE_ON_CLOSE) # don't exit on close
215
+ child.parent=self # this should have failed in the PPL
216
+ # make both windows visible by moving the child down and to the right of its parent
217
+ x, y = self.get_location.x, self.get_location.y
218
+ child.set_location(x + 100, y + 100)
219
+ child
220
+ end
221
+
222
+ def run_smplayer_non_blocking *args
223
+ @background_thread = Thread.new {
224
+ run_smplayer_blocking *args
225
+ }
226
+ end
227
+
228
+ # basically run mplayer/smplayer on a file or DVD
229
+ def run_smplayer_blocking play_this, title_track_maybe_nil, passed_in_extra_options, force_use_mplayer, show_subs, start_full_screen, srt_filename
230
+ raise unless passed_in_extra_options # cannot be nil
231
+ extra_options = []
232
+ # -framedrop is for slow CPU's
233
+ # same with -autosync to try and help it stay in sync... -mc 0.03 is to A/V correct 1s audio per 2s video
234
+ # -hardframedrop might help but hurts the eyes just too much
235
+ extra_options << "-framedrop" # even in create mode, if the audio ever gets off, we're hosed with making accurate timestamps...so drop, until I hear otherwise...
236
+ extra_options << "-mc 2"
237
+ extra_options << "-autosync 30"
238
+
239
+ if we_are_in_create_mode
240
+ extra_options << "-osdlevel 2"
241
+ extra_options << "-osd-verbose" if we_are_in_developer_mode?
242
+ end
243
+ extra_options << "-osd-fractions 1"
244
+
245
+ if !show_subs && !srt_filename
246
+ # disable all subtitles :P
247
+ extra_options << "-nosub -noautosub -forcedsubsonly -sid 1000"
248
+ end
249
+ if srt_filename
250
+ extra_options << "-sub #{srt_filename}"
251
+ else
252
+ extra_options << "-alang en"
253
+ extra_options << "-slang en"
254
+ end
255
+
256
+ force_use_mplayer ||= OS.mac?
257
+ parent_parent = File.basename(File.dirname(play_this))
258
+ if parent_parent == 'VIDEO_TS'
259
+ # case d:\yo\VIDEO_TS\title0.vob
260
+ dvd_device_dir = normalize_path(File.dirname(play_this))
261
+ play_this = "\"dvdnav://#{title_track_maybe_nil}/#{dvd_device_dir}/..\""
262
+ elsif File.exist?(play_this + '/VIDEO_TS') || (play_this =~ /\/dev\/rdisk\d/)
263
+ # case d:\ where d:\VIDEO_TS exists [DVD mounted in drive, or DVD dir] or mac's /dev/rdisk1
264
+ play_this = "\"dvdnav://#{title_track_maybe_nil}/#{play_this}\""
265
+ else
266
+ # case g:\video\filename.mpg
267
+ # leave it the same...
268
+ end
269
+ if play_this =~ /dvdnav/ && title_track_maybe_nil
270
+ extra_options << "-msglevel identify=4" # prevent smplayer from using *forever* to look up info on DVD's with -identify ...
271
+ end
272
+
273
+ extra_options << "-mouse-movements #{get_upconvert_secondary_settings}" # just in case smplayer also needs -mouse-movements... :) LODO
274
+ extra_options << "-lavdopts threads=#{OS.cpu_count}" # just in case this helps [supposed to with h.264] # NB fast *crashes* doze...
275
+ if force_use_mplayer
276
+ extra_options << "-font #{File.expand_path('vendor/subfont.ttf')}"
277
+ key_strokes = <<-EOL
278
+ RIGHT seek +10
279
+ LEFT seek -10
280
+ DOWN seek -60
281
+ UP seek +60
282
+ ENTER {dvdnav} dvdnav select
283
+ MOUSE_BTN0 {dvdnav} dvdnav select
284
+ MOUSE_BTN0_DBL vo_fullscreen
285
+ MOUSE_BTN2 vo_fullscreen
286
+ KP_ENTER dvdnav select
287
+ # some for os x, some for doze. huh?
288
+ EOL
289
+ conf_file = File.expand_path './mplayer_input_conf'
290
+ File.write conf_file, key_strokes
291
+ extra_options << "-volume 100" # why start low? mplayer why oh why LODO
292
+ if OS.windows?
293
+ # direct3d for windows 7 old nvidia cards' sake [yipes] and also dvdnav sake
294
+ extra_options << "-vo direct3d"
295
+ conf_file = conf_file[2..-1] # strip off drive letter, which it doesn't seem to like no sir
296
+ end
297
+ if start_full_screen
298
+ extra_options << "-fs"
299
+ upconv = get_upconvert_vf_settings
300
+ upconv = "-vf #{upconv}" if upconv.present?
301
+ else
302
+ upconv = ""
303
+ end
304
+ if OS.doze?
305
+ # we want this even if we don't have -osd-add, since it adds confidence :)
306
+ mplayer_loc = LocalModifiedMplayer
307
+ assert File.exist?(mplayer_loc)
308
+ else
309
+ mplayer_loc = "mplayer"
310
+ end
311
+ c = "#{mplayer_loc} #{extra_options.join(' ')} #{upconv} -input conf=\"#{conf_file}\" #{passed_in_extra_options} \"#{play_this}\" "
312
+ else
313
+ if OS.windows?
314
+ extra_options << "-vo direct3d" # more light nvidia...should be ok...this wastes cpu...but we have to have it I guess...
315
+ end
316
+ set_smplayer_opts extra_options.join(' ') + " " + passed_in_extra_options, get_upconvert_vf_settings, show_subs
317
+ c = "smplayer \"#{play_this}\" -config-path \"#{File.dirname EightThree.convert_path_to_8_3(SMPlayerIniFile)}\" "
318
+ c += " -fullscreen " if start_full_screen
319
+ if !we_are_in_create_mode
320
+ #c += " -close-at-end " # still too unstable...
321
+ end
322
+ end
323
+ puts c
324
+ system_blocking c
325
+ end
326
+
327
+ SMPlayerIniFile = File.expand_path("~/.smplayer_sensible_cinema/smplayer.ini")
328
+ LocalModifiedMplayer = File.expand_path "vendor/cache/mplayer_edl/mplayer.exe"
329
+
330
+ def set_smplayer_opts to_this, video_settings, show_subs = false
331
+ p 'setting smplayer extra opts to this:' + to_this
332
+ old_prefs = File.read(SMPlayerIniFile) rescue ''
333
+ unless old_prefs.length > 0
334
+ # LODO double check the rest here...
335
+ old_prefs = "[%General]\nmplayer_bin=\n[advanced]\nmplayer_additional_options=\nmplayer_additional_video_filters=\n[subtitles]\nautoload_sub=false\n[performance]\npriority=3"
336
+ end
337
+ raise to_this if to_this =~ /"/ # unexpected, unfortunately... <smplayer bug>
338
+ assert new_prefs = old_prefs.gsub(/mplayer_additional_options=.*/, "mplayer_additional_options=#{to_this}")
339
+ assert new_prefs.gsub!(/autoload_sub=.*$/, "autoload_sub=#{show_subs.to_s}")
340
+ raise 'unexpected' if get_upconvert_vf_settings =~ /"/
341
+ assert new_prefs.gsub!(/mplayer_additional_video_filters=.*$/, "mplayer_additional_video_filters=\"#{video_settings}\"")
342
+ raise 'smplayer on non doze not expected...' unless OS.doze?
343
+ mplayer_to_use = LocalModifiedMplayer
344
+ assert File.exist?(mplayer_to_use)
345
+ new_value = "\"" + mplayer_to_use.to_filename.gsub("\\", '/') + '"' # forward slashes. Weird.
346
+ assert new_prefs.gsub!(/mplayer_bin=.*$/, "mplayer_bin=" + new_value)
347
+ # now some less important ones...
348
+ new_prefs.gsub!(/priority=.*$/, "priority=3") # normal priority...scary otherwise! lodo tell smplayer...
349
+ # enable dvdnav navigation, just for kicks I guess.
350
+ new_prefs.gsub!(/use_dvdnav=.*$/, "use_dvdnav=true")
351
+
352
+ FileUtils.mkdir_p File.dirname(SMPlayerIniFile)
353
+ File.write(SMPlayerIniFile, new_prefs)
354
+ new_prefs.each_line{|l| print l if l =~ /additional_video/} # debug
355
+ end
356
+
357
+ def system_blocking command, low_prio = false
358
+ return true if command =~ /^@rem/ # JRUBY-5890 bug
359
+ if low_prio
360
+ out = IO.popen(command) # + " 2>&1"
361
+ low_prio = 64 # from msdn
362
+
363
+ if command =~ /(ffmpeg|mencoder)/
364
+ # XXXX not sure if there's a better way...because some *are* complex and have ampersands...
365
+ # unfortunately have to check for nil because it could exit too early [?]
366
+ exe_name = $1 + '.exe'
367
+ begin
368
+ p = proc{ ole = ::WMI::Win32_Process.find(:first, :conditions => {'Name' => exe_name}); sleep 1 unless ole; ole }
369
+ piddy = p.call || p.call || p.call # we actually do need this to loop...guess we're too quick
370
+ # but the first time through this still inexplicably fails all 3...odd
371
+ piddys = ::WMI::Win32_Process.find(:all, :conditions => {'Name' => exe_name})
372
+ for piddy in piddys
373
+ # piddy.SetPriority low_prio # this call can seg fault at times...JRUBY-5422
374
+ pid = piddy.ProcessId # this doesn't seg fault, tho
375
+ system_original("vendor\\setpriority -lowest #{pid}") # uses PID for the command line
376
+ end
377
+ rescue Exception => e
378
+ p 'warning, got exception trying to set priority [jruby prob? ...]', e
379
+ end
380
+ end
381
+ print out.read # let it finish
382
+ out.close
383
+ $?.exitstatus == 0 # 0 means success
384
+ else
385
+ raise command + " failed env #{ENV['PATH']}" unless system_original command
386
+ end
387
+ end
388
+
389
+ def system_non_blocking command
390
+ @background_thread = Thread.new { system_original command }
391
+ end
392
+
393
+ # force them choose which system call to use explicitly
394
+ if respond_to? :system
395
+ undef system
396
+ else # it's a reload
397
+ end
398
+
399
+ def play_dvd_smplayer_unedited use_mplayer_instead
400
+ drive_or_file, dvd_volume_name, dvd_id, edl_path_maybe_nil, descriptors = choose_dvd_or_file_and_edl_for_it(force_choose_edl_file_if_no_easy_match = true)
401
+ title_track_maybe_nil = get_title_track_string(descriptors, false)
402
+ if is_dvd?(drive_or_file)
403
+ dvd_options = get_dvd_playback_options(descriptors)
404
+ else
405
+ dvd_options = ''
406
+ end
407
+ run_smplayer_non_blocking drive_or_file, title_track_maybe_nil, dvd_options, use_mplayer_instead, true, false, get_srt_filename(descriptors, edl_path_maybe_nil)
408
+ end
409
+
410
+ def get_dvd_playback_options descriptors
411
+ out = []
412
+
413
+ nav, mpeg_time = descriptors['dvd_nav_packet_offset'] # like [0.5, 0.734067]
414
+ if nav
415
+ offset_time = mpeg_time*1/1.001 - nav # our reading like 0.5 is actually a bit offset, since in reality that "means 4.997" or what not
416
+ else
417
+ # readings: 0.213 0.173 0.233 0.21 0.18 0.197 they're almost all right around 0.20...
418
+ show_blocking_message_dialog "error--your DVD EDL doesn\'t list a start offset time [dvd_nav_packet_offset] which is needed for precise accurate timing. Please run\nadvanced mode -> Display information about current DVD\nand add it to the EDL. Using a default for now..."
419
+ offset_time = 0.20
420
+ p caller
421
+ end
422
+ raise if offset_time <= 0 # unexpected...
423
+ out << "-osd-add #{ "%0.3f" % offset_time}"
424
+ out.join(' ')
425
+ end
426
+
427
+ if OS.doze? # avoids spaces in filenames :)
428
+ EdlTempFile = EightThree.convert_path_to_8_3(Dir.tmpdir) + '\\mplayer.temp.edl'
429
+ else
430
+ raise if Dir.tmpdir =~ / / # that would be unexpected, and possibly cause problems...
431
+ EdlTempFile = Dir.tmpdir + '/mplayer.temp.edl'
432
+ end
433
+
434
+ EdlFilesChosen = {} # allow for switching tapes but still cache EDL loc. :P
435
+
436
+ def choose_dvd_or_file_and_edl_for_it force_choose_edl_file_if_no_easy_match = true
437
+ drive_or_file, dvd_volume_name, dvd_id = choose_dvd_drive_or_file false
438
+ edl_path = EdlFilesChosen[dvd_id]
439
+ if !edl_path
440
+ edl_path = EdlParser.single_edit_list_matches_dvd(dvd_id)
441
+ if !edl_path && force_choose_edl_file_if_no_easy_match
442
+ message = "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, if one doesn't exist yet"
443
+ show_blocking_message_dialog message
444
+ edl_path = new_existing_file_selector_and_select_file(message, EdlParser::EDL_DIR)
445
+ end
446
+ end
447
+ p 're/loading ' + edl_path # in case it has changed on disk
448
+ if edl_path # sometimes they don't have to choose one [?]
449
+ descriptors = nil
450
+ begin
451
+ descriptors = parse_edl edl_path
452
+ rescue SyntaxError => e
453
+ show_non_blocking_message_dialog("this file has an error--please fix then hit ok: \n" + edl_path + "\n " + e)
454
+ raise e
455
+ end
456
+ end
457
+ EdlFilesChosen[dvd_id] ||= edl_path
458
+ [drive_or_file, dvd_volume_name, dvd_id, edl_path, descriptors]
459
+ end
460
+
461
+ LocalStorage.set_default('mplayer_beginning_buffer', 0.5)
462
+
463
+ def is_dvd? drive_or_file
464
+ # it's like a/b/VIDEO_TS or d:/
465
+ if File.basename(File.dirname(drive_or_file)) == 'VIDEO_TS'
466
+ # /a/b/c/VIDEO_TS/yo.vob
467
+ true
468
+ elsif File.exist?(drive_or_file + '/VIDEO_TS')
469
+ # d:\
470
+ true
471
+ elsif OS.mac? && (drive_or_file =~ /\/dev\/rdisk\d+/)
472
+ true
473
+ else
474
+ false
475
+ end
476
+ end
477
+
478
+ # MplayerBeginingBuffer = 1.0
479
+ # MplayerEndBuffer = 0.0
480
+
481
+ def play_smplayer_edl_non_blocking optional_file_with_edl_path = nil, extra_mplayer_commands_array = [], force_mplayer = false, start_full_screen = true, add_secs_end = 0, add_secs_begin = LocalStorage['mplayer_beginning_buffer'], show_subs = false
482
+ if we_are_in_create_mode
483
+ assert(add_secs_begin == 0 && add_secs_end == 0)
484
+ end
485
+ if optional_file_with_edl_path
486
+ drive_or_file, edl_path = optional_file_with_edl_path
487
+ dvd_id = NonDvd # fake it out...LODO a bit smelly/ugly
488
+ else
489
+ drive_or_file, dvd_volume_name, dvd_id, edl_path, descriptors = choose_dvd_or_file_and_edl_for_it
490
+ end
491
+ start_add_this_to_all_ts = 0
492
+ if edl_path # some don't care...
493
+ descriptors = EdlParser.parse_file edl_path
494
+ title_track = get_title_track_string(descriptors)
495
+ end
496
+
497
+ if is_dvd?(drive_or_file)
498
+ # it's a DVD of some sort
499
+ extra_mplayer_commands_array << get_dvd_playback_options(descriptors)
500
+ else
501
+ # it's a file
502
+ # check if it has a start offset...
503
+ all = `ffmpeg -i "#{drive_or_file}" 2>&1` # => Duration: 01:35:49.59, start: 600.000000
504
+ all =~ /Duration.*start: ([\d\.]+)/
505
+ start = $1.to_f
506
+ if start > 1 # LODO dvd's themselves start at 0.3 [sintel], but I don't think much higher than that never seen it...
507
+ show_non_blocking_message_dialog "Warning: file seems to start at an extra offset, adding it to the timestamps... #{start}
508
+ maybe not compatible with XBMC, if that's what you use, and you probably don't" # LODO test it XBMC...
509
+ start_add_this_to_all_ts = start
510
+ end
511
+ end
512
+
513
+ if edl_path
514
+ splits = [] # TODO not pass as parameter either
515
+ edl_contents = MplayerEdl.convert_to_edl descriptors, add_secs_end, add_secs_begin, splits, start_add_this_to_all_ts # add a sec to mutes to accomodate for mplayer's oddness..
516
+ File.write(EdlTempFile, edl_contents)
517
+ extra_mplayer_commands_array << "-edl #{File.expand_path EdlTempFile}"
518
+ end
519
+
520
+ run_smplayer_non_blocking drive_or_file, title_track, extra_mplayer_commands_array.join(' '), force_mplayer, show_subs, start_full_screen, get_srt_filename(descriptors, edl_path)
521
+ end
522
+
523
+ def print *args
524
+ 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)
525
+ end
526
+
527
+ def open_file_to_edit_it filename, options = {} # :start_minimized is the only option
528
+ if OS.windows?
529
+ if options[:start_minimized]
530
+ system_non_blocking "start /min notepad \"#{filename}\""
531
+ else
532
+ system_non_blocking "notepad \"#{filename}\""
533
+ end
534
+ else
535
+ # ignore minimized :P
536
+ system_non_blocking "open -a TextEdit \"#{filename}\""
537
+ end
538
+ end
539
+
540
+ def new_nonexisting_filechooser_and_go title = nil, default_dir = nil, default_file = nil
541
+ bring_to_front unless OS.mac? # causes triples on mac!
542
+ JFileChooser.new_nonexisting_filechooser_and_go title, default_dir, default_file
543
+ end
544
+
545
+ # also caches directory previously selected ...
546
+ def new_existing_file_selector_and_select_file title, dir = nil
547
+ bring_to_front unless OS.mac?
548
+ unique_trace = caller.inspect
549
+ if LocalStorage[unique_trace]
550
+ dir = LocalStorage[unique_trace]
551
+ end
552
+ p 'using system default dir' unless dir # happens more frequently after code changes alter the path :P
553
+ p 'using lookup dir ' + dir, LocalStorage[unique_trace] if $VERBOSE
554
+ got = SwingHelpers.new_previously_existing_file_selector_and_go title, dir
555
+ LocalStorage[unique_trace] = File.dirname(got)
556
+ got
557
+ end
558
+
559
+ def show_blocking_message_dialog(message, title = message.split("\n")[0], style= JOptionPane::INFORMATION_MESSAGE)
560
+ bring_to_front
561
+ SwingHelpers.show_blocking_message_dialog message, title, style
562
+ end
563
+
564
+ # call dispose on this to close it if it hasn't been canceled yet...
565
+ def show_non_blocking_message_dialog message, close_button_text = 'Close'
566
+ bring_to_front
567
+ # lodo NonBlockingDialog it can get to the top instead of being so buried...
568
+ SwingHelpers.show_non_blocking_message_dialog message, close_button_text
569
+ end
570
+
571
+ include_class javax.swing.UIManager
572
+
573
+ def get_user_input(message, default = '', cancel_ok = false)
574
+ bring_to_front
575
+ SwingHelpers.get_user_input message, default, cancel_ok
576
+ end
577
+
578
+ def show_copy_pastable_string(message, value)
579
+ bring_to_front
580
+ RubyClip.set_clipboard value
581
+ get_user_input message + " (has been copied to clipboard)", value, true
582
+ end
583
+
584
+ def show_in_explorer filename
585
+ SwingHelpers.show_in_explorer filename
586
+ end
587
+
588
+ def show_select_buttons_prompt message, names ={}
589
+ JOptionPane.show_select_buttons_prompt(message, names)
590
+ end
591
+
592
+
593
+ def parse_edl path
594
+ EdlParser.parse_file path
595
+ end
596
+
597
+ def get_freespace path
598
+ JFile.new(File.dirname(path)).get_usable_space
599
+ end
600
+
601
+ def get_drive_with_most_space_with_slash
602
+ DriveInfo.get_drive_with_most_space_with_slash
603
+ end
604
+
605
+ attr_accessor :background_thread, :buttons
606
+
607
+ NonDvd = 'non dvd has no dvdid' # we need it for convenience, say you want to go through your indexed vids and convert them all?
608
+
609
+ # returns e:\, volume_name, dvd_id
610
+ # or full_path.mkv, filename, ''
611
+ def choose_dvd_drive_or_file force_choose_only_dvd_drive
612
+ opticals = DriveInfo.get_dvd_drives_as_openstruct
613
+ if @saved_opticals == opticals && @_choose_dvd_drive_or_file
614
+ # memoize...if disks haven't changed :)
615
+ return @_choose_dvd_drive_or_file
616
+ else
617
+ @saved_opticals = opticals # save currently mounted disk list, so we know if we should re-select later...
618
+ # is this ok for os x?
619
+ end
620
+
621
+ has_at_least_one_dvd_inserted = opticals.find{|d| d.VolumeName }
622
+ if !has_at_least_one_dvd_inserted && force_choose_only_dvd_drive
623
+ show_blocking_message_dialog 'insert a dvd first'
624
+ raise 'no dvd found'
625
+ end
626
+ names = opticals.map{|d| d.Name + "\\" + " (" + (d.VolumeName || 'Insert DVD to use') + ")"}
627
+ if !force_choose_only_dvd_drive && !has_at_least_one_dvd_inserted
628
+ names += ['No DVD mounted so click to choose a Local File (or insert DVD, then re-try)']
629
+ used_local_file_option = true
630
+ end
631
+
632
+ count = 0
633
+ opticals.each{|d| count += 1 if d.VolumeName}
634
+ if count == 1 && !used_local_file_option
635
+ # just choose it if there's only one disk available..
636
+ p 'selecting only disk currently present in the various DVD drives [if you have more than one, that is]'
637
+ selected_idx = opticals.index{|d| d.VolumeName}
638
+ unless selected_idx
639
+ show_blocking_message_dialog "Please insert a disk first"
640
+ raise 'inset disk'
641
+ end
642
+ else
643
+ dialog = get_disk_chooser_window names
644
+ selected_idx = dialog.go_selected_index
645
+ end
646
+ if used_local_file_option
647
+ raise unless selected_idx == 0 # it was our only option...they must have selected it!
648
+ filename = new_existing_file_selector_and_select_file("Select yer previously grabbed from DVD file")
649
+ assert_ownership_dialog
650
+ return [filename, File.basename(filename), NonDvd]
651
+ else
652
+ disk = opticals[selected_idx]
653
+ out = show_non_blocking_message_dialog "calculating current disk's unique id...if this pauses more than 10s then clean your DVD..."
654
+ begin
655
+ dvd_id = DriveInfo.md5sum_disk(disk.MountPoint)
656
+ rescue Exception => e
657
+ show_blocking_message_dialog e.to_s # todo a bit ugly...
658
+ raise
659
+ ensure
660
+ out.dispose
661
+ end
662
+ @_choose_dvd_drive_or_file = [disk.DevicePoint, opticals[selected_idx].VolumeName, dvd_id]
663
+ return @_choose_dvd_drive_or_file
664
+ end
665
+ end
666
+
667
+
668
+ def display_and_raise error_message
669
+ show_blocking_message_dialog error_message
670
+ raise error_message
671
+ end
672
+
673
+ def with_autoclose_message(message)
674
+ a = show_non_blocking_message_dialog message
675
+ yield
676
+ a.close
677
+ end
678
+
679
+ end
680
+
681
+ end
682
+
683
+
684
+ class ::File
685
+ def self.get_root_dir this_path
686
+ this_path = File.expand_path this_path
687
+ if OS.doze?
688
+ this_path[0..2]
689
+ else
690
+ this_path.split('/')[0]
691
+ end
692
+ end
693
+
694
+ def self.strip_drive_windows this_complete_path
695
+ if OS.doze?
696
+ this_complete_path[2..-1]
697
+ else
698
+ this_complete_path
699
+ end
700
+ end
701
+ end
702
+
703
+ class ::String
704
+ def just_letters
705
+ gsub(/[^a-z0-9]/i, '').downcase
706
+ end
707
+ end