insta-import-pixelfed/src/main.rs
2025-01-17 15:37:19 +01:00

104 lines
3.1 KiB
Rust

use std::fs;
use std::path::Path;
use serde::{Deserialize, Serialize};
use reqwest::blocking::Client;
use std::error::Error;
#[derive(Debug, Deserialize)]
struct Config {
pixelfed_url: String,
access_token: String,
visibility: String, // Should be "unlisted"
default_text: String,
}
fn load_config() -> Result<Config, Box<dyn Error>> {
let config_str = fs::read_to_string("config.json")?;
let config: Config = serde_json::from_str(&config_str)?;
Ok(config)
}
fn get_jpeg_files(directory: &str) -> Vec<String> {
let mut images = Vec::new();
if let Ok(entries) = fs::read_dir(directory) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext.eq_ignore_ascii_case("jpg") || ext.eq_ignore_ascii_case("jpeg") {
images.push(path.to_string_lossy().to_string());
}
}
}
}
images
}
fn format_post_text(template: &str, batch_num: usize, total_batches: usize, title: &str) -> String {
template
.replace("@batch@", &format!("Batch {} out of {}", batch_num, total_batches))
.replace("@title@", title)
}
fn upload_images_batch(client: &Client, config: &Config, images: &[String], batch_num: usize, total_batches: usize, title: &str) -> Result<(), Box<dyn Error>> {
let url = format!("{}/api/v1/media", config.pixelfed_url);
let mut media_ids = Vec::new();
for image_path in images {
let form = reqwest::blocking::multipart::Form::new()
.file("file", image_path)?;
let res = client.post(&url)
.bearer_auth(&config.access_token)
.multipart(form)
.send()?;
let json: serde_json::Value = res.json()?;
if let Some(id) = json["id"].as_str() {
media_ids.push(id.to_string());
}
}
if !media_ids.is_empty() {
let post_url = format!("{}/api/v1/statuses", config.pixelfed_url);
let post_text = format_post_text(&config.default_text, batch_num, total_batches, title);
let body = serde_json::json!({
"status": post_text,
"media_ids": media_ids,
"visibility": config.visibility,
});
client.post(&post_url)
.bearer_auth(&config.access_token)
.json(&body)
.send()?;
}
Ok(())
}
fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <directory> [--title <title>]", args[0]);
std::process::exit(1);
}
let mut title = "".to_string();
if let Some(index) = args.iter().position(|x| x == "--title") {
if index + 1 < args.len() {
title = args[index + 1].clone();
}
}
let config = load_config()?;
let images = get_jpeg_files(&args[1]);
let client = Client::new();
let total_batches = (images.len() + 19) / 20;
for (i, chunk) in images.chunks(20).enumerate() {
upload_images_batch(&client, &config, chunk, i + 1, total_batches, &title)?;
}
println!("All images uploaded successfully.");
Ok(())
}