#!/bin/bash
# Това го знаеш покрай Смоко. Казва се на обвивката къде да търси
# интерпретатора. Без него ще се пробва със /bin/sh, но само ако има
# chmod +x. Другия вариант е да го подадеш директно на интерпретатора
# с например sh script
# Нещо, което винаги съм ненавиждал в обяснение на нов език. Никой,
# никога не казва, как се отбелязват коментари. За тях може да е
# подразбиращо, но за новак това е безценна информация, която не се
# споделя. Bash има само едноредови коментари и всички те започват с
# диез #. Ако ще пишеш коментари на няколко реда, всеки трябва да
# започва с диез. Когато се пише нещо за конзола или обикновен текст,
# конвенцията е да е ограничено до 70 знака на ред. Както виждаш, не
# го спазвам и в реална ситуация, биха ми дърпали ушите.
unset y f l d ft
declare -a yar
declare -a far
# Първия ред премахва стойностите на променливите, които се
# ползват. Това е със санитарна цел. Може да прескочи нещо от
# средата. С другите две изрично декларирам, че ще ползвам масиви. Не
# е задължително, може да стане в движение създаването им, но кой
# знае защо се води добра практика във всички езици през всички
# времена. Е то bash не е език, ама все пак. Най-вероятно спестява
# работа на професора.
echo "Loading..."
# Почвам да давам съобщения в конзола, какво се случва, защото следват
# бавни операции. Има по изключение само едно съобщение, което не е с
# цел да ми казва, че следва нещо бавно. Ще стигнем и до него.
if [ oti -nt oti2 ]; then
# Навлизане в първото условие. Тук питам дали файл oti2
# съществува и дали той е със същата дата на промяна, каквато има
# oti. Вече обясних, че всички обръщения към y-dl са бавни
# операции траещи по много минути (поне пет), затова гледам да ги
# избягвам.
# Oti e моя списък за теглене. Той може да съдържа празни редове
# за разграничение и коментари. Oti2 съдържа само и единствено
# индефикатори. Oti2 ще се промени, когато се промени и oti.
# Това условие Ако е първата причина този код да не може да се
# изпълни от обикновен sh. Също е демонстрация, колко много е
# мислено по Bash през годините. Специално тази магия е само една
# от многото:
# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html. След
# малко ще ползваме и масиви, също нещо непосилно за sh.
y=$(youtube-dl --get-id -a oti)
echo "$y" > "oti2"
# Това е бавната операция по вземане на индефикаторите. Те се
# записват в променлива на средата, която веднага в следващия ред
# наливам във временния документ oti2. Налага ми се да записвам
# променливите във временни документи, защото непрекъснато скачам
# от едно пакетно изпълнение към друго. Това ще го разясня към
# края, защо е така, но съм го писал и в темата.
#https://askubuntu.com/questions/62492/how-can-i-change-the-date-modified-created-of-a-file
touch -d "$(date -R -r oti)" oti2
# Touch е команда, която служи за промяна времената на
# документите. Тук я ползвам в оригиналното ѝ значение като казвам
# oti2 да придобие времето за промяна на oti. По този начин в
# условието на 17-и ред [ oti -nt oti2 ] разбирам дали oti е бил
# променен и се налага oti2 отново да бъде налят. Страничния ефект
# на touch е, че създава нови документи. Парадоксално, това е
# много по-използваната му функция.
#Тук също ще създаде oti2, ако не съществува. Но би трябвало
#предния ред да го е създал. Ако нещо между тези две инструкции
#изтрие oti2, тук ще получим първата си катастрофа. Ето, какво
#значи да плаваш в непознати земи. В програмирането винаги е така.
fi
# Край на условието [ oti -nt oti2 ].
# Ако условието не е изпълнено, то ще мина през това за под секунда.
echo "Check..."
# Следва нова бавна операция и ново побутване на потребителя, стига да наблюдава случващото се.
while read -r l;
do
# Тази магия значи, че ще се чете обикновен текст от документ и
# всеки ред ще бъде присвояван на променливата $l (от line или на
# български − ред). Както си забелязал, във философията на Юникс
# е да се пести. Всичко което не може да се сведе до една буква,
# се свежда до две, а ако не може до три. Трябва да сме ефективни
# и да мислим за ресурсите. При бързи професори и изобилие от
# памет лесно се забравя, но все пак трябва да се стараем.
# До края на цикъла Докато ще се извършват действия върху тази
# променлива. Всяка врътка ще е върху следващия ред.
echo "$l e super";
# Ето тук открих, че Докато между врътките ми яде буква. Слагането
# на съобщения в кода е лесният и удобен начин да се прави това,
# което споменатия от Спец. strace прави за транслиран код. Когато
# имаме интерактивно изпълнение сме благословени. Същото би
# трябвало да е и за Смоку. Макар там във версия три да влязоха
# едни езикови конструкции, които са много тежки. Идеята за
# структури от данни като масиви и вектори е безкрайно по-проста
# от структури като класове и обекти. Мен те ме отказаха от
# програмирането. Разбира се, ако си като да речем Ремо и пишеш
# някакви чудовищни системи като примерно Sales Forge, то без тях
# не може. Но, ако нещото е само няколко хиляди реда, то такива
# чудовищни структури от данни само пречат и внасят неразбиране,
# какво точно се прави. Затова за Смоку от версия три може и да не
# е вече вярно. Там съобщенията биха били безсмислени, ако се
# ползват обекти. Затова са измислени едни системи за изпитване,
# като са разделени на класове. Например най-популярните класове
# са точкови и непрекъснати.
if [ -f *"$l.mkv" ]; then
# Тук проверявам дали съществува обикновен документ наречен
# *име.mkv. Звездичката и въпросителния знак (както със
# сигурност знаеш) са специален клас знаци наречени
# заместващи. Ydl именува изтеглените клипове с име, което
# съдържа заглавието на клипа, както е в тубата, дефис,
# идентификатора му и разширение. Това не може да се предугади
# с --get-name, защото Ydl на свой ред сменя част от знаците в
# заглавието, които смята за специални. Такъв например е
# наклонена черта (/). Схемите за именуване на Ydl подлежат на
# промяна, но подразбиращите се, ми харесват. Например, ако
# теглиш всички налични формати, Ydl е достатъчно умен, за да
# сложи в заглавията им и тази информация. Изобщо Ydl е един
# много мощен и добре измислен инструмент от вида „швейцарско
# ножче“.
echo "Matroska: OK"
# Това е единственото безсмислено съобщение. Сложено е, защото
# Bash не позволява да не се прави нищо при изпълнено условие
# Ако. А нямаме обръщане на логиката (!), когато проверяваме
# за наличие на документ. Логиката, която се развива надолу е
# следната: първо се тегли звука, ако той е изтеглен, се тегли
# картинката, ако са изтеглени и двете, тогава с ffmpg се
# съединяват. Ако вече имаме направен mkv, то работата е
# свършена и трябва да минем на следващия ред от oti2.
else
if [ -f *"$l.mp4" ]; then
# Тук навлизаме в условието, ако не е свършена работата на
# обратно. Проверяваме дали картинката е потеглена. Винаги
# тегля mp4, за да не стават обърквания. И без това винаги
# е по-малък от webm при еднакви показатели. Тук системата
# за именуване на Ydl би била полезна, но все пак така се
# усложняват излишно нещата. Трябва човек да се придържа
# към по-простите решения, доколкото е възможно.
far[0]="*$l.mp4"
# Тук се започва наливането на масива (този е едномерен,
# неименован пак за удобство). При изтеглен mp4, значи е
# време за съединяване. Първия елемент е името на
# въпросния mp4.
if [ -f *"$l.m4a" ]; then
far[5]="m4a"
# Както казах звука може да е webm или m4a, затова
# проверяваме, кое от двете се е свалило. За нас Ydl е
# външен инструмент и няма друг начин да научим, какво
# е изтеглил. Присвояваме на шести елемент това
# име. Защо точно шести, след малко.
else
far[5]="webm"
fi
echo "Convert..."
# След като вече имаме имената на двата медийни документа,
# вече може да пристъпим към съединяването им. Тази задача
# за ffmpeg е бърза на професори от последните двадесет
# години. Но все пак е нашата крайна цел и е добре да я
# отбележим. Затова, въпреки, че задачата е бърза, а и
# ffmpeg e приказлив като жена, ние даваме съобщение за
# предстоящото ѝ изпълнение.
far[1]=$(echo ${far[0]})
far[4]=${far[1]:0:-3}
# Първия ред е поради особености на Bash. Не можем да
# използваме директно първия елемент, защото Bash няма да
# ни преобразува звездичката. Има второ важно познание за
# Bash, освен, че не е език за програмиране. А второто
# произлиза от първото. То е, че не трябва да мислим Bash
# за по-умен, от колкото е. Той е мъж, а не жена. Не може
# да прави по две неща едновременно. Трябва да му се
# подават ясни инструкции, еднозначни и по най-много едно
# нещо наведнъж. В това си качество прилича на Асемблер.
# Втория ред ни казва да направим подниз от низ. Странното
# заклинание значи, че махаме последните три букви
# (mp4). Тях ще сменим с нашите разширения. Тук става ясно
# защо този елемент е пети, а преди малко използвахме
# шести. Запазваме втори, трети и четвърти за истинските
# имена. Втория вече го съставихме на предния ред. Остават
# трети и четвърти. Вадене на подниз с този синтаксис в
# по-стари версии на Bash не беше възможно и тогава
# трябваше да ползваме малко математика. Да изчисляваме
# дължината на името и т.н.
far[2]=${far[4]}${far[5]}
far[6]="mkv"
far[3]=${far[4]}${far[6]}
ffmpeg -i "${far[1]}" -i "${far[2]}" -c:v copy -c:a copy -strict experimental "${far[3]}"
# НАЙ-накрая се стига и до самото съединяване, след като
# сме си сглобили имената. Новото тук на по-горните
# редове отнасящи се към трети и четвърти елемент. Тези
# заклинания са просто сливане на низове.
d=$(date -R -r "${far[1]}")
touch -d "$d" "${far[3]}"
# Тук правим нещо, което вече сме правили. Сменяме датата
# на промяна на туко що произведения контейнер Матрьошка с
# датата на изтеглените потоци. Това е нещо, което и самия
# Ydl също прави. Слага датата на качване в тубата. Също е
# нещо, което през годините съществуване на Ydl много ме е
# дразнило, защото прави ls -rt безполезен. Но пък те
# знаят, какво правят. Така се запазва поредност. Затова
# го правя и аз. Ydl няма как да му се препише вина, ако
# клиповете не са качвани в поредност, а разбъркано според
# странични причини.
# Тук по-горния ред е отново, защото Bash е мъж. Не може
# да прави две неща едновременно, затова ни се налага да
# ползваме променлива. Излишно е да казвам, че d е
# съкращение от „дата“(date).
# Забравих да го спомена по-нагоре, но синтаксиса $( ) е
# нова форма на все още поддържания и по-кратък, но не
# толкова отличителен ``. Това значи, че създаваме нова
# обвивка, в която изпълняваме нещо и го връщаме на
# майчиния процес. Ако забелязваш управлението на масиви
# се извършва по същата технология, затова Bash се
# обърква. Стават детски процес на детски процес. Той не
# може да работи толкова надълбоко.
rm "${far[1]}" "${far[2]}"
# Изтриваме вече ненужните звук и видео. Добрия погромист
# кърти, чисти и извозва боклука след себе си. Не остава
# отпадъци след като е свършил. Питай ме защо не ползвам
# Усхадуя! Тук имаме риск, ако не са се събрали добре
# документите по някаква причина, то да повторите бавния
# процес по тегленето. Но това е по-малко вероятно и
# по-добре да сте си поринали. Ако е писано пак да чакате
# с часове, ще чакате, няма де да ходите. Бърза е само
# работата за другите, които желаят резултатите. За този,
# който я прави е толкова, колкото е.
else
# Тук сме научили, че все още нямаме mp4, следователно
# трябва да си го потеглим. По принцип променливата за тип
# на документа за теглене ($ft) е ненужна, но ще я
# използваме пак, когато искаме да разберем, какво ще
# теглим. Тук може да сложим и друга променлива и след
# това вместо два пъти (или три според случая) да извикаме
# само веднъж --get-filename, но както коментирах, това би
# направило кода много сложен и още по-чуплив от сега.
ft="597/160/133"
if [ -f *"$l.m4a" ]; then
# Проверяваме имаме ли *идентификатор.m4a. Ако вече е
# наличен, ще теглим mp4. Тук особеното е, че Ydl ни
# позволява да създадем текстова база от данни с вече
# изтеглени неща, да не се теглят отново. Използваме
# тази чудесна възможност, едва след като ще теглим
# картинката, защото тази база е проста и не ни дава
# възможност да теглим нещо в различни формати. Което
# може да се счита за нейно предимство. Това е
# единственото в този код, което нямаше да ни достави
# -f 17/36, докато съществуваше. Забравих да кажа, че
# разделителя наклонена черта означава избор, като се
# тегли първия наличен. Сиреч това е логическо
# Ако. Логическото И е запетайка.
yar[1]="youtube-dl --download-archive ito -f "$ft" $l &"
# Защо отново сглобяваме масиви, ще уточним след малко.
elif [ -f *"$l.webm" ]; then
# Казахме, че звука може да е webm, освен m4a. Ако
# нямаме m4a не е задължително да не сме го изтеглили
# в webm. Webm e с по-голям приоритет във възможните
# формати, а нашата логика тече на обратно. Първо
# проверихме за mkv, после за mp4 и накрая за
# звука. Ако и това не мине, значи нямаме дори звук и
# ще теглим и него.
yar[1]="youtube-dl --download-archive ito -f "$ft" $l &"
# Имаме звук със сигурност, значи е време за
# картинката. Тук имаме „суха“(DRY) ситуация с горната
# проверка, но няма как. Може да се изнесе на
# променлива и да не сме сухи, но усложняваме кода и
# предизвикваме съдбата да влезе в ролята на
# разгорещен негър. По-добре да сме сухи.
if [ -f *"$l.m4a."* ]; then
rm *"$l.m4a."*
fi
# Докато сме теглили е възможно да сме започнали с m4a, но вече да е наличен webm.
#Това е начина на работа на тубата. Първо предоставя
#m4a, след време прекодира и във webm, но не се знае,
#кога ще го направи. Твърде често се случва по време
#на теглене. Тубата те надушва, че теглиш m4a и бърза
#да ти даде webm. Ако го правиш достатъчно бавно, то
#той ще те изпревари. А точно това се случва при бавна
#връзка. Ако канала и видеото са непопулярни, то може
#и никога да не се прекодират, но Гългъл надушат ли
#те, веднага се правят на отзивчиви. Тогава имаш едно
#пропиляно време и едни разхвърляни остатъци по диска
#във формата на *.part и *.ytdl, a са възможни, и
#други. Печалбата е, че ако клипа е достатъчно кратък,
#то може да изпревариш тубата и да спестиш време,
#защото m4a е по-малък по размер, ако не е формат
#140. „Риск печели, риск губи“ с Кръстьо Вапцаров.
else
# Тук стигаме, ако се окаже, че нямаме никой от
# търсените формати. Значи трябва да теглим
# звука. Защото винаги първо теглим звука, а това е
# последното разклонение на логиката ни, когато сме
# достигнали: Тогава.
ft="599/600/249/250/139/251/140"
yar[1]="youtube-dl -f "$ft" $l &"
echo "tralala 3 $l"
# Това съм си правил някакви изпитания, които са
# изостанали. При крайния код не би съществувало.
fi
# Изхода от конструкцията Ако.
if [ -f "$l" ]; then
f=$(cat $l)
else
f=$(youtube-dl --get-filename -f $ft $l)
echo "$f" > $l
touch "$f.part"
# Тук използваме страничното действие на touch, ако
# няма *.part да го създаде без съдържание. Ако
# съществува, нищо няма да направи. Но на този етап
# ние нямаме *.part, а наличието му е жизнено важно за
# хака, който ще направим след няколко реда. Логиката
# ни върви обратно. Целта е последна, начина за
# достигане е пръв. Това е основата на всяко
# порномиране.
fi
# Тази особена конструкция Ако е отново хак с цел
# намаляване загубата на време. Как ще се казва бъдещия
# документ е отново една много бавна операция, вършена от
# външна система, която може и да надвиши пет минути. За
# целта минимизираме извикването Ydl. Отново ще запишем
# тази информация във временен документ, защото съвсем
# скоро ще стане ясно, че се лашкаме между обвивки и си
# прехвърляме топката, а ние искаме между тези действия,
# тази информация да е достъпна. Както е забелязал Мейк,
# името на временния документ ще е идентификатора на
# нужното ни видео. Това име ще го ползваме при
# сглобяването след малко, за да спестим мъничко
# време. Защо? След малко.
yar[0]='for i in `seq 1 20`;do if [ -f "'$f'.part" ]; then '
yar[2]=' sleep 200 && pkill -9 youtube-dl; else rm $0 "'$l'" && ./for & exit ;fi ;done'
# Завършваме сглобяването на масива. Става ясно, защо
# преди малко попълвахме втория му елемент, а не
# първия. Това, което правим е нов цикъл. В темата вече
# споменах, че за Bash e невъзможно да завърти цикъл в
# цикъл, защото това значи внучеста обвивка, което е
# невъзможно действие. Единствения друг начин е с goto, но
# то в Bash не е налично. Има привнесени решения по
# мрежата, но това усложнява кода, сиреч прави го още
# по-чуплив. Не е възможно да контролираш средата на
# изпълнение, затова всеки ред код е потенциална
# катастрофа. Трябва кода да е възможно най-малко.
# Със заклинанието от първия ред, казваме, че ще въртим
# двадесет пъти. Помниш, че `` означава изпълнение в
# подобвивка. Сиреч този код ни казва дай ми: 1, 2, 3
# ... ,20.
# Тук е и хака за спестяване на време. Възможно е да
# теглим кратко клипче (грубо под час, час и нещо). Тогава
# си вкарваме условие, че ако няма наличен
# *идентификатор.разширение.part, то значи е потеглен и не
# щем повече да въртим и чакаме по 3 мин. и 20 сек. на
# врътка.
# Използваме факта, че Ydl по подразбиране си създава
# *.part за всичко, което тегли в същия каталог, в който
# се изпълнява. Като чудесен инструмент, той ни позволява
# настройка на това поведение. Може да налива направо в
# окончателния документ или пък временния документ да е с
# друго разположение, а не работния каталог. За нашия
# случай обаче подразбиращото поведение е
# предпочитано. Затова Ydl е чудесен. Винаги
# подразбиращото поведение е най-доброто, но ни дава да
# настоим всичко по наше предпочитание и нужда.
# Както казах, налага ни се да убиваме Ydl, защото той
# блокира често и оставаме без нищо в ръцете. Ако
# скоростта ни беше бърза, то нямаше да се налага да
# въртим между започване, чакане, убиване, отново (хе-хе,
# нещо като REPL), но тогава нямаше и да се налага да
# пишем наш код, защото Ydl може да съединява потоци
# (стига да не са webm за звук), като сам извиква
# ffmpeg. FFmpeg има широко приложение в Ydl. Не е само за
# това. Без FFmpeg, Ydl не би успял да направи и 10% от
# нещата, които прави.
# На втория ред, след като цикъла от врътки е приключил,
# връщаме топката във for (сиреч този код), за да прецени,
# какво е успяло да се изтегли.
# Сигурно го знаеш, но специалната променлива $0 означава
# името на документа, който изпълняваме. Сиреч с rm $0
# изриваме боклука, който сме създали.
echo ${yar[0]}${yar[1]}${yar[2]} > for2
# Наливаме съставения низ в нов временен документ наречен
# for2. Не го споменах преди, но съм сигурен, че
# знаеш. Все пак ще спомена. Знака по-голямо (great than
# или >) значи, че изхода от изпълнението на команда ще се
# запише в документ, а не на стандартния изход (демек
# обвивката). Ако са два, ще бъде добавяне. Ако не
# съществува, ще се създаде. Обаче ние не искаме да
# добавяме. Ако все още съществува по някаква причина (а
# не би трябвало), ние заличаваме съдържанието
# му. По-голямо е наш отчаян опит да контролираме
# неконтрилируема среда. Това е причината и за сетен път
# го казвам, всеки код, който прави какво да е, трябва да
# е максимално кратък и да се изпълнява максимално бързо,
# за да намаляваме доколкото е възможно рисковете на
# обкръжението ни, а те по никакъв начин не зависят от
# нас.
echo "Download..."
# Следва най-бавната задача по потегляне на нещо, какво да
# е, та е редно да го съобщим на потребителя, въпреки, че
# самия Ydl е предостатъчно приказлив.
sh for2 &
echo "bibi $l"
exit
# Изпълняваме туко що създадения от нас for2. За да
# избегнем усложняване в кода (съответно възможни
# катастрофи), не сме го чесали с chmod +x, а директно го
# подаваме на sh. Кода вътре е най-простия и краткия
# възможен, затова е по силите и на обикновения sh.
# С exit спираме по-нататъшно изпълнение. На този етап то
# вече не е необходимо. Прехвърлили сме работата на
# друг. Онова „bibi“ e било проверка, какво ме проваля и
# не би съществувало в окончателния вариант на кода.
fi
fi
done < oti2
# Свършили са всички редове в oti2 и излизаме благополучно от
# Докато. Идеята на по-малко е логично да е обратната на по-голямо,
# което вече обсъдихме преди малко.
rm oti2
# Почистваме.
echo "Done!"
#Казваме на потребителя да си отдъхне. Всъщност го лъжем. Той те първа
#ще види, какви сме ги свършили и изобщо има ли резултат.
exit
# Казваме на обвивката да се самоубие без грешка. По принцип не е
# необходимо. Bash така или иначе ще излезе от изпълнение след и
# последния ред на документа, ама ей тъй за добре свършена
# работа. Мърлявщината не е качество, което е за поощрение.