Скачиваем Youtube плейлист в формате mp3 одним bash-скриптом

Так сложилось, что в данный момент мой рабочий ноутбук оснащен лишь 2GB оперативной памяти. В связи с этим возникла необходимость оптимизации браузера, т.к. при большом количестве открытых вкладок памяти становится недостаточно и используется swap-раздел, что ведет к тормозам.

В работе мне помогает музыка, обычно это открытый таб с плейлистом Youtube. Так вот этот таб в просессе работы съедает до 500MB (!) и даже больше (Google Chrome).

Такое положение дел вынудило написать bash-скрипт, который на входе получает ID плейлиста, на выходе – mp3 файлы, которые можно слушать в любимом плеере, например, в MOC:
MOC


Скачать mp3 с Youtube нельзя, поэтому процесс делим на 3 шага:
  1. скачиваем flv
  2. извлекаем звуковую дорожку
  3. удаляем временный flv


На всякий случай напомню, что ID плейлиста это get-параметр «list».

Зависимости:
sudo apt-get install youtube-dl ffmpeg libavcodec-extra-53

  • youtube-dl для скачивания видеофайла
  • ffmpeg libavcodec-extra-53 для конвертации в mp3


Собственно сам скрипт, подробно прокомментированный(скачать):
#!/bin/bash

usage='usage: 
  ./get_youtube_playlist <playlist_id> <target_folder> <num_songs>
    target_folder: (default: songs will be downloaded in current folder)
    num_songs:     number of songs to get (default: 50)

examples: 
  ./get_youtube_playlist RD02HIkZaLeuF9k
  ./get_youtube_playlist RD02HIkZaLeuF9k "instrumental hip-hop beats" 10
'

playlist_id=$1
target_folder=$2
num_songs=$3

if [ -z "$playlist_id" ]; then
    echo "$usage"
    exit 1
fi

if ! [[ "$num_songs" =~ ^[0-9]+$ ]] ; then
    num_songs=50
fi

if [ -z "$target_folder" ]; then
    target_folder='./'
elif [ ! -d "$target_folder" ]; then
    echo "Parameter target_folder is incorrect, $usage"
    exit 1
fi


# используем Youtube API для получения списка песен
# https://developers.google.com/youtube/2.0/developers_guide_protocol_playlist_search
youtube_api="`wget -qO- https://gdata.youtube.com/feeds/api/playlists/$playlist_id\?max-results\=$num_songs`"
if [ -z "$youtube_api" ]; then
    echo "Playlist ID is incorrect, $usage"
    exit 1
fi

# cписок ID песен помещаем в массив songs
songs=( 
    $(echo $youtube_api | \
    grep -P -o "<media:player url='.*?&" | \
    grep -P -o "(\w|-){11}") 
)

if [ -z "$songs" ]; then
    echo "Nothing to do, $usage"
    exit 1
fi

# теперь работаем с каждой отдельной песней
for (( i = 1 ; i <= ${#songs[@]} ; i++ )) 
do
    youtube_id=${songs[$i-1]}
    track_number=`printf "%0*d" 2 $i`
    flv_path="$target_folder/$youtube_id.flv"
    mp3_path="$target_folder/$track_number. $youtube_id.mp3"

    # 1. скачиваем flv
    youtube-dl --audio-format=mp3 -o "$flv_path" "http://youtu.be/$youtube_id"
    
    if [ -f "$flv_path" ]
    then
        # 2. flv -> mp3
        avconv -i "$flv_path" -y "$mp3_path" -acodec libmp3lame -ac 2 -ab 128k -vn

        # 3. удаляем flv
        rm "$flv_path"
    fi
done


Надеюсь, этот скрипт полезен не только мне и будет экономить много гигабайт оперативной памяти у хабражителей.
Share post

Similar posts

Comments 48

    +2
    Ух. А чего он такой здоровый? Кстати, вытаскивать из flv намного проще связкой из mplayer и lame:
    Скрытый текст
    #!/usr/bin/env csharp
    using System.IO;
    using System.Diagnostics;
    
    var output =  System.Environment.GetCommandLineArgs ()[2];
    Console.WriteLine ("Output directory is " + output);
    
    foreach (var filename in Directory.GetFiles(Directory.GetCurrentDirectory()))
    {
            if(filename == "audiodump.wav")
                    continue;
            if(File.Exists ("audiodump.wav"))
                    File.Delete ("audiodump.wav");
    
            Console.WriteLine ("Processing " + filename);
            var mplayer = Process.Start ("mplayer", "-vc null -vo null -ao pcm -benchmark -- \""+filename+"\"");
            mplayer.WaitForExit ();
            if(mplayer.ExitCode!=0)
                    System.Environment.Exit(1);
    
            var lame = Process.Start ("lame", string.Format ("audiodump.wav \"{1}/{0}.mp3\"", Path.GetFileNameWithoutExtension(filename), output));
            lame.WaitForExit ();
            if(lame.ExitCode!=0)
                    System.Environment.Exit(2);
    
    }
    
    

    А плейлист качается и вовсе однострочным скриптом (первый параметр — ссылка на плейлист):
    #!/bin/sh
    curl "$1" |grep "/watch"|grep index|sed "s/.amp.*//"|sed "s/.*href../http:\/\/youtube.com/"|xargs -n 1 youtube-dl
    
    
      +6
      А, прошу прощения, скрипт у вас не здоровый, а хорошо задокументированный. Тем не менее связка из mplayer + lame видится мне более универсальной ввиду всеядности первого.
        +6
        Спасибо за критику. Кусок скрипта, который конвертирует видео в mp3 используется мной также и в другом bash-скрипте, который живет на сервере, устанавливать на который mplayer со всеми его дополнительными пакетами нерационально.

        Что касается всеядности, не было ни одного flv файла, который бы не понял avconv.

        PS: первый комментарий на хабре! woooohooo! :)
          0
          ffmpeg (или avconv) тоже всеяден.
            0
            У меня, помнится, года два или три назад он отказался что-то выкачанное из интернета читать вываливаясь с сегфолтом, а mplayer сжевал. Возможно, сейчас ситуация лучше.
        –4
        Скачивать ролики с ютуба ради только музыки? Неужели на всяких grooveshark.com/ или Я.Музыке нету ваших любимых исполнителей?
          0
          Покажите, где там можно скачать подобный набор аранжировок треков из Тохо, например. Или ещё какую-нибудь не совсем мейнстримную штуку.
            +3
            Так я не настаиваю, я удивлённо вопрошаю :) Лично мне намного больше нравится слушать музыку с сайтов типа pandora.com в режиме радио — частенько попадается новые интересные исполнители и треки.
            0
            А вот не скажите, на YouTube музыка разная бывает.

            Я, например, очень люблю слушать саундтреки, переложенные на фортепиано. На YouTube тысячи кошерных версий, которых вы больше нигде не найдете, ибо авторы выкладывают только туда, записывая их игру на видео.
              0
              Youtube плейлисты очень удобны тем, что сформированы тематически, например «classic guitar» или «instrumental hip-hop beats». Насколько мне известно, в Grooveshark такой возможности нет. Сервис Яндекс.музыка недоступен в моей стране.
                0
                Про грувшарк там немного другой механизм — там есть broadcast по тегам, что, в принципе, тоже самое. Причем, там можно говорить, что композиция не нравится, и он будет это учитывать (уж как именно, не знаю). А получившийся в процессе броадкастинья лист можно и сохранить, как свой.

                Но скрипт полезный, спасибо, в свою копилочку положу.
              0
              [Удалено, не туда]
                +1
                Круто! Я пользовался до сего момента 4k YouTube to MP3, попробую перейти на Ваше решение =)
                  0
                  Вот инженерный подход к решению проблем)
                    +3
                    к слову об использовании памяти: на старом ноутбуке стал использовать Firefox вместо Chrome — желание его выкинуть пропало :)
                      0
                      А нет ли подобного скрипта для VK?
                        0
                        Не проще ли использовать -acodec copy? Даже если там будет не mp3, а какой-нибудь AAC, разве это большая проблема?
                          0
                          MOC по умолчанию не дружит с AAC
                            0
                            faad2 у меня в системе уже стоял, он в обязательных зависимостях тех же mplayer и vlc.
                          0
                          Кстати, для себя я заменил подписку на каналы на youtube таким скриптом-однострочником на bash:
                          while true; do for s in $(cat subscriptions); do ../youtube-dl -c --max-quality 22 -o '/home/youtube-dl/Youtube/%(upload_date)s %(uploader)s — %(title)s %(id)s.%(ext)s' --max-downloads 10 "$s"; done; sleep 1000; done
                          Соответственно, в файле subscriptions лежат каналы, которые нужно скачивать.
                            +1
                            спасибо что поделились, хорошая идея.
                              0
                              Только есть нюанс в том, в каком формате нужно указывать подписки в файле. Впринципе, можно указывать любой, понимаемый youtube-dl: хоть ссылку на канал, хоть ссылку на плейлист, хоть ytuser:channername, и даже ytsearch:cats. Но проблема в том, что не всегда эти списки возвращают видео отсортированные по дате в порядке убывания, а это проблема (скорее всего баг youtube-dl). К слову, ytuser:blahblahblah работает нормально.
                                0
                                Спасибо, не знал, полез читать доки, нашел еще --extract-audio так что начальный скрипт можно еще сократить.
                                А мой скрипт порядком сократился — до этого парсил страницу юзера регулярками
                            +1
                            А в чём смысл использования конструкции $(echo $target_folder)? Почему нельзя просто написать $target_folder?
                              0
                              Хорошее замечание, в своих bash-скриптах я стараюсь всегда использовать конструкцию $(echo $target_folder) в строках, это позволяет не задумываться об экранизации переменных + они визуально отделены.

                              Обновил скрипт, заменив везде $(echo $target_folder) на $target_folder, код стал красивее и не потерял читабельности.

                              Спасибо за внимательность к коду.
                                +2
                                Не за что, но я, честно говоря, не понял вашей аргументации. Если нужно отделить переменную от окружающих символов, просто используйте выражение вида ${my_var}_1.

                                Если же каждое обращение к переменной оборачивать в сабшелл, это приведёт к созданию множества лишних процессов.
                                  +2
                                  Не знал про эту конструкцию, стыд-позор.
                                  Первый день на хабре прожит не зря :)
                              0
                              Вопрос почти в тему — как избавиться от настырной видео-рекламы перед роликами на youtube? Только не говорите что adblock справляется.
                                0
                                Я на оперу поставил вот эту штуку, она режет (хотя пишут, что оно только для всплывающей, возможно, именно видео-рекламу режет ещё что-то, надо посмотреть, cкорее всего adblock).
                                  0
                                  В опере urlfilter.ini помогает.
                                    0
                                    Спасибо, а в Firefox?
                                  0
                                  # ./get_youtube_playlist PL6DB07B6741FE6EA3
                                  wget: missing URL
                                  Usage: wget [OPTION]... [URL]...
                                  Try `wget --help' for more options.
                                  ./get_youtube_playlist: line 36: https://gdata.youtube.com/feeds/api/playlists/PL6DB07B6741FE6EA3?max-results=50: No such file or directory
                                  Playlist ID is incorrect, usage:
                                   ./get_youtube_playlist <playlist_id> <target_folder> <num_songs>
                                     target_folder: (default: songs will be downloaded in current folder)
                                     num_songs:     number of songs to get (default: 50)
                                  

                                    0
                                    wget говорит, что не существует https://gdata.youtube.com/feeds/api/playlists/PL6DB07B6741FE6EA3?max-results=50 хотя он есть.

                                    Что-то мне подсказывет, что ваша проблема в https://, попробуйте в 36й строке заменить https:// на http://.
                                      0
                                      Не помогает.
                                        0
                                        можете ли вы в браузере открыть https://gdata.youtube.com/feeds/api/playlists/PL6DB07B6741FE6EA3?max-results=50?
                                          0
                                          Могу, проверял.
                                            0
                                            неужели wget не работает… попробуем curl. Попробуйте, пожалуйста, 36ю строку заменить на
                                            youtube_api="`curl https://gdata.youtube.com/feeds/api/playlists/$playlist_id?max-results=$num_songs`"
                                              0
                                              тот же результат…
                                                +1
                                                замените 36 строку на
                                                youtube_api="`wget -qO- https://gdata.youtube.com/feeds/api/playlists/$playlist_id\?max-results\=$num_songs`"
                                                  0
                                                  Вот где собака зарыта… спасибо, исправленно.
                                                  Интересно почему у меня работает без экранирования, полез читать доки.
                                                    0
                                                    Спасибо, помогло!
                                        0
                                        Еще вопрос, а если я, например, хочу скачать песни с 53 по 106 из определенного плейлиста — это возможно?
                                          0
                                          API отдает максимум 50 треков.
                                          Границы 'от' и 'до' конечно возможны, но я бы не усложнял скрипт.
                                            +1
                                            Можете воспользоваться хламом из моего комментария. В вашем случае будет что-то типа
                                            #!/bin/sh
                                            PLAYLIST_URL=$1
                                            FROM=$2
                                            TO=$3
                                            
                                            
                                            curl $PLAYLIST_URL |grep "/watch"|grep index|sed "s/.amp.*//"|sed "s/.*href../http:\/\/youtube.com/"|head -n $TO|tail -n+$FROM | xargs -n 1 youtube-dl
                                            
                                            

                                            А дальше натравите скрипт из скрытого текста
                                              0
                                              Спасибо.
                                            +2
                                            К стати, по теме, хочу поделиться одной хитростью. Я всегда «лайкал» что мне нравится на YouTube прежде всего с целью потом иметь возможность легко найти это, для меня это было как закладка. Но однажды я заметил, что в списке моих лайков всего около 200 пунктов и последними идут далеко не первые, а сравнительно недавние лайки. Боялся, что старые потеряны, но нет. Да, YouTube ограничивает длину этого списка как и любого плэйлиста случайной цийрой между 190 и 200 (как так получается и почему не точно 200 я не понял, но не суть). Но остальные хоть и скрываются, но не теряются, а появляются по мере удаления верхних. В результате я просто создал несколько отдельных плэйлистов и поперетаскивал всё из лайков туда.
                                              0
                                              в чем отличие вашего скрипта от вот такой команды, например?

                                              youtube-dl www.youtube.com/watch?list=playlist_name -x -o '%(playlist)s/%(playlist_index)s-%(title)s.%(ext)s'
                                                0
                                                Мои поделки:

                                                hg.f-y.name/svv — сайт (создан конкретно под один канал, но можно в настройках указать плейлист, убрать ускорение, приделать плейлист к аудио-плееру...)

                                                github.com/Forever-Young/speedup-extract — скрипты для ускорения-вытаскивания (можно вытаскивать без ускорения)

                                                Only users with full accounts can post comments. Log in, please.