package main import ( "encoding/json" "flag" "fmt" "io" "log" "net/http" "os" "strconv" "strings" "sync" ) func writeDataToDisk(dest *string, board string, verlog *bool, post map[string]interface{}, cdnresbody []byte) { // Save the mediadata to file err := os.WriteFile(*dest + "/" + board + "-" + strconv.Itoa(int(post["tim"].(float64))) + post["ext"].(string), cdnresbody, 0664 ) if err != nil { log.Fatal(err) } else if (*verlog) { log.Println("Successfully wrote image/video data to disk") } } func getPostData(post map[string]interface{}, board string, verlog *bool) []byte { // Check if post contains media (Video or Image) if post["ext"] != nil { cdnurlstr := "https://i.4cdn.org/" + board + "/" + strconv.Itoa(int(post["tim"].(float64))) + post["ext"].(string) // Requesting the media from CDN cdnres, err := http.Get(cdnurlstr) if err != nil { log.Fatal(err) } // Check if respons was valid if cdnres.StatusCode > 299 { log.Fatalf("Response failed with status code: %d and\n", cdnres.StatusCode) } else if (*verlog) { log.Println("Got image/video " + strconv.Itoa(int(post["tim"].(float64))) + post["ext"].(string) + " data") } // Read data form respons cdnresbody, err := io.ReadAll(cdnres.Body) cdnres.Body.Close() if err != nil { log.Fatal(err) } else if (*verlog) { log.Println("Successfully got data from responds body") } return cdnresbody } else if (*verlog) { log.Println("Post " + strconv.Itoa(int(post["no"].(float64))) + " didn't include a image or video") } return nil } func main () { // Setting up command flags wdpath, _ := os.Getwd(); url := flag.String("u", "", "The url of the 4chan thread") dest := flag.String("o", wdpath, "Target dir of the conntent") verlog := flag.Bool("v", false, "Set logging to verbose") flag.Parse() // Check if flags are valid if *url == "" { fmt.Println("no thread URL provided") fmt.Println("use the -u= flag to provid URL") os.Exit(1) } // Getting the boardname board := strings.Split(*url, "/")[3] // Get thread info from API res, err := http.Get(*url + ".json") if err != nil { log.Fatal(err) } else { log.Println("Got thread data") } // Check if API response is valid if res.StatusCode > 299 { log.Fatalf("Response failed with status code: %d and\n", res.StatusCode) } else if (*verlog) { log.Println("API response was ok") } //Geting the data from the response resbody, err := io.ReadAll(res.Body) res.Body.Close() if err != nil { log.Fatal(err) } else if (*verlog) { log.Println("Got body of API response") } // Var to save the JSON data var jdata map[string]interface{} //Unmarshaling the API JSON respons if err := json.Unmarshal(resbody, &jdata); err != nil { log.Fatalln(err) } else if (*verlog) { log.Println("Unmarsheled API responsebody") } var wg sync.WaitGroup // Iterating the posts from JSON data for _, v := range jdata["posts"].([]interface{}) { post := v.(map[string]interface{}) wg.Add(1) go func() { defer wg.Done() if postdata := getPostData(post, board, verlog); postdata != nil { writeDataToDisk(dest, board, verlog, post, postdata) } }() } wg.Wait() log.Println("DONE!!!") }