Скачиваем видео в формате m3u8

Введение

На днях у меня появилась потребность сохранить видео лекции с некоторого сайта, которой такой функциональности не представляет, а причиной этой необходимости послужило то, что после определенного времени с момента оплаты видео становится не доступным.

Анализ

Первое что пришло мне в голову это записать видео с экрана, например через QuickTime, но в таком случае нужно все их просмотреть а это по времени достаточно долго и этот вариант мне не понравился.

Следующим шагом было исследовать html код страницы, и там я нашел интересную ссылку:

демо

Далее я попробовал подставить эту ссылку в адресную строку браузера и в результате скачался файл master.m3u8 следующего содержания:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=768000,CODECS="mp4a.40.2, avc1.640028",RESOLUTION=640x360
https://vh-04.getcourse.ru/player/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/media/360.m3u8?sid=sid&host=vh-04&cdn=1&cdn-second=0&integros-s3=1&akamai-defence=0&v=2:0:1:1
#EXT-X-STREAM-INF:BANDWIDTH=1024000,CODECS="mp4a.40.2, avc1.640028",RESOLUTION=853x480
https://vh-04.getcourse.ru/player/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/media/480.m3u8?sid=sid&host=vh-04&cdn=1&cdn-second=0&integros-s3=1&akamai-defence=0&v=2:0:1:1
#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="mp4a.40.2, avc1.640028",RESOLUTION=1280x720
https://vh-04.getcourse.ru/player/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/media/720.m3u8?sid=sid&host=vh-04&cdn=1&cdn-second=0&integros-s3=1&akamai-defence=0&v=2:0:1:1
#EXT-X-STREAM-INF:BANDWIDTH=4096000,CODECS="mp4a.40.2, avc1.640028",RESOLUTION=1920x1080
https://vh-04.getcourse.ru/player/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/media/1080.m3u8?sid=sid&host=vh-04&cdn=1&cdn-second=0&integros-s3=1&akamai-defence=0&v=2:0:1:1

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

Я решил посмотреть что из себя представляет файл 720.m3u8, для этого я его скачал, перейдя по соответствующей ссылке:

#EXTM3U
#EXT-X-TARGETDURATION:11
#EXT-X-ALLOW-CACHE:YES
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD

#EXTINF:9.160000,
https://getcourse-cdn-a1a5df3e.cdn.integros.com/ch/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/720/0.ts?sid=sid&host=vh-04
#EXTINF:10.080000,
https://getcourse-cdn-a1a5df3e.cdn.integros.com/ch/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/720/1.ts?sid=sid&host=vh-04
#EXTINF:10.080000,
https://getcourse-cdn-a1a5df3e.cdn.integros.com/ch/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/720/2.ts?sid=sid&host=vh-04
#EXTINF:10.080000,
...
#EXTINF:8.800000,
https://getcourse-cdn-a1a5df3e.cdn.integros.com/ch/89cc32eeb182080b719abfc7f106aaaf/5b7008b4e8fab9852419a5c00e4aa618/720/119.ts?sid=sid&host=vh-04

#EXT-X-ENDLIST

Если присмотреться внимательней то можно увидеть что все ссылки содержат *.ts, после того как я их скачал, оказалось что это как раз куски данного урока.

После небольшого поиска что-же такое m3u8 файл, я выяснил, что это формат для хранения плейлистов.

Теперь был вопрос как же его сохранить локально.

Сохранение видео из m3u8

Первое что я нашел в Интернете предлагало это сделать через VLC. Для этого нужно зайти в Файл -> Конвертировать/Передавать, после чего можно увидеть следующее окно:

демо

В указывается исходный файл плейлиста, профиль сохранения и выходной файл. После чего нажимается кнопка Сохранить и процесс начинается (надо отметить, что процесс не быстрый).

Альтернативное сохранение

После того как таким способом я сохранил пару видео, я начал думать как оптимизировать процесс. Тут меня посетила мысль, что можно просто попробовать скачать все эти маленькие файлы и объединить их в один.

Для этого я написал небольшую утилитку на Go, для обработки файла *.m3u8 и объединения всех маленьких кусков из него в один файл.

Алгоритм работы ее достаточно прост: сначала я перехожу по заданной ссылке на m3u8, затем построчно обхожу его и если строка содержит URL кусочка, я скачиваю его и результат сразу пишу в выходной файл.

Код который это делает следующий:

	// received file from server
	resp, err := http.Get(inputUrl)
	if err != nil {
		log.Fatal("Download error: ", err)
	}
	defer resp.Body.Close()

	// create output file
	f, err := os.Create(outputFile)
	if err != nil {
		log.Fatal("Download error: ", err)
	}
	defer f.Close()

	// read server response line by line
	scanner := bufio.NewScanner(resp.Body)
	i := 0
	for scanner.Scan() {
		l := scanner.Text()

		// if line contains url address
		if strings.HasPrefix(l, "http") {
			// download file part
			part, err := downloadFilePart(l)
			if err != nil {
				log.Fatal("Download part error: ", err)
			}

			// write part to output file
			if _, err = f.Write(part); err != nil {
				log.Fatal("Write part to output file: ", err)
			}
			log.Printf("Download part %d\n", i)
			i++
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

Функция downloadFilePart отвечает именно за скачивание файла и вывод его в байтовый массив:

func downloadFilePart(url string) ([]byte, error) {
	result := make([]byte, 0)

	resp, err := http.Get(url)
	if err != nil {
		return result, err
	}

	if result, err = ioutil.ReadAll(resp.Body); err != nil {
		return result, err
	}

	return result, err
}

Такой метод полностью себя оправдал и оказался самый быстрый.

Заключение

В конечном счете я не только скачал все нужные лекции, но еще и создал утилиту для скачки и сохранения *.m3u8 файлов.

Ссылки

  1. m3u8-downloader
 
comments powered by Disqus