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

@ -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