aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Segundo2023-06-10 00:11:39 +0200
committerChristian Segundo2023-06-10 00:11:39 +0200
commitbe52bd17ea01d8c302e39ba444194de282dc4728 (patch)
treeb1d69842363bce20f3b3141b529b52877dfdbbd7
downloadtransmission-hacks-be52bd17ea01d8c302e39ba444194de282dc4728.tar.gz
First commit
-rw-r--r--.buildkite/pipeline.yml7
-rw-r--r--Dockerfile12
-rw-r--r--Makefile47
-rwxr-xr-xscripts/ask-tracker-for-more-peers.py21
-rw-r--r--scripts/cron.sh28
-rwxr-xr-xscripts/delete-based-on-tag.py40
-rw-r--r--scripts/entrypoint.sh28
-rw-r--r--scripts/requirements.txt8
-rwxr-xr-xscripts/tag-based-on-dir.py30
9 files changed, 221 insertions, 0 deletions
diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml
new file mode 100644
index 0000000..313fd75
--- /dev/null
+++ b/.buildkite/pipeline.yml
@@ -0,0 +1,7 @@
+---
+steps:
+ - name: push
+ branches: [master]
+ command: |
+ make docker-push \
+ DOCKER_EXTRA_ARGS="-v ${HOME}/.docker:/root/.docker"
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..e28354d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM python:slim-bullseye
+COPY scripts /scripts
+RUN \
+ apt-get update && \
+ apt-get install -y --no-install-recommends cron && \
+ apt-get clean && \
+ pip install --no-cache-dir -r /scripts/requirements.txt && \
+ rm -rf /scripts/requirements.txt && \
+ rm -rf /var/lib/apt/lists/* && \
+ touch crontab /etc/cron.d/crontab && \
+ chmod 0644 /etc/cron.d/crontab
+CMD ["bash", "/scripts/entrypoint.sh"]
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..54c1696
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,47 @@
+IMAGENAME := docker.io/chn2guevara/transmission-hacks
+VERSION := 1
+BUILDARG_PLATFORM := --platform linux/amd64,linux/arm64/v8
+DOCKER_EXTRA_ARGS :=
+
+ci-deps:
+ apt-get -qq -y install \
+ binfmt-support \
+ ca-certificates \
+ curl \
+ git \
+ gnupg \
+ lsb-release \
+ qemu-user-static \
+ wget \
+ jq
+
+ci-deps-docker:
+ curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \
+ echo "deb [arch=$(shell dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(shell lsb_release -cs) stable" |\
+ tee /etc/apt/sources.list.d/docker.list > /dev/null && \
+ cat /etc/apt/sources.list.d/docker.list && \
+ apt-get update && \
+ apt-get -qq -y install \
+ docker-ce \
+ docker-ce-cli \
+ containerd.io
+
+ci-setup-buildx:
+ docker run --privileged --rm tonistiigi/binfmt --install all
+ -docker buildx create --name mybuilder
+ docker buildx use mybuilder
+
+ci-prepare: ci-deps ci-deps-docker ci-setup-buildx
+
+push: ci-prepare
+ docker buildx build -t $(IMAGENAME):latest . --push
+ docker buildx build -t $(IMAGENAME):$(VERSION) . --push
+
+docker-%:
+ docker run \
+ --rm \
+ --privileged \
+ -v /var/run/docker.sock:/var/run/docker.sock \
+ -v $(shell pwd):/data \
+ -w /data $(DOCKER_EXTRA_ARGS) \
+ debian:stable sh -c "apt-get update && apt-get install make && make $*"
diff --git a/scripts/ask-tracker-for-more-peers.py b/scripts/ask-tracker-for-more-peers.py
new file mode 100755
index 0000000..8b874f0
--- /dev/null
+++ b/scripts/ask-tracker-for-more-peers.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+import click
+from transmission_rpc import Client
+
+
+@click.command()
+@click.option('--port', default=9091)
+@click.option('--host', default="localhost")
+def main(host, port):
+ """ Reannounces all torrents that have not received any peers. """
+
+ c = Client(host=host, port=port)
+ torrents = c.get_torrents()
+ for torrent in torrents:
+ if torrent.progress == 0 and torrent.downloading:
+ print(f"Reannouncing {torrent.name}")
+ c.reannounce_torrent(torrent.id)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/cron.sh b/scripts/cron.sh
new file mode 100644
index 0000000..3cde994
--- /dev/null
+++ b/scripts/cron.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+set -euo pipefail
+
+declare -A tag_age
+i=0
+while true; do
+ key="DELETE_TAG_${i}"
+ val="DELETE_AGE_${i}"
+ if [ -z "${!key:-}" ]; then
+ break
+ fi
+ tag_age["${!key}"]="${!val}"
+ i=$((i+1))
+done
+
+${PYTHON_PATH} "${SCRIPT_PREFIX}"/tag-based-on-dir.py \
+ --host "${TRANSMISSION_HOST}"
+
+${PYTHON_PATH} "${SCRIPT_PREFIX}"/ask-tracker-for-more-peers.py \
+ --host "${TRANSMISSION_HOST}"
+
+for key in "${!tag_age[@]}"
+do
+ ${PYTHON_PATH} "${SCRIPT_PREFIX}"/delete-based-on-tag.py \
+ --host "${TRANSMISSION_HOST}" \
+ --tag "${key}" \
+ --age "${tag_age[$key]}"
+done
diff --git a/scripts/delete-based-on-tag.py b/scripts/delete-based-on-tag.py
new file mode 100755
index 0000000..a5e2d0b
--- /dev/null
+++ b/scripts/delete-based-on-tag.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+import click
+from transmission_rpc import Client
+from datetime import timedelta, datetime
+
+
+def convert_to_seconds(s):
+ units = {"s": "seconds", "m": "minutes",
+ "h": "hours", "d": "days", "w": "weeks"}
+ count = int(s[:-1])
+ unit = units[s[-1]]
+ td = timedelta(**{unit: count})
+ return td.seconds + 60 * 60 * 24 * td.days
+
+
+@click.command()
+@click.option('--port', default=9091)
+@click.option('--host', default="localhost")
+@click.option('--tag', required=True)
+@click.option('--age', default='1w')
+def main(host, port, tag, age):
+ """ Deletes torrents older than the specified age. """
+
+ c = Client(host=host, port=port)
+ torrents = c.get_torrents()
+ for torrent in torrents:
+ if tag not in torrent.labels:
+ continue
+
+ specified_age = convert_to_seconds(age)
+ age_in_seconds = int((datetime.today().timestamp() -
+ torrent.added_date.timestamp()))
+
+ if age_in_seconds > specified_age:
+ print(f"Deleting {torrent.name}")
+ c.remove_torrent(torrent.id, delete_data=True)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh
new file mode 100644
index 0000000..4d89be5
--- /dev/null
+++ b/scripts/entrypoint.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+set -euo pipefail
+
+cat <<EOF >> /etc/cron.d/crontab
+PYTHON_PATH=/usr/local/bin/python3
+SCRIPT_PREFIX=/scripts
+TRANSMISSION_HOST=${TRANSMISSION_HOST:-localhost}
+TRANSMISSION_PORT=${TRANSMISSION_PORT:-9091}
+EOF
+
+i=0
+while true; do
+ key="DELETE_TAG_${i}_NAME"
+ val="DELETE_TAG_${i}_AGE"
+ if [ -z "${!key:-}" ]; then
+ break
+ fi
+ cat <<EOF >> /etc/cron.d/crontab
+DELETE_TAG_${i}=${!key}
+DELETE_AGE_${i}=${!val}
+EOF
+ i=$((i+1))
+done
+
+echo "${CRON_EXPRESSION:-* * * * *} /bin/bash /scripts/cron.sh >/proc/1/fd/1 2>/proc/1/fd/2" >> /etc/cron.d/crontab
+cat /etc/cron.d/crontab
+crontab /etc/cron.d/crontab
+cron -f -l "${CRON_LOG_LEVEL:-0}"
diff --git a/scripts/requirements.txt b/scripts/requirements.txt
new file mode 100644
index 0000000..66904d3
--- /dev/null
+++ b/scripts/requirements.txt
@@ -0,0 +1,8 @@
+certifi==2023.5.7
+charset-normalizer==3.1.0
+click==8.1.3
+idna==3.4
+requests==2.31.0
+transmission-rpc==4.3.0
+typing_extensions==4.6.3
+urllib3==2.0.3
diff --git a/scripts/tag-based-on-dir.py b/scripts/tag-based-on-dir.py
new file mode 100755
index 0000000..8cec41b
--- /dev/null
+++ b/scripts/tag-based-on-dir.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+import click
+from transmission_rpc import Client
+
+
+@click.command()
+@click.option('--port', default=9091)
+@click.option('--root-dir', default="/downloads/complete")
+@click.option('--host', default="localhost")
+def main(host, port, root_dir):
+ """Adds tags to torrents based on their download directory."""
+
+ c = Client(host=host, port=port)
+ torrents = c.get_torrents()
+ for torrent in torrents:
+ dir = torrent.download_dir.replace(root_dir, '')
+ label = 'none'
+ if dir != '':
+ label = dir.split('/')[1]
+
+ labels = list([label])
+ labels.extend(x for x in torrent.labels if x not in labels)
+
+ if set(labels) != set(torrent.labels):
+ print(f"Tagging {torrent.name}")
+ c.change_torrent(torrent.id, labels=labels)
+
+
+if __name__ == '__main__':
+ main()