16 октября 2011 г.

mplayer, сетевое вещание и тэги в cp1251

Я практически постоянно слушаю через интернет радиостанцию "Наше радио". При всем разнообразии плееров доступных под Linux мне все же по душе старый-добрый mplayer который работает как автомат калашникова.

Так вот большая часть из известных мне радиостанций передает тэги в кодировке cp1251 (почему бы им не перейти на utf-8, ведь 21 век на дворе? видать только по религиозным соображениям), а так как локаль у меня utf-8 то "автомат калашникова" то-бишь mplayer отображает названия песен просто знаками вопроса. И если практически всех исполнителей я узнаю по голосам, то с названиями песен все намного хуже. Короче поставил перед собой задачу сделать что-нить чтобы можно было посмотреть названия привычными русскими буквами.

Немного погугливши была найдена статья на Хабре теперь уже по моим религиозным соображениям решение описанное в ней не подошло - не хочу я при каждом обновлении пересобирать mplayer или ставить его на hold. Хотя метод самый правильный, почему бы разработчикам не предусмотреть возможность указания кодировки тегов или её автоопределения как это сделано для субтитров.

Продолжив гуглить я нашел пост в котором было описано практически все, что я хотел получить и даже больше - помимо перекодировки название песни отсылалось как уведомление по dbus. Но вот только с одной проблемой - kdialog каждое сообщение отсылает как новое и эти сообщения накапливаются (по крайней мере в KDE), что меня однозначно не устраивает, мне нужно работать, а не закрывать целую гору сообщений о новых песнях...

Почитав Desktop Notifications Specification я понял, что счастье возможно и начал искать инструмент который помог бы мне отсылать сообщение и при необходимости его переписать поверху. Т.к. я последние несколько лет пишу на C++ с использованием Qt перво наперво я обратил свой взор на замечательную утилиту qdbus которой уже пользовался в одном проекте. Но что-то не срослось у меня через нее отправить параметры "actions" и "hint", при этом через qdbusviewer все отправлялось как положено . Пойдя дальше я остановился на не менее замечательной утилите dbus-send но и тут меня поджидали грабли с этими параметрами вернее не с самими параметрами, а с их типами данных - ну никак не получалось заставить его работать.

При этом если смотреть на то что отправляет kdialog и notify-send (который тоже работает) через dbus-monitor, данные 100% соответствуют тому, что я отправляю через dbus-send и qdbus. Но что-то там в дебрях dbus мне сопротивляется.

В итоге наткнулся я на мануал по gdbus. Первая мысль была - это тоже самое, что я пытался использовать до этого только в профиль...

Но не тут то было. Прямо в мануале есть рабочий пример как отправить Notify. И это заработало!!!

Теперь настало время собрать все требования в одну кучу:

  1. необходимо чтобы mplayer работал где-нить в фоновом режиме
  2. т.к. любая связь имеет два устойчивых положения - "либо нет, либо только что была" необходимо чтобы mplayer при обрыве связи сам пытался достучаться до радиостанции без моего пинка
  3. необходимо конвертировать все теги из кодировки cp1251 в utf-8
  4. необходимо отображать название каждой новой песни как стандартное уведомление Desktop Notifications при этом не плодить этих уведомлений, а обновлять уже существующее

И вот готовое решение (все листинги чуть ниже):

  1. mplayer запускаем в screen, пусть там себе работает и нам не мешает
  2. помимо всего прочего запускаем mplayer в бесконечном цикле чтобы при обрыве связи он запустился сам по себе
  3. тут iconv нам в помощь
  4. как я уже писал все делаем через gdbus

Теперь листинги:

Сам mplayer и скрипт по отсылке уведомления я запускаю под screen одной командой:

$ screen -c ~/.screenrc-mplayer &

Сам файл конфигурации screen-a имеет вид

$ cat ~/.screenrc-mplayer

# ScreenRC
# vi:ts=4:sw=4

vbell off
multiuser on
autodetach on
startup_message off
defscrollback 100000
defsilence on

screen  -t shell         0
screen  -t mplayer       1    /home/termit/.mplayer/mplayer.sh
screen  -t title         2    /home/termit/.mplayer/title.sh

hardstatus alwayslastline "%{= KW}%-w%{= wk}%50>%n %t%{-}%+w%<"

Этот конфиг делает в screen 3 терминала. На 0 запущен просто shell, на 1 запущен сам mplayer, на 2 запущен скрипт который отсылает уведомления.

Скрипт запускающий mplayer

$ cat /home/termit/.mplayer/mplayer.sh

#!/bin/sh
while sleep 2; do 
mplayer -msgcolor -playlist \
    http://188.127.243.169/nashe-128.m3u 2>&1 | \
    grep --line-buffered -i icy | \
    tee  ~/.mplayer/mplayer.log; 
done

Скрипт выводящий уведомления

$ cat /home/termit/.mplayer/title.sh

#!/bin/bash

# Имя приложения
NAMEAPP=mplayer

# Номер сообщения, необходим чтобы сообщение перезаписывалось
# поверху, может быть любым, если будет = 0, будут добавляться
# новые сообщения
NUMBER=1

# Имя иконки или путь к файлу с иконкой
ICON=kmplayer
#ICON=amarok
#ICON=dragonplayer

# Заголовок
HEAD="Сейчас играет"

# Время отображения сообщения
TIMEOUT=5000

tail -n1 -F ~/.mplayer/mplayer.log |
while read title
do

# вычислим название песни
SONGNAME=`echo $title | cut -d = -f 2 | cut -d \' -f 2 | grep -v '^ -'  `

# проверим не пустое ли название
[[ -z ${SONGNAME} ]] && continue

# отобразим сообщение
gdbus call --session \
        --dest org.freedesktop.Notifications \
        --object-path /org/freedesktop/Notifications \
        --method org.freedesktop.Notifications.Notify \
        ${NAMEAPP} \
        ${NUMBER} \
        ${ICON} \
        "`echo ${HEAD}`" \
        "`echo ${SONGNAME} | iconv -f cp1251`" \
        [] \
        {} \
        ${TIMEOUT}

done

Результат:

В KDE это выглядит так:

В Unity это выглядит так: