示例#1
0
def ParseFFMPEGNumFramesManually( lines ):
    
    frame_lines = [ l for l in lines if l.startswith( 'frame=' ) ]
    
    if len( frame_lines ) == 0:
        
        raise HydrusExceptions.MimeException( 'Video appears to be broken and non-renderable--perhaps a damaged single-frame video?' )
        
    
    final_line = frame_lines[-1] # there will be many progress rows, counting up as the file renders. we hence want the final one
    
    l = final_line
    
    l = l.replace( 'frame=', '' )
    
    while l.startswith( ' ' ):
        
        l = l[1:]
        
    
    try:
        
        frames_string = l.split( ' ' )[0]
        
        num_frames = int( frames_string )
        
    except:
        
        raise HydrusExceptions.MimeException( 'Video was unable to render correctly--could not parse ffmpeg output line: "{}"'.format( final_line ) )
        
    
    return num_frames
示例#2
0
def CheckFFMPEGError( lines ):
    
    if len( lines ) == 0:
        
        raise HydrusExceptions.MimeException( 'Could not parse that file--no FFMPEG output given.' )
        
    
    if "No such file or directory" in lines[-1]:
        
        raise IOError( "File not found!" )
        
    
    if 'Invalid data' in lines[-1]:
        
        raise HydrusExceptions.MimeException( 'FFMPEG could not parse.' )
示例#3
0
def ParseFFMPEGDuration( lines ):
    
    # get duration (in seconds)
    #   Duration: 00:00:02.46, start: 0.033000, bitrate: 1069 kb/s
    try:
        
        # had a vid with 'Duration:' in title, ha ha, so now a regex
        line = [ l for l in lines if re.search( r'^\s*Duration:', l ) is not None ][0]
        
        if 'Duration: N/A' in line:
            
            return ( None, None )
            
        
        if 'start:' in line:
            
            m = re.search( '(start\\: )' + '-?[0-9]+\\.[0-9]*', line )
            
            start_offset = float( line[ m.start() + 7 : m.end() ] )
            
            if abs( start_offset ) > 1.0: # once had a file with start offset of 957499 seconds jej
                
                start_offset = 0
                
            
        else:
            
            start_offset = 0
            
        
        match = re.search("[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line)
        hms = list(map(float, line[match.start()+1:match.end()].split(':')))
        
        if len( hms ) == 1:
            
            duration = hms[0]
            
        elif len( hms ) == 2:
            
            duration = 60 * hms[0] + hms[1]
            
        elif len( hms ) ==3:
            
            duration = 3600 * hms[0] + 60 * hms[1] + hms[2]
            
        
        if duration == 0:
            
            return ( None, None )
            
        
        file_duration = duration + start_offset
        stream_duration = duration
        
        return ( file_duration, stream_duration )
        
    except:
        
        raise HydrusExceptions.MimeException( 'Error reading duration!' )
示例#4
0
def ParseFFMPEGVideoLine( lines ):
    
    # get the output line that speaks about video
    # the ^\sStream is to exclude the 'title' line, when it exists, includes the string 'Video: ', ha ha
    lines_video = [ l for l in lines if re.search( r'^\s*Stream', l ) is not None and 'Video: ' in l and not ( 'Video: png' in l or 'Video: jpg' in l ) ] # mp3 says it has a 'png' video stream
    
    if len( lines_video ) == 0:
        
        raise HydrusExceptions.MimeException( 'Could not find video information!' )
        
    
    line = lines_video[0]
    
    return line
示例#5
0
def ParseFFMPEGMimeText( lines ):
    
    try:
        
        ( input_line, ) = [ l for l in lines if l.startswith( 'Input #0' ) ]
        
        # Input #0, matroska, webm, from 'm.mkv':
        
        text = input_line[10:]
        
        mime_text = text.split( ', from' )[0]
        
        return mime_text
        
    except:
        
        raise HydrusExceptions.MimeException( 'Error reading mime!' )
示例#6
0
def ParseFFMPEGVideoResolution( lines ):
    
    try:
        
        line = ParseFFMPEGVideoLine( lines )
        
        # get the size, of the form 460x320 (w x h)
        match = re.search(" [0-9]*x[0-9]*(,| )", line)
        
        resolution = list(map(int, line[match.start():match.end()-1].split('x')))
        
        sar_match = re.search( " SAR [0-9]*:[0-9]* ", line )
        
        if sar_match is not None:
            
            # ' SAR 2:3 '
            sar_string = line[ sar_match.start() : sar_match.end() ]
            
            # '2:3'
            sar_string = sar_string[5:-1]
            
            ( sar_w, sar_h ) = sar_string.split( ':' )
            
            ( sar_w, sar_h ) = ( int( sar_w ), int( sar_h ) )
            
            ( x, y ) = resolution
            
            x *= sar_w
            x //= sar_h
            
            resolution = ( x, y )
            
        
        return resolution
        
    except:
        
        raise HydrusExceptions.MimeException( 'Error parsing resolution!' )
示例#7
0
def GeneratePILImage( path ):
    
    try:
        
        pil_image = PILImage.open( path )
        
    except Exception as e:
        
        raise HydrusExceptions.MimeException( 'Could not load the image--it was likely malformed!' )
        
    
    if pil_image.format == 'JPEG' and hasattr( pil_image, '_getexif' ):
        
        try:
            
            exif_dict = pil_image._getexif()
            
        except:
            
            exif_dict = None
            
        
        if exif_dict is not None:
            
            EXIF_ORIENTATION = 274
            
            if EXIF_ORIENTATION in exif_dict:
                
                orientation = exif_dict[ EXIF_ORIENTATION ]
                
                if orientation == 1:
                    
                    pass # normal
                    
                elif orientation == 2:
                    
                    # mirrored horizontal
                    
                    pil_image = pil_image.transpose( PILImage.FLIP_LEFT_RIGHT )
                    
                elif orientation == 3:
                    
                    # 180
                    
                    pil_image = pil_image.transpose( PILImage.ROTATE_180 )
                    
                elif orientation == 4:
                    
                    # mirrored vertical
                    
                    pil_image = pil_image.transpose( PILImage.FLIP_TOP_BOTTOM )
                    
                elif orientation == 5:
                    
                    # seems like these 90 degree rotations are wrong, but fliping them works for my posh example images, so I guess the PIL constants are odd
                    
                    # mirrored horizontal, then 90 CCW
                    
                    pil_image = pil_image.transpose( PILImage.FLIP_LEFT_RIGHT ).transpose( PILImage.ROTATE_90 )
                    
                elif orientation == 6:
                    
                    # 90 CW
                    
                    pil_image = pil_image.transpose( PILImage.ROTATE_270 )
                    
                elif orientation == 7:
                    
                    # mirrored horizontal, then 90 CCW
                    
                    pil_image = pil_image.transpose( PILImage.FLIP_LEFT_RIGHT ).transpose( PILImage.ROTATE_270 )
                    
                elif orientation == 8:
                    
                    # 90 CCW
                    
                    pil_image = pil_image.transpose( PILImage.ROTATE_90 )
                    
                
            
        
    
    if pil_image is None:
        
        raise Exception( 'The file at ' + path + ' could not be rendered!' )
        
    
    return pil_image
示例#8
0
def GetFileInfo(path, mime=None, ok_to_look_for_hydrus_updates=False):

    size = os.path.getsize(path)

    if size == 0:

        raise HydrusExceptions.SizeException('File is of zero length!')

    if mime is None:

        mime = GetMime(
            path, ok_to_look_for_hydrus_updates=ok_to_look_for_hydrus_updates)

    if mime not in HC.ALLOWED_MIMES:

        if mime == HC.TEXT_HTML:

            raise HydrusExceptions.MimeException(
                'Looks like HTML -- maybe the client needs to be taught how to parse this?'
            )

        elif mime == HC.APPLICATION_UNKNOWN:

            raise HydrusExceptions.MimeException('Unknown filetype!')

        else:

            raise HydrusExceptions.MimeException('Filetype is not permitted!')

    width = None
    height = None
    duration = None
    num_frames = None
    num_words = None

    if mime in (HC.IMAGE_JPEG, HC.IMAGE_PNG, HC.IMAGE_GIF, HC.IMAGE_WEBP,
                HC.IMAGE_TIFF, HC.IMAGE_ICON):

        ((width, height), duration,
         num_frames) = HydrusImageHandling.GetImageProperties(path, mime)

    elif mime == HC.APPLICATION_FLASH:

        ((width, height), duration,
         num_frames) = HydrusFlashHandling.GetFlashProperties(path)

    elif mime in (HC.IMAGE_APNG, HC.VIDEO_AVI, HC.VIDEO_FLV, HC.VIDEO_WMV,
                  HC.VIDEO_MOV, HC.VIDEO_MP4, HC.VIDEO_MKV, HC.VIDEO_REALMEDIA,
                  HC.VIDEO_WEBM, HC.VIDEO_MPEG):

        ((width, height), duration,
         num_frames) = HydrusVideoHandling.GetFFMPEGVideoProperties(path)

    elif mime == HC.APPLICATION_PDF:

        num_words = HydrusDocumentHandling.GetPDFNumWords(
            path)  # this now give None until a better solution can be found

    elif mime == HC.APPLICATION_PSD:

        (width, height) = HydrusImageHandling.GetPSDResolution(path)

    elif mime in HC.AUDIO:

        ffmpeg_lines = HydrusVideoHandling.GetFFMPEGInfoLines(path)

        (file_duration_in_s, stream_duration_in_s
         ) = HydrusVideoHandling.ParseFFMPEGDuration(ffmpeg_lines)

        duration = int(file_duration_in_s * 1000)

    if mime in HC.MIMES_THAT_DEFINITELY_HAVE_AUDIO:

        has_audio = True

    elif mime in HC.MIMES_THAT_MAY_HAVE_AUDIO:

        has_audio = HydrusAudioHandling.VideoHasAudio(path)

    else:

        has_audio = False

    if width is not None and width < 0:

        width *= -1

    if height is not None and height < 0:

        width *= -1

    if duration is not None and duration < 0:

        duration *= -1

    if num_frames is not None and num_frames < 0:

        num_frames *= -1

    if num_words is not None and num_words < 0:

        num_words *= -1

    return (size, mime, width, height, duration, num_frames, has_audio,
            num_words)
示例#9
0
def ParseFFMPEGFPS( first_second_lines ):
    
    try:
        
        line = ParseFFMPEGVideoLine( first_second_lines )
        
        # get the frame rate
        
        possible_results = set()
        
        match = re.search("( [0-9]*.| )[0-9]* tbr", line)
        
        if match is not None:
            
            tbr = line[match.start():match.end()].split(' ')[1]
            
            tbr_fps_is_likely_garbage = match is None or tbr.endswith( 'k' ) or float( tbr ) > 144
            
            if not tbr_fps_is_likely_garbage:
                
                possible_results.add( float( tbr ) )
                
            
        
        #
        
        match = re.search("( [0-9]*.| )[0-9]* fps", line)
        
        if match is not None:
            
            fps = line[match.start():match.end()].split(' ')[1]
            
            fps_is_likely_garbage = match is None or fps.endswith( 'k' ) or float( fps ) > 144
            
            if not fps_is_likely_garbage:
                
                possible_results.add( float( fps ) )
                
            
        
        num_frames_in_first_second = ParseFFMPEGNumFramesManually( first_second_lines )
        
        confident = len( possible_results ) <= 1
        
        if len( possible_results ) == 0:
            
            fps = num_frames_in_first_second
            confident = False
            
        else:
            
            # in some cases, fps is 0.77 and tbr is incorrectly 20. extreme values cause bad results. let's default to slowest, but test our actual first second for most legit-looking
            
            sensible_first_second = num_frames_in_first_second > 1
            
            fps = min( possible_results )
            
            fps_matches_with_first_second = False
            
            for possible_fps in possible_results:
                
                if num_frames_in_first_second - 1 <= possible_fps and possible_fps <= num_frames_in_first_second + 1:
                    
                    fps = possible_fps
                    
                    fps_matches_with_first_second = True
                    
                    break
                    
                
            
            confident = sensible_first_second and fps_matches_with_first_second
            
        
        if fps is None or fps == 0:
            
            fps = 1
            confident = False
            
        
        return ( fps, confident )
        
    except:
        
        raise HydrusExceptions.MimeException( 'Error estimating framerate!' )
示例#10
0
def GetFFMPEGVideoProperties( path, force_count_frames_manually = False ):
    
    first_second_lines = GetFFMPEGInfoLines( path, count_frames_manually = True, only_first_second = True )
    
    ( has_video, video_format ) = ParseFFMPEGVideoFormat( first_second_lines )
    
    if not has_video:
        
        raise HydrusExceptions.MimeException( 'File did not appear to have a video stream!' )
        
    
    resolution = ParseFFMPEGVideoResolution( first_second_lines )
    
    ( file_duration_in_s, stream_duration_in_s ) = ParseFFMPEGDuration( first_second_lines )
    
    # this will have to be fixed when I add audio, and dynamically accounted for on dual vid/audio rendering
    duration = stream_duration_in_s
    
    ( fps, confident_fps ) = ParseFFMPEGFPS( first_second_lines )
    
    if duration is None and not confident_fps:
        
        # ok default to fall back on
        ( fps, confident_fps ) = ( 24, True )
        
    
    if fps is None or fps == 0:
        
        fps = 1
        
    
    if duration is None:
        
        force_count_frames_manually = True
        
        num_frames_inferrence_likely_odd = True # i.e. inferrence not possible!
        
    else:
        
        num_frames_estimate = int( duration * fps )
        
        # if file is big or long, don't try to force a manual count when one not explicitly asked for
        # we don't care about a dropped frame on a 10min vid tbh
        num_frames_seems_ok_to_count = num_frames_estimate < 2400
        file_is_ok_size = os.path.getsize( path ) < 128 * 1024 * 1024
        
        if num_frames_seems_ok_to_count and file_is_ok_size:
            
            last_frame_has_unusual_duration = num_frames_estimate != duration * fps
            
            unusual_video_start = file_duration_in_s != stream_duration_in_s
            
            if not confident_fps or last_frame_has_unusual_duration or unusual_video_start:
                
                force_count_frames_manually = True
                
            
        
    
    if force_count_frames_manually:
        
        lines = GetFFMPEGInfoLines( path, count_frames_manually = True )
        
        num_frames = ParseFFMPEGNumFramesManually( lines )
        
        if duration is None:
            
            duration = num_frames / fps
            
        
    else:
        
        num_frames = int( duration * fps )
        
    
    duration_in_ms = int( duration * 1000 )
    
    return ( resolution, duration_in_ms, num_frames )