Skip to content

Commit

Permalink
0.5.8 - Fix Custom Paths
Browse files Browse the repository at this point in the history
  • Loading branch information
ShayBox committed Apr 25, 2024
1 parent 108a72f commit c17c923
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 125 deletions.
167 changes: 86 additions & 81 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "vrc-log"
version = "0.5.7"
version = "0.5.8"
authors = ["Shayne Hartford <[email protected]>"]
edition = "2021"
description = "VRChat Local Cache Avatar ID Logger"
Expand Down Expand Up @@ -28,7 +28,7 @@ regex = "1"
reqwest = { version = "0.12", features = ["blocking", "json"], optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlite = { version = "0.34", features = ["bundled"], optional = true }
sqlite = { version = "0.36", features = ["bundled"], optional = true }
strum = { version = "0.26", features = ["derive"] }
terminal-link = "0.1"

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ VRChat Local Cache Avatar ID Logger
This project does **NOT** rip or steal avatars, it just scans your local cache for avatar ids and sends them to avatar database providers

### Steam Support
This program supports Steam Launch Options
Place the file within the VRChat directory and set your launch options
`vrc-log.exe %command%` or `/home/.../.cargo/bin/vrc-log %command%`
This program supports Steam Launch Options (headless)
Place the file in the VRChat directory or PATH and set your launch options
`vrc-log(.exe) %command%`

### VRCX Support
This program prints [VRCX] avatar links when a new (to you) avatar is found
Expand All @@ -31,7 +31,7 @@ You can place a **shortcut** to this program within the [VRCX] Auto-Launch Folde

Additional providers are welcome, please open an issue, pull request, or join Discord

[Avatar Search]: ttps://sites.smokes-hub.de
[Avatar Search]: https://sites.smokes-hub.de
[Just H Party (Web & VRCX)]: https://avtr.just-h.party
[Prismic's Avatar Search (World)]: https://vrchat.com/home/world/wrld_57514404-7f4e-4aee-a50a-57f55d3084bf
[VRCX]: https://github.com/vrcx-team/VRCX?tab=readme-ov-file#--vrcx
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ use regex::{Captures, Regex};

use crate::provider::{prelude::*, Providers, Type};

pub mod config;
#[cfg(feature = "discord")]
pub mod discord;
pub mod provider;
pub mod vrchat;

pub const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const CARGO_PKG_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
Expand Down Expand Up @@ -168,13 +168,13 @@ pub fn process_avatars((_tx, rx, _): WatchResponse) -> anyhow::Result<()> {
///
/// Will panic if an environment variable doesn't exist
pub fn parse_path_env(haystack: &str) -> Result<PathBuf, Error> {
lazy_static! {
static ref RE: Regex = Regex::new(r"%(\w+)%").unwrap();
lazy_static! { // This is the best regex I could come up with
static ref RE: Regex = Regex::new(r"(\$|%)(\w+)%?").unwrap();
}

let str = RE.replace_all(haystack, |captures: &Captures| {
let key = &captures[1];
std::env::var(key).expect("Environment Variable not found")
let key = &captures[2];
std::env::var(key).unwrap_or_else(|_| panic!("Environment Variable not found: {key}"))
});
let path = std::fs::canonicalize(str.as_ref())?;

Expand Down
9 changes: 5 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
#[cfg(feature = "title")]
use crossterm::{execute, terminal::SetTitle};
use terminal_link::Link;
use vrc_log::{config::VRChat, CARGO_PKG_HOMEPAGE};
use vrc_log::{vrchat::VRChat, CARGO_PKG_HOMEPAGE};

fn main() -> anyhow::Result<()> {
#[cfg(feature = "title")]
execute!(std::io::stdout(), SetTitle("VRC-LOG"))?;

if vrc_log::check_for_updates()? {
let link = Link::new("An update is available", CARGO_PKG_HOMEPAGE);
let text = "An update is available";
let link = Link::new(text, CARGO_PKG_HOMEPAGE);
println!("{link}");
}

let args = std::env::args();
let config = VRChat::load()?;
let watcher = vrc_log::watch(config.cache_directory)?;
let vrchat = VRChat::load()?;
let watcher = vrc_log::watch(vrchat.cache_directory)?;

vrc_log::launch_game(args)?;
vrc_log::process_avatars(watcher)
Expand Down
16 changes: 13 additions & 3 deletions src/provider/sqlite.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use anyhow::Result;
use sqlite::Connection;

use crate::{config::DEFAULT_PATH, provider::Provider};
use crate::{
provider::{Provider, Type},
vrchat::VRCHAT_PATH,
};

pub struct Sqlite {
connection: Connection,
Expand All @@ -12,12 +15,19 @@ impl Sqlite {
///
/// Will return `Err` if `sqlite::open` errors
pub fn new() -> Result<Self> {
let path = DEFAULT_PATH.join("avatars.sqlite");
let path = VRCHAT_PATH.join("avatars.sqlite");
let connection = sqlite::open(path)?;

// Create the table if it doesn't exist
let query = "CREATE TABLE avatars (id TEXT PRIMARY KEY)";
let _ = connection.execute(query);
if connection.execute(query).is_err() {
// Print cache statistics
let query = "SELECT * FROM avatars";
if let Ok(statement) = connection.prepare(query) {
let rows = statement.into_iter().filter_map(Result::ok);
println!("[{}] {} Cached Avatars", Type::Cache, rows.count());
}
}

Ok(Self { connection })
}
Expand Down
7 changes: 5 additions & 2 deletions src/provider/vrcdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub struct VRCDB {
impl VRCDB {
#[must_use]
pub const fn new(client: Client, userid: String) -> Self {
// TODO: Print VRCDB Statistics
// Waiting on VRCDB Leaderboard

Self { client, userid }
}

Expand All @@ -32,7 +35,7 @@ impl VRCDB {
eprintln!("2. VRC-LOG was restarted too quickly.\n");
eprintln!("The User ID will default to the developer: ShayBox");

DEVELOPER_ID.to_owned()
std::env::var("DISCORD").unwrap_or_else(|_| DEVELOPER_ID.to_owned())
}
}

Expand All @@ -45,7 +48,7 @@ impl Default for VRCDB {
eprintln!("Vesktop & arRPC do not support fetching user info.");
eprintln!("The User ID will default to the developer: ShayBox");

DEVELOPER_ID.to_owned()
std::env::var("DISCORD").unwrap_or_else(|_| DEVELOPER_ID.to_owned())
} else {
if let Some(username) = user.username {
println!("[{}] Authenticated as {username}", Type::VRCDB);
Expand Down
56 changes: 32 additions & 24 deletions src/config.rs → src/vrchat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,52 @@ use std::{
path::PathBuf,
};

use anyhow::Context;
use serde::{Deserialize, Deserializer, Serialize};

#[cfg(target_os = "windows")]
const DEFAULT: &str = "%AppData%\\..\\LocalLow\\VRChat\\VRChat";
const VRCHAT: &str = "%AppData%\\..\\LocalLow\\VRChat\\VRChat";

#[cfg(target_os = "linux")]
const DEFAULT: &str = "%HOME%/.local/share/Steam/steamapps/compatdata/438100/pfx/drive_c/users/steamuser/AppData/LocalLow/VRChat/VRChat";
const VRCHAT: &str = "$HOME/.local/share/Steam/steamapps/compatdata/438100/pfx/drive_c/users/steamuser/AppData/LocalLow/VRChat/VRChat";

lazy_static::lazy_static! {
pub static ref DEFAULT_PATH: PathBuf = crate::parse_path_env(DEFAULT).unwrap();
/// This is a static path and cannot be changed (without symlinks)
pub static ref VRCHAT_PATH: PathBuf = crate::parse_path_env(VRCHAT).unwrap();
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct VRChat {
/// This is actually the path to the cache parent directory
/// VRChat doesn't allow you to change the cache directory name
/// The `Cache-WindowsPlayer` path is appended during deserialization below
#[serde(deserialize_with = "deserialize")]
pub cache_directory: PathBuf,
}

impl Default for VRChat {
fn default() -> Self {
Self {
cache_directory: DEFAULT_PATH.join("Cache-WindowsPlayer"),
}
}
/// Try to deserialize the `VRChat` `config.json` `cache_directory`, `parse_path_env`, and append `Cache-WindowsPlayer`
///
/// # Errors
///
/// Will return `Err` if `crate::parse_path_env` errors
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<PathBuf, D::Error> {
let haystack = String::deserialize(deserializer)?;
let path = crate::parse_path_env(&haystack)
.context("Failed to parse the default path")
.map_err(serde::de::Error::custom)?
.join("Cache-WindowsPlayer");

Ok(path)
}

impl VRChat {
#[must_use]
pub fn get_path() -> PathBuf {
DEFAULT_PATH.join("config.json")
VRCHAT_PATH.join("config.json")
}

/// Try to load the `VRChat` `config.json` file for the `cache_directory` field
///
/// # Errors
///
/// Will return `Err` if `File::open`, `File::read_to_string`, or `File::rewind` errors
Expand All @@ -51,22 +66,15 @@ impl VRChat {
file.read_to_string(&mut text)?;
file.rewind()?;

// Fallback to default below if config fails to deserialize
serde_json::from_str(&text).map_or_else(|_| Ok(Self::default()), Ok)
}
}

/// # Errors
///
/// Will never return `Err`
///
/// # Panics
///
/// Will panic if `crate::parse_path_env` errors
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<PathBuf, D::Error> {
let str = Deserialize::deserialize(deserializer).unwrap_or(DEFAULT);
let path = crate::parse_path_env(str)
.expect("Failed to parse the default path")
.join("Cache-WindowsPlayer");

Ok(path)
impl Default for VRChat {
fn default() -> Self {
Self {
cache_directory: VRCHAT_PATH.join("Cache-WindowsPlayer"),
}
}
}

0 comments on commit c17c923

Please sign in to comment.