def is_any_title_uhd(bdmv_root, titles): ''' determines if the disc is a UHD :return: true if UHD, false if not. ''' from madmeasurer.loggers import main_logger is_uhd = False error = '' for t in titles: if t.NumberOfClips > 0: clip = t.GetClip(0) if clip is not None: if clip.NumberOfVideosPrimary > 0: video = clip.GetVideo(0) if video is not None: is_uhd |= video.Format == '2160p' else: error = 'main title clip 0 video 0 = None' else: error = 'main title clip 0 has no videos' else: error = 'main title clip 0 is None' else: error = 'main title has no clips' if error != '': main_logger.error(f"Unable to determine if {bdmv_root} - {t.Playlist} is a UHD; {error}") return is_uhd
def mount_if_necessary(bd_path, args): ''' A context manager that can mount and iso and return the mounted path then dismounts afterwards. ''' target = bd_path mounted = False main_requires_mount = args.main_by_mpc_be is True \ or args.analyse_main_algos is True \ or args.main_by_jriver is True \ or args.main_by_jriver_minute_resolution is True if args.measure is True or args.copy is True or main_requires_mount is True or args.describe_bd is True: mounted = target[-4:] == '.iso' if mounted is True: if platform.system() == "Windows": target = mount_iso_on_windows(bd_path) if target is not None: if not os.path.exists(f"{target}BDMV/index.bdmv"): main_logger.error( f"{bd_path} does not contain a BD folder") target = None elif platform.system() == "Linux": pass try: yield target finally: if mounted: if platform.system() == "Windows": dismount_iso_on_windows(bd_path) elif platform.system() == "Linux": pass
def dismount_iso_on_windows(iso): ''' Dismounts the ISO. :param iso: the iso. ''' iso_to_dismount = os.path.abspath(iso) command = f"PowerShell Dismount-DiskImage {iso_to_dismount}" main_logger.debug(f"Triggering : {command}") result = subprocess.run(command, capture_output=True) if result is not None and result.returncode == 0: main_logger.info(f"Dismounted {iso_to_dismount}") else: main_logger.error( f"Unable to dismount {iso_to_dismount} , stdout: {result.stdout.decode('utf-8')}, stderr: {result.stderr.decode('utf-8')}" )
def mount_iso_on_windows(iso): ''' Mounts ISO and returns the mounted drive path. :param iso: the iso. :return: the mounted path. ''' iso_to_mount = os.path.abspath(iso) command = f"PowerShell ((Mount-DiskImage {iso_to_mount} -PassThru) | Get-Volume).DriveLetter" main_logger.debug(f"Triggering : {command}") result = subprocess.run(command, capture_output=True) if result is not None and result.returncode == 0: target = f"{result.stdout.decode('utf-8').rstrip()}:{os.path.sep}" main_logger.info(f"Mounted {iso_to_mount} on {target}") else: main_logger.error( f"Unable to mount {iso_to_mount} , stdout: {result.stdout.decode('utf-8')}, stderr: {result.stderr.decode('utf-8')}" ) target = None return target
def run_mad_measure_hdr(measure_target, args): ''' triggers madMeasureHDR and bridges the stdout back to this process stdout live :param args: the cli args. :param measure_target: file to measure. ''' from madmeasurer.loggers import main_logger, output_logger exe = "" if args.mad_measure_path is None else f"{args.mad_measure_path}{os.path.sep}" command = [os.path.abspath(f"{exe}madMeasureHDR.exe"), os.path.abspath(measure_target)] if args.dry_run is True: main_logger.error(f"DRY RUN! Triggering : {command}") else: if not os.path.isfile(command[0]): main_logger.error(f"FAILED! madMeasureHDR.exe not found at {command[0]}") else: main_logger.info(f"Triggering : {command}") txt_output = os.path.abspath(f"{measure_target}-madvr.txt") with open(txt_output, 'w') as details: process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=4) line_num = 0 output = None tmp_output = None while True: if line_num == 0: output = process.stdout.readline().decode('utf-8') line_num = 1 elif line_num == 1: tmp = process.stdout.read(1) if tmp == b'\x08': output = tmp_output tmp_output = '' elif tmp == b'': output = tmp_output tmp_output = '' else: tmp_output = tmp_output + tmp.decode('utf-8') if output is not None: if output == '' and process.poll() is not None: break if output: txt = output.strip() output_logger.error(txt) details.write(txt + '\n') details.flush() output = None rc = process.poll() if rc == 0: main_logger.error(f"Completed OK {command}") else: main_logger.error(f"FAILED {command}")
def get_main_title_by_jriver(bd, bd_folder_path, resolution='seconds'): ''' Locates the main title using JRiver's algorithm which compares entries one by one by duration, audio stream count and then playlist name albeit allowing a slightly (within 10%) shorter track with more audio streams to still win. :param bd: the pybluread BD. :param bd_folder_path: the folder path. :param resolution: the resolution to use when comparison durations. :return: the main title. ''' candidate_titles = __read_playlists_from_disc_inf(bd, bd_folder_path) if len(candidate_titles) == 0: candidate_titles = {x: bd.GetTitle(x) for x in range(bd.NumberOfTitles)} max_audio_titles = 0 main_title = None main_title_num = 0 for title_num, title in candidate_titles.items(): new_main = False reason = '' if main_title is None: new_main = True else: audio_titles = __get_max_audio(title) cmp = 0 if title.Length >= (main_title.Length*0.9): cmp = audio_titles - max_audio_titles if cmp == 0: this_len = TicksToTuple(title.Length) main_len = TicksToTuple(main_title.Length) if resolution == 'minutes': cmp = ((this_len[0] * 60) + this_len[1]) - ((main_len[0] * 60) + main_len[1]) elif resolution == 'seconds': cmp = ((this_len[0] * 60 * 60) + (this_len[1] * 60) + this_len[2]) \ - ((main_len[0] * 60 * 60) + (main_len[1] * 60) + main_len[2]) else: cmp = title.Length - main_title.Length if cmp == 0: cmp = audio_titles - max_audio_titles if cmp == 0: if title.Playlist < main_title.Playlist: cmp = 1 reason = 'playlist name order' else: reason = 'audio stream count' else: reason = 'duration' elif cmp > 0: reason = 'audio stream count, duration within 10%' if cmp > 0: new_main = True if new_main is True: if main_title is not None: main_logger.debug(f"New main title found {title.Playlist} vs {main_title.Playlist} : {reason}") else: main_logger.debug(f"Initialising main title search with {title.Playlist}") main_title = title main_title_num = title_num max_audio_titles = __get_max_audio(title) else: main_logger.debug(f"Main title remains {main_title.Playlist}, discarding {title.Playlist}") if main_title is None: main_logger.error(f"No main title found in {bd.Path}") return main_title_num