package compareID import ( "bytes" "encoding/json" "fmt" "github.com/agext/levenshtein" "io/ioutil" "net/http" "strings" "time" ) //Anime contains the essentials for being able to compare anidb to meikan type Anime struct { ID int Type string Episodes int Title string StartDate time.Time EndDate time.Time } //Result is the way the json received is structured //It contains a collection of animes made up of AnimeRes type Result struct { Total int `json:"total"` Anime []AnimeRes `json:"data"` } //AnimeRes is how the search results from meikan are built up in the JSON type AnimeRes struct { EndDate string `json:"end_date"` Episodes int `json:"episodes"` ID int `json:"id"` Rating string `json:"rating"` StartDate string `json:"start_date"` State string `json:"state"` Title string `json:"title"` Type string `json:"type"` } //Animes is a collection of animes type Animes []MatchedAnime //MatchedAnime contains all the neccessary information after matching two animes type MatchedAnime struct { MeikanTitle string AnidbTitle string MeikanID int AnidbID int Score int } //check does a default error check func check(e error) { if e != nil { panic(e) } } //lessType makes types when comparing less specific //So missmatches like OVA == Specials don't happen. func LessType(Type string) string { switch Type { case "TV", "ONA": return "Shows" case "OVA", "Specials": return "Extra" } return Type } //fixType changes the types anidb uses to ones that match with meikan specifications. func FixType(Type string) string { switch Type { case "TV Series": return "TV" case "TV Special": return "Special" case "Web": return "ONA" } return Type } //fixMeikanDates takes two strings and tries to parse them returning two Time structs func FixMeikanDates(Date, Date2 string) (date, date2 time.Time) { if Date != "" { date, _ = time.Parse(`2006-01-02`, Date) } if Date2 != "" { date2, _ = time.Parse(`2006-01-02`, Date) } return date, date2 } //subAnimeDates subtracts one date from the other. //Returning an integer that is added onto the total score when comparing. func SubAnimeDates(Date, Date2 time.Time) (total int) { if Date.Year() == 1 && Date2.Year() == 1 { return 0 } diff := Date.Sub(Date2) if diff < 0 { diff = diff * -1 } if diff <= 2190*time.Hour { return 50 } return -150 } //checkResults takes an Anime struct and a channel. //It compares the supplied anime to all the search results returned from searching the title. //If the score is over 100 it should be a match. func CheckResults(anime Anime, c chan MatchedAnime) { fmt.Println(`Searching anime`, anime.ID) var highest, hI int var err error anime.Title = strings.Replace(strings.Replace(anime.Title, `\`, `\\`, -1), `"`, `\"`, -1) var search = bytes.NewBuffer([]byte(`{"title":"` + anime.Title + `", "show_r18": true}`)) resp, err := http.Post("https://api.meikan.moe/v1/anime?incl=start_date,end_date", "application/json", search) check(err) fmt.Println(`Completed meikan search for`, anime.ID) // nolint: errcheck defer resp.Body.Close() var result Result body, err := ioutil.ReadAll(resp.Body) check(err) _ = json.NewDecoder(bytes.NewReader(body)).Decode(&result) anime.Type = FixType(anime.Type) if anime.Episodes < 1 { anime.Episodes = 1 } for i := 0; i < len(result.Anime); i++ { var total int meikan := result.Anime[i] stDate, enDate := FixMeikanDates(meikan.StartDate, meikan.EndDate) if meikan.Type == anime.Type { total += 25 } else { MType := LessType(meikan.Type) AType := LessType(anime.Type) if MType == AType { total += 12 } } score := levenshtein.Match(meikan.Title, anime.Title, nil) if score > 0.95 { total += 50 } if meikan.Episodes == anime.Episodes { total += 25 } total += SubAnimeDates(stDate, anime.StartDate) if anime.Episodes != 1 { total += SubAnimeDates(enDate, anime.EndDate) } if total > highest { highest = total hI = i } } //return highest, hI if len(result.Anime) != 0 { c <- MatchedAnime{ MeikanTitle: result.Anime[hI].Title, AnidbTitle: anime.Title, MeikanID: result.Anime[hI].ID, AnidbID: anime.ID, Score: highest, } } } func main() { http.DefaultTransport = &http.Transport{MaxIdleConnsPerHost: 50, MaxIdleConns: 50} }