diff --git a/subflakes/powercord/flake.nix b/subflakes/powercord/flake.nix index b9b60af..6e2eb36 100644 --- a/subflakes/powercord/flake.nix +++ b/subflakes/powercord/flake.nix @@ -95,6 +95,14 @@ defaultPackage = packages.powercord; packages = with channels.nixpkgs; { powercord = (discord-plugged.override { + patches = (map patch [ + "remove-heygirl" + "remove-lmgtfy" + "remove-mock" + "remove-spotify" + "remove-updater" + ]); + plugins = with inputs; [ discord-Custom-Volume-Range discord-In-app-notifs diff --git a/subflakes/powercord/patches/remove-heygirl.patch b/subflakes/powercord/patches/remove-heygirl.patch new file mode 100644 index 0000000..62da8a0 --- /dev/null +++ b/subflakes/powercord/patches/remove-heygirl.patch @@ -0,0 +1,68 @@ +diff --git a/src/Powercord/plugins/pc-heygirl/index.js b/src/Powercord/plugins/pc-heygirl/index.js +deleted file mode 100644 +index bd97579..0000000 +--- a/src/Powercord/plugins/pc-heygirl/index.js ++++ /dev/null +@@ -1,49 +0,0 @@ +-// idea based on http://heygirl.io/ (+used their images) +- +-const { Plugin } = require('powercord/entities'); +- +-module.exports = class HeyGirl extends Plugin { +- constructor () { +- super(); +- +- this.URLs = [].concat( +- Array(3).fill('jpg'), +- Array(2).fill('gif'), +- Array(1).fill('png'), +- Array(4).fill('gif'), +- Array(3).fill('jpg'), +- Array(1).fill('png') +- ).map((format, id) => ( +- `http://heygirl.io/img/gosling-square-${id + 1}.${format}` +- )); +- } +- +- startPlugin () { +- powercord.api.commands.registerCommand({ +- command: 'heygirl', +- description: 'Replaces every image with a random image of Ryan Gosling', +- usage: '{c}', +- executor: this.heygirl.bind(this) +- }); +- } +- +- pluginWillUnload () { +- powercord.api.commands.unregisterCommand('heygirl'); +- } +- +- getRandomURL () { +- return this.URLs[Math.floor(Math.random() * this.URLs.length)]; +- } +- +- heygirl () { +- document.querySelectorAll('[style*="background-image"]') +- .forEach(({ style }) => ( +- style.backgroundImage = `url("${this.getRandomURL()}")` +- )); +- +- document.querySelectorAll('img') +- .forEach(image => ( +- image.src = this.getRandomURL() +- )); +- } +-}; +diff --git a/src/Powercord/plugins/pc-heygirl/manifest.json b/src/Powercord/plugins/pc-heygirl/manifest.json +deleted file mode 100644 +index 084c039..0000000 +--- a/src/Powercord/plugins/pc-heygirl/manifest.json ++++ /dev/null +@@ -1,7 +0,0 @@ +-{ +- "name": "Heygirl", +- "version": "1.0.0", +- "description": "heygirl", +- "author": "Powercord Team", +- "license": "MIT" +-} diff --git a/subflakes/powercord/patches/remove-lmgtfy.patch b/subflakes/powercord/patches/remove-lmgtfy.patch new file mode 100644 index 0000000..044ae5d --- /dev/null +++ b/subflakes/powercord/patches/remove-lmgtfy.patch @@ -0,0 +1,189 @@ +diff --git a/src/Powercord/plugins/pc-lmgtfy/Settings.jsx b/src/Powercord/plugins/pc-lmgtfy/Settings.jsx +deleted file mode 100644 +index bd26ff3..0000000 +--- a/src/Powercord/plugins/pc-lmgtfy/Settings.jsx ++++ /dev/null +@@ -1,21 +0,0 @@ +-const { React } = require('powercord/webpack'); +-const { SwitchItem } = require('powercord/components/settings'); +- +-module.exports = ({ getSetting, toggleSetting }) => ( +-
+- toggleSetting('iie')} +- > +- Enable Internet Explainer +- +- toggleSetting('autocompletes')} +- > +- Display Autocompletes +- +-
+-); +diff --git a/src/Powercord/plugins/pc-lmgtfy/index.js b/src/Powercord/plugins/pc-lmgtfy/index.js +deleted file mode 100644 +index 1c46bfb..0000000 +--- a/src/Powercord/plugins/pc-lmgtfy/index.js ++++ /dev/null +@@ -1,143 +0,0 @@ +-const { Plugin } = require('powercord/entities'); +- +-const Constants = Object.freeze({ +- SEARCH_ENGINES: { +- google: 'g', +- lmgtfy: 'l', +- bing: 'b', +- yahoo: 'y', +- aol: 'a', +- ask: 'k', +- duckduckgo: 'd', +- snopes: 's', +- startpage: 't' +- }, +- SEARCH_TYPES: { +- web: 'w', +- images: 'i', +- videos: 'v', +- news: 'n', +- shopping: 's' +- } +-}); +- +-const Settings = require('./Settings'); +- +-module.exports = class LMGTFY extends Plugin { +- startPlugin () { +- powercord.api.settings.registerSettings(this.entityID, { +- category: this.entityID, +- label: 'LMGTFY', +- render: Settings +- }); +- +- powercord.api.commands.registerCommand({ +- command: 'lmgtfy', +- description: 'Let me Google that for you...', +- usage: '{c} [--iie] [...search terms] ', +- executor: this.handleCommand.bind(this), +- autocomplete: this.handleAutocomplete.bind(this) +- }); +- } +- +- pluginWillUnload () { +- powercord.api.settings.unregisterSettings(this.entityID); +- powercord.api.commands.unregisterCommand('lmgtfy'); +- } +- +- handleCommand (args) { +- if (args.length < 1) { +- return; +- } +- +- const iie = args[0].includes('--iie') ? !!args.splice(args.indexOf('--iie'), 1) : this.settings.get('iie', false); +- const options = args.slice(-2).map(arg => arg.toLowerCase()); +- const params = {}; +- params.searchEngine = Constants.SEARCH_ENGINES.google; +- +- for (const key of Object.keys(Constants)) { +- if (key === 'SEARCH_ENGINES') { +- for (const searchEngine of Object.keys(Constants[key])) { +- for (let i = 0; i < options.length; i++) { +- const match = options[i].toLowerCase() === searchEngine; +- if (match) { +- params.searchEngine = Constants.SEARCH_ENGINES[searchEngine]; +- args.splice(args.lastIndexOf(searchEngine), 1); +- options.splice(i, 1); +- break; +- } +- } +- } +- } else if (key === 'SEARCH_TYPES') { +- for (const searchType of Object.keys(Constants[key])) { +- for (let i = 0; i < options.length; i++) { +- const match = options[i].toLowerCase() === searchType; +- if (match) { +- if (params.searchEngine === 'g') { +- params.searchType = Constants.SEARCH_TYPES[searchType]; +- args.splice(args.lastIndexOf(searchType), 1); +- } +- } +- } +- } +- } +- } +- +- const { searchEngine, searchType } = params; +- const queryString = new URLSearchParams(); +- queryString.append('q', args.join(' ')); +- if (searchType) { +- queryString.append('s', searchEngine); +- queryString.append('t', searchType); +- } else if (searchEngine !== 'g') { +- queryString.append('s', searchEngine); +- } +- +- if (iie) { +- queryString.append('iie', +iie); +- } +- +- return { +- send: true, +- result: `` +- }; +- } +- +- handleAutocomplete (args) { +- if (!this.settings.get('autocompletes', true) || args.length === 0) { +- return false; +- } +- +- if (args[1] === void 0) { +- return { +- commands: [ { +- command: 'Please input your search terms...', +- instruction: true +- } ] +- }; +- } +- +- const lastArg = args[args.length - 1]; +- const searchEngines = Object.keys(Constants.SEARCH_ENGINES); +- const searchEngine = searchEngines.find(engine => args[args.lastIndexOf(engine)] === engine); +- if (!searchEngine) { +- return { +- commands: searchEngines +- .filter(engine => engine.includes(lastArg)) +- .map(engine => ({ command: engine })), +- header: 'select a search engine...' +- }; +- } +- +- const searchTypes = Object.keys(Constants.SEARCH_TYPES); +- const searchType = searchTypes.find(type => args[args.indexOf(searchEngine) + 1] === type); +- if (searchEngine === 'google' && !searchType) { +- return { +- commands: searchTypes +- .filter(type => type.includes(lastArg)) +- .map(type => ({ command: type })), +- header: 'select a search type...' +- }; +- } +- } +-}; +diff --git a/src/Powercord/plugins/pc-lmgtfy/manifest.json b/src/Powercord/plugins/pc-lmgtfy/manifest.json +deleted file mode 100644 +index 08a9989..0000000 +--- a/src/Powercord/plugins/pc-lmgtfy/manifest.json ++++ /dev/null +@@ -1,7 +0,0 @@ +-{ +- "name": "LMGTFY", +- "version": "1.0.0", +- "description": "Let me Google that for you...", +- "author": "Powercord Team", +- "license": "MIT" +-} diff --git a/subflakes/powercord/patches/remove-mock.patch b/subflakes/powercord/patches/remove-mock.patch new file mode 100644 index 0000000..e3ae0cb --- /dev/null +++ b/subflakes/powercord/patches/remove-mock.patch @@ -0,0 +1,38 @@ +diff --git a/src/Powercord/plugins/pc-mock/index.js b/src/Powercord/plugins/pc-mock/index.js +deleted file mode 100644 +index e35988b..0000000 +--- a/src/Powercord/plugins/pc-mock/index.js ++++ /dev/null +@@ -1,19 +0,0 @@ +-const { Plugin } = require('powercord/entities'); +- +-module.exports = class Mock extends Plugin { +- startPlugin () { +- powercord.api.commands.registerCommand({ +- command: 'mock', +- description: 'Mock a user...', +- usage: '{c} [text to mock]', +- executor: (args) => ({ +- send: true, +- result: args.join(' ').split('').map((c, i) => i % 2 ? c.toUpperCase() : c).join('') +- }) +- }); +- } +- +- pluginWillUnload () { +- powercord.api.commands.unregisterCommand('mock'); +- } +-}; +diff --git a/src/Powercord/plugins/pc-mock/manifest.json b/src/Powercord/plugins/pc-mock/manifest.json +deleted file mode 100644 +index 8b0bc06..0000000 +--- a/src/Powercord/plugins/pc-mock/manifest.json ++++ /dev/null +@@ -1,7 +0,0 @@ +-{ +- "name": "Mock", +- "version": "1.0.0", +- "description": "Mock people", +- "author": "Melmsie", +- "license": "MIT" +-} diff --git a/subflakes/powercord/patches/remove-spotify.patch b/subflakes/powercord/patches/remove-spotify.patch new file mode 100644 index 0000000..08cd60c --- /dev/null +++ b/subflakes/powercord/patches/remove-spotify.patch @@ -0,0 +1,2551 @@ +diff --git a/src/Powercord/plugins/pc-spotify/SpotifyAPI.js b/src/Powercord/plugins/pc-spotify/SpotifyAPI.js +deleted file mode 100644 +index 35bbc83..0000000 +--- a/src/Powercord/plugins/pc-spotify/SpotifyAPI.js ++++ /dev/null +@@ -1,245 +0,0 @@ +-const { WEBSITE } = require('powercord/constants'); +-const { shell: { openExternal } } = require('electron'); +-const { get, put, post, del } = require('powercord/http'); +-const { getModule, http, spotify, constants: { Endpoints } } = require('powercord/webpack'); +-const { SPOTIFY_BASE_URL, SPOTIFY_PLAYER_URL } = require('./constants'); +-const playerStore = require('./playerStore/store'); +- +-const revokedMessages = { +- SCOPES_UPDATED: 'Your Spotify account needs to be relinked to your Powercord account due to new authorizations required.', +- ACCESS_DENIED: 'Powercord is no longer able to connect to your Spotify account. Therefore, it has been automatically unlinked.' +-}; +- +-let usedCached = false; +- +-module.exports = { +- accessToken: null, +- +- async getAccessToken () { +- if (!powercord.account) { +- await powercord.fetchAccount(); +- } +- +- if (powercord.account && powercord.account.accounts.spotify) { +- const baseUrl = powercord.settings.get('backendURL', WEBSITE); +- const resp = await get(`${baseUrl}/api/v2/users/@me/spotify`) +- .set('Authorization', powercord.account.token) +- .then(r => r.body); +- +- if (resp.revoked) { +- powercord.api.notices.sendAnnouncement('spotify-revoked', { +- color: 'orange', +- message: revokedMessages[resp.revoked], +- button: { +- text: 'Relink Spotify', +- onClick: () => openExternal(`${baseUrl}/api/v2/oauth/spotify`) +- } +- }); +- } else if (resp.token) { +- return resp.token; +- } +- } +- +- console.debug('%c[SpotifyAPI]', 'color: #1ed860', 'No Spotify account linked to Powercord; Falling back to Discord\'s token'); +- if (!usedCached) { +- const spotifyMdl = await getModule([ 'getActiveSocketAndDevice' ]); +- const active = spotifyMdl.getActiveSocketAndDevice(); +- if (active && active.socket && active.socket.accessToken) { +- usedCached = true; +- return active.socket.accessToken; +- } +- } +- +- usedCached = false; +- const spotifyUserID = await http.get(Endpoints.CONNECTIONS) +- .then(res => +- res.body.find(connection => +- connection.type === 'spotify' +- ).id +- ); +- +- return spotify.getAccessToken(spotifyUserID) +- .then(r => r.body.access_token); +- }, +- +- genericRequest (request, isConnectWeb) { +- request.set('Authorization', `Bearer ${this.accessToken}`); +- if (isConnectWeb) { +- const currentDeviceId = playerStore.getLastActiveDeviceId(); +- if (currentDeviceId) { +- request.query('device_id', currentDeviceId); +- } +- } +- return request +- .catch(async (err) => { +- if (err) { +- if (err.statusCode === 401) { +- this.accessToken = await this.getAccessToken(); +- delete request._res; +- return this.genericRequest(request); +- } +- console.error(err.body, request.opts); +- throw err; +- } +- }); +- }, +- +- getTrack (trackId) { +- return this.genericRequest( +- get(`${SPOTIFY_BASE_URL}/tracks/${trackId}`) +- ).then(r => r.body); +- }, +- +- getPlaylists (limit = 50, offset = 0) { +- return this._fetchAll(`${SPOTIFY_BASE_URL}/me/playlists`, limit, offset); +- }, +- +- getPlaylistTracks (playlistId, limit = 100, offset = 0) { +- return this._fetchAll(`${SPOTIFY_BASE_URL}/playlists/${playlistId}/tracks`, limit, offset); +- }, +- +- addToPlaylist (playlistID, songURI) { +- return this.genericRequest( +- post(`${SPOTIFY_BASE_URL}/playlists/${playlistID}/tracks`) +- .query('uris', songURI) +- ).then(r => r.body); +- }, +- +- getAlbums (limit = 50, offset = 0) { +- return this._fetchAll(`${SPOTIFY_BASE_URL}/me/albums`, limit, offset); +- }, +- +- getAlbumTracks (albumId, limit = 100, offset = 0) { +- return this._fetchAll(`${SPOTIFY_BASE_URL}/albums/${albumId}/tracks`, limit, offset); +- }, +- +- getTopSongs () { +- return this.genericRequest( +- get(`${SPOTIFY_BASE_URL}/me/top/tracks`) +- .query('limit', 50) +- ).then(r => r.body); +- }, +- +- getSongs (limit = 50, offset = 0) { +- return this._fetchAll(`${SPOTIFY_BASE_URL}/me/tracks`, limit, offset); +- }, +- +- search (query, type = 'track', limit = 20) { +- return this.genericRequest( +- get(`${SPOTIFY_BASE_URL}/search`) +- .query('q', query) +- .query('type', type) +- .query('limit', limit) +- ).then(r => r.body); +- }, +- +- play (data) { +- return this.genericRequest( +- put(`${SPOTIFY_PLAYER_URL}/play`).send(data), true +- ); +- }, +- +- pause () { +- return this.genericRequest( +- put(`${SPOTIFY_PLAYER_URL}/pause`), true +- ); +- }, +- +- seek (position) { +- return this.genericRequest( +- put(`${SPOTIFY_PLAYER_URL}/seek`).query('position_ms', position), true +- ); +- }, +- +- next () { +- return this.genericRequest( +- post(`${SPOTIFY_PLAYER_URL}/next`), true +- ); +- }, +- +- prev () { +- return this.genericRequest( +- post(`${SPOTIFY_PLAYER_URL}/previous`), true +- ); +- }, +- +- getPlayer () { +- return this.genericRequest( +- get(SPOTIFY_PLAYER_URL) +- ).then(r => r.body); +- }, +- +- getDevices () { +- return this.genericRequest( +- get(`${SPOTIFY_PLAYER_URL}/devices`) +- ).then(r => r.body); +- }, +- +- setVolume (volume) { +- return this.genericRequest( +- put(`${SPOTIFY_PLAYER_URL}/volume`).query('volume_percent', volume), true +- ); +- }, +- +- setActiveDevice (deviceID) { +- return this.genericRequest( +- put(SPOTIFY_PLAYER_URL) +- .send({ +- device_ids: [ deviceID ], +- play: true +- }) +- ); +- }, +- +- setRepeatState (state) { +- return this.genericRequest( +- put(`${SPOTIFY_PLAYER_URL}/repeat`).query('state', state), true +- ); +- }, +- +- setShuffleState (state) { +- return this.genericRequest( +- put(`${SPOTIFY_PLAYER_URL}/shuffle`).query('state', state), true +- ); +- }, +- +- addSong (songID) { +- return this.genericRequest( +- put(`${SPOTIFY_BASE_URL}/me/tracks`) +- .query('ids', songID) +- ); +- }, +- +- removeSong (songID) { +- return this.genericRequest( +- del(`${SPOTIFY_BASE_URL}/me/tracks`) +- .query('ids', songID) +- ); +- }, +- +- checkLibrary (songID) { +- return this.genericRequest( +- get(`${SPOTIFY_BASE_URL}/me/tracks/contains`) +- .query('ids', songID) +- ); +- }, +- +- async _fetchAll (url, limit, offset) { +- const items = []; +- while (url) { +- const req = get(url); +- if (limit) { +- req.query('limit', limit); +- limit = 0; +- } +- if (offset) { +- req.query('offset', offset); +- offset = 0; +- } +- const res = await this.genericRequest(req).then(r => r.body); +- items.push(...res.items); +- url = res.next; +- } +- return items; +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/album.js b/src/Powercord/plugins/pc-spotify/commands/album.js +deleted file mode 100644 +index 0d5e298..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/album.js ++++ /dev/null +@@ -1,26 +0,0 @@ +-const playerStore = require('../playerStore/store'); +- +-module.exports = { +- command: 'album', +- description: 'Send album of current playing song to selected channel', +- category: 'Spotify', +- executor () { +- const currentTrack = playerStore.getCurrentTrack(); +- if (!currentTrack) { +- return { +- send: false, +- result: 'You are not currently listening to anything.' +- }; +- } +- if (!currentTrack.urls.album) { +- return { +- send: false, +- result: 'The track you\'re listening to doesn\'t belong to an album.' +- }; +- } +- return { +- send: true, +- result: currentTrack.urls.album +- }; +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/find.js b/src/Powercord/plugins/pc-spotify/commands/find.js +deleted file mode 100644 +index f5d6fe9..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/find.js ++++ /dev/null +@@ -1,39 +0,0 @@ +-const { getModule } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- command: 'find', +- description: 'Searches for a song and plays it!', +- usage: '{c} {song}', +- category: 'Spotify', +- executor (args) { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (!isPremium) { +- return { +- send: false, +- result: 'Oops, it looks like you are not a Spotify Premium member. Unfortunately, this feature isn\'t available to you as per Spotify\'s requirements.' +- }; +- } +- if (!args[0]) { +- return { +- send: false, +- result: 'You need to specify a song to search for and play!' +- }; +- } +- +- return SpotifyAPI.search(args.join(' '), 'track', 1).then((body) => { +- const tracksArray = body.tracks.items; +- if (tracksArray.length > 0) { +- const trackURL = tracksArray[0].uri; +- SpotifyAPI.play({ +- uris: [ trackURL ] +- }); +- return; +- } +- return { +- send: false, +- result: 'Couldn\'t find a song!' +- }; +- }); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/index.js b/src/Powercord/plugins/pc-spotify/commands/index.js +deleted file mode 100644 +index d59ce4c..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/index.js ++++ /dev/null +@@ -1,7 +0,0 @@ +-require('fs') +- .readdirSync(__dirname) +- .filter((file) => file !== 'index.js' && file !== '.DS_Store') +- .forEach(filename => { +- const moduleName = filename.split('.')[0]; +- exports[moduleName] = require(`${__dirname}/${filename}`); +- }); +diff --git a/src/Powercord/plugins/pc-spotify/commands/like.js b/src/Powercord/plugins/pc-spotify/commands/like.js +deleted file mode 100644 +index 3840aac..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/like.js ++++ /dev/null +@@ -1,29 +0,0 @@ +-const playerStore = require('../playerStore/store'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- command: 'like', +- description: 'Like the current playing song', +- category: 'Spotify', +- async executor () { +- if (!powercord.account || !powercord.account.accounts.spotify) { +- return { +- send: false, +- result: 'You need a Powercord account and connected Spotify account to use this feature!' +- }; +- } +- const currentTrack = playerStore.getCurrentTrack(); +- if (!currentTrack) { +- return { +- send: false, +- result: 'You are not currently listening to anything.' +- }; +- } +- const { body } = await SpotifyAPI.checkLibrary(currentTrack.id); +- SpotifyAPI[body[0] ? 'removeSong' : 'addSong'](currentTrack.id); +- return { +- send: false, +- result: `You ${body[0] ? 'removed' : 'added'} **${currentTrack.name}** by **${currentTrack.artists}** ${body[0] ? 'from' : 'to'} your Liked Songs.` +- }; +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/next.js b/src/Powercord/plugins/pc-spotify/commands/next.js +deleted file mode 100644 +index 730c4db..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/next.js ++++ /dev/null +@@ -1,20 +0,0 @@ +-const { getModule } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- command: 'next', +- aliases: [ 'skip' ], +- description: 'Skip Spotify song', +- usage: '{c}', +- category: 'Spotify', +- executor () { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (!isPremium) { +- return { +- send: false, +- result: 'Oops, it looks like you are not a Spotify Premium member. Unfortunately, this feature isn\'t available to you as per Spotify\'s requirements.' +- }; +- } +- return SpotifyAPI.next(); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/pause.js b/src/Powercord/plugins/pc-spotify/commands/pause.js +deleted file mode 100644 +index 4d576d7..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/pause.js ++++ /dev/null +@@ -1,19 +0,0 @@ +-const { getModule } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- command: 'pause', +- description: 'Pause Spotify playback', +- usage: '{c}', +- category: 'Spotify', +- executor () { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (!isPremium) { +- return { +- send: false, +- result: 'Oops, it looks like you are not a Spotify Premium member. Unfortunately, this feature isn\'t available to you as per Spotify\'s requirements.' +- }; +- } +- return SpotifyAPI.pause(); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/play.js b/src/Powercord/plugins/pc-spotify/commands/play.js +deleted file mode 100644 +index a857325..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/play.js ++++ /dev/null +@@ -1,38 +0,0 @@ +-const { getModule } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +-const urlRegex = /\/track\/([A-z0-9]*)/; +- +-module.exports = { +- command: 'play', +- description: 'Play a Spotify URL', +- usage: '{c} ', +- category: 'Spotify', +- executor ([ url ]) { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (!isPremium) { +- return { +- send: false, +- result: 'Oops, it looks like you are not a Spotify Premium member. Unfortunately, this feature isn\'t available to you as per Spotify\'s requirements.' +- }; +- } +- +- if (!url) { +- const spotifyModals = document.querySelectorAll('.embedSpotify-tvxDCr'); +- const spotifyModal = spotifyModals[spotifyModals.length - 1]; +- url = spotifyModal && spotifyModal.children[0].src; +- +- if (!url) { +- return { +- send: false, +- result: 'No URL specified.' +- }; +- } +- } +- +- SpotifyAPI.play({ +- uris: [ +- `spotify:track:${urlRegex.exec(url)[1]}` +- ] +- }); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/previous.js b/src/Powercord/plugins/pc-spotify/commands/previous.js +deleted file mode 100644 +index 8cb704d..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/previous.js ++++ /dev/null +@@ -1,20 +0,0 @@ +-const { getModule } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- command: 'previous', +- aliases: [ 'prev' ], +- description: 'Go back one Spotify song', +- usage: '{c}', +- category: 'Spotify', +- executor () { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (!isPremium) { +- return { +- send: false, +- result: 'Oops, it looks like you are not a Spotify Premium member. Unfortunately, this feature isn\'t available to you as per Spotify\'s requirements.' +- }; +- } +- return SpotifyAPI.prev(); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/resume.js b/src/Powercord/plugins/pc-spotify/commands/resume.js +deleted file mode 100644 +index da6486c..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/resume.js ++++ /dev/null +@@ -1,19 +0,0 @@ +-const { getModule } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- command: 'resume', +- description: 'Resume Spotify playback', +- usage: '{c}', +- category: 'Spotify', +- executor () { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (!isPremium) { +- return { +- send: false, +- result: 'Oops, it looks like you are not a Spotify Premium member. Unfortunately, this feature isn\'t available to you as per Spotify\'s requirements.' +- }; +- } +- return SpotifyAPI.play(); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/share.js b/src/Powercord/plugins/pc-spotify/commands/share.js +deleted file mode 100644 +index b5d71c5..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/share.js ++++ /dev/null +@@ -1,50 +0,0 @@ +-const { open: openModal } = require('powercord/modal'); +-const { React } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +-const playerStore = require('../playerStore/store'); +-const ShareModal = require('../components/ShareModal'); +- +-module.exports = { +- command: 'share', +- description: 'Send specified or current playing song to selected channel', +- usage: '{c} {song name/artist}', +- category: 'Spotify', +- async executor (query) { +- query = query.join(' '); +- +- if (query.length > 0) { +- const result = await SpotifyAPI.search(query, 'track', 14); +- const closestTrack = result.tracks.items[0]; +- +- if (result.tracks.items.length > 1) { +- return openModal(() => React.createElement(ShareModal, { +- tracks: result.tracks, +- query +- })); +- } else if (closestTrack) { +- return { +- send: true, +- result: closestTrack.external_urls.spotify +- }; +- } +- +- return { +- send: false, +- result: `Couldn't find "\`${query}\`". Try searching again using a different spelling or keyword.` +- }; +- } +- +- const currentTrack = playerStore.getCurrentTrack(); +- if (!currentTrack) { +- return { +- send: false, +- result: 'You are not currently listening to anything.' +- }; +- } +- +- return { +- send: true, +- result: currentTrack.urls.track +- }; +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/commands/volume.js b/src/Powercord/plugins/pc-spotify/commands/volume.js +deleted file mode 100644 +index 83fd046..0000000 +--- a/src/Powercord/plugins/pc-spotify/commands/volume.js ++++ /dev/null +@@ -1,20 +0,0 @@ +-const { getModule } = require('powercord/webpack'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- command: 'volume', +- aliases: [ 'vol' ], +- description: 'Change Spotify volume', +- usage: '{c} ', +- category: 'Spotify', +- executor ([ args ]) { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (!isPremium) { +- return { +- send: false, +- result: 'Oops, it looks like you are not a Spotify Premium member. Unfortunately, this feature isn\'t available to you as per Spotify\'s requirements.' +- }; +- } +- return SpotifyAPI.setVolume(args); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/components/AddToPlaylist.jsx b/src/Powercord/plugins/pc-spotify/components/AddToPlaylist.jsx +deleted file mode 100644 +index 77cd1af..0000000 +--- a/src/Powercord/plugins/pc-spotify/components/AddToPlaylist.jsx ++++ /dev/null +@@ -1,124 +0,0 @@ +-const { React, Flux, i18n: { Messages } } = require('powercord/webpack'); +-const { FormTitle, Button, Divider, Spinner, Card, Tooltip } = require('powercord/components'); +-const { Modal, Confirm } = require('powercord/components/modal'); +-const { open: openModal, close: closeModal } = require('powercord/modal'); +- +-const { SPOTIFY_DEFAULT_IMAGE } = require('../constants'); +-const songsStore = require('../songsStore/store'); +-const songsStoreActions = require('../songsStore/actions'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-class AddToPlaylist extends React.PureComponent { +- componentDidMount () { +- if (!this.props.loaded) { +- songsStoreActions.loadPlaylists(); +- } +- } +- +- render () { +- return ( +- +- +- Add to Playlist +- closeModal()}/> +- +- +-

Where do you want to save this very nice tune?

+-
+- Spotify Cover +-
+- +- {this.props.track.name} +- +- +- {Messages.USER_ACTIVITY_LISTENING_ARTISTS.format({ +- artists: this.props.track.artists, +- artistsHook: t => t +- })} +- +-
+-
+- +-
+- {!this.props.loaded +- ? +- : Object.keys(this.props.playlists).map(p => this.renderItem(p))} +-
+-
+- +- +- +-
+- ); +- } +- +- renderItem (playlistId) { +- const playlist = this.props.playlists[playlistId]; +- if (!playlist.editable) { +- return null; +- } +- +- return ( +- +- Spotify Cover +-
+- +- {playlist.name} +- +- +- {playlist.tracksLoaded +- ? `${Object.keys(playlist.tracks).length} tracks` +- : Messages.DEFAULT_INPUT_PLACEHOLDER} +- +-
+- {playlist.tracksLoaded +- ? ( +- +- ) +- : ( +- +- +- +- )} +-
+- ); +- } +- +- handleAddToPlaylist (playlistId) { +- closeModal(); +- const playlist = this.props.playlists[playlistId]; +- if (playlist.tracks[this.props.track.id]) { +- openModal(() => ( +- { +- SpotifyAPI.addToPlaylist(playlistId, this.props.track.uri); +- closeModal(); +- }} +- onCancel={closeModal} +- > +-
+- This item is already in this playlist. Do you want to add it anyway? +-
+-
+- )); +- } else { +- SpotifyAPI.addToPlaylist(playlistId, this.props.track.uri); +- } +- console.log(playlistId, this.props.playlists[playlistId]); +- } +-} +- +-module.exports = Flux.connectStores( +- [ songsStore ], +- () => ({ +- loaded: songsStore.getPlaylistsLoaded(), +- playlists: songsStore.getPlaylists() +- }) +-)(AddToPlaylist); +diff --git a/src/Powercord/plugins/pc-spotify/components/ContextMenu.jsx b/src/Powercord/plugins/pc-spotify/components/ContextMenu.jsx +deleted file mode 100644 +index 6d27bc1..0000000 +--- a/src/Powercord/plugins/pc-spotify/components/ContextMenu.jsx ++++ /dev/null +@@ -1,285 +0,0 @@ +-const { clipboard, shell } = require('electron'); +-const { React, Flux, getModule, messages, channels, contextMenu: { closeContextMenu }, i18n: { Messages } } = require('powercord/webpack'); +-const { open: openModal } = require('powercord/modal'); +-const { Menu } = require('powercord/components'); +-const { formatTime } = require('powercord/util'); +- +-const songsStore = require('../songsStore/store'); +-const songsStoreActions = require('../songsStore/actions'); +-const playerStore = require('../playerStore/store'); +-const SpotifyAPI = require('../SpotifyAPI'); +-const AddToPlaylist = require('./AddToPlaylist'); +- +-class ContextMenu extends React.PureComponent { +- constructor (props) { +- super(props); +- this.handleVolumeSlide = global._.debounce(this.handleVolumeSlide.bind(this), 200); +- } +- +- handleVolumeSlide (volume) { +- SpotifyAPI.setVolume(Math.round(volume)); +- } +- +- componentDidMount () { +- if (powercord.account && powercord.account.accounts.spotify) { +- if (!this.props.songsLoaded) { +- songsStoreActions.loadSongs(); +- } +- if (!this.props.topSongsLoaded) { +- songsStoreActions.loadTopSongs(); +- } +- if (!this.props.albumsLoaded) { +- songsStoreActions.loadAlbums(); +- } +- } +- if (!this.props.playlistsLoaded) { +- songsStoreActions.loadPlaylists(); +- } +- } +- +- render () { +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- +- return ( +- +- {isPremium && this.renderDevices()} +- {isPremium && this.renderSongs()} +- {isPremium && this.renderPlaybackSettings()} +- {isPremium && this.renderVolume()} +- {isPremium && this.renderSave()} +- {this.renderActions()} +- +- ); +- } +- +- renderDevices () { +- return ( +- +- +- {this.props.devices.sort(d => -Number(d.is_active)).map((device, i) => ( +- <> +- +- {i === 0 && } +- +- ))} +- +- +- ); +- } +- +- renderSongs () { +- const hasCoolFeatures = powercord.account && powercord.account.accounts.spotify; +- +- return ( +- +- +- {this.props.playlistsLoaded +- ? this._renderList(this.props.playlists) +- : null} +- +- {hasCoolFeatures && +- {this.props.albumsLoaded +- ? this._renderList(this.props.albums) +- : null} +- } +- {hasCoolFeatures && +- {this.props.topSongsLoaded +- ? this._renderSongs(this.props.topSongs) +- : null} +- } +- {hasCoolFeatures && +- {this.props.songsLoaded +- ? this._renderSongs(this.props.songs) +- : null} +- } +- +- ); +- } +- +- _renderList (list) { +- return Object.entries(list).map(([ id, item ]) => ( +- setTimeout(() => SpotifyAPI.play({ context_uri: item.uri }), 10)} +- > +- {item.tracksLoaded +- ? this._renderSongs(item.tracks, item.uri) +- : null} +- +- )); +- } +- +- _renderSongs (list, uri) { +- return Object.entries(list).map(([ id, item ]) => ( +- setTimeout(() => { +- if (uri) { +- SpotifyAPI.play({ +- context_uri: uri, +- offset: { uri: item.uri } +- }); +- } else { +- SpotifyAPI.play({ +- uris: [ item.uri ] +- }); +- } +- }, 10)} +- /> +- )); +- } +- +- renderPlaybackSettings () { +- if (!powercord.account || !powercord.account.accounts.spotify) { +- return null; +- } +- +- const cannotAll = !this.props.playerState.canRepeat && !this.props.playerState.canRepeatOne; +- const isOff = this.props.playerState.repeat === playerStore.RepeatState.NO_REPEAT; +- const isContext = this.props.playerState.repeat === playerStore.RepeatState.REPEAT_CONTEXT; +- const isTrack = this.props.playerState.repeat === playerStore.RepeatState.REPEAT_TRACK; +- +- return ( +- +- +- SpotifyAPI.setRepeatState('off')} +- disabled={isOff} +- /> +- SpotifyAPI.setRepeatState('context')} +- disabled={isContext || !this.props.playerState.canRepeat} +- /> +- SpotifyAPI.setRepeatState('track')} +- disabled={isTrack || !this.props.playerState.canRepeatOne} +- /> +- +- SpotifyAPI.setShuffleState(!this.props.playerState.shuffle)} +- disabled={!this.props.playerState.canShuffle} +- /> +- +- ); +- } +- +- renderVolume () { +- const Slider = getModule(m => m.render && m.render.toString().includes('sliderContainer'), false); +- return ( +- +- ( +- +- )} +- /> +- +- ); +- } +- +- renderSave () { +- if (!powercord.account || !powercord.account.accounts.spotify) { +- return null; +- } +- +- return ( +- +- {this.props.currentLibraryState === playerStore.LibraryState.IN_LIBRARY +- ? SpotifyAPI.removeSong(this.props.currentTrack.id)} +- disabled={[ playerStore.LibraryState.UNKNOWN, playerStore.LibraryState.LOCAL_SONG ].includes(this.props.currentLibraryState)} +- /> +- : SpotifyAPI.addSong(this.props.currentTrack.id)} +- disabled={[ playerStore.LibraryState.UNKNOWN, playerStore.LibraryState.LOCAL_SONG ].includes(this.props.currentLibraryState)} +- />} +- openModal(() => React.createElement(AddToPlaylist, { track: this.props.currentTrack }))} +- /> +- +- ); +- } +- +- renderActions () { +- return ( +- +- { +- const protocol = getModule([ 'isProtocolRegistered', '_dispatchToken' ], false).isProtocolRegistered(); +- shell.openExternal(protocol ? this.props.currentTrack.uri : this.props.currentTrack.urls.track); +- }} +- /> +- messages.sendMessage( +- channels.getChannelId(), +- { content: this.props.currentTrack.urls.album } +- )} +- /> +- messages.sendMessage( +- channels.getChannelId(), +- { content: this.props.currentTrack.urls.track } +- )} +- /> +- clipboard.writeText(this.props.currentTrack.urls.album)} +- /> +- clipboard.writeText(this.props.currentTrack.urls.track)} +- /> +- +- ); +- } +-} +- +-module.exports = Flux.connectStores( +- [ songsStore, playerStore, powercord.api.settings.store ], +- (props) => ({ +- ...songsStore.getStore(), +- ...playerStore.getStore(), +- ...powercord.api.settings._fluxProps(props.entityID) +- }) +-)(ContextMenu); +diff --git a/src/Powercord/plugins/pc-spotify/components/Modal.jsx b/src/Powercord/plugins/pc-spotify/components/Modal.jsx +deleted file mode 100644 +index c62bece..0000000 +--- a/src/Powercord/plugins/pc-spotify/components/Modal.jsx ++++ /dev/null +@@ -1,274 +0,0 @@ +-const { shell } = require('electron'); +-const { React, Flux, getModule, getModuleByDisplayName, contextMenu, i18n: { Messages } } = require('powercord/webpack'); +-const { AsyncComponent, Icon, Icons: { FontAwesome } } = require('powercord/components'); +-const { open: openModal } = require('powercord/modal'); +- +-const { SPOTIFY_DEFAULT_IMAGE } = require('../constants'); +-const SpotifyAPI = require('../SpotifyAPI'); +-const playerStore = require('../playerStore/store'); +-const playerStoreActions = require('../playerStore/actions'); +-const AddToPlaylist = require('./AddToPlaylist'); +-const ContextMenu = require('./ContextMenu'); +-const SeekBar = require('./SeekBar'); +-const PayUp = require('./PayUp'); +- +-const PanelSubtext = AsyncComponent.from(getModuleByDisplayName('PanelSubtext')); +-const Tooltip = AsyncComponent.from(getModuleByDisplayName('Tooltip')); +- +-class Modal extends React.PureComponent { +- constructor (props) { +- super(props); +- this._rerenderScheduled = false; +- this.state = { +- hover: false +- }; +- } +- +- render () { +- if (this.props.devices.length === 0 || !this.props.currentTrack) { +- return null; +- } +- +- const isPremium = getModule([ 'isSpotifyPremium' ], false).isSpotifyPremium(); +- if (isPremium === null && !this._rerenderScheduled) { +- this._rerenderScheduled = true; +- setTimeout(() => this.forceUpdate(), 1e3); +- } +- +- return ( +-
this.setState({ hover: true })} +- onMouseLeave={() => this.setState({ hover: false })} +- > +- {this.renderFromBase(isPremium)} +- {isPremium && this.renderExtraControls()} +- this.setState({ seeking })} +- onDurationOverflow={() => { +- const playerState = playerStore.getPlayerState(); +- playerStoreActions.updatePlayerState({ +- ...playerState, +- playing: false +- }); +- }} +- /> +-
+- ); +- } +- +- renderFromBase (isPremium) { +- const { avatar, avatarWrapper } = getModule([ 'container', 'usernameContainer' ], false); +- +- return { +- ...this.props.base, +- props: { +- ...this.props.base.props, +- onMouseEnter: () => void 0, +- onMouseLeave: () => void 0, +- onContextMenu: e => contextMenu.openContextMenu(e, () => React.createElement(ContextMenu)), +- className: `${this.props.base.props.className || ''}`, +- children: [ +- ( +-
{ +- const protocol = getModule([ 'isProtocolRegistered', '_dispatchToken' ], false).isProtocolRegistered(); +- shell.openExternal(protocol ? this.props.currentTrack.uri : this.props.currentTrack.urls.track); +- }} +- > +- +- {(props) => ( +- Spotify cover +- )} +- +-
+- ), +- ( +- +- {(tooltipProps) => this.renderNameComponent(tooltipProps)} +- +- ), +- { +- ...this.props.base.props.children[1], +- props: { +- ...this.props.base.props.children[1].props, +- className: `${this.props.base.props.children[1].props.className || ''} spotify-buttons`.trim(), +- children: isPremium +- ? [ +- this.renderButton(() => Messages.PAGINATION_PREVIOUS, 'backward', () => SpotifyAPI.prev()), +- this.props.playerState.playing +- ? this.renderButton(() => Messages.PAUSE, 'pause', () => SpotifyAPI.pause()) +- : this.renderButton(() => Messages.PLAY, 'play', () => SpotifyAPI.play()), +- this.renderButton(() => Messages.NEXT, 'forward', () => SpotifyAPI.next()) +- ] +- : this.renderInfoPremium() +- } +- } +- ] +- } +- }; +- } +- +- renderNameComponent (props = {}) { +- const nameComponent = this.props.base.props.children[0].props.children[1].props.children({}); +- delete nameComponent.props.onMouseLeave; +- delete nameComponent.props.onMouseEnter; +- delete nameComponent.props.onClick; +- +- // [ nameComponent.props.className ] = nameComponent.props.className.split(' '); +- Object.assign(nameComponent.props, props); +- nameComponent.props.children.props.children[0].props.className = 'spotify-title'; +- nameComponent.props.children.props.children[0].props.children.props.children = this.props.currentTrack.name; +- nameComponent.props.children.props.children[1] = ( +- +- {Messages.USER_ACTIVITY_LISTENING_ARTISTS.format({ +- artists: this.props.currentTrack.artists, +- artistsHook: t => t +- })} +- +- ); +- return nameComponent; +- } +- +- renderExtraControls () { +- if (!this.props.getSetting('showControls', true)) { +- return null; +- } +- +- const hasCoolFeatures = powercord.account && powercord.account.accounts.spotify; +- return ( +-
+- {hasCoolFeatures && this.renderAddToLibrary()} +- {this.renderShuffle()} +- {this.renderRepeat()} +- {hasCoolFeatures && this.renderAddToPlaylist()} +-
+- ); +- } +- +- renderAddToLibrary () { +- switch (this.props.currentLibraryState) { +- case playerStore.LibraryState.LOCAL_SONG: +- return this.renderButton(() => Messages.SPOTIFY_CANT_LIKE_LOCAL, 'heart', () => +- SpotifyAPI.removeSong(this.props.currentTrack.id), false, 'active'); +- case playerStore.LibraryState.IN_LIBRARY: +- return this.renderButton(() => Messages.SPOTIFY_REMOVE_LIKED_SONGS, 'heart', () => +- SpotifyAPI.removeSong(this.props.currentTrack.id), false, 'active'); +- case playerStore.LibraryState.NOT_IN_LIBRARY: +- return this.renderButton(() => Messages.SPOTIFY_ADD_LIKED_SONGS, 'heart-regular', () => +- SpotifyAPI.addSong(this.props.currentTrack.id)); +- default: +- return this.renderButton(() => Messages.DEFAULT_INPUT_PLACEHOLDER, 'heart', () => void 0, true); +- } +- } +- +- renderShuffle () { +- if (!this.props.playerState.canShuffle) { +- return this.renderButton(() => 'Cannot shuffle right now', 'random', () => void 0, true); +- } +- const { shuffle } = this.props.playerState; +- return this.renderButton(() => 'Shuffle', 'random', () => +- SpotifyAPI.setShuffleState(!shuffle), false, shuffle ? 'active' : ''); +- } +- +- renderRepeat () { +- if (!this.props.playerState.canRepeat && !this.props.playerState.canRepeatOne) { +- return this.renderButton(() => 'Cannot repeat right now', 'sync', () => void 0, true); +- } +- +- switch (this.props.playerState.repeat) { +- case playerStore.RepeatState.NO_REPEAT: +- return this.renderButton(() => 'Repeat', 'sync', () => this.handleSetRepeat(), false); +- case playerStore.RepeatState.REPEAT_CONTEXT: +- return this.renderButton(() => 'Repeat Track', 'sync', () => this.handleSetRepeat(), false, 'active'); +- case playerStore.RepeatState.REPEAT_TRACK: +- return this.renderButton(() => 'No Repeat', 'undo', () => this.handleSetRepeat(), false, 'active'); +- } +- } +- +- renderAddToPlaylist () { +- return this.renderButton(() => 'Save to Playlist', 'plus-circle', () => this.handleAddToPlaylist()); +- } +- +- renderButton (tooltipText, icon, onClick, disabled, className) { +- return { +- ...this.props.base.props.children[1].props.children[0], +- props: { +- ...this.props.base.props.children[1].props.children[0].props, +- icon: () => React.createElement(FontAwesome, { +- className, +- icon +- }), +- tooltipText: tooltipText(), +- disabled, +- onClick +- } +- }; +- } +- +- renderInfoPremium () { +- return { +- ...this.props.base.props.children[1].props.children[0], +- props: { +- ...this.props.base.props.children[1].props.children[0].props, +- tooltipText: 'Not seeing controls?', +- icon: () => React.createElement(Icon, { +- name: 'Info', +- width: 20, +- height: 20, +- style: { color: 'var(--interactive-normal)' } +- }), +- onClick: () => openModal(() => React.createElement(PayUp)) +- } +- }; +- } +- +- handleSetRepeat () { +- const possibleStates = [ +- playerStore.RepeatState.NO_REPEAT, +- this.props.playerState.canRepeat && playerStore.RepeatState.REPEAT_CONTEXT, +- this.props.playerState.canRepeatOne && playerStore.RepeatState.REPEAT_TRACK +- ].filter(Boolean); +- const currentIndex = possibleStates.indexOf(this.props.playerState.repeat); +- const nextState = possibleStates[(currentIndex + 1) % possibleStates.length]; +- switch (nextState) { +- case playerStore.RepeatState.NO_REPEAT: +- SpotifyAPI.setRepeatState('off'); +- break; +- case playerStore.RepeatState.REPEAT_CONTEXT: +- SpotifyAPI.setRepeatState('context'); +- break; +- case playerStore.RepeatState.REPEAT_TRACK: +- SpotifyAPI.setRepeatState('track'); +- break; +- } +- } +- +- handleAddToPlaylist () { +- openModal(() => React.createElement(AddToPlaylist, { track: this.props.currentTrack })); +- } +-} +- +-module.exports = Flux.connectStores( +- [ playerStore, powercord.api.settings.store ], +- (props) => ({ +- ...playerStore.getStore(), +- ...powercord.api.settings._fluxProps(props.entityID) +- }) +-)(Modal); +diff --git a/src/Powercord/plugins/pc-spotify/components/PayUp.jsx b/src/Powercord/plugins/pc-spotify/components/PayUp.jsx +deleted file mode 100644 +index c5af79a..0000000 +--- a/src/Powercord/plugins/pc-spotify/components/PayUp.jsx ++++ /dev/null +@@ -1,47 +0,0 @@ +-const { React, getModule, constants: { SpotifyEndpoints }, i18n: { Messages } } = require('powercord/webpack'); +-const { FormTitle, Button } = require('powercord/components'); +-const { Modal } = require('powercord/components/modal'); +-const { close: closeModal } = require('powercord/modal'); +- +-module.exports = React.memo( +- () => { +- const { size16 } = getModule([ 'size16' ], false); +- const { marginBottom20 } = getModule([ 'marginBottom20' ], false); +- +- return ( +- +- +- Spotify Premium Required +- closeModal()}/> +- +- +-
+- To control your Spotify playback we use Spotify's "Connect Web" API, which is unfortunately locked to +- Spotify Premium users. In order for you to control Spotify's playback you'll need to get a Premium +- subscription. +-
+-
+- If you do happen to have a Spotify Premium subscription but you're still not seeing the buttons show up, +- it might happen that Spotify is reporting inaccurate data about your Premium status and alters the +- availability of the buttons. Try changing the playback in any way (play, pause, change track, ...) to +- trigger and update and let us get accurate data from Spotify. +-
+-
+- If this still did not fix it, make sure you're not in a private session as we've received a few reports +- saying this causes your Premium subscription status to not be properly sent to us. Also make sure you do +- have your Spotify account linked to your Discord account (You can tell Discord to not show it on your +- profile and not show the song you're listening to in your status, if you don't want to) +-
+-
+- +- +- +- +-
+- ); +- } +-); +diff --git a/src/Powercord/plugins/pc-spotify/components/SeekBar.jsx b/src/Powercord/plugins/pc-spotify/components/SeekBar.jsx +deleted file mode 100644 +index 48b96cb..0000000 +--- a/src/Powercord/plugins/pc-spotify/components/SeekBar.jsx ++++ /dev/null +@@ -1,121 +0,0 @@ +-const { React } = require('powercord/webpack'); +-const { formatTime } = require('powercord/util'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-class SeekBar extends React.PureComponent { +- constructor (props) { +- super(props); +- +- this.state = { +- seeking: false, +- progress: null, +- wasPlaying: false, +- listeners: {} +- }; +- +- this._overflowFired = false; +- this.seek = this.seek.bind(this); +- this.endSeek = this.endSeek.bind(this, false); +- } +- +- componentDidMount () { +- this._renderInterval = setInterval(() => this.forceUpdate(), 500); +- } +- +- componentDidUpdate (prevProps) { +- if (!this.state.seeking && this.props.progress !== prevProps.progress) { +- this.setState({ progress: null }); +- } +- } +- +- componentWillUnmount () { +- if (this.state.listeners.seek) { +- document.removeEventListener('mousemove', this.seek); +- } +- +- if (this.state.listeners.stop) { +- document.removeEventListener('mouseup', this.endSeek); +- } +- +- if (this._renderInterval) { +- clearInterval(this._renderInterval); +- } +- } +- +- async startSeek (e) { +- if (!this.props.isPremium) { +- return; +- } +- +- this.seek(e); +- document.addEventListener('mousemove', this.seek); +- document.addEventListener('mouseup', this.endSeek); +- +- this.props.onSeeking(true); +- this.setState({ +- seeking: true, +- wasPlaying: this.props.isPlaying +- }); +- if (this.props.isPlaying && !await SpotifyAPI.pause()) { +- await this.endSeek(true); +- } +- } +- +- seek ({ clientX: mouseX }) { +- const { x, width } = document.querySelector('.spotify-seek-bar').getBoundingClientRect(); +- const delta = mouseX - x; +- const seek = delta / width; +- this.setState({ progress: Math.round(this.props.duration * Math.max(0, Math.min(seek, 1))) }); +- } +- +- async endSeek (cancel) { +- document.removeEventListener('mousemove', this.seek); +- document.removeEventListener('mouseup', this.endSeek); +- +- this.props.onSeeking(false); +- this.setState({ seeking: false }); +- if (cancel) { +- this.setState({ progress: false }); +- } else { +- await SpotifyAPI.seek(this.state.progress); +- if (this.state.wasPlaying) { +- await SpotifyAPI.play(); +- } +- } +- } +- +- render () { +- const rawProgress = this.state.progress || this.props.progress; +- const progress = (this.props.isPlaying && !this.state.seeking) +- ? rawProgress + (Date.now() - this.props.progressAt) +- : rawProgress; +- const trimmedProgress = Math.min(progress, this.props.duration); +- const current = trimmedProgress / this.props.duration * 100; +- const isOverflowing = progress - trimmedProgress > 2000; +- if (isOverflowing && !this._overflowFired) { +- this._overflowFired = true; +- this.props.onDurationOverflow(); +- } else if (!isOverflowing && this._overflowFired) { +- this._overflowFired = false; +- } +- +- return ( +-
+-
+- +- {formatTime(progress)} +- +- +- {formatTime(this.props.duration)} +- +-
+-
this.startSeek(e)}> +- +- +-
+-
+- ); +- } +-} +- +-module.exports = SeekBar; +diff --git a/src/Powercord/plugins/pc-spotify/components/Settings.jsx b/src/Powercord/plugins/pc-spotify/components/Settings.jsx +deleted file mode 100644 +index 94ceb55..0000000 +--- a/src/Powercord/plugins/pc-spotify/components/Settings.jsx ++++ /dev/null +@@ -1,35 +0,0 @@ +-const { React } = require('powercord/webpack'); +-const { SwitchItem } = require('powercord/components/settings'); +- +-module.exports = React.memo( +- ({ getSetting, toggleSetting, patch }) => ( +-
+- toggleSetting('squareCovers')} +- > +- Squared covers +- +- +- toggleSetting('showControls')} +- > +- Show advanced controls +- +- +- { +- patch(getSetting('noAutoPause', true)); +- toggleSetting('noAutoPause'); +- }} +- > +- No auto pause +- +-
+- ) +-); +diff --git a/src/Powercord/plugins/pc-spotify/components/ShareModal.jsx b/src/Powercord/plugins/pc-spotify/components/ShareModal.jsx +deleted file mode 100644 +index dbbfd8b..0000000 +--- a/src/Powercord/plugins/pc-spotify/components/ShareModal.jsx ++++ /dev/null +@@ -1,65 +0,0 @@ +-const { React, messages, channels } = require('powercord/webpack'); +-const { FormTitle, Text } = require('powercord/components'); +-const { Modal } = require('powercord/components/modal'); +-const { close: closeModal } = require('powercord/modal'); +-const { SPOTIFY_DEFAULT_IMAGE } = require('../constants'); +- +-class Track extends React.PureComponent { +- handleClick (item) { +- return messages.sendMessage( +- channels.getChannelId(), +- { content: item.external_urls.spotify } +- ).then(() => closeModal()); +- } +- +- render () { +- const image = this.props.item.album.images[0] +- ? cover +- : cover; +- return ( +-
this.handleClick(this.props.item)}> +- {image} +- {this.props.item.name} +-
+- ); +- } +-} +- +-module.exports = class ShareModal extends React.PureComponent { +- constructor () { +- super(); +- +- this.state = { +- tracks: [] +- }; +- } +- +- async componentDidMount () { +- this.setState({ tracks: await this.props.tracks.items }); +- } +- +- render () { +- const { tracks } = this.state; +- const trackList = []; +- tracks.forEach(track => { +- trackList.push(); +- }); +- return ( +- +- +- Multiple tracks found - "{this.props.query}" +- closeModal()}/> +- +- +- +- Please select the track that you wish to share to the current channel. +- +- Available tracks +-
+- {trackList} +-
+-
+-
+- ); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/constants.js b/src/Powercord/plugins/pc-spotify/constants.js +deleted file mode 100644 +index 4245866..0000000 +--- a/src/Powercord/plugins/pc-spotify/constants.js ++++ /dev/null +@@ -1,21 +0,0 @@ +-module.exports = Object.freeze({ +- FluxActions: { +- DEVICES_FETCHED: 'SPOTIFY_DEVICES_FETCHED', +- CURRENT_TRACK_UPDATED: 'SPOTIFY_CURRENT_TRACK_UPDATED', +- PLAYER_STATE_UPDATED: 'SPOTIFY_PLAYER_STATE_UPDATED', +- LIBRARY_STATE_UPDATED: 'SPOTIFY_LIBRARY_STATE_UPDATED', +- SONGS_LOADED: 'SPOTIFY_SONGS_LOADED', +- TOP_SONGS_LOADED: 'SPOTIFY_TOP_SONGS_LOADED', +- ALBUMS_LOADED: 'SPOTIFY_ALBUMS_LOADED', +- ALBUM_TRACKS_LOADED: 'SPOTIFY_ALBUMS_TRACKS_LOADED', +- PLAYLISTS_LOADED: 'SPOTIFY_PLAYLISTS_LOADED', +- PLAYLIST_TRACKS_LOADED: 'SPOTIFY_PLAYLISTS_TRACKS_LOADED', +- PLAYLIST_TRACK_ADDED: 'SPOTIFY_PLAYLIST_TRACK_ADDED', +- PLAYLIST_TRACK_REMOVED: 'SPOTIFY_PLAYLIST_TRACK_REMOVED', +- PURGE_SONGS: 'SPOTIFY_PURGE_SONGS' +- }, +- SPOTIFY_BASE_URL: 'https://api.spotify.com/v1', +- SPOTIFY_PLAYER_URL: 'https://api.spotify.com/v1/me/player', +- SPOTIFY_DEFAULT_IMAGE: 'https://www.scdn.co/i/_global/favicon.png', +- SPOTIFY_COLOR: '#1ed860' +-}); +diff --git a/src/Powercord/plugins/pc-spotify/i18n/en-US.json b/src/Powercord/plugins/pc-spotify/i18n/en-US.json +deleted file mode 100644 +index 8174978..0000000 +--- a/src/Powercord/plugins/pc-spotify/i18n/en-US.json ++++ /dev/null +@@ -1,6 +0,0 @@ +-{ +- "SPOTIFY_NOT_PREMIUM": "You are not Spotify Premium.", +- "SPOTIFY_ADD_LIKED_SONGS": "Save to Liked Songs", +- "SPOTIFY_REMOVE_LIKED_SONGS": "Remove from Liked Songs", +- "SPOTIFY_CANT_LIKE_LOCAL": "You can't add a local song to your Liked Songs" +-} +diff --git a/src/Powercord/plugins/pc-spotify/i18n/index.js b/src/Powercord/plugins/pc-spotify/i18n/index.js +deleted file mode 100644 +index d59ce4c..0000000 +--- a/src/Powercord/plugins/pc-spotify/i18n/index.js ++++ /dev/null +@@ -1,7 +0,0 @@ +-require('fs') +- .readdirSync(__dirname) +- .filter((file) => file !== 'index.js' && file !== '.DS_Store') +- .forEach(filename => { +- const moduleName = filename.split('.')[0]; +- exports[moduleName] = require(`${__dirname}/${filename}`); +- }); +diff --git a/src/Powercord/plugins/pc-spotify/index.js b/src/Powercord/plugins/pc-spotify/index.js +deleted file mode 100644 +index 331f773..0000000 +--- a/src/Powercord/plugins/pc-spotify/index.js ++++ /dev/null +@@ -1,260 +0,0 @@ +-const { Plugin } = require('powercord/entities'); +-const { React, getModule, spotify, spotifySocket } = require('powercord/webpack'); +-const { inject, uninject } = require('powercord/injector'); +-const { waitFor, getOwnerInstance, findInTree, sleep } = require('powercord/util'); +-const playerStoreActions = require('./playerStore/actions'); +-const playerStore = require('./playerStore/store'); +-const songsStoreActions = require('./songsStore/actions'); +-const songsStore = require('./songsStore/store'); +-const i18n = require('./i18n'); +-const commands = require('./commands'); +- +-const SpotifyAPI = require('./SpotifyAPI'); +- +-const Settings = require('./components/Settings'); +-const Modal = require('./components/Modal'); +- +-class Spotify extends Plugin { +- get color () { +- return '#1ed860'; +- } +- +- get playerStore () { +- return playerStore; +- } +- +- get songsStore () { +- return songsStore; +- } +- +- get SpotifyAPI () { +- return SpotifyAPI; +- } +- +- startPlugin () { +- powercord.api.i18n.loadAllStrings(i18n); +- this.loadStylesheet('style.scss'); +- this._injectSocket(); +- this._injectModal(); +- this._patchAutoPause(); +- spotify.fetchIsSpotifyProtocolRegistered(); +- +- SpotifyAPI.getPlayer() +- .then((player) => this._handlePlayerState(player)) +- .catch((e) => this.error('Failed to get player', e)); +- +- playerStoreActions.fetchDevices() +- .catch((e) => this.error('Failed to fetch devices', e)); +- +- powercord.api.settings.registerSettings('pc-spotify', { +- category: this.entityID, +- label: 'Spotify', +- render: (props) => +- React.createElement(Settings, { +- patch: this._patchAutoPause.bind(this), +- ...props +- }) +- }); +- +- Object.values(commands).forEach(cmd => powercord.api.commands.registerCommand(cmd)); +- } +- +- pluginWillUnload () { +- uninject('pc-spotify-socket'); +- uninject('pc-spotify-modal'); +- // this._applySocketChanges(); +- this._patchAutoPause(true); +- Object.values(commands).forEach(cmd => powercord.api.commands.unregisterCommand(cmd.command)); +- powercord.api.settings.unregisterSettings('pc-spotify'); +- spotifySocket.getActiveSocketAndDevice()?.socket.socket.close(); +- songsStoreActions.purgeSongs(); +- +- const { container } = getModule([ 'container', 'usernameContainer' ], false); +- const accountContainer = document.querySelector(`section > .${container}`); +- const instance = getOwnerInstance(accountContainer); +- instance.forceUpdate(); +- } +- +- async _injectSocket () { +- const { SpotifySocket } = await getModule([ 'SpotifySocket' ]); +- inject('pc-spotify-socket', SpotifySocket.prototype, 'handleMessage', ([ e ]) => this._handleSpotifyMessage(e)); +- spotifySocket.getActiveSocketAndDevice()?.socket.socket.close(); +- } +- +- async _injectModal () { +- await sleep(1e3); // It ain't stupid if it works +- const { container } = await getModule([ 'container', 'usernameContainer' ]); +- const accountContainer = await waitFor(`section > .${container}`); +- const instance = getOwnerInstance(accountContainer); +- await inject('pc-spotify-modal', instance.__proto__, 'render', (_, res) => { +- const realRes = findInTree(res, t => t.props && t.props.className === container); +- return [ +- React.createElement(Modal, { +- entityID: this.entityID, +- base: realRes +- }), +- res +- ]; +- }); +- instance.forceUpdate(); +- } +- +- _patchAutoPause (revert) { +- if (this.settings.get('noAutoPause', true)) { +- const spotifyMdl = getModule([ 'initialize', 'wasAutoPaused' ], false); +- if (revert) { +- spotifyMdl.wasAutoPaused = spotifyMdl._wasAutoPaused; +- spotify.pause = spotify._pause; +- } else { +- spotifyMdl._wasAutoPaused = spotifyMdl.wasAutoPaused; +- spotifyMdl.wasAutoPaused = () => false; +- spotify._pause = spotify.pause; +- spotify.pause = () => void 0; +- } +- } +- } +- +- _handleSpotifyMessage (msg) { +- const data = JSON.parse(msg.data); +- if (!data.type === 'message' || !data.payloads) { +- return; +- } +- +- const collectionRegex = /hm:\/\/collection\/collection\/[\w\d]+\/json/i; +- const playlistRegex = /hm:\/\/playlist\/v2\/playlist\/[\w\d]+/i; +- switch (true) { +- case data.uri === 'wss://event': +- for (const payload of data.payloads || []) { +- for (const event of payload.events || []) { +- this._handleSpotifyEvent(event); +- } +- } +- break; +- case collectionRegex.test(data.uri): { +- const currentTrack = playerStore.getCurrentTrack(); +- if (!currentTrack) { +- // Useless to further process the event +- return; +- } +- +- for (const rawPayload of data.payloads || []) { +- const payload = JSON.parse(rawPayload); +- for (const track of payload.items) { +- if (track.identifier === currentTrack.id) { +- playerStoreActions.updateCurrentLibraryState( +- track.removed ? playerStore.LibraryState.NOT_IN_LIBRARY : playerStore.LibraryState.IN_LIBRARY +- ); +- } +- } +- } +- break; +- } +- case playlistRegex.test(data.uri): +- for (const hermes of data.payloads || []) { +- const payload = this._decodePlaylistHermes(hermes); +- if (payload.added) { +- songsStoreActions.addTrack(payload.playlistId, payload.trackId); +- } else { +- songsStoreActions.deleteTrack(payload.playlistId, payload.trackId); +- } +- } +- break; +- } +- } +- +- _handleSpotifyEvent (evt) { +- switch (evt.type) { +- case 'PLAYER_STATE_CHANGED': +- this._handlePlayerState(evt.event.state); +- break; +- case 'DEVICE_STATE_CHANGED': +- playerStoreActions.fetchDevices(); +- break; +- } +- } +- +- _handlePlayerState (state) { +- if (!state.timestamp) { +- return; +- } +- +- // Handle track +- const currentTrack = playerStore.getCurrentTrack(); +- if (!currentTrack || currentTrack.id !== state.item.id) { +- if (this._libraryTimeout) { +- clearTimeout(this._libraryTimeout); +- } +- if (!state.item.is_local && powercord.account && powercord.account.accounts.spotify) { +- this._libraryTimeout = setTimeout(() => { +- SpotifyAPI.checkLibrary(state.item.id).then(r => playerStoreActions.updateCurrentLibraryState( +- r.body[0] +- ? playerStore.LibraryState.IN_LIBRARY +- : playerStore.LibraryState.NOT_IN_LIBRARY +- )); +- }, 1500); +- } else if (state.item.is_local) { +- playerStoreActions.updateCurrentLibraryState(playerStore.LibraryState.LOCAL_SONG); +- } +- playerStoreActions.updateCurrentTrack({ +- id: state.item.id, +- uri: state.item.uri, +- name: state.item.name, +- isLocal: state.item.is_local, +- duration: state.item.duration_ms, +- explicit: state.item.explicit, +- cover: state.item.album && state.item.album.images[0] ? state.item.album.images[0].url : null, +- artists: state.item.artists.map(a => a.name).join(', '), +- album: state.item.album ? state.item.album.name : null, +- urls: { +- track: state.item.external_urls.spotify, +- album: state.item.album ? state.item.album.external_urls.spotify : null +- } +- }); +- } +- +- // Handle state +- playerStoreActions.updatePlayerState({ +- repeat: state.repeat_state === 'track' +- ? playerStore.RepeatState.REPEAT_TRACK +- : state.repeat_state === 'context' +- ? playerStore.RepeatState.REPEAT_CONTEXT +- : playerStore.RepeatState.NO_REPEAT, +- shuffle: state.shuffle_state, +- canRepeat: !state.actions.disallows.toggling_repeat_context, +- canRepeatOne: !state.actions.disallows.toggling_repeat_track, +- canShuffle: !state.actions.disallows.toggling_shuffle, +- spotifyRecordedProgress: state.progress_ms, +- playing: state.is_playing, +- volume: state.device.volume_percent +- }); +- } +- +- _decodePlaylistHermes (hermes) { +- const hex = Buffer.from(hermes, 'base64').toString('hex'); +- const decoded = this._decodeHermes(hex); +- const trackDetails = this._decodeHermes(decoded[3].hex.substring(18)); +- return { +- playlistId: decoded[0].utf8.split(':').pop(), +- trackId: trackDetails[0].utf8.replace(/[\n$]/g, '').split(':').pop(), +- added: trackDetails.length === 2 +- }; +- } +- +- _decodeHermes (hex) { +- const res = []; +- for (let i = 0; i < hex.length;) { +- const length = parseInt(hex.substring(i + 2, i + 4), 16); +- const rawStr = hex.substring(i + 4, i + 4 + (length * 2)); +- i += (length * 2) + 4; +- res.push({ +- hex: rawStr, +- get utf8 () { +- return Buffer.from(rawStr, 'hex').toString('utf8'); +- } +- }); +- } +- return res; +- } +-} +- +-module.exports = Spotify; +diff --git a/src/Powercord/plugins/pc-spotify/manifest.json b/src/Powercord/plugins/pc-spotify/manifest.json +deleted file mode 100644 +index d3b4bf4..0000000 +--- a/src/Powercord/plugins/pc-spotify/manifest.json ++++ /dev/null +@@ -1,7 +0,0 @@ +-{ +- "name": "Spotify Modal", +- "version": "2.0.0", +- "description": "Better Spotify integration in Discord", +- "author": "Powercord Team", +- "license": "MIT" +-} +diff --git a/src/Powercord/plugins/pc-spotify/playerStore/actions.js b/src/Powercord/plugins/pc-spotify/playerStore/actions.js +deleted file mode 100644 +index efaa990..0000000 +--- a/src/Powercord/plugins/pc-spotify/playerStore/actions.js ++++ /dev/null +@@ -1,37 +0,0 @@ +-const { FluxDispatcher } = require('powercord/webpack'); +-const { FluxActions } = require('../constants'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-module.exports = { +- fetchDevices: async () => { +- const { devices } = await SpotifyAPI.getDevices(); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.DEVICES_FETCHED, +- devices +- }); +- }, +- +- updateCurrentTrack: (newTrack) => { +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.CURRENT_TRACK_UPDATED, +- track: newTrack +- }); +- }, +- +- updatePlayerState: (newState) => { +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.PLAYER_STATE_UPDATED, +- state: { +- ...newState, +- spotifyRecordedProgressAt: Date.now() +- } +- }); +- }, +- +- updateCurrentLibraryState: (newState) => { +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.LIBRARY_STATE_UPDATED, +- state: newState +- }); +- } +-}; +diff --git a/src/Powercord/plugins/pc-spotify/playerStore/store.js b/src/Powercord/plugins/pc-spotify/playerStore/store.js +deleted file mode 100644 +index 96bb284..0000000 +--- a/src/Powercord/plugins/pc-spotify/playerStore/store.js ++++ /dev/null +@@ -1,98 +0,0 @@ +-const { Flux, FluxDispatcher } = require('powercord/webpack'); +-const { FluxActions } = require('../constants'); +- +-const LibraryState = Object.freeze({ +- UNKNOWN: 'UNKNOWN', +- IN_LIBRARY: 'IN_LIBRARY', +- NOT_IN_LIBRARY: 'NOT_IN_LIBRARY', +- LOCAL_SONG: 'LOCAL_SONG' +-}); +- +-const RepeatState = Object.freeze({ +- NO_REPEAT: 'NO_REPEAT', +- REPEAT_TRACK: 'REPEAT_TRACK', +- REPEAT_CONTEXT: 'REPEAT_CONTEXT' +-}); +- +-let lastActiveDeviceId = null; +-let devices = []; +-let currentTrack = null; +-let currentLibraryState = LibraryState.UNKNOWN; +-let playerState = { +- repeat: RepeatState.NO_REPEAT, +- shuffle: false, +- canRepeat: true, +- canRepeatOne: true, +- canShuffle: true, +- spotifyRecordedProgress: 0, +- spotifyRecordedProgressAt: Date.now(), +- playing: false, +- volume: 100 +-}; +- +-function handleDevicesFetched (fetchedDevices) { +- devices = fetchedDevices; +- const activeDevice = devices.find(d => d.is_active); +- if (activeDevice) { +- lastActiveDeviceId = activeDevice.id; +- } +-} +- +-function handleCurrentTrackUpdated (track) { +- currentLibraryState = LibraryState.UNKNOWN; +- currentTrack = track; +-} +- +-function handlePlayerStateUpdated (state) { +- playerState = state; +-} +- +-function handleLibraryStateUpdated (state) { +- currentLibraryState = state; +-} +- +-class SpotifyPlayerStore extends Flux.Store { +- get LibraryState () { +- return LibraryState; +- } +- +- get RepeatState () { +- return RepeatState; +- } +- +- getStore () { +- return { +- devices, +- currentTrack, +- currentLibraryState, +- playerState +- }; +- } +- +- getDevices () { +- return devices; +- } +- +- getLastActiveDeviceId () { +- return lastActiveDeviceId; +- } +- +- getCurrentTrack () { +- return currentTrack; +- } +- +- getCurrentLibraryState () { +- return currentLibraryState; +- } +- +- getPlayerState () { +- return playerState; +- } +-} +- +-module.exports = new SpotifyPlayerStore(FluxDispatcher, { +- [FluxActions.DEVICES_FETCHED]: ({ devices }) => handleDevicesFetched(devices), +- [FluxActions.CURRENT_TRACK_UPDATED]: ({ track }) => handleCurrentTrackUpdated(track), +- [FluxActions.PLAYER_STATE_UPDATED]: ({ state }) => handlePlayerStateUpdated(state), +- [FluxActions.LIBRARY_STATE_UPDATED]: ({ state }) => handleLibraryStateUpdated(state) +-}); +diff --git a/src/Powercord/plugins/pc-spotify/songsStore/actions.js b/src/Powercord/plugins/pc-spotify/songsStore/actions.js +deleted file mode 100644 +index 6ebeb6b..0000000 +--- a/src/Powercord/plugins/pc-spotify/songsStore/actions.js ++++ /dev/null +@@ -1,115 +0,0 @@ +-const { FluxDispatcher } = require('powercord/webpack'); +-const { FluxActions } = require('../constants'); +-const SpotifyAPI = require('../SpotifyAPI'); +- +-function formatTracks (spotifyTracks) { +- return Object.fromEntries( +- spotifyTracks.map(t => [ +- t.id || t.uri, +- { +- uri: t.uri, +- name: t.name, +- isLocal: t.is_local, +- duration: t.duration_ms, +- explicit: t.explicit +- } +- ]) +- ); +-} +- +-module.exports = { +- loadSongs: async () => { +- const songs = await SpotifyAPI.getSongs(); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.SONGS_LOADED, +- songs: formatTracks(songs.map(s => s.track)) +- }); +- }, +- +- loadTopSongs: async () => { +- const topSongs = await SpotifyAPI.getTopSongs(); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.TOP_SONGS_LOADED, +- topSongs: formatTracks(topSongs.items) +- }); +- }, +- +- loadAlbums: async () => { +- const albums = await SpotifyAPI.getAlbums(); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.ALBUMS_LOADED, +- albums: Object.fromEntries( +- albums.map(({ album }) => [ +- album.id, +- { +- uri: album.uri, +- name: album.name, +- tracks: formatTracks(album.tracks.items), +- tracksLoaded: !album.tracks.next +- } +- ]) +- ) +- }); +- +- albums.filter(({ album: { tracks: { next } } }) => !next).forEach(async ({ album: { id, tracks } }) => { +- const albumTracks = await SpotifyAPI.getAlbumTracks(id, tracks.limit, tracks.limit); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.ALBUM_TRACKS_LOADED, +- albumId: id, +- tracks: formatTracks(tracks.items.concat(albumTracks)) +- }); +- }); +- }, +- +- loadPlaylists: async () => { +- const playlists = await SpotifyAPI.getPlaylists(); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.PLAYLISTS_LOADED, +- playlists: Object.fromEntries( +- playlists.map(playlist => [ +- playlist.id, +- { +- uri: playlist.uri, +- name: playlist.name, +- icon: playlist.images[0] ? playlist.images[0].url : null, +- editable: playlist.owner.display_name === powercord.account.accounts.spotify || playlist.collaborative, +- tracksLoaded: false +- } +- ]) +- ) +- }); +- +- playlists.forEach(async ({ id }) => { +- const playlistTracks = await SpotifyAPI.getPlaylistTracks(id); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.PLAYLIST_TRACKS_LOADED, +- playlistId: id, +- tracks: formatTracks(playlistTracks.map(pt => pt.track)) +- }); +- }); +- }, +- +- addTrack: async (playlistId, trackId) => { +- const track = await SpotifyAPI.getTrack(trackId); +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.PLAYLIST_TRACK_ADDED, +- playlistId, +- trackId, +- trackDetails: { +- name: track.name, +- duration: track.duration_ms, +- explicit: track.explicit +- } +- }); +- }, +- +- deleteTrack: (playlistId, trackId) => { +- FluxDispatcher.dirtyDispatch({ +- type: FluxActions.PLAYLIST_TRACK_REMOVED, +- playlistId, +- trackId +- }); +- }, +- +- purgeSongs: () => FluxDispatcher.dirtyDispatch({ type: FluxActions.PURGE_SONGS }) +-}; +diff --git a/src/Powercord/plugins/pc-spotify/songsStore/store.js b/src/Powercord/plugins/pc-spotify/songsStore/store.js +deleted file mode 100644 +index 1c66283..0000000 +--- a/src/Powercord/plugins/pc-spotify/songsStore/store.js ++++ /dev/null +@@ -1,161 +0,0 @@ +-const { Flux, FluxDispatcher } = require('powercord/webpack'); +-const { FluxActions } = require('../constants'); +- +-let songsLoaded = false; +-let topSongsLoaded = false; +-let albumsLoaded = false; +-let playlistsLoaded = false; +- +-let songs = {}; +-let topSongs = {}; +-let albums = {}; +-let playlists = {}; +- +-function handleTopSongsLoaded (topSongsData) { +- topSongsLoaded = true; +- topSongs = topSongsData; +-} +- +-function handleSongsLoaded (songsData) { +- songsLoaded = true; +- songs = songsData; +-} +- +-function handleAlbumsLoaded (albumsData) { +- albumsLoaded = true; +- albums = albumsData; +-} +- +-function handleAlbumTracksLoaded (albumId, tracks) { +- albums = { +- ...albums, +- [albumId]: { +- ...albums[albumId], +- tracksLoaded: true, +- tracks +- } +- }; +-} +- +-function handlePlaylistsLoaded (playlistsData) { +- playlistsLoaded = true; +- playlists = playlistsData; +-} +- +-function handlePlaylistTracksLoaded (playlistId, tracks) { +- playlists = { +- ...playlists, +- [playlistId]: { +- ...playlists[playlistId], +- tracksLoaded: true, +- tracks +- } +- }; +-} +- +-function handlePlaylistTrackAdded (playlistId, trackId, trackDetails) { +- if (playlists[playlistId]) { // If the playlist doesn't exist it means it hasn't been loaded; Let's skip the event +- playlists = { +- ...playlists, +- [playlistId]: { +- ...playlists[playlistId], +- tracks: { +- ...playlists[playlistId].tracks, +- [trackId]: trackDetails +- } +- } +- }; +- } +-} +- +-function handlePlaylistTrackRemoved (playlistId, trackId) { +- if (playlists[playlistId]) { // If the playlist doesn't exist it means it hasn't been loaded; Let's skip the event +- delete playlists[playlistId].tracks[trackId]; +- playlists = global._.cloneDeep(playlists); +- } +-} +- +-function handlePurgeSongs () { +- songsLoaded = false; +- topSongsLoaded = false; +- albumsLoaded = false; +- playlistsLoaded = false; +- songs = {}; +- topSongs = {}; +- albums = {}; +- playlists = {}; +-} +- +-class SpotifyPlaylistsStore extends Flux.Store { +- getStore () { +- return { +- songsLoaded, +- topSongsLoaded, +- albumsLoaded, +- playlistsLoaded, +- songs, +- topSongs, +- albums, +- playlists +- }; +- } +- +- getSongsLoaded () { +- return songsLoaded; +- } +- +- getSongs () { +- return songs; +- } +- +- getTopSongsLoaded () { +- return topSongsLoaded; +- } +- +- getTopSongs () { +- return topSongs; +- } +- +- getPlaylistsLoaded () { +- return playlistsLoaded; +- } +- +- getPlaylists () { +- return playlists; +- } +- +- getPlaylist (playlistId) { +- return playlists[playlistId]; +- } +- +- isInPlaylist (playlistId, trackId) { +- if (!playlists[playlistId]) { +- return false; +- } +- return Object.keys(playlists[playlistId].tracks).includes(trackId); +- } +- +- getAlbumsLoaded () { +- return albumsLoaded; +- } +- +- getAlbumbs () { +- return albums; +- } +- +- getAlbum (albumId) { +- return albums[albumId]; +- } +-} +- +-module.exports = new SpotifyPlaylistsStore(FluxDispatcher, { +- [FluxActions.SONGS_LOADED]: ({ songs }) => handleSongsLoaded(songs), +- [FluxActions.TOP_SONGS_LOADED]: ({ topSongs }) => handleTopSongsLoaded(topSongs), +- [FluxActions.ALBUMS_LOADED]: ({ albums }) => handleAlbumsLoaded(albums), +- [FluxActions.ALBUM_TRACKS_LOADED]: ({ albumId, tracks }) => handleAlbumTracksLoaded(albumId, tracks), +- [FluxActions.PLAYLISTS_LOADED]: ({ playlists }) => handlePlaylistsLoaded(playlists), +- [FluxActions.PLAYLIST_TRACKS_LOADED]: ({ playlistId, tracks }) => handlePlaylistTracksLoaded(playlistId, tracks), +- [FluxActions.PLAYLIST_TRACK_ADDED]: ({ playlistId, trackId, trackDetails }) => handlePlaylistTrackAdded(playlistId, trackId, trackDetails), +- [FluxActions.PLAYLIST_TRACK_REMOVED]: ({ playlistId, trackId }) => handlePlaylistTrackRemoved(playlistId, trackId), +- [FluxActions.PURGE_SONGS]: () => handlePurgeSongs() +-}); +diff --git a/src/Powercord/plugins/pc-spotify/style.scss b/src/Powercord/plugins/pc-spotify/style.scss +deleted file mode 100644 +index a4a742a..0000000 +--- a/src/Powercord/plugins/pc-spotify/style.scss ++++ /dev/null +@@ -1,182 +0,0 @@ +-:root { +- --spotify-color: #1ed860; +-} +- +-.powercord-spotify { +- img { +- margin-bottom: -4px; +- } +- +- .spotify-buttons button { +- color: var(--spotify-color) !important; +- +- > div { +- color: inherit; +- } +- } +- +- .spotify-extra-controls { +- pointer-events: none; +- display: flex; +- align-items: center; +- justify-content: center; +- margin-top: -10px; +- opacity: 0; +- height: 0; +- transition: height .2s, opacity .2s; +- +- .active { +- color: var(--spotify-color) !important; +- } +- +- + .spotify-seek { +- margin-top: 3px; +- } +- } +- +- .spotify-seek { +- margin-top: -6px; +- transition: margin-top .2s; +- +- &-elements { +- display: flex; +- justify-content: space-between; +- color: var(--text-normal); +- font-size: 12px; +- padding: 0 4px 4px; +- font-weight: 500; +- opacity: 0; +- height: 4px; +- transition: height .2s, opacity .2s; +- } +- +- &-bar { +- position: relative; +- height: 2px; +- transition: height .2s; +- +- &-progress { +- height: 100%; +- display: block; +- background-color: var(--spotify-color); +- } +- +- &-cursor { +- top: 50%; +- position: absolute; +- transform: translateY(-50%) translateX(-50%); +- width: 8px; +- height: 8px; +- border-radius: 50%; +- border: 2px #b9bbbe solid; +- background-color: #fff; +- opacity: 0; +- transition: opacity .2s; +- } +- } +- +- &:not(.no-premium) { +- .spotify-seek-bar-progress, .spotify-seek-bar-cursor { +- cursor: pointer; +- } +- } +- } +- +- &.hover { +- .spotify-extra-controls { +- pointer-events: initial; +- height: 32px; +- opacity: 1; +- +- + .spotify-seek { +- margin-top: -12px; +- } +- } +- +- .spotify-seek { +- &-elements { +- opacity: 1; +- height: 12px; +- } +- +- &:not(.no-premium) { +- .spotify-seek-bar { +- height: 4px; +- +- &-cursor { +- opacity: 1; +- } +- } +- } +- } +- } +-} +- +-.spotify-tooltip { +- max-width: none !important; +- white-space: nowrap +-} +- +-.spotify-add-to-playlist { +- .playlists { +- margin-top: 20px; +- } +- +- .track, .playlist { +- display: flex; +- align-items: center; +- +- img { +- width: 48px; +- height: 48px; +- margin-right: 16px; +- } +- +- .details { +- display: flex; +- flex-direction: column; +- +- .title { +- font-size: 18px; +- color: var(--header-primary); +- margin-bottom: 4px; +- } +- +- .artist { +- font-size: 16px; +- color: var(--header-secondary); +- } +- } +- } +- +- .playlist { +- display: flex; +- padding: 16px; +- margin-bottom: 16px; +- +- .details { +- margin-right: auto; +- } +- } +-} +- +-#powercord-spotify-menu { +- .disabled-1WRMNA[id^='powercord-spotify-menu-devices--'], #powercord-spotify-menu-repeat + div [id*='-active'] { +- color: var(--spotify-color) !important; +- opacity: 1 !important; +- } +- +- .colorDefault-2K3EoJ .checkbox-3s5GYZ { +- color: var(--spotify-color); +- } +- +- .barFill-23-gu- { +- background-color: var(--spotify-color); +- } +-} +- +-.theme-dark { +- .powercord-spotify img[src*='_global/favicon.png'], .spotify-add-to-playlist img[src*='_global/favicon.png'] { +- filter: invert(1); +- } +-} diff --git a/subflakes/powercord/patches/remove-updater.patch b/subflakes/powercord/patches/remove-updater.patch new file mode 100644 index 0000000..a8b83bf --- /dev/null +++ b/subflakes/powercord/patches/remove-updater.patch @@ -0,0 +1,1186 @@ +diff --git a/src/Powercord/plugins/pc-updater/components/Icons.jsx b/src/Powercord/plugins/pc-updater/components/Icons.jsx +deleted file mode 100644 +index dc8a71d..0000000 +--- a/src/Powercord/plugins/pc-updater/components/Icons.jsx ++++ /dev/null +@@ -1,52 +0,0 @@ +-const { React } = require('powercord/webpack'); +- +-module.exports = { +- UpToDate: () => +- +- , +- Update: ({ color, animated }) => +- +- +- +- , +- Paused: () => +- +- , +- Error: () => +- +- , +- +- Powercord: (props) => +- +- , +- Plugin: (props) => +- +- , +- Theme: (props) => +- +- +-}; +diff --git a/src/Powercord/plugins/pc-updater/components/Settings.jsx b/src/Powercord/plugins/pc-updater/components/Settings.jsx +deleted file mode 100644 +index a1b8b9b..0000000 +--- a/src/Powercord/plugins/pc-updater/components/Settings.jsx ++++ /dev/null +@@ -1,464 +0,0 @@ +-const { React, getModule, i18n: { Messages, _chosenLocale: currentLocale } } = require('powercord/webpack'); +-const { Button, FormNotice, FormTitle, Tooltip, Icons: { FontAwesome } } = require('powercord/components'); +-const { SwitchItem, TextInput, Category, ButtonItem } = require('powercord/components/settings'); +-const { open: openModal, close: closeModal } = require('powercord/modal'); +-const { Confirm } = require('powercord/components/modal'); +-const { REPO_URL, CACHE_FOLDER } = require('powercord/constants'); +-const { clipboard } = require('electron'); +-const { readdirSync, existsSync, lstatSync } = require('fs'); +- +-const Icons = require('./Icons'); +-const Update = require('./Update'); +- +-module.exports = class UpdaterSettings extends React.PureComponent { +- constructor () { +- super(); +- this.plugin = powercord.pluginManager.get('pc-updater'); +- this.state = { +- opened: false, +- copied: false +- }; +- } +- +- render () { +- const isUnsupported = window.GLOBAL_ENV.RELEASE_CHANNEL !== 'canary'; +- const moment = getModule([ 'momentProperties' ], false); +- // @todo: Make this be in its own store +- const awaitingReload = this.props.getSetting('awaiting_reload', false); +- const updating = this.props.getSetting('updating', false); +- const checking = this.props.getSetting('checking', false); +- const disabled = this.props.getSetting('disabled', false); +- const paused = this.props.getSetting('paused', false); +- const failed = this.props.getSetting('failed', false); +- +- const updates = this.props.getSetting('updates', []); +- const disabledEntities = this.props.getSetting('entities_disabled', []); +- const checkingProgress = this.props.getSetting('checking_progress', [ 0, 0 ]); +- const last = moment(this.props.getSetting('last_check', false)).calendar(); +- +- let icon, +- title; +- if (disabled) { +- icon = ; +- title = Messages.POWERCORD_UPDATES_DISABLED; +- } else if (paused) { +- icon = ; +- title = Messages.POWERCORD_UPDATES_PAUSED; +- } else if (checking) { +- icon = ; +- title = Messages.POWERCORD_UPDATES_CHECKING; +- } else if (updating) { +- icon = ; +- title = Messages.POWERCORD_UPDATES_UPDATING; +- } else if (failed) { +- icon = ; +- title = Messages.POWERCORD_UPDATES_FAILED; +- } else if (updates.length > 0) { +- icon = ; +- title = Messages.POWERCORD_UPDATES_AVAILABLE; +- } else { +- icon = ; +- title = Messages.POWERCORD_UPDATES_UP_TO_DATE; +- } +- +- return
+- {awaitingReload +- ? this.renderReload() +- : isUnsupported && this.renderUnsupported()} +-
+-
{icon}
+-
+-

{title}

+- {!disabled && !updating && (!checking || checkingProgress[1] > 0) &&
+- {paused +- ? Messages.POWERCORD_UPDATES_PAUSED_RESUME +- : checking +- ? Messages.POWERCORD_UPDATES_CHECKING_STATUS.format({ +- checked: checkingProgress[0], +- total: checkingProgress[1] +- }) +- : Messages.POWERCORD_UPDATES_LAST_CHECKED.format({ date: last })} +-
} +-
+-
+-
+- {Messages.POWERCORD_UPDATES_UPSTREAM} +- {powercord.gitInfos.upstream.replace(REPO_URL, Messages.POWERCORD_UPDATES_UPSTREAM_OFFICIAL)} +-
+-
+- {Messages.POWERCORD_UPDATES_REVISION} +- {powercord.gitInfos.revision.substring(0, 7)} +-
+-
+- {Messages.POWERCORD_UPDATES_BRANCH} +- {powercord.gitInfos.branch} +-
+-
+-
+-
+- {disabled || paused +- ? +- : (!checking && !updating && <> +- {updates.length > 0 && } +- +- +- +- )} +-
+- {!disabled && !paused && !checking && updates.length > 0 &&
+- {updates.map(update => this.askSkipUpdate(() => this.plugin.skipUpdate(update.id, update.commits[0].id))} +- onDisable={() => this.askDisableUpdates(false, () => this.plugin.disableUpdates(update))} +- />)} +-
} +- +- {disabledEntities.length > 0 && this.setState({ opened: !this.state.opened })} +- > +- {disabledEntities.map(entity =>
+-
+-
+- +- {React.createElement(Icons[entity.icon])} +- +-
+-
{entity.name}
+-
+- +-
+-
+-
)} +-
} +- {Messages.OPTIONS} +- {!disabled && <> +- this.props.toggleSetting('automatic')} +- note={Messages.POWERCORD_UPDATES_OPTS_AUTO_DESC} +- > +- {Messages.POWERCORD_UPDATES_OPTS_AUTO} +- +- this.props.updateSetting('interval', (Number(val) && Number(val) >= 10) ? Math.ceil(Number(val)) : 10, 15)} +- defaultValue={this.props.getSetting('interval', 15)} +- required={true} +- > +- {Messages.POWERCORD_UPDATES_OPTS_INTERVAL} +- +- this.props.updateSetting('concurrency', (Number(val) && Number(val) >= 1) ? Math.ceil(Number(val)) : 1, 2)} +- defaultValue={this.props.getSetting('concurrency', 2)} +- required={true} +- > +- {Messages.POWERCORD_UPDATES_OPTS_CONCURRENCY} +- +- this.plugin.openChangeLogs()} +- > +- {Messages.POWERCORD_UPDATES_OPTS_CHANGE_LOGS} +- +- {/* this.askChangeChannel( +- () => this.plugin.changeBranch(powercord.gitInfos.branch === 'v2' ? 'v2-dev' : 'v2') +- )} +- > +- {Messages.POWERCORD_UPDATES_OPTS_RELEASE} +- */} +- +- this.setState({ debugInfoOpened: !this.state.debugInfoOpened })} +- > +- {this.renderDebugInfo(moment)} +- +- } +-
; +- } +- +- // --- PARTS +- renderReload () { +- const body = <> +-

{Messages.POWERCORD_UPDATES_AWAITING_RELOAD_DESC}

+- +- ; +- return this._renderFormNotice(Messages.POWERCORD_UPDATES_AWAITING_RELOAD_TITLE, body); +- } +- +- renderUnsupported () { +- const body =

+- {Messages.POWERCORD_UPDATES_UNSUPPORTED_DESC.format({ releaseChannel: window.GLOBAL_ENV.RELEASE_CHANNEL })} +-

; +- return this._renderFormNotice(Messages.POWERCORD_UPDATES_UNSUPPORTED_TITLE, body); +- } +- +- _renderFormNotice (title, body) { +- return ; +- } +- +- // --- PROMPTS +- askSkipUpdate (callback) { +- this._ask( +- Messages.POWERCORD_UPDATES_SKIP_MODAL_TITLE, +- Messages.POWERCORD_UPDATES_SKIP_MODAL, +- Messages.POWERCORD_UPDATES_SKIP, +- callback +- ); +- } +- +- askPauseUpdates () { +- this._ask( +- Messages.POWERCORD_UPDATES_PAUSE, +- Messages.POWERCORD_UPDATES_PAUSE_MODAL, +- Messages.POWERCORD_UPDATES_PAUSE, +- () => this.props.updateSetting('paused', true) +- ); +- } +- +- askDisableUpdates (all, callback) { +- this._ask( +- Messages.POWERCORD_UPDATES_DISABLE, +- all ? Messages.POWERCORD_UPDATES_DISABLE_MODAL_ALL : Messages.POWERCORD_UPDATES_DISABLE_MODAL, +- Messages.POWERCORD_UPDATES_DISABLE, +- callback +- ); +- } +- +- askChangeChannel (callback) { +- this._ask( +- Messages.POWERCORD_UPDATES_OPTS_RELEASE_MODAL_HEADER, +- Messages.POWERCORD_UPDATES_OPTS_RELEASE_MODAL, +- Messages.POWERCORD_UPDATES_OPTS_RELEASE_SWITCH, +- callback +- ); +- } +- +- _ask (title, content, confirm, callback, red = true) { +- openModal(() => +-
{content}
+-
); +- } +- +- // --- DEBUG STUFF (Intentionally left english-only) +- renderDebugInfo () { +- const { getRegisteredExperiments, getExperimentOverrides } = getModule([ 'initialize', 'getExperimentOverrides' ], false); +- const { apiManager: { apis }, api: { commands: { commands }, settings: { store: settingsStore } } } = powercord; +- const superProperties = getModule([ 'getSuperPropertiesBase64' ], false).getSuperProperties(); +- const unauthorizedPlugins = Array.from(powercord.pluginManager.plugins.values()).filter(plugin => +- plugin.__shortCircuit).map(plugin => plugin.manifest.name); +- const plugins = powercord.pluginManager.getPlugins().filter(plugin => +- !powercord.pluginManager.get(plugin).isInternal && powercord.pluginManager.isEnabled(plugin) +- ); +- +- const enabledLabs = powercord.api.labs.experiments.filter(e => powercord.api.labs.isExperimentEnabled(e.id)); +- const experimentOverrides = Object.keys(getExperimentOverrides()).length; +- const availableExperiments = Object.keys(getRegisteredExperiments()).length; +- +- const discordPath = process.resourcesPath.slice(0, -10); +- const maskPath = (path) => { +- path = path.replace(/(?:\/home\/|C:\\Users\\|\/Users\/)([ \w.-]+).*/i, (path, username) => { +- const usernameIndex = path.indexOf(username); +- return [ path.slice(0, usernameIndex), username.charAt(0) + username.slice(1).replace(/[a-zA-Z]/g, '*'), +- path.slice(usernameIndex + username.length) ].join(''); +- }); +- +- return path; +- }; +- +- const cachedFiles = (existsSync(CACHE_FOLDER) && readdirSync(CACHE_FOLDER) +- .filter(d => lstatSync(`${CACHE_FOLDER}/${d}`).isDirectory()) +- .map(d => readdirSync(`${CACHE_FOLDER}/${d}`)) +- .flat().length) || 'n/a'; +- +- const createPathReveal = (title, path) => +- ; +- +- return +- +- System / Discord +-
+-
Locale: {currentLocale}
+-
OS: {window.platform.os.family}{window.platform.os.architecture === 64 ? ' 64-bit' : ''}
+-
Architecture: {superProperties.os_arch}
+- {process.platform === 'linux' && ( +-
Distro: {superProperties.distro || 'n/a'}
+- )} +-
Release Channel: {superProperties.release_channel}
+-
App Version: {superProperties.client_version}
+-
Build Number: {superProperties.client_build_number}
+-
Build ID: {window.GLOBAL_ENV.SENTRY_TAGS.buildId.slice(0, 7)}
+-
Experiments: {experimentOverrides} / {availableExperiments}
+-
+- +- Process Versions +-
+-
React: {React.version}
+- {[ 'electron', 'chrome', 'node' ].map(proc => +-
{proc.charAt(0).toUpperCase() + proc.slice(1)}: {process.versions[proc]}
+- )} +-
+- +- Powercord +-
+-
Commands: {Object.keys(commands).length}
+-
Settings: {Object.keys(settingsStore.getAllSettings()).length}
+-
Plugins: {powercord.pluginManager.getPlugins() +- .filter(plugin => powercord.pluginManager.isEnabled(plugin)).length} / {powercord.pluginManager.plugins.size} +-
+-
Themes: {powercord.styleManager.getThemes() +- .filter(theme => powercord.styleManager.isEnabled(theme)).length} / {powercord.styleManager.themes.size} +-
+-
Labs: {enabledLabs.length} / {powercord.api.labs.experiments.length} +-
+-
{`Settings Sync:\n${powercord.settings.get('settingsSync', false)}`}
+-
Cached Files: {cachedFiles}
+-
{`Account:\n${!!powercord.account}`}
+-
APIs: {apis.length}
+-
+- +- Git +-
+-
Upstream: {powercord.gitInfos.upstream.replace(REPO_URL, 'Official')}
+- +-
Branch: {powercord.gitInfos.branch}
+-
{`Latest:\n${!this.props.getSetting('updates', []).find(update => update.id === 'powercord')}`}
+-
+- +- Listings +-
+- {createPathReveal('Powercord Path', powercord.basePath)} +- {createPathReveal('Discord Path', discordPath)} +-
Experiments: {experimentOverrides > 0 ? Object.keys(getExperimentOverrides()).join(', ') : 'n/a'}
+-
Labs: +- {enabledLabs.length ? enabledLabs.map(e => e.name).join(', ') : 'n/a'} +-
+-
+- Plugins: +- {(plugins.length > 6 ? `${(this.state.pluginsRevealed ? plugins : plugins.slice(0, 6)).join(', ')};` : plugins.join(', ')) || 'n/a'}  +- {plugins.length > 6 && +- this.setState({ pluginsRevealed: !this.state.pluginsRevealed })}> +- {this.state.pluginsRevealed ? 'Show less' : 'Show more'} +- } +-
+- {unauthorizedPlugins.length > 0 &&
+- Unauthorized Plugins: +- {unauthorizedPlugins.join(', ')} +-
} +- {window.bdplugins &&
+- BetterDiscord Plugins: +- {Object.keys(window.bdplugins).join(', ')} +-
} +-
+-
+- +- } +- />; +- } +- +- handleDebugInfoCopy (plugins) { +- const extract = document.querySelector('.debug-info > code') +- .innerText.replace(/([A-Z/ ]+) (?=\s(?!C:\\).*?:)/g, '\n[$1]').replace(/(.*?):\s(.*.+)/g, '$1="$2"').replace(/[ -](\w*(?=.*=))/g, '$1'); +- +- this.setState({ copied: true }); +- clipboard.writeText( +- `\`\`\`ini +- # Debugging Information | Result created: ${new Date().toUTCString()} +- ${extract.substring(0, extract.indexOf('\nPlugins', extract.indexOf('\nPlugins') + 1))} +- Plugins="${plugins.join(', ')}"${window.bdplugins ? `\nBDPlugins="${Object.keys(window.bdplugins).join(', ')}"` : ''} +- \`\`\``.replace(/ {6}|n\/a/g, '').replace(/(?![0-9]{1,3}) \/ (?=[0-9]{1,3})/g, '/') +- ); +- setTimeout(() => this.setState({ copied: false }), 2500); +- } +-}; +diff --git a/src/Powercord/plugins/pc-updater/components/Update.jsx b/src/Powercord/plugins/pc-updater/components/Update.jsx +deleted file mode 100644 +index d2d9fb7..0000000 +--- a/src/Powercord/plugins/pc-updater/components/Update.jsx ++++ /dev/null +@@ -1,45 +0,0 @@ +-const { React, i18n: { Messages } } = require('powercord/webpack'); +-const { Button } = require('powercord/components'); +- +-const Icons = require('./Icons'); +- +-module.exports = class Update extends React.PureComponent { +- constructor () { +- super(); +- this.plugin = powercord.pluginManager.get('pc-updater'); +- } +- +- render () { +- const { name, icon, repo, commits, updating, onSkip, onDisable } = this.props; +- return
+-
+-
+- {React.createElement(Icons[icon])} +-
+-
{name}
+-
+- {updating +- ? +- : <> +- +- +- } +-
+-
+-
+- {commits.map(commit =>
+- +- {commit.id.substring(0, 7)} +- +- {commit.message} - {commit.author} +-
)} +-
+-
; +- } +-}; +diff --git a/src/Powercord/plugins/pc-updater/index.js b/src/Powercord/plugins/pc-updater/index.js +deleted file mode 100644 +index 2a531d8..0000000 +--- a/src/Powercord/plugins/pc-updater/index.js ++++ /dev/null +@@ -1,371 +0,0 @@ +-const { React, getModule, getModuleByDisplayName, i18n: { Messages } } = require('powercord/webpack'); +-const { open: openModal, close: closeModal } = require('powercord/modal'); +-const { Confirm } = require('powercord/components/modal'); +-const { Plugin } = require('powercord/entities'); +- +-const { join } = require('path'); +-const { promisify } = require('util'); +-const cp = require('child_process'); +-const exec = promisify(cp.exec); +- +-const Settings = require('./components/Settings.jsx'); +- +-const changelog = require('../../../../changelogs.json'); +- +-module.exports = class Updater extends Plugin { +- constructor () { +- super(); +- +- this.checking = false; +- this.cwd = { cwd: join(__dirname, ...Array(4).fill('..')) }; +- } +- +- async startPlugin () { +- this.settings.set('paused', false); +- this.settings.set('failed', false); +- this.settings.set('checking', false); +- this.settings.set('updating', false); +- this.settings.set('awaiting_reload', false); +- this.settings.set('checking_progress', null); +- +- this.loadStylesheet('style.scss'); +- powercord.api.settings.registerSettings('pc-updater', { +- category: this.entityID, +- label: 'Updater', // Note to self: add this string to i18n last :^) +- render: Settings +- }); +- +- let minutes = Number(this.settings.get('interval', 15)); +- if (minutes < 1) { +- this.settings.set('interval', 1); +- minutes = 1; +- } +- +- this._interval = setInterval(this.checkForUpdates.bind(this), minutes * 60 * 1000); +- setTimeout(() => { +- this.checkForUpdates(); +- }, 10e3); +- +- const lastChangelog = this.settings.get('last_changelog', ''); +- if (changelog.id !== lastChangelog) { +- this.openChangeLogs(); +- } +- } +- +- pluginWillUnload () { +- powercord.api.settings.unregisterSettings('pc-updater'); +- clearInterval(this._interval); +- } +- +- async checkForUpdates (allConcurrent = false) { +- if ( +- this.settings.get('disabled', false) || +- this.settings.get('paused', false) || +- this.settings.get('checking', false) || +- this.settings.get('updating', false) +- ) { +- return; +- } +- +- this.settings.set('checking', true); +- this.settings.set('checking_progress', [ 0, 0 ]); +- const disabled = this.settings.get('entities_disabled', []).map(e => e.id); +- const skipped = this.settings.get('entities_skipped', []); +- const plugins = [ ...powercord.pluginManager.plugins.values() ].filter(p => !p.isInternal); +- const themes = [ ...powercord.styleManager.themes.values() ]; +- +- const entities = plugins.concat(themes).filter(e => !disabled.includes(e.updateIdentifier) && e.isUpdatable()); +- if (!disabled.includes(powercord.updateIdentifier)) { +- entities.push(powercord); +- } +- +- let done = 0; +- const updates = []; +- const entitiesLength = entities.length; +- const parallel = allConcurrent ? entitiesLength : this.settings.get('concurrency', 2); +- await Promise.all(Array(parallel).fill(null).map(async () => { +- let entity; +- while ((entity = entities.shift())) { +- try { +- const repo = await entity.getGitRepo(); +- if (repo) { +- const shouldUpdate = await entity._checkForUpdates(); +- if (shouldUpdate) { +- const commits = await entity._getUpdateCommits(); +- if (commits[0] && skipped[entity.updateIdentifier] === commits[0].id) { +- continue; +- } +- updates.push({ +- id: entity.updateIdentifier, +- name: entity.manifest?.name ?? 'Powercord', +- icon: entity.constructor.name === 'Theme' || entity.constructor.name === 'Powercord' +- ? entity.constructor.name +- : 'Plugin', +- commits, +- repo +- }); +- } +- } +- } catch (e) { +- console.error('An error occurred while checking for updates for %s', entity.manifest?.name ?? 'Powercord', e); +- } finally { +- this.settings.set('checking_progress', [ ++done, entitiesLength ]); +- } +- } +- })); +- +- this.settings.set('updates', updates); +- this.settings.set('last_check', Date.now()); +- this.settings.set('checking', false); +- if (updates.length > 0) { +- if (this.settings.get('automatic', false)) { +- this.doUpdate(); +- } else if (!document.querySelector('#powercord-updater, .powercord-updater')) { +- powercord.api.notices.sendToast('powercord-updater', { +- header: Messages.POWERCORD_UPDATES_TOAST_AVAILABLE_HEADER, +- content: Messages.POWERCORD_UPDATES_TOAST_AVAILABLE_DESC, +- icon: 'wrench', +- buttons: [ { +- text: Messages.POWERCORD_UPDATES_UPDATE, +- color: 'green', +- look: 'outlined', +- onClick: () => this.doUpdate() +- }, { +- text: Messages.POWERCORD_UPDATES_OPEN_UPDATER, +- color: 'blue', +- look: 'ghost', +- onClick: async () => { +- const settingsModule = await getModule([ 'open', 'saveAccountChanges' ]); +- settingsModule.open('pc-updater'); +- } +- } ] +- }); +- } +- } +- } +- +- async doUpdate (force = false) { +- this.settings.set('failed', false); +- this.settings.set('updating', true); +- const updates = this.settings.get('updates', []); +- const failed = []; +- for (const update of [ ...updates ]) { +- let entity = powercord; +- if (update.id.startsWith('plugin')) { +- entity = powercord.pluginManager.get(update.id.replace('plugins_', '')); +- } else if (update.id.startsWith('theme')) { +- entity = powercord.styleManager.get(update.id.replace('themes_', '')); +- } +- +- const success = await entity._update(force); +- updates.shift(); +- this.settings.get('updates', updates); +- if (!success) { +- failed.push(update); +- } +- } +- +- this.settings.set('updating', false); +- if (failed.length > 0) { +- this.settings.set('failed', true); +- this.settings.set('updates', failed); +- if (!document.querySelector('#powercord-updater, .powercord-updater')) { +- powercord.api.notices.sendToast('powercord-updater', { +- header: Messages.POWERCORD_UPDATES_TOAST_FAILED, +- type: 'danger', +- buttons: [ { +- text: Messages.POWERCORD_UPDATES_FORCE, +- color: 'red', +- look: 'outlined', +- onClick: () => this.askForce() +- }, { +- text: Messages.FRIEND_REQUEST_IGNORE, +- look: 'outlined', +- color: 'grey' +- }, { +- text: Messages.POWERCORD_UPDATES_OPEN_UPDATER, +- color: 'blue', +- look: 'ghost', +- onClick: async () => { +- const settingsModule = await getModule([ 'open', 'saveAccountChanges' ]); +- settingsModule.open('pc-updater'); +- } +- } ] +- }); +- } +- } +- } +- +- // MODALS +- askForce (callback) { +- openModal(() => +- React.createElement(Confirm, { +- red: true, +- header: Messages.SUPPRESS_EMBED_TITLE, +- confirmText: Messages.POWERCORD_UPDATES_FORCE, +- cancelText: Messages.CANCEL, +- onConfirm: () => { +- if (callback) { +- // eslint-disable-next-line callback-return +- callback(); +- } +- this.doUpdate(true); +- }, +- onCancel: closeModal +- }, React.createElement('div', { className: 'powercord-text' }, Messages.POWERCORD_UPDATES_FORCE_MODAL)) +- ); +- } +- +- // UTILS +- skipUpdate (id, commit) { +- this.settings.set('entities_skipped', { +- ...this.settings.get('entities_skipped', {}), +- [id]: commit +- }); +- this._removeUpdate(id); +- } +- +- disableUpdates (entity) { +- this.settings.set('entities_disabled', [ +- ...this.settings.get('entities_disabled', []), +- { +- id: entity.id, +- name: entity.name, +- icon: entity.icon +- } +- ]); +- this._removeUpdate(entity.id); +- } +- +- enableUpdates (id) { +- this.settings.set('entities_disabled', this.settings.get('entities_disabled', []).filter(d => d.id !== id)); +- } +- +- _removeUpdate (id) { +- this.settings.set('updates', this.settings.get('updates', []).filter(u => u.id !== id)); +- } +- +- async getGitInfos () { +- const branch = await exec('git branch', this.cwd) +- .then(({ stdout }) => +- stdout +- .toString() +- .split('\n') +- .find(l => l.startsWith('*')) +- .slice(2) +- .trim() +- ); +- +- const revision = await exec(`git rev-parse ${branch}`, this.cwd) +- .then(r => r.stdout.toString().trim()); +- +- const upstream = await exec('git remote get-url origin', this.cwd) +- .then(r => r.stdout.toString().match(/github\.com[:/]([\w-_]+\/[\w-_]+)/)[1]); +- +- return { +- upstream, +- branch, +- revision +- }; +- } +- +- async changeBranch (branch) { +- await exec('git fetch origin +v2:v2', this.cwd); +- await exec(`git checkout ${branch}`, this.cwd); +- // location.reload(); +- } +- +- // Change Log +- async openChangeLogs () { +- const ChangeLog = await this._getChangeLogsComponent(); +- openModal(() => React.createElement(ChangeLog, { +- changeLog: this.formatChangeLog(changelog) +- })); +- } +- +- async _getChangeLogsComponent () { +- if (!this._ChangeLog) { +- const _this = this; +- const { video } = await getModule([ 'video', 'added' ]); +- const DiscordChangeLog = await getModuleByDisplayName('ChangelogStandardTemplate'); +- +- class ChangeLog extends DiscordChangeLog { +- constructor (props) { +- super(props); +- +- this.oldRenderHeader = this.renderHeader; +- this.renderHeader = this.renderNewHeader.bind(this); +- +- this.track = () => void 0; +- this.handleScroll = () => void 0; +- } +- +- renderNewHeader () { +- const header = this.oldRenderHeader(); +- header.props.children[0].props.children = `Powercord - ${header.props.children[0].props.children}`; +- return header; +- } +- +- renderVideo () { +- if (!changelog.image) { +- return null; +- } +- +- return React.createElement('img', { +- src: changelog.image, +- className: video, +- alt: '' +- }); +- } +- +- renderFooter () { +- const footer = super.renderFooter(); +- footer.props.children = React.createElement('span', { +- style: { color: 'var(--text-normal)' }, +- dangerouslySetInnerHTML: { +- __html: changelog.footer +- } +- }); +- return footer; +- } +- +- componentWillUnmount () { +- _this.settings.set('last_changelog', changelog.id); +- } +- } +- +- this._ChangeLog = ChangeLog; +- } +- return this._ChangeLog; +- } +- +- formatChangeLog (json) { +- let body = ''; +- const colorToClass = { +- GREEN: 'added', +- ORANGE: 'progress', +- RED: 'fixed', +- BLURPLE: 'improved' +- }; +- json.contents.forEach(item => { +- if (item.type === 'HEADER') { +- body += `${item.text.toUpperCase()} {${colorToClass[item.color]}${item.noMargin ? ' marginTop' : ''}}\n======================\n\n`; +- } else { +- if (item.text) { +- body += item.text; +- body += '\n\n'; +- } +- if (item.list) { +- body += ` * ${item.list.join('\n\n * ')}`; +- body += '\n\n'; +- } +- } +- }); +- return { +- date: json.date, +- locale: 'en-us', +- revision: 1, +- body +- }; +- } +-}; +diff --git a/src/Powercord/plugins/pc-updater/manifest.json b/src/Powercord/plugins/pc-updater/manifest.json +deleted file mode 100644 +index 07e89ba..0000000 +--- a/src/Powercord/plugins/pc-updater/manifest.json ++++ /dev/null +@@ -1,7 +0,0 @@ +-{ +- "name": "Updater", +- "version": "2.0.0", +- "description": "Ensure Powercord, plugins and themes are always up to date", +- "author": "Powercord Team", +- "license": "MIT" +-} +diff --git a/src/Powercord/plugins/pc-updater/style.scss b/src/Powercord/plugins/pc-updater/style.scss +deleted file mode 100644 +index 55e7576..0000000 +--- a/src/Powercord/plugins/pc-updater/style.scss ++++ /dev/null +@@ -1,211 +0,0 @@ +-.powercord-updater { +- .top-section { +- margin-top: 35px; +- display: flex; +- +- .icon { +- width: 64px; +- margin-right: 15px; +- flex-shrink: 0; +- } +- +- .animated path + path { +- transform-origin: 50% 50%; +- animation: rotate 1.2s infinite; +- } +- +- @keyframes rotate { +- from { +- transform: rotate(0deg); +- } +- to { +- transform: rotate(360deg); +- } +- } +- +- .status { +- display: flex; +- flex-direction: column; +- justify-content: space-evenly; +- +- h3 { +- font-size: 28px; +- } +- +- div { +- opacity: .7; +- } +- } +- +- .about { +- display: flex; +- margin-left: auto; +- flex-direction: column; +- justify-content: space-evenly; +- font-family: SourceCodePro, Consolas, Liberation Mono, Menlo, Courier, monospace; +- font-size: 14px; +- opacity: .7; +- +- div { +- display: flex; +- justify-content: space-between; +- +- span { +- margin-left: 10px; +- width: 80px; +- } +- } +- } +- } +- +- .buttons { +- display: flex; +- margin-top: 25px; +- +- button + button { +- margin-left: 15px; +- } +- } +- +- .updates { +- margin-top: 25px; +- } +- +- .update { +- margin: 15px 0; +- +- .title { +- display: flex; +- align-items: center; +- +- .icon { +- width: 28px; +- height: 28px; +- margin-right: 10px; +- } +- +- .name { +- font-size: 22px; +- } +- +- .actions { +- display: flex; +- align-items: center; +- margin-left: auto; +- +- button { +- height: 26px; +- min-height: 26px; +- padding: 2px 6px; +- +- &:not(:last-child) { +- margin-right: 10px; +- } +- } +- } +- } +- +- .summary { +- padding: 10px 55px; +- +- div { +- margin-bottom: 10px; +- } +- +- a { +- background-color: var(--background-secondary); +- margin-right: 10px; +- } +- } +- } +- +- .buttons, .update { +- padding-bottom: 15px; +- margin-bottom: 20px; +- border-bottom: 1px var(--background-modifier-accent) solid; +- } +- +- &-ft { +- margin-top: 40px; +- margin-bottom: 8px; +- } +- +- .debug-info { +- display: flex; +- color: var(--interactive-normal); +- user-select: text; +- cursor: text; +- +- &.copied b { +- color: #3ca374; +- } +- +- code { +- white-space: pre-wrap; +- font-family: inherit; +- font-size: 12px; +- } +- +- b { +- align-items: center; +- color: #7289da; +- text-transform: uppercase; +- display: flex; +- +- &::after { +- content: ""; +- margin-left: 4px; +- border: thin dashed; +- opacity: .6; +- flex: 1; +- } +- +- transition: color .2s ease; +- } +- +- .row { +- display: flex; +- flex-direction: row; +- flex-wrap: wrap; +- margin: 8px 0 8px 20px; +- } +- +- .column { +- flex-basis: 25%; +- +- &:nth-child(n+5) { +- margin-top: 8px; +- } +- } +- +- .full-column { +- flex-basis: 100%; +- +- &:nth-child(n+2) { +- margin-top: 8px; +- } +- +- &> a { +- user-select: none; +- } +- } +- +- &:hover { +- button { +- opacity: 1; +- } +- } +- +- :last-child:not(button) > :last-child { +- flex-basis: 85%; +- } +- +- button { +- opacity: 0; +- align-self: flex-end; +- transition: opacity .2s ease; +- position: absolute; +- right: 20px; +- } +- } +-}