Разделение страниц пополам в DJVU с помощью ImageMagick

Bash script:

#!/bin/bash
#sudo apt-get install djvulibre-bin imagemagick libtiff-tools
#rm *.tif
for (( i=0; i<=317; i++ ))
        do ddjvu -page=$i -format=tiff assimil_france.djvu tmp$i.tif
        convert tmp$i.tif -crop 50%x100% $i%d.tif
        rm tmp$i.tif
done
for a in [0-9]*.tif; do
    mv $a "printf %05d.%s ${a%.*} ${a##*.}"
done
tiffcp *.tif out.tif && tiff2pdf out.tif -j -o out.pdf && rm *.tif

Выбор самого быстрого зеркала репозиториев через консоль в Ubuntu Linux

С помощью GUI легко выбирать самый быстрый сервер репозиториев для загрузки обновлений. Но как сделать это через командную строку? Очень просто! apt поддерживает директиву mirror, с помощью которой система может выбирать наиболее лучшие зеркала в зависимости от вашего местоположения.

Всё, что нужно – это добавить следующие строки вверху файла

/etc/apt/sources.list
deb mirror://mirrors.ubuntu.com/mirrors.txt oneiric main restricted universe multiverse
deb mirror://mirrors.ubuntu.com/mirrors.txt oneiric-updates main restricted universe multiverse

Пользователи Lucid (10.04), Maverick (10.10), Natty (11.04) могут поменять слово oneiric в командах на имя своего дистрибутива.

Настройка трекбола Logitech TrackMan Marble Mouse в Ubuntu Linux


В статье описывается настройка трекбола Logitech TrackMan Marble с возможностью прокрутки страниц. Основной ref тут (документация сообщества Ubuntu) и тут.

Добавьте следующий код в

/usr/share/X11/xorg.conf.d/11-evdev-quirks.conf

и перезапусте X Window сервер:
(или в /etc/X11/xorg.conf.d/50-marblemouse.conf, предварительно выполнив

gsettings set org.gnome.settings-daemon.plugins.mouse active false

)

Section "InputClass"
        Identifier  "Marble Mouse"
        MatchProduct "Logitech USB Trackball"
        MatchIsPointer "on"
        MatchDevicePath "/dev/input/event*"
        Driver "evdev"
        Option "ButtonMapping" "1 8 3 4 5 6 7 2 9"
        Option "EmulateWheel" "true"
        Option "EmulateWheelButton" "8"
        Option "ZAxisMapping" "4 5"
        Option "XAxisMapping" "6 7"
        Option "Emulate3Buttons" "true"
EndSection

Я обычно предпочитаю ставить ButtonMapping таким, как указано выше, но некоторые предпочитают вот такой:

Option "ButtonMapping" "1 2 3 4 5 6 7 8 9"

С моим ButtonMapping я могу легко закрывать ненужные вкладки в браузере, использовать функцию копирования-вставки, и то, и другое с помощью малой левой клавиши. С альтернативным ButtonMapping теряются “возможности средней кнопки” и малая левая кнопка работает как кнопка “назад” при нажатии.

Обновление: работает также в Ubuntu 13.10, 14.04, Debian Squeezy.

Воспроизведение MIDI в Ubuntu Linux

Установил однажды я виртуальную MIDI клавиатуру (vmpk), протестировал, не работает. Перерыл кучу страниц в интернете, полезной оказалась эта.

Инструкция пригодится для всех, кроме тех,

  • кто использует Linux-поддерживаемые hardware synth звуковые карты
  • кто использует внешние MIDI инструменты

Итак, для того, чтобы заработал MIDI требуется настроить программный синтез. Существует три основных программы, которые способны на такое: TiMidity++, Fluidsynth и ZynAddSubFX.

ZynAddSubFX – наиболее прост для тех ситуаций, когда вы хотите иметь один инструмент на выходе, т.к. он не требует сэмплов или саундфонтов (samples, soundfonts). В тех случаях, когда вы хотите прогрывать MIDI потоки с более чем одним инструментом, такие как General MIDI файлы, FluidSynth или Timidity++ более просты. FluidSynth имеет хороший графический интерфейс, но вам придётся потрудиться при поиске подходящего soundfont для него. TiMidity++ чуть труднее при установке, но samples легко устанавливаются из репозиториев.

Для своих целей я выбрал TiMidity++. Устанавливается он вот так:

sudo apt-get install timidity

Легко подключаются сэмплы (у меня установились сразу с timidity):

sudo apt-get install freepats

Сэмплы из freepats не полные на настоящий момент и полностью не покрывают стандарт General MIDI.

Если вы хотите звук получше, можете установить сэмплы Eawpatches, для этого добавьте следующий репозитарий в свой /etc/apt/sources.list файл:

deb http://www.fbriere.net/debian squeeze opencbm misc

Верифицируйте репозитарий командой:

wget -O- http://www.fbriere.net/public_key.html | sudo apt-key add -

После этого вы можете установить пакет ‘eawpatches’ (31 MB). Если же вы не хотите подключать репозиторий, можете скачать файл отсюда: http://www.fbriere.net/debian/dists/stable/misc/deb/eawpatches_12-1~fbriere.5_all.deb
Eawpatches подключается к Timidity следующим образом. После установки Eawpatches, необходимо найти и заменить следующую строку в файле /etc/timidity/timidity.cfg:

source /etc/timidity/freepats.cfg

Её заменить на такую строку:

source /etc/timidity/eawpatches.cfg

Всё, eawpatches установлены.

После установки timidity и подключения к нему сэмплов, необходимо проверить, работает ли он или нет:

timidity myfile.mid

Если звук играет, то всё ок!

Далее для использования timidity в других приложениях, необходимо запустить TiMidity++ Server. Для этого подключаем модули:

sudo modprobe snd-seq-device
sudo modprobe snd-seq-midi
sudo modprobe snd-seq-oss
sudo modprobe snd-seq-midi-event
sudo modprobe snd-seq

И запускаем сервер:

timidity -iA -B2,8 -Os1l -s 44100

Команда запускает TiMidity++ midi сервер и открывает 2 midi порта. После этой команды сторонние midi программы должны работать! Необходимо зайти в их настройки и выбрать timidity как output.

Объединение нескольких PDF файлов в один в Ubuntu Linux

Иногда удобно бывает объединять несколько файлов PDF в один. Например, можно аккумулировать много PDF счетов, новостных файлов и др. в один сложный.

Подготовка системы:

sudo apt-get install gs pdftk

Теперь, рассмотрим пример объединения pdf файлов. К примеру, у нас есть файлы 1.pdf,2.pdf,3.pdf и мы хотим их объединить. Делается это следующим образом:

gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=combinedpdf.pdf -dBATCH 1.pdf 2.pdf 3.pdf

В команде сверху после “-sOUTPUTFILE=” укажите название файла на выходе на такое, какое посчитаете нужным. Переназовите файлы “1.pdf 2.pdf 3.pdf” на те, которые у вас имеются и которые вы хотите объединить (названия файлов отделяются с помощью пробелов).

После запуска комманды сверху будет создан новый PDF файл под названием combinedpdf.pdf и он будет содержать все файлы, которые были указаны для объединения в порядке указания.

Если вы хотите узнать о других возможностях комманды gs – проверьте его man страницу.

FFmpeg: Объединение нескольких avi файлов

Некоторые мультимедиа контейнеры (MPEG-1, MPEG-2 PS, DV) позволяют объединять несколько видеофайлов обыкновенной конкатенацией.

Таким образом, объединить мультимедиа файлы можно с помощью первичного транскодирования видео в привеллигированные форматы, затем используя команду cat (или copy в Windows) и конечного транскодирования видеоматериалов в любой формат на ваш выбор.

ffmpeg -i input1.avi -sameq intermediate1.mpg
ffmpeg -i input2.avi -sameq intermediate2.mpg
cat intermediate1.mpg intermediate2.mpg > intermediate_all.mpg
ffmpeg -i intermediate_all.mpg -sameq output.avi

Команда -sameq позволяет сохранять битрейт в процессе обработки для исключения деградации качества видео.

Также, использование pipes в вашей операционной системе позволяет избежать создание промежуточных файлов:

mkfifo intermediate1.mpg
mkfifo intermediate2.mpg
ffmpeg -i input1.avi -sameq -y intermediate1.mpg < /dev/null &
ffmpeg -i input2.avi -sameq -y intermediate2.mpg < /dev/null &
cat intermediate1.mpg intermediate2.mpg |\
ffmpeg -f mpeg -i - -sameq -vcodec mpeg4 -acodec libmp3lame output.avi

Источник: FAQ по FFmpeg

Кнопки “P” и “W” на клавиатуре телефона

В современных телефонах на клавиатуре при наборе номера есть такие символы как P и W. В моём HTC Desire для их набора необходимо несколько раз быстро нажать звёздочку “*”.

Предназначены они для эффективного работы с автоматическими голосовыми меню (IVR).

Кнопка “P” (pause) даёт команду телефону ждать небольшое количество времени, затем продолжить набор номера. Кнопка “W” (wait) означает ждать взаимодействия от пользователя (по нажатию кнопки набор продолжится).

Очень удобно звонить с помощью этих кнопок через IP телефонию.
Набор номера в таком случае может быть таким:

+XXXXXXpYYYYYY

Где XXXXXX – номер провайдера IP-телефонии, а YYYYYY – сам номер абонента.

О’КЕЙ торгует тухлой рыбой

Пошли вчера с женой в О’КЕЙ за продуктами. Ближайший от нас – О’КЕЙ на Лиговском проспекте, дом 40, лит. А (тот, что в Галерее).

Помимо всего прочего купили свежей рыбы – тилапию. Как потом оказалось, просроченной. Когда нам её взвешивали, запаковали по несколько тушек отдельно в пакет, потом все пакеты ещё в один пакет, в общем запах мы тогда никакой не почувствовали.

Придя домой, разбирая продукты, обнаружили очень сильный запах тухлой рыбы. Долго думали, что делать. Уверен, порядка 85% семей в такой ситуации принимают решение выбросить и никуда не ехать. Взвесив все за и против, решили – едем обратно в О’КЕЙ. На звонки О’КЕЙ не отвечает: звонил по трём телефонам.

Составил дома претензию в двух экзмеплярах, распечатал, взял копию чека. По закону, магазин обязан вернуть деньги за просроченный продовольственный товар, либо предложить похожий. Обязан он также вернуть все расходы, возникшие в результате такой покупки, а также моральный ущерб. Тот факт, что потребитель не смог поужинать в определённое время можно смело указывать как моральный ущерб.

Приехав в О’КЕЙ подошли с женой к охране, спросили кто у них таким занимается. Послали на кассу, там нужно было спросить администратора. Встретившись с администратором, рассказали свой случай, администратор нас прекрасно поняла, и позвала заместителя директора.

Зам. дирекора принесла нам свои извинения от имени магазина и рассказала, почему такое происходит у них в магазине. Рыба портится, её списывают. Нас спросили, кто нам продавал рыбу, не мужчина ли? Ответили, да, мужчина. Оказывается, мужчины у них работают молодые и не знают, какую брать рыбу, иногда вместо свежей они берут другую. Как такое может произойти в магазине – непонятно. Зам. директора подтвердила, что купленная нами рыба была списанной!

Претензию зам. директора подписать отказалась, предложила вернуть только деньги за рыбу, либо дать другую рыбу за такие же деньги, выплату денег для проезда к магазину и моральный ущерб выдать отказалась. Т.к. жена попросила меня дома, что ссориться по крупному не будем, то дело решили “замять” на месте. Нам было предложено пойти вместе с администратором, понюхать вместе рыбу. Подойдя к месту продажи рыбы, администратор попросила продавца выдать нам свежей рыбы. Продавец рассказала, что некоторые виды рыбы, которые у них лежат на лотке покупать не стоит. Так и лежит у них просроченная рыба вместе со всей другой. В итоге ушли домой с рыбкой посвежее.

И такая ситуация уже не в первый раз. В прошлый раз попросили взвесить рыбы, на кассе запах тухлой рыбы, всё-таки почувствовали через толщу пакетов.

Где покупать в городе рыбу – неизвестно… На рынках анти-санитария, чеков не выдают. Раньше доверяли всяким О’КЕЙ, но, увы, доверия теперь больше нет. Интересно, что в Стокманне.

Запоминание позиции воспроизведения файла в MPlayer

Понравилась статья, оригинал тут.
Спасибо автору, давно искал. Mplayer Tools компилил, не понравлось.
От себя добавлю: хотел пользоваться этим вместе с gnome-mplayer, но он, к сожалению, не знает параметр -ss и не может стартовать с заданного времени.

Думаю, многим знакома следующая ситуация. Вы запускаете свой любимый MPlayer, чтобы посмотреть какой-нибудь фильм, смотрите, но до конца досмотреть не успеваете, т. к. вам нужно куда-нибудь уходить. Вы закрываете MPlayer, выключаете компьютер и идете по своим делам. Вернувшись, вы
решаете досмотреть фильм. Вот только где же вы остановились? Что делать? «Проматывать», пока не наткнетесь на тот момент, который не видели?

Записывать время остановки на бумажке? 🙂 Нет, это не наш метод…

Когда я в очередной раз столкнулся с этой проблемой, то пошел на домашнюю страницу MPlayer, на которой нашел два скрипта для ее решения: mplayer-resume и MPlayer Tools.

mplayer-resume у меня отказался запоминать позиции в файлах и к тому же подавлял весь вывод mplayer’a, что довольно неаккуратно с его стороны, так что я сразу же отказался от него, а MPlayer Tools показался мне слишком неудобным в использовании. Поэтому я решил изобрести собственный велосипед. 🙂

В итоге на свет появился относительно небольшой скрипт, представленный ниже. Скрипт полностью сохраняет вывод MPlayer’a и может принимать все аргументы, которые принимает MPlayer. В том числе ему можно передавать одновременно несколько файлов для воспроизведения — каждый из них он
воспроизведет с того места, на котором было остановлено воспроизведение в прошлый раз.

Краткое описание можно прочитать в комментариях, располагающихся в начале самого скрипта.

Удачи, надеюсь, скрипт окажется вам полезен и сэкономит хотя бы немного вашего времени и нервов. 🙂

#!/bin/bash
#***************************************************************************
#*   Copyright © 2008, Konishchev Dmitry                                 *
#*   http://konishchevdmitry.blogspot.com/                                 *
#*                                                                         *
#*   Project homepage:                                                     *
#*   http://sourceforge.net/projects/mplayerext/                           *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU General Public License as published by  *
#*   the Free Software Foundation; either version 3 of the License, or     *
#*   (at your option) any later version.                                   *
#*                                                                         *
#*   This program is distributed in the hope that it will be useful,       *
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#*   GNU General Public License for more details.                          *
#**************************************************************************/

# mplayer.ext - скрипт-оболочка для mplayer.
# 
# Предназначен для продолжения прослушивания/просмотра аудио и видео
# файлов с той позиции, на которой просмотр/прослушивание завершился в
# прошлый раз при закрытии mplayer'a.
# 
# Использование:
# Если вы хотите пользоваться возможностями скрипта, вам необходимо
# всегда, когда вы хотите проиграть аудио/видео файл, вызывать этот скрипт
# вместо mplayer'a.
# 
# Как работает скрипт:
# Если завершение работы mplayer'a происходит во время просмотра фильма,
# то в файл, путь к которому задан переменной $resume_info_file, заносится
# информация о времени, на котором произошло прерывание просмотра. Время
# привязывается к имени файла (имени, а не пути!), таким образом, если файл
# будет перемещен в другую дирректорию, то скрипт все равно его "узнает".
# В следующий раз, когда пользователь запросит проигрывание этого фильма,
# скрипт просмотрит файл, заданный переменной $resume_info_file и
# продолжит воспроизведение фильма с того момента, на котором завершилось
# воспроизведение в прошлый раз.
# 
# Максимальное количество файлов, информация о которых может храниться в
# $resume_info_file задается переменной $max_resume_info_length.
# 
# Ограничения скрипта:
# * Скрипт не обрабатывает файлы DVD вида VTS_*_*.VOB, т. к. mplayer не
#   позволяет начинать воспроизведение таких файлов с произвольного места.
# * Т. к. mplayer позволяет начинать воспроизведение фильма только с
#   ключевого кадра, то, если воспроизведение фильма в прошлый раз
#   прервалось не на ключевом кадре, при попытке воспроизвести фильм с
#   того же места произойдет перемотка вперед до следующего ключевого
#   кадра, т. е. часть фильма останется непросмотренной. В связи с этим
#   скрипт производит "отматывание" на $keyint секунд назад (по умолчанию
#   - 10), т. к., при кодировании большинства MPEG-4 файлов данная
#   величина используется в качестве максимального расстояния между
#   ключевыми кадрами. Если в ваших видеофайлах интервал между ключевыми
#   кадрами больше этого значения, то измените значение переменной $keyint.  

# Настойки -->
        # Максимальный интервал между ключевыми кадрами
        keyint=10

        # Файл, в котором будет храниться информация о недосмотренных файлах
        resume_info_file=~/.mplayer/resume_info

        # Максимальное количесво файлов, информация о которых будет храниться в $resume_info_file
        max_resume_info_length=100
# Настойки <--

mplayer_ext_echo()
{
        echo "mplayer.ext: $@"
}

mplayer_ext_warning()
{
        mplayer_ext_echo "$@" >&2
}

cleanup()
{
        rm -f $tmp_file
}

die()
{
        mplayer_ext_warning "$@"
        cleanup
        exit 1
}

# Возвращает идентификатор видео по имени файла
get_video_name_by_file_name()
{
        local video_name=$(basename "$1")

        if [[ "$video_name" == "" ]]
        then
                return 1
        fi

        # Не обрабатываем файлы DVD, т. к. в них невозможно осуществлять воспроизведение
        # с произвольного места
        if echo -n "$video_name" | egrep -i '^vts_[[:digit:]]+_[[:digit:]]+.vob$' > /dev/null
        then
                return 1
        fi

        echo -n "$video_name"
}

# Если $2 == 0, то файл помечается как просмотренный
set_resume_pos()
{
        declare -a resume_info_array
        local resume_info resume_info_time resume_info_time i

        # Устанавливаем разделитель слов равным \n
        local IFS=$'\n'

        i=0
        for resume_info in `cat "$resume_info_file" | tail --lines $((max_resume_info_length - 1))`
        do
                resume_info_time=`echo -n "$resume_info" | egrep '^.+:[[:digit:]]+$' | sed -r 's/^.+://' | egrep '^[[:digit:]]+$'`
                resume_info_name=`echo -n "$resume_info" | sed "s/:${resume_info_time}\$//"`

                # Пропускаем неверно сформированные записи
                if [[ "$resume_info_time" == "" || "$resume_info_name" == "" ]]
                then
                        mplayer_ext_warning "Bad resume info string: '$resume_info'."
                        continue
                fi
                
                # Если это тот файл, который мы ищем
                if [[ "$resume_info_name" == "$1" ]]
                then
                        # Пропускаем старую запись
                        continue
                # Остальные файлы - оставляем без изменений
                else
                        resume_info_array[$((i++))]="$resume_info"
                fi
        done

        # Если видео не досмотрели до конца
        if [[ "$2" != "0" ]]
        then
                mplayer_ext_echo "Writing resume time information: '$1': $2."
                resume_info_array[$i]="$1:$2"
        else
                mplayer_ext_echo "Writing resume time information: '$1': viewed."
        fi

        # Заносим изменения в файл
        echo "${resume_info_array[*]}" > "$resume_info_file" || die
}

# Получает строку времени, на котором было приостановлено воспроизведение файла.
# Преобразует строки вида:
# A: 308.4 V: 308.4 A-V:  -0.006 ct:  -0.041 7395/7395  4%  0%  5.5% 0 0
# A: 308.4 V: 308.4 A-V:  0.006 ct:  0.041 7395/7395  4%  0%  5.5% 0 0
# A:   2.0 V:   2.0 A-V: -0.006 ct:  0.007  50/ 50  6%  3%  3.9% 0
# A:  87.6 (01:27.5) of 228.0 (03:48.0)  4.4%
# V:   1.8  45/ 45 15%  3%  0.0% 0 0
# в строку вида:
# [AV]:308
# в зависимости от наличия в файле аудио/видео дорожек
get_cur_pos_info()
{
        local pos_info=`cat $tmp_file | head --lines $end_line | tail --lines $((end_line - start_line + 1)) | tr '\33\15' '\n' \
                | egrep '^[AV]:[[:space:]]*[[:digit:]]+\.[[:digit:]]+[[:space:]]+' \
                        | tail --lines 1 \
                                | sed -r 's/:\s+/:/g' | sed -r 's/\s+/ /g'`

        if [[ $pos_info == "" ]]
        then
                return 1
        fi

        # Видео со звуком
        if echo "$pos_info" | egrep -o '^A:[[:digit:]]+\.[[:digit:]]+ V:[[:digit:]]+\.[[:digit:]]' > /dev/null
        then
                pos_info=`echo -n "$pos_info" | awk '{ print $2 }' | awk -F '.' '{ print $1 }'`
        # Видео без звука
        elif echo "$pos_info" | egrep -o '^V:[[:digit:]]+\.[[:digit:]]' > /dev/null
        then
                pos_info=`echo -n "$pos_info" | awk -F '.' '{ print $1 }'`
        # Аудио без видео
        elif echo "$pos_info" | egrep -o '^A:[[:digit:]]+\.[[:digit:]]' > /dev/null
        then
                pos_info=`echo -n "$pos_info" | awk -F '.' '{ print $1 }'`
        # Логическая ошибка
        else
                die "Logical error!"
        fi

        if [[ $pos_info == "" ]]
        then
                die "Logical error!"
        fi

        echo -n "$pos_info"
}

get_resume_pos()
{
        local resume_info resume_info_time resume_info_time

        # Устанавливаем разделитель слов равным \n
        local IFS=$'\n'

        for resume_info in $(< "$resume_info_file")
        do
                resume_info_time=`echo "$resume_info" | egrep '^.+:[[:digit:]]+$' | sed -r 's/^.+://' | egrep '^[[:digit:]]+$'`
                resume_info_name=`echo "$resume_info" | sed "s/:${resume_info_time}\$//"`

                # Пропускаем неверно сформированные записи
                if [[ "$resume_info_time" == "" || "$resume_info_name" == "" ]]
                then
                        # Предупреждение не выводим, т. к. оно будет выведено при записи в файл.
                        continue
                fi
                
                # Если это тот файл, который мы ищем
                if [[ "$resume_info_name" == "$1" ]]
                then
                        echo $resume_info_time
                        return 0
                fi
        done

        return 1
}

if ! tmp_file=`mktemp`
then
        die "Can't create temp file."
fi

if ! which mplayer > /dev/null
then
        die "Error! Mplayer not installed."
fi

if [[ ! -e "$resume_info_file" ]]
then
        touch "$resume_info_file" || die
fi

# Изменяем агрументы, переданные mplayer'у так, чтобы выбранные видеофайлы
# воспроизводились с того момента, где в прошлый раз было прервано
# воспроизведение.
i=0
for option
do
        options[$((i++))]="$option"

        if [[ ${option:0:1} != '-' ]]
        then
                # Если значение параметра похоже на имя файла, то считаем, что
                # требуется проиграть этот файл.  Если это просто значение опции, то
                # скрипт все равно сработает нормально, разве что добавится лишний
                # ключ -ss в случае когда значение параметра будет совпадать с
                # именем какого-либо файла в системе.
                if [[ -e "$option" ]]
                then
                        if video_name=`get_video_name_by_file_name "$option"`
                        then
                                # Если воспроизведение этого видео файла было прервано ранее
                                if video_resume_pos=`get_resume_pos "$video_name"`
                                then
                                        options[$((i++))]='-ss'
                                        options[$((i++))]="$video_resume_pos"
                                fi
                        fi
                fi
        fi
done

# Запускаем mplayer с измененными параметрами командной строки
mplayer_ext_echo "Starting mplayer: mplayer ${options[@]}"
mplayer "${options[@]}" | tee "$tmp_file"

# Получаем все файлы, которые проигрывал mplayer -->
        files_in_output="`egrep --line-number 'Playing[[:space:]]+.+\.' $tmp_file`"

        for line in `seq \`echo "$files_in_output" | wc --lines\``
        do
                file_in_output=`echo "$files_in_output" | head --lines $line | tail --lines 1`
                files_lines[$((line-1))]=`echo "$file_in_output" | awk -F ':' '{ print $1 }'`
                files_paths[$((line-1))]=`echo "$file_in_output" | sed -r 's/^[[:digit:]]+:Playing[[:space:]]+//' | sed 's/\.$//g'`
        done
# Получаем все файлы, которые проигрывал mplayer <--

# Получаем всю необходимую информацию о каждом проигранном файле -->
        for num in `seq 1 ${#files_lines[*]}`
        do
                i=$((num-1))

                start_line=${files_lines[$i]}

                # Генерируем имя видео по имени файла
                if ! video_name=`get_video_name_by_file_name "${files_paths[$i]}"`
                then
                        mplayer_ext_echo "Skiping file '${files_paths[$i]}'"
                        continue
                fi

                # Если файл последний
                if [[ $num -eq ${#files_lines[*]} ]]
                then
                        end_line=$((`cat $tmp_file | wc --lines` + 1))

                        # Получаем строку со временем, на котором остановилось воспроизведение
                        if ! video_resume_string=`get_cur_pos_info`
                        then
                                # Получить строку не удалось - это может произойти по разным причинам,
                                # например, если не удалось открыть файл.
                                # Т. к. файл не проигрывался, то не запоминаем его позицию.
                                mplayer_ext_echo "Skiping file '${files_paths[$i]}'"
                                continue
                        fi

                        # Файл проигрался до конца
                        if [[ `cat $tmp_file | tail --lines 1` == 'Exiting... (End of file)' ]]
                        then
                                set_resume_pos "$video_name" 0
                        # Проигрывание файла было прервано
                        else
                                video_resume_time=`echo -n "$video_resume_string" | awk -F ':' '{ print $2 }'`

                                # Видео (для аудио отматывать не надо)
                                if [[ `echo -n "$video_resume_string" | awk -F ':' '{ print $1 }'` == 'V' ]]
                                then
                                        # "Отматываем" видео назад (приблизительно) до предыдущего ключевого кадра
                                        if [[ $((video_resume_time - keyint)) -lt 0 ]]
                                        then
                                                video_resume_time=0
                                        else
                                                video_resume_time=$((video_resume_time - keyint))
                                        fi
                                fi

                                set_resume_pos "$video_name" "$video_resume_time"
                        fi
                # Файл не последний
                else
                        end_line=${files_lines[$((i+1))]}

                        # Получаем строку со временем, на котором остановилось воспроизведение
                        if ! video_resume_string=`get_cur_pos_info`
                        then
                                # Получить строку не удалось - это может произойти по разным причинам,
                                # например, если не удалось открыть файл.
                                # Т. к. файл не проигрывался, то не запоминаем его позицию.
                                mplayer_ext_echo "Skiping file '${files_names[$i]}'"
                                continue
                        fi

                        set_resume_pos "$video_name" 0
                fi
        done
# Получаем всю необходимую информацию о каждом проигранном файле <--

cleanup

Приглашения, рассадочные карточки в Libreoffice

Потребовалось мне тут как-то оформить рассадочные карточки для свадьбы. Для своей свадьбы имена писались на каждую карточку вручную, для чужой свадьбы каждое имя стало писать лень и я решил это дело оптимизировать. С помощью данного способа можно также легко создать кучу персональных приглашений на мероприятие, напечатать почтовые конверты для многих адресатов и др. Задача решалась в свободном офисном приложении LibreOffice Writer (3.3.3), действия в OpenOffice аналогичные.

  1. Для начала выбрал красивый шрифт! Мне понравился Corinthia Regular TrueType Font.
    Шрифт Corinthia
    Шрифт устанавливал в Ubuntu через консоль следующей командой:

    sudo cp CORINTHI.TTF /usr/share/fonts/truetype/openoffice/
    

    Кнопку “Install Font” при кликании на шрифт сначала не разглядел, но думаю должно заработать и так.

  2. После этого создал новый документ в Writer:
    Новый документ в LibreOffice Writer
  3. Установил нужные параметры страницы – ориентацию, размер бумаги, поля
    Размеры полей
  4. Создал таблицу 3х1
    Создание таблицы
  5. После этого преобразовал список имён из такого вида:
    Иванов Иван Иванович
    Петров Пётр Петрович
    Сидоров Сидор Сидорович

    в удобный вид для дальнейшего использования макросами LibreOffice:

    "Иванов Иван Иванович", "Петров Пётр Петрович", "Сидоров Сидор Сидорович"

    Т.е. записал все имена в строчку через запятую и каждое имя в кавычках.
    Имена записывал в строчку с помощью VIM и следующих простых команд:

    :%s/^/"/g
    :%s/$/"/g
    :%s/\n/,/g
    :%s/"\ /"/g
    :%s/\ "/"/g
    :%s/"\ /"/g
    :%s/,$//g
    
  6. По умолчанию макросы отключены в LibreOffice и являются экспериментальной функцией. Включить макросы можно с помощью нажатия галочки в меню:
    “Tools | Options… | General | Enable experimental”
  7. Далее был написан следующий индусский код, который был записан в новый макрос Writer:
    sub Main
    dim document   as object
    dim dispatcher as object
    document   = ThisComponent.CurrentController.Frame
    dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
    dim col as integer
    dim row as integer
    dim args4(0) as new com.sun.star.beans.PropertyValue
    
    myarray=Array("Иванов Иван Иванович", "Петров Пётр Петрович", "Сидоров Сидор Сидорович")
    row=0
    Do while row<(UBound(MyArray) \ 3)+1
            col=0
            Do While col<3
                    dispatcher.executeDispatch(document, ".uno:JumpToNextCell", "", 0, Array())
                    col=col+1
            Loop
            args4(0).Name = "Text"
            col=0
            Do While col<3
                    args4(0).Value = myarray(row*3+col)
                    dispatcher.executeDispatch(document, ".uno:InsertText", "", 0, args4())
                    dispatcher.executeDispatch(document, ".uno:JumpToNextCell", "", 0, Array())
                    col=col+1
            Loop
            row=row+1
    Loop
    end sub
    
  8. Далее при установке курсора в первую ячейку таблицы и активации макроса Иванов, Петров и Сидоров автоматически помещались в таблицу.
  9. Остаётся только изменить размеры ячеек и выбрать нужный шрифт и его размер
    Размеры ячеек

Вот, что получилось в конечном итоге:
Вот, что получилось в конечном итоге

В моём случае 17 листов имён людей создались за пятнадцать-двадцать кликов мышкой!