func (a *addictedProxy) GetShowSubtitle(reqEpisode *polochon.ShowEpisode, log *logrus.Entry) (polochon.Subtitle, error) { // TODO: add year // TODO: handle release shows, err := a.client.GetTvShows() if err != nil { return nil, err } var guessID string guessDist := 1000 for showName, showID := range shows { dist := levenshtein.Distance(strings.ToLower(showName), strings.ToLower(reqEpisode.ShowTitle)) if dist < guessDist { guessDist = dist guessID = showID } } subtitles, err := a.client.GetSubtitles(guessID, reqEpisode.Season, reqEpisode.Episode) if err != nil { return nil, err } filteredSubs := subtitles.FilterByLang(a.language) if len(filteredSubs) == 0 { return nil, polochon.ErrNoSubtitleFound } sort.Sort(addicted.ByDownloads(filteredSubs)) if reqEpisode.ReleaseGroup == "" { log.Info("No release group specified get the most downloaded subtitle") return &filteredSubs[0], err } subDist := 1000 var subtitle polochon.Subtitle var release string for _, sub := range filteredSubs { dist := levenshtein.Distance(strings.ToLower(reqEpisode.ReleaseGroup), strings.ToLower(sub.Release)) if dist < subDist { subDist = dist subtitle = &sub release = sub.Release } } log.Info("Subtitle chosen ", release, " whit distance ", subDist) return subtitle, err }
func main() { s1 := "kitten" s2 := "sitting" fmt.Printf("The distance between %v and %v is %v\n", s1, s2, levenshtein.Distance(s1, s2)) // -> The distance between kitten and sitting is 3 }
// getShowByName helps find a show on tvdb using its name func getShowByName(s *polochon.Show) error { if s.Title == "" { return ErrMissingShowTitle } // Add the year to the search if defined query := s.Title if s.Year != 0 { query = fmt.Sprintf("%s (%d)", query, s.Year) } // Search on tvdb by name list, err := tvdbGetSeries(query) if err != nil { return err } // Any result ? if len(list.Series) == 0 { return ErrShowNotFound } // Find the most accurate serie base on the levenshtein distance var show *tvdb.Series minDistance := 100 for _, tvdbSerie := range list.Series { d := levenshtein.Distance(query, tvdbSerie.SeriesName) if d < minDistance { minDistance = d show = tvdbSerie } } return updateShow(s, show) }
func calcScore(a, b string) float64 { score := levenshtein.Distance(a, b) lena := len(a) lenb := len(b) var bigger float64 if lena > lenb { bigger = float64(lena) } else { bigger = float64(lenb) } return (bigger - float64(score)) / bigger }
// SearchByTitle searches a movie by its title. It adds the tmdb id into the // movie struct so it can get details later func (t *TmDB) searchByTitle(m *polochon.Movie, log *logrus.Entry) error { // No title, no search if m.Title == "" { return ErrNoMovieTitle } // ID already found if m.TmdbID != 0 { return nil } // Add year option if given options := map[string]string{} if m.Year != 0 { options["year"] = fmt.Sprintf("%d", m.Year) } // Search on tmdb r, err := tmdbSearchMovie(t.client, m.Title, options) if err != nil { return err } // Check if there is any results if len(r.Results) == 0 { log.Debugf("Failed to find movie from imdb title %q", m.Title) return ErrNoMovieFound } // Find the most accurate serie based on the levenshtein distance var movieShort tmdb.MovieShort minDistance := 100 for _, result := range r.Results { d := levenshtein.Distance(m.Title, result.Title) if d < minDistance { minDistance = d movieShort = result } } m.TmdbID = movieShort.ID log.Debugf("Found movie from title %q", m.Title) return nil }
/** * Generate two random indices and a random number. * If the levenshtein distance between the names found at * index1 and index2 is the same as the random number, * one of index1 and index2 will be chosen at random to * be our winner. */ func raffle(names []string, winnerChannel chan string) { defer wg.Done() var index1, index2, n, d int var winner string for { index1 = rand.Int() % len(names) index2 = rand.Int() % len(names) n = 3 + rand.Int()%100 d = levenshtein.Distance(names[index1], names[index2]) if d == n { if rand.Int()%2 == 1 { winner = names[index1] } else { winner = names[index2] } winnerChannel <- winner return } } }
func getDistance(s1 string, s2 string) int { s1 = strings.ToLower(s1) s2 = strings.ToLower(s2) // dist := hamming.Calc(s1, s2) // if dist > 0 { // return dist // } else { dist := levenshtein.Distance(s1, s2) if Normalize { minWord := len(s1) if len(s2) < minWord { minWord = len(s2) } if dist > minWord { dist = (dist-minWord)/2 + minWord } lcsDist := LCS(s1, s2) dist = dist + -1*lcsDist } return dist }
// Guess helps find the best guess func (g *Guesser) Guess() (*Guess, error) { // If nothing was found on imdb and guessit we're done if g.Guessit == nil && len(g.OpenSubtitle) == 0 { return nil, fmt.Errorf("openguessit: guessit and opensub failed to guess this file") } // If only guessit was found if len(g.OpenSubtitle) == 0 { return &Guess{Guessit: g.Guessit}, nil } // Rank the datas provided by OpenSubtitle and guessit guesses := []*Guess{} for _, sub := range g.OpenSubtitle { tmpGuess := &Guess{OpenSubtitle: sub} if g.Guessit != nil { if g.Guessit.Type == sub.Type { tmpGuess.Guessit = g.Guessit tmpGuess.Ranking += 10 } // Movie if g.Guessit.Type == MovieType { d := levenshtein.Distance(sub.MovieName, g.Guessit.MovieName) // d == 0 means exact same name if d == 0 { d = 1 } tmpGuess.Ranking += (1 / float64(d)) * 100 } // Show if g.Guessit.Type == ShowType { if sub.Episode == g.Guessit.Episode && sub.Season == g.Guessit.Season { tmpGuess.Ranking += 100 } d := levenshtein.Distance(sub.ShowName, g.Guessit.ShowName) if d == 0 { d = 1 } tmpGuess.Ranking += (1 / float64(d)) * 100 } // Year if sub.Year == g.Guessit.Year { tmpGuess.Ranking += 10 } } // LevDist if sub.LevDist == 0 { sub.LevDist = 1 } tmpGuess.Ranking += (1 / float64(sub.LevDist)) * 100 guesses = append(guesses, tmpGuess) } var bestGuess *Guess for _, guess := range guesses { // First value if bestGuess == nil { bestGuess = guess continue } if guess.Ranking > bestGuess.Ranking { bestGuess = guess } } return bestGuess, nil }
// UpdateFromOpenSubtitle updates the guess with the OpenSubtitle informations func (g *Guesser) UpdateFromOpenSubtitle() error { // Base path of the filename basePath := filepath.Base(g.FilePath) // OpenSubtitle client client, err := osdb.NewClient() if err != nil { return err } // Log in if err := client.LogIn("", "", "eng"); err != nil { return err } // Set the languages languages := []string{"eng"} // Hash movie file, and search subtitles, err := client.FileSearch(g.FilePath, languages) if err != nil { return err } // If nothing found, search by filename if len(subtitles) == 0 { client, err := osdb.NewClient() if err != nil { return err } err = client.LogIn("", "", "eng") if err != nil { return err } params := []interface{}{ client.Token, []map[string]string{ { "query": basePath, "sublanguageid": "en", }, }, } subtitles, err = client.SearchSubtitles(¶ms) if err != nil { return err } } // No subtitles found if len(subtitles) == 0 { return nil } for _, sub := range subtitles { switch sub.MovieKind { case MovieType: g.OpenSubtitle = append(g.OpenSubtitle, &OpenSubtitle{ ImdbID: fmt.Sprintf("tt%07s", sub.IDMovieImdb), Type: MovieType, MovieName: sub.MovieFileName, Year: sub.MovieYear, LevDist: levenshtein.Distance(sub.SubFileName, basePath), }) case ShowType: // The MovieFileName field returned by openSubtitles contains the // show name and the episode name. // e.g. `"Show Title" Show episode title` // Only the title is relevant showName := sub.MovieFileName s := strings.Split(sub.MovieFileName, `"`) if len(s) >= 2 { showName = s[1] } g.OpenSubtitle = append(g.OpenSubtitle, &OpenSubtitle{ ImdbID: fmt.Sprintf("tt%07s", sub.SeriesIMDBParent), Type: ShowType, ShowName: showName, Season: sub.SeriesSeason, Episode: sub.SeriesEpisode, Year: sub.MovieYear, LevDist: levenshtein.Distance(sub.SubFileName, basePath), }) default: return fmt.Errorf("Invalid movie kind: %q", sub.MovieKind) } } return nil }