Skip to content

Conversation

@Donaldino7712
Copy link

@Donaldino7712 Donaldino7712 commented Mar 26, 2025

This PR reworks backup handling with Gists.

On startup, instead of making a new backup, it performs a check to see if the local version is older or newer than the Gist version.

  • If the local version is newer, it creates a backup.
  • If the local version is older, it restores the backup from the Gist.

Backup settings are now also not synced.
I also tried my best to clean up the code.

Let me know what you think!

@ohitstom ohitstom requested a review from Copilot September 29, 2025 16:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR reworks the Spotify backup system to implement smarter backup/restore logic using GitHub Gists. Instead of blindly creating backups on startup, it now compares local and remote timestamps to determine whether to backup or restore data.

Key changes:

  • Introduced timestamp-based comparison logic to decide between backup and restore operations
  • Added gist caching and improved error handling for GitHub API interactions
  • Enhanced UI with a new "Check" button and cleaner dialog management

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
spotifyBackup/spotifyBackup.js Core logic overhaul with new backup/restore decision making, improved API handling, and UI enhancements
spotifyBackup/README.md Minor grammar and clarity improvements in documentation
README.md Formatting cleanup removing extra blank lines

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +67 to +68
// A hacky way to remove the backup settings from the backup
const data = JSON.stringify({ ...localStorage, "spotifyBackup:settings": undefined });
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

Using spread operator with undefined assignment is confusing. Consider using JSON.parse and delete or object destructuring with rest operator to exclude the settings key more clearly.

Suggested change
// A hacky way to remove the backup settings from the backup
const data = JSON.stringify({ ...localStorage, "spotifyBackup:settings": undefined });
// Build a plain object from localStorage, excluding the backup settings
const backupObj = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key !== "spotifyBackup:settings") {
backupObj[key] = localStorage.getItem(key);
}
}
const data = JSON.stringify(backupObj);

Copilot uses AI. Check for mistakes.
if (getConfig("gistEnabled")) {
// Check if the data is the same as the last backup
gist = await getGist();
if (data === JSON.stringify(gist.data)) {
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

String comparison of large JSON objects is inefficient and may fail due to property ordering differences. Consider using a deep equality check or comparing relevant properties directly.

Copilot uses AI. Check for mistakes.
function restoreData(parsedBackupData) {
async function restoreData() {
gist = await getGist();
const data = getConfig("gistEnabled") ? gist.data : JSON.parse(await Spicetify.Platform.ClipboardAPI.paste());
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

If clipboard parsing fails, the JSON.parse error will be cryptic. Consider wrapping this in a try-catch with a more descriptive error message for users.

Suggested change
const data = getConfig("gistEnabled") ? gist.data : JSON.parse(await Spicetify.Platform.ClipboardAPI.paste());
let data;
if (getConfig("gistEnabled")) {
data = gist.data;
} else {
try {
data = JSON.parse(await Spicetify.Platform.ClipboardAPI.paste());
} catch (error) {
Spicetify.showNotification("Clipboard does not contain valid backup data.", true);
console.error("Failed to parse clipboard data as JSON:", error);
return;
}
}

Copilot uses AI. Check for mistakes.
if (!getConfig("gistEnabled")) return false;
gist = await getGist();
const lastUpdate = new Date(gist.lastUpdate);
const lastBackup = new Date(getConfig("lastBackupTimestamp"));
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

If getConfig("lastBackupTimestamp") returns null or undefined, new Date(null) creates an invalid date that could cause unexpected comparison behavior. Add null checks before date creation.

Suggested change
const lastBackup = new Date(getConfig("lastBackupTimestamp"));
const lastBackupTimestamp = getConfig("lastBackupTimestamp");
if (!lastBackupTimestamp) {
// No previous backup, so always treat as new backup available
return true;
}
const lastBackup = new Date(lastBackupTimestamp);

Copilot uses AI. Check for mistakes.
Comment on lines +476 to 484
document.querySelectorAll(".x-settings-section").forEach(section => {
if (section.firstChild.textContent !== Spicetify.Locale._dictionary["desktop.settings.storage"]) return;

clearInterval(checkHeaderInterval);
const sectionContainer = document.createElement("div");
sectionContainer.className = "x-settings-section";
Spicetify.ReactDOM.render(Spicetify.React.createElement(Section), sectionContainer);
section.parentNode.insertBefore(sectionContainer, section.nextSibling);
});
Copy link

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

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

The interval is cleared inside the forEach loop, but the loop continues to execute for remaining sections. This could cause multiple sections to be processed. Move the clearInterval outside the forEach or add a break mechanism.

Copilot uses AI. Check for mistakes.
@ohitstom
Copy link
Owner

I never got to this as it's less of a feature PR and more of a total refactor, I'm unsure of why some changes have been made and as copilot has pointed out some of your changes are less effective.

I agree with your fundamental proposal of using dates and times when loading them in but it's a lot of effort reviewing this code, could you confirm it still works in current date ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants