Fix typos & add more comments

This commit is contained in:
bytedream 2022-04-16 00:17:36 +02:00
parent 2e9ce3cf52
commit 3617955bc5
16 changed files with 82 additions and 68 deletions

View file

@ -73,7 +73,7 @@ _Before reading_: Because of the huge functionality not all cases can be covered
### Login
Before you can do something, you have to login first.
Before you can do something, you have to log in first.
This can be performed via crunchyroll account email and password.

View file

@ -728,7 +728,7 @@ func (tc *tarCompress) Close() error {
err = tc.dst.Close()
if err != nil && err2 != nil {
// best way to show double errors at once that i've found
// best way to show double errors at once that I've found
return fmt.Errorf("%v\n%v", err, err2)
} else if err == nil && err2 != nil {
err = err2
@ -750,11 +750,11 @@ func (tc *tarCompress) NewFile(information formatInformation) (io.WriteCloser, e
ModTime: time.Now(),
Mode: 0644,
Typeflag: tar.TypeReg,
// fun fact: i did not set the size for quiet some time because i thought that it isn't
// required. well because of this i debugged this part for multiple hours because without
// fun fact: I did not set the size for quiet some time because I thought that it isn't
// required. well because of this I debugged this part for multiple hours because without
// proper size information only a tiny amount gets copied into the tar (or zip) writer.
// this is also the reason why the file content is completely copied into a buffer before
// writing it to the writer. i could bypass this and save some memory but this requires
// writing it to the writer. I could bypass this and save some memory but this requires
// some rewriting and im nearly at the (planned) finish for version 2 so nah in the future
// maybe
Size: int64(buf.Len()),

View file

@ -403,7 +403,7 @@ func (dp *downloadProgress) update(msg string, permanent bool) {
pre := fmt.Sprintf("%s%s [", dp.Prefix, msg)
post := fmt.Sprintf("]%4d%% %8d/%d", int(percentage), dp.Current, dp.Total)
// i don't really know why +2 is needed here but without it the Printf below would not print to the line end
// I don't really know why +2 is needed here but without it the Printf below would not print to the line end
progressWidth := terminalWidth() - len(pre) - len(post) + 2
repeatCount := int(percentage / float32(100) * float32(progressWidth))
// it can be lower than zero when the terminal is very tiny

View file

@ -13,7 +13,7 @@ import (
"strconv"
)
// LOCALE represents a locale / language
// LOCALE represents a locale / language.
type LOCALE string
const (
@ -31,16 +31,16 @@ const (
)
type Crunchyroll struct {
// Client is the http.Client to perform all requests over
// Client is the http.Client to perform all requests over.
Client *http.Client
// Context can be used to stop requests with Client and is context.Background by default
// Context can be used to stop requests with Client and is context.Background by default.
Context context.Context
// Locale specifies in which language all results should be returned / requested
// Locale specifies in which language all results should be returned / requested.
Locale LOCALE
// SessionID is the crunchyroll session id which was used for authentication
// SessionID is the crunchyroll session id which was used for authentication.
SessionID string
// Config stores parameters which are needed by some api calls
// Config stores parameters which are needed by some api calls.
Config struct {
TokenType string
AccessToken string
@ -56,11 +56,11 @@ type Crunchyroll struct {
MaturityRating string
}
// If cache is true, internal caching is enabled
// If cache is true, internal caching is enabled.
cache bool
}
// LoginWithCredentials logs in via crunchyroll username or email and password
// LoginWithCredentials logs in via crunchyroll username or email and password.
func LoginWithCredentials(user string, password string, locale LOCALE, client *http.Client) (*Crunchyroll, error) {
sessionIDEndpoint := fmt.Sprintf("https://api.crunchyroll.com/start_session.0.json?version=1.0&access_token=%s&device_type=%s&device_id=%s",
"LNDJgOit5yaRIWN", "com.crunchyroll.windows.desktop", "Az2srGnChW65fuxYz2Xxl1GcZQgtGgI")
@ -87,7 +87,7 @@ func LoginWithCredentials(user string, password string, locale LOCALE, client *h
}
// LoginWithSessionID logs in via a crunchyroll session id.
// Session ids are automatically generated as a cookie when visiting https://www.crunchyroll.com
// Session ids are automatically generated as a cookie when visiting https://www.crunchyroll.com.
func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (*Crunchyroll, error) {
crunchy := &Crunchyroll{
Client: client,
@ -205,7 +205,7 @@ func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (*
return crunchy, nil
}
// request is a base function which handles api requests
// request is a base function which handles api requests.
func (c *Crunchyroll) request(endpoint string) (*http.Response, error) {
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil {
@ -241,7 +241,7 @@ func (c *Crunchyroll) request(endpoint string) (*http.Response, error) {
}
// IsCaching returns if data gets cached or not.
// See SetCaching for more information
// See SetCaching for more information.
func (c *Crunchyroll) IsCaching() bool {
return c.cache
}
@ -249,12 +249,12 @@ func (c *Crunchyroll) IsCaching() bool {
// SetCaching enables or disables internal caching of requests made.
// Caching is enabled by default.
// If it is disabled the already cached data still gets called.
// The best way to prevent this is to create a complete new Crunchyroll struct
// The best way to prevent this is to create a complete new Crunchyroll struct.
func (c *Crunchyroll) SetCaching(caching bool) {
c.cache = caching
}
// Search searches a query and returns all found series and movies within the given limit
// Search searches a query and returns all found series and movies within the given limit.
func (c *Crunchyroll) Search(query string, limit uint) (s []*Series, m []*Movie, err error) {
searchEndpoint := fmt.Sprintf("https://beta-api.crunchyroll.com/content/v1/search?q=%s&n=%d&type=&locale=%s",
query, limit, c.Locale)
@ -397,7 +397,7 @@ func ParseEpisodeURL(url string) (seriesName, title string, episodeNumber int, w
return
}
// ParseBetaSeriesURL tries to extract the season id of the given crunchyroll beta url, pointing to a season
// ParseBetaSeriesURL tries to extract the season id of the given crunchyroll beta url, pointing to a season.
func ParseBetaSeriesURL(url string) (seasonId string, ok bool) {
pattern := regexp.MustCompile(`(?m)^https?://(www\.)?beta\.crunchyroll\.com/(\w{2}/)?series/(?P<seasonId>\w+).*`)
if urlMatch := pattern.FindAllStringSubmatch(url, -1); len(urlMatch) != 0 {
@ -408,7 +408,7 @@ func ParseBetaSeriesURL(url string) (seasonId string, ok bool) {
return
}
// ParseBetaEpisodeURL tries to extract the episode id of the given crunchyroll beta url, pointing to an episode
// ParseBetaEpisodeURL tries to extract the episode id of the given crunchyroll beta url, pointing to an episode.
func ParseBetaEpisodeURL(url string) (episodeId string, ok bool) {
pattern := regexp.MustCompile(`(?m)^https?://(www\.)?beta\.crunchyroll\.com/(\w{2}/)?watch/(?P<episodeId>\w+).*`)
if urlMatch := pattern.FindAllStringSubmatch(url, -1); len(urlMatch) != 0 {

View file

@ -20,7 +20,7 @@ import (
)
// NewDownloader creates a downloader with default settings which should
// fit the most needs
// fit the most needs.
func NewDownloader(context context.Context, writer io.Writer, goroutines int, onSegmentDownload func(segment *m3u8.MediaSegment, current, total int, file *os.File) error) Downloader {
tmp, _ := os.MkdirTemp("", "crunchy_")
@ -43,12 +43,12 @@ type Downloader struct {
// The files will be placed directly into the root of the directory.
// If empty a random temporary directory on the system's default tempdir
// will be created.
// If the directory does not exist, it will be created
// If the directory does not exist, it will be created.
TempDir string
// If DeleteTempAfter is true, the temp directory gets deleted afterwards.
// Note that in case of a hard signal exit (os.Interrupt, ...) the directory
// will NOT be deleted. In such situations try to catch the signal and
// cancel Context
// cancel Context.
DeleteTempAfter bool
// Context to control the download process with.
@ -56,7 +56,7 @@ type Downloader struct {
// process. So it is not recommend stopping the program immediately after calling
// the cancel function. It's better when canceling it and then exit the program
// when Format.Download throws an error. See the signal handling section in
// cmd/crunchyroll-go/cmd/download.go for an example
// cmd/crunchyroll-go/cmd/download.go for an example.
Context context.Context
// Goroutines is the number of goroutines to download segments with
@ -65,11 +65,11 @@ type Downloader struct {
// A method to call when a segment was downloaded.
// Note that the segments are downloaded asynchronously (depending on the count of
// Goroutines) and the function gets called asynchronously too, so for example it is
// first called on segment 1, then segment 254, then segment 3 and so on
// first called on segment 1, then segment 254, then segment 3 and so on.
OnSegmentDownload func(segment *m3u8.MediaSegment, current, total int, file *os.File) error
// If LockOnSegmentDownload is true, only one OnSegmentDownload function can be called at
// once. Normally (because of the use of goroutines while downloading) multiple could get
// called simultaneously
// called simultaneously.
LockOnSegmentDownload bool
// If FFmpegOpts is not nil, ffmpeg will be used to merge and convert files.
@ -82,7 +82,7 @@ type Downloader struct {
FFmpegOpts []string
}
// download's the given format
// download's the given format.
func (d Downloader) download(format *Format) error {
if err := format.InitVideo(); err != nil {
return err
@ -109,7 +109,7 @@ func (d Downloader) download(format *Format) error {
}
// mergeSegments reads every file in tempDir and writes their content to Downloader.Writer.
// The given output file gets created or overwritten if already existing
// The given output file gets created or overwritten if already existing.
func (d Downloader) mergeSegments(files []string) error {
for _, file := range files {
select {
@ -132,7 +132,7 @@ func (d Downloader) mergeSegments(files []string) error {
// mergeSegmentsFFmpeg reads every file in tempDir and merges their content to the outputFile
// with ffmpeg (https://ffmpeg.org/).
// The given output file gets created or overwritten if already existing
// The given output file gets created or overwritten if already existing.
func (d Downloader) mergeSegmentsFFmpeg(files []string) error {
list, err := os.Create(filepath.Join(d.TempDir, "list.txt"))
if err != nil {
@ -214,13 +214,13 @@ func (d Downloader) mergeSegmentsFFmpeg(files []string) error {
// After every segment download onSegmentDownload will be called with:
// the downloaded segment, the current position, the total size of segments to download,
// the file where the segment content was written to an error (if occurred).
// The filename is always <number of downloaded segment>.ts
// The filename is always <number of downloaded segment>.ts.
//
// Short explanation:
// The actual crunchyroll video is split up in multiple segments (or video files) which
// have to be downloaded and merged after to generate a single video file.
// And this function just downloads each of this segment into the given directory.
// See https://en.wikipedia.org/wiki/MPEG_transport_stream for more information
// See https://en.wikipedia.org/wiki/MPEG_transport_stream for more information.
func (d Downloader) downloadSegments(format *Format) ([]string, error) {
if err := format.InitVideo(); err != nil {
return nil, err
@ -318,7 +318,7 @@ func (d Downloader) downloadSegments(format *Format) ([]string, error) {
}
}
// getCrypt extracts the key and iv of a m3u8 segment and converts it into a cipher.Block and an iv byte sequence
// getCrypt extracts the key and iv of a m3u8 segment and converts it into a cipher.Block and an iv byte sequence.
func getCrypt(format *Format, segment *m3u8.MediaSegment) (block cipher.Block, iv []byte, err error) {
var resp *http.Response
@ -341,7 +341,7 @@ func getCrypt(format *Format, segment *m3u8.MediaSegment) (block cipher.Block, i
return block, iv, nil
}
// downloadSegment downloads a segment, decrypts it and names it after the given index
// downloadSegment downloads a segment, decrypts it and names it after the given index.
func (d Downloader) downloadSegment(format *Format, segment *m3u8.MediaSegment, filename string, block cipher.Block, iv []byte) (*os.File, error) {
// every segment is aes-128 encrypted and has to be decrypted when downloaded
content, err := d.decryptSegment(format.crunchy.Client, segment, block, iv)
@ -361,7 +361,7 @@ func (d Downloader) downloadSegment(format *Format, segment *m3u8.MediaSegment,
return file, nil
}
// https://github.com/oopsguy/m3u8/blob/4150e93ec8f4f8718875a02973f5d792648ecb97/tool/crypt.go#L25
// https://github.com/oopsguy/m3u8/blob/4150e93ec8f4f8718875a02973f5d792648ecb97/tool/crypt.go#L25.
func (d Downloader) decryptSegment(client *http.Client, segment *m3u8.MediaSegment, block cipher.Block, iv []byte) ([]byte, error) {
req, err := http.NewRequestWithContext(d.Context, http.MethodGet, segment.URI, nil)
if err != nil {
@ -387,7 +387,7 @@ func (d Downloader) decryptSegment(client *http.Client, segment *m3u8.MediaSegme
return raw, nil
}
// https://github.com/oopsguy/m3u8/blob/4150e93ec8f4f8718875a02973f5d792648ecb97/tool/crypt.go#L47
// https://github.com/oopsguy/m3u8/blob/4150e93ec8f4f8718875a02973f5d792648ecb97/tool/crypt.go#L47.
func (d Downloader) pkcs5UnPadding(origData []byte) []byte {
length := len(origData)
unPadding := int(origData[length-1])

View file

@ -9,6 +9,7 @@ import (
"time"
)
// Episode contains all information about an episode.
type Episode struct {
crunchy *Crunchyroll
@ -74,7 +75,7 @@ type Episode struct {
StreamID string
}
// EpisodeFromID returns an episode by its api id
// EpisodeFromID returns an episode by its api id.
func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) {
resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/episodes/%s?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
crunchy.Config.CountryCode,
@ -111,7 +112,8 @@ func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) {
// AudioLocale returns the audio locale of the episode.
// Every episode in a season (should) have the same audio locale,
// so if you want to get the audio locale of a season, just call this method on the first episode of the season
// so if you want to get the audio locale of a season, just call
// this method on the first episode of the season.
func (e *Episode) AudioLocale() (LOCALE, error) {
streams, err := e.Streams()
if err != nil {
@ -120,7 +122,7 @@ func (e *Episode) AudioLocale() (LOCALE, error) {
return streams[0].AudioLocale, nil
}
// GetFormat returns the format which matches the given resolution and subtitle locale
// GetFormat returns the format which matches the given resolution and subtitle locale.
func (e *Episode) GetFormat(resolution string, subtitle LOCALE, hardsub bool) (*Format, error) {
streams, err := e.Streams()
if err != nil {
@ -186,7 +188,7 @@ func (e *Episode) GetFormat(resolution string, subtitle LOCALE, hardsub bool) (*
return nil, fmt.Errorf("no matching resolution found")
}
// Streams returns all streams which are available for the episode
// Streams returns all streams which are available for the episode.
func (e *Episode) Streams() ([]*Stream, error) {
if e.children != nil {
return e.children, nil

View file

@ -3,7 +3,7 @@ package crunchyroll
import "fmt"
// AccessError is an error which will be returned when some special sort of api request fails.
// See Crunchyroll.request when the error gets used
// See Crunchyroll.request when the error gets used.
type AccessError struct {
error

View file

@ -11,11 +11,12 @@ const (
MOVIE = "movies"
)
// Format contains detailed information about an episode video stream.
type Format struct {
crunchy *Crunchyroll
ID string
// FormatType represents if the format parent is an episode or a movie
// FormatType represents if the format parent is an episode or a movie.
FormatType FormatType
Video *m3u8.Variant
AudioLocale LOCALE
@ -27,7 +28,7 @@ type Format struct {
// The Format.Video.Chunklist pointer is, by default, nil because an additional
// request must be made to receive its content. The request is not made when
// initializing a Format struct because it would probably cause an intense overhead
// since Format.Video.Chunklist is only used sometimes
// since Format.Video.Chunklist is only used sometimes.
func (f *Format) InitVideo() error {
if f.Video.Chunklist == nil {
resp, err := f.crunchy.Client.Get(f.Video.URI)
@ -45,7 +46,7 @@ func (f *Format) InitVideo() error {
return nil
}
// Download downloads the Format with the via Downloader specified options
// Download downloads the Format with the via Downloader specified options.
func (f *Format) Download(downloader Downloader) error {
return downloader.download(f)
}

View file

@ -5,6 +5,8 @@ import (
"fmt"
)
// MovieListing contains information about something which is called
// movie listing. I don't know what this means thb.
type MovieListing struct {
crunchy *Crunchyroll
@ -36,7 +38,7 @@ type MovieListing struct {
AvailabilityNotes string `json:"availability_notes"`
}
// MovieListingFromID returns a movie listing by its api id
// MovieListingFromID returns a movie listing by its api id.
func MovieListingFromID(crunchy *Crunchyroll, id string) (*MovieListing, error) {
resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/movie_listing/%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
crunchy.Config.CountryCode,
@ -65,7 +67,7 @@ func MovieListingFromID(crunchy *Crunchyroll, id string) (*MovieListing, error)
return movieListing, nil
}
// AudioLocale is same as Episode.AudioLocale
// AudioLocale is same as Episode.AudioLocale.
func (ml *MovieListing) AudioLocale() (LOCALE, error) {
resp, err := ml.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
ml.crunchy.Config.CountryCode,
@ -86,7 +88,7 @@ func (ml *MovieListing) AudioLocale() (LOCALE, error) {
return LOCALE(jsonBody["audio_locale"].(string)), nil
}
// Streams returns all streams which are available for the movie listing
// Streams returns all streams which are available for the movie listing.
func (ml *MovieListing) Streams() ([]*Stream, error) {
return fromVideoStreams(ml.crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
ml.crunchy.Config.CountryCode,

View file

@ -6,6 +6,7 @@ import (
"regexp"
)
// Season contains information about an anime season.
type Season struct {
crunchy *Crunchyroll
@ -41,7 +42,7 @@ type Season struct {
SubtitleLocales []LOCALE
}
// SeasonFromID returns a season by its api id
// SeasonFromID returns a season by its api id.
func SeasonFromID(crunchy *Crunchyroll, id string) (*Season, error) {
resp, err := crunchy.Client.Get(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/seasons/%s?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
crunchy.Config.CountryCode,
@ -70,6 +71,7 @@ func SeasonFromID(crunchy *Crunchyroll, id string) (*Season, error) {
return season, nil
}
// AudioLocale returns the audio locale of the season.
func (s *Season) AudioLocale() (LOCALE, error) {
episodes, err := s.Episodes()
if err != nil {
@ -78,7 +80,7 @@ func (s *Season) AudioLocale() (LOCALE, error) {
return episodes[0].AudioLocale()
}
// Episodes returns all episodes which are available for the season
// Episodes returns all episodes which are available for the season.
func (s *Season) Episodes() (episodes []*Episode, err error) {
if s.children != nil {
return s.children, nil

View file

@ -8,6 +8,7 @@ import (
"regexp"
)
// Stream contains information about all available video stream of an episode.
type Stream struct {
crunchy *Crunchyroll
@ -22,7 +23,7 @@ type Stream struct {
streamURL string
}
// StreamsFromID returns a stream by its api id
// StreamsFromID returns a stream by its api id.
func StreamsFromID(crunchy *Crunchyroll, id string) ([]*Stream, error) {
return fromVideoStreams(crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
crunchy.Config.CountryCode,
@ -35,7 +36,7 @@ func StreamsFromID(crunchy *Crunchyroll, id string) ([]*Stream, error) {
crunchy.Config.KeyPairID))
}
// Formats returns all formats which are available for the stream
// Formats returns all formats which are available for the stream.
func (s *Stream) Formats() ([]*Format, error) {
if s.children != nil {
return s.children, nil
@ -70,7 +71,7 @@ func (s *Stream) Formats() ([]*Format, error) {
return formats, nil
}
// fromVideoStreams returns all streams which are accessible via the endpoint
// fromVideoStreams returns all streams which are accessible via the endpoint.
func fromVideoStreams(crunchy *Crunchyroll, endpoint string) (streams []*Stream, err error) {
resp, err := crunchy.request(endpoint)
if err != nil {

View file

@ -5,6 +5,7 @@ import (
"net/http"
)
// Subtitle contains the information about a video subtitle.
type Subtitle struct {
crunchy *Crunchyroll
@ -13,6 +14,7 @@ type Subtitle struct {
Format string `json:"format"`
}
// Save writes the subtitle to the given io.Writer.
func (s Subtitle) Save(writer io.Writer) error {
req, err := http.NewRequestWithContext(s.crunchy.Context, http.MethodGet, s.URL, nil)
if err != nil {

4
url.go
View file

@ -5,7 +5,7 @@ import (
)
// ExtractEpisodesFromUrl extracts all episodes from an url.
// If audio is not empty, the episodes gets filtered after the given locale
// If audio is not empty, the episodes gets filtered after the given locale.
func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Episode, error) {
series, episodes, err := c.ParseUrl(url)
if err != nil {
@ -78,7 +78,7 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep
}
// ParseUrl parses the given url into a series or episode.
// The returning episode is a slice because non-beta urls have the same episode with different languages
// The returning episode is a slice because non-beta urls have the same episode with different languages.
func (c *Crunchyroll) ParseUrl(url string) (*Series, []*Episode, error) {
if seriesId, ok := ParseBetaSeriesURL(url); ok {
series, err := SeriesFromID(c, seriesId)

View file

@ -4,6 +4,7 @@ import (
"github.com/ByteDream/crunchyroll-go"
)
// AllLocales is an array of all available locales.
var AllLocales = []crunchyroll.LOCALE{
crunchyroll.JP,
crunchyroll.US,
@ -18,7 +19,7 @@ var AllLocales = []crunchyroll.LOCALE{
crunchyroll.AR,
}
// ValidateLocale validates if the given locale actually exist
// ValidateLocale validates if the given locale actually exist.
func ValidateLocale(locale crunchyroll.LOCALE) bool {
for _, l := range AllLocales {
if l == locale {
@ -28,7 +29,7 @@ func ValidateLocale(locale crunchyroll.LOCALE) bool {
return false
}
// LocaleLanguage returns the country by its locale
// LocaleLanguage returns the country by its locale.
func LocaleLanguage(locale crunchyroll.LOCALE) string {
switch locale {
case crunchyroll.JP:

View file

@ -9,7 +9,7 @@ import (
)
// SortEpisodesBySeason sorts the given episodes by their seasons.
// Note that the same episodes just with different audio locales will cause problems
// Note that the same episodes just with different audio locales will cause problems.
func SortEpisodesBySeason(episodes []*crunchyroll.Episode) [][]*crunchyroll.Episode {
sortMap := map[string]map[int][]*crunchyroll.Episode{}
@ -43,7 +43,7 @@ func SortEpisodesBySeason(episodes []*crunchyroll.Episode) [][]*crunchyroll.Epis
return eps
}
// SortEpisodesByAudio sort the given episodes by their audio locale
// SortEpisodesByAudio sort the given episodes by their audio locale.
func SortEpisodesByAudio(episodes []*crunchyroll.Episode) (map[crunchyroll.LOCALE][]*crunchyroll.Episode, error) {
eps := map[crunchyroll.LOCALE][]*crunchyroll.Episode{}
@ -81,7 +81,7 @@ func SortEpisodesByAudio(episodes []*crunchyroll.Episode) (map[crunchyroll.LOCAL
return eps, nil
}
// MovieListingsByDuration sorts movie listings by their duration
// MovieListingsByDuration sorts movie listings by their duration.
type MovieListingsByDuration []*crunchyroll.MovieListing
func (mlbd MovieListingsByDuration) Len() int {
@ -94,7 +94,7 @@ func (mlbd MovieListingsByDuration) Less(i, j int) bool {
return mlbd[i].DurationMS < mlbd[j].DurationMS
}
// EpisodesByDuration sorts episodes by their duration
// EpisodesByDuration sorts episodes by their duration.
type EpisodesByDuration []*crunchyroll.Episode
func (ebd EpisodesByDuration) Len() int {
@ -107,6 +107,7 @@ func (ebd EpisodesByDuration) Less(i, j int) bool {
return ebd[i].DurationMS < ebd[j].DurationMS
}
// EpisodesByNumber sorts episodes after their episode number.
type EpisodesByNumber []*crunchyroll.Episode
func (ebn EpisodesByNumber) Len() int {
@ -119,7 +120,7 @@ func (ebn EpisodesByNumber) Less(i, j int) bool {
return ebn[i].EpisodeNumber < ebn[j].EpisodeNumber
}
// FormatsByResolution sorts formats after their resolution
// FormatsByResolution sorts formats after their resolution.
type FormatsByResolution []*crunchyroll.Format
func (fbr FormatsByResolution) Len() int {
@ -140,6 +141,7 @@ func (fbr FormatsByResolution) Less(i, j int) bool {
return iResX+iResY < jResX+jResY
}
// SubtitlesByLocale sorts subtitles after their locale.
type SubtitlesByLocale []*crunchyroll.Subtitle
func (sbl SubtitlesByLocale) Len() int {

View file

@ -30,8 +30,10 @@ type video struct {
} `json:"images"`
}
// Video is the base for Movie and Season.
type Video interface{}
// Movie contains information about a movie.
type Movie struct {
video
Video
@ -40,7 +42,7 @@ type Movie struct {
children []*MovieListing
// not generated when calling MovieFromID
// not generated when calling MovieFromID.
MovieListingMetadata struct {
AvailabilityNotes string `json:"availability_notes"`
AvailableOffline bool `json:"available_offline"`
@ -65,7 +67,7 @@ type Movie struct {
}
}
// MovieFromID returns a movie by its api id
// MovieFromID returns a movie by its api id.
func MovieFromID(crunchy *Crunchyroll, id string) (*Movie, error) {
resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/movies/%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
crunchy.Config.CountryCode,
@ -95,8 +97,6 @@ func MovieFromID(crunchy *Crunchyroll, id string) (*Movie, error) {
}
// MovieListing returns all videos corresponding with the movie.
// Beside the normal movie, sometimes movie previews are returned too, but you can try to get the actual movie
// by sorting the returning MovieListing slice with the utils.MovieListingByDuration interface
func (m *Movie) MovieListing() (movieListings []*MovieListing, err error) {
if m.children != nil {
return m.children, nil
@ -134,6 +134,7 @@ func (m *Movie) MovieListing() (movieListings []*MovieListing, err error) {
return movieListings, nil
}
// Series contains information about an anime series.
type Series struct {
video
Video
@ -156,13 +157,13 @@ type Series struct {
MatureRatings []string `json:"mature_ratings"`
SeasonCount int `json:"season_count"`
// not generated when calling SeriesFromID
// not generated when calling SeriesFromID.
SearchMetadata struct {
Score float64 `json:"score"`
}
}
// SeriesFromID returns a series by its api id
// SeriesFromID returns a series by its api id.
func SeriesFromID(crunchy *Crunchyroll, id string) (*Series, error) {
resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/movies?movie_listing_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s",
crunchy.Config.CountryCode,
@ -191,7 +192,7 @@ func SeriesFromID(crunchy *Crunchyroll, id string) (*Series, error) {
return series, nil
}
// Seasons returns all seasons of a series
// Seasons returns all seasons of a series.
func (s *Series) Seasons() (seasons []*Season, err error) {
if s.children != nil {
return s.children, nil