Skip to content
Merged
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
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ This repository contains the source code for the BetterDiscord installer. The ap
Linux install support:

- Native Discord install: ✅ Supported
- Flatpak Discord install: ✅ Supported
- Snap Discord install: ❌ Unsupported due to upstream Snap packaging/runtime changes
- Flatpak Discord install: ✅ Supported for per-user installs; ⚠️ system-wide/global installs need elevated write access and aren't supported yet
- Snap Discord install: ❌ Unsupported — Snap mounts Discord's files read-only, so the installer can't modify the app

## Downloads

Expand Down Expand Up @@ -85,11 +85,19 @@ xattr -d com.apple.quarantine "/Applications/BetterDiscord Installer.app"

### Does the installer support Flatpak Discord on Linux?

Yes. Flatpak Discord installs are supported.
Yes, for **per-user** Flatpak installs (the default `flatpak install --user …`). **System-wide/global** Flatpak installs live under `/var/lib/flatpak`, which is root-owned; the installer needs to write into the app to inject BetterDiscord, and it doesn't request elevation yet, so global Flatpak installs aren't supported for now.

### Why is Snap Discord unsupported on Linux?

Discord Snap packaging/runtime changes prevent the installer from supporting Snap installs.
Snap mounts Discord's application files as a read-only squashfs. The installer injects BetterDiscord by modifying files inside the Discord app, which isn't possible on a read-only mount — so Snap can't be supported.

### How does the installer add BetterDiscord to Discord?

It places a small loader inside Discord's own app files (a `resources/app` folder) and preserves Discord's original `app.asar` next to it. Because this loads before Discord's updater runs, BetterDiscord can keep itself injected across Discord updates — so you generally only need to run the installer once. Uninstalling restores Discord's original files.

### I'm running the installer under WSL — do I need to do anything special?

Yes: **fully close Discord before installing, repairing, or uninstalling.** WSL support targets a Windows Discord install, but the installer can't see or manage the Windows Discord process from the Linux side, so it can't stop Discord for you. If Discord is still running it holds a lock on `app.asar` and the operation will fail — close Discord and try again. (For headless or CLI-based workflows, the BetterDiscord CLI is a better fit.)

### How can I use the global BetterDiscord folder with Flatpak?

Expand Down
29 changes: 11 additions & 18 deletions api/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"log"
"os"

"installer/discord"
"installer/types"
Expand Down Expand Up @@ -37,9 +36,9 @@ func (d *Controller) GetDiscordPath(channel string) string {
}

// #region Actions
func (action *Controller) Install(corePaths []string, options types.InstallOptions) {
for i := range corePaths {
install := discord.ResolvePath(corePaths[i])
func (action *Controller) Install(resourcePaths []string, options types.InstallOptions) {
for i := range resourcePaths {
install := discord.ResolvePath(resourcePaths[i])
if install == nil {
continue
}
Expand All @@ -56,9 +55,9 @@ func (action *Controller) Install(corePaths []string, options types.InstallOptio
runtime.EventsEmit(action.ctx, "success")
}

func (action *Controller) Uninstall(corePaths []string, options types.UninstallOptions) {
for i := range corePaths {
install := discord.ResolvePath(corePaths[i])
func (action *Controller) Uninstall(resourcePaths []string, options types.UninstallOptions) {
for i := range resourcePaths {
install := discord.ResolvePath(resourcePaths[i])
if install == nil {
continue
}
Expand All @@ -73,9 +72,9 @@ func (action *Controller) Uninstall(corePaths []string, options types.UninstallO
runtime.EventsEmit(action.ctx, "success")
}

func (action *Controller) Repair(corePaths []string, options types.RepairOptions) {
for i := range corePaths {
install := discord.ResolvePath(corePaths[i])
func (action *Controller) Repair(resourcePaths []string, options types.RepairOptions) {
for i := range resourcePaths {
install := discord.ResolvePath(resourcePaths[i])
if install == nil {
continue
}
Expand Down Expand Up @@ -119,17 +118,11 @@ func (action *Controller) Repair(corePaths []string, options types.RepairOptions

// #region Dialogs
func (d *Controller) BrowseForDiscord(schannel string) string {
var browsePath string
browsePath, err := os.UserConfigDir()
if err != nil {
browsePath = os.Getenv("HOME")
}

channel := types.ParseChannel(schannel)

selection, err := runtime.OpenDirectoryDialog(d.ctx, runtime.OpenDialogOptions{
Title: "Browsing to " + channel.Name(),
DefaultDirectory: browsePath,
DefaultDirectory: discord.DefaultBrowseDir(),
ShowHiddenFiles: true,
TreatPackagesAsDirectories: true,
})
Expand All @@ -139,7 +132,7 @@ func (d *Controller) BrowseForDiscord(schannel string) string {
}

if result := discord.ResolvePath(selection); result != nil {
return result.CorePath
return result.ResourcesPath
}
Comment thread
zerebos marked this conversation as resolved.

return ""
Expand Down
29 changes: 29 additions & 0 deletions discord/assets/app_index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// BetterDiscord's Injection Script (app.asar method)
const path = require("path");
const electron = require("electron");

// Never let a missing or broken BetterDiscord asar keep Discord from launching:
// this file is the app entry point, so an unhandled throw here bricks the client.
// The whole BetterDiscord load — path resolution included — is wrapped so any
// failure (e.g. an unset HOME) falls through to Discord's real app below.
try {
// The global BetterDiscord folder lives one directory above userData (the
// appData root). Electron gives the postfixed userData, so go up a directory.
let userConfig = path.join(electron.app.getPath("userData"), "..");

// If we're on Linux there are a couple cases to deal with
if (process.platform !== "win32" && process.platform !== "darwin") {
// Use || instead of ?? because a falsey value of "" is invalid per XDG spec.
// os.homedir() resolves the home directory even if the HOME env var is unset.
const homeDir = process.env.HOME || require("os").homedir();
userConfig = process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config");
}
Comment thread
zerebos marked this conversation as resolved.

require(path.join(userConfig, "BetterDiscord", "data", "betterdiscord.asar"));
}
catch (error) {
console.error("Failed to load BetterDiscord:", error);
}

// Hand off to Discord's real (renamed) app entry point
module.exports = require("../betterdiscord.app.asar");
1 change: 1 addition & 0 deletions discord/assets/app_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"main": "./index.js"}
18 changes: 0 additions & 18 deletions discord/assets/injection.js

This file was deleted.

Loading