27 February 2012

Bash-script с gui для проигрывания видео из Vkontakte в обычном плеере

Social networks and communities
Sandbox
Часто, занимаясь своими делами, я люблю открыть какие-нибудь фильмы/клипы/сериалы, которые не требуют никакого внимания, повесив плеер где-нибудь в углу экрана. В наш век быстрого интернета и большого количества свободного видео в социальных сетях(например Vkontakte) во многом удобно смотреть их в потоке, не захламляя ненужными файлами жесткий диск… Но, попытавшись реализовать описанную выше схему, мы сталкиваемся с тем, что повесить окно браузера с плеером «где-нибудь» в углу экрана так, чтобы делать что-то полезное, больше не отвлекаясь проигрывание видео, не так уж просто. Кроме того, flash обычно ест больше ресурсов, чем обычный плеер в системе, что несколько критично для маломощных нетбуков.

Поэтому ( а еще потому, что в плеере Vkontakte отсутствует playlist, и это заставляет отвлекаться от основного занятия, когда заканчивается серия), взвесив все плюсы и минусы и посмотрев на исходный код страниц с видео, я понял, что в большинстве своем, они не сильно отличаются, и возможностей bash вполне достаточно, для реализации родившейся идеи — всё, что требуется для получения со страницы ссылки на видео — подобрать необходимые регулярные выражения и разобрать полученные wget-ом данные с помощью стандартных команд, таких как grep, sed и tr.

Получение Видео

Первой, да и, наверное, единственной проблемой стала необходимость авторизации в контакте, для получения большинства страниц. С помощью wget не было найдено возможности воспользоваться cookies из браузера и, из-за этого возникла необходимость предварительного создания файла с cookies непосредственно для wget:

wget -O- -q --save-cookies .vk-cookie.txt "http://vk.com/login.php?act=login&email=$mail&pass=$pass" >>/dev/null

Дальнейшее казалось не очень сложным — найти закономерность и фразы от которых можно оттолкнуться и, воспользовавшись возможностями редактора sed, получить ссылку на видео. Но и здесь поджидали некоторые проблемы: одной из самых больших неожиданностей стало различие в получаемых значениях в таких случаях как

echo "/me/" | sed  "s/\//\\\//"g>>var
echo "/me/" | sed  s/'\/'/'\\\/'/g >>var
var=`echo "/me/" | sed  s/'\/'/'\\\/'/g` 
var=`echo "/me/" | sed  s#'\/'#'\\\/'#g` 
var=`echo "/me/" | sed  "s/\//\\\//"g`
В первом и третьем случае — команда не выполняется, сообщая о синтаксической ошибке в где-то в середине строки. В пятом случае команда ничего не выполняет никаких действий с передаваемой строкой. И только воспользовавшись вторым и четвертым вариантами — мы получим желаемый результат( замену "/" на "\/").
Перед добавлением файла в плейлист или его скачиванием необходимо было реализовать проверку на его наличие на сервере — что было очень просто сделать при помощи стандартных возможностей wget:

wget --spider $addr >>/dev/null # checking
if [ $? -ne 0 ]; then #if file not exist
else
fi
 


Собрав это все вместе, я получил не очень сложный скрипт, для просмотра видео. Но для добавления каждого файла необходимо было
  1. Скопировать ссылку на него
  2. Открыть консоль
  3. Вызвать скрипт
  4. Передать ему ссылку
  5. Выбрать действия с файлом(сохранить/проиграть)

Не самое короткая последовательность действий, особенно. когда добавляешь целый сезон какого-нибудь сериала. Поэтому, немного подумав, я сел искать возможности по облегчению процесса, без его существенного усложнения.

Упрощение процесса


Первым пунктом в этом стало использование очень полезной утилиты xsel, которая позволяет обращаться к буферу системы. Так команда была сокращена от «vkvid http ://csNNNNNN.vk.com/uNNNNNN/video/nnnnnnnnn.mov», где всегда присутствовала необходимость менять ссылку, до постоянной vkvid "$(xsel -o)", которую можно было брать из истории команд не задумываясь.

Графический интерфейс

Следующим, вполне ожидаемым шагом, стало добавление какого-никакого, а GUI. Сложные варианты даже не рассматривались, так как для них, было бы необходимо переписывать основную часть скрипта. Поэтому, после непродолжительного поиска, были найдены несколько вариантов: Xdialog и более широкий в плане возможностей Zeniti.

Но ни один, ни другой не позволяют реализовывать хоть сколько-нибудь сложные окна, считающие в себе несколько вариантов выбора, как, например, Radiobutton, checklist и текстовая форма.

Поэтому, поиск был продолжен, пока не был найден еще один вариант GUI для Bash-скриптов — YAD. Этот еще довольно молодой, развивающийся проект почти полностью подходил для поставленной задачи.

Создание не утяжеленного ничем лишним окна не отняло много времени:



action=$(yad --center --width=400 --title="Vkvideo Save" --text="Simple video 
downloader"  --form --field="Select Resolution::CB"   --field="Name"  --field="Folder:DIR"   --field="Keep Cookies:CHK" "720!480!360!240" "$tname" "$HOME" TRUE --button="Open File:2" --button="Save File:0" --button="gtk-cancel:1") 

Результат выполнения формы — это строка разделенная "|", в разборке которой опят же пригодился редактор sed.

	tres=`echo $action | sed -e s/'|.*|'/''/`
	tname=`echo $action | sed -e s/"$tres"'|'/''/ | sed -e s/"|.*"/''/` 
	tfolder=`echo $action | sed -e s/"$tres"'|'"$tname"'|'/''/ | sed -e s/"|.*"/''/`  

Как дополнительный бонус, очень простой оказалась реализация процесса загрузки файла при скачивании файла. В Zeniti и очень похожем на неё Yad для этого потребовалась всего одна строчка
 wget  $addr -O "$tfolder/$tname.$tres.mov" 2>&1 |sed -u 's/.* \([0-9]\+%\)\ \+\([0-9.]\+.\) \(.*\)/\1\n# Downloading at \2\/s, time left: \3/'| yad --width=500 --center --title="Downloading progress" \  --text="Downloading '$tname.$tres.mov' " --progress --auto-close --auto-kill & 


После написания, команда была прикреплена к сочетанию клавиш на клавиатуре. Таким образом, количество операций, необходимых для добавления файла, сократилось до 2-х:
  1. Скопировать ссылку из поиска видео Vkontakte
  2. Нажать сочетание клавиш и выбрать сохраниение или добавление в playlist


Для корректной работы необходимы:
  • enco
  • YAD
  • Player — написано для двух вариантов — SMPlayer и VLC


Код:

#!/bin/bash

#$1 link name
#$2 resolution
#$3 save file name

#checking for existing cookies 
res=(720 480 360 240)

tadr=$1
tres=$2
tname=$3


if [ -z "$tres" ]; then
	echo "$tres"
	tres=720
fi	

if [ ! -f "$HOME/.vk-cookie.txt" ]; then
	
	action1=$(yad --center --title="Lost cookies" --text="You need to enter mail and password" --form --field='Mail'  --field='Password':H   --button="gtk-ok:0" --button="gtk-cancel:1")
	mail=`echo $action1 | sed -e s/'|.*|'/''/`
	password=`echo $action1 | sed -e s/"$mail|"/''/ | tr -d '|'`

	echo "Getting cookies"
 	wget -O- -q --save-cookies .vk-cookie.txt "http://vk.com/login.php?act=login&email=$mail&pass=$pass" >>/dev/null    
	echo "Cookies saved"
fi

while [ "$SoD" != "finishdone" ]
do
	echo "Page downloading...."
	LANG=ru_RU.CP1251 wget -q  --load-cookies .vk-cookie.txt "$tadr" -O .tempf #download the page
	echo "Page  recieved"
	enconv -x=UTF-8 -L ru .tempf
	grep -i ltag .tempf| egrep -o "http:\\\/\\\/cs[0-9]*.vkontakte.ru\\\/[a-z][0-9]*\\\/"| sed -e s/'http:\\\/\\\/cs'/'http:\/\/cs'/ | sed -e s/'vkontakte.ru\\\/'/'vk.com\/'/| sed -e s/'\\'/''/ >> .tempama #get server and  owner names
	echo video/>> .tempama
	if [ "$tname" == "" ]; then
		#echo `cat .tempf`		
		egrep -o "md_title.*md_author" .tempf | sed -e s/'md_title\\\"\:\\\"'/''/ |sed -e s/'\\\"\,\\\"md_author.*'/''/>>.tempnam
		tname=`cat .tempnam|sed s#' | '#' '#g|sed s#'[|,/,\]'#' '#g|tr --squeeze-repeats ' '| tr '[]{}' '()()'|sed s#' \([(,)]\)'#'\1'#g|sed s#'\([(,)]\) '#'\1'#g`	
		rm .tempnam
	fi
	addr=`cat .tempama`
	addr=`echo $addr | tr -d ' '`
	if [ $addr == "video/" ]; then
		tadr=$(yad --width 500 --center --title="Bad address" --text="Bad address. Reenter please" --entry --button="gtk-cancel:1" )
		rm .tempama
		rm .tempf	
	else
		SoD=finishdone
	fi
	
done

echo "Ready for downloading"

action=$(yad --center --width=400 --title="Vkvideo Save" --text="Simple video downloader"  --form --field="Select Resolution::CB"   --field="Name"  --field="Folder:DIR"   --field="Keep Cookies:CHK" "720!480!360!240" "$tname" "$HOME" TRUE --button="Open File:2" --button="Save File:0" --button="gtk-cancel:1")
SO=$?
	tres=`echo $action | sed -e s/'|.*|'/''/`
	tname=`echo $action | sed -e s/"$tres"'|'/''/ | sed -e s/"|.*"/''/` 
	tfolder=`echo $action | sed -e s/"$tres"'|'"$tname"'|'/''/ | sed -e s/"|.*"/''/` 
	echo $tfolder | sed  "s$\/$\\\/$"g>>tvar
	tvar=`cat tvar`
	keep=`echo $action | sed -e s/"$tres"'|'"$tname"'|'"$tvar"'|'/''/ | tr -d '|'`
	rm tvar
	grep -i ltag .tempf| egrep -o 'vtag\\\"\:\\\"[a-z0-9]*\\'| sed 's/vtag\\\"\:\\\"//'|sed -e s/'\\'/'\.'"$tres"'\.mov'/ >>.tempam #get video file name
	addr=`cat .tempama .tempam` #recover full link
	addr=`echo $addr | tr -d ' '` #remove spaces
	rm .tempama
	rm .tempam
	rm .tempf
case $keep in
		TRUE)
			;;
		FALSE)
			rm $HOME/.vk-cookie.txt;;
esac
case $SO in
		0)
		wget --spider $addr >>/dev/null
		if [ $? -ne 0 ]; then
			rm "$tname.$tres.mov"
			for ((i=0; i <= 3 ; i++))
			do
				if [ $i -ne 0 ];then				
					let "j = $i - 1"
				else 
					j=0;				
				fi				
				if [ ${res[$i]} -le $tres ]; then
					addr=`echo $addr | sed -e s/"${res[$j]}"/"${res[$i]}"/`
					if [ ${res[$i]} -ne $tres  ];then					
					wget --spider  $addr >>/dev/null
						if [ $? -eq 0 ]; then
							RtD=2
							tres=${res[$i]}
							i=10
						fi							
					fi				
				fi
			done			
		else
			RtD=1
		fi

		rand="$RANDOM `date`"
		pipe="/tmp/pipe.`echo '$rand' | md5sum | tr -d ' -'`"
		mkfifo $pipe
		tdown=0
		tnamesp=`echo "$tname" |tr -d '|'| tr ' ' '_'`
		wget -c $addr -O "$tfolder/$tnamesp.$tres.mov" 2>&1 | while read data; do		
		if [ "`echo $data | grep '^Length:'`" ]; then
			total_size=`echo $data | grep "^Length:" | sed 's/.*\((.*)\).*/\1/' |  tr -d '()'`
		fi
		if [ "`echo $data | grep '[0-9]*%' `" ];then 
			percent=`echo $data | grep -o "[0-9]*%" | tr -d '%'` 
			current=`echo $data | grep "[0-9]*%" | sed 's/\([0-9BKMG.]\+\).*/\1/' ` 
			echo $percent
			echo "#Name: $tnamesp \nVideo size: $tres \nFrom: $1\n$current of $total_size ($percent%)\n"
		fi
		if [ "$current" != "$total_size" ];then
			tdown=fail
		fi
		done > $pipe &
		wget_info=`ps ax |grep "wget.*$addr" |awk '{print $1"|"$2}'`
		wget_pid=`echo $wget_info|cut -d'|' -f1 `
		zenity --progress --auto-close --text="Connecting to $addr\n\n\n" --width="350" --title="Downloading"< $pipe
		if [ "`ps -A |grep "$wget_pid"`" ];then
		kill $wget_pid
		rm $tnamesp.$tres.mov
		tdown=fail

		fi
		rm -f $pipe

		if [ "$tdown" != "fail" ]
			then
			case $RtD in
				2)
					echo "Resolution change. File "$tnamesp"."$tres".mov successfully saved"
					yad --center --text-align=center --height=25 --title="File Saved" --no-buttons --timeout=8 --text="Resolution change. File $tnamesp.$tres.mov successfully saved";;						
				1)
					yad --center --text-align=center --height=25 --title="File Saved" --no-buttons --timeout=8 --text="File $tnamesp.$tres.mov successfully saved"							
					echo "File "$tnamesp"."$tres".mov successfully saved";;
			esac					
		else
			yad --center --text-align=center --height=25 --title="ERROR" --no-buttons --timeout=8 --text="File $tname.$tres.mov has NOT been saved"				
		fi;;

		2)		
		wget --spider $addr >>/dev/null # checking
		if [ $? -ne 0 ]; then
			for ((i=0; i <= 3 ; i++))
			do
				if [ $i -ne 0 ];then				
					let "j = $i - 1"
				else 
					j=0;				
				fi				
				if [ ${res[$i]} -le $tres ]; then
					addr=`echo $addr | sed -e s/"${res[$j]}"/"${res[$i]}"/`
					if [ ${res[$i]} -ne $tres  ];then
						wget --spider $addr >>/dev/null # checking
						if [ $? -eq 0 ]; then
							i=10;
						fi
					fi				
				fi
			done
		fi	
		echo "Playing file" 
		echo "$tname"		
		#vlc --started-from-file --playlist-enqueu "$addr" &		
		smplayer -minigui -add-to-playlist $addr &
		sleep 5	
		echo "File is playing";;
esac
exit


UPD: Простота реализации прогресс бара оказалась несколько преувеличенной. GUI загрузки отключался, когда размер сохраненной части файла превышал 100 Mb. Поэтому, с помощью Google и Ubuntuforums, были внесены небольшие корректировки:
rand="$RANDOM `date`"
			  pipe="/tmp/pipe.`echo '$rand' | md5sum | tr -d ' -'`"
			  mkfifo $pipe
			  wget -c $addr -O "$tfolder/$tname.$tres.mov" 2>&1 | while read data; do
			    if [ "`echo $data | grep '^Length:'`" ]; then
			      total_size=`echo $data | grep "^Length:" | sed 's/.*\((.*)\).*/\1/' |  tr -d '()'`
			    fi
			    if [ "`echo $data | grep '[0-9]*%' `" ];then 
			      percent=`echo $data | grep -o "[0-9]*%" | tr -d '%'` 
			      current=`echo $data | grep "[0-9]*%" | sed 's/\([0-9BKMG.]\+\).*/\1/' ` 
			      echo $percent
			      echo "#Name: $tname \nVideo size: $tres \nFrom: $1\n$current of $total_size ($percent%)\n"
			    fi
			    if [ "$current" != "$total_size" ];then
				tdown=fail
			    fi
			  done > $pipe &

			  wget_info=`ps ax |grep "wget.*$addr" |awk '{print $1"|"$2}'`
			  wget_pid=`echo $wget_info|cut -d'|' -f1 `

			  zenity --progress --auto-close --text="Connecting to $addr\n\n\n" --width="350" --title="Downloading"< $pipe

UPD2: поправил баг, проявляющийся при сохранении, если в названии присутствуют спецсимволы: [,],{.},|

Полный вариант кода исправлен.
Tags:vkontaktebashyadвидео
Hubs: Social networks and communities
+29
6.4k 128
Comments 10
Popular right now
SEO-специалист
December 7, 202064,900 ₽Нетология
UX-дизайнер
December 7, 202047,940 ₽Нетология
iOS-разработчик с нуля
December 7, 202070,740 ₽Нетология
Python для работы с данными
December 7, 202031,500 ₽Нетология