вторник, 3 июня 2014 г.

Плеер mopidy

Плеер Mopidy оказался для меня открытием. Плеер может работать как сервер MPD. Управляться с клиентов MPD. Проигрывать как локальный контент, так и с SoundCloud, GooglePlay Music, TuneIn ... etc.
Если запускаем плеер как сервис, то конфиг лежит в /etc/mopidy/mopidy.conf

[logging]
config_file = /etc/mopidy/logging.conf
debug_file = /var/log/mopidy/mopidy-debug.log

[local]
enabled = true
data_dir = /var/lib/mopidy/local
media_dir = /var/lib/mopidy/media
playlists_dir = /var/lib/mopidy/playlists

[soundcloud]
enabled = true
explore_songs = 25
auth_token = ####

[tunein]
timeout = 5000

[mpd]
enabled = true
hostname = 127.0.0.1
port = 6600
#password =
#max_connections = 20
#connection_timeout = 60
#zeroconf = Mopidy MPD server on $hostname

Я активировал tunein, mpd и soundcloud. tunein  и soundcloud доставляются отдельными плагинами. Mpd встроен изначально.
Текущая конфигурация смотрится командой
mopidy config

Оконный менеджер Qtile

QTile это tiling оконный менеджер написанный полностью на Python. Основные его плюсы состоят в том что написан на Python и вроде как сейчас активно развивается. Конфиг тоже пишется на Python, поэтому для человека знакомого с данным языком проблем с конфигурацией не должно быть.
Установить Qtile можно из git репозитория, репозиториев дистрибутивов, с помощью питоновский утилит pip и easy_install, а так же и из исходных кодов.
Если вы ставили Qtile из репозитория дистрибутива, то на экране login должен быть доступен сеанс Qtile, в него и загружаемся.
Пока у вас нету своего конфига, будет работать DefaulConfig. Можно сохранить его в ~/.config/qtile/config.py и начать править.
 Переключение между рабочими столами происходит по кнопке WinKey+буква(a, s, d, f, u, i, o, p)
Запуск терминала Winkey+Enter
Что бы запустить любой приложение, можно нажать WinKey+r, появится строка ввода, куда надо вводить имя программы для запуска.
Завершение Qtile по нажатию WinKey+Ctrl+q.
Перезагрузка Qtile по нажатию WinKey+Ctrl+r. (удобно пока экспериментируешь с конфигом)

По сути, это все что вам необходимо для того что бы начать создавать свой конфиг. На сайте разработчика есть небольшая документация, в которой освещены основные моменты настройки. Я описывать тоже самое не будет, просто приведу свой конфиг.
Разберем не которые детали:

выставляем клавиши
mod = "mod4"
alt = "mod1"

Управление музыкой через mpc
    Key([mod], "Home", lazy.spawn("mpc toggle")),
    Key([mod], "End", lazy.spawn("mpc stop")),
    Key([mod], "Page_Down", lazy.spawn("mpc next")),
    Key([mod], "Delete", lazy.spawn("mpc prev")),
    Key(
        [mod], "m",
        lazy.spawn(
            """
            mpc playlist | dmenu -l 10  |\
            xargs -I '{}' sh -c "mpc playlist \
            | grep -rne '{}' | awk -F: '{print \$1}'" \
            | xargs -I '{}' sh -c "test -n '{}' && mpc play '{}'"
            """)
    ),

закрыть текущее окно
    Key(
        [mod], "w",
        lazy.window.kill()
    ),
поднимаем спрятавшееся окно наружу(grave -  это клавиша ~)
    Key(
        [alt], "grave",
        lazy.window.bring_to_front()
    ),

переключение на следующее окно в группе(полезно когда какой-нибудь всплывающее окно ушло за задний фон)
    Key(
        [alt], "Tab",
        lazy.group.next_window()),

Переключение между группами
    Key(
        [mod], "Left",
        lazy.group.prevgroup()
    ),
    Key(
        [mod], "Right",
        lazy.group.nextgroup()
    ),
переключение между слоями
    Key(
        [mod], "h",
        lazy.layout.previous()
    ),
    Key(
        [mod], "l",
        lazy.layout.next()
    ),
    Key(
        [mod], "j",
        lazy.layout.up()
    ),
    Key(
        [mod], "k",
        lazy.layout.down()
    ),

переключить окно в плавающее(не обходимо для окон работающий не навесь экран, например диалоги)
    Key(
        [mod], "f",
        lazy.window.toggle_floating()
    ),
различные переключения и изменения размеров для разных layout. проще попробовать самому.
    Key(
        [mod, "shift"], "space",
        lazy.layout.rotate(),
        lazy.layout.flip(),              # xmonad-tall
        ),
    Key(
        [mod, "shift"], "k",
        lazy.layout.shuffle_up(),         # Stack, xmonad-tall
        ),
    Key(
        [mod, "shift"], "j",
        lazy.layout.shuffle_down(),       # Stack, xmonad-tall
        ),
    Key(
        [mod, "control"], "l",
        lazy.layout.add(),                # Stack
        lazy.layout.increase_ratio(),     # Tile
        lazy.layout.maximize(),           # xmonad-tall
        ),
    Key(
        [mod, "control"], "h",
        lazy.layout.delete(),             # Stack
        lazy.layout.decrease_ratio(),     # Tile
        lazy.layout.normalize(),          # xmonad-tall
        ),
    Key(
        [mod, "control"], "k",
        lazy.layout.shrink(),             # xmonad-tall
        lazy.layout.decrease_nmaster(),   # Tile
        ),
    Key(
        [mod, "control"], "j",
        lazy.layout.grow(),               # xmonad-tall
        lazy.layout.increase_nmaster(),   # Tile
        ), 
запуск различных программ
    Key(
        [mod], "n",
        lazy.spawn("firefox")
        ),
    Key(
        [mod], "d",
        lazy.spawn("subl")
        ),

    Key(
        [mod], "u",
        lazy.spawn("uzbl-tabbed")
        ),
    Key([mod], "Return", lazy.spawn("urxvt")), 
переключение на следующий layout
Key([mod], "Tab",    lazy.nextlayout()),
запуск приложение через dmenu
    Key(
        [mod], "space",
        lazy.spawn(
            "dmenu_run -fn 'Terminus:size=8' -nb '#000000' -nf '#fefefe'")
    ),
настройка громкости
    Key(
        [mod], "equal",
        lazy.spawn("amixer -c 0 -q set Master 2dB+")
    ),
    Key(
        [mod], "minus",
        lazy.spawn("amixer -c 0 -q set Master 2dB-")
    ),
управление мышкой
 mouse = [
    Drag(
        [mod], "Button1",
        lazy.window.set_position_floating(),
        start=lazy.window.get_position()
    ),
    Drag(
        [mod], "Button3",
        lazy.window.set_size_floating(),
        start=lazy.window.get_size()
    ),
    Click(
        [mod], "Button2",
        lazy.window.bring_to_front()
    ),
]
задаем рабочие столы. У меня это клавиши от 1 до 0. Для переключение нажимаем клавишу mod + номер. Для переноса окна на другой стол, mod+shift+номер
groups = [Group(str(i)) for i in (1, 2, 3, 4, 5, 6, 7, 8, 9, 0)]
for i in groups:
    keys.append(Key([mod], i.name, lazy.group[i.name].toscreen()))
    keys.append(
        Key(
            [mod, "shift"], i.name, lazy.window.togroup(i.name),
            lazy.group[i.name].toscreen()))
настраиваем border для окон
border = dict(
    border_normal='#808080',
    border_width=1,
)
задаем какие будем использоваться layouts. Здесь можно почитать про встроенные layouts, но документация как всегда не успевает за разработчиками, так что лучше все посмотреть в git
layouts = [
    layout.Max(),
    layout.MonadTall(**border),
    layout.Matrix(**border)
]
теперь задаем наш экран. у меня один монитор, поэтому screen один. Можно настраивать поведение при нескольких мониторах. В нашем мониторе создаем Bar и задаем что на необходимо там видеть. Опять же, вот тут про встроенные виджеты или тут в исходных кодах.
В вкратце, у меня используется GroupBox - отображает рабочие столы, WindowName - отображает заголовок текущего окна, Notify - типа статусной строки, Systray - системный трай, KeyboardLayout - раскладка клавиатуры, Volume - громкость, BitcoinTicker - все ясно, и часы. У  каждого виджета есть свои настройки, более подробно в исходнике виджета.
screens = [
    Screen(
        top=bar.Bar(
            [
                widget.GroupBox(margin_y=1,
                                margin_x=1,
                                borderwidth=1,
                                padding=1,),
                widget.WindowName(foreground="a0a0a0",),
                widget.Notify(),
                widget.Systray(),
                widget.KeyboardLayout(configured_keyboards=["us", "ru"]),
                widget.Volume(foreground="70ff70",),
                widget.BitcoinTicker(currency="rub", format="{buy}"),
                widget.Clock(foreground="a0a0a0",
                             fmt="%H:%M %d.%m.%Y",),
            ],
            18,
        ),
    ),
]
эти хуки необходимы для того что бы некоторые всплывающие окна и диалоги автоматом переключались в режим floating и не открывались во весь экран
@hook.subscribe.client_new
def dialogs(window):
    if (window.window.get_wm_type() == 'dialog'
            or window.window.get_wm_transient_for()):
        window.floating = True


@hook.subscribe.client_new
def vue_tools(window):
    if((window.window.get_wm_class() == (
        'sun-awt-X11-XWindowPeer', 'tufts-vue-VUE')
            and window.window.get_wm_hints()['window_group'] != 0)
            or (window.window.get_wm_class() == (
                'sun-awt-X11-XDialogPeer', 'tufts-vue-VUE'))):
        window.floating = True


@hook.subscribe.client_new
def idle_dialogues(window):
    if((window.window.get_name() == 'Search Dialog') or
      (window.window.get_name() == 'Module') or
      (window.window.get_name() == 'Goto') or
      (window.window.get_name() == 'IDLE Preferences')):
        window.floating = True


@hook.subscribe.client_new
def libreoffice_dialogues(window):
    if ((window.window.get_wm_class() == (
        'VCLSalFrame', 'libreoffice-calc')) or
            (window.window.get_wm_class() == (
                'VCLSalFrame', 'LibreOffice 3.4'))):
        window.floating = True
а здесь мы создаем метод для запуск программ и описываем программы которые необходимо запускать при старте qtile
def is_running(process):
    s = subprocess.Popen(["ps", "axw"], stdout=subprocess.PIPE)
    for x in s.stdout:
        if re.search(process, x):
            return True
    return False


def execute_once(process):
    if not is_running(process):
        return subprocess.Popen(process.split())


@hook.subscribe.startup
def startup():
    execute_once("nm-applet")
    execute_once("synergy")
    execute_once("feh --bg-fill /home/pavel/Pictures/arch.png")
    execute_once(
        'setxkbmap -layout us,ru -option grp:caps_toggle,grp_led:scroll')

Переключение трека в mpd через dmenu и mpc

Если вы пользуетесь MPD  и не хотите постоянно держать запущенным какой-нибудь клиент, типа Sonata, для переключения треков, то можно повесить на hotkey вот такой простой скрипт
mpc playlist | dmenu -l 10  | xargs -I '{}' sh -c "mpc playlist | grep -rne '{}' | awk -F:  '{print \$1}'"  | xargs -I '{}' sh -c "test -n '{}'&& mpc play '{}'"
получаем текущей playlist
mpc playlist
и выводим его с помощью dmenu(вертикально, 10 строчек)
dmenu -l 10
посылаем выбранный трек
xargs -I '{}' sh -c 
фильтруем playlist по выбранному треку и выставляем номер строки данного трека
mpc playlist | grep -rne '{}' 
оставляем только номер трека
awk -F:  '{print \$1}'"
проверяем что трек был выбран. В случае если нажали Esc, трек не надо переключать. Запускаем выбранные трек
xargs -I '{}' sh -c "test -n '{}'&& mpc play '{}'"

У меня в Qtile hotkey выглядит вот так:

Key(
        [mod], "m",
        lazy.spawn(
            """
            mpc playlist | dmenu -l 10  |\
            xargs -I '{}' sh -c "mpc playlist \
            | grep -rne '{}' | awk -F: '{print \$1}'" \
            | xargs -I '{}' sh -c "test -n '{}' && mpc play '{}'"
            """)
    ),
Переключение вперед/назад/стоп
Key([mod], "Home", lazy.spawn("mpc toggle")),
Key([mod], "End", lazy.spawn("mpc stop")),
Key([mod], "Page_Down", lazy.spawn("mpc next")),
Key([mod], "Delete", lazy.spawn("mpc prev")),