step1_get_challenge.go step2_login.go step3_get_auth_code.go step4_get_token.go unext.go
func GeneratePKCE() string
func GetEpisodeCodes(titleCode string) ([]string, error)
GetEpisodeCodes fetches all episode codes (ED...) for a given title code (SID...) using the Mad_AllEpisodes operation.
func Step2Login(email, password, challengeID string) (string, error)
Step2Login authenticates with email/password and returns the post_auth_endpoint.
func Step3GetAuthCode(postAuthEndpoint string, auth *AuthState) (string, error)
Step3GetAuthCode sends the code_challenge to the post_auth_endpoint and extracts the authorization code from the 302 redirect Location header.
func Step6GetLicense(licenseURL *url.URL, playToken string, challenge []byte) ([]byte, error)
Step6GetLicense POSTs a Widevine license challenge to the U-NEXT license proxy and returns the raw license response bytes. challenge is the binary SignedMessage (protobuf) produced by a Widevine CDM. The play_token must match the one used to fetch the MPD.
AudioTrack describes an audio track language.
type AudioTrack struct { Lang string `json:"lang"` IsNative bool `json:"isNative"` }
AuthState holds the challenge_id and PKCE verifier produced by Step1.
type AuthState struct { ChallengeID string CodeVerifier string }
func Step1GetChallenge() (*AuthState, error)
Step1GetChallenge performs the initial GET to /oauth2/auth and extracts the challenge_id from the 302 redirect Location header.
DownloadTitleMeta is metadata about the downloaded title.
type DownloadTitleMeta struct { TitleInKatakana string `json:"titleInKatakana"` Keywords []string `json:"keywords"` }
EpisodesResponse is the JSON envelope for the Mad_AllEpisodes query. Only webfront_title_titleEpisodes is decoded; extra fields are ignored.
type EpisodesResponse struct { Data struct { WebfrontTitleStage struct { TitleName string `json:"titleName"` } `json:"webfront_title_stage"` WebfrontTitleTitleEpisodes struct { Episodes []struct { ID string `json:"id"` } `json:"episodes"` } `json:"webfront_title_titleEpisodes"` } `json:"data"` Errors []GraphQLError `json:"errors"` }
GraphQLError represents a GraphQL error.
type GraphQLError struct { Message string `json:"message"` }
LicenseUrl describes a DRM license endpoint.
type LicenseUrl struct { Type string `json:"type"` LicenseUrl string `json:"licenseUrl"` }
LoginResponse is the JSON returned by /oauth2/login.
type LoginResponse struct { PostAuthEndpoint string `json:"post_auth_endpoint"` }
MovieAudio describes the audio codec type.
type MovieAudio struct { AudioType string `json:"audioType"` }
MoviePartsPosition describes a part of the movie (e.g. ENDING).
type MoviePartsPosition struct { Type string `json:"type"` FromSeconds float64 `json:"fromSeconds"` EndSeconds float64 `json:"endSeconds"` HasRemainingPart bool `json:"hasRemainingPart"` }
MovieProfile describes a streaming profile (DASH, HLS, SMOOTH, etc.).
type MovieProfile struct { CdnId string `json:"cdnId"` Type string `json:"type"` PlaylistUrl string `json:"playlistUrl"` MovieAudioList []MovieAudio `json:"movieAudioList"` LicenseUrlList []LicenseUrl `json:"licenseUrlList"` AudioTrackList []AudioTrack `json:"audioTrackList"` }
PlaylistResponse is the JSON envelope returned by the GraphQL endpoint.
type PlaylistResponse struct { Data struct { WebfrontPlaylistUrl *PlaylistUrl `json:"webfront_playlistUrl"` } `json:"data"` Errors []GraphQLError `json:"errors"` }
PlaylistResult contains error info from the playlist request.
type PlaylistResult struct { ErrorCode string `json:"errorCode"` ErrorMessage string `json:"errorMessage"` }
PlaylistUrl maps to the PlayList fragment on the PlaylistUrl type.
type PlaylistUrl struct { SubTitle string `json:"subTitle"` PlayToken string `json:"playToken"` PlayTokenHash string `json:"playTokenHash"` BeaconSpan int `json:"beaconSpan"` ResultStatus int `json:"resultStatus"` LicenseExpireDate string `json:"licenseExpireDate"` IsKids bool `json:"isKids"` DownloadTitleMeta *DownloadTitleMeta `json:"downloadTitleMeta"` UrlInfo []UrlInfo `json:"urlInfo"` Result *PlaylistResult `json:"result"` }
func Step5GetPlaylist(accessToken, code, playMode string) (*PlaylistUrl, error)
Step5GetPlaylist fetches the playlist using the access token obtained in step 4. playMode must be either "dub" or "caption".
func (*PlaylistUrl) CachePath() string
func (p *PlaylistUrl) MPDURL() (*url.URL, error)
MPDURL searches the playlist for the first DASH movie profile and returns its playlistUrl as a *url.URL with the play_token query parameter appended. Returns an error if no DASH profile is found or the URL cannot be parsed.
func (p *PlaylistUrl) WidevineLicenseURL() (*url.URL, error)
WidevineLicenseURL searches the playlist for the first DASH movie profile with a WIDEVINE license URL and returns it (without query parameters). Returns an error if no such profile is found.
SceneSearchList contains IMS (image search) URLs.
type SceneSearchList struct { IMS_AD1 string `json:"ims_ad1"` IMS_L string `json:"ims_l"` IMS_M string `json:"ims_m"` IMS_S string `json:"ims_s"` }
TokenResponse is the JSON returned by /oauth2/token.
type TokenResponse struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` Scope string `json:"scope"` TokenType string `json:"token_type"` }
func Step4GetToken(authCode string, auth *AuthState) (*TokenResponse, error)
Step4GetToken exchanges the authorization code for access and refresh tokens.
func (*TokenResponse) CachePath() string
func (t *TokenResponse) Refresh() error
Refresh exchanges the receiver's RefreshToken for a new set of tokens and writes the result back into the receiver.
UrlInfo represents one entry in the urlInfo array.
type UrlInfo struct { Code string `json:"code"` StartPoint float64 `json:"startPoint"` EndPoint float64 `json:"endPoint"` ResumePoint float64 `json:"resumePoint"` EndrollStartPosition float64 `json:"endrollStartPosition"` CommodityCode string `json:"commodityCode"` SaleTypeCode string `json:"saleTypeCode"` CaptionFlg bool `json:"captionFlg"` DubFlg bool `json:"dubFlg"` SceneSearchList *SceneSearchList `json:"sceneSearchList"` MovieProfile []MovieProfile `json:"movieProfile"` MoviePartsPositionList []MoviePartsPosition `json:"moviePartsPositionList"` }