

Thanks. Just a small correction. The API isn’t really REST, it’s REST-ish but probably closer to JSON-RPC.


Thanks. Just a small correction. The API isn’t really REST, it’s REST-ish but probably closer to JSON-RPC.


That could be, but I don’t think that it should be relied upon. The shortener itself can execute malicious code, so that kind of security is, in my opinion, essentially theatre. I’d just say that don’t click on links that you don’t trust.
This project is for own use/use with friends/family/internally in an org etc., where trust isn’t an issue. Of course, I cannot stop anyone from using it in any other way that they see fit. It can help shorten annoying long links for ease of sharing, but that’s it.


Here’s a pretty small project that’s still practically useful, at least to me.


Thanks for your feedback.
You just need an Immich API key, and run it from any machine from where you can reach your Immich instance. It does everything using the Immich API, so only a key with the proper permissions is needed. (I’ll add what the minimum required permissions are in the README.)
Also, if you want to do it for many users, you don’t need them to run it on individual machines/accounts. You can create multiple config files, each with that user’s key, and pass it to the script via the --config flag.
If running it for multiple users is a thing that people are interested in, I can add a way to supply an array of options in the config file, each belonging to one user.


Not sure about LaTeX, but TeX is widely considered to be almost “perfect” code.


Good work, but this can be done in a more efficient way by utilizing the qBittorrent API in more places. Also, you may wanna utilize gluetun’s VPN_PORT_FORWARDING_UP_COMMAND for calling the script.
Here’s my script. I used bash since the gluetun container doesn’t have Python in it.
#!/bin/sh
# Adapted from https://github.com/claabs/qbittorrent-port-forward-file/blob/master/main.sh
# set -e
qbt_username="${QBT_USERNAME}"
qbt_password="${QBT_PASSWORD}"
qbt_addr="${QBT_ADDR:-http://localhost:8085/}"
if [ -z ${qbt_username} ]; then
echo "You need to provide a username by the QBT_USERNAME env variable"
exit 1
fi
if [ -z ${qbt_password} ]; then
echo "You need to provide a password by the QBT_PASSWORD env variable"
exit 1
fi
port_number="$1"
if [ -z "$port_number" ]; then
port_number=$(cat /tmp/gluetun/forwarded_port)
fi
if [ -z "$port_number" ]; then
echo "Could not figure out which port to set."
exit 1
fi
wait_time=1
tries=0
while [ $tries -lt 10 ]; do
wget --save-cookies=/tmp/cookies.txt --keep-session-cookies --header="Referer: $qbt_addr" --header="Content-Type: application/x-www-form-urlencoded" \
--post-data="username=$qbt_username&password=$qbt_password" --output-document /dev/null --quiet "$qbt_addr/api/v2/auth/login"
listen_port=$(wget --load-cookies=/tmp/cookies.txt --output-document - --quiet "$qbt_addr/api/v2/app/preferences" | grep -Eo '"listen_port":[0-9]+' | awk -F: '{print $2}')
if [ ! "$listen_port" ]; then
[ $wait_time -eq 1 ] && second_word="second" || second_word="seconds"
echo "Could not get current listen port, trying again after $wait_time $second_word..."
sleep $wait_time
[ $wait_time -lt 32 ] && wait_time=$(( wait_time*2 )) # Set a max wait time of 32 secs
tries=$(( tries+1 ))
continue
fi
if [ "$port_number" = "$listen_port" ]; then
echo "Port already set to $port_number, exiting..."
exit 0
fi
echo "Updating port to $port_number"
wget --load-cookies=/tmp/cookies.txt --header="Content-Type: application/x-www-form-urlencoded" --post-data='json={"listen_port": "'$port_number'"}' \
--output-document /dev/null --quiet "$qbt_addr/api/v2/app/setPreferences"
echo "Successfully updated port"
exit 0
done
echo "Failed after 10 attempts!"
exit 2
For the auto-exit stuff, you may wanna check out docker’s healthcheck functionality.
Not trying to put you down or anything here, it’s great to learn to do things by yourself. Just giving you some pointers.


Chhoto URL - It’s a simple URL shortener written in Rust.
I’ve written more programs, some of which are more useful in my daily life than this (e.g. movie-rename) but this is one that many seem to find interesting, and that’s kinda cool I guess. Also, I’m proud of some of my Lean code, but that stuff’s not published.


Ah that makes sense. To be fair tho, there’s a lot of unwarranted hate towards Rust so it can be hard to tell.


I hope you’re joking. If anything, Rust makes error handling easier by returning them as values using the Result monad. As someone else pointed out, they literally used unwrap in their code, which basically means “panic if this ever returns error”. You don’t do this unless it’s impossible to handle the error inside the program, or if panicking is the behavior you want due to e.g. security reasons.
Even as an absolute amateur, whenever I post any Rust to the public, the first thing I do is get rid of unwrap as much as possible, unless I intentionally want the application to crash. Even then, I use expect instead of unwrap to have some logging. This is definitely the work of some underpaid intern.
Also, Python is sloooowwww.
Welcome to the club. Don’t worry too much about setting it up perfectly in your first attempt. You’re gonna rewrite your whole config every year-ish anyway. (Or is that just me? 😥) Also, try Neovim. It’ll be a drop-in replacement for your current config. But Lua is just a superior language compared to Vimscript, so you’ll have a much better performance in the future. You also get all the sweet LSP and treesitter features.


Yeah, same. I like to code in Neovim, and OOP just doesn’t make any sense in there. Fortunately, I don’t have to code in Java often. I had to install Android Studio just because I needed to make a small bugfix in an app, it was so annoying. The fix itself was easy, but I had to spend around an hour trying to figure out where the relevant code exactly is.


As an amateur with some experience in the functional style of programming, anything that does SOLID seems so unreadable to me. Everything is scattered, and it just doesn’t feel natural. I feel like you need to know how things are named, and what the whole thing looks like before anything makes any sense. I thought SOLID is supposed to make code more local. But at least to my eyes, it makes everything a tangled mess.


I’m confused. What do you even mean?


It seems that I’d still need to modify net.ipv4.ip_unprivileged_port_start=80 in sysctl, which I don’t want to do. If I do it, the socket isn’t even strictly necessary.


Just a couple of friends use it. But I’d like to use this as a learning opportunity and do it the proper way. It seems that if I turn of masquerade in general, and use firewalld fine-grained rules to enable it when I actually need it, I might be able to achieve what I want. I’ll post an update to the original post if I can get it to work.


This is interesting. I need to figure out how it works for podman and it’ll be the perfect setup.


I think it’s the masquerade that’s causing problems for me. I have to keep it enabled since I’m running a tailscale exit node. But maybe I can selectively disable it here.
Thanks. Hope you like it.