Как стать автором
Обновить

Shutdown при завершении всех закачек Transmission

Время на прочтение 8 мин
Количество просмотров 11K
image
Люблю Transmission по его простоте и удобстве в использовании. Но то, что бы я добавил — это возможность автоматического выключения компьютера при завершении всех закачек. Делал эту фичу для себя под Убунту, но тем, кому это станет интересно, думаю, могут свободно переделать под другую ОС.

Задача была решена посредством службы RPC-JSON, любезно предоставляемой Transmission. Для включения данной фичи нужно в Transmission передя на вкладку «Веб-интерфейс» в Настройках поставить галочку «Включить веб-интерфейс». Если же нужно ограничить доступ к веб-панели от посторонних, можно задать пароль и имя пользователя.

Первый шаг — установка модуля для питона transmissionrpc. Достанем его:
hg clone www.bitbucket.org/blueluna/transmissionrpc
Примечание: Программа hg находится в пакете mercurial. Если он не установлен, потребуется его заинсталлить:
sudo apt-get install mercurial
После этого, когда исходник модуля лежит на локальном компьютере, нужно его установить, зайдя в директорию transmissionrpc:
cd transmissionrpc
sudo python setup.py install

Модуль установлен, можно писать сам скрипт. Но, поскольку наша конечная цель — выключение компьютера, которое по умолчанию не удастся сделать без административных полномочий, нам придется сделать еще несколько телодвижений.
  • Создадим скрипт /usr/bin/shutdown, написав в него:
    #!/bin/sh
    sudo /sbin/shutdown $*

    Как видно из текста скрипта — там используется обычный /sbin/shutdown, которому передаются все параметры командной строки, передаваемые нашему /usr/bin/shutdown. А также /sbin/shutdown выполняется с командной sudo, которая позволяет выполнять другие команды от имени администратора системы.
  • Присвоим этот скрипт группе shutdown:
    chgrp shutdown /usr/bin/shutdown
  • Установим право на запуск только группе shutdown:
    chmod g+x /usr/bin/shutdown
  • Чтобы sudo не попросило у нас пароль, добавим в файл /etc/sudoers строчку:
    %shutdown ALL= NOPASSWD: /sbin/shutdown
    Она значит то, что всем пользователям, входящим в группу shutdown разрешен запуск скрипта /sbin/shutdown с административными полномочиями без ввода пароля.
  • Создадим группу shutdown
    sudo groupadd shutdown
    и добавим себя в нее, воспользуясь groupadd/usermod или отредактировав файл /etc/group. Я пользовался последним способом, для этого нужно найти в файле /etc/group строку типа
    shutdown:x:1002:
    Чтобы добавить себя в эту группу, измените эту запись на вот такую:
    shutdown:x:1002:user1
    где user1 — это логин вашего пользователя. Если необходимо добавить еще несколько пользователей в эту группу, перечислите их через запятые в той же строке (без пробелов). Цифра 1001 (ИД группы) может у вас быть другой.
    В конечном виде у меня эта строка получилась такой:
    shutdown:x:1002:skymanphp

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

А вот и сам скрипт. Думаю, в комментариях ко скрипту все ясно.
Примечание. Поскольку я использовал собственный обработчик ошибок, пришлось в файл
/usr/local/lib/python2.6/dist-packages/transmissionrpc-0.5-py2.6.egg/transmissionrpc/transmission.py
внести изменения: строки 219-221 закомментировать, хотя это и некритично.

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Обратите внимание на вторую строку-комментарий в скрипте. Она позволяет избежать недоразумений с unicodной кодировкой в тексте скрипта.
  5.  
  6. #Импортируем необходимые функции из соответствующих модулей
  7. from time import localtime, strftime, sleep
  8. from sys import argv
  9. from os import geteuid,system
  10. from transmissionrpc import Client
  11. from os.path import isfile
  12.  
  13. # Не надо, чтобы пользователь был администратором. Ибо потом запускать Transmission от имени rootа нежелательно. Да и зачем тут админские полномочия?
  14. if geteuid()==0:
  15.     print "\033[1;31mВы не должны запускать этот скрипт от имени администратора!\033[1;m"
  16.     exit()
  17.  
  18. # Проверка, установлен ли libnotify-bin
  19. use_libnotify = isfile("/usr/bin/notify-send")
  20.  
  21. # Если нет, то выведем рекоммендацию сделать это для юзер-френдли уведомлений.
  22. if not use_libnotify:
  23.     # Спросим у пользователя
  24.     res = raw_input("\033[1;33mПакет libnotify-bin не установлен. Вы можете его установить командой\nsudo apt-get install libnotify-bin\nСделать это сейчас? Введите Y, если согласны и нажмите Enter: \033[1;m")
  25.     if res in ['Y','y']:
  26.         # Если он согласен, установим пакет
  27.         res = system("sudo apt-get install -y libnotify-bin")
  28.         if res == 0:
  29.             # Интересно, установился ли пакет?
  30.             print "\033[1;32mПакет libnotify-bin успешно установлен!\033[1;m"
  31.         else:
  32.             # Похоже, что неправильно введен пароль или же проблема с диспетчером пакетов. Хотя, далеко не последняя причина.
  33.             print "\033[1;31mОшибка установки пакета libnotify-bin. Попробуйте сделать это сами позже.\033[1;m"
  34.     else:
  35.         # Или не установим
  36.         print "\033[1;33mВ таком случае Вы можете установить его позже или вообще не устанавливать.\033[1;m"
  37.  
  38. try:
  39.     # Что ж, попробуем подключится к RPC-службе Transmission, успользуя указанный в его настрйках соответствующие параметры
  40.     tc = Client('localhost', port=9091, user=None, password=None)
  41. except:
  42.     # Э-э-э.. Transmission, похоже не запущен?
  43.     print "\033[1;31mОшибка подключения к Transmission. Возможно, он не запущен или веб-интерфейс не включен в настройках программы.\033[1;m"
  44.     # Поинтересуемся, нужно ли запустить программу сейчас
  45.     res = raw_input ("\033[1;33mЗапустить Transmission сейчас? Введите Y, если согласны и нажмите Enter: \033[1;m")
  46.     # Итак, что он там ввел?
  47.     if res in ['Y','y']:
  48.         # Что ж, запустим, если он согласен. Обязательно в фоновом режиме!
  49.         system("transmission &")
  50.         # и подожджем секунд 5, чтобы он загрузился
  51.         sleep(5)
  52.         print "\033[1;32mTransmission успешно запущен.\033[1;m"
  53.         # Попробуем еще раз подключится к RPC-службе Transmission
  54.             tc = Client('localhost', port=9091, user=None, password=None)
  55.  
  56.     else:
  57.         # Выходим. Не хотите, как хотите. Зачем тогда вообще было запускать этот скрипт?
  58.         print "\033[1;33mЗавершение работы скрипта.\033[1;m"
  59.         exit()
  60. else:
  61.     # Если все-таки мы установили связь с Transmission, то порадуем об этом пользователя
  62.     print "\033[1;32mПодключение к Transmission установлено.\033[1;m"
  63.  
  64. # Будем использовать флаг-переменную downloading для индикации, скачивается ли хотя бы один торрент. Начальное значение = True, чтобы запустить наш цикл проверки.
  65. downloading = True
  66.  
  67. # Пока идут закачки (проверка флага downloading)
  68. while downloading:
  69.     # И сразу сбросим его в False
  70.     downloading = False
  71.     # Сколько торрентов?
  72.     trc = len(tc.list())
  73.     # Очистим консоль для красивого вывода
  74.         system("clear")
  75.     # Раскрасим: \033[яркость;цвет
  76.         print "\033[1;36m==== Статус загрузки ====\033[1;m"
  77.     # начнем перебирать все торренты
  78.     for i in range(1, trc):
  79.         # Получим информацию о торренте с индексом i
  80.         torrent = tc.info(i)[i]
  81.         # Цвет по умолчанию
  82.         col = "\033[2;37m"
  83.         # На вкус и цвет товарища нет!
  84.         if torrent.status == "seeding":
  85.             col = "\033[1;36m"
  86.         elif torrent.status == "downloading":
  87.             col = "\033[1;32m"
  88.         elif torrent.status == "checking":
  89.             col = "\033[1;34m"
  90.         print col + torrent.status + " (" + str("%.3g" % torrent.progress) + "%) " + torrent.name + "\033[1;m"
  91.         # Он скачивается/проверяется? Если нет, то этот цикл прервется, так как флаг downloading будет установлен в False
  92.         downloading = downloading or (torrent.status == "downloading") or (torrent.status == "checking")
  93.         print "\033[1;36m==== Всего торрентов: " + str(trc) + " ====\033[1;m"
  94.     # Передохнем перед последующей проверкой
  95.     sleep(10)
  96.  
  97. # Раз уж мы сюда дошли, значит downloading = False. Что ж, начнем процедуру завершения работы компьютера
  98. try:
  99.     # Если установлен пакет libnotify-bin, скажем это пользователю красиво.
  100.     if use_libnotify:
  101.          system('notify-send "Все загрузки завершены" "Система завершает работу. Можете прервать работу скрипта нажатием Ctrl+C в окне скрипта." -i /usr/share/icons/gnome/scalable/actions/exit.svg')
  102.     # Или не совсем красиво...
  103.     print "\033[1;31mВсе загрузки завершены. Система завершает работу. Можете прервать работу скрипта нажатием Ctrl+C в окне скрипта.\033[1;m"
  104.     # Дадим пользователю последний шанс - 30 секунд на прерывание скрипта
  105.     sleep(30)
  106. # если все-таки он прервал его нажатием комбинации клавиш Ctrl+C
  107. except KeyboardInterrupt:
  108.     # Если установлен пакет libnotify-bin, скажем это пользователю красиво.
  109.         if use_libnotify:
  110.                  system('notify-send "Завершение работы остановлено" "Прервано пользователем." -i /usr/share/icons/gnome/scalable/actions/redo.svg')
  111.     # Или не совсем красиво...
  112.     print "\033[1;31mЗавершение работы остановлено" "Прервано пользователем. Выход из скрипта\033[1;m"
  113.     # Выходим прочь из скрипта
  114.     exit()
  115.  
  116. # А вот она, собственно функция, инициирующая завершение работы системы. Shutdown!
  117. system("/usr/bin/shutdown -h now")
  118. # Если вдруг будет интересно уснувшему пользователю, когда скрипт начал завершение работы, откроем для этого файлик в режиме дозаписи
  119. f = open('/tmp/shutdownctl.time', 'a')
  120. # И запишем туда наше событие, указав время.
  121. f.write('Завершение работы было начато в ' + strftime("%a, %d %b %Y %H:%M:%S", localtime()) + '\n')
  122. # И, наконец, закроем лог-файл.
  123. f.close
  124. # Вот и все!
* This source code was highlighted with Source Code Highlighter and modified by SkyManPHP.


При желании можно допилить, восполуясь документацией по модулю transmissionrpc. Хотя, полагаю, что такие решения уже в Интернете есть, хоть я их не нашел в таком виде, как бы мне хотелось.
Поэтому, на оригинальность не претендую. Да и не пишу в Питоне вообще — некоторые команды в гугле подсмотрел. Но если кому понадобится — буду рад.
Теги:
Хабы:
+20
Комментарии 94
Комментарии Комментарии 94

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн