initial commit
This commit is contained in:
		
						commit
						0c63ab3272
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
*/downloads/
 | 
			
		||||
*/target/
 | 
			
		||||
*/**/mkbsd-go
 | 
			
		||||
							
								
								
									
										11
									
								
								golang/go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								golang/go.mod
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
module git.vomitblood.com/Vomitblood/mkbsd/golang
 | 
			
		||||
 | 
			
		||||
go 1.23.1
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
 | 
			
		||||
	github.com/rivo/uniseg v0.4.7 // indirect
 | 
			
		||||
	github.com/schollz/progressbar/v3 v3.16.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.25.0 // indirect
 | 
			
		||||
	golang.org/x/term v0.24.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										10
									
								
								golang/go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								golang/go.sum
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
 | 
			
		||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
 | 
			
		||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 | 
			
		||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
			
		||||
github.com/schollz/progressbar/v3 v3.16.0 h1:+MbBim/cE9DqDb8UXRfLJ6RZdyDkXG1BDy/sWc5s0Mc=
 | 
			
		||||
github.com/schollz/progressbar/v3 v3.16.0/go.mod h1:lLiKjKJ9/yzc9Q8jk+sVLfxWxgXKsktvUf6TO+4Y2nw=
 | 
			
		||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
 | 
			
		||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
			
		||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
 | 
			
		||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
 | 
			
		||||
							
								
								
									
										152
									
								
								golang/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								golang/main.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,152 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/schollz/progressbar/v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const url = "https://storage.googleapis.com/panels-api/data/20240916/media-1a-i-p~s"
 | 
			
		||||
 | 
			
		||||
// data struct to get only dhd stuff
 | 
			
		||||
type Data struct {
 | 
			
		||||
	Data map[string]struct {
 | 
			
		||||
		Dhd string `json:"dhd"`
 | 
			
		||||
	} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	response, error := fetchData(url)
 | 
			
		||||
	if error != nil {
 | 
			
		||||
		fmt.Printf("Failed to fetch data: %v\n", error)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	jsonData, error := parseData(response)
 | 
			
		||||
	if error != nil {
 | 
			
		||||
		fmt.Printf("Failed to parse data: %v\n", error)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get the total number of images to download
 | 
			
		||||
	imageCount := countImages(jsonData)
 | 
			
		||||
	fmt.Printf("Total images to download: %d\n", imageCount)
 | 
			
		||||
 | 
			
		||||
	// ensure that the downloads directory exists in the same directory as the executable
 | 
			
		||||
	downloadDir := "downloads"
 | 
			
		||||
 | 
			
		||||
	if err := createDirectory(downloadDir); err != nil {
 | 
			
		||||
		fmt.Printf("Failed to create directory: %v\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bar := progressbar.Default(int64(imageCount))
 | 
			
		||||
	fileIndex := 1
 | 
			
		||||
 | 
			
		||||
	// iterate over the data with dhd prop and download each image
 | 
			
		||||
	for _, subproperty := range jsonData.Data {
 | 
			
		||||
		if subproperty.Dhd != "" {
 | 
			
		||||
			imageUrl := subproperty.Dhd
 | 
			
		||||
 | 
			
		||||
			// download the image and save it
 | 
			
		||||
			err := downloadImage(imageUrl, downloadDir, fileIndex)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Printf("Error downloading image: %v\n", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			bar.Add(1)
 | 
			
		||||
			fileIndex++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("%d/%d images downloaded successfully\n", fileIndex, imageCount)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fetchData(url string) (*http.Response, error) {
 | 
			
		||||
	response, error := http.Get(url)
 | 
			
		||||
 | 
			
		||||
	if error != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to fetch JSON file: %v", error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if response.StatusCode != http.StatusOK {
 | 
			
		||||
		return nil, fmt.Errorf("failed to fetch JSON file: %s", response.Status)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseData(response *http.Response) (Data, error) {
 | 
			
		||||
	var jsonData Data
 | 
			
		||||
	error := json.NewDecoder(response.Body).Decode(&jsonData)
 | 
			
		||||
 | 
			
		||||
	if error != nil {
 | 
			
		||||
		return Data{}, fmt.Errorf("failed to parse JSON: %v", error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return jsonData, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// countImages returns the total number of images to download
 | 
			
		||||
func countImages(jsonData Data) int {
 | 
			
		||||
	count := 0
 | 
			
		||||
	for _, subproperty := range jsonData.Data {
 | 
			
		||||
		if subproperty.Dhd != "" {
 | 
			
		||||
			count++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return count
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createDirectory(dir string) error {
 | 
			
		||||
	if _, error := os.Stat(dir); os.IsNotExist(error) {
 | 
			
		||||
		error = os.Mkdir(dir, os.ModePerm)
 | 
			
		||||
		if error != nil {
 | 
			
		||||
			return error
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("Created directory: %s\n", dir)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// downloadImage downloads an image from a url and saves it to the download directory
 | 
			
		||||
func downloadImage(imageUrl, downloadDir string, fileIndex int) error {
 | 
			
		||||
	// fetch the image
 | 
			
		||||
	response, error := http.Get(imageUrl)
 | 
			
		||||
	if error != nil {
 | 
			
		||||
		return fmt.Errorf("failed to download image: %v", error)
 | 
			
		||||
	}
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if response.StatusCode != http.StatusOK {
 | 
			
		||||
		return fmt.Errorf("failed to download image: %s", response.Status)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove all query parameters from the image url
 | 
			
		||||
	imageUrlWithoutParams := strings.Split(imageUrl, "?")[0]
 | 
			
		||||
 | 
			
		||||
	// create the image file
 | 
			
		||||
	fileExtension := filepath.Ext(imageUrlWithoutParams)
 | 
			
		||||
	filePath := filepath.Join(downloadDir, fmt.Sprintf("%d%s", fileIndex, fileExtension))
 | 
			
		||||
	file, error := os.Create(filePath)
 | 
			
		||||
	if error != nil {
 | 
			
		||||
		return fmt.Errorf("failed to create file: %v", error)
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	// write the image data to file
 | 
			
		||||
	_, error = io.Copy(file, response.Body)
 | 
			
		||||
	if error != nil {
 | 
			
		||||
		return fmt.Errorf("failed to save image: %v", error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1302
									
								
								rust/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1302
									
								
								rust/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										10
									
								
								rust/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								rust/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "mkbsd-rs"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
reqwest = { version = "0.12.7", features = ["blocking", "json"] }
 | 
			
		||||
serde = { version = "1.0.210", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.128"
 | 
			
		||||
indicatif = "0.17.8"
 | 
			
		||||
							
								
								
									
										105
									
								
								rust/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								rust/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,105 @@
 | 
			
		|||
const URL: &str = "https://storage.googleapis.com/panels-api/data/20240916/media-1a-i-p~s";
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
struct Data {
 | 
			
		||||
    data: std::collections::HashMap<String, SubProperty>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
struct SubProperty {
 | 
			
		||||
    dhd: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    println!("Fetching data from: {URL}");
 | 
			
		||||
    let response = fetch_data(URL)?;
 | 
			
		||||
    let json_data: Data = parse_data(&response)?;
 | 
			
		||||
 | 
			
		||||
    let image_count = count_images(&json_data);
 | 
			
		||||
    println!("Total images to download: {}", image_count);
 | 
			
		||||
 | 
			
		||||
    let download_dir = "downloads";
 | 
			
		||||
    create_directory(download_dir)?;
 | 
			
		||||
 | 
			
		||||
    // create the progress bar
 | 
			
		||||
    let bar = indicatif::ProgressBar::new(image_count as u64);
 | 
			
		||||
    bar.set_style(
 | 
			
		||||
        indicatif::ProgressStyle::with_template(
 | 
			
		||||
            "{spinner} [{pos}/{len}] {wide_bar} {percent_precise}% ({eta_precise})",
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .progress_chars("##-"),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::blocking::Client::new();
 | 
			
		||||
    let mut file_index = 1;
 | 
			
		||||
    let mut success_count = 0;
 | 
			
		||||
 | 
			
		||||
    for subproperty in json_data.data.values() {
 | 
			
		||||
        if let Some(image_url) = &subproperty.dhd {
 | 
			
		||||
            match download_image(&client, image_url, download_dir, file_index) {
 | 
			
		||||
                Ok(_) => {
 | 
			
		||||
                    bar.inc(1);
 | 
			
		||||
                    file_index += 1;
 | 
			
		||||
                    success_count += 1;
 | 
			
		||||
                }
 | 
			
		||||
                Err(e) => eprintln!("Error downloading image: {}", e),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        bar.tick();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bar.finish();
 | 
			
		||||
    println!(
 | 
			
		||||
        "{}/{} images downloaded successfully",
 | 
			
		||||
        success_count, image_count
 | 
			
		||||
    );
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn fetch_data(url: &str) -> Result<String, reqwest::Error> {
 | 
			
		||||
    reqwest::blocking::get(url)?.text()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_data(response: &str) -> Result<Data, serde_json::Error> {
 | 
			
		||||
    serde_json::from_str(response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn count_images(json_data: &Data) -> usize {
 | 
			
		||||
    json_data
 | 
			
		||||
        .data
 | 
			
		||||
        .values()
 | 
			
		||||
        .filter(|subprop| subprop.dhd.is_some())
 | 
			
		||||
        .count()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn create_directory(dir: &str) -> std::io::Result<()> {
 | 
			
		||||
    std::fs::create_dir_all(dir)?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn download_image(
 | 
			
		||||
    client: &reqwest::blocking::Client,
 | 
			
		||||
    image_url: &str,
 | 
			
		||||
    download_dir: &str,
 | 
			
		||||
    file_index: usize,
 | 
			
		||||
) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let response = client.get(image_url).send()?;
 | 
			
		||||
    if !response.status().is_success() {
 | 
			
		||||
        return Err(format!("Failed to download image: {}", response.status()).into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let image_url_without_params = image_url.split('?').next().unwrap_or(image_url);
 | 
			
		||||
    let file_extension = std::path::Path::new(image_url_without_params)
 | 
			
		||||
        .extension()
 | 
			
		||||
        .and_then(|ext| ext.to_str())
 | 
			
		||||
        .map(|ext| format!(".{}", ext))
 | 
			
		||||
        .unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
    let file_path = format!("{}/{}{}", download_dir, file_index, file_extension);
 | 
			
		||||
 | 
			
		||||
    let mut file = std::fs::File::create(&file_path)?;
 | 
			
		||||
    std::io::Write::write_all(&mut file, &response.bytes()?)?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in a new issue