A photo manager for VRChat.

small bugfix

phaz.uk d45066b6 23c16064

verified
+5 -1
changelog
··· 117 117 - Added update available prompt 118 118 119 119 - Remove all sync stuff 120 - - Removed automatic updates 120 + - Removed automatic updates 121 + 122 + v0.2.7: 123 + - Fixed loading when an image file is corrupted 124 + - Fixed update prompt when not connected to internet
+1 -1
src-tauri/Cargo.lock
··· 4 4 5 5 [[package]] 6 6 name = "VRChatPhotoManager" 7 - version = "0.2.6" 7 + version = "0.2.7" 8 8 dependencies = [ 9 9 "arboard", 10 10 "dirs 5.0.1",
+1 -1
src-tauri/Cargo.toml
··· 1 1 [package] 2 2 name = "VRChatPhotoManager" 3 - version = "0.2.6" 3 + version = "0.2.7" 4 4 description = "VRChat Photo Manager" 5 5 authors = ["_phaz"] 6 6 edition = "2021"
+21 -3
src-tauri/src/frontend_calls/load_photo_meta.rs
··· 20 20 let mut buffer = Vec::new(); 21 21 22 22 let _out = file.read_to_end(&mut buffer); 23 - window 24 - .emit("photo_meta_loaded", PNGImage::new(buffer, photo)) 25 - .unwrap(); 23 + let png = PNGImage::new(buffer, photo.clone()); 24 + 25 + if png.is_err(){ 26 + println!("Failed to load: {} => {}", base_dir, png.unwrap_err()); 27 + 28 + window.emit("photo_meta_loaded", PNGImage { 29 + width: 1920, 30 + height: 1080, 31 + bit_depth: 0, 32 + colour_type: 0, 33 + compression_method: 0, 34 + filter_method: 0, 35 + interlace_method: 0, 36 + metadata: "".into(), 37 + error: true, 38 + path: photo 39 + }).unwrap(); 40 + } else{ 41 + println!("Loaded: {}", base_dir); 42 + window.emit("photo_meta_loaded", png.unwrap()).unwrap(); 43 + } 26 44 } 27 45 Err(_) => { 28 46 println!("Cannot read image file: {:?}", base_dir);
+8 -24
src-tauri/src/pngmeta.rs
··· 1 - use serde::ser::{Serialize, SerializeStruct, Serializer}; 2 1 use std::str; 3 2 4 - #[derive(Clone)] 3 + use serde::Serialize; 4 + 5 + #[derive(Clone, Debug, Serialize)] 5 6 pub struct PNGImage { 6 7 pub width: u32, 7 8 pub height: u32, ··· 12 13 pub interlace_method: u8, 13 14 pub metadata: String, 14 15 pub path: String, 16 + pub error: bool 15 17 } 16 18 17 19 impl PNGImage { 18 - pub fn new(buff: Vec<u8>, path: String) -> PNGImage { 20 + pub fn new(buff: Vec<u8>, path: String) -> Result<PNGImage, &'static str> { 19 21 if buff[0] != 0x89 20 22 || buff[1] != 0x50 21 23 || buff[2] != 0x4E ··· 26 28 || buff[7] != 0x0A 27 29 { 28 30 dbg!(path); 29 - panic!("Image is not a PNG file"); 31 + return Err("Image is not a PNG file"); 30 32 } 31 33 32 34 let mut img = PNGImage { ··· 39 41 interlace_method: 0, 40 42 metadata: "".to_string(), 41 43 path: path, 44 + error: false 42 45 }; 43 46 44 47 img.read_png_chunk(8, buff); 45 - img 48 + Ok(img) 46 49 } 47 50 48 51 fn read_png_chunk(&mut self, start_byte: usize, buff: Vec<u8>) { ··· 82 85 } 83 86 } 84 87 } 85 - 86 - impl Serialize for PNGImage { 87 - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 88 - where 89 - S: Serializer, 90 - { 91 - let mut s = serializer.serialize_struct("PNGImage", 7)?; 92 - s.serialize_field("width", &self.width)?; 93 - s.serialize_field("height", &self.height)?; 94 - s.serialize_field("bit_depth", &self.bit_depth)?; 95 - s.serialize_field("colour_type", &self.colour_type)?; 96 - s.serialize_field("compression_method", &self.compression_method)?; 97 - s.serialize_field("filter_method", &self.filter_method)?; 98 - s.serialize_field("interlace_method", &self.interlace_method)?; 99 - s.serialize_field("metadata", &self.metadata)?; 100 - s.serialize_field("path", &self.path)?; 101 - s.end() 102 - } 103 - }
+11 -1
src/Components/Managers/PhotoListRenderingManager.tsx
··· 127 127 // ctx.strokeStyle = '#f00'; 128 128 // ctx.strokeRect((rowXPos - row.Width / 2) + canvas.width / 2, currentY - scroll, photo.scaledWidth!, row.Height); 129 129 130 - if(!photo.loaded) 130 + if(photo.error){ 131 + ctx.fillStyle = '#fff'; 132 + ctx.textAlign = 'center'; 133 + ctx.textBaseline = 'middle'; 134 + 135 + photo.x = (rowXPos - row.Width / 2) + canvas.width / 2; 136 + photo.y = currentY - scroll; 137 + 138 + ctx.font = '18px Rubik' 139 + ctx.fillText('Error loading image.', photo.x + photo.scaledWidth! / 2, photo.y + photo.scaledHeight! / 2); 140 + } else if(!photo.loaded) 131 141 // If the photo is not loaded, start a new task and load it in that task 132 142 setTimeout(() => photo.loadImage(), 1); 133 143 else{
+3 -1
src/Components/Managers/PhotoManager.tsx
··· 112 112 // we don't need to store metadata of those photos as they inherit this 113 113 // data from the main photo. 114 114 115 + photo.error = data.error; 115 116 this._lastLoaded = photo.index; 116 117 117 118 if(this._onLoadedMeta[photo.index]){ ··· 129 130 130 131 photo.metadata = data.metadata.split('\u0000').filter(x => x !== '')[1]; 131 132 this._amountLoaded++; 132 - 133 + 134 + photo.loadingMeta = false; 133 135 photo.metaLoaded = true; 134 136 photo.onMetaLoaded(); 135 137
+11 -4
src/Components/PhotoList.tsx
··· 162 162 onMount(() => { 163 163 // Update Stuff 164 164 fetch('https://api.github.com/repos/phaze-the-dumb/VRChat-Photo-Manager/releases/latest') 165 - .then(data => data.json()) 166 - .then(async data => { 167 - let currentVersion = await invoke('get_version'); 168 - setUpdateAvailable(data.tag_name !== currentVersion); 165 + .then(data => { 166 + if(data.status !== 200)return; 167 + 168 + data.json().then(async data => { 169 + let currentVersion = await invoke('get_version'); 170 + setUpdateAvailable(data.tag_name !== currentVersion); 171 + }) 172 + }) 173 + .catch(e => { 174 + console.error(e); 175 + setUpdateAvailable(false); 169 176 }) 170 177 171 178 // Other Stuff
+9 -4
src/Components/Structs/Photo.ts
··· 6 6 export class Photo{ 7 7 path: string; 8 8 loaded: boolean = false; 9 + loadingMeta: boolean = false; 9 10 loading: boolean = false; 10 11 metaLoaded: boolean = false; 11 12 image?: HTMLCanvasElement; ··· 14 15 height?: number; 15 16 loadingRotate: number = 0; 16 17 metadata: any; 18 + 19 + error: boolean = false; 17 20 18 21 frames: number = 0; 19 22 shown: boolean = false; ··· 90 93 } 91 94 92 95 loadMeta(){ 96 + this.loadingMeta = true; 93 97 invoke('load_photo_meta', { photo: this.path }); 94 98 } 95 99 96 100 loadImage(){ 97 - if(this.loading || this.loaded || imagesLoading >= Vars.MAX_IMAGE_LOAD)return; 98 - 99 - // this.loadMeta(); 101 + if(this.loadingMeta || this.loading || this.loaded || imagesLoading >= Vars.MAX_IMAGE_LOAD)return; 100 102 if(!this.metaLoaded)return this.loadMeta(); 101 103 102 104 this.loading = true; 103 - 104 105 imagesLoading++; 105 106 106 107 this.image = document.createElement('canvas'); ··· 120 121 this.loading = false; 121 122 122 123 imagesLoading--; 124 + } 125 + 126 + this.imageEl.onerror = () => { 127 + console.log('Cannot load image'); 123 128 } 124 129 } 125 130 }
+1
src/Components/Structs/PhotoMetadata.ts
··· 3 3 height!: number; 4 4 metadata!: string; 5 5 path!: string; 6 + error!: boolean; 6 7 }