Hulu downloader written in Go

Related tags

Network hulu
Overview

Hulu Downloader

The code in this repository allows you to download videos unencumbered with DRM from Hulu. The code in widevine is in general independent of the Hulu related code and can be used for Widevine license generation/decryption. The code in hulu is also standalone but only implements a handful of endpoints that are basically only useful for a command line tool of this nature.

Prerequisites

The code in this repository by itself does not require any external libraries or tools to be installed. It merely finds the video URLs and decryption keys. The only dependencies required are cryptographic libraraies specified in go.mod but Go should handle these automatically. However, to actually perform MP4 decryption, Bento4 (and specifically its mp4decrypt tool) are required. Bento4 is an open source library for MP4 manipulation. Binary releases of its tools can be downloaded here. yt-dlp is also required to download the MPD playlist files to mp4s. Technically, this could be implemented rather easily in this repository but I want to keep this repository simple and avoid rewriting code to deal with segment merging or quality selection menus.

Retrieving Hulu Session Cookie

Hulu requires Recaptcha for authentication so just passing account credentials is not possible without captcha solving services. To work around this, this tool simply takes a Hulu session cookie.

Note: Ensure you are signed in before following these steps.

Chrome

Visit https://hulu.com. Click the lock icon in the URL bar. Then select the item labelled Cookies. Then find hulu.com in the list, select it, and expand the "Cookies" list with an icon that looks like a folder. Then select the cookie titled _hulu_session. Chrome will then show various attributes of this cookie. Right click the area labelled "Content", press select all and then right click again and press copy. The value of the Hulu session cookie is now on your clipboard. A demonstration can be found here.

Firefox

Visit https://hulu.com. Right click and then click Inspect. Then visit the Storage tab. Now, under the cookies pane on the left, select hulu.com. Then retrieve the value of _hulu_session from the list of cookies. A demonstration can be found here.

Demonstration

Say we want to download an episode of M*A*S*H.

$ go install github.com/chris124567/hulu # The rest of these commands assume $GOPATH/bin is in your PATH.  If it is not, just cd to $GOPATH/bin and run "./hulu" instead of "hulu"
$ HULU_SESSION="abc" hulu search -query="m*a*s*h"
Title                           ID
M*A*S*H                         ae94231d-0f04-482a-b9ee-9911e339e3ed
MASH (1970)                     42f7eefe-2448-4ed5-87cb-6233c89c20f6
American Psycho (2000)          404a410c-ef36-469d-8fcd-1f93ec44a5c0
American Horror Story           a67a233c-fcfe-4e8e-b000-052603ddd616
Hitman: Agent 47 (2015)         a4d96c8d-ba7d-4d99-b4b3-942ecde47282
Ma (2019)                       dbb13a18-79d2-4567-8ed4-e2eddbec9492
The Martian (2015)              e52328e3-6e2b-4565-91d5-2f7ee7c846ab
HBO Max                         1b3523c1-3090-4c27-a1e8-a04d33867c34
...

We want the M*A*S*H TV show, so we choose the ID "ae94231d-0f04-482a-b9ee-9911e339e3ed." We want to look at the first season so we specify that the season number equals 1.

$ HULU_SESSION="abc" hulu season -id="ae94231d-0f04-482a-b9ee-9911e339e3ed" -number=1
Title                           ID
Pilot                           4045ee04-07e8-4c33-94a6-4244b7b67c5f
To Market, to Market            7a43d075-2b47-4c94-8767-8531e20bab81
Requiem for a Lightweight       2ccd2cf5-a013-4501-a689-1ed6b94a9549
Chief Surgeon Who?              112b061b-1c18-4f15-bed8-042d44919735
The Moose                       688e10d3-6db4-47ba-a99b-bfd8aacd6c7a
Yankee Doodle Doctor            3ff14d70-e2ac-4bc2-83c6-87b9cf132c13
...

Now to get the episode we want (the pilot), pass the ID of the episode to the download subcommand.

Note: If we wanted to download a movie, instead of getting the episode list (which movies don't have) and selecting the specific episode ID, just pass the original ID from the search results above to download.

$ HULU_SESSION="abc" hulu download -id="4045ee04-07e8-4c33-94a6-4244b7b67c5f"
MPD URL:  https://manifest-dp.hulustream.com/OMITTED
Decryption command:  mp4decrypt input.mp4 output.mp4 --key OMITTED:OMITTED

Now we have the URL and the keys. First, let's see what formats are available:

$ yt-dlp --allow-unplayable-formats -F "https://manifest-dp.hulustream.com/OMITTED"
WARNING: You have asked for unplayable formats to be listed/downloaded. This is a developer option intended for debugging. 
         If you experience any issues while using this option, DO NOT open a bug report
[generic] xxxxxxxx: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] xxxxxxxx: Downloading webpage
[generic] xxxxxxxx: Extracting information
[info] Available formats for xxxxxxxx:
ID               EXT RESOLUTION |   TBR PROTO | VCODEC        VBR ACODEC     ABR  ASR    MORE INFO
---------------- --- ---------- - ----- ----- - ----------- ----- --------- ---- ------- --------------------------
132545434.add-0  m4a audio only |   68k https |                   mp4a.40.5  68k 48000Hz [en], DASH audio, m4a_dash
132545434.add-1  m4a audio only |   68k https |                   mp4a.40.5  68k 48000Hz [en], DASH audio, m4a_dash
...
132545434.add-11 m4a audio only |   68k https |                   mp4a.40.5  68k 48000Hz [en], DASH audio, m4a_dash
132545134.vdd-0  mp4 512x288    |  460k https | avc1.640015  460k                        DASH video, mp4_dash
132545134.vdd-1  mp4 512x288    |  460k https | avc1.640015  460k                        DASH video, mp4_dash
...
132545134.vdd-11 mp4 512x288    |  460k https | avc1.640015  460k                        DASH video, mp4_dash

Let's get the audio first. We will choose 132545434.add-0, the lowest quality format, for this example. Download it with:

$ yt-dlp --allow-unplayable-formats -f "132545434.add-0" "https://manifest-dp.hulustream.com/OMITTED" -o audio.mp4

Next we will get the video. We will also just take the lowest quality format (132545134.vdd-0) here.

$ yt-dlp --allow-unplayable-formats -f "132545134.vdd-0" "https://manifest-dp.hulustream.com/OMITTED" -o video.mp4

Now we should have two mp4 files, one for the video and one for the audio. We ultimately will merge these, but first we need to decrypt them.

Remember the mp4decrypt command from above? Specifically look at the --key OMITTED:OMITTED part. The decryption key is the same for both the video and the audio. So we can run:

$ mp4decrypt audio.mp4 audio_dec.mp4 --key OMITTED:OMITTED
$ mp4decrypt video.mp4 video_dec.mp4 --key OMITTED:OMITTED

Finally, we can merge the two sources (this command does not do any reencoding):

$ ffmpeg -i video_dec.mp4 -i audio_dec.mp4 -acodec copy -vcodec copy merged.mp4

And now merged.mp4 will be a DRM free mp4 file straight from Hulu! It is possible to automate these steps by writing a simple script.

TODO

  • Subtitles
  • Storing authentication cookie in a text file to avoid having to pass it for every command

Credits

The bulk of the Widevine related code was ported from pywidevine which is a library floating around the Internet of unknown provenance.

Issues
  • Clarify key situation

    Clarify key situation

    I see this [1]:

    Widevine is currently revoking a lot of keys. This program won't work unless you have your own Widevine key and device information (I do not have any working keys).

    but then further down, it seems this project outputs the keys:

    $ HULU_SESSION="abc" hulu download -id="4045ee04-07e8-4c33-94a6-4244b7b67c5f"
    MPD URL:  https://manifest-dp.hulustream.com/OMITTED
    Decryption command:  mp4decrypt input.mp4 output.mp4 --key OMITTED:OMITTED
    

    So do we need our own keys, or not?

    1. https://github.com/chris124567/hulu#notice
    opened by 89z 15
  • Download issue

    Download issue

    Whether movie or tv show, same error. I can use the query command just fine, but not the download command.

    panic: proto: cannot parse invalid wire-format data
    
    goroutine 1 [running]:
    main.main()
            /home/mantiqulla/hulu/main.go:172 +0x1c93
    

    Edit: Using Ubuntu on windows; go version 1.16.12

    Edit2: crap, I don't think I read through the dependencies good enough. looking into that now

    Edit3: nope wasn't a bento problem

    opened by B4U2D0 15
  • panic: Get

    panic: Get "": unsupported protocol scheme "" | panic: proto: cannot parse invalid wire-format data

    When I attempt to run

    HULU_SESSION="blah" hulu download -id="blah"

    I alternately get one of the following two errors:

    • panic: Get "": unsupported protocol scheme ""
    • panic: proto: cannot parse invalid wire-format data

    Which error depends on what movie ID I use.

    EXACT ERROR MESSAGES

    panic: Get "": unsupported protocol scheme ""

    HULU_SESSION="REDACTED" ~/go/bin/hulu download -id="42f7eefe-2448-4ed5-87cb-6233c89c20f6" panic: Get "": unsupported protocol scheme ""

    goroutine 1 [running]: main.main() /home/jake/go/pkg/mod/github.com/chris124567/[email protected]/main.go:131 +0x1d05


    panic: proto: cannot parse invalid wire-format data

    HULU_SESSION="REDACTED" ~/go/bin/hulu download -id="fd311578-bbce-45f4-949f-8d62ebcec582" panic: proto: cannot parse invalid wire-format data

    goroutine 1 [running]: main.main() /home/jake/go/pkg/mod/github.com/chris124567/[email protected]/main.go:172 +0x1c93


    Package version: latest go version: 1.16 hulu is installed in ~/go/bin Bento4 SDK is installed in ~/Bento4 yt-dlp is installed in ~

    opened by azfirefighter 11
  •  download issue

    download issue

    when you do multiple searches kinda close together you get this error

    panic: Get "": unsupported protocol scheme ""
    
    goroutine 1 [running]:
    main.main()
            c:/Users/dir/dir/dir/main.go:115 +0x153b
    

    I believe this might be Hulu limiting the number of people that can watch Hulu at a time as when I tried to watch in a browser it gave me that error.

    opened by NoIDont-Zhara 7
  • Please Help

    Please Help

    I have no idea how to get this started, struggling to download bento4 and YT-DLP or whatever those two things are, and I'm also struggling to understand the rest of the guide as well. A video would be nice to show how to install all of these (Except bento4 and YT-DLP, since those are libraries not made or owned by you).

    opened by UnderseaKnight 6
  • Is this Software still Working?

    Is this Software still Working?

    Thank you for this excellent piece of software. If you don’t mind me asking, I have a few questions.

    1. Does this program still work as of today? I know that Google recently revoked their old Windows keys.
    2. From my understanding, this program works by emulating the functionality of widevinecdm.so rather than hooking the functions of the binary. Is this correct?
    3. Is the 'Android SDK built for x86' to which you refer an AVD on a 64-bit machine?

    Thanks. -WV

    opened by WVkirai 4
  • HULU_SESSION

    HULU_SESSION

    I'm sorry for being an idiot if this is an easy fix but I have not been able to figure out the HULU_SESSION variable on Windows. I saw there was a previous issue that someone opened and you said you don't use Windows so you may not have an answer though.

    Whenever I run the following, HULU_SESSION="xyz" hulu search -query="Test", I get that the HULU_SESSION is not an external or internal command. In the previous issue you mentioned using SET to set the HULU_SESSION variable but when I do that and run just, hulu search -query="Test", I get a message saying "It is necessary to specify the HULU_SESSION environment variable because the Hulu API requires this for all requests."

    Thank you for any help.

    opened by jameshoyt 2
  • privateKey should be []byte

    privateKey should be []byte

    https://github.com/chris124567/hulu/blob/8c095fc96aa9a03dd1eb5c6a6971b7580eede2ea/widevine/cdm.go#L37-L39

    Currently its a string, that gets immediately cast to a []byte. I think it would make sense if the function just called for a byte slice directly. That way, if someone is reading the key from a file device_private_key, its going to be a byte slice as well

    opened by 89z 1
  • Movies no longer work -eabID issue

    Movies no longer work -eabID issue

    Downloading a movie: panic: Get "": unsupported protocol scheme ""

    All good with TV.

    Seems that because Hulu changed something on their end, eabID for movies is no longer obtained correctly for movies, eg

    Response body of PlaybackInformation with movie {"_type":"deep_link_playback","browse":{"target_type":"movie","target_id":"f82b95f5-13da-4acd-b378-7d3f6864919f","target_theme":"hub_theme_entity_detail","params":{},"type":"browse"},"href":"https://discover.hulu.com/content/v5/hubs/movie/f82b95f5-13da-4acd-b378-7d3f6864919f?schema=1","id":"f82b95f5-13da-4acd-b378-7d3f6864919f","href_type":"movie","restriction_level":"not_rated"}

    With TV {"_type":"deep_link_playback","browse":{"target_type":"series","target_id":"227de06a-d3d4-42e0-9df1-bb5495e1738d","target_theme":"hub_theme_entity_detail","params":{},"type":"browse"},"eab_id":"EAB::48ccc382-71f4-49cb-9f37-394311790a9b::151224174::150327995","href":"https://discover.hulu.com/content/v5/hubs/series/227de06a-d3d4-42e0-9df1-bb5495e1738d?schema=1","id":"48ccc382-71f4-49cb-9f37-394311790a9b","href_type":"series","restriction_level":"not_rated"}

    opened by hldr4 1
  • MPD audio/video download

    MPD audio/video download

    Would you be open to contributions that would add the MPD audio/video download part to the repository? To basically remove the yt-dlp dependency?

    Asking because of:

    Technically, this could be implemented rather easily in this repository but I want to keep this repository simple and avoid rewriting code to deal with segment merging or quality selection menus.

    opened by jybp 1
Owner
Christopher Tarry
@SiaFoundation; @umich 2025
Christopher Tarry
A Patreon Image Downloader

patreon-dl: Patreon Image Downloader patreon-dl lets you download images of creators you support. Installation Download a release from https://github.

null 42 Jun 25, 2022
NUS Downloader but in Go

GoNUSD GoNUSD is a tool which can download Title Metadata (TMD), Tickets and Contents (commonly .app files) from the Nintendo Update Server. Documenta

null 4 Apr 13, 2022
🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go./ gnet 是一个高性能、轻量级、非阻塞的事件驱动 Go 网络框架。

English | ???? 中文 ?? Introduction gnet is an event-driven networking framework that is fast and lightweight. It makes direct epoll and kqueue syscalls

Andy Pan 6.6k Jun 30, 2022
An SNMP library written in GoLang.

gosnmp GoSNMP is an SNMP client library fully written in Go. It provides Get, GetNext, GetBulk, Walk, BulkWalk, Set and Traps. It supports IPv4 and IP

Go SNMP 857 Jun 28, 2022
Jazigo is a tool written in Go for retrieving configuration for multiple devices, similar to rancid, fetchconfig, oxidized, Sweet.

Table of Contents About Jazigo Supported Platforms Features Requirements Quick Start - Short version Quick Start - Detailed version Global Settings Im

null 179 Jun 26, 2022
A simple TUN/TAP library written in native Go.

water water is a native Go library for TUN/TAP interfaces. water is designed to be simple and efficient. It wraps almost only syscalls and uses only G

Song Gao 1.5k Jun 22, 2022
An n:m message multiplexer written in Go

What is Gollum? Gollum is an n:m multiplexer that gathers messages from different sources and broadcasts them to a set of destinations. Gollum origina

trivago N.V. 918 Apr 24, 2022
A Socket.IO backend implementation written in Go

go-socket.io The socketio package is a simple abstraction layer for different web browser- supported transport mechanisms. It is fully compatible with

Jukka-Pekka Kekkonen 405 May 24, 2022
A simple low bandwidth simulator written in go

NETSNAIL 0.8 ABOUT Netsnail is a simple network proxy that simulates low bandwidth. RUNNING Usage of netsnail: -d=0: the delay on data transfe

Per Arneng 23 May 19, 2021
A Windows named pipe implementation written in pure Go.

npipe Package npipe provides a pure Go wrapper around Windows named pipes. Windows named pipe documentation: http://msdn.microsoft.com/en-us/library/w

Nate Finch 247 Jun 30, 2022
A TCP throughput measuring tool written in Go language

tcpmeter - a tool for measuring TCP upload and download speeds and RTT latency. Build go build Run start the server on the remote machine: tcpmeter -s

Skip Tavakkolian 39 Apr 17, 2022
Multi-threaded socks proxy checker written in Go!

Soxy - a very fast tool for checking open SOCKS proxies in Golang I was looking for some open socks proxies, and so I needed to test them - but really

pry0cc 45 Jun 13, 2022
The hotwire demo chat written in Golang

Hotwire Go Example This is a recreation of the Hotwire Rails Demo Chat with a Go backend. See the Hotwire docs for more information about Hotwire. Qui

John Turner 86 May 15, 2022
HTTP proxy written in Go. COW can automatically identify blocked sites and use parent proxies to access.

COW (Climb Over the Wall) proxy COW 是一个简化穿墙的 HTTP 代理服务器。它能自动检测被墙网站,仅对这些网站使用二级代理。 English README. 当前版本:0.9.8 CHANGELOG 欢迎在 develop branch 进行开发并发送 pull

Chen Yufei 8.3k Jun 28, 2022
A small TCP proxy written in Go

tcp-proxy A small TCP proxy written in Go This project was intended for debugging text-based protocols. The next version will address binary protocols

Jaime Pillora 627 Jun 28, 2022
Fast, multithreaded, modular and extensible DHCP server written in Go

coredhcp Fast, multithreaded, modular and extensible DHCP server written in Go This is still a work-in-progress Example configuration In CoreDHCP almo

CoreDHCP 745 Jul 3, 2022
A modern, fast and scalable websocket framework with elegant API written in Go

About neffos Neffos is a cross-platform real-time framework with expressive, elegant API written in Go. Neffos takes the pain out of development by ea

Gerasimos (Makis) Maropoulos 387 Jun 29, 2022
High-performance PHP application server, load-balancer and process manager written in Golang

RoadRunner is an open-source (MIT licensed) high-performance PHP application server, load balancer, and process manager. It supports running as a serv

Spiral Scout 6.5k Jun 30, 2022
An URL shortener service written in Golang

ggz An URL shortener service written in Golang. Features Support MySQL, Postgres or SQLite Database. Support RESTful or GraphQL API. Support Auth0 or

null 184 Jun 27, 2022