lrcdis (外挂式显歌词脚本)

https://linuxtoy.org/archives/lrcdis.html
http://www.oschina.net/p/lrcdis/
http://code.google.com/p/lrcdis/downloads/list


#!/bin/bash
# Name:     lrcdis (外挂式显歌词脚本)
# License:  GPLv3
# Credit:   xiooli,solcomo,bones7456,oldherl  (2008-2009)
# Encoding: UTF-8
# Thanks to: 搜狗歌词搜索

Version=090908
#是否开启调试信息
Debug=0
Conf_file=~/.config/lrcdis.conf
Cookie_file=/dev/shm/lrcdis-cookie-file-$USER-"`date +"%s.%N"`-$RANDOM"

#=========================定义函数=============================

INIT() {
    #程序初始化函数

    #没有参数,正确执行如果没有配置文件则会产生配置文件并加载配置,有配置文件则加载配置文件

    [ -f "$Conf_file" ] || cat<< EOF > "$Conf_file"
#===================LRCDIS-CONFIG================
#保存lrc文件的文件夹
Lrcdir=~/.lyrics
#显示模式: osd|notify|fifo|title|cli|kdialog|echo (将歌词输出到一个管道文件,以便用其他命令访问)
Dismode=osd
#播放器类型: mpd|mocp|muine|audacious|juk|amarok|exaile|gmusicbrowser|quodlibet|qmmp|rhythmbox|banshee|audacious2|xmms2
Player=rhythmbox
#获取歌曲名称方式: id3(id3标签方式)|filename(文件名方式)
Readname=id3
#自身时间发生器产生时间的间隔(最好小于1秒)
Sleeptm=0.6
#======================[cli]=====================
#命令行显示时的参数
#一屏显示行数
Row=10
#歌曲标题的颜色(见最后的颜色定义)
Titlecolor=green
#歌曲歌词的颜色(见最后的颜色定义)
Lyricscolor=blue
#==============[osd, notify & kdialog]=============
#osd显示时的参数
#osd显示位置:top, bottom
Vposition=top
#osd对齐位置:left, right, center
Halignment=center
#osd最长显示时间(超过则隐藏,单位毫秒?)
Timeout=10000
#span size
Size=20000
#字体颜色: green|white|blue|yellow|grey|...
#注意,这个颜色与cli模式的颜色不是一回事
Foreground=green
#动画效果: off, on
Animations=off
#是否加新行,以免挡住panel等: off|on
Addnewline=off
#notify模式的图标文件名(只支持绝对路径)
NotifyIcon=""
#kdialog显示的timeout
Ktimeout=5
#====================[lrc]======================
#从何处下载歌词: SOGOU_URI,BAIDU_URI,QIANQIAN_URI
Uri=SOGOU_URI
#是否检查歌词文件,yes/no,yes将会检查歌词文件是否正确,若不正确则重新下载歌词
Checklrc=yes
#下载歌词的最大重试次数,不宜过大
Maxdowntimes=4
#================================================
#定义颜色(仅在cli模式下使用)
black="30"
red="31"
green="32"
yellow="33"
blue="34"
magenta="35"
cyan="36"
white="37"
#======================END========================
EOF

    . "$Conf_file"
}

DEBUGECHO() {
    [ "$Debug" = 1 ] && echo "  $@" >&2
}

DO_NAME() {
     1 argument, the filename

     removing .mp3/.wma/.ogg/.ape/.flac etc.
    local    a
    a="`basename "$1"`"
    #理论上说以 . 分隔的最后那部分是扩展名
    a="${a%.[a-zA-Z0-9]*}"
    #a="${a/%.mp3/}"; a="${a/%.MP3/}"
    #a="${a/%.wma/}"; a="${a/%.WMA/}"
    #a="${a/%.ogg/}"; a="${a/%.OGG/}"
    #a="${a/%.ape/}"; a="${a/%.APE/}"
    #a="${a/%.flac/}"; a="${a/%.FLAC/}"
    # 去掉前置的一位或两位数字和".",一般是专辑中的序号
    a="${a/#[0-9]./}"; a="${a/#[0-9][0-9]./}"
    echo "$a"
}

USAGE() {
    #显示帮助信息的函数

    #没有参数,执行显示帮助信息

    cat << EOF
lrcdis $Version (http://code.google.com/p/lrcdis)
用法: $0 [选项]
选项:

    -C, --config <配置文件路径>
        指定配置文件 (默认: ~/.config/lrcdis.conf)
    -s, --save-dir <保存目录>
        指定歌词的保存目录 (默认: ~/.lyrics)
    -d, --download <歌曲名>
        仅下载歌词, 保存至歌词目录
    -E, --edit-conf
        进入编辑配置文件的模式
    -p, --player <播放器>
        优先检查此播放器,如果此播放器未运行则继续检查其他播放器
    -m, --mode <显示模式>
        以 <显示模式> 显示歌词,详细参数见下面:
    -m osd
        以osd模式显示歌词(needs gnome-osd)
    -m notify
        以notify模式显示歌词(needs notify-send)
    -m fifo
        将歌词输出到管道文件/dev/shm/lrcfifo
    -m title
        将歌词输出到终端的标题中(支持大多数终端)
    -m kdialog
        将歌词以kdialog的通知模式输出
    -m echo
        将歌词以普通echo模式输出
    -m cli
        以cli模式显示歌词
    -D, --debug
        Debug mode
    -h, --help
        显示本帮助信息并退出
    -v, --version
        显示版本号并退出
备注:不加任何选项则以 $Conf_file 为配置文件初始化运行
EOF
}

PLAYER_USERS() {
    #一个参数,播放器进程名,返回该进程可能对应的用户ID.
    id -u | tr -d "\n"
    if [ "$1" = "mpd" ];then
        echo -n ",0" #mpd以root运行算合法
        if id -u mpd >/dev/null 2>&1 ;then
            echo -n ","
            id -u mpd | tr -d "\n" #mpd以mpd用户运行算合法
        fi
    fi
}

PLAYER_RUNNING(){
    #判断播放器进程是否存在
    #一个参数, 播放器名
    #返回0表示进程存在,1表示不存在
    {
    if [ "$1" = "quodlibet" ]; then
        pgrep -fx "python `which quodlibet`" -u "`PLAYER_USERS "$1"`" && return 0
    elif [ "$1" = "banshee" ]; then
        pgrep -x "banshee-1" -u "`PLAYER_USERS "$1"`" && return 0
    elif [ "$1" = "xmms2" ]; then
        pgrep -x "xmms2d" -u "`PLAYER_USERS "$1"`" && return 0
    else
        pgrep -x "$1" -u "`PLAYER_USERS "$1"`" && return 0
    fi
    return 1
    } >/dev/null 2>&1
}

CHECK_PLAYER() {
    #自动检查播放器类型的函数

    #没有参数,返回播放器类型
    
    local players i

    #所有被支持的播放器都保存下面的字符串里面
    #gmusicbrowser需要perl-net-dbus让其支持dbus通信才可以
    players="rhythmbox audacious juk amarok amarokapp exaile banshee gmusicbrowser quodlibet qmmp mocp mpd muine mplayer audacious2 xmms2"
    if PLAYER_RUNNING "$Player";then
        echo -n "$Player"
    else
        for i in $players;do
            DEBUGECHO "$Player   $i"
            if PLAYER_RUNNING "$i";then
                echo -n "$i"|sed "s/amarokapp/amarok/"
                break
            fi
        done
    fi
}

BAIDU_URI() {
    #得到百度歌词文件的下载URL

    #两个参数,$1:gbk编码的歌曲名的urlencode; $2:提取的下载的链接序数
    local nm page items urlstmp
    page="$((${2:-1}/10))"
    items="${#Urls[@]}";[ "$items" = "0" ] && items="1"
    Lrcurl0="http://www.baidu.com/s?&wd=${1}+filetype:lrc&pn=${page}"
    [ "$Lrcurl" != "$Lrcurl0" ] && Lrcurl="$Lrcurl0" \
    && Htm="`wget "$Lrcurl"  -q -T 10 -O- 2>/dev/null | iconv -f gbk`"
    if [ ! "${Urls[@]}" ] || [ "$2" -gt "$items" ]; then
           ((page++))
           Urls=($(echo $Htm|grep -o "http[^<>]*\.lrc"))
           fi
    DEBUGECHO "BAIDU_URI $Lrcurl0 ${Urls[@]}"
    nm="$((${2:-1}%$items))";((nm--));[ "$nm" -lt 0 ] && nm=0
    [ "${Urls[$nm]}" = "" ] || echo -n "${Urls[$nm]}"
}

SOGOU_URI() {
    #得到搜狗歌词文件的下载URL

    #两个参数,$1:gbk编码的歌曲名的urlencode; $2:提取的下载的链接序数
    #(用于第一个链接的歌词不对时重新下载下一个链接)

    local a page nm
    page="$((${2:-1}/5))"
    nm="$((${2:-1}%5))"
    #先获取一个cookie
    [ ! -s "$Cookie_file" ] && \
    wget --keep-session-cookies --save-cookies "$Cookie_file" "http://mp3.sogou.com" -O- >/dev/null 2>&1
    Lrcurl0="http://mp3.sogou.com/gecisearch.so?query=${1}&page=${page}"
    [ "$Lrcurl" != "$Lrcurl0" ] && Lrcurl="$Lrcurl0" \
    && Htm="`wget --load-cookies "$Cookie_file" --save-cookies "$Cookie_file" "$Lrcurl" -q -T 10 -O- 2>/dev/null | iconv -f gbk`"
    a="`echo "$Htm" | grep -Fim"${nm}" "downlrc"|awk -F\\\" '{print $2}'|tail -n1`"
    DEBUGECHO "A $Lrcurl0 $a"
    [ "$a" = "" ] || echo -n 'http://mp3.sogou.com/'"$a"
}

QIANQIAN_URI(){
    #得到千千歌词文件的下载URL
    local htm
    DEBUGECHO "$1 | $2"
    htm="`wget "http://www.qianqian.com/lrcresult.php?qfield=1&pageflag=1&qword="$1"" -q -T 10 -O- 2>/dev/null`"
    a="http://www.qianqian.com/""`echo "$htm" | grep -Fim1 lrcresult_frame | awk -F\\\" '{print $6}'`"
    DEBUGECHO "A $a"
    b="`wget "$a" --referer=www.qianqian.com -O- -q | grep -Fim1 downfromlrc | awk -F\\\" '{print $6}'`"
    DEBUGECHO "B $b"
    if [ "$b" != "" ]; then
        echo -n 'http://www.qianqian.com/'"$b";
    else
        htm="`wget "http://www.qianqian.com/lrcresult.php?qfield=3&pageflag=1&qword="$1"" -q -T 10 -O- 2>/dev/null`"
        a="http://www.qianqian.com/""`echo "$htm" | grep -Fim1 lrcresult_frame | awk -F\\\" '{print $6}'`"
        DEBUGECHO "A $a"
        b="`wget "$a" --referer=www.qianqian.com -O- -q | grep -Fim1 downfromlrc | awk -F\\\" '{print $6}'`"
        DEBUGECHO "B $b"
        if [ "$b" != "" ]; then
            echo -n 'http://www.qianqian.com/'"$b";
        else
            htm="`wget "http://www.qianqian.com/lrcresult.php?qfield=3&pageflag=1&qword="$2"" -q -T 10 -O- 2>/dev/null`"
            a="http://www.qianqian.com/""`echo "$htm" | grep -Fim1 lrcresult_frame | awk -F\\\" '{print $6}'`"
            DEBUGECHO "A $a"
            b="`wget "$a" --referer=www.qianqian.com -O- -q | grep -Fim1 downfromlrc | awk -F\\\" '{print $6}'`"
            DEBUGECHO "B $b"
            if [ "$b" != "" ]; then
                echo -n 'http://www.qianqian.com/'"$b";
            fi
        fi
    fi
}

DOWNLRC() {
    #下载歌词的函数

    #参数两个,$1 (str): 歌曲名字; $2 (int):欲提取的下载的链接序数
    #没有返回值,正确执行会下载一个 lrc 文件至 Lrcdir

    local nm link full_link gb file

    nm="$1"
    [ ! -d "$Lrcdir" ] && mkdir -p "$Lrcdir"
    file="$Lrcdir/$nm.lrc"
    
    #将歌曲名字转换为urlencode,utf-8的locale必须先转换为gbk的编码
      gb="$(echo -n "$nm" | iconv -c -t gbk | od -t x1 -A n -w1000|tr " " "%")"

    #从歌词搜索里面找出当前歌曲的歌词下载页,默认从搜狗下
      link="`eval "\${Uri:-SOGOU_URI} \"\$gb\" \"\$2\""`"
    DEBUGECHO "Link: $link"
    [ "$link" ] && {
        if [ "$Uri" = "QIANQIAN_URI" ];then
            #千千的需要referer,且不需要转码.
              wget "$link" --referer=www.qianqian.com -T 5 -t 2 -q -O - | tr -d "\r" > "$file"
          else
            wget --load-cookies "$Cookie_file" "$link" -T 5 -t 2 -q -O -|iconv -f gbk -c | tr -d "\r" > "$file"
        fi
        { echo ''; cat "$file"; echo '';} > /tmp/lrcdisTMP.xml
        xmllint /tmp/lrcdisTMP.xml --encode UTF-8 2>/dev/null > /tmp/lrcdisTMP.lrc && {
            cat /tmp/lrcdisTMP.lrc | head -n -1 | tail -n +3 > "$file"
        }
        rm /tmp/lrcdisTMP* &>/dev/null
    }
}

CHECK_LRC() {
    #检查歌词文件是否正确
    
    #参数三个,$1: 歌词文件路径; $2: 当前歌曲的标题(包含题目和/或艺人)
    # $3:检查是否放水(yes严格执行,no则总返回合格)
    #正确返回字符串LRC-RIGHT,反之LRC-WRONG
    
    local ti0 ti1 ar0 ar1 judge
    if [ "${3:-yes}" = "yes" ];then
        ti0="`cat "$1"|grep -o "\[ti:[^\[]*\]"|tr -d "[]"|tr [:upper:] [:lower:]`";ti0="${ti0/ti:}"
        ar0="`cat "$1"|grep -o "\[ar:[^\[]*\]"|tr -d "[]"|tr [:upper:] [:lower:]`";ar0="${ar0/ar:}"
        ti1="${2/}
            tmptm=${tm/}
        tmptm=${tm/Dismode=$Dismode/" "$Conf_file"
    ;;
    *)
    echo "无效的显示模式: $Dismode ,用默认的 echo 模式替代." >&2
    ;;
esac
Player="`CHECK_PLAYER`"
if [ "$Player" ];then
    sed -i "s/^Player=.*/Player=$Player/" "$Conf_file"
else
    DIS_WORDS E "未发现被支持的播放器进程!"
    [ "$Dismode" = cli ] && echo
    exit
fi
Title="`GET_TITLE $Player $Readname`"
pltm="`GET_PLTM $Player`"
#TODO 目前PlayerStat的值只有Unknow和Playing,以后可以考虑加入Pause等状态.
PlayerStat="Unknow"

GET_STAT |while read line0;do
    [ "$Dismode" = "fifo" -a -p "/dev/shm/lrcfifo" ] && echo -n "">/dev/shm/lrcfifo #防止读lrcfifo时等待 amoblin 4.14 9:49 #非fifo模式不必要生成该文件 bones7456
    DEBUGECHO "GET_STAT: $line0<<"
    [ "$line0" = "$line1" ] && continue
    line1="$line0"
    read op arg <<< "$line0"
    [ "$op" = "Error" ] && DIS_WORDS E "$arg"
    [ "$op" = "Title" ] && [ "$title" != "$arg" ] && [ "$arg" != "" ] && {
        title="$arg"
        Title="$arg"
        DIS_WORDS T "$Title";
        CHECK_AND_DOWN_LRC "$Title" "$Checklrc"
        if [ -f "$Lrcdir/$Title.lrc" ]; then
            lrc="$(< "$Lrcdir/$Title.lrc")"
        else
            lrc=""
            DIS_WORDS E "未能下载歌词"
        fi
        #处理 offset
        offset="`echo $lrc|awk -F"offset:" '{gsub("].*$","",$2);print $2/1000}'`";offset=${offset:-0}
    }
    [ "$op" = "Time" ] && [ "$lrc" ] && {
        arg="${arg:-0}";
        arg="`eval "echo|awk '{print "$arg+$offset"}'"`"
        arg=${arg//.*/};arg=${arg:-0}
        arg="`printf '%.2d:%.2d' $(($arg/60)) $(($arg`))`"
         DISPLAY "$arg" "$lrc"
    }
done
posted @ 2014-08-18 16:29  李庆喜  阅读(270)  评论(0编辑  收藏  举报