Żegnaj Youtubie!

Posted on Sun 16 May 2021 in Hacks (wannabe), Shitz • 3 min read

Zrobiłem skrypty w Bashu do pobierania świeżych filmów z konkretnego kanału. Prymityw potrzebny przy budowie Platformy ale może być zalążkiem własnego systemu subksrypcji, który pozwoli na skasowanie konta na YT.

Zadanie okazało bardziej skomplikowane niż początkowo myślałem. Na podstawie wcześniejszych doświaczeń w pobiernaiu pojedynczych filmów, uważałem że pobranie większej ilości i generowanie JSON-a załatwię szybkim, prostym skryptem.

To (błędne) założenie spowodowało, że zabrnąłem już na tyle głęboko, że w pewnym momencie siła bezwładności nie pozawalała mi porzucić Basha i pisać w Pythonie. Utknąłem jakby w kałuży smoły.

Co chwila pojawiały się kolejne kłopoty: a to Youtube używał paskudnych sekwencji w nazwach filmów, a to programy nie potrafiły tych sekwencji eskejpować, a to główny skrypt nie zapisywał częściowo wykonanej pracy do pliku, itd.

Najciekawszym odkryciem dla mnie był fakt, że grep nie potrafi czytać ze standard error i musiałem użyć named pipe.

Skrypty bazują na napisanym w Pythonie youtube-dl, więc właściwie mogłyby i powinny być przepisane właśnie w Pythonie, co być może nastapi.

Skrypty są dwa, główny: yt2.sh oraz yt-convert2.sh.

Pierwszy odpala youtube-dl i pobiera najnowsze filmy, zadaniem drugiego jest konwersja do niższej rozdzielczości i birate, ewentualna konwersja miniatur z JPEG do formatu WEBP, a także wypluwanie opisu filmu w formacie JSON.

Główny skrypt wygląda tak i jak widać uruchamia yt-convert2.sh dla każdego pobieranego filmu:

#!/bin/bash
# skrypt pobiera najnowsze filmy

POBRANE="./pobrane2"                    # plik zapisem pobranych filmów
JSONFILE="./videos.json"                # plik z opisami w JSONie
JSONOUT="./v.json"              # plik z opisami w JSONie
YT=`which youtube-dl`
MAXVIDS=$2                              # ile najnowszych z danego odpalenia
DEFAULT_MAXVIDS=4                       # domyślna ilość filmów
MAXOLD=1day                             # ile dni wstecz
VIDEO=$1                                # url do kanału
VOD="."                                 # folder dla gotowych plików
MAXDURATION=4800                        # maksymalna długość filmu 1,5h
DEFAULT_VIDEO="https://www.youtube.com/channel/UCiwsDgj8mJnsGOr6oN-2OVQ/videos"         # url do kanału wRealu24

# upgrade
$YT -U
#youtube-dl --rm-cache-dir

# usuwam górne i dolne nawiasy
sed -i '1 s/\[//' $JSONFILE
sed -i '$ s/\]//' $JSONFILE

#       --format "worst" \

$YT \
        --quiet \
        --match-filter "duration < $MAXDURATION & !is_live" \
        --ignore-errors \
        --add-metadata \
        --format "best[height=480]/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" \
        --download-archive $POBRANE \
        --playlist-end $DEFAULT_MAXVIDS \
        --write-thumbnail \
        --no-progress \
        --merge-output-format mp4 \
        --restrict-filenames \
        --exec './yt-convert2.sh {}' \
        -o "%(id)s.%(ext)s" \
        $DEFAULT_VIDEO

# kopiowanie miniaturek
echo -n -e "\e[31mPrzenoszę miniaturki do $VOD... \033[0m       "
mv -f *.webp $VOD 2> /dev/null
echo "gotowe."

# dopisuję co trzeba do $JSONFILE
echo -n -e "\e[31mTworzę plik: $JSONOUT... \033[0m      "
sed '$ s/.$//' $JSONFILE > $JSONOUT
sed -i '1i \[' $JSONOUT
echo "]" >> $JSONOUT
echo -e "gotowe."


# czyszczenie
echo -n -e "\e[31mKasuję zbędne pliki... \033[0m        "
cd /home/paczor
rm -f *.webp
rm -f *.jpg
rm -f *.jpeg
rm -f *.mp4
echo "gotowe."

Konwersja filmów, miniaturek i wypluwanie JSON-a wygląda zaś tak:

#!/bin/bash
# skrypt konwertuje video i generuje miniatury

JSONFILE="./videos.json"        # plik z opisami w JSONie
VOD="."                 # folder dla gotowych plików
FF=`which ffmpeg`
MINFO=`which mediainfo`                                 # mediainfo wyciąga datę utworzenia filmu
YT=`which youtube-dl`
PREFIX="https://youtu.be"                               # YT prefix
VODURL="https://vod.fubar.pl"                      # url gdzie leżą pliki video

mkfifo mypipe

# parsowanie JSONa
function parse {
         jq \
        --arg status public \
        --arg videoUrl "$VODURL/$1.mp4" \
        --arg thumbnail "$VODURL/$1.webp" \
        --arg seed "true" \
        --arg upload_date "$VIDEODATE" \
        '{id: .id,title: .title,uploadedAt: $upload_date,publishedAt: $upload_date,status: $status,videoUrl: $videoUrl,desc: .description,views: .view_count,likes: .like_count,tags: .tags[:3],thumbnailUrl: $thumbnail,duration: .duration,seed: $seed}'
}

VIDEODATE=$(exiftool  -t -time:FileModifyDate -- "$1" | cut -d" " -f2 | sed 's/+.*$//')
echo -e "\033[31mPrzetwarzam video: $PREFIX/$1\033[0m"
echo -e "\e[92m data emisji:\033[0m     $VIDEODATE"

echo -n -e "\e[92m      zapisuję metadata do: $JSONFILE ...\033[0m      "

$YT --skip-download -j "$PREFIX/$1" 2> mypipe | grep "ERR" mypipe
if [ $? -eq 1 ]; then
        $YT --skip-download -j "$PREFIX/$1" | parse "$1" >> $JSONFILE
        echo -n "," >> $JSONFILE
        echo "bez błędu"
else
        echo "błąd!"
fi

echo -n -e "\e[92m      skaluję do:     $VOD/$1 ... \033[0m     "
#$FF -i $1 -filter:v scale=-2:480 -threads 16 -hide_banner -loglevel error -y $VOD/$1
echo " gotowe."

echo -n -e "\e[92m      konwertuję miniaturkę... \033[0m"
if [[ -e `basename $1 .mp4`.jpg ]]; then
        THUMB=`basename $1 .mp4`.jpg
        convert $THUMB `basename $THUMB .jpg`.webp
        rm -f $THUMB
        echo "zmieniłem JPEG na WEBP."
else
        echo "WEBP już istnieje."
fi

rm mypipe

Miałem z tym sporo grzebania, zgłosiłem dwa błędy które wykryłem w youtube-dl oraz w programie Mediainfo. Pisanie skryptów w Bashu coraz mniej mnie cieszy, nie lubię jego składni, podstawień, itp.