From 4c620e9b846589918b2e6c270e735d3063978676 Mon Sep 17 00:00:00 2001 From: deadnews Date: Wed, 24 Jun 2026 17:57:38 +0700 Subject: [PATCH 1/6] fix(deps): update deps --- .github/workflows/deps.yml | 2 +- .github/workflows/main.yml | 20 ++-- .pre-commit-config.yaml | 2 +- Cargo.lock | 229 ++++++------------------------------- Cargo.toml | 2 +- 5 files changed, 46 insertions(+), 209 deletions(-) diff --git a/.github/workflows/deps.yml b/.github/workflows/deps.yml index bed35f2b..e64e679a 100644 --- a/.github/workflows/deps.yml +++ b/.github/workflows/deps.yml @@ -11,7 +11,7 @@ jobs: name: Check deps runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check deps uses: embarkstudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9a24f835..bd183387 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: name: Prek runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Run prek uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4 @@ -24,7 +24,7 @@ jobs: name: Lint code runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Rust Cache uses: swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 @@ -42,12 +42,12 @@ jobs: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Rust Cache uses: swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - uses: taiki-e/install-action@15449e3094499af05d8d964a1c884208e4b8b595 # v2.81.11 + - uses: taiki-e/install-action@ace6ebe54a6a0c86dfb5f7764b17f793b6925bc3 # v2.82.3 with: tool: nextest,cargo-llvm-cov @@ -70,10 +70,10 @@ jobs: permissions: id-token: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Authenticate to crates.io - uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4 + uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 id: auth - name: Set version in Cargo.toml @@ -103,7 +103,7 @@ jobs: - { os: windows-latest, target: x86_64 } runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set version shell: bash @@ -153,7 +153,7 @@ jobs: PRERELEASE: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Create GitHub Release run: | @@ -184,7 +184,7 @@ jobs: - { os: windows-latest, target: aarch64-pc-windows-msvc } runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set version shell: bash @@ -205,7 +205,7 @@ jobs: url: https://aur.archlinux.org/packages/${{ github.event.repository.name }}-bin runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Update version in PKGBUILD run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b65b99a3..040c548f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: - id: actionlint - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: 9257c6050c0261b8c57e712f632dc4a8010109a9 # frozen: v1.25.2 + rev: e3eebf65325ccc992422292cb7a4baee967cf815 # frozen: v1.26.1 hooks: - id: zizmor args: [--fix=safe] diff --git a/Cargo.lock b/Cargo.lock index 734657cb..9e438515 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -326,15 +326,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "cc" -version = "1.2.64" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", @@ -892,15 +892,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] @@ -1165,12 +1163,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -1252,8 +1244,6 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", - "serde", - "serde_core", ] [[package]] @@ -1350,12 +1340,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" version = "0.2.186" @@ -1400,9 +1384,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "lru-slab" @@ -1412,9 +1396,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "mac-notification-sys" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04fd0110fd05744c3c904acf1a8ca624a721d75a35638f127cd5fb6b7ccfb1bf" +checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca" dependencies = [ "cc", "log", @@ -1497,9 +1481,9 @@ dependencies = [ [[package]] name = "notify-rust" -version = "4.17.0" +version = "4.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ff2e74231b72c832d82982193b417f230945be6bdb5575b251d941d31adb00" +checksum = "c5b4c1b4f2aa9f25f63a7a49d3dd0ed567b3670da15330a66b29434be899b891" dependencies = [ "futures-lite", "log", @@ -1782,16 +1766,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -1858,9 +1832,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -1878,9 +1852,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" dependencies = [ "aws-lc-rs", "bytes", @@ -1914,9 +1888,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -2096,9 +2070,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ "aws-lc-rs", "once_cell", @@ -2389,9 +2363,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -2437,7 +2411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2488,9 +2462,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.49" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", "num-conv", @@ -2508,9 +2482,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", @@ -2754,12 +2728,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" @@ -2796,7 +2764,7 @@ version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -2839,16 +2807,7 @@ version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -2906,40 +2865,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "wayland-backend" version = "0.3.15" @@ -2967,9 +2892,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.12" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ "bitflags", "wayland-backend", @@ -3032,9 +2957,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] @@ -3368,100 +3293,12 @@ dependencies = [ "url", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "wl-clipboard-rs" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 892f56fe..2d10fa59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ image = { version = "0.25", default-features = false, features = ["png", "jpeg # System integration arboard = { version = "3.6", default-features = false, features = ["wayland-data-control"] } -notify-rust = { version = "4.17" } +notify-rust = { version = "4.18" } # Error handling & logging anyhow = { version = "1.0" } From 2401f49abeb478fe3d25152b8ab3032b73281cdf Mon Sep 17 00:00:00 2001 From: deadnews Date: Wed, 24 Jun 2026 17:57:46 +0700 Subject: [PATCH 2/6] fix(imgbox): derive upload filename extension from image format --- src/upload/imgbox.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/upload/imgbox.rs b/src/upload/imgbox.rs index 5b159e9a..0c68594f 100644 --- a/src/upload/imgbox.rs +++ b/src/upload/imgbox.rs @@ -5,6 +5,7 @@ use serde::Deserialize; use tracing::debug; use super::{parse_json, response_text}; +use crate::image::detect_format; use crate::{TIMEOUT, USER_AGENT}; pub const API_URL: &str = "https://imgbox.com"; @@ -32,6 +33,9 @@ struct UploadResponse { /// No API key required. Uses a CSRF token + session cookie flow. /// A local cookie-aware client maintains the session across the three steps. pub async fn upload(_client: &Client, data: Vec, url: &str) -> Result { + let ext = detect_format(&data)?; + let filename = format!("image.{}", ext.extensions_str()[0]); + let client = reqwest::Client::builder() .user_agent(USER_AGENT) .cookie_store(true) @@ -53,7 +57,7 @@ pub async fn upload(_client: &Client, data: Vec, url: &str) -> Result Date: Wed, 24 Jun 2026 17:58:37 +0700 Subject: [PATCH 3/6] feat(imglink): add hosting --- README.md | 2 ++ src/upload/imglink.rs | 72 +++++++++++++++++++++++++++++++++++++++++++ src/upload/mod.rs | 6 ++++ 3 files changed, 80 insertions(+) create mode 100644 src/upload/imglink.rs diff --git a/README.md b/README.md index e81ddc1a..ca87ca59 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ yay -S imgup-bin | [imghippo](https://imghippo.com/) | + | `https://i.imghippo.com/files/{id}.png` | | [imgbox](https://imgbox.com/) | - | `https://images2.imgbox.com/52/8c/{id}_o.png` | | [imgchest](https://imgchest.com/) | + | `https://cdn.imgchest.com/files/{id}.png` | +| [imglink](https://imglink.cc/) | + | `https://imglink.cc/cdn/{id}.png` | | [imgur](https://imgur.com/) | - | `https://i.imgur.com/{id}.png` | | [lensdump](https://lensdump.com/) | + | `https://i.lensdump.com/i/{id}.png` | | [pixeldrain](https://pixeldrain.com/) | + | `https://pixeldrain.com/api/file/{id}` | @@ -88,6 +89,7 @@ IMAGEKIT_PRIVATE_KEY= IMGBB_KEY= IMGHIPPO_KEY= IMGCHEST_KEY= +IMGLINK_KEY= IMGUR_CLIENT_ID= LENSDUMP_KEY= PIXELDRAIN_KEY= diff --git a/src/upload/imglink.rs b/src/upload/imglink.rs new file mode 100644 index 00000000..d52004b1 --- /dev/null +++ b/src/upload/imglink.rs @@ -0,0 +1,72 @@ +use anyhow::{Context, Result}; +use reqwest::Client; +use reqwest::multipart::{Form, Part}; +use serde::Deserialize; + +use super::parse_json; +use crate::image::detect_format; + +pub const API_URL: &str = "https://imglink.cc/api/v1/upload"; + +#[derive(Deserialize)] +struct Response { + url: String, +} + +/// Upload image bytes to imglink.cc. +/// +/// Requires API key. +pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { + let ext = detect_format(&data)?; + let name = format!("img.{}", ext.extensions_str()[0]); + let content_type = ext.to_mime_type(); + + let form = Form::new().part( + "file", + Part::bytes(data) + .file_name(name) + .mime_str(content_type) + .context("invalid mime type")?, + ); + + let resp = client + .post(url) + .header("X-API-Key", key) + .multipart(form) + .send() + .await + .context("failed to send request to imglink")?; + + let resp: Response = parse_json(resp, "imglink").await?; + Ok(resp.url) +} + +#[cfg(test)] +#[expect(clippy::unwrap_used)] +mod tests { + use wiremock::matchers::{header, method}; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + use super::*; + + #[tokio::test] + async fn test_upload_success() { + let mock_server = MockServer::start().await; + + Mock::given(method("POST")) + .and(header("X-API-Key", "test_key")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "url": "https://imglink.cc/cdn/aBcDeFgHiJ.png" + }))) + .mount(&mock_server) + .await; + + let png = crate::image::create_test_png(); + + let client = Client::new(); + let url = upload(&client, png, &mock_server.uri(), "test_key") + .await + .unwrap(); + assert_eq!(url, "https://imglink.cc/cdn/aBcDeFgHiJ.png"); + } +} diff --git a/src/upload/mod.rs b/src/upload/mod.rs index 2753c79d..b297a318 100644 --- a/src/upload/mod.rs +++ b/src/upload/mod.rs @@ -11,6 +11,7 @@ mod imgbb; mod imgbox; mod imgchest; mod imghippo; +mod imglink; mod imgur; mod lensdump; mod pixeldrain; @@ -71,6 +72,7 @@ pub enum Hosting { Imgbox, Imgchest, Imghippo, + Imglink, Imgur, Lensdump, Pixeldrain, @@ -136,6 +138,10 @@ pub async fn upload(client: &Client, hosting: Hosting, data: Vec) -> Result< let key = get_env("IMGHIPPO_KEY")?; imghippo::upload(client, data, imghippo::API_URL, &key).await } + Hosting::Imglink => { + let key = get_env("IMGLINK_KEY")?; + imglink::upload(client, data, imglink::API_URL, &key).await + } Hosting::Imgbox => imgbox::upload(client, data, imgbox::API_URL).await, Hosting::Imgchest => { let key = get_env("IMGCHEST_KEY")?; From 3d67a4b4ea01d738f054e9af63a3abd261ca5691 Mon Sep 17 00:00:00 2001 From: deadnews Date: Wed, 24 Jun 2026 18:05:08 +0700 Subject: [PATCH 4/6] feat(kappa): add hosting --- README.md | 1 + src/upload/kappa.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++ src/upload/mod.rs | 3 ++ 3 files changed, 74 insertions(+) create mode 100644 src/upload/kappa.rs diff --git a/README.md b/README.md index ca87ca59..31436552 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ yay -S imgup-bin | [imgchest](https://imgchest.com/) | + | `https://cdn.imgchest.com/files/{id}.png` | | [imglink](https://imglink.cc/) | + | `https://imglink.cc/cdn/{id}.png` | | [imgur](https://imgur.com/) | - | `https://i.imgur.com/{id}.png` | +| [kappa](https://kappa.lol/) | - | `https://kappa.lol/{id}.png` | | [lensdump](https://lensdump.com/) | + | `https://i.lensdump.com/i/{id}.png` | | [pixeldrain](https://pixeldrain.com/) | + | `https://pixeldrain.com/api/file/{id}` | | [pixhost](https://pixhost.to/) | - | `https://img75.pixhost.to/images/69/{id}_img.png` | diff --git a/src/upload/kappa.rs b/src/upload/kappa.rs new file mode 100644 index 00000000..87a24f06 --- /dev/null +++ b/src/upload/kappa.rs @@ -0,0 +1,70 @@ +use anyhow::{Context, Result}; +use reqwest::Client; +use reqwest::multipart::{Form, Part}; +use serde::Deserialize; + +use super::parse_json; +use crate::image::detect_format; + +pub const API_URL: &str = "https://kappa.lol/api/upload"; + +#[derive(Deserialize)] +struct Response { + link: String, + ext: String, +} + +/// Upload image bytes to kappa.lol. +/// +/// No authentication required. +pub async fn upload(client: &Client, data: Vec, url: &str) -> Result { + let ext = detect_format(&data)?; + let name = format!("img.{}", ext.extensions_str()[0]); + let content_type = ext.to_mime_type(); + + let form = Form::new().part( + "file", + Part::bytes(data) + .file_name(name) + .mime_str(content_type) + .context("invalid mime type")?, + ); + + let resp = client + .post(url) + .multipart(form) + .send() + .await + .context("failed to send request to kappa")?; + + let resp: Response = parse_json(resp, "kappa").await?; + Ok(format!("{}{}", resp.link, resp.ext)) +} + +#[cfg(test)] +#[expect(clippy::unwrap_used)] +mod tests { + use wiremock::matchers::method; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + use super::*; + + #[tokio::test] + async fn test_upload_success() { + let mock_server = MockServer::start().await; + + Mock::given(method("POST")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "link": "https://kappa.lol/dBZyFV", + "ext": ".png" + }))) + .mount(&mock_server) + .await; + + let png = crate::image::create_test_png(); + + let client = Client::new(); + let url = upload(&client, png, &mock_server.uri()).await.unwrap(); + assert_eq!(url, "https://kappa.lol/dBZyFV.png"); + } +} diff --git a/src/upload/mod.rs b/src/upload/mod.rs index b297a318..88fd4193 100644 --- a/src/upload/mod.rs +++ b/src/upload/mod.rs @@ -13,6 +13,7 @@ mod imgchest; mod imghippo; mod imglink; mod imgur; +mod kappa; mod lensdump; mod pixeldrain; mod pixhost; @@ -74,6 +75,7 @@ pub enum Hosting { Imghippo, Imglink, Imgur, + Kappa, Lensdump, Pixeldrain, Pixhost, @@ -104,6 +106,7 @@ pub async fn upload(client: &Client, hosting: Hosting, data: Vec) -> Result< Hosting::Beeimg => beeimg::upload(client, data, beeimg::API_URL).await, Hosting::Catbox => catbox::upload(client, data, catbox::API_URL).await, Hosting::Fastpic => fastpic::upload(client, data, fastpic::API_URL).await, + Hosting::Kappa => kappa::upload(client, data, kappa::API_URL).await, Hosting::Pixhost => pixhost::upload(client, data, pixhost::API_URL).await, Hosting::Sxcu => sxcu::upload(client, data, sxcu::API_URL).await, Hosting::Cloudinary => { From c5f5ad8c2406335fe93da7fa166338309c78079c Mon Sep 17 00:00:00 2001 From: deadnews Date: Wed, 24 Jun 2026 20:45:37 +0700 Subject: [PATCH 5/6] feat(filepost,pixvid): add hostings --- README.md | 4 +++ src/upload/filepost.rs | 75 ++++++++++++++++++++++++++++++++++++++++++ src/upload/mod.rs | 16 +++++++++ src/upload/pixvid.rs | 66 +++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 src/upload/filepost.rs create mode 100644 src/upload/pixvid.rs diff --git a/README.md b/README.md index 31436552..23ad5965 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ yay -S imgup-bin | [catbox](https://catbox.moe/) | - | `https://files.catbox.moe/{id}` | | [cloudinary](https://cloudinary.com/) | + | `https://res.cloudinary.com/{cloud}/image/upload/{id}.png` | | [fastpic](https://fastpic.org/) | - | `https://i120.fastpic.org/big/2022/0730/d9/{id}.png` | +| [filepost](https://filepost.dev/) | + | `https://cdn.filepost.dev/file/.../{id}.png` | | [freeimage](https://freeimage.host/) | + | `https://iili.io/{id}.png` | | [gofile](https://gofile.io/) | - | `https://gofile.io/d/{id}` | | [gyazo](https://gyazo.com/) | + | `https://i.gyazo.com/{id}.png` | @@ -44,6 +45,7 @@ yay -S imgup-bin | [lensdump](https://lensdump.com/) | + | `https://i.lensdump.com/i/{id}.png` | | [pixeldrain](https://pixeldrain.com/) | + | `https://pixeldrain.com/api/file/{id}` | | [pixhost](https://pixhost.to/) | - | `https://img75.pixhost.to/images/69/{id}_img.png` | +| [pixvid](https://pixvid.org/) | + | `https://pixvid.org/images/2022/07/30/{id}.png` | | [postimages](https://postimages.org/) | + | `https://i.postimg.cc/{id}/img.png` | | [ptpimg](https://ptpimg.me/) | + | `https://ptpimg.me/{id}.png` | | [sxcu](https://sxcu.net/) | - | `https://sxcu.net/{id}.png` | @@ -83,6 +85,7 @@ Options: CLOUDINARY_CLOUD_NAME= CLOUDINARY_API_KEY= CLOUDINARY_API_SECRET= +FILEPOST_KEY= FREEIMAGE_KEY= GYAZO_TOKEN= IMAGEBAN_TOKEN= @@ -94,6 +97,7 @@ IMGLINK_KEY= IMGUR_CLIENT_ID= LENSDUMP_KEY= PIXELDRAIN_KEY= +PIXVID_KEY= POSTIMAGES_KEY= PTPIMG_KEY= THUMBSNAP_KEY= diff --git a/src/upload/filepost.rs b/src/upload/filepost.rs new file mode 100644 index 00000000..f492150e --- /dev/null +++ b/src/upload/filepost.rs @@ -0,0 +1,75 @@ +use anyhow::{Context, Result}; +use reqwest::Client; +use reqwest::multipart::{Form, Part}; +use serde::Deserialize; + +use super::parse_json; +use crate::image::detect_format; + +pub const API_URL: &str = "https://filepost.dev/v1/upload"; + +#[derive(Deserialize)] +struct Response { + url: String, +} + +/// Upload image bytes to filepost.dev. +/// +/// Requires API key. +pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { + let ext = detect_format(&data)?; + let name = format!("img.{}", ext.extensions_str()[0]); + let content_type = ext.to_mime_type(); + + let form = Form::new().part( + "file", + Part::bytes(data) + .file_name(name) + .mime_str(content_type) + .context("invalid mime type")?, + ); + + let resp = client + .post(url) + .header("X-API-Key", key) + .multipart(form) + .send() + .await + .context("failed to send request to filepost")?; + + let resp: Response = parse_json(resp, "filepost").await?; + Ok(resp.url) +} + +#[cfg(test)] +#[expect(clippy::unwrap_used)] +mod tests { + use wiremock::matchers::{header, method}; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + use super::*; + + #[tokio::test] + async fn test_upload_success() { + let mock_server = MockServer::start().await; + + Mock::given(method("POST")) + .and(header("X-API-Key", "test_key")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "url": "https://cdn.filepost.dev/file/filepost/uploads/5b/abc123.png" + }))) + .mount(&mock_server) + .await; + + let png = crate::image::create_test_png(); + + let client = Client::new(); + let url = upload(&client, png, &mock_server.uri(), "test_key") + .await + .unwrap(); + assert_eq!( + url, + "https://cdn.filepost.dev/file/filepost/uploads/5b/abc123.png" + ); + } +} diff --git a/src/upload/mod.rs b/src/upload/mod.rs index 88fd4193..aef9c9dc 100644 --- a/src/upload/mod.rs +++ b/src/upload/mod.rs @@ -2,6 +2,7 @@ mod beeimg; mod catbox; mod cloudinary; mod fastpic; +mod filepost; mod freeimage; mod gofile; mod gyazo; @@ -17,6 +18,7 @@ mod kappa; mod lensdump; mod pixeldrain; mod pixhost; +mod pixvid; mod postimages; mod ptpimg; mod sxcu; @@ -64,6 +66,7 @@ pub enum Hosting { Catbox, Cloudinary, Fastpic, + Filepost, Freeimage, Gofile, Gyazo, @@ -79,6 +82,7 @@ pub enum Hosting { Lensdump, Pixeldrain, Pixhost, + Pixvid, Postimages, Ptpimg, Sxcu, @@ -101,6 +105,10 @@ impl std::fmt::Display for Hosting { } /// Dispatch upload to the appropriate provider. +#[expect( + clippy::too_many_lines, + reason = "flat provider dispatch grows with hostings" +)] pub async fn upload(client: &Client, hosting: Hosting, data: Vec) -> Result { let url = match hosting { Hosting::Beeimg => beeimg::upload(client, data, beeimg::API_URL).await, @@ -116,6 +124,10 @@ pub async fn upload(client: &Client, hosting: Hosting, data: Vec) -> Result< let url = format!("{}/{cloud_name}/image/upload", cloudinary::API_URL); cloudinary::upload(client, data, &url, &api_key, &api_secret).await } + Hosting::Filepost => { + let key = get_env("FILEPOST_KEY")?; + filepost::upload(client, data, filepost::API_URL, &key).await + } Hosting::Freeimage => { let key = get_env("FREEIMAGE_KEY")?; freeimage::upload(client, data, freeimage::API_URL, &key).await @@ -163,6 +175,10 @@ pub async fn upload(client: &Client, hosting: Hosting, data: Vec) -> Result< let key = get_env("PIXELDRAIN_KEY")?; pixeldrain::upload(client, data, pixeldrain::API_URL, &key).await } + Hosting::Pixvid => { + let key = get_env("PIXVID_KEY")?; + pixvid::upload(client, data, pixvid::API_URL, &key).await + } Hosting::Postimages => { let key = get_env("POSTIMAGES_KEY")?; postimages::upload(client, data, postimages::API_URL, &key).await diff --git a/src/upload/pixvid.rs b/src/upload/pixvid.rs new file mode 100644 index 00000000..df5bab17 --- /dev/null +++ b/src/upload/pixvid.rs @@ -0,0 +1,66 @@ +use anyhow::{Context, Result}; +use reqwest::Client; +use reqwest::multipart::{Form, Part}; +use serde::Deserialize; + +use super::parse_json; + +pub const API_URL: &str = "https://pixvid.org/api/1/upload"; + +#[derive(Deserialize)] +struct Response { + image: Image, +} + +#[derive(Deserialize)] +struct Image { + url: String, +} + +/// Upload image bytes to pixvid.org. +/// +/// Requires API key. +pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { + let form = Form::new() + .text("format", "json") + .part("source", Part::bytes(data).file_name("image")); + + let resp = client + .post(url) + .header("X-API-Key", key) + .multipart(form) + .send() + .await + .context("failed to send request to pixvid")?; + + let resp: Response = parse_json(resp, "pixvid").await?; + Ok(resp.image.url) +} + +#[cfg(test)] +#[expect(clippy::unwrap_used)] +mod tests { + use wiremock::matchers::{header, method}; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + use super::*; + + #[tokio::test] + async fn test_upload_success() { + let mock_server = MockServer::start().await; + + Mock::given(method("POST")) + .and(header("X-API-Key", "test_key")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "image": {"url": "https://pixvid.org/images/2026/06/24/77z7e.png"} + }))) + .mount(&mock_server) + .await; + + let client = Client::new(); + let url = upload(&client, vec![1, 2, 3], &mock_server.uri(), "test_key") + .await + .unwrap(); + assert_eq!(url, "https://pixvid.org/images/2026/06/24/77z7e.png"); + } +} From dae6e2372cd655551fecc346e9c64444574e4e83 Mon Sep 17 00:00:00 2001 From: deadnews Date: Wed, 24 Jun 2026 21:33:40 +0700 Subject: [PATCH 6/6] fix(upload): tidy dispatch --- README.md | 68 ++++++++++++------------ src/upload/mod.rs | 131 ++++++++++++++-------------------------------- 2 files changed, 74 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 23ad5965..c42c7305 100644 --- a/README.md +++ b/README.md @@ -23,38 +23,40 @@ yay -S imgup-bin ## Hostings -| host | key required | return example | -| :------------------------------------ | :----------: | :--------------------------------------------------------- | -| [beeimg](https://beeimg.com/) | - | `https://beeimg.com/images/{id}.png` | -| [catbox](https://catbox.moe/) | - | `https://files.catbox.moe/{id}` | -| [cloudinary](https://cloudinary.com/) | + | `https://res.cloudinary.com/{cloud}/image/upload/{id}.png` | -| [fastpic](https://fastpic.org/) | - | `https://i120.fastpic.org/big/2022/0730/d9/{id}.png` | -| [filepost](https://filepost.dev/) | + | `https://cdn.filepost.dev/file/.../{id}.png` | -| [freeimage](https://freeimage.host/) | + | `https://iili.io/{id}.png` | -| [gofile](https://gofile.io/) | - | `https://gofile.io/d/{id}` | -| [gyazo](https://gyazo.com/) | + | `https://i.gyazo.com/{id}.png` | -| [imageban](https://imageban.ru/) | + | `https://i2.imageban.ru/out/2022/07/30/{id}.png` | -| [imagekit](https://imagekit.io/) | + | `https://ik.imagekit.io/{id}/img_{id}.png` | -| [imgbb](https://imgbb.com/) | + | `https://i.ibb.co/{id}/image.png` | -| [imghippo](https://imghippo.com/) | + | `https://i.imghippo.com/files/{id}.png` | -| [imgbox](https://imgbox.com/) | - | `https://images2.imgbox.com/52/8c/{id}_o.png` | -| [imgchest](https://imgchest.com/) | + | `https://cdn.imgchest.com/files/{id}.png` | -| [imglink](https://imglink.cc/) | + | `https://imglink.cc/cdn/{id}.png` | -| [imgur](https://imgur.com/) | - | `https://i.imgur.com/{id}.png` | -| [kappa](https://kappa.lol/) | - | `https://kappa.lol/{id}.png` | -| [lensdump](https://lensdump.com/) | + | `https://i.lensdump.com/i/{id}.png` | -| [pixeldrain](https://pixeldrain.com/) | + | `https://pixeldrain.com/api/file/{id}` | -| [pixhost](https://pixhost.to/) | - | `https://img75.pixhost.to/images/69/{id}_img.png` | -| [pixvid](https://pixvid.org/) | + | `https://pixvid.org/images/2022/07/30/{id}.png` | -| [postimages](https://postimages.org/) | + | `https://i.postimg.cc/{id}/img.png` | -| [ptpimg](https://ptpimg.me/) | + | `https://ptpimg.me/{id}.png` | -| [sxcu](https://sxcu.net/) | - | `https://sxcu.net/{id}.png` | -| [thumbsnap](https://thumbsnap.com/) | + | `https://thumbsnap.com/i/{id}.png` | -| [tixte](https://tixte.com/) | + | `https://{domain}.tixte.co/r/{id}.png` | -| [uplio](https://upl.io/) | + | `https://upl.io/i/{id}.png` | -| [uploadcare](https://uploadcare.com/) | + | `https://ucarecdn.com/{id}/img.png` | -| [vgy](https://vgy.me/) | + | `https://i.vgy.me/{id}.png` | -| [zpic](https://zpic.biz/) | + | `https://zpi.cx/b/{id}.png` | +> key: `-` none · `±` optional · `+` required + +| host | key | return example | +| :------------------------------------ | :-: | :--------------------------------------------------------- | +| [beeimg](https://beeimg.com/) | - | `https://beeimg.com/images/{id}.png` | +| [catbox](https://catbox.moe/) | - | `https://files.catbox.moe/{id}` | +| [fastpic](https://fastpic.org/) | - | `https://i120.fastpic.org/big/2022/0730/d9/{id}.png` | +| [gofile](https://gofile.io/) | - | `https://gofile.io/d/{id}` | +| [imgbox](https://imgbox.com/) | - | `https://images2.imgbox.com/52/8c/{id}_o.png` | +| [kappa](https://kappa.lol/) | - | `https://kappa.lol/{id}.png` | +| [pixhost](https://pixhost.to/) | - | `https://img75.pixhost.to/images/69/{id}_img.png` | +| [sxcu](https://sxcu.net/) | - | `https://sxcu.net/{id}.png` | +| [imgur](https://imgur.com/) | ± | `https://i.imgur.com/{id}.png` | +| [cloudinary](https://cloudinary.com/) | + | `https://res.cloudinary.com/{cloud}/image/upload/{id}.png` | +| [filepost](https://filepost.dev/) | + | `https://cdn.filepost.dev/file/.../{id}.png` | +| [freeimage](https://freeimage.host/) | + | `https://iili.io/{id}.png` | +| [gyazo](https://gyazo.com/) | + | `https://i.gyazo.com/{id}.png` | +| [imageban](https://imageban.ru/) | + | `https://i2.imageban.ru/out/2022/07/30/{id}.png` | +| [imagekit](https://imagekit.io/) | + | `https://ik.imagekit.io/{id}/img_{id}.png` | +| [imgbb](https://imgbb.com/) | + | `https://i.ibb.co/{id}/image.png` | +| [imgchest](https://imgchest.com/) | + | `https://cdn.imgchest.com/files/{id}.png` | +| [imghippo](https://imghippo.com/) | + | `https://i.imghippo.com/files/{id}.png` | +| [imglink](https://imglink.cc/) | + | `https://imglink.cc/cdn/{id}.png` | +| [lensdump](https://lensdump.com/) | + | `https://i.lensdump.com/i/{id}.png` | +| [pixeldrain](https://pixeldrain.com/) | + | `https://pixeldrain.com/api/file/{id}` | +| [pixvid](https://pixvid.org/) | + | `https://pixvid.org/images/2022/07/30/{id}.png` | +| [postimages](https://postimages.org/) | + | `https://i.postimg.cc/{id}/img.png` | +| [ptpimg](https://ptpimg.me/) | + | `https://ptpimg.me/{id}.png` | +| [thumbsnap](https://thumbsnap.com/) | + | `https://thumbsnap.com/i/{id}.png` | +| [tixte](https://tixte.com/) | + | `https://{domain}.tixte.co/r/{id}.png` | +| [uplio](https://upl.io/) | + | `https://upl.io/i/{id}.png` | +| [uploadcare](https://uploadcare.com/) | + | `https://ucarecdn.com/{id}/img.png` | +| [vgy](https://vgy.me/) | + | `https://i.vgy.me/{id}.png` | +| [zpic](https://zpic.biz/) | + | `https://zpi.cx/b/{id}.png` | ## Usage @@ -94,7 +96,7 @@ IMGBB_KEY= IMGHIPPO_KEY= IMGCHEST_KEY= IMGLINK_KEY= -IMGUR_CLIENT_ID= +IMGUR_CLIENT_ID= # optional LENSDUMP_KEY= PIXELDRAIN_KEY= PIXVID_KEY= diff --git a/src/upload/mod.rs b/src/upload/mod.rs index aef9c9dc..c24e909a 100644 --- a/src/upload/mod.rs +++ b/src/upload/mod.rs @@ -105,18 +105,47 @@ impl std::fmt::Display for Hosting { } /// Dispatch upload to the appropriate provider. -#[expect( - clippy::too_many_lines, - reason = "flat provider dispatch grows with hostings" -)] pub async fn upload(client: &Client, hosting: Hosting, data: Vec) -> Result { + macro_rules! anon { + ($m:ident) => { + $m::upload(client, data, $m::API_URL).await + }; + } + macro_rules! keyed { + ($m:ident, $env:literal) => { + $m::upload(client, data, $m::API_URL, &get_env($env)?).await + }; + } + let url = match hosting { - Hosting::Beeimg => beeimg::upload(client, data, beeimg::API_URL).await, - Hosting::Catbox => catbox::upload(client, data, catbox::API_URL).await, - Hosting::Fastpic => fastpic::upload(client, data, fastpic::API_URL).await, - Hosting::Kappa => kappa::upload(client, data, kappa::API_URL).await, - Hosting::Pixhost => pixhost::upload(client, data, pixhost::API_URL).await, - Hosting::Sxcu => sxcu::upload(client, data, sxcu::API_URL).await, + Hosting::Beeimg => anon!(beeimg), + Hosting::Catbox => anon!(catbox), + Hosting::Fastpic => anon!(fastpic), + Hosting::Gofile => anon!(gofile), + Hosting::Imgbox => anon!(imgbox), + Hosting::Kappa => anon!(kappa), + Hosting::Pixhost => anon!(pixhost), + Hosting::Sxcu => anon!(sxcu), + Hosting::Filepost => keyed!(filepost, "FILEPOST_KEY"), + Hosting::Freeimage => keyed!(freeimage, "FREEIMAGE_KEY"), + Hosting::Gyazo => keyed!(gyazo, "GYAZO_TOKEN"), + Hosting::Imageban => keyed!(imageban, "IMAGEBAN_TOKEN"), + Hosting::Imagekit => keyed!(imagekit, "IMAGEKIT_PRIVATE_KEY"), + Hosting::Imgbb => keyed!(imgbb, "IMGBB_KEY"), + Hosting::Imgchest => keyed!(imgchest, "IMGCHEST_KEY"), + Hosting::Imghippo => keyed!(imghippo, "IMGHIPPO_KEY"), + Hosting::Imglink => keyed!(imglink, "IMGLINK_KEY"), + Hosting::Lensdump => keyed!(lensdump, "LENSDUMP_KEY"), + Hosting::Pixeldrain => keyed!(pixeldrain, "PIXELDRAIN_KEY"), + Hosting::Pixvid => keyed!(pixvid, "PIXVID_KEY"), + Hosting::Postimages => keyed!(postimages, "POSTIMAGES_KEY"), + Hosting::Ptpimg => keyed!(ptpimg, "PTPIMG_KEY"), + Hosting::Thumbsnap => keyed!(thumbsnap, "THUMBSNAP_KEY"), + Hosting::Tixte => keyed!(tixte, "TIXTE_KEY"), + Hosting::Uplio => keyed!(uplio, "UPLIO_KEY"), + Hosting::Uploadcare => keyed!(uploadcare, "UPLOADCARE_KEY"), + Hosting::Vgy => keyed!(vgy, "VGY_KEY"), + Hosting::Zpic => keyed!(zpic, "ZPIC_KEY"), Hosting::Cloudinary => { let cloud_name = get_env("CLOUDINARY_CLOUD_NAME")?; let api_key = get_env("CLOUDINARY_API_KEY")?; @@ -124,93 +153,11 @@ pub async fn upload(client: &Client, hosting: Hosting, data: Vec) -> Result< let url = format!("{}/{cloud_name}/image/upload", cloudinary::API_URL); cloudinary::upload(client, data, &url, &api_key, &api_secret).await } - Hosting::Filepost => { - let key = get_env("FILEPOST_KEY")?; - filepost::upload(client, data, filepost::API_URL, &key).await - } - Hosting::Freeimage => { - let key = get_env("FREEIMAGE_KEY")?; - freeimage::upload(client, data, freeimage::API_URL, &key).await - } - Hosting::Gofile => gofile::upload(client, data, gofile::API_URL).await, - Hosting::Gyazo => { - let token = get_env("GYAZO_TOKEN")?; - gyazo::upload(client, data, gyazo::API_URL, &token).await - } - Hosting::Imageban => { - let token = get_env("IMAGEBAN_TOKEN")?; - imageban::upload(client, data, imageban::API_URL, &token).await - } - Hosting::Imagekit => { - let key = get_env("IMAGEKIT_PRIVATE_KEY")?; - imagekit::upload(client, data, imagekit::API_URL, &key).await - } - Hosting::Imgbb => { - let key = get_env("IMGBB_KEY")?; - imgbb::upload(client, data, imgbb::API_URL, &key).await - } - Hosting::Imghippo => { - let key = get_env("IMGHIPPO_KEY")?; - imghippo::upload(client, data, imghippo::API_URL, &key).await - } - Hosting::Imglink => { - let key = get_env("IMGLINK_KEY")?; - imglink::upload(client, data, imglink::API_URL, &key).await - } - Hosting::Imgbox => imgbox::upload(client, data, imgbox::API_URL).await, - Hosting::Imgchest => { - let key = get_env("IMGCHEST_KEY")?; - imgchest::upload(client, data, imgchest::API_URL, &key).await - } Hosting::Imgur => { let client_id = std::env::var("IMGUR_CLIENT_ID") .unwrap_or_else(|_| imgur::DEFAULT_CLIENT_ID.to_owned()); imgur::upload(client, data, imgur::API_URL, &client_id).await } - Hosting::Lensdump => { - let key = get_env("LENSDUMP_KEY")?; - lensdump::upload(client, data, lensdump::API_URL, &key).await - } - Hosting::Pixeldrain => { - let key = get_env("PIXELDRAIN_KEY")?; - pixeldrain::upload(client, data, pixeldrain::API_URL, &key).await - } - Hosting::Pixvid => { - let key = get_env("PIXVID_KEY")?; - pixvid::upload(client, data, pixvid::API_URL, &key).await - } - Hosting::Postimages => { - let key = get_env("POSTIMAGES_KEY")?; - postimages::upload(client, data, postimages::API_URL, &key).await - } - Hosting::Ptpimg => { - let key = get_env("PTPIMG_KEY")?; - ptpimg::upload(client, data, ptpimg::API_URL, &key).await - } - Hosting::Thumbsnap => { - let key = get_env("THUMBSNAP_KEY")?; - thumbsnap::upload(client, data, thumbsnap::API_URL, &key).await - } - Hosting::Tixte => { - let key = get_env("TIXTE_KEY")?; - tixte::upload(client, data, tixte::API_URL, &key).await - } - Hosting::Uplio => { - let key = get_env("UPLIO_KEY")?; - uplio::upload(client, data, uplio::API_URL, &key).await - } - Hosting::Uploadcare => { - let key = get_env("UPLOADCARE_KEY")?; - uploadcare::upload(client, data, uploadcare::API_URL, &key).await - } - Hosting::Vgy => { - let key = get_env("VGY_KEY")?; - vgy::upload(client, data, vgy::API_URL, &key).await - } - Hosting::Zpic => { - let key = get_env("ZPIC_KEY")?; - zpic::upload(client, data, zpic::API_URL, &key).await - } }?; ensure!(!url.is_empty(), "{hosting} returned empty URL");