Песочница →
Парсер RSS на bash для LostFilm.TV: Transmission + SQLite + mkvtools
Здравия желаю, Хабр!
В ответ на предыдущий топик про парсер RSS-ленты LostFilm.TV хочу выложить свой вариант работающий уже около 4х месяцев без каких-либо ошибок.
Суть идеи состоит в том, что сервер качает сериалы и раскладывает по папкам, оформляя при этом файлы с обложкой и нормальным заголовком.
Однако, в работе всей системы участвует не один скрипт, а целый набор скриптов. В такой системе скрипты разделены на pre-обработку и post-обработку.
И, конечно же, для эстетов: файлы каждой серии должны выглядеть красиво и быть разложены по папкам.
• Прочитать RSS-ленту
• Выполнить разбор на отдельные элементы
• Отсеять старые элементы
• Добавить новые элементы в базу и в очередь загрузки transmission
Далее, когда файл загружен его нужно привести в эстетический вид (моя жена предпочитает видеть все сериалы, разложенными по папкам с именем сериала и, имеющими имя файла, содержащее номер сезона/эпизода и название серии, а также с красивой обложкой-иконкой сериала).
После загрузки сериала срабатывает цепочка скриптов, направленных на обработку различного рода файлов, среди которых есть и lostfilm.tv.
• Выполнить проверку наличия в БД имени файла полученного от демона, иначе завершить работу
• Выгрузить обложку из базы
• Применить mkvtools (фильтр англ дорожки, вставка обложки, запись заголовка)
• Положить в папку по имени сериала (создать, если нет)
• Уведомить подписанных по почте и через SMS (мне на телефон, жене на почту)
Из скриптов видно, что работа идет с базой данных, в моем варианте используется SQLite3
Обложки хранятся внутри БД в виде base64, понятно, что от этого база станет жирнее, но все же я предпочел хранить все о сериалах внутри одной базы.
Осталось сделать те вещи, которые уже не так значимы для меня и может быть будут когда-то сделаны.
1. Перевести базу на MySQL/PostgreSQL
2. Сделать веб-интерфейс к базе
3. Сделать автоматическое добавление нового сериала в базу (с обложкой)
n. Будущие исправления в распознователь RSS-ленты
Остальное публике должно быть понятно. В коде есть комментарии.
Скрипт работает уже 4 месяца без сбоев и ошибок (хотя сегодня вот была Эврика с сезоном номер 0, но это был косяк на самом сайте). Сам я военнослужащий и дома бываю примерно раз в 1-2 недели, соответственно работает все в автоматическом режиме с редким контролем, я лишь приезжаю и сливаю новые серии на винт, чтобы что-то посмотреть в части.
Спасибо за внимание!
В ответ на предыдущий топик про парсер RSS-ленты LostFilm.TV хочу выложить свой вариант работающий уже около 4х месяцев без каких-либо ошибок.
Суть идеи состоит в том, что сервер качает сериалы и раскладывает по папкам, оформляя при этом файлы с обложкой и нормальным заголовком.
Однако, в работе всей системы участвует не один скрипт, а целый набор скриптов. В такой системе скрипты разделены на pre-обработку и post-обработку.
И, конечно же, для эстетов: файлы каждой серии должны выглядеть красиво и быть разложены по папкам.
pre-обработчик
Алгоритм работы парсера
• Прочитать RSS-ленту
• Выполнить разбор на отдельные элементы
• Отсеять старые элементы
• Добавить новые элементы в базу и в очередь загрузки transmission
Код парсера
#!/bin/bash
export PRFX="/var/lib/transmission-daemon"
export SELF=$(basename $(readlink -f $0))
# Загрузка конфигурации
. $PRFX/.$SELF/config
# Загрузка необходимых функций
. $PRFX/.funcs/sqlite
. $PRFX/.funcs/transmission
# Проверка и запись значения последней даты
check_last() {
if [ ! -f $LFW_RSS_LAST ] || [ $1 -gt $(cat $LFW_RSS_LAST) ]; then
printf "$1" > $LFW_RSS_LAST
return 0
else
return 1
fi
}
# Функция разбора заголовка
parse_title() {
echo "$1" | sed -r 's/^([^(]+)[. ]+\((.+)\)[. ]+([^(]+)[. ]+\((.+)\)[. ]*(\[720p\]){0,1}[. ]*\(S0*([0-9]+)E0*([0-9]+).*\)$/\1|\2|\3|\4|\5|\6|\7/'
}
logger -t $SELF -- 'Запрос данных RSS/Atom'
# Запрос RSS-ленты
rsstail -1NHlp -n 30 -u "$LFW_RSS_URL" |
# Конвертация потока в совместимый со скриптом формат
iconv -f cp1251 | sed -r '/^\s*$/d; s/^\s+//; s/\s+$//' | sed -r '$!N; s/\n/|/; $!N; s/\n/|/' |
# Конвертация даты в UNIXTIME и сортировка по убыванию даты
(IFS='|'; while read item_title item_link item_date; do
printf '%s|%s|%s\n' $(date -d "$item_date" +%s) "$item_title" "$item_link"
done) | sort |
# Отсеивание новых элементов
(IFS='|'; while read item_date item_title item_link; do
if check_last $item_date; then
printf '%s|%s|%s\n' $item_date "$(parse_title "$item_title")" "$item_link"
fi
done) |
# Обработка новых элементов
(IFS='|'; while read date name_ru name_en title_ru title_en hd season episode link; do
# получение идентификатора сериала из базы
id=$(printf 'SELECT id FROM series WHERE title_en = "%s";' "$name_en" | db_query $LFW_DB)
# если есть то обрабатываем, иначе уходим
if [ -n "$id" ]; then
# загрузка торрент-файла в во временное расположение
tr_file="/tmp/lostfilm_$(uuidgen).torrent"
if wget -nv -q --header "$LFW_WGET_AUTH" "$link" -O "$tr_file"; then
# проверяем добавляли ли ранее сведения о серии в базу, если нет то добавить
if [ -z $(printf 'SELECT id FROM episodes WHERE series = %d AND season = %d AND episode = %d;' $id $season $episode | db_query $LFW_DB) ]; then
printf 'INSERT INTO episodes (series, season, episode, title_en, title_ru) VALUES (%d, %d, %d, "%s", "%s");' \
$id $season $episode $title_en $title_ru | db_query $LFW_DB
fi
# здесь идет разделение для HD и SD вариантов серий
if [ -z "$hd" ]; then
file=$(transmission-show "$tr_file" | sed -r '/^Name:/!d; s/^Name:\s*(.+)\s*$/\1/') #'
printf 'INSERT INTO files (id, date, filename) VALUES ((SELECT id FROM episodes WHERE series = %d AND season = %d AND episode = %d), %d, "%s");' \
$id $season $episode $date $file | db_query $LFW_DB
if [ $(printf 'SELECT tracked FROM series WHERE id = %d;' $id | db_query $LFW_DB) -ne 0 ]; then
transmission --add "$tr_file" --start > /dev/null
fi
logger -t $SELF -- $(printf 'Добавлена новая SD серия «%s» (сезон %s, серия %s) — «%s»' "$name_ru" "$season" "$episode" "$title_ru") #'
else
file=$(transmission-show "$tr_file" | sed -r '/^Name:/!d; s/^Name:\s*(.+)\s*$/\1/') #'
printf 'INSERT INTO files_hd (id, date, filename) VALUES ((SELECT id FROM episodes WHERE series = %d AND season = %d AND episode = %d), %d, "%s");' \
$id $season $episode $date $file | db_query $LFW_DB
if [ $(printf 'SELECT tracked_hd FROM series WHERE id = %d;' $id | db_query $LFW_DB) -ne 0 ]; then
transmission --add "$tr_file" --start > /dev/null
fi
logger -t $SELF -- $(printf 'Добавлена новая HD серия «%s» (сезон %s, серия %s) — «%s»' "$name_ru" "$season" "$episode" "$title_ru") #'
fi
fi
[ -f "$tr_file" ] && rm -f "$tr_file"
fi
done)
logger -t $SELF -- 'Завершено'
post-обработчик
Далее, когда файл загружен его нужно привести в эстетический вид (моя жена предпочитает видеть все сериалы, разложенными по папкам с именем сериала и, имеющими имя файла, содержащее номер сезона/эпизода и название серии, а также с красивой обложкой-иконкой сериала).
После загрузки сериала срабатывает цепочка скриптов, направленных на обработку различного рода файлов, среди которых есть и lostfilm.tv.
Алгоритм работы скрипта пост-обработки
• Выполнить проверку наличия в БД имени файла полученного от демона, иначе завершить работу
• Выгрузить обложку из базы
• Применить mkvtools (фильтр англ дорожки, вставка обложки, запись заголовка)
• Положить в папку по имени сериала (создать, если нет)
• Уведомить подписанных по почте и через SMS (мне на телефон, жене на почту)
Скрипт post-обработки
#!/bin/bash
export SELF="lostfilm-rss"
# Загрузка конфигурации
. $PRFX/.$SELF/config
# Загрузка необходимых функций
. $PRFX/.funcs/sqlite
. $PRFX/.funcs/transmission
. $PRFX/.funcs/mkv_tools
. $PRFX/.funcs/mail_notify
. $PRFX/.funcs/utils
. $PRFX/.funcs/sms_notify
MAIL_LIST_HD="me@a***n.ru"
# выполняем запрос к БД о наличии файла
data="$(printf 'SELECT s.title_ru, e.season, e.episode, e.title_ru, s.id FROM episodes e, series s, files_hd f WHERE e.series = s.id AND e.id = f.id AND f.filename = "%s";' "$TR_TORRENT_NAME" | db_query $LFW_DB)"
# если есть то обрабатываем, иначе отбой
if [ -n "$data" ]; then
# разбор результата запроса
name=$(echo $data | cut -d'|' -f1)
s=$(echo $data | cut -d'|' -f2)
e=$(echo $data | cut -d'|' -f3)
part=$(echo $data | cut -d'|' -f4)
id=$(echo $data | cut -d'|' -f5)
# эстетическое имя файла
mkv_file=$(printf '/mnt/videos/Series.HD/%s [%s.%s] — %s.mkv' "$name" "$s" "$e" "$part")
# форматирование заголовка видео-файла
mkv_title=$(printf '«%s» • Сезон %s, Серия %s • «%s»' "$name" "$s" "$e" "$part")
# выгрузка обложки из базы
mkv_poster="/tmp/mkv_poster_$(uuidgen).jpg"
printf 'SELECT data FROM posters WHERE series = %d ORDER BY date DESC LIMIT 1;' $id | db_query $LFW_DB | base64 -d - > $mkv_poster
logger -t $SELF -- $(printf 'Загружена HD серия «%s» (сезон %s, серия %s) — «%s»' "$name" "$s" "$e" "$part") #'
# ремукс видео-файла с заданными параметрами
if to_mkv "/mnt/torrent/$TR_TORRENT_NAME" "$mkv_file" "$mkv_title" "$mkv_poster"; then
logger -t $SELF -- $(printf 'Обработана HD серия «%s» (сезон %s, серия %s) — «%s»' "$name" "$s" "$e" "$part") #'
# я не раздаю файлы, поскольку подсчет рейтинга сломан уже два года, он у меня не меняется, удаляем торрент вместе с исходником
transmission -t $TR_TORRENT_ID --remove-and-delete > /dev/null
logger -t $SELF -- $(printf 'Удалена из торрента HD серия «%s» (сезон %s, серия %s) — «%s»' "$name" "$s" "$e" "$part") #'
# уведомляем по почте
mail_notify "$MAIL_LIST_HD" "$(printf 'Доступна новая серия «%s» (сезон %s, серия %s) — «%s»' "$name" "$s" "$e" "$part")" \
"$(printf '<h3>«%s» (сезон %s, серия %s) — «%s»</h3>
<i>Качество: HD</i>' "$name" "$s" "$e" "$part")"
# отправляем смс
sms_notify a***n "$(printf 'Новая серия «%s» (сезон %s, серия %s) — «%s» \n[%s]' "$name" "$s" "$e" "$part" "$(disk_info)")"
else
logger -t $SELF -- $(printf 'Произошла ошибка при обработке HD серии «%s» (сезон %s, серия %s) — «%s»' "$name" "$s" "$e" "$part") #'
fi
rm -f $mkv_poster
fi
База данных
Из скриптов видно, что работа идет с базой данных, в моем варианте используется SQLite3
Структура базы данных
CREATE TABLE episodes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
series INTEGER REFERENCES series(id) ON UPDATE CASCADE ON DELETE CASCADE,
season INTEGER NOT NULL,
episode INTEGER NOT NULL,
title_en TEXT NOT NULL,
title_ru TEXT NOT NULL,
UNIQUE(series, season, episode)
);
CREATE TABLE files (
id INTEGER PRIMARY KEY REFERENCES episodes(id) ON UPDATE CASCADE ON DELETE CASCADE,
date INTEGER NOT NULL,
filename TEXT NOT NULL
);
CREATE TABLE files_hd (
id INTEGER PRIMARY KEY REFERENCES episodes(id) ON UPDATE CASCADE ON DELETE CASCADE,
date INTEGER NOT NULL,
filename TEXT NOT NULL
);
CREATE TABLE posters (
id INTEGER PRIMARY KEY AUTOINCREMENT,
series INTEGER REFERENCES series(id) ON UPDATE CASCADE ON DELETE CASCADE,
date INTEGER NOT NULL,
data TEXT NOT NULL
);
CREATE TABLE series (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title_en TEXT NOT NULL,
title_ru TEXT NOT NULL,
tracked INTEGER DEFAULT 0
, tracked_hd integer default 1);
Обложки хранятся внутри БД в виде base64, понятно, что от этого база станет жирнее, но все же я предпочел хранить все о сериалах внутри одной базы.
TODO и PS
Осталось сделать те вещи, которые уже не так значимы для меня и может быть будут когда-то сделаны.
1. Перевести базу на MySQL/PostgreSQL
2. Сделать веб-интерфейс к базе
3. Сделать автоматическое добавление нового сериала в базу (с обложкой)
n. Будущие исправления в распознователь RSS-ленты
Остальное публике должно быть понятно. В коде есть комментарии.
Скрипт работает уже 4 месяца без сбоев и ошибок (хотя сегодня вот была Эврика с сезоном номер 0, но это был косяк на самом сайте). Сам я военнослужащий и дома бываю примерно раз в 1-2 недели, соответственно работает все в автоматическом режиме с редким контролем, я лишь приезжаю и сливаю новые серии на винт, чтобы что-то посмотреть в части.
Спасибо за внимание!
18.09.2011 01:07+0400