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
Здесь можно Markdown