mplayer 이어서 보기 스크립트.

몇일동안 써 봤는데 별 문제는 없네요.


(뭐 생기면 고치면 되고...)


원리를 간단하게 설명하자면.


1,mplayer를 터미널상에서 재생시키면, 현재 재생중인 위치가 가장 마지막 위치에 나옵니다.


2, 재생전에 로그파일에 정보가 있는지 확인하고, 있으면 그곳을 시작위치로 저장해서 mplayer를 재생.


3, 이후 계속 출력 메세지를 받아놓고,


4, mplayer가 종료되면 로그의 가장 마지막줄을 읽어서 끝난 위치를 알아내는 거죠.


5, 그리고 난 다음, 로그파일에 정보가 없으면 새로운 로그 라인에 (기록초, 파일경로) 추가, 있으면 수정합니다.


(사실 원래는 중간에 메세지를 가로채서 파이프로 보내려고 했습니다만, 아무래도 뻘짓같아서...  포기하고 있다가 AUR에 mplayer-resumer(https://aur.archlinux.org/packages/mplayer-resumer/) 패키지를 보고 힌트를 얻어서 작성. (대체 왜 나는 쓸데없이 복잡한 방법을 생각해 냈을까?)


동영상 길이도 검사해서 맨뒷부분에서 exit시에는 기록을 하지 않게 만들어 놓았습니다.


저는 mplayer를 slave mode(외부에서 파이프로 mplayer 제어) 로도 사용하기 때문에 파이프를 설정하는 옵션도 들어갔습니다만, 필요없으면 없애주세요.


길이만 길다뿐이지 별거 아닙니다. 그럼 코드.


#!/bin/bash


# 필요 패키지 : bash, mplayer, ffmpeg, perl, 기타 쉘 관련 유틸리티
MPLAYER_PATH="/home/lowid/bin/mplayer"
MPLAYER_PIPE_PATH="/tmp/mplayer_pipe"
MPLAYER_LOG_FILEPATH="/share/ani/.mplayer_position.log"

# 키 후킹법 => 실패.
#while [ 1 ];do read -n 1 CMD >/dev/null;HEX="$(echo -n "$CMD" | hexdump -e \"%d\")";echo "key_down_event $HEX" > /tmp/mplayer_pipe;done
export MPLAYER_STATUS_OK_END=0
export MPLAYER_STATUS_OK_PLAYING=1
export MPLAYER_ERROR_NOT_PLAY_FILE=2
export MPLAYER_ERROR_LOG_POSITION_GET=3
export MPLAYER_ERROR_PLAYING=4

# mplayer에서 재생할 파일경로와 옵션을 분리해 냅니다
# 주의 : 각 인자를 확장자를 기준으로 분류합니다. (파싱 오류 가능성 있음!)
function Option_Parse()
{
    local Param=""
    unset OPTION_PARSE_RETURN_FILEPATH
    unset OPTION_PARSE_RETURN_OPTIONS

    for Param in "$@";do
        if [ "$(echo "$Param" | grep -E "\.mpg|\.wvx|\.rmvb|\.avi|\.mkv|\.wmv|\.mp4")" ];then

            OPTION_PARSE_RETURN_FILEPATH="$(realpath "$Param")"
        else
            OPTION_PARSE_RETURN_OPTIONS=""$OPTION_PARSE_RETURN_OPTIONS" "$Param""
        fi
    done
}

function Mplayer_Play_Duration()
{
    local Play_filepath="$1"
    local Play_file_duration=""
    local Hour=0
    local Minute=0
    local Second=""
    unset MPLAYER_PLAY_DURATION_RETURN_SECOND

    if [ ! -f "$Play_filepath" ];then
        return 1
    fi

    Play_file_duration="$(ffmpeg -i "$Play_filepath" 2>&1 | grep '^\s*Duration: [0-9][0-9]:[0-9][0-9]:[0-9][0-9]' | perl -pe 's/^.*?\: //g,s/\..*$//g')"
    Hour="$(echo "$Play_file_duration"   | cut -d ':' -f1 | perl -pe 's/^0//g')"
    Minute="$(echo "$Play_file_duration" | cut -d ':' -f2 | perl -pe 's/^0//g')"
    Second="$(echo "$Play_file_duration" | cut -d ':' -f3 | perl -pe 's/^0//g')"

    MPLAYER_PLAY_DURATION_RETURN_SECOND=$(($Hour * 60 * 60 + $Minute * 60 + $Second))

    return 0
}

function Mplayer_Log_Position_Get()
{
    local Play_filepath="$1"
    local Log_filepath="$2"
    local Log_play_file_line=""
    MPLAYER_POSITION_GET_RETURN_PLAY_SECOND=0

    if [ ! -f "$Play_filepath" ];then
        return 1
    fi

    if [ ! -f "$Log_filepath" ];then
        touch "$Log_filepath"
    fi

    Log_play_file_line="$(fgrep -n "$Play_filepath" "$Log_filepath" | cut -d ':' -f1 2>/dev/null)"
   
    if [ -n "$Log_play_file_line" ];then
        MPLAYER_POSITION_GET_RETURN_PLAY_SECOND="$(sed -n "$Log_play_file_line"'p' "$Log_filepath" | cut -d ' ' -f1)"
    fi

    return 0
}

function Mplayer_Log_Position_Set()
{
    local Play_filepath="$1"
    local Play_second="$2"
    local Log_filepath="$3"
    local Log_play_file_line=""
    local Log_play_file_data=""
    local Temp_filepath="/tmp/mpp_$RANDOM"

    if [ ! -f "$Play_filepath" ];then
        return 1
    fi

    if [ -z "$Play_second" ];then
        return 2
    fi

    if [ ! -f "$Log_filepath" ];then
        return 3
    fi

    Log_play_file_line="$(fgrep -n "$Play_filepath" "$Log_filepath" | cut -d ':' -f1 2>/dev/null)"
   
    # 없으면 뒤에 쓰고, 없으면 치환.
    if [ -z "$Log_play_file_line" ];then
        echo "$Play_second" "$Play_filepath" >> "$Log_filepath"
    else
        Log_play_file_data=""$Log_play_file_line"c "$Play_second" "$Play_filepath""
        sed -e "$Log_play_file_data" "$Log_filepath" > "$Temp_filepath"
        mv -f "$Temp_filepath" "$Log_filepath" 2>/dev/null
    fi

    return 0
}

function Mplayer_Log_Position_Delete()
{
    local Play_filepath="$1"
    local Log_filepath="$2"
    local Log_play_file_line=""
    local Temp_filepath="/tmp/mpp_$RANDOM"

    if [ ! -f "$Play_filepath" ];then
        return 1
    fi

    if [ ! -f "$Log_filepath" ];then
        return 2
    fi

    Log_play_file_line="$(fgrep -n "$Play_filepath" "$Log_filepath" | cut -d ':' -f1 2>/dev/null)"

    if [ -z "$Log_play_file_line" ];then
        return 3
    fi

    sed -e "$Log_play_file_line"'d' "$Log_filepath" > "$Temp_filepath"
    mv -f "$Temp_filepath" "$Log_filepath" 2>/dev/null

    return 0
}

function Mplayer_Play_Continue()
{
    local Options="$1"
    local Play_filepath="$2"
    local Play_position_second="$3"
    local Play_backward_second="$4"
    local Mplayer_end_second=""
    local Mplayer_start_position_second=""
    unset MPLAYER_PLAY_RETURN_POSITION

    if [ ! -f "$Play_filepath" ];then
        return 1
    fi

    if [ -z "$Play_position_second" ];then
        return 2
    fi

    if [ -z "$Play_backward_second" ];then
        return 3
    fi

    # mplayer 경로는 전역임 주의, -ss옵션에 0이 들어가도 상관 없다
    Mplayer_end_second="$("$MPLAYER_PATH" $Options -ss "$Play_position_second" "$Play_filepath" 2>/dev/null | strings | tail -2 | egrep '^A:' | perl -pe 's/^A:\s*//g,s/ V:.*//g')"

    if [ -z "$Mplayer_end_second" ];then
        return 4
    fi

    Mplayer_start_position_second="$(echo "$Mplayer_end_second" | cut -d '.' -f1)"
    if [ -z "$Mplayer_start_position_second" ];then
        return 5
    fi

    MPLAYER_PLAY_RETURN_POSITION="$(($Mplayer_start_position_second - $Play_backward_second))"
}

function Mplayer_Pipe()
{
    local Play_filepath="$1"
    local Pipe_path="$2"
    local Pipe_option=""
    unset MPLAYER_PIPE_RETURN_OPTION

    # mkv는 fifo 사용이 불가능한듯함
    if [ -z "$(echo "$Play_filepath" | grep \.mkv)" ];then
        if [ "$(file "$Pipe_path" | cut -d ' ' -f2)" != "fifo" ];then
            mkfifo "$Pipe_path"
        fi

        Pipe_option="-slave -input file="$Pipe_path""
    fi

    MPLAYER_PIPE_RETURN_OPTION="$Pipe_option"
}

function Mplayer()
{
    local Play_filepath=""
    local Options=""
    local Played_position_second=""
    local Restart_backword_second=2
    local Playing_position_second=""
    local Playing_file_duration=""
    local Restart_last_second=10
    local Return_succeed_value=0

    Option_Parse "$@"
    Play_filepath="$OPTION_PARSE_RETURN_FILEPATH"
    Options="$OPTION_PARSE_RETURN_OPTIONS"

    if [ -z "$Play_filepath" ];then
        return $MPLAYER_ERROR_NOT_PLAY_FILE
    fi

    Mplayer_Pipe "$Play_filepath" "$MPLAYER_PIPE_PATH"
    Options=""$Options" "$MPLAYER_PIPE_RETURN_OPTION""

    Mplayer_Log_Position_Get "$Play_filepath" "$MPLAYER_LOG_FILEPATH"
    if [ $? != 0 ];then
        return $MPLAYER_ERROR_LOG_POSITION_GET
    fi

    Played_position_second="$MPLAYER_POSITION_GET_RETURN_PLAY_SECOND"

    Mplayer_Play_Continue "$Options" "$Play_filepath" "$Played_position_second" "$Restart_backword_second"
    if [ $? != 0 ];then
        return $MPLAYER_ERROR_PLAYING
    fi

    Playing_position_second="$MPLAYER_PLAY_RETURN_POSITION"

    # 동영상의 재생 길이를 알때에만 기록을 한다.
    Mplayer_Play_Duration "$Play_filepath"
    if [ $? = 0 ];then
        Playing_file_duration="$MPLAYER_PLAY_DURATION_RETURN_SECOND"

        if [ $(($Playing_position_second + $Restart_last_second )) -lt "$Playing_file_duration" ];then
            Mplayer_Log_Position_Set "$Play_filepath" "$Playing_position_second" "$MPLAYER_LOG_FILEPATH"
            Return_succeed_value=$MPLAYER_STATUS_OK_PLAYING
        else
            Mplayer_Log_Position_Delete "$Play_filepath" "$MPLAYER_LOG_FILEPATH"
            Return_succeed_value=$MPLAYER_STATUS_OK_END
        fi
    fi

    return $Return_succeed_value
}


따로 설정할수 있는 부분은 전역으로 빼놨으니까 수정하시기 편할거에요.


ps; ~/.bashrc에 다음과 같이 쓰시길 추천. 아니면 따로 빼서 쓰시던지요.

alias mp='. /path/Mplayer.sh;Mplayer'