git-annex/doc/special_remotes/external/git-annex-remote-torrent
Joey Hess 18d326cb6f
external protocol VERSION 2
Support VERSION 2 in the external special remote protocol, which is
identical to VERSION 1, but avoids external remote programs neededing to
work around the above bug. External remote program that support
exporttree=yes are recommended to be updated to send VERSION 2.

Sponsored-by: Kevin Mueller on Patreon
2023-03-28 17:00:08 -04:00

205 lines
5.2 KiB
Bash
Executable file

#!/bin/sh
# This is a demo git-annex external special remote program,
# which adds basic torrent download support to git-annex.
#
# Uses aria2c. Also needs the original bittorrent (or bittornado) for the
# btshowmetainfo command.
#
# Install in PATH as git-annex-remote-torrent
#
# Enable remote by running:
# git annex initremote torrent type=external encryption=none externaltype=torrent
# git annex untrust torrent
#
# Copyright 2014 Joey Hess; licenced under the GNU GPL version 3 or higher.
set -e
# This program speaks a line-based protocol on stdin and stdout.
# When running any commands, their stdout should be redirected to stderr
# (or /dev/null) to avoid messing up the protocol.
runcmd () {
"$@" >&2
}
# Gets a VALUE response and stores it in $RET
getvalue () {
read resp
# Tricky POSIX shell code to split first word of the resp,
# preserving all other whitespace
case "${resp%% *}" in
VALUE)
RET="$(echo "$resp" | sed 's/^VALUE \?//')"
;;
*)
RET=""
;;
esac
}
# Get a list of all known torrent urls for a key,
# storing it in a temp file.
geturls () {
key="$1"
tmp="$2"
echo GETURLS "$key"
getvalue
while [ -n "$RET" ]; do
if istorrent "$RET"; then
echo "$RET" >> "$tmp"
fi
getvalue
done
}
# Does the url end in .torrent?
# Note that we use #N on the url to indicate which file
# from a multi-file torrent is wanted.
istorrent () {
echo "$1" | egrep -q "\.torrent(#.*)?$"
}
# Download a single file from a torrent.
#
# Note: Does not support resuming interrupted transfers.
# Note: Does not feed progress info back to git-annex, and since
# the destination file is only populated at the end, git-annex will fail
# to display a progress bar for this download.
downloadtorrent () {
torrent="$1"
n="$2"
dest="$3"
tmpdir="$(mktemp -d)"
# aria2c will create part of the directory structure
# contained in the torrent. It may download parts of other files
# in addition to the one we asked for. So, we need to find
# out the filename we want, and look for it.
wantdir="$(btshowmetainfo "$torrent" | grep "^directory name: " | sed "s/^directory name: //" || true)"
if [ -n "$wantdir" ]; then
wantfile="$(btshowmetainfo "$torrent" | grep '^ ' | sed 's/^ //' | head -n "$n" | tail -n 1 | sed 's/ ([0-9]*)$//')"
if ! runcmd aria2c --select-file="$n" "$torrent" -d "$tmpdir"; then
false
fi
else
wantfile="$(btshowmetainfo "$torrent" | egrep "^file name.*: " | sed "s/^file name.*: //")"
wantdir=.
if ! runcmd aria2c "$torrent" -d "$tmpdir"; then
false
fi
fi
if [ -e "$tmpdir/$wantdir/$wantfile" ]; then
mv "$tmpdir/$wantdir/$wantfile" "$dest"
rm -rf "$tmpdir"
else
rm -rf "$tmpdir"
false
fi
}
# This has to come first, to get the protocol started.
echo VERSION 2
while read line; do
set -- $line
case "$1" in
INITREMOTE)
echo INITREMOTE-SUCCESS
;;
PREPARE)
echo PREPARE-SUCCESS
;;
CLAIMURL)
url="$2"
if istorrent "$url"; then
echo CLAIMURL-SUCCESS
else
echo CLAIMURL-FAILURE
fi
;;
CHECKURL)
url="$2"
# List contents of torrent.
tmp=$(mktemp)
if ! runcmd curl --proto -all,http,https -o "$tmp" "$url"; then
echo CHECKURL-FAILURE
else
oldIFS="$IFS"
IFS="
"
printf "CHECKURL-MULTI"
n=0
for l in $(btshowmetainfo "$tmp" | grep '^ ' | sed 's/^ //'); do
# Note that the file cannot contain spaces.
file="$(echo "$l" | sed 's/ ([0-9]*)$//' | sed 's/ /_/g')"
size="$(echo "$l" | sed 's/.* (\([0-9]*\))$/\1/')"
n=$(expr $n + 1)
printf " $url#$n $size $file"
done
if [ "$n" = 0 ]; then
file="$(btshowmetainfo "$tmp" | egrep "^file name.*: " | sed "s/^file name.*: //")"
size="$(btshowmetainfo "$tmp" | egrep "^file size.*: " | sed "s/^file size.*: \([0-9]*\).*/\1/")"
printf " $url $size $file"
fi
printf "\n"
IFS="$oldIFS"
fi
rm -f "$tmp"
;;
TRANSFER)
op="$2"
key="$3"
shift 3
file="$@"
case "$op" in
STORE)
echo TRANSFER-FAILURE STORE "$key" "upload not supported"
;;
RETRIEVE)
urltmp=$(mktemp)
geturls "$key" "$urltmp"
url="$(head "$urltmp")" || true
rm -f "$urltmp"
if [ -z "$url" ]; then
echo TRANSFER-FAILURE RETRIEVE "$key" "no known torrent urls for this key"
else
tmp=$(mktemp)
if ! runcmd curl --proto -all,http,https -o "$tmp" "$url"; then
echo TRANSFER-FAILURE RETRIEVE "$key" "failed downloading torrent file from $url"
else
filenum="$(echo "$url" | sed 's/(.*#\(\d*\)/\1/')"
if downloadtorrent "$tmp" "$filenum" "$file"; then
echo TRANSFER-SUCCESS RETRIEVE "$key"
else
echo TRANSFER-FAILURE RETRIEVE "$key" "failed to download torrent contents from $url"
fi
fi
rm -f "$tmp"
fi
;;
esac
;;
CHECKPRESENT)
key="$2"
# Let's just assume that torrents are never present
# for simplicity.
echo CHECKPRESENT-UNKNOWN "$key" "cannot reliably check torrent status"
;;
REMOVE)
key="$2"
# Remove all torrent urls for the key.
tmp=$(mktemp)
geturls "$key" "$tmp"
for url in $(cat "$tmp"); do
echo SETURLMISSING "$key" "$url"
done
rm -f "$tmp"
echo REMOVE-SUCCESS "$key"
;;
*)
echo UNSUPPORTED-REQUEST
;;
esac
done