18d326cb6f
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
205 lines
5.2 KiB
Bash
Executable file
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
|