initial commit

This commit is contained in:
Vomitblood 2024-09-27 19:39:09 +08:00
commit 0c63ab3272
7 changed files with 1593 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*/downloads/
*/target/
*/**/mkbsd-go

11
golang/go.mod Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

10
rust/Cargo.toml Normal file
View 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
View 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(())
}