2020from pathlib import Path
2121from tempfile import NamedTemporaryFile
2222
23- from scenedetect import AVAILABLE_BACKENDS , FrameTimecode , open_video
23+ from scenedetect import AVAILABLE_BACKENDS , FrameTimecode , VideoOpenFailure , open_video
2424
2525from dvr_scan .app .about_window import AboutWindow
2626from dvr_scan .app .common import register_icon
6161
6262logger = getLogger ("dvr_scan" )
6363
64- #
65- # TODO: This is ALL the controls that should be included in the initial release.
66- # Any additions or modifications can come in the future. Even overlay settings should
67- # be ignored for now and added later.
68- #
69- # Things that need unblocking for a beta release:
70- #
71- # 1. Map all existing UI controls to the DVR-Scan config types (in progress)
72- # 2. Figure out how to run the scan in the background and report the process
73- # and status back. (done)
74- # 3. Handle the video input widget. (requires background task model already)
75- # A lot of headaches can be solved if we take some time to validate the video,
76- # and maybe generate some thumbnails or check other metadata, which could take
77- # a few seconds when adding lots of videos. We can't block the UI for this long
78- # so we already need to have a task model in place before this. (todo)
79- #
80- # At that point DVR-Scan should be ready for a beta release.
81- #
82-
8364
8465# TODO: Allow this to be sorted by columns.
8566# TODO: Should we have a default sort method when bulk adding videos?
@@ -247,12 +228,16 @@ def _add_video(self, path: str = ""):
247228 return
248229 else :
249230 paths = [path ]
231+ failed_to_load = False
250232 for path in paths :
251233 if not Path (path ).exists ():
252234 logger .error (f"File does not exist: { path } " )
253235 return
254- # TODO: error handling
255- video = open_video (path , backend = "opencv" )
236+ try :
237+ video = open_video (path , backend = "opencv" )
238+ except VideoOpenFailure :
239+ failed_to_load = True
240+ continue
256241 duration = video .duration .get_timecode ()
257242 framerate = f"{ video .frame_rate :g} "
258243 resolution = f"{ video .frame_size [0 ]} x { video .frame_size [1 ]} "
@@ -263,7 +248,11 @@ def _add_video(self, path: str = ""):
263248 text = video .name ,
264249 values = (duration , framerate , resolution , path ),
265250 )
266- self ._remove_button ["state" ] = tk .NORMAL
251+ if failed_to_load :
252+ tkinter .messagebox .showwarning (
253+ "Video Open Failure" , "Failed to open one or more videos."
254+ )
255+ self ._remove_button ["state" ] = tk .NORMAL if self ._videos .get_children () else tk .DISABLED
267256
268257 @property
269258 def concatenate (self ) -> bool :
@@ -310,9 +299,14 @@ def _on_set_time(self):
310299 self ._end_time_label .grid_remove ()
311300
312301 def _on_use_regions (self ):
313- state = tk .NORMAL if self ._set_region .get () else tk .DISABLED
314- self ._region_editor_button ["state" ] = state
315- # self._load_region_file["state"] = tk.DISABLED #state
302+ if self ._set_region .get ():
303+ self ._region_editor_button ["state" ] = tk .NORMAL
304+ self ._current_region_label .grid (
305+ row = 4 , column = 3 , sticky = EXPAND_HORIZONTAL , padx = PADDING , columnspan = 2
306+ )
307+ else :
308+ self ._region_editor_button ["state" ] = tk .DISABLED
309+ self ._current_region_label .grid_remove ()
316310
317311 def _on_edit_regions (self ):
318312 videos = self .videos
@@ -1189,7 +1183,6 @@ def save(self, settings: ScanSettings) -> ScanSettings:
11891183 settings .set ("output-mode" , self ._output_mode )
11901184 if self ._output_dir :
11911185 settings .set ("output-dir" , self ._output_dir )
1192- # TODO: Should we save all these settings instead of being dependent on output mode?
11931186 if self ._output_mode == OutputMode .FFMPEG :
11941187 settings .set ("ffmpeg-input-args" , self ._ffmpeg_input_args .get ())
11951188 settings .set ("ffmpeg-output-args" , self ._ffmpeg_output_args .get ())
@@ -1234,7 +1227,6 @@ def __init__(self, root: tk.Tk, frame: tk.Widget):
12341227 pady = (0 , PADDING ),
12351228 )
12361229 self ._scan_only = tk .BooleanVar (frame , value = False )
1237- # TODO: This should be merged into output-mode to match the config file option.
12381230 self ._scan_only_button = ttk .Checkbutton (
12391231 frame ,
12401232 text = "Scan Only" ,
@@ -1353,7 +1345,6 @@ def _create_menubar(self):
13531345 underline = 0 ,
13541346 command = self ._load_config ,
13551347 )
1356- # TODO: Add functionality to save settings to a config file.
13571348 settings_menu .add_command (label = "Save..." , underline = 0 , command = self ._on_save_config )
13581349 settings_menu .add_command (
13591350 label = "Save As User Default" , underline = 2 , command = self ._on_save_config_as_user_default
0 commit comments