English | Русский

Блог о Linux и велосипедах

Пакетное перемещение торрентов в Transmission

Недавно я приобрёл бюджетный неттоп ZOTAC ZBOX SD-ID12, который использую в качестве домашнего сервера, wi-fi точки доступа и для хранения резервных копий данных. Вот и решил я перенести на него все торренты. Пользовался я раньше gtk+-версией Transmission, а на «коробочку» я поставил, разумеется, trasmsission-daemon. Необходимо было перенести все файлы и торренты. Пути к файлам, к сожалению, изменились, и нужно было это как-то указать Transmission'у. Собственно, тому, как это можно сделать, и посвящена данная заметка.

Transmission хранит все торренты в каталоге ~/.config/transmission/torrents, а всю дополнительную информацию, такую как место загрузки, приоритет, состояние и прочее в ~/.config/transmission/resume1. Одному файлу *.torrent в первом каталоге соответствует один файл *.resume во втором. Имена обоих файлов определяются по шаблону <имя торрента>.<хеш>.<расширение>

Файлы *.resume хранятся в том же формате, что и торрент-файлы — в Bencode. Формат бинарный, хоть и (тяжело) читаемый в текстовом виде. В нём могут храниться целочисленные значения, массивы байт, списки и словари. Целочисленное значение хранится как i<число>e, где <число> имеет десятичное представление в виде строки, например i935e. Массив байт кодируется как <длина>:<содержимое>, например, 4:data. Списки и словари кодируются как l<содержимое>e и d<содержимое>e соответственно. Подробнее рассматривать формат не имеет смысла, так как нам этого вполне хватит.

resume-файл представляет из себя словарь свойств, описание которых можно найти тут или в самом исходном файле resume.c. Нас интересует свойство destination, которое и определяет место расположения данных. Очевидно, что если просто заменить путь на новый, то файл повредится из-за несоответствия длины старого и нового пути (если конечно нам не повезёт, и длина пути совпадёт). При этом, если место хранения для всех торрентов одинаковое, то пакетное перемещение сводится к тому, что нужно посчитать новую длину пути и заменить участки всех resume-файлов с 11:destination<old_length>/old/destination/path на 11:destination<new_length>/new/destination/path. В случае, если значение destination отличается, то необходимо пересчитывать длину пути для каждого файла. Так как у меня именно этот случай, я создал небольшой shell-скрипт, который делает это автоматически:

#!/bin/bash

CONFIG="${HOME}/.config/transmission"
RESUME="${CONFIG}/resume"
ORIG="${CONFIG}/resume.orig"

if test $# != 2; then
    echo "Usage: $0 PATTERN REPLACEMENT"
    exit 1
fi

# escape comma in PATTERN and REPLACEMENT for use in sed
PATTERN=$(sed -e "s=,=\\\\,=g" <<< "$1")
REPLACEMENT=$(sed -e "s=,=\\\\,=g" <<< "$2")

if test ! -d "${RESUME}"; then
    echo "error: $RESUME doesn't exist or is not a directory"
    exit 1
fi

if test -d "$ORIG"; then
    echo "error: $ORIG already exists"
    exit 1
fi

echo "copying $RESUME to $ORIG"
cp -R "$RESUME" "$ORIG"
test $? == 0 || exit 1
echo "copying finished"

echo "processing directory \"${ORIG}\"..."

find "$RESUME" -type f -name "*.resume" | while read file
do
    echo "processing $(basename "$file")"

    # calculate position of destination property
    dest=$(grep -aob "11:destination[0-9]\+" "$file")
    old_len=$(grep -o "[0-9]\+$" <<< "$dest")
    pos=$(grep -o "^[0-9]\+" <<< "$dest")
    end=$(($pos+14+${#old_len}+1+$old_len))

    # fetch the destination path, make the substitution
    old_path="$(head -c $end "$file"| tail -c $old_len)"
    new_path="$(sed -e "s,${PATTERN},${REPLACEMENT}," <<< "$old_path")"

    if test "$old_path" = "$new_path"; then
        echo "no need to update the file"
        continue
    fi

    # new path length in bytes, not characters
    new_len=$(expr length "$new_path")
    new_dest="11:destination${new_len}:${new_path}"

    # write temporary file and replace the original one
    tmpfile="${file}.tmp.$$"
    head -c $pos "$file" > "$tmpfile"
    echo -n "$new_dest" >> "$tmpfile"
    tail -c +$((${end}+1)) "$file" >> "$tmpfile"
    mv "$tmpfile" "$file"

    echo "file processed"
done

Использовать скрипт так:

transmission-batch-move ШАБЛОН ЗАМЕНА

где ШАБЛОН представляет собой регулярное выражение (как можно видеть, оно в конечном итоге подставляется в sed). Например, если данные переместились из /home/olduser/olddata/torrents в /home/user/data/torrents, то скрипт можно запускать так:

transmission-batch-move /home/olduser/olddata/ /home/user/data/

Скрипт создаст резервную копию каталога с resume-файлами и назовёт его resume.orig. А resume-файлы по стандартному пути будут заменены на новые. Скрипт укажет на те файлы, в которых замена не нужна (скорее всего, это файлы, в свойстве destination которых не был найден ШАБЛОН).

Скрипт должен нормально работать с путями, которые содержат пробелы и нелатинские символы. Но всё равно я рекомендую самостоятельно удостоверится в правильном результате.

PS Завёл аккаунт на github. Этот скромный скрипт там пока единственный.

PPS Новый год что ли? Хотелось бы, чтобы в новом году заметки появлялись регулярно. Благо, идей для этого достаточно. Нужно лишь немного времени и желания. В начале года было бы хорошо обновить статус xatk на Google Code, сделать релиз, написать заметку об изменениях. Но до этого ещё нужно поработать... И конечно же я верю, что в следующем году блог, наконец-то, оправдает своё название!


  1. trasmsission-daemon хранит все файлы аналогичным образом в ~/.config/transmission-daemon 

Комментарии

RSS
  • Oleg
    08 Июл 2018, 14:15

    А представьте ситуацию: оригинальная раздача содержит каталог, а в нём несколько файлов. А мне необходимо поменять путь только к одному файлу из этой раздачи. Как это реализовать?

  • Евген
    15 Дек 2018, 18:43

    Приветсвую, хотел прикрутить на free4nas , на первой же строчке с sed затык. На bsd синтаксис отличается очевидно? Прошу прощения , я уже стар для разработки.

  • spfsab
    13 Янв 2020, 08:13

    Спасибо за идею! Очень помогло при переезде с одного NAS на другой. К сожалению, вот только приведенный код оказался абсолютно не рабочим, как со стороны предустановленного shell, так и алгоритма в целом. Пришлось вникать и править, после чего удалось успешно поднять старые торренты на новой коробочке.

  • Алексей
    23 Сен 2020, 14:55

    Есть более простой способ: Останавливаем раздачу всех торрентов Transmission. При помощи Transmission Remote GUI задаем расположение скаченных торрентом без переноса данных.(С указанием того пути, который будет на новом устройстве) Данное действие можно делать группами раздач. Я таким образом перенес около 1000 раздач при смене NAS. Потратил дня два на то, как это можно сделать. И найдя решение за 30 мин изменил пути. Само копирование медиатеки - это совсем другой процесс, После этого файл настройки с папками торрент и резюме файлами скопировал в новое устройство. Взлетело с пол пинка.

Здесь можно Markdown