// Package gomdb is a golang implementation of the OMDB API. package gomdb import ( "encoding/json" "errors" "fmt" "net/http" "net/url" ) const ( baseURL = "https://www.omdbapi.com" plot = "full" tomatoes = "true" MovieSearch = "movie" SeriesSearch = "series" EpisodeSearch = "episode" ) type OmdbApi struct { apiKey string } func Init(apiKey string) *OmdbApi { return &OmdbApi{apiKey: apiKey} } // QueryData is the type to create the search query type QueryData struct { Title string Year string ImdbId string SearchType string Season string Episode string } // SearchResult is the type for the search results type SearchResult struct { Title string Year string ImdbID string Type string } // SearchResponse is the struct of the response in a search type SearchResponse struct { Search []SearchResult Response string Error string totalResults int } // Ratings is a combination of all ratings type Ratings struct { Source string Value string } // MovieResult is the result struct of an specific movie search type MovieResult struct { Title string `json:"Title"` Year string `json:"Year"` Rated string `json:"Rated"` Released string `json:"Released"` Runtime string `json:"Runtime"` Genre string `json:"Genre"` Director string `json:"Director"` Writer string `json:"Writer"` Actors string `json:"Actors"` Plot string `json:"Plot"` Language string `json:"Language"` Country string `json:"Country"` Awards string `json:"Awards"` Poster string `json:"Poster"` Ratings []Ratings `json:"Ratings"` Metascore string `json:"Metascore"` ImdbRating string `json:"imdbRating"` ImdbVotes string `json:"imdbVotes"` ImdbID string `json:"imdbID"` Type string `json:"Type"` DVD string `json:"DVD"` BoxOffice string `json:"BoxOffice"` Production string `json:"Production"` Website string `json:"Website"` Response string `json:"Response"` Error string `json:"Error"` } // Search for movies given a Title and year, Year is optional you can pass nil func (api *OmdbApi) Search(query *QueryData) (*SearchResponse, error) { resp, err := api.requestAPI("search", query.Title, query.Year, query.SearchType) if err != nil { return nil, err } defer resp.Body.Close() r := new(SearchResponse) err = json.NewDecoder(resp.Body).Decode(r) if err != nil { return nil, err } if r.Response == "False" { return r, errors.New(r.Error) } return r, nil } // MovieByTitle returns a MovieResult given Title func (api *OmdbApi) MovieByTitle(query *QueryData) (*MovieResult, error) { resp, err := api.requestAPI("title", query.Title, query.Year, query.SearchType, query.Season, query.Episode) if err != nil { return nil, err } defer resp.Body.Close() r := new(MovieResult) err = json.NewDecoder(resp.Body).Decode(r) if err != nil { return nil, err } if r.Response == "False" { return r, errors.New(r.Error) } return r, nil } // MovieByImdbID returns a MovieResult given a ImdbID ex:"tt2015381" func (api *OmdbApi) MovieByImdbID(query *QueryData) (*MovieResult, error) { resp, err := api.requestAPI("id", query.ImdbId, query.Year, query.SearchType, query.Season, query.Episode) if err != nil { return nil, err } defer resp.Body.Close() r := new(MovieResult) err = json.NewDecoder(resp.Body).Decode(r) if err != nil { return nil, err } if r.Response == "False" { return r, errors.New(r.Error) } return r, nil } // helper function to call the API // param: apiCategory refers to which API we are calling. Can be "search", "title" or "id" // Depending on that value, we will search by "t" or "s" or "i" // param: params are the variadic list of params passed for that category func (api *OmdbApi) requestAPI(apiCategory string, params ...string) (resp *http.Response, err error) { var URL *url.URL URL, err = url.Parse(baseURL) if err != nil { return nil, err } // Checking for invalid category if len(params) > 1 && params[2] != "" { if params[2] != MovieSearch && params[2] != SeriesSearch && params[2] != EpisodeSearch { return nil, errors.New("Invalid search category- " + params[2]) } } URL.Path += "/" parameters := url.Values{} parameters.Add("apikey", api.apiKey) switch apiCategory { case "search": parameters.Add("s", params[0]) parameters.Add("y", params[1]) parameters.Add("type", params[2]) case "title": parameters.Add("t", params[0]) parameters.Add("y", params[1]) parameters.Add("type", params[2]) parameters.Add("Season", params[3]) parameters.Add("Episode", params[4]) parameters.Add("plot", plot) parameters.Add("tomatoes", tomatoes) case "id": parameters.Add("i", params[0]) parameters.Add("type", params[2]) parameters.Add("Season", params[3]) parameters.Add("Episode", params[4]) parameters.Add("plot", plot) parameters.Add("tomatoes", tomatoes) } URL.RawQuery = parameters.Encode() res, err := http.Get(URL.String()) err = checkErr(res.StatusCode) if err != nil { return nil, err } return res, nil } func checkErr(status int) error { if status != 200 { return fmt.Errorf("Status Code %d received from OMDb", status) } return nil } // Stringer Interface for MovieResult func (mr MovieResult) String() string { return fmt.Sprintf("#%s: %s (%s)", mr.ImdbID, mr.Title, mr.Year) } // Stringer Interface for SearchResult func (sr SearchResult) String() string { return fmt.Sprintf("#%s: %s (%s) Type: %s", sr.ImdbID, sr.Title, sr.Year, sr.Type) }