Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions psst-core/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ pub struct Cache {

fn create_cache_dirs(base: &Path) -> io::Result<()> {
mkdir_if_not_exists(base)?;
mkdir_if_not_exists(&base.join("track"))?;
mkdir_if_not_exists(&base.join("episode"))?;
mkdir_if_not_exists(&base.join("tracks"))?;
mkdir_if_not_exists(&base.join("episodes"))?;
mkdir_if_not_exists(&base.join("audio"))?;
mkdir_if_not_exists(&base.join("key"))?;
mkdir_if_not_exists(&base.join("keys"))?;
Ok(())
}

Expand Down Expand Up @@ -71,7 +71,7 @@ impl Cache {
}

fn track_path(&self, item_id: ItemId) -> PathBuf {
self.base.join("track").join(item_id.to_base62())
self.base.join("tracks").join(item_id.to_base62())
}
}

Expand All @@ -89,7 +89,7 @@ impl Cache {
}

fn episode_path(&self, item_id: ItemId) -> PathBuf {
self.base.join("episode").join(item_id.to_base62())
self.base.join("episodes").join(item_id.to_base62())
}
}

Expand All @@ -115,7 +115,7 @@ impl Cache {
let mut key_id = String::new();
key_id += &item_id.to_base62()[..16];
key_id += &file_id.to_base16()[..16];
self.base.join("key").join(key_id)
self.base.join("keys").join(key_id)
}
}

Expand Down
18 changes: 9 additions & 9 deletions psst-gui/src/data/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use std::{
#[cfg(target_family = "unix")]
use std::os::unix::fs::OpenOptionsExt;

use directories::ProjectDirs;
use druid::{Data, Lens, Size};
use platform_dirs::AppDirs;
use psst_core::{
cache::{mkdir_if_not_exists, CacheHandle},
connection::Credentials,
Expand Down Expand Up @@ -153,32 +153,32 @@ impl Default for Config {
}

impl Config {
fn app_dirs() -> Option<AppDirs> {
const USE_XDG_ON_MACOS: bool = false;

AppDirs::new(Some(APP_NAME), USE_XDG_ON_MACOS)
fn app_dirs() -> Option<ProjectDirs> {
ProjectDirs::from("", "", APP_NAME)
}

pub fn spotify_local_files_file(username: &str) -> Option<PathBuf> {
AppDirs::new(Some("spotify"), false).map(|dir| {
ProjectDirs::from("", "", "spotify").map(|dir| {
let path = format!("Users/{username}-user/local-files.bnk");
dir.config_dir.join(path)
dir.config_dir().join(path)
})
}

pub fn cache_dir() -> Option<PathBuf> {
Self::app_dirs().map(|dirs| dirs.cache_dir)
Self::app_dirs().map(|dirs| dirs.cache_dir().to_path_buf())
}

pub fn config_dir() -> Option<PathBuf> {
Self::app_dirs().map(|dirs| dirs.config_dir)
Self::app_dirs().map(|dirs| dirs.config_dir().to_path_buf())
}

fn config_path() -> Option<PathBuf> {
Self::config_dir().map(|dir| dir.join(CONFIG_FILENAME))
}

pub fn load() -> Option<Config> {
// To be removed... at some point soon!
crate::data::migration::perform_migration();
let path = Self::config_path().expect("Failed to get config path");
if let Ok(file) = File::open(&path) {
log::info!("loading config: {:?}", &path);
Expand Down
109 changes: 109 additions & 0 deletions psst-gui/src/data/migration.rs
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the file could be renamed, just in case. What if there is going to be a migration of another kind in the future, how are we going to differentiate it to this migration? IMO we can afford to call this file something verbose like migration_paths_to_use_directories.rs since we aim to remove this some time in the future eventually.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Hmm, I renamed legacy which might be enough? Everything in that module should eventually go away.

Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::{fs, path::Path};

use directories::ProjectDirs;
use platform_dirs::AppDirs;

use crate::data::config::Config;

const APP_NAME: &str = "Psst";

pub fn perform_migration() {
let old_app_dirs = AppDirs::new(Some(APP_NAME), false);
let new_project_dirs = ProjectDirs::from("", "", APP_NAME);

if let (Some(old_dirs), Some(new_dirs)) = (old_app_dirs, new_project_dirs) {
migrate_path(
&old_dirs.config_dir,
new_dirs.config_dir(),
&["config.json"],
);
migrate_path(
&old_dirs.cache_dir,
new_dirs.cache_dir(),
&[
"tracks",
"track",
"episodes",
"episode",
"audio",
"keys",
"key",
"lyrics",
"images",
"artist-info",
"related-artists",
"album",
"show",
"user-info",
],
Comment on lines +37 to +38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"user-info",
],
"user-info",
"country_code",
],

Country code is cached by psst-core... Honestly it feels weird that it's a tiny 2B file cached like this, but that's a story for another day.

);
}

if let Some(active_cache_dir) = Config::cache_dir() {
rename_cache_subdirectories(&active_cache_dir);
}
}

fn migrate_path(old_dir: &Path, new_dir: &Path, items_to_move: &[&str]) {
if old_dir == new_dir || !old_dir.exists() {
return;
}

// New path is a subdirectory of the old path (e.g. .../Psst/config vs .../Psst).
// We must move specific items into the new subdirectory to avoid recursion.
if new_dir.starts_with(old_dir) {
log::info!(
"migrating content from {:?} into subdirectory {:?}",
old_dir,
new_dir
);
if let Err(err) = fs::create_dir_all(new_dir) {
log::error!("failed to create directory {:?}: {}", new_dir, err);
return;
}

for &item in items_to_move {
let old_item = old_dir.join(item);
let new_item = new_dir.join(item);
move_if_exists(&old_item, &new_item);
}
} else if !new_dir.exists() {
log::info!("migrating directory from {:?} to {:?}", old_dir, new_dir);
if let Some(parent) = new_dir.parent() {
let _ = fs::create_dir_all(parent);
}
if let Err(err) = fs::rename(old_dir, new_dir) {
log::error!("failed to migrate directory: {}", err);
}
}
}

fn rename_cache_subdirectories(cache_dir: &Path) {
if !cache_dir.exists() {
return;
}

let renames = [
("track", "tracks"),
("episode", "episodes"),
("show", "shows"),
("album", "albums"),
("artist", "artists"),
("key", "keys"),
];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming is a good idea


for (old_name, new_name) in renames {
let old_path = cache_dir.join(old_name);
let new_path = cache_dir.join(new_name);
move_if_exists(&old_path, &new_path);
}
}

fn move_if_exists(from: &Path, to: &Path) {
if from.exists() && !to.exists() {
log::info!("moving {:?} to {:?}", from, to);
if let Err(err) = fs::rename(from, to) {
log::error!("failed to move {:?}: {}", from, err);
}
}
}
1 change: 1 addition & 0 deletions psst-gui/src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod config;
mod ctx;
mod find;
mod id;
mod migration;
mod nav;
mod playback;
mod playlist;
Expand Down
32 changes: 13 additions & 19 deletions psst-gui/src/webapi/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use parking_lot::Mutex;
use serde::{de::DeserializeOwned, Deserialize};
use serde_json::json;

use psst_core::session::{SessionService};
use psst_core::session::SessionService;
use ureq::{
http::{Response, StatusCode},
Agent, Body,
Expand All @@ -39,9 +39,9 @@ use crate::{
};

use super::{cache::WebApiCache, local::LocalTrackManager};
use psst_core::session::login5::Login5;
use sanitize_html::rules::predefined::DEFAULT;
use sanitize_html::sanitize_str;
use psst_core::session::login5::Login5;

pub struct WebApi {
session: SessionService,
Expand Down Expand Up @@ -711,7 +711,7 @@ impl WebApi {
// https://developer.spotify.com/documentation/web-api/reference/get-artist/
pub fn get_artist(&self, id: &str) -> Result<Artist, Error> {
let request = &RequestBuilder::new(format!("v1/artists/{id}"), Method::Get, None);
let result = self.load_cached(request, "artist", id)?;
let result = self.load_cached(request, "artists", id)?;
Ok(result.data)
}

Expand Down Expand Up @@ -909,7 +909,7 @@ impl WebApi {
pub fn get_album(&self, id: &str) -> Result<Cached<Arc<Album>>, Error> {
let request = &RequestBuilder::new(format!("v1/albums/{id}"), Method::Get, None)
.query("market", "from_token");
let result = self.load_cached(request, "album", id)?;
let result = self.load_cached(request, "albums", id)?;
Ok(result)
}
}
Expand All @@ -921,7 +921,7 @@ impl WebApi {
let request = &RequestBuilder::new(format!("v1/shows/{id}"), Method::Get, None)
.query("market", "from_token");

let result = self.load_cached(request, "show", id)?;
let result = self.load_cached(request, "shows", id)?;

Ok(result)
}
Expand Down Expand Up @@ -1043,8 +1043,7 @@ impl WebApi {

// https://developer.spotify.com/documentation/web-api/reference/remove-albums-user/
pub fn unsave_album(&self, id: &str) -> Result<(), Error> {
let request =
&RequestBuilder::new("v1/me/albums", Method::Delete, None).query("ids", id);
let request = &RequestBuilder::new("v1/me/albums", Method::Delete, None).query("ids", id);
self.send_empty_json(request)
}

Expand Down Expand Up @@ -1221,11 +1220,8 @@ impl WebApi {
}

pub fn unfollow_playlist(&self, id: &str) -> Result<(), Error> {
let request = &RequestBuilder::new(
format!("v1/playlists/{id}/followers"),
Method::Delete,
None,
);
let request =
&RequestBuilder::new(format!("v1/playlists/{id}/followers"), Method::Delete, None);
self.request(request)?;
Ok(())
}
Expand Down Expand Up @@ -1254,10 +1250,9 @@ impl WebApi {
Json(serde_json::Value),
}

let request =
&RequestBuilder::new(format!("v1/playlists/{id}/tracks"), Method::Get, None)
.query("marker", "from_token")
.query("additional_types", "track");
let request = &RequestBuilder::new(format!("v1/playlists/{id}/tracks"), Method::Get, None)
.query("marker", "from_token")
.query("additional_types", "track");

let result: Vector<PlaylistItem> = self.load_all_pages(request)?;

Expand All @@ -1278,9 +1273,8 @@ impl WebApi {
}

pub fn change_playlist_details(&self, id: &str, name: &str) -> Result<(), Error> {
let request =
&RequestBuilder::new(format!("v1/playlists/{id}/tracks"), Method::Get, None)
.set_body(Some(json!({ "name": name })));
let request = &RequestBuilder::new(format!("v1/playlists/{id}/tracks"), Method::Get, None)
.set_body(Some(json!({ "name": name })));
self.request(request)?;
Ok(())
}
Expand Down