Compare commits
143 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa537c63d9 | ||
|
|
3f6cba6bdd | ||
|
|
65dcb761bd | ||
|
|
df4be06104 | ||
|
|
a6c9716521 | ||
|
|
86ea359ab0 | ||
|
|
2f207d2667 | ||
|
|
d26ef77ead | ||
|
|
a174eda2e0 | ||
|
|
cc256be6ac | ||
|
|
2892713442 | ||
|
|
acab906723 | ||
|
|
7728100b3b | ||
|
|
6ccbafbb47 | ||
|
|
162fa66840 | ||
|
|
7d8b10ba7d | ||
|
|
fcc8e2b822 | ||
|
|
fe889d1026 | ||
|
|
8728576c0c | ||
|
|
3a6a75235c | ||
|
|
c197397e58 | ||
|
|
9c21bf44bf | ||
|
|
f8fc5f57a0 | ||
|
|
3b72261622 | ||
|
|
61c8175e2a | ||
|
|
5002fadff1 | ||
|
|
45bc01e8bd | ||
|
|
5618c88aaf | ||
|
|
72054c2fec | ||
|
|
c73b6f8c90 | ||
|
|
02c04bbbda | ||
|
|
a5b9f1ff60 | ||
|
|
d683626a49 | ||
|
|
9b82042f98 | ||
|
|
0618b8d452 | ||
|
|
ad30f8389b | ||
|
|
d10399826f | ||
|
|
90c98c0eb8 | ||
|
|
12e0093856 | ||
|
|
80e6947c87 | ||
|
|
88e997b6dd | ||
|
|
ca7de1e166 | ||
|
|
7e70f4f339 | ||
|
|
41d837b5b0 | ||
|
|
f399b5cdd6 | ||
|
|
7c071de816 | ||
|
|
be781c007e | ||
|
|
43e10f7e0a | ||
|
|
392a0235f8 | ||
|
|
b25f0cf848 | ||
|
|
7c537a68a0 | ||
|
|
3fd9169c15 | ||
|
|
3bb8caf576 | ||
|
|
fda738dde1 | ||
|
|
c255e9eab8 | ||
|
|
4e61673309 | ||
|
|
e179da8e48 | ||
|
|
9e079d0968 | ||
|
|
5fba045c3a | ||
|
|
41a318e417 | ||
|
|
80bb785d07 | ||
|
|
319354b1ac | ||
|
|
501a8c9877 | ||
|
|
88e67df823 | ||
|
|
2cdf95f4ad | ||
|
|
84854e9bf3 | ||
|
|
59e6d4f47b | ||
|
|
6a1219a76e | ||
|
|
5f3103d908 | ||
|
|
18e8130d04 | ||
|
|
0874d7f305 | ||
|
|
8d610ceaad | ||
|
|
29b9b1e37e | ||
|
|
18774fa88b | ||
|
|
f450ca818c | ||
|
|
97fff96bd7 | ||
|
|
225d47e09e | ||
|
|
1caa61daa7 | ||
|
|
8b3fce1ec2 | ||
|
|
e060b29562 | ||
|
|
47258b2b26 | ||
|
|
ed8fbaf286 | ||
|
|
43ce7f9302 | ||
|
|
27ecd89f27 | ||
|
|
f4200c56a6 | ||
|
|
ee2288360e | ||
|
|
27d16425b7 | ||
|
|
f7bc952f82 | ||
|
|
07cf7fea90 | ||
|
|
e5cd965530 | ||
|
|
43271f0004 | ||
|
|
8d94b700f1 | ||
|
|
ee6cc93a0b | ||
|
|
b89472d397 | ||
|
|
a505191e0e | ||
|
|
387b329651 | ||
|
|
ca9b8c328b | ||
|
|
9a887ca2d0 | ||
|
|
947986fd6c | ||
|
|
d6fb0f0483 | ||
|
|
1fb610a32e | ||
|
|
6e8aeba93d | ||
|
|
ff58033e51 | ||
|
|
1c69604346 | ||
|
|
660715dd7b | ||
|
|
06ba92fc54 | ||
|
|
5717fb178c | ||
|
|
c77736ccc5 | ||
|
|
549117406c | ||
|
|
820937d353 | ||
|
|
7af18cd8b2 | ||
|
|
815d92cd48 | ||
|
|
4a0a7bb0de | ||
|
|
d4981cf75c | ||
|
|
e61063df56 | ||
|
|
a9433d2f73 | ||
|
|
5f4ac6a10c | ||
|
|
8e49fe7dd7 | ||
|
|
9046d7d8c2 | ||
|
|
fe1af720e3 | ||
|
|
f3d0d53e20 | ||
|
|
0d00cac03f | ||
|
|
4847fa31bd | ||
|
|
259f31a1d3 | ||
|
|
a2fd6913cc | ||
|
|
e6b410b837 | ||
|
|
39b110ff83 | ||
|
|
fff751fbcc | ||
|
|
871ebfb35f | ||
|
|
b8c01ad340 | ||
|
|
dee58698c7 | ||
|
|
f4b24453ac | ||
|
|
d699d2ac8c | ||
|
|
3f952f65bc | ||
|
|
1e1fbb6fcb | ||
|
|
8dd85ffcfd | ||
|
|
2a7d1845eb | ||
|
|
172b066e95 | ||
|
|
2c5e18b40c | ||
|
|
f7ebdc8915 | ||
|
|
e7f647aa3c | ||
|
|
4b936d86a4 | ||
|
|
26344b96b4 |
13 changed files with 1903 additions and 154 deletions
14
.github/workflows/build-to-release.yml
vendored
14
.github/workflows/build-to-release.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
rust_target: aarch64-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
targets: ${{ matrix.rust_target }}
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
Write-Host "CLI executable ready: loaderspot-cli-win-$env:ARCH.exe"
|
||||
|
||||
- name: Upload Windows artifacts
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: windows-artifacts-${{ matrix.arch }}
|
||||
path: artifacts/*
|
||||
|
|
@ -69,7 +69,7 @@ jobs:
|
|||
build_linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
@ -77,7 +77,7 @@ jobs:
|
|||
targets: x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
|
|
@ -110,7 +110,7 @@ jobs:
|
|||
echo "CLI executable ready: loaderspot-cli-linux-x64"
|
||||
|
||||
- name: Upload Linux artifacts
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-artifacts-x64
|
||||
path: artifacts/*
|
||||
|
|
@ -122,7 +122,7 @@ jobs:
|
|||
contents: write
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
|
|
|
|||
2
.github/workflows/check-deb-versions.yml
vendored
2
.github/workflows/check-deb-versions.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.PERSONAL_TOKEN }}
|
||||
|
||||
|
|
|
|||
4
.github/workflows/ms-spotify.yml
vendored
4
.github/workflows/ms-spotify.yml
vendored
|
|
@ -1,8 +1,6 @@
|
|||
name: check Spotify MS
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 */5 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
|
@ -11,7 +9,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run ms-spotify-check.ps1
|
||||
shell: pwsh
|
||||
|
|
|
|||
18
.github/workflows/sync-files.yml
vendored
18
.github/workflows/sync-files.yml
vendored
|
|
@ -1,12 +1,14 @@
|
|||
name: Sync files to table
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'versions.json'
|
||||
- 'versions_deb.json'
|
||||
# Temporarily disabled automatic sync trigger.
|
||||
# Uncomment this block to restore automatic runs on push to main.
|
||||
# push:
|
||||
# branches:
|
||||
# - main
|
||||
# paths:
|
||||
# - 'versions.json'
|
||||
# - 'versions_deb.json'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
|
@ -17,10 +19,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout source repo
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Checkout target repo
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: LoaderSpot/table
|
||||
path: table
|
||||
|
|
|
|||
2
.github/workflows/update_versions.yml
vendored
2
.github/workflows/update_versions.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.PERSONAL_TOKEN }}
|
||||
|
||||
|
|
|
|||
4
.github/workflows/version-check.yml
vendored
4
.github/workflows/version-check.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
versions: ${{ steps.search.outputs.versions }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download latest loaderspot_cli_linux_x64
|
||||
env:
|
||||
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
if: success() && needs.version-search.outputs.versions
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get build info and send to GAS
|
||||
shell: pwsh
|
||||
|
|
|
|||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/LoaderSpot_CLI/target
|
||||
/LoaderSpot_UI/target
|
||||
10
LoaderSpot_UI/Cargo.lock
generated
10
LoaderSpot_UI/Cargo.lock
generated
|
|
@ -762,6 +762,15 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
|
|
@ -1896,6 +1905,7 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
|||
name = "loaderspot_ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"eframe",
|
||||
"egui",
|
||||
"regex",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ reqwest = { version = "0.12", features = ["json"] }
|
|||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
regex = "1.10"
|
||||
crossbeam-channel = "0.5"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["winuser"] }
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
// Hide console window on Windows
|
||||
#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
|
||||
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use eframe::egui;
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::time::Instant;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
|
|
@ -31,13 +31,13 @@ impl Platform {
|
|||
}
|
||||
}
|
||||
|
||||
fn path_template(&self) -> &str {
|
||||
fn generate_path(&self, version: &str, number: i32) -> String {
|
||||
match self {
|
||||
Platform::WinX86 => "win32-x86/spotify_installer-{version}-{number}.exe",
|
||||
Platform::WinX64 => "win32-x86_64/spotify_installer-{version}-{number}.exe",
|
||||
Platform::WinArm64 => "win32-arm64/spotify_installer-{version}-{number}.exe",
|
||||
Platform::MacOsIntel => "osx-x86_64/spotify-autoupdate-{version}-{number}.tbz",
|
||||
Platform::MacOsArm64 => "osx-arm64/spotify-autoupdate-{version}-{number}.tbz",
|
||||
Platform::WinX86 => format!("win32-x86/spotify_installer-{version}-{number}.exe"),
|
||||
Platform::WinX64 => format!("win32-x86_64/spotify_installer-{version}-{number}.exe"),
|
||||
Platform::WinArm64 => format!("win32-arm64/spotify_installer-{version}-{number}.exe"),
|
||||
Platform::MacOsIntel => format!("osx-x86_64/spotify-autoupdate-{version}-{number}.tbz"),
|
||||
Platform::MacOsArm64 => format!("osx-arm64/spotify-autoupdate-{version}-{number}.tbz"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,11 +63,11 @@ impl UrlGenerator {
|
|||
const BASE_URL: &'static str = "https://upgrade.scdn.co/upgrade/client/";
|
||||
|
||||
fn generate_url(platform: Platform, version: &str, number: i32) -> String {
|
||||
let path = platform
|
||||
.path_template()
|
||||
.replace("{version}", version)
|
||||
.replace("{number}", &number.to_string());
|
||||
format!("{}{}", Self::BASE_URL, path)
|
||||
format!(
|
||||
"{}{}",
|
||||
Self::BASE_URL,
|
||||
platform.generate_path(version, number)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,15 +83,12 @@ fn extract_base_version(version: &str) -> String {
|
|||
|
||||
#[allow(dead_code)]
|
||||
fn should_use_win_x86(version: &str) -> bool {
|
||||
let base_version = extract_base_version(version);
|
||||
let parts: Vec<&str> = base_version.split('.').collect();
|
||||
let mut parts = version.split('.');
|
||||
|
||||
if parts.len() >= 3 {
|
||||
if let (Ok(major), Ok(minor), Ok(patch)) = (
|
||||
parts[0].parse::<u32>(),
|
||||
parts[1].parse::<u32>(),
|
||||
parts[2].parse::<u32>(),
|
||||
) {
|
||||
if let (Some(p1), Some(p2), Some(p3)) = (parts.next(), parts.next(), parts.next()) {
|
||||
if let (Ok(major), Ok(minor), Ok(patch)) =
|
||||
(p1.parse::<u32>(), p2.parse::<u32>(), p3.parse::<u32>())
|
||||
{
|
||||
return (major, minor, patch) <= (1, 2, 53);
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +96,8 @@ fn should_use_win_x86(version: &str) -> bool {
|
|||
}
|
||||
|
||||
fn validate_version(version: &str) -> bool {
|
||||
let re = regex::Regex::new(r"^\d+\.\d+\.\d+\.\d+\.g[0-9a-f]{8}$").unwrap();
|
||||
static RE: OnceLock<regex::Regex> = OnceLock::new();
|
||||
let re = RE.get_or_init(|| regex::Regex::new(r"^\d+\.\d+\.\d+\.\d+\.g[0-9a-f]{8}$").unwrap());
|
||||
re.is_match(version)
|
||||
}
|
||||
|
||||
|
|
@ -117,8 +115,23 @@ const MAX_CONNECTION_OPTIONS: [usize; 6] = [50, 100, 150, 200, 250, 300];
|
|||
|
||||
async fn check_url(client: &Client, url: String, platform: Platform) -> Option<(String, Platform)> {
|
||||
match client.head(&url).send().await {
|
||||
Ok(response) if response.status().is_success() => Some((url, platform)),
|
||||
_ => None,
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
Some((url, platform))
|
||||
} else if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS {
|
||||
eprintln!(
|
||||
"RATE LIMITED (429)! Server blocked the request for: {}",
|
||||
url
|
||||
);
|
||||
None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Network error: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +164,7 @@ async fn check_version_and_submit(client: &Client, version: &str) {
|
|||
|
||||
let version_exists = versions_json
|
||||
.values()
|
||||
.any(|v| v.fullversion.as_ref().map_or(false, |fv| fv == version));
|
||||
.any(|v| v.fullversion.as_ref().is_some_and(|fv| fv == version));
|
||||
|
||||
if !version_exists {
|
||||
submit_to_google_form(client, version).await;
|
||||
|
|
@ -159,16 +172,15 @@ async fn check_version_and_submit(client: &Client, version: &str) {
|
|||
}
|
||||
|
||||
enum SearchMessage {
|
||||
Progress(u64, u64),
|
||||
Result(String, Platform),
|
||||
Complete(String),
|
||||
VersionStart(String, usize, usize),
|
||||
CompleteAll,
|
||||
}
|
||||
|
||||
async fn search_installers(
|
||||
client: &Client,
|
||||
version: &str,
|
||||
struct SearchOptions {
|
||||
client: Client,
|
||||
version: String,
|
||||
start: i32,
|
||||
end: i32,
|
||||
platforms: Vec<Platform>,
|
||||
|
|
@ -176,45 +188,59 @@ async fn search_installers(
|
|||
tx: Sender<SearchMessage>,
|
||||
pause_flag: Arc<AtomicBool>,
|
||||
cancel_flag: Arc<AtomicBool>,
|
||||
) {
|
||||
processed: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
async fn search_installers(opts: SearchOptions) {
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
let total = ((end - start + 1) * platforms.len() as i32) as u64;
|
||||
let semaphore = Arc::new(Semaphore::new(max_connections));
|
||||
let semaphore = Arc::new(Semaphore::new(opts.max_connections));
|
||||
let mut tasks = Vec::new();
|
||||
let processed = Arc::new(Mutex::new(0u64));
|
||||
|
||||
for platform in platforms {
|
||||
for number in start..=end {
|
||||
let url = UrlGenerator::generate_url(platform, version, number);
|
||||
let client = client.clone();
|
||||
let permit = semaphore.clone().acquire_owned().await.unwrap();
|
||||
let tx_clone = tx.clone();
|
||||
let processed_clone = processed.clone();
|
||||
let pause_local = pause_flag.clone();
|
||||
let cancel_local = cancel_flag.clone();
|
||||
'outer: for platform in opts.platforms {
|
||||
for number in opts.start..=opts.end {
|
||||
if opts.cancel_flag.load(Ordering::Relaxed) {
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
while opts.pause_flag.load(Ordering::Relaxed) {
|
||||
if opts.cancel_flag.load(Ordering::Relaxed) {
|
||||
break 'outer;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
let permit = match semaphore.clone().acquire_owned().await {
|
||||
Ok(p) => p,
|
||||
Err(_) => break 'outer,
|
||||
};
|
||||
|
||||
while opts.pause_flag.load(Ordering::Relaxed) {
|
||||
if opts.cancel_flag.load(Ordering::Relaxed) {
|
||||
break 'outer;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
if opts.cancel_flag.load(Ordering::Relaxed) {
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
let url = UrlGenerator::generate_url(platform, &opts.version, number);
|
||||
let client = opts.client.clone();
|
||||
let tx_clone = opts.tx.clone();
|
||||
let processed_clone = opts.processed.clone();
|
||||
let cancel_local = opts.cancel_flag.clone();
|
||||
|
||||
let task = tokio::spawn(async move {
|
||||
if cancel_local.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
let _permit = permit;
|
||||
|
||||
while pause_local.load(Ordering::Relaxed) {
|
||||
if cancel_local.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
}
|
||||
|
||||
let result = check_url(&client, url, platform).await;
|
||||
drop(permit);
|
||||
|
||||
let mut p = processed_clone.lock().unwrap();
|
||||
*p += 1;
|
||||
let current = *p;
|
||||
drop(p);
|
||||
|
||||
let _ = tx_clone.send(SearchMessage::Progress(current, total));
|
||||
processed_clone.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
if let Some((url, platform)) = result {
|
||||
let _ = tx_clone.send(SearchMessage::Result(url, platform));
|
||||
|
|
@ -225,11 +251,17 @@ async fn search_installers(
|
|||
}
|
||||
}
|
||||
|
||||
if opts.cancel_flag.load(Ordering::Relaxed) {
|
||||
for task in &tasks {
|
||||
task.abort();
|
||||
}
|
||||
}
|
||||
|
||||
for task in tasks {
|
||||
let _ = task.await;
|
||||
}
|
||||
|
||||
let _ = tx.send(SearchMessage::Complete(version.to_string()));
|
||||
let _ = opts.tx.send(SearchMessage::Complete(opts.version));
|
||||
}
|
||||
|
||||
struct SpotifyFinderApp {
|
||||
|
|
@ -247,7 +279,6 @@ struct SpotifyFinderApp {
|
|||
|
||||
report_unknown: bool,
|
||||
|
||||
search_results: String,
|
||||
is_searching: bool,
|
||||
is_paused: bool,
|
||||
displayed_results: String,
|
||||
|
|
@ -262,7 +293,8 @@ struct SpotifyFinderApp {
|
|||
progress: f32,
|
||||
progress_text: String,
|
||||
total_work: u64,
|
||||
processed_global: u64,
|
||||
|
||||
processed_global: Arc<AtomicU64>,
|
||||
|
||||
rx: Option<Receiver<SearchMessage>>,
|
||||
found_urls: HashMap<Platform, Vec<String>>,
|
||||
|
|
@ -284,7 +316,6 @@ impl Default for SpotifyFinderApp {
|
|||
platform_macos_intel: false,
|
||||
platform_macos_arm64: false,
|
||||
report_unknown: false,
|
||||
search_results: String::new(),
|
||||
is_searching: false,
|
||||
is_paused: false,
|
||||
displayed_results: String::new(),
|
||||
|
|
@ -296,7 +327,7 @@ impl Default for SpotifyFinderApp {
|
|||
progress: 0.0,
|
||||
progress_text: String::new(),
|
||||
total_work: 0,
|
||||
processed_global: 0,
|
||||
processed_global: Arc::new(AtomicU64::new(0)),
|
||||
rx: None,
|
||||
found_urls: HashMap::new(),
|
||||
pause_flag: Arc::new(AtomicBool::new(false)),
|
||||
|
|
@ -309,7 +340,6 @@ impl Default for SpotifyFinderApp {
|
|||
}
|
||||
|
||||
impl SpotifyFinderApp {
|
||||
// Improved reveal logic: finish current item immediately and fast-forward the queue
|
||||
fn advance_reveal(&mut self) {
|
||||
if self.current_reveal.is_some() && !self.reveal_queue.is_empty() {
|
||||
if let Some(cur) = self.current_reveal.take() {
|
||||
|
|
@ -436,7 +466,6 @@ impl SpotifyFinderApp {
|
|||
self.is_searching = true;
|
||||
self.progress = 0.0;
|
||||
self.progress_text = "Starting...".to_string();
|
||||
self.search_results.clear();
|
||||
self.displayed_results.clear();
|
||||
self.reveal_queue.clear();
|
||||
self.current_reveal = None;
|
||||
|
|
@ -451,13 +480,15 @@ impl SpotifyFinderApp {
|
|||
let per_version = ((end - start + 1) * cnt as i32) as u64;
|
||||
total_work_calc = total_work_calc.saturating_add(per_version);
|
||||
}
|
||||
|
||||
self.total_work = total_work_calc;
|
||||
self.processed_global = 0;
|
||||
self.processed_global.store(0, Ordering::Relaxed);
|
||||
|
||||
self.current_version = None;
|
||||
self.current_version_index = 0;
|
||||
self.total_versions = versions.len();
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let (tx, rx): (Sender<SearchMessage>, Receiver<SearchMessage>) = unbounded();
|
||||
self.rx = Some(rx);
|
||||
|
||||
self.pause_flag.store(false, Ordering::Relaxed);
|
||||
|
|
@ -469,6 +500,7 @@ impl SpotifyFinderApp {
|
|||
let pause = self.pause_flag.clone();
|
||||
let cancel = self.cancel_flag.clone();
|
||||
let base_platforms_for_spawn = base_platforms.clone();
|
||||
let processed_for_spawn = self.processed_global.clone();
|
||||
|
||||
self.runtime.spawn(async move {
|
||||
let client = Client::builder()
|
||||
|
|
@ -502,17 +534,18 @@ impl SpotifyFinderApp {
|
|||
continue;
|
||||
}
|
||||
|
||||
search_installers(
|
||||
&client,
|
||||
&version,
|
||||
search_installers(SearchOptions {
|
||||
client: client.clone(),
|
||||
version,
|
||||
start,
|
||||
end,
|
||||
platforms_for_version,
|
||||
max_conn,
|
||||
tx.clone(),
|
||||
pause.clone(),
|
||||
cancel.clone(),
|
||||
)
|
||||
platforms: platforms_for_version,
|
||||
max_connections: max_conn,
|
||||
tx: tx.clone(),
|
||||
pause_flag: pause.clone(),
|
||||
cancel_flag: cancel.clone(),
|
||||
processed: processed_for_spawn.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
if cancel.load(Ordering::Relaxed) {
|
||||
|
|
@ -534,7 +567,6 @@ impl SpotifyFinderApp {
|
|||
}
|
||||
|
||||
fn clear_results(&mut self) {
|
||||
self.search_results.clear();
|
||||
self.displayed_results.clear();
|
||||
self.reveal_queue.clear();
|
||||
self.current_reveal = None;
|
||||
|
|
@ -542,31 +574,24 @@ impl SpotifyFinderApp {
|
|||
self.progress = 0.0;
|
||||
self.progress_text.clear();
|
||||
self.total_work = 0;
|
||||
self.processed_global = 0;
|
||||
self.processed_global.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn update_search_progress(&mut self) {
|
||||
if let Some(rx_owned) = self.rx.take() {
|
||||
let rx = rx_owned;
|
||||
let mut completed = false;
|
||||
|
||||
while let Ok(msg) = rx.try_recv() {
|
||||
match msg {
|
||||
SearchMessage::Progress(_current, _total) => {
|
||||
self.processed_global = self.processed_global.saturating_add(1);
|
||||
let current_processed = self.processed_global.load(Ordering::Relaxed);
|
||||
let denom = if self.total_work == 0 {
|
||||
1
|
||||
} else {
|
||||
self.total_work
|
||||
};
|
||||
self.progress = self.processed_global as f32 / denom as f32;
|
||||
self.progress = current_processed as f32 / denom as f32;
|
||||
|
||||
if let Some(v) = &self.current_version {
|
||||
let short = short_version(v);
|
||||
if self.total_versions > 1 {
|
||||
self.progress_text = format!(
|
||||
"Checking: {}/{}, Version: {}, No. {}/{}",
|
||||
self.processed_global,
|
||||
current_processed,
|
||||
denom,
|
||||
short,
|
||||
self.current_version_index,
|
||||
|
|
@ -575,16 +600,23 @@ impl SpotifyFinderApp {
|
|||
} else {
|
||||
self.progress_text = format!(
|
||||
"Checking: {}/{}, Version: {}",
|
||||
self.processed_global, denom, short
|
||||
current_processed, denom, short
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.progress_text =
|
||||
format!("Checking: {}/{}", self.processed_global, denom);
|
||||
}
|
||||
} else if self.is_searching {
|
||||
self.progress_text = format!("Checking: {}/{}", current_processed, denom);
|
||||
}
|
||||
|
||||
if let Some(rx_owned) = self.rx.take() {
|
||||
let mut completed = false;
|
||||
let mut processed_this_frame = 0;
|
||||
|
||||
while let Ok(msg) = rx_owned.try_recv() {
|
||||
processed_this_frame += 1;
|
||||
|
||||
match msg {
|
||||
SearchMessage::Result(url, platform) => {
|
||||
let entry = self.found_urls.entry(platform).or_insert_with(Vec::new);
|
||||
let entry = self.found_urls.entry(platform).or_default();
|
||||
let first = entry.is_empty();
|
||||
entry.push(url.clone());
|
||||
|
||||
|
|
@ -626,10 +658,14 @@ impl SpotifyFinderApp {
|
|||
completed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if processed_this_frame > 1000 && !completed {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !completed {
|
||||
self.rx = Some(rx);
|
||||
self.rx = Some(rx_owned);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -954,8 +990,7 @@ impl eframe::App for SpotifyFinderApp {
|
|||
self.is_paused = false;
|
||||
self.pause_flag.store(false, Ordering::Relaxed);
|
||||
}
|
||||
} else {
|
||||
if ui
|
||||
} else if ui
|
||||
.add_sized(btn_size, egui::Button::new("⏸ Pause"))
|
||||
.clicked()
|
||||
{
|
||||
|
|
@ -963,7 +998,6 @@ impl eframe::App for SpotifyFinderApp {
|
|||
self.pause_flag.store(true, Ordering::Relaxed);
|
||||
self.progress_text = "Paused".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
if ui
|
||||
.add_sized(btn_size, egui::Button::new("⏹ Stop"))
|
||||
|
|
@ -971,14 +1005,12 @@ impl eframe::App for SpotifyFinderApp {
|
|||
{
|
||||
self.stop_search();
|
||||
}
|
||||
} else {
|
||||
if ui
|
||||
} else if ui
|
||||
.add_sized(btn_size, egui::Button::new("▶ Start Search"))
|
||||
.clicked()
|
||||
{
|
||||
self.start_search();
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_enabled_ui(!self.is_searching, |ui| {
|
||||
if ui
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@ def commit_changes(new_versions):
|
|||
# Формируем сообщение коммита в зависимости от количества новых версий
|
||||
if new_versions:
|
||||
commit_message = (
|
||||
f"Added deb version {new_versions[0][0]}"
|
||||
f"Added deb version {new_versions[0][1]}"
|
||||
if len(new_versions) == 1
|
||||
else f"Added deb versions: {', '.join(v[0] for v in new_versions)}"
|
||||
else f"Added deb versions: {', '.join(v[1] for v in new_versions)}"
|
||||
)
|
||||
else:
|
||||
commit_message = "Update Spotify versions"
|
||||
|
|
|
|||
1664
versions.json
1664
versions.json
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,44 @@
|
|||
{
|
||||
"1.2.84.476": {
|
||||
"fullversion": "1.2.84.476.ga1ff6607",
|
||||
"size": "157643240",
|
||||
"data": "09.03.2026",
|
||||
"links": {
|
||||
"amd64": "https://github.com/LoaderSpot/deb-builds/releases/download/builds/spotify-client_1.2.84.476.ga1ff6607_amd64.deb"
|
||||
}
|
||||
},
|
||||
"1.2.82.428": {
|
||||
"fullversion": "1.2.82.428.g0ac8be2b",
|
||||
"size": "156375346",
|
||||
"data": "09.02.2026",
|
||||
"links": {
|
||||
"amd64": "https://github.com/LoaderSpot/deb-builds/releases/download/builds/spotify-client_1.2.82.428.g0ac8be2b_amd64.deb"
|
||||
}
|
||||
},
|
||||
"1.2.79.427": {
|
||||
"fullversion": "1.2.79.427.g80eb4a07",
|
||||
"size": "155456648",
|
||||
"data": "12.01.2026",
|
||||
"links": {
|
||||
"amd64": "https://github.com/LoaderSpot/deb-builds/releases/download/builds/spotify-client_1.2.79.427.g80eb4a07_amd64.deb"
|
||||
}
|
||||
},
|
||||
"1.2.79.425": {
|
||||
"fullversion": "1.2.79.425.g1d0fcf61",
|
||||
"size": "155381166",
|
||||
"data": "17.12.2025",
|
||||
"links": {
|
||||
"amd64": "https://github.com/LoaderSpot/deb-builds/releases/download/builds/spotify-client_1.2.79.425.g1d0fcf61_amd64.deb"
|
||||
}
|
||||
},
|
||||
"1.2.77.358": {
|
||||
"fullversion": "1.2.77.358.g4339a634",
|
||||
"size": "155510286",
|
||||
"data": "25.11.2025",
|
||||
"links": {
|
||||
"amd64": "https://github.com/LoaderSpot/deb-builds/releases/download/builds/spotify-client_1.2.77.358.g4339a634_amd64.deb"
|
||||
}
|
||||
},
|
||||
"1.2.74.477": {
|
||||
"fullversion": "1.2.74.477.g3be53afe",
|
||||
"size": "154653044",
|
||||
|
|
|
|||
Loading…
Reference in a new issue