Cериалокачалка на python3 с поддержкой расширений

в 11:48, , рубрики: aria2c, MacOS, OS X, p2p, python, python3, terminal-notifier, torrent, говнокод, Программирование, Разработка под OS X, сериалы

В качестве вступления стоит сказать что я пользуюсь macOS и потому некоторые части:

  • Автозапуск (launchd)
  • Формат конфигурационных файлов (plist)
  • Программа, используемая для уведомлений (terminal-notifier)

будут специфичны для этой ОС. Однако, если Вас заинтересует проект, думаю, поменять пару путей и слегка поправить несколько функций, чтобы заставить этот код работать с Вашей системой уведомлений, yaml'ом и, допустим, кроном, не составит труда.

Итак, к делу.

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

Что же он содержит?

  1. Класс парсера в parser.py
  2. Класс загрузчика в core.py
  3. Плагины, наследующие класс загрузчика в директории pugins
  4. Файлы конфигурации плагинов в директории configs
  5. Директорию icons для иконок увдомлений
  6. Файл main.py, дергающий плагины
  7. Файл install.py

Класс парсинга

Так как почти на всех сайтах сериалов есть свои RSS-ленты, было решено для упрощения процесса написания плагинов слепить функцию парсинга, позволяющую автоматически распарсить rss по ссылке и выдать список пар (заголовок, ссылка) с возможностью указания кодировки.

Чуть позже пришла необходимость простого парсинга html, а потом подумалось что некоторые сайты могут отдавать ссылки и через api в формате json.

Так функция превратилась в класс, хранящий сессию (requests Session) для сайтов, требующих авторизации и возвращающий response-объект с методами html(), rss(), json() и auto(). Последний пытается угадать содержимое (xml, html или json) и кодировку по заголовку 'Content-Type'. По умолчанию — 'utf-8' и html.

  • Метод html() возвращает объект beautifulsoup всей страницы
  • Метод rss() вызвращает массив кортежей (заголовок, ссылка)
  • Метод json() возвращает объект, полученный методом json.loads текста страницы

Класс загрузчика

Предоставляет плагинам методы parse() и download().

  • Метод parse() предоставляет возможность передать ссылку и параметры url. Он же проверяет 'If-Modified-Since' если сайт предоставляет данные о последнем изменении. Возвращает response-объект из файла parse.py.
  • Метод download() принимает словарь с данными о названии сериала, эпизода, ссылкой и прочими мелочами, а так же http-заголовки и куки. А далее самостоятельно производит всю магию. Загружает файл в предварительно созданную им папку "Название_сериала[имя_класса_плагина]", где предполагается что имя класса плагина — название студии озвучания/сайта с которого происходит загрузка. Если файл — торрент, загружает эпизод с торрента и удаляет файл торрента. Загрузка производится с помощью aria2c, который должен быть установлен в системе и находиться в одной из директорий, прописанных в $PATH. После чего вызывается метод save(), который сохраняет ссылки в файл конфигурации для предотвращения повторной загрузки.
  • Метод __notify(), который вызывается при начале и окончании загрузки, если по пути /usr/local/bin/terminal-notifier обнаружено соответствующее приложение. Посмотреть как это выглядит можно чуть ниже.

Плагины

Классы, наследующие класс загрузчика, реализующие метод start() в котором надо произвести парсинг и вызвать метод download() с ожидаемыми параметрами.

Файлы конфигурации плагинов

*.plist

Содержат:

  • Интересующее качество
  • Список подписок
  • Список исключений ("полный сезон", например)
  • last_modified для сайтов, дающих информацию о последнем изменении
  • Сюда же будет записан список ссылок для предотвращения повторной загрузки

Директория icons

Папка в которую можно положить файлы иконок, которые будут использованы при уведомлении о загрузке/окончании загрузки эпизодов из соответствующих источников. Названия иконок должны соответствовать названиям плагинов, но с расширеним jpg/ico/png.

main.py

Дергает поочередно метод start() всех плагинов в директории plugins.

install.py

  • Установит зависимости
  • Переместит файлы проекта в ~/.NB-series-downloader
  • Создаст файл автозапуска
  • Удалит директорию склонированного репозитория

Автозагрузка

Файл install.py сделает это за Вас, но если Вы решили сделать все вручную, то...

Насколько мне известно, launchd ничего не знает о переменных окружения, посему Вам придется самостоятельно заменить в тексте ниже $HOME на путь до домашнего каталога вашего пользователя /Users/Username и, возможно, изменить путь до директории со скриптами, если вы положили их в другое место.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Disabled</key>
    <false/>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/usr/local/bin</string>
    </dict>
    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>
    <key>Label</key>
    <string>series_downloader</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/python3</string>
        <string>$HOME/.NB-series-downloader/main.py</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StartInterval</key>
    <integer>900</integer>
    <key>WorkingDirectory</key>
    <string>$HOME/.NB-series-downloader</string>
</dict>
</plist>

Сам файл с этим содержимым стоит назвать series_downloader.plist и положить в ~/Library/LaunchAgents/.
Загрузить описанные в этом файле задания сразу можно командой

launchctl load ~/Library/LaunchAgents/series_downloader.plist

Иначе задания активируются при следующей загрузке компьютера.
Эти действия заставят main.py, лежащий в директории $HOME/.NB-series-downloader/, запускаться каждые 900 секунд (15 минут).

Итого

Ссылка на github.

Честно говоря, первое что я туда выкладываю, так что мог напортачить. Подскажите, если что не так.

Уведомление о начале загрузки:

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

Сериалы в моем случае складываются в Series в том же каталоге (можно указать иное место в self.series_dir класса downloader в core.py).

А для просмотра загруженных серий мною написан workflow для Alfred 3:
Список сериалов
После просмотра каждой серии он предлагает:

  • Удалить оную (я не храню архив, всё просмотренное отправляется в корзину)
  • Запрашивает оценку эпизода от 1 до 5, а так же отмечает только что просмотренную таковой в моем аккаунте на myshows.me и выставляет рейтинг эпизода используя их api
  • Выводит уведомление о том удачно ли все прошло
  • По клику на уведомление можно открыть страницу только что просмотренного эпизода на myshows

Но это всё — уже совсем другая история...

Автор: FantomNotaBene

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js