downloading and extracting

This commit is contained in:
SpikeHD
2022-07-24 18:14:18 -07:00
parent 975b04fd0e
commit 36c2302f1b
13 changed files with 143 additions and 28 deletions

View File

@@ -8,6 +8,13 @@ use std::path::PathBuf;
static API_URL: &str = "https://api.gamebanana.com";
static SITE_URL: &str = "https://gamebanana.com";
#[tauri::command]
pub async fn get_download_links(modId: String) -> String {
let res = web::query(format!("{}/apiv9/Mod/{}/DownloadPage", SITE_URL, modId).as_str()).await;
res
}
#[tauri::command]
pub async fn list_submissions(mode: String) -> String {
let res = web::query(

View File

@@ -60,6 +60,7 @@ fn main() {
lang::get_languages,
web::valid_url,
web::web_get,
gamebanana::get_download_links,
gamebanana::list_submissions,
gamebanana::list_mods,
metadata_patcher::patch_metadata

View File

@@ -3,7 +3,7 @@ use std::path;
use std::thread;
#[tauri::command]
pub fn unzip(window: tauri::Window, zipfile: String, destpath: String) {
pub fn unzip(window: tauri::Window, zipfile: String, destpath: String, top_level: Option<bool>) {
// Read file TODO: replace test file
let f = match File::open(&zipfile) {
Ok(f) => f,
@@ -21,7 +21,7 @@ pub fn unzip(window: tauri::Window, zipfile: String, destpath: String) {
window.emit("extract_start", &zipfile).unwrap();
match zip_extract::extract(&f, &full_path, true) {
match zip_extract::extract(&f, &full_path, top_level.unwrap_or(false)) {
Ok(_) => {
println!(
"Extracted zip file to: {}",

View File

@@ -3,3 +3,17 @@
height: 90%;
width: 100%;
}
/* Stuff for the top bar progress bar */
.TopDownloads {
position: absolute;
top: 10px;
left: 35.5%;
width: 30%;
}
.TopDownloads .ProgressBar {
width: 100%;
height: 10px;
}

View File

@@ -1,6 +1,9 @@
import React from 'react'
import DownloadHandler from '../utils/download'
import { ModData } from '../utils/gamebanana'
import { getModDownload, ModData } from '../utils/gamebanana'
import { getModsFolder } from '../utils/mods'
import { unzip } from '../utils/zipUtils'
import ProgressBar from './components/common/MainProgressBar'
import { ModHeader } from './components/mods/ModHeader'
import { ModList } from './components/mods/ModList'
import TopBar from './components/TopBar'
@@ -40,7 +43,12 @@ export class Mods extends React.Component<IProps, IState> {
category: '',
}
setInterval(() => {
console.log(this.props.downloadHandler.downloads)
}, 5000)
this.setCategory = this.setCategory.bind(this)
this.addDownload = this.addDownload.bind(this)
}
async componentDidMount() {
@@ -49,6 +57,22 @@ export class Mods extends React.Component<IProps, IState> {
async addDownload(mod: ModData) {
console.log('Downloading:', mod.name)
const modFolder = await getModsFolder()
const modPath = `${modFolder}${mod.id}.zip`
const dlLinks = await getModDownload(String(mod.id))
if (!modFolder || dlLinks.length === 0) return
// Not gonna bother allowing sorting for now
const firstLink = dlLinks[0].downloadUrl
this.props.downloadHandler.addDownload(firstLink, modPath, async () => {
console.log('Unzipping:', mod.name)
unzip(modPath, modFolder, false, () => {
console.log('DONE MOD DOWNLOAD')
})
})
}
async setCategory(value: string) {
@@ -65,6 +89,10 @@ export class Mods extends React.Component<IProps, IState> {
<div className="Mods">
<TopBar />
<div className="TopDownloads">
<ProgressBar downloadManager={this.props.downloadHandler} />
</div>
<ModHeader onChange={this.setCategory} headers={headers} defaultHeader={'ripe'} />
<ModList key={this.state.category} mode={this.state.category} addDownload={this.addDownload} />

View File

@@ -1,13 +1,12 @@
import React from 'react'
import { app } from '@tauri-apps/api'
import { appWindow } from '@tauri-apps/api/window'
import closeIcon from '../../resources/icons/close.svg'
import minIcon from '../../resources/icons/min.svg'
import { getConfig, setConfigOption } from '../../utils/configuration'
import Tr from '../../utils/language'
import './TopBar.css'
import { getConfig, setConfigOption } from '../../utils/configuration'
import closeIcon from '../../resources/icons/close.svg'
import minIcon from '../../resources/icons/min.svg'
interface IProps {
children?: React.ReactNode[]

View File

@@ -5,6 +5,7 @@ import './ProgressBar.css'
interface IProps {
downloadManager: DownloadHandler
withStats?: boolean
}
interface IState {
@@ -70,11 +71,13 @@ export default class ProgressBar extends React.Component<IProps, IState> {
></div>
</div>
<div className="MainProgressText">
<Tr text="main.files_downloading" /> {this.state.files} ({this.state.speed})
<br />
<Tr text="main.files_extracting" /> {this.state.extracting}
</div>
{this.props.withStats && (
<div className="MainProgressText">
<Tr text="main.files_downloading" /> {this.state.files} ({this.state.speed})
<br />
<Tr text="main.files_extracting" /> {this.state.extracting}
</div>
)}
</div>
)
}

View File

@@ -112,7 +112,7 @@ export default class Downloads extends React.Component<IProps, IState> {
async downloadGrasscutterStableRepo() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(STABLE_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', () => {
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', this.toggleButtons)
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', true, this.toggleButtons)
})
this.toggleButtons()
@@ -121,7 +121,7 @@ export default class Downloads extends React.Component<IProps, IState> {
async downloadGrasscutterDevRepo() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(DEV_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', () => {
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', this.toggleButtons)
unzip(folder + '\\grasscutter_repo.zip', folder + '\\', true, this.toggleButtons)
})
this.toggleButtons()
@@ -130,7 +130,7 @@ export default class Downloads extends React.Component<IProps, IState> {
async downloadGrasscutterStable() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(STABLE_DOWNLOAD, folder + '\\grasscutter.zip', () => {
unzip(folder + '\\grasscutter.zip', folder + '\\', this.toggleButtons)
unzip(folder + '\\grasscutter.zip', folder + '\\', true, this.toggleButtons)
})
// Also add repo download
@@ -142,7 +142,7 @@ export default class Downloads extends React.Component<IProps, IState> {
async downloadGrasscutterLatest() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(DEV_DOWNLOAD, folder + '\\grasscutter.zip', () => {
unzip(folder + '\\grasscutter.zip', folder + '\\', this.toggleButtons)
unzip(folder + '\\grasscutter.zip', folder + '\\', true, this.toggleButtons)
})
// Also add repo download
@@ -165,7 +165,7 @@ export default class Downloads extends React.Component<IProps, IState> {
})
}
await unzip(folder + '\\resources.zip', folder + '\\', () => {
await unzip(folder + '\\resources.zip', folder + '\\', true, () => {
// Rename folder to resources
invoke('rename', {
path: folder + '\\Resources',

View File

@@ -46,7 +46,7 @@ export default class Downloads extends React.Component<IProps, IState> {
async downloadGame() {
const folder = this.state.gameDownloadFolder
this.props.downloadManager.addDownload(GAME_DOWNLOAD, folder + '\\game.zip', () => {
unzip(folder + '\\game.zip', folder + '\\', () => {
unzip(folder + '\\game.zip', folder + '\\', true, () => {
this.setState({
gameDownloading: false,
})

View File

@@ -24,8 +24,6 @@ export class ModList extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props)
console.log('Getting')
this.state = {
modList: null,
installedList: null,

View File

@@ -2,7 +2,7 @@ import { invoke } from '@tauri-apps/api'
import { getConfigOption } from './configuration'
// Generated with https://transform.tools/json-to-typescript I'm lazy cry about it
export interface GamebananaResponse {
interface GamebananaResponse {
_idRow: number
_sModelName: string
_sSingularTitle: string
@@ -23,12 +23,12 @@ export interface GamebananaResponse {
_tsDateUpdated?: number
}
export interface PreviewMedia {
interface PreviewMedia {
_aImages?: Image[]
_aMetadata?: Metadata
}
export interface Image {
interface Image {
_sType: string
_sBaseUrl: string
_sFile: string
@@ -38,14 +38,14 @@ export interface Image {
_sCaption?: string
}
export interface Metadata {
interface Metadata {
_sState?: string
_sSnippet: string
_nPostCount?: number
_nBounty?: number
}
export interface Submitter {
interface Submitter {
_idRow: number
_sName: string
_bIsOnline: boolean
@@ -57,7 +57,7 @@ export interface Submitter {
_sHdAvatarUrl?: string
}
export interface RootCategory {
interface RootCategory {
_sName: string
_sProfileUrl: string
_sIconUrl: string
@@ -78,6 +78,35 @@ export interface ModData {
type: string
}
interface GamebananaDownloads {
_bIsTrashed: boolean
_bIsWithheld: boolean
_aFiles: File[]
_sLicense: string
}
interface File {
_idRow: number
_sFile: string
_nFilesize: number
_sDescription: string
_tsDateAdded: number
_nDownloadCount: number
_sAnalysisState: string
_sDownloadUrl: string
_sMd5Checksum: string
_sClamAvResult: string
_sAnalysisResult: string
_bContainsExe: boolean
}
interface ModDownload {
filename: string
downloadUrl: string
filesize: number
containsExe: boolean
}
export async function getMods(mode: string) {
const resp = JSON.parse(
await invoke('list_submissions', {
@@ -134,3 +163,26 @@ export async function getInstalledMods() {
}
})
}
export async function getModDownload(modId: string) {
const resp = JSON.parse(
await invoke('get_download_links', {
modId,
})
) as GamebananaDownloads
return formatDownloadsData(resp)
}
export async function formatDownloadsData(obj: GamebananaDownloads) {
if (!obj) return []
return obj._aFiles.map((itm) => {
return {
filename: itm._sFile,
downloadUrl: `https://files.gamebanana.com/mods/${itm._sFile}`,
filesize: itm._nFilesize,
containsExe: itm._bContainsExe,
} as ModDownload
})
}

View File

@@ -1 +1,13 @@
export {}
import { getConfigOption } from './configuration'
export async function getModsFolder() {
const migotoPath = await getConfigOption('migoto_path')
if (!migotoPath) return null
// Remove exe from path
const pathArr = migotoPath.replace(/\\/g, '/').split('/')
pathArr.pop()
return pathArr.join('/') + '/Mods/'
}

View File

@@ -1,10 +1,11 @@
import { invoke } from '@tauri-apps/api'
import { listen } from '@tauri-apps/api/event'
export function unzip(file: string, dest: string, onFinish?: () => void) {
export function unzip(file: string, dest: string, topLevelStrip?: boolean, onFinish?: () => void) {
invoke('unzip', {
zipfile: file,
destpath: dest,
topLevelStrip,
})
listen('extract_end', ({ payload }) => {