发表于 2026年08月13日

MPC手机dlan推流音乐到mpd恢复到当前播放记录

现在先别重启

把下面 4 个命令的结果给我:

ps -o pid,ppid,lstart,cmd -p 804
systemctl list-units --type=service --all | grep -Ei 'mpd|autostart'
grep -R "mpd" /etc/systemd/system /etc/rc.local /usr/local/bin 2>/dev/null | head -100
mpc status
killall -9 mpd
sudo -u mpd /usr/bin/mpd /etc/mpd.conf
这就是问题根源。

现在先恢复 MPD,执行这几条

不要重启机器。

systemctl stop mpd-dlna-resume.service 2>/dev/null
systemctl disable mpd-dlna-resume.service 2>/dev/null
kill 804

sleep 2

systemctl reset-failed mpd.service
systemctl start mpd.service

sleep 3

systemctl status mpd --no-pager
mpc status
systemctl restart upmpdcli
这样应该恢复成:
mpd.service
   ↓
MPD :6600
   ↓
upmpdcli
   ↓
BubbleUPnP
ss -lntp | grep ':6600'
lsof -nP -iTCP:6600 -sTCP:LISTEN
ps aux | grep '[m]pd'
systemctl status mpd-manager.service --no-pager
如果 6600 已经没有输出

马上执行:

systemctl reset-failed mpd.service
systemctl start mpd.service
systemctl status mpd --no-pager
说明现在没有 MPD 进程。

第一步:启动 MPD

执行:

systemctl reset-failed mpd.service
systemctl start mpd.service
sleep 3
systemctl status mpd --no-pager

然后:

mpc status
如果正常,应该能看到当前歌曲和类似: 核心改变是:不再持续把 MPD 当前队列覆盖保存。DLNA 开始时只保存一次,DLNA 结束时只恢复这一次。恢复完成后,脚本不会再碰你的新队列。

  1. 替换 mpd-autostart.sh

直接执行:

cat > /usr/local/bin/mpd-autostart.sh <<'EOF'
#!/bin/bash

# ============================================================
# MPD 开机自动播放本地音乐
# 只负责开机播放一次
# 不参与 DLNA
# 不修改 /etc/mpd.conf
# ============================================================

export MPD_HOST="127.0.0.1"
export MPD_PORT="6600"

LOG="/var/log/mpd-autostart.log"

log() {
    echo "$(date '+%F %T') $1" >> "$LOG"
}

mkdir -p /var/log/mpd
mkdir -p /run/mpd
mkdir -p /var/lib/mpd/playlists
mkdir -p /var/lib/mpd/cache
mkdir -p /var/lib/mpd/tag_cache
mkdir -p /media/music

chown -R mpd:audio \
    /var/log/mpd \
    /run/mpd \
    /var/lib/mpd \
    /media/music

chmod -R 775 \
    /var/log/mpd \
    /run/mpd \
    /var/lib/mpd \
    /media/music

log "MPD 开机自动播放启动"

# MPD 由 systemd 管理,这里只等待它启动
systemctl start mpd

# 等待 MPD 可连接
for i in $(seq 1 20); do
    if mpc status >/dev/null 2>&1; then
        break
    fi
    sleep 1
done

if ! mpc status >/dev/null 2>&1; then
    log "MPD 无法连接,退出"
    exit 0
fi

# 更新本地音乐库
mpc update >/dev/null 2>&1 || true

# 给数据库一点时间
sleep 3

# 如果 MPD 已经有队列,就不重新建立
COUNT=$(mpc playlist 2>/dev/null | wc -l)

if [ "$COUNT" -gt 0 ]; then
    log "已有队列 ${COUNT} 首,不重新建立"
else

    # 没有队列才建立本地音乐队列
    mpc listall 2>/dev/null | mpc add >/dev/null 2>&1 || true

    COUNT=$(mpc playlist 2>/dev/null | wc -l)

    log "建立本地队列 ${COUNT} 首"
fi

# 播放设置
mpc repeat on >/dev/null 2>&1 || true
mpc random off >/dev/null 2>&1 || true

# 保持你原来的音量
mpc volume 100 >/dev/null 2>&1 || true

# 声卡音量
amixer sset Master 100% unmute 2>/dev/null || true
amixer sset PCM 100% unmute 2>/dev/null || true
amixer sset Line 100% unmute 2>/dev/null || true

# 有队列并且当前没有播放才开始
STATUS=$(mpc status 2>/dev/null)

if echo "$STATUS" | grep -q '\[playing\]'; then
    log "MPD 已经在播放,不干预"
elif [ "$COUNT" -gt 0 ]; then
    mpc play >/dev/null 2>&1 || true
    log "开始播放本地音乐"
fi

log "MPD 开机自动播放完成"

exit 0
EOF

chmod +x /usr/local/bin/mpd-autostart.sh
2. 替换 DLNA 恢复脚本

这一份是重点。

#!/bin/bash

# ============================================================
# MPD + DLNA + YouTube 网络流恢复系统
#
# 功能:
#
# 1. 本地播放每5秒保存队列和进度
# 2. DLNA开始时绝不覆盖本地队列
# 3. DLNA结束后直接播放下一首
# 4. 切换过程中临时静音,防止旧歌曲/第一首窜声
# 5. 下一首从0:00开始
# 6. 最后一首自动循环第一首
# 7. 网络流卡死自动检测
# 8. MPD无响应自动重启
# 9. 网络流卡死后自动恢复本地队列
# 10. 恢复过程中禁止保存状态
#
# ============================================================


# ============================================================
# 路径
# ============================================================

STATE="/var/lib/mpd-dlna-resume"

QUEUE="$STATE/queue.txt"

INFO="$STATE/info.txt"

LOG="/var/log/mpd-dlna-resume.log"


mkdir -p "$STATE"

touch "$LOG"


# ============================================================
# MPD
# ============================================================

export MPD_HOST="127.0.0.1"

export MPD_PORT="6600"


# ============================================================
# 全局状态
# ============================================================

DLNA=0

RESTORING=0

FAILCOUNT=0


# ============================================================
# 网络流卡死检测
#
# 连续多少次播放位置没有变化
#
# 主循环5秒一次
#
# 6次 = 30秒
# ============================================================

STREAM_STUCK_COUNT=0

STREAM_LAST_POSITION=""

STREAM_LAST_URI=""


STREAM_STUCK_LIMIT=6


# ============================================================
# 日志
# ============================================================

log() {

    echo "$(date '+%F %T') $*" >> "$LOG"

}


# ============================================================
# MPD 是否正常
# ============================================================

mpc_ok() {

    timeout 3 mpc status >/dev/null 2>&1

}


# ============================================================
# 获取当前URI
# ============================================================

get_uri() {

    timeout 3 mpc -f "%file%" current 2>/dev/null

}


# ============================================================
# 获取当前歌曲序号
# ============================================================

get_current() {

    timeout 3 mpc status 2>/dev/null |
        grep -oE '#[0-9]+/' |
        head -1 |
        tr -dc '0-9'

}


# ============================================================
# 获取当前播放时间
# ============================================================

get_position() {

    timeout 3 mpc status 2>/dev/null |
        grep -oE '[0-9]+:[0-9]+/' |
        head -1 |
        cut -d/ -f1

}


# ============================================================
# 获取MPD播放状态
#
# 返回:
#
# playing
# paused
# stopped
# ============================================================

get_state() {

    timeout 3 mpc status 2>/dev/null |
        grep -oE '\[(playing|paused|stopped)\]' |
        head -1 |
        tr -d '[]'

}


# ============================================================
# 判断网络流 / DLNA
# ============================================================

is_network_stream() {

    echo "$1" |
        grep -qiE \
        '^(http|https|rtsp|rtmp|mms|ffmpegpcmdecode|stream)'

}


# ============================================================
# 获取当前音量
# ============================================================

get_volume() {

    timeout 3 mpc volume 2>/dev/null |
        grep -oE '[0-9]+%' |
        head -1 |
        tr -dc '0-9'

}


# ============================================================
# 安全等待MPD
# ============================================================

wait_mpd() {

    for i in $(seq 1 15)
    do

        if mpc_ok; then
            return 0
        fi

        sleep 1

    done

    return 1

}


# ============================================================
# 保存本地状态
#
# 注意:
#
# DLNA:
#   不保存
#
# 恢复:
#   不保存
# ============================================================

save_local_state() {


    # --------------------------------------------------------
    # 恢复期间禁止保存
    # --------------------------------------------------------

    if [ "$RESTORING" = "1" ]; then

        return 1

    fi


    # --------------------------------------------------------
    # MPD必须正常
    # --------------------------------------------------------

    if ! mpc_ok; then

        return 1

    fi


    # --------------------------------------------------------
    # 获取当前URI
    # --------------------------------------------------------

    URI=$(get_uri)


    # --------------------------------------------------------
    # 网络流绝对不保存
    # --------------------------------------------------------

    if is_network_stream "$URI"; then

        return 1

    fi


    # --------------------------------------------------------
    # 队列数量
    # --------------------------------------------------------

    COUNT=$(timeout 5 mpc playlist 2>/dev/null | wc -l)


    if [ "$COUNT" -lt 2 ]; then

        return 1

    fi


    # --------------------------------------------------------
    # 当前歌曲
    # --------------------------------------------------------

    CURRENT=$(get_current)


    # --------------------------------------------------------
    # 当前播放位置
    # --------------------------------------------------------

    POSITION=$(get_position)


    FILE="$URI"


    [ -z "$CURRENT" ] && CURRENT=1

    [ -z "$POSITION" ] && POSITION="0:00"


    # ========================================================
    # 保存完整队列
    # ========================================================

    timeout 10 \
        mpc -f "%file%" playlist \
        > "$QUEUE.tmp" 2>/dev/null


    if [ ! -s "$QUEUE.tmp" ]; then

        rm -f "$QUEUE.tmp"

        return 1

    fi


    # --------------------------------------------------------
    # 第一首不能是网络流
    # --------------------------------------------------------

    FIRST=$(head -1 "$QUEUE.tmp")


    if is_network_stream "$FIRST"; then

        rm -f "$QUEUE.tmp"

        return 1

    fi


    # --------------------------------------------------------
    # 原子替换
    # --------------------------------------------------------

    mv "$QUEUE.tmp" "$QUEUE"


    # ========================================================
    # 保存信息
    # ========================================================

    cat > "$INFO.tmp" <<EOT
COUNT=$COUNT
CURRENT=$CURRENT
POSITION=$POSITION
FILE=$FILE
EOT


    mv "$INFO.tmp" "$INFO"


    log "保存本地队列:${COUNT}首 / 第${CURRENT}首 / ${POSITION}"


    return 0

}


# ============================================================
# 恢复本地队列
#
# MODE:
#
# normal
#   开机恢复原来的歌曲和位置
#
# next
#   DLNA结束
#   直接下一首
#
# ============================================================

restore_local_state() {


    MODE="$1"


    # ========================================================
    # 开启恢复保护
    # ========================================================

    RESTORING=1


    log "开始进入恢复保护模式"


    # ========================================================
    # 检查队列
    # ========================================================

    if [ ! -s "$QUEUE" ]; then

        log "没有保存的本地队列"

        RESTORING=0

        return 1

    fi


    if [ ! -s "$INFO" ]; then

        log "没有保存的播放状态"

        RESTORING=0

        return 1

    fi


    # ========================================================
    # 读取状态
    # ========================================================

    source "$INFO"


    log "准备恢复:${COUNT}首 / 第${CURRENT}首 / ${POSITION}"


    # ========================================================
    # 等待MPD
    # ========================================================

    if ! wait_mpd; then

        log "MPD等待失败"

        RESTORING=0

        return 1

    fi


    # ========================================================
    # 保存当前音量
    # ========================================================

    OLD_VOLUME=$(get_volume)


    if [ -z "$OLD_VOLUME" ]; then

        OLD_VOLUME=100

    fi


    log "保存当前音量:${OLD_VOLUME}%"


    # ========================================================
    # ★★★ 恢复期间静音 ★★★
    #
    # 防止mpc play瞬间播放第一首
    # ========================================================

    timeout 5 mpc volume 0 >/dev/null 2>&1 || true


    # ========================================================
    # 停止当前播放
    # ========================================================

    timeout 5 mpc stop >/dev/null 2>&1 || true

    sleep 0.1


    # ========================================================
    # 清除当前DLNA队列
    # ========================================================

    timeout 5 mpc clear >/dev/null 2>&1 || true

    sleep 0.1


    # ========================================================
    # 重新加入本地队列
    # ========================================================

    ADDED=0


    while IFS= read -r FILE
    do

        [ -z "$FILE" ] && continue


        timeout 5 \
            mpc add "$FILE" \
            >/dev/null 2>&1


        if [ $? -eq 0 ]; then

            ADDED=$((ADDED + 1))

        fi


    done < "$QUEUE"


    log "重新加入队列:${ADDED}/${COUNT}首"


    # ========================================================
    # 恢复失败
    # ========================================================

    if [ "$ADDED" -lt 1 ]; then

        log "恢复队列失败"


        timeout 5 \
            mpc volume "$OLD_VOLUME" \
            >/dev/null 2>&1 || true


        RESTORING=0

        return 1

    fi


    # ========================================================
    # 修正CURRENT
    # ========================================================

    if [ -z "$CURRENT" ]; then

        CURRENT=1

    fi


    if [ "$CURRENT" -lt 1 ]; then

        CURRENT=1

    fi


    if [ "$CURRENT" -gt "$ADDED" ]; then

        CURRENT=1

        POSITION="0:00"

        log "原歌曲序号超出当前队列,改为第1首"

    fi


    # ========================================================
    # ★★★ DLNA结束 → 下一首 ★★★
    # ========================================================

    if [ "$MODE" = "next" ]; then


        NEXT=$((CURRENT + 1))


        # ----------------------------------------------------
        # 最后一首 → 第一首
        # ----------------------------------------------------

        if [ "$NEXT" -gt "$ADDED" ]; then

            NEXT=1

            log "DLNA结束:原来是最后一首,循环到第1首"

        else

            log "DLNA结束:从第${CURRENT}首切换到下一首第${NEXT}首"

        fi


        CURRENT="$NEXT"

        POSITION="0:00"


        # ====================================================
        # ★★★ 静音状态下选择歌曲
        # ====================================================

        timeout 5 \
            mpc play "$CURRENT" \
            >/dev/null 2>&1 || true


        sleep 0.05


        # ----------------------------------------------------
        # 暂停
        # ----------------------------------------------------

        timeout 5 \
            mpc pause \
            >/dev/null 2>&1 || true


        sleep 0.05


        # ----------------------------------------------------
        # 定位0秒
        # ----------------------------------------------------

        timeout 5 \
            mpc seek 0 \
            >/dev/null 2>&1 || true


        sleep 0.05


        # ====================================================
        # 确认当前歌曲
        # ====================================================

        CHECK_CURRENT=$(get_current)


        if [ "$CHECK_CURRENT" != "$CURRENT" ]; then

            log "警告:当前第${CHECK_CURRENT}首,期望第${CURRENT}首"

        else

            log "已锁定下一首:第${CURRENT}首 / 0:00"

        fi


        # ====================================================
        # 恢复音量
        # ====================================================

        timeout 5 \
            mpc volume "$OLD_VOLUME" \
            >/dev/null 2>&1 || true


        sleep 0.05


        # ====================================================
        # 最后开始播放
        # ====================================================

        timeout 5 \
            mpc play \
            >/dev/null 2>&1 || true


        log "DLNA恢复完成:${ADDED}首 / 第${CURRENT}首 / 0:00 / 音量${OLD_VOLUME}%"


        RESTORING=0


        return 0

    fi


    # ========================================================
    # ★★★ 普通开机恢复 ★★★
    # ========================================================

    log "普通恢复:第${CURRENT}首 / ${POSITION}"


    # --------------------------------------------------------
    # 选择歌曲
    # --------------------------------------------------------

    timeout 5 \
        mpc play "$CURRENT" \
        >/dev/null 2>&1 || true


    sleep 0.05


    # --------------------------------------------------------
    # 暂停
    # --------------------------------------------------------

    timeout 5 \
        mpc pause \
        >/dev/null 2>&1 || true


    sleep 0.05


    # --------------------------------------------------------
    # 恢复进度
    # --------------------------------------------------------

    if [ -n "$POSITION" ] && [ "$POSITION" != "0:00" ]; then

        timeout 5 \
            mpc seek "$POSITION" \
            >/dev/null 2>&1 || true

        log "定位到第${CURRENT}首 / ${POSITION}"

    else

        timeout 5 \
            mpc seek 0 \
            >/dev/null 2>&1 || true

        log "从第${CURRENT}首 / 0:00开始"

    fi


    sleep 0.05


    # --------------------------------------------------------
    # 恢复音量
    # --------------------------------------------------------

    timeout 5 \
        mpc volume "$OLD_VOLUME" \
        >/dev/null 2>&1 || true


    sleep 0.05


    # --------------------------------------------------------
    # 开始播放
    # --------------------------------------------------------

    timeout 5 \
        mpc play \
        >/dev/null 2>&1 || true


    log "普通恢复完成:${ADDED}首 / 第${CURRENT}首 / ${POSITION} / 音量${OLD_VOLUME}%"


    RESTORING=0


    return 0

}


# ============================================================
# MPD进程健康检查
# ============================================================

check_mpd() {


    if mpc_ok; then

        FAILCOUNT=0

        return 0

    fi


    FAILCOUNT=$((FAILCOUNT + 1))


    log "MPD无响应 ${FAILCOUNT}/3"


    if [ "$FAILCOUNT" -ge 3 ]; then


        log "MPD确认无响应,尝试重启"


        systemctl restart mpd


        sleep 3


        FAILCOUNT=0


        if mpc_ok; then

            log "MPD已恢复"

        else

            log "MPD恢复失败"

        fi

    fi

}


# ============================================================
# 网络流卡死检测
#
# 只检测:
#
# 网络流
# +
# playing
# +
# 播放位置长时间不变化
#
# 不检测:
#
# 本地音乐
# pause
# stopped
# DLNA切换
# 恢复过程
# ============================================================

check_network_stream_stuck() {


    # --------------------------------------------------------
    # 恢复过程中不检测
    # --------------------------------------------------------

    if [ "$RESTORING" = "1" ]; then

        STREAM_STUCK_COUNT=0

        STREAM_LAST_POSITION=""

        STREAM_LAST_URI=""

        return 0

    fi


    # --------------------------------------------------------
    # MPD无响应
    # --------------------------------------------------------

    if ! mpc_ok; then

        return 0

    fi


    URI=$(get_uri)


    # --------------------------------------------------------
    # 当前不是网络流
    # --------------------------------------------------------

    if ! is_network_stream "$URI"; then

        STREAM_STUCK_COUNT=0

        STREAM_LAST_POSITION=""

        STREAM_LAST_URI=""

        return 0

    fi


    # --------------------------------------------------------
    # 当前播放状态
    # --------------------------------------------------------

    STATE_NOW=$(get_state)


    # --------------------------------------------------------
    # 暂停/停止不算卡死
    # --------------------------------------------------------

    if [ "$STATE_NOW" != "playing" ]; then

        STREAM_STUCK_COUNT=0

        STREAM_LAST_POSITION=""

        STREAM_LAST_URI="$URI"

        return 0

    fi


    POSITION_NOW=$(get_position)


    # --------------------------------------------------------
    # 没拿到播放时间
    # --------------------------------------------------------

    if [ -z "$POSITION_NOW" ]; then

        return 0

    fi


    # ========================================================
    # 第一次检测
    # ========================================================

    if [ -z "$STREAM_LAST_URI" ]; then

        STREAM_LAST_URI="$URI"

        STREAM_LAST_POSITION="$POSITION_NOW"

        STREAM_STUCK_COUNT=0

        return 0

    fi


    # ========================================================
    # 网络流换了
    # ========================================================

    if [ "$URI" != "$STREAM_LAST_URI" ]; then

        STREAM_LAST_URI="$URI"

        STREAM_LAST_POSITION="$POSITION_NOW"

        STREAM_STUCK_COUNT=0

        return 0

    fi


    # ========================================================
    # 播放时间有变化
    # ========================================================

    if [ "$POSITION_NOW" != "$STREAM_LAST_POSITION" ]; then

        STREAM_LAST_POSITION="$POSITION_NOW"

        STREAM_STUCK_COUNT=0

        return 0

    fi


    # ========================================================
    # 播放时间没有变化
    # ========================================================

    STREAM_STUCK_COUNT=$((STREAM_STUCK_COUNT + 1))


    log "网络流播放位置未变化:${POSITION_NOW} / ${STREAM_STUCK_COUNT}/${STREAM_STUCK_LIMIT}"


    # ========================================================
    # 超过30秒
    # ========================================================

    if [ "$STREAM_STUCK_COUNT" -ge "$STREAM_STUCK_LIMIT" ]; then


        log "检测到网络流卡死:${URI}"


        # ----------------------------------------------------
        # 防止重复触发
        # ----------------------------------------------------

        RESTORING=1


        # ----------------------------------------------------
        # 记住当前是DLNA
        # ----------------------------------------------------

        DLNA=1


        # ----------------------------------------------------
        # 重置监控
        # ----------------------------------------------------

        STREAM_STUCK_COUNT=0

        STREAM_LAST_POSITION=""

        STREAM_LAST_URI=""


        # ====================================================
        # 重启MPD
        # ====================================================

        log "网络流卡死,开始重启MPD"


        systemctl restart mpd


        sleep 3


        # ----------------------------------------------------
        # 等MPD
        # ----------------------------------------------------

        if ! wait_mpd; then

            log "MPD重启后仍然无响应"

            RESTORING=0

            return 1

        fi


        log "MPD重启成功"


        # ====================================================
        # 网络流卡死等同于DLNA结束
        #
        # 直接进入下一首
        # ====================================================

        RESTORING=0


        restore_local_state "next"


        DLNA=0


        return 0

    fi


    return 0

}


# ============================================================
# 主程序
# ============================================================

log "MPD DLNA Resume + Network Watchdog 启动"


DLNA=0

RESTORING=0

FAILCOUNT=0


# ============================================================
# 等待MPD
# ============================================================

while ! mpc_ok
do

    sleep 2

done


# ============================================================
# 启动后保存当前本地队列
# ============================================================

save_local_state


# ============================================================
# 主循环
# ============================================================

while true
do


    # ========================================================
    # MPD进程健康检查
    # ========================================================

    check_mpd


    if ! mpc_ok; then

        sleep 2

        continue

    fi


    # ========================================================
    # 网络流卡死检查
    # ========================================================

    check_network_stream_stuck


    # ========================================================
    # 恢复过程中跳过下面状态判断
    # ========================================================

    if [ "$RESTORING" = "1" ]; then

        sleep 5

        continue

    fi


    # ========================================================
    # 获取当前URI
    # ========================================================

    URI=$(get_uri)


    # ========================================================
    # 当前是DLNA / 网络流
    # ========================================================

    if is_network_stream "$URI"; then


        if [ "$DLNA" = "0" ]; then


            DLNA=1


            # ------------------------------------------------
            # 重置网络流监控
            # ------------------------------------------------

            STREAM_STUCK_COUNT=0

            STREAM_LAST_POSITION=""

            STREAM_LAST_URI="$URI"


            log "检测到DLNA流"


            # ------------------------------------------------
            # ★★★ 绝对不保存DLNA队列 ★★★
            # ------------------------------------------------

            if [ -s "$QUEUE" ] && [ -s "$INFO" ]; then


                source "$INFO"


                log "使用DLNA前保存的本地队列:${COUNT}首 / 第${CURRENT}首 / ${POSITION}"


            else

                log "没有找到DLNA前的本地队列"

            fi

        fi


    else


        # ====================================================
        # 当前不是DLNA
        # ====================================================

        if [ "$DLNA" = "1" ]; then


            log "检测到DLNA结束"


            # ------------------------------------------------
            # 给upmpdcli一点释放时间
            # ------------------------------------------------

            sleep 2


            URI2=$(get_uri)


            # ------------------------------------------------
            # 手机又重新推送
            # ------------------------------------------------

            if is_network_stream "$URI2" then

                log "DLNA重新连接,取消本次恢复"

            else

                log "确认DLNA结束"


                # ------------------------------------------------
                # ★★★ 直接下一首 ★★★
                # ------------------------------------------------

                restore_local_state "next"

            fi


            DLNA=0


            # ------------------------------------------------
            # 重置网络流监控
            # ------------------------------------------------

            STREAM_STUCK_COUNT=0

            STREAM_LAST_POSITION=""

            STREAM_LAST_URI=""


        else


            # =================================================
            # 本地正常播放
            #
            # 每5秒保存
            # =================================================

            save_local_state

        fi

    fi


    sleep 5

done
6. 很重要:检查两个服务启动顺序

你的 mpd-autostart.service 也建议确认一下。

cat > /etc/systemd/system/mpd-autostart.service <<'EOF'
[Unit]
Description=MPD Auto Start Play
After=mpd.service
Requires=mpd.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mpd-autostart.sh
RemainAfterExit=yes
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable mpd-autostart.service
systemctl restart mpd-autostart.service
systemctl status mpd-autostart.service --no-pager
. 重建 DLNA Resume 服务

先确保以前错误的服务内容全部替换:

cat > /etc/systemd/system/mpd-dlna-resume.service <<'EOF'
[Unit]
Description=MPD DLNA Resume and Network Watchdog
After=mpd.service upmpdcli.service
Requires=mpd.service

[Service]
Type=simple

ExecStart=/usr/local/bin/mpd-dlna-resume.sh

Restart=always
RestartSec=5

User=root

# 防止缓冲输出
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
EOF
3. 启动
systemctl daemon-reload

systemctl enable mpd-dlna-resume.service

systemctl restart mpd-dlna-resume.service
systemctl status mpd-dlna-resume.service --no-pager
4. 现在把旧状态清掉一次

这个很重要,因为你之前已经产生过:

207 首 101 首 4811 首 1770 首

这些旧快照不能继续使用。

执行:

systemctl stop mpd-dlna-resume.service

rm -f /var/lib/mpd-dlna-resume/dlna_queue.txt
rm -f /var/lib/mpd-dlna-resume/dlna_info.txt
rm -f /var/lib/mpd-dlna-resume/dlna_queue.tmp
rm -f /var/lib/mpd-dlna-resume/dlna_info.tmp

systemctl daemon-reload
systemctl start mpd-dlna-resume.service
systemctl status mpd-dlna-resume.service --no-pager
systemctl list-units --type=service --all | grep -Ei 'mpd|autostart'
再看:
tail -f /var/log/mpd-dlna-resume.log
这次你应该看到类似

平时本地播放:

保存本地队列:101首 / 第37首 / 2:18
保存本地队列:101首 / 第37首 / 2:23
保存本地队列:101首 / 第37首 / 2:28

这个看要不要改 先改 mpd-manager.py

你现在执行

cp /usr/local/bin/mpd-manager.py /usr/local/bin/mpd-manager.py.bak
然后:
sed -i 's|START="/usr/local/bin/mpd-autostart.sh"|START="systemctl restart mpd"|g' /usr/local/bin/mpd-manager.py
但是这里我建议再把调用方式改得更稳,不使用 Popen("systemctl restart mpd") 的 shell 方式。

执行:

python3 - <<'PY'
p="/usr/local/bin/mpd-manager.py"

s=open(p).read()

s=s.replace(
'''subprocess.Popen(
    START
    )''',
'''subprocess.Popen(
    ["systemctl", "restart", "mpd"],
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL
    )'''
)

open(p,"w").write(s)
PY
检查:
grep -n -C 4 'systemctl\|START' /usr/local/bin/mpd-manager.py
应该能看到:
["systemctl", "restart", "mpd"]
然后重启 Web 管理器
systemctl restart mpd-manager.service
完整配置
#!/usr/bin/env python3

from flask import Flask,request,redirect,render_template_string
import subprocess
import re
import shutil
import os


app=Flask(__name__)


MPD_CONF="/etc/mpd.conf"

START="systemctl restart mpd"


HTML="""

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>MPD DAC管理</title>

<style>

body{
background:#111;
color:#fff;
font-family:Arial;
padding:20px;
}

.box{
background:#222;
padding:20px;
border-radius:12px;
margin-bottom:20px;
}

button,select{
font-size:18px;
padding:10px;
width:100%;
}

</style>

</head>


<body>


<h2>CM101H MPD DAC管理</h2>


<div class="box">

<h3>当前配置</h3>

<pre>
{{current}}
</pre>

</div>



<div class="box">

<h3>检测到DAC</h3>


<form method="post" action="/set">


<select name="dac">


{% for d in dacs %}

<option value="{{d.hw}}">
{{d.hw}}  {{d.name}}

</option>


{% endfor %}


</select>


<br><br>


<button>
切换DAC
</button>


</form>


</div>



<div class="box">


<a href="/restart">

<button>
重启MPD
</button>

</a>


</div>


</body>

</html>

"""



def run(cmd):

    try:

        return subprocess.check_output(
        cmd,
        shell=True,
        stderr=subprocess.STDOUT
        ).decode()

    except:

        return ""



def scan_dac():

    out=run("aplay -l")

    result=[]


    for line in out.splitlines():

        m=re.search(
        r"card (\d+): (.*?) \[.*?\], device (\d+):",
        line
        )


        if m:

            result.append(
            {
            "hw":
            "hw:%s,%s"%(m.group(1),m.group(3)),

            "name":
            m.group(2)
            }
            )


    return result



def change_dac(hw,name):


    text=open(MPD_CONF).read()



    if not os.path.exists(
    "/etc/mpd.conf.backup"
    ):

        shutil.copy(
        MPD_CONF,
        "/etc/mpd.conf.backup"
        )


    start=text.find("audio_output {")


    if start<0:
        return



    count=0
    end=start


    for i in range(start,len(text)):

        if text[i]=="{":
            count+=1

        elif text[i]=="}":

            count-=1

            if count==0:
                end=i+1
                break



    new=f'''
audio_output {{

    type            "alsa"

    name            "{name}"

    device          "{hw}"

    mixer_type      "none"

    auto_resample   "no"

    auto_channels   "no"

    auto_format     "no"

}}
'''


    text=text[:start]+new+text[end:]


    open(
    MPD_CONF,
    "w"
    ).write(text)




@app.route("/")

def index():

    return render_template_string(
    HTML,
    dacs=scan_dac(),
    current=run(
    "grep -A12 audio_output /etc/mpd.conf"
    )
    )




@app.route("/set",methods=["POST"])

def setdac():

    hw=request.form["dac"]


    name="USB DAC"


    for d in scan_dac():

        if d["hw"]==hw:

            name=d["name"]


    change_dac(hw,name)


    subprocess.Popen(
    ["systemctl", "restart", "mpd"],
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL
    )


    return redirect("/")




@app.route("/restart")

def restart():

    subprocess.Popen(
    ["systemctl", "restart", "mpd"],
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL
    )

    return redirect("/")




app.run(
host="0.0.0.0",
port=8090
)

延伸阅读