📹 Saving social media videos to your phone
By goz
I happen to have a problem with scrolling through a lot of social media. In my travels, I’ll come across a lot of videos that I’d like to share or save. The problem with sharing the videos is that sometimes the site doesn’t make it very pleasant for a person without an account to view the video. Facebook, I’m looking at you. Another issue is that the video may get pulled for various reasons. Since I’m a big geek always looking for a way to overengineer a solution, I wrote a couple of scripts to make it easy to download videos from social media sites so I can share them.
Prerequisites
If you’d like to try this yourself, here are the tools you’ll need:
- An Unix based host that you can access publicly on the internet. I use a virtual private server, but you can use the free tier from Amazon or any VPS provider.
- yt-dlp: This software is the magic that allows everything to happen. It’s a fork of
youtube-dl
and is a command line app that downloads videos from various services. - Telegram: Telegram is my instant messaging client of choice, and I use
telegram-send
to send me the movie file once it’s downloaded - sqlite3: I could probably queue up videos with a plain text file, but why do that when I can use a full blown database. Another bonus is that I’ve wanted a reason to use sqlite3 in a project.
- tmux:
tmux
is a terminal multiplexer which allows you to have multiple windows open over a command line interface. You can also detach from the session and it stays running.
Installing the software
On your VPS you’ll need to install the software needed. You can follow the directions at each site for installing and configuring the software.
- yt-dlp
- telegram-send
- sqlite3
- tmux (or your favorite terminal multiplexer)
You don’t have to use Telegram, you can use something else, but I would recommend at least checking it out. It gives you unlimited storage with file sizes up to 2GB (however, telegram-send
can only send videos up to 50MB, which isn’t a problem with short social media videos).
getvideo.sh
First up is a script to download the video.
Yes, we could call yt-dlp
directly, but I want control over where the videos are saved along with how they are named.
#!/bin/bash
cd ${HOME}/Movies
filename=$(/usr/local/bin/yt-dlp --windows-filenames --get-filename "${1}")
file="${filename:0:33}-${filename: -24}"
/usr/local/bin/yt-dlp --windows-filenames -o "${file}" "${1}"
telegram-send --video "${file}" --caption "${filename}"
You will have to adjust the paths to fit your situation.
I store the downloaded videos in ~/Movies
.
Let me know how to improve the naming of the file.
This way works well enough for me with Tik Tok videos, but I’m always ready for improvement.
Once we chmod +x getvideo.sh
we can call the script with one parameter, the url for the video.
This could be the link to the tweet with the video, or the link from Tik Tok.
The video will be downloaded and then sent to me in Telegram.
This is what my first iteration of downloading videos looked like.
On my iPhone I made a Shortcut that would call the getvideo.sh
script over ssh.
The problem was that once I initiated the Shortcut, I was stuck waiting for it to finish.
Since that could take a minute or so, I needed to come up with a better solution.
Enter a download queue in an sqlite3 database.
The database
The database will store a command and a variable to the command. I’m wanting to extend the queuing system into other areas, like scheduling tweets or Mastodon toots.
I use ~/Development
as a place to store all of my scripts, so inside that folder I created a folder called gozcron.
That will be where I save the database and any other scripts I may need.
sqlite3
is pretty cool, creating the database is as simple as sqlite3 gozcron.db
. You will enter the sqlite3
prompt.
Here is the create statement I used to create a table in the database called actions
:
CREATE TABLE actions (id INTEGER PRIMARY KEY,action varchar(30),variable varchar(1024),due varchar(32),completed BYTE DEFAULT '0');
There are 5 columns:
- id: An autoincrementing field to use for delete or update actions
- action: Denotes the steps to take with this record. Currently only supports
getvideo
but will support other tasks in the future. - variable: Stores the arguments for the action. For
getvideo
that would be the URL of the video. - due: In the future I want to be able to schedule when tasks happen. The date/time of the scheduled action will be stored in this field but for
getvideo
it is ignored. - completed: Once the task is done, this field will be set to 1.
After the create statement is ran, you can exit sqlite3 with .quit
.
addtask.sh
Now that we have a database, let’s add tasks to it.
The addtask.sh
script will take 3 arguments and add them as a record to the actions
table.
#!/bin/bash
cd ${HOME}/Development/gozcron
action="${1}"
var="${2}"
due="${3}"
echo "${action}-${var}-${due}" >> addtask.log
sqlite3 gozcron.db <<EOF
insert into actions (action,variable,due)
.
.
VALUES
("${action}","${var}","${3}");
EOF>>
The echo
line is used for debugging, I don’t know if I’ll leave it in the script, but it’s there for now.
For downloading videos I run the script with only two arguments, addtask.sh getvideo https://MEDIAURL
.
This runs from an SSH step in an iOS Shortcut.
Since it only adds one record to a database, it finishes a lot quicker on the phone.
gozcrond.sh
We’ve got a script that will download the video, and a script to store which videos to download, now we need a script that will go through our queue in the database and download the videos. That’s where gozcrond.sh comes in.
#!/bin/bash
getvideo="${HOME}/Development/gozcron/getvideo.sh"
now=$(date +"%Y-%m-%d %H:%M")
list=$(sqlite3 -batch -separator ',' gozcron.db "select id,action,variable from actions where completed=\"0\" AND (due < \"${now}\" OR due IS NULL)";)
while read -r line; do
id=$(echo "${line}" | cut -d "," -f 1)
action=$(echo "${line}" | cut -d "," -f 2)
var=$(echo "${line}" | cut -d "," -f 3)
#echo "id: ${id}, Action: ${action}, Variable: ${var}"
case "${action}" in
getvideo)
echo "Downloading movie ${var}"
${getvideo} ${var}
sqlite3 -batch gozcron.db "update actions set completed=\"1\" where id=\"${id}\";"
;;
esac
done <<<"${list}"
gozcrond.sh
selects all of the tasks that aren’t completed and that are either past their due date or do not have a due date set.
It then iterates through the list, and runs getvideo.sh
on any row that has getvideo
as the action.
If I run the script, it checks once and then exits.
We don’t want to do that, so I run it with while TRUE; do ./gozcrond.sh; sleep 60; done
.
I probably should just incorporate the loop in the script, and maybe I will.
Running all the time
Since I get tired of looking up how to create services, I run the script in a window under tmux
. I can then detach from my session and my script stays running.
tmux
is a terminal multiplexer, allowing you to run multiple windows or screens from one connection. It also lets you disconnect and leave your stuff running.
for when you reconnect.