package main import ( "bufio" "encoding/csv" "encoding/json" "fmt" "net/http" "net/url" "os" "strings" "time" ) type Movie struct { Adult bool Backdrop_path string Genre_ids []int Id int Original_language string Original_title string Overview string Popularity float64 Poster_path string Release_date string Title string Video bool Vote_average float64 Vote_count int } type Response struct { Page int Results []Movie Total_pages int Total_results int } func main() { fmt.Println("Enter the input CSV file name:") inputFileName := getInput() fmt.Println("Enter the TMDB API Key:") apiKey := getInput() // Open output CSV file outputFileName := "movies.csv" outputFile, err := os.Create(outputFileName) if err != nil { fmt.Printf("Error creating output file: %v\n", err) return } defer outputFile.Close() writer := csv.NewWriter(outputFile) writer.UseCRLF = true writer.Comma = ',' defer writer.Flush() processFile(inputFileName, apiKey, writer) // Check failed.csv checkFailedCsv(apiKey, writer) } func getInput() string { scanner := bufio.NewScanner(os.Stdin) scanner.Scan() return scanner.Text() } func processFile(inputFileName, apiKey string, writer *csv.Writer) { // Open the file file, err := os.Open(inputFileName) if err != nil { fmt.Printf("Error opening file: %v\n", err) return } defer file.Close() // Open failed.csv failedFileName := "failed.csv" failedFile, err := os.Create(failedFileName) if err != nil { fmt.Printf("Error creating failed.csv: %v\n", err) return } defer failedFile.Close() failedWriter := csv.NewWriter(failedFile) failedWriter.UseCRLF = true failedWriter.Comma = ',' defer failedWriter.Flush() scanner := bufio.NewScanner(file) requests := time.Tick(time.Millisecond * 20) // Limit requests to 50 per second for scanner.Scan() { <-requests // Wait until we are allowed to perform a request movie := scanner.Text() movie = strings.Trim(movie, "\"") // Remove surrounding quotes title, year, err := parseMovieTitleYear(movie) if err != nil { fmt.Printf("Failed to parse movie: %s, Error: %v\n", movie, err) _ = failedWriter.Write([]string{title, year}) failedWriter.Flush() // Immediately write the failed record to the file continue } fmt.Printf("Searching for movie: %s\n", movie) popularity, err := getMovieData(title, year, apiKey) if err != nil { fmt.Printf("Failed to get data for movie: %s, Error: %v\n", movie, err) _ = failedWriter.Write([]string{title, year}) failedWriter.Flush() // Immediately write the failed record to the file continue } record := []string{title, year, fmt.Sprintf("%f", popularity)} _ = writer.Write(record) fmt.Printf("Movie: %s, Popularity: %f\n", movie, popularity) } } func parseMovieTitleYear(movie string) (string, string, error) { parts := strings.SplitN(movie, ",", 2) if len(parts) < 2 { return "", "", fmt.Errorf("invalid movie format: %s", movie) } title := parts[0] year := parts[1] return title, year, nil } func getMovieData(title, year, apiKey string) (float64, error) { // url encode the title encodedTitle := url.QueryEscape(title) url := fmt.Sprintf("https://api.themoviedb.org/3/search/movie?include_adult=false&language=en-US&year=%s&page=1&query=%s", year, encodedTitle) req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer "+apiKey) req.Header.Add("accept", "application/json") res, err := http.DefaultClient.Do(req) if err != nil || res == nil { return 0, fmt.Errorf("failed to make request: %v", err) } if res.StatusCode != 200 { return 0, fmt.Errorf("received non-200 response: %d", res.StatusCode) } defer res.Body.Close() var response Response if err := json.NewDecoder(res.Body).Decode(&response); err != nil { return 0, fmt.Errorf("failed to decode response: %v", err) } if len(response.Results) == 0 { return 0, fmt.Errorf("no results found") } return response.Results[0].Popularity, nil } func checkFailedCsv(apiKey string, writer *csv.Writer) { for { fmt.Println("Do you want to check failed.csv and retry fetching data for failed movies? (Y/n)") retry := getInput() if strings.EqualFold(retry, "n") { break } if retry == "" || strings.EqualFold(retry, "y") { fmt.Println("Processing failed.csv...") // Recreate failed.csv for the next run failedFileName := "failed.csv" failedFile, err := os.Create(failedFileName) if err != nil { fmt.Printf("Error creating failed.csv: %v\n", err) return } failedFile.Close() // Process the old failed.csv processFile(failedFileName, apiKey, writer) } } }