{"id":13861899,"url":"https://github.com/danielfm/smudge","last_synced_at":"2025-12-29T23:49:47.179Z","repository":{"id":23829921,"uuid":"27206984","full_name":"danielfm/smudge","owner":"danielfm","description":"Control the Spotify app from within Emacs.","archived":false,"fork":false,"pushed_at":"2024-05-24T23:00:56.000Z","size":2785,"stargazers_count":307,"open_issues_count":20,"forks_count":47,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-08-05T06:04:13.867Z","etag":null,"topics":["api","client","controller","emacs","integration","player","playlists","spotify"],"latest_commit_sha":null,"homepage":"https://asciinema.org/a/218654","language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielfm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"danielfm","liberapay":"danielfm"}},"created_at":"2014-11-27T03:17:11.000Z","updated_at":"2024-07-18T11:24:42.000Z","dependencies_parsed_at":"2024-08-05T06:14:07.344Z","dependency_job_id":null,"html_url":"https://github.com/danielfm/smudge","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfm%2Fsmudge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfm%2Fsmudge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfm%2Fsmudge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfm%2Fsmudge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielfm","download_url":"https://codeload.github.com/danielfm/smudge/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225968893,"owners_count":17553157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","client","controller","emacs","integration","player","playlists","spotify"],"created_at":"2024-08-05T06:01:32.449Z","updated_at":"2025-12-29T23:49:47.168Z","avatar_url":"https://github.com/danielfm.png","language":"Emacs Lisp","readme":"# Smudge\n\n[![MELPA](https://melpa.org/packages/smudge-badge.svg)](https://melpa.org/#/smudge)\n\n**Control Spotify app from within Emacs.**\n\n[![asciicast](https://asciinema.org/a/218654.svg)](https://asciinema.org/a/218654)\n\nSmudge allows you to control the Spotify application from within your favorite text\neditor. If you are running on Mac OS X or Linux, you can control the locally running instance. If\nyou are running on any platform with a network connection (including Windows - and even headless!)\nand have a Spotify premium subscription, you can control an instance of Spotify via the Spotify\nConnect feature.\n\n## Features\n\n* Spotify client integration for GNU/Linux (via D-Bus) and OS X (via AppleScript)\n* Device playback display \u0026 selection using the Spotify Connect API (requires premium)\n* Communicates with the Spotify API via Oauth2\n* Displays the current track in mode line or title bar\n* Create playlists (public or private)\n* Browse your own playlists, and their tracks\n* Search for tracks and playlists that match the given keywords\n* Easily control basic Spotify player features like, play/pause, previous,\n  next, shuffle, and repeat with the Smudge Remote minor mode\n\n## Installation\n\nSmudge requires Emacs 27.1+.\n\n### Vanilla Emacs\n\n`package.el` is the built-in package manager in Emacs.\n\nSmudge is available on the two major package.el community maintained repos MELPA Stable and MELPA.\n\nYou can install Smudge with the following command:\n\n\u003ckbd\u003eM-x\u003c/kbd\u003e package-install \u003ckbd\u003e[RET]\u003c/kbd\u003e smudge \u003ckbd\u003e[RET]\u003c/kbd\u003e\n\nOr put the following snippet into your Emacs configuration:\n\n```elisp\n(use-package! smudge\n  :bind-keymap (\"C-c .\" . smudge-command-map)\n  :custom\n  (smudge-oauth2-client-secret \"...\")\n  (smudge-oauth2-client-id \"...\")\n  ;; optional: enable transient map for frequent commands\n  (smudge-player-use-transient-map t)\n  :config\n  ;; optional: display current song in mode line\n  (global-smudge-remote-mode))\n```\n\n### Doom Emacs\n\nAdd the following to the `packages.el` file:\n\n```elisp\n;; Fetch from MELPA\n(package! smudge)\n\n;; Fetch from GitHub\n(package! smudge\n  :recipe (:host github :repo \"danielfm/smudge\"))\n```\n\nAdd the following to the `config.el` file:\n\n``` elisp\n(use-package! smudge\n  :bind-keymap (\"C-c .\" . smudge-command-map)\n  :custom\n  (smudge-oauth2-client-secret \"...\")\n  (smudge-oauth2-client-id \"...\")\n  ;; optional: enable transient map for frequent commands\n  (smudge-player-use-transient-map t)\n  :config\n  ;; optional: display current song in mode line\n  (global-smudge-remote-mode))\n```\n\n## Configuration\n\n```elisp\n(setq smudge-oauth2-client-secret \"\u003cspotify-app-client-secret\u003e\")\n(setq smudge-oauth2-client-id \"\u003cspotify-app-client-id\u003e\")\n```\n\nIn order to get the client ID and client secret, you need to create a\n[Spotify app][app-list], specifying \u003chttp://127.0.0.1:8080/smudge_api_callback\u003e\nas the redirect URI (or whichever port you have specified via customize). The\nOAuth2 exchange is handled by `simple-httpd`. If you are not already using\nthis package for something else, you should not need to customize this port.\nOtherwise, you'll want to set it to whatever port you are running on.\n\nTo use the \"Spotify Connect\" transport (vs. controlling only your local\ninstance - though you can also control your local instance as well), set\n`smudge-transport` to `'connect` as follows. **This feature requires a Spotify\npremium subscription.**\n\n```elisp\n(setq smudge-transport 'connect)\n```\n\n### Key Bindings\n\n``` elisp\n; Set C-c . as the Smudge [prefix]\n(define-key smudge-mode-map (kbd \"C-c .\") 'smudge-command-map)\n```\n\nThe keymap prefix \u003ckbd\u003eC-c .\u003c/kbd\u003e is just a suggestion, following the\nconventions suggested for minor modes as defined in the Emacs manual\n[Key Binding Conventions][kbd-conv]. Previous versions of this package used\n\u003ckbd\u003eM-p\u003c/kbd\u003e.\n\nThe default bindings provided by the `smudge-command-map` is as follows:\n\n| Key                     | Function                                   | Description                                      |\n|:------------------------|:-------------------------------------------|:-------------------------------------------------|\n| \u003ckbd\u003e[prefix] d\u003c/kbd\u003e   | `smudge-select-device`                     | Select a playback device [2]                     |\n| \u003ckbd\u003e[prefix] SPC\u003c/kbd\u003e | `smudge-controller-toggle-play`            | Play/pause                                       |\n| \u003ckbd\u003e[prefix] s\u003c/kbd\u003e   | `smudge-controller-toggle-shuffle`         | Turn shuffle on/off [1]                          |\n| \u003ckbd\u003e[prefix] r\u003c/kbd\u003e   | `smudge-controller-toggle-repeat`          | Turn repeat on/off [1]                           |\n| \u003ckbd\u003e[prefix] n\u003c/kbd\u003e   | `smudge-controller-next-track`             | Next track                                       |\n| \u003ckbd\u003e[prefix] b\u003c/kbd\u003e   | `smudge-controller-previous-track`         | Previous track                                   |\n| \u003ckbd\u003e[prefix] v u\u003c/kbd\u003e | `smudge-controller-volume-up`              | Increase the volume [2]                          |\n| \u003ckbd\u003e[prefix] v d\u003c/kbd\u003e | `smudge-controller-volume-down`            | Decrease the volume [2]                          |\n| \u003ckbd\u003e[prefix] v m\u003c/kbd\u003e | `smudge-controller-volume-mute-unmute`     | Alternate the volume between 0 and 100 [2]       |\n| \u003ckbd\u003e[prefix] p m\u003c/kbd\u003e | `smudge-my-playlists`                      | Show your playlists                              |\n| \u003ckbd\u003e[prefix] p s\u003c/kbd\u003e | `smudge-playlist-search`                   | Search for playlists                             |\n| \u003ckbd\u003e[prefix] p u\u003c/kbd\u003e | `smudge-user-playlists`                    | Show playlists for the given user                |\n| \u003ckbd\u003e[prefix] p c\u003c/kbd\u003e | `smudge-create-playlist`                   | Create a new playlist                            |\n| \u003ckbd\u003e[prefix] t s\u003c/kbd\u003e | `smudge-track-search`                      | Search for tracks                                |\n| \u003ckbd\u003e[prefix] t r\u003c/kbd\u003e | `smudge-recently-played`                   | List of recently played tracks                   |\n| \u003ckbd\u003e[prefix] t l\u003c/kbd\u003e | `smudge-save-playing-track-to-library`     | Save currently playing track to your Library     |\n| \u003ckbd\u003e[prefix] t k\u003c/kbd\u003e | `smudge-remove-playing-track-from-library` | Remove currently playing track from your Library |\n\n[1] No proper support for this in D-Bus implementation for GNU/Linux\n[2] This feature uses Spotify Connect and requires a premium subscription\n\nUsers of the package hydra may find the code below more convenient for managing\nSpotify, _although this is isn't officially supported:_\n\n```elisp\n;; A hydra for controlling spotify.\n(defhydra hydra-spotify (:hint nil)\n\"\n^Search^                  ^Control^               ^Manage^\n^^^^^^^^-----------------------------------------------------------------\n_t_: Track               _SPC_: Play/Pause        _+_: Volume up\n_m_: My Playlists        _n_  : Next Track        _-_: Volume down\n_u_: User Playlists      _r_  : Repeat            _d_: Device\n^^                       _s_  : Shuffle           _q_: Quit\n\"\n    (\"t\" smudge-track-search :exit t)\n    (\"m\" smudge-my-playlists :exit t)\n    (\"u\" smudge-user-playlists :exit t)\n    (\"SPC\" smudge-controller-toggle-play :exit nil)\n    (\"n\" smudge-controller-next-track :exit nil)\n    (\"p\" smudge-controller-previous-track :exit nil)\n    (\"r\" smudge-controller-toggle-repeat :exit nil)\n    (\"s\" smudge-controller-toggle-shuffle :exit nil)\n    (\"+\" smudge-controller-volume-up :exit nil)\n    (\"-\" smudge-controller-volume-down :exit nil)\n    (\"x\" smudge-controller-volume-mute-unmute :exit nil)\n    (\"d\" smudge-select-device :exit nil)\n    (\"q\" quit-window \"quit\" :color blue))\n\n(bind-key \"a\" #'hydra-spotify/body some-map)\n```\n\nA transient map can be enabled to allow repeating frequent commands\n(defined in `smudge-transient-command-map`) without having to repeat the\nprefix key for `smudge-command-map`.\n\n```elisp\n(setq smudge-player-use-transient-map t)\n```\n\n### Creating The Spotify App\n\nGo to [Create an Application][app-create] and give your application a name and\na description:\n\n![Creating a Spotify App 1/3](./img/spotify-app-01.png)\n\nAfter creating the new app, click the **Edit Settings**, scroll down a little bit,\ntype \u003chttp://127.0.0.1:8080/smudge_api_callback\u003e as the Redirect URI for the\napplication, and click **Add**. Then, hit **Save**.\n\n**IMPORTANT**: After recent changes you must make sure the Redirect URI has underscores '_' and not hyphens '-'!\n\n![Creating a Spotify App 2/3](./img/spotify-app-02.png)\n\nAt this point, the client ID and the client secret are available, so set those values to\n`smudge-oauth2-client-id` and `smudge-oauth2-client-secret`, respectively.\n\n![Creating a Spotify App 3/3](./img/spotify-app-03.png)\n\n## Usage\n\n### Remote Minor Mode\n\nTo display the currently song in the mode line, you can enable the\n`global-smudge-remote-mode`. The interval in which the player status is updated\ncan be configured via the `smudge-player-status-refresh-interval` variable:\n\n```elisp\n;; Updates the player status every 10 seconds (default is 5)\n;; Note: Set 0 to disable this feature, and avoid values between 1 and 4 when\n;; using the 'connect transport.\n(setq smudge-player-status-refresh-interval 10)\n```\n#### Customizing The Player Status\n\nThe information displayed in the player status can be customized by setting the\ndesired format in `smudge-player-status-format`. The following placeholders\nare supported:\n\n| Symbol | Description                | Example                        |\n|:------:|:---------------------------|:-------------------------------|\n|  `%u`  | Track URI                  | `spotify:track:\u003cid\u003e`           |\n|  `%a`  | Artist name (truncated)    | `Pink Floyd`                   |\n|  `%t`  | Track name (truncated)     | `Us and Them`                  |\n|  `%n`  | Track #                    | `7`                            |\n|  `%l`  | Track duration, in minutes | `7:49`                         |\n|  `%r`  | Player repeat status       | `R`, `-`                       |\n|  `%s`  | Player shuffle status      | `S`, `-`                       |\n|  `%p`  | Player playing status      | `Playing`, `Paused`, `Stopped` |\n\nThe default format is `\"[%p: %a - %t ◷ %l %r%s]\"`.\n\nThe number of characters to be shown in truncated fields can be configured via\nthe `smudge-player-status-truncate-length` variable.\n\n```elisp\n(setq smudge-player-status-truncate-length 10) ; default: 15\n```\n\nThe text indicator for each of the following player statuses can be configured\nvia their corresponding variables:\n\n| Player State  | Variable                                  | Default Value |\n|:--------------|:------------------------------------------|:-------------:|\n| Playing       | `smudge-player-status-playing-text`       |  `\"Playing\"`  |\n| Paused        | `smudge-player-status-paused-text`        |  `\"Paused\"`   |\n| Stopped       | `smudge-player-status-stopped-text`       |  `\"Stopped\"`  |\n| Repeating On  | `smudge-player-status-repeating-text`     |     `\"R\"`     |\n| Repeating Off | `smudge-player-status-not-repeating-text` |     `\"-\"`     |\n| Shuffling On  | `smudge-player-status-shuffling-text`     |     `\"S\"`     |\n| Shuffling Off | `smudge-player-status-not-shuffling-text` |     `\"-\"`     |\n\n#### Global Remote Mode\n\nThis mode can be enabled globally by running\n\u003ckbd\u003eM-x global-smudge-remote-mode\u003c/kbd\u003e.\n\n### Searching For Tracks\n\nTo search for tracks, run \u003ckbd\u003eM-x smudge-track-search\u003c/kbd\u003e and type in your\nquery. The results will be displayed in a separate buffer with the following\nkey bindings:\n\n| Key              | Description                                                        |\n|:-----------------|:-------------------------------------------------------------------|\n| \u003ckbd\u003ea\u003c/kbd\u003e     | Adds track to a playlist                                           |\n| \u003ckbd\u003el\u003c/kbd\u003e     | Loads the next page of results (pagination)                        |\n| \u003ckbd\u003eg\u003c/kbd\u003e     | Clears the results and reloads the first page of results           |\n| \u003ckbd\u003ek\u003c/kbd\u003e     | Adds track(s) under the cursor (or inside the region) to the queue |\n| \u003ckbd\u003eM-RET\u003c/kbd\u003e | Plays the track under the cursor in the context of its album [1]   |\n\n[1] D-Bus implementation for GNU/Linux do not support passing the context, so\nonly the track under the cursor will be played\n\nThe resulting buffer loads the `global-smudge-remote-mode` by default.\n\n**Tip:** In order to customize the number of items fetched per page, just change\nthe variable `smudge-api-search-limit`:\n\n```elisp\n;; Do not use values larger than 50 for better compatibility across endpoints\n(setq smudge-api-search-limit 50)\n```\n\n### Playing a Spotify URI\n\nTo ask Smudge to play a resource by URI, run\n\u003ckbd\u003eM-x smudge-play-uri\u003c/kbd\u003e and enter the resource URI.\n\n### Creating Playlists\n\nTo create new playlists, run \u003ckbd\u003eM-x smudge-create-playlist\u003c/kbd\u003e and follow\nthe prompts.\n\nCurrently it's not possible to add tracks to a playlist you own, or to remove\ntracks from them.\n\n### Searching For Playlists\n\nTo return the playlists for the current user, run\n\u003ckbd\u003eM-x smudge-my-playlists\u003c/kbd\u003e, or\n\u003ckbd\u003eM-x smudge-user-playlists\u003c/kbd\u003e to list the public playlists for some\ngiven user. To search playlists that match the given search criteria, run\n\u003ckbd\u003eM-x smudge-playlist-search CRITERIA\u003c/kbd\u003e. Also, run\n\nAll these commands will display results in a separate buffer with the following\nkey bindings:\n\n| Key              | Description                                              |\n|:-----------------|:---------------------------------------------------------|\n| \u003ckbd\u003el\u003c/kbd\u003e     | Loads the next page of results (pagination)              |\n| \u003ckbd\u003eg\u003c/kbd\u003e     | Clears the results and reloads the first page of results |\n| \u003ckbd\u003ef\u003c/kbd\u003e     | Follows the playlist under the cursor                    |\n| \u003ckbd\u003eu\u003c/kbd\u003e     | Unfollows the playlist under the cursor                  |\n| \u003ckbd\u003et\u003c/kbd\u003e     | Lists the tracks of the playlist under the cursor        |\n| \u003ckbd\u003eM-RET\u003c/kbd\u003e | Plays the playlist under the cursor                      |\n\nOnce you open the list of tracks of a playlist, you get the following key\nbindings in the resulting buffer:\n\n| Key              | Description                                                         |\n|:-----------------|:--------------------------------------------------------------------|\n| \u003ckbd\u003ea\u003c/kbd\u003e     | Adds track to a playlist                                            |\n| \u003ckbd\u003er\u003c/kbd\u003e     | Removes track from current playlist                                 |\n| \u003ckbd\u003el\u003c/kbd\u003e     | Loads the next page of results (pagination)                         |\n| \u003ckbd\u003eg\u003c/kbd\u003e     | Clears the results and reloads the first page of results            |\n| \u003ckbd\u003ef\u003c/kbd\u003e     | Follows the current playlist                                        |\n| \u003ckbd\u003eu\u003c/kbd\u003e     | Unfollows the current playlist                                      |\n| \u003ckbd\u003ek\u003c/kbd\u003e     | Adds track(s) under the cursor (or inside the region) to the queue  |\n| \u003ckbd\u003eM-RET\u003c/kbd\u003e | Plays the track under the cursor in the context of the playlist [1] |\n\nBoth buffers load the `global-smudge-remote-mode` by default.\n\n[1] D-Bus implementation for GNU/Linux do not support passing the context, so\nonly the track under the cursor will be played\n\n## Selecting a Device for Playback\n\n\u003ckbd\u003eM-x smudge-select-device\u003c/kbd\u003e will display a list of devices available for playback in a separate buffer.\n\nNote: use of this feature requires a Spotify premium subscription.\n\nOnce you open the list of devices, you get the following key bindings in the resulting buffer:\n\n| Key            | Description                                       |\n|:---------------|:--------------------------------------------------|\n| \u003ckbd\u003eRET\u003c/kbd\u003e | Transfer playback to the device under the cursor. |\n| \u003ckbd\u003eg\u003c/kbd\u003e   | Reloads the list of devices                       |\n\n## Specifying the Player Status Location\n\nBy default, the player status (playing, paused, track name, time, shuffle, repeat, etc.) are shown\nin the modeline. If you want to display the status in the title bar when using a graphical display,\nyou can set the following:\n\n```elisp\n(setq smudge-status-location 'title-bar)\n```\n\nValid values include `'title-bar`, `'modeline` and `nil`, where nil turns off the display of the\nplayer status completely. If the value is set to `title-bar` but you are not using a graphical\ndisplay, the player status will be displayed in the mode line instead.\n\nIf you want to customize the separator between the existing title bar text and the player status,\nyou can set the following, i.e.:\n\n```elisp\n(setq smudge-title-bar-separator \"----\")\n```\n\nOtherwise, it defaults to 4 spaces.\n\n## License\n\nCopyright (C) Daniel Martins\n\nDistributed under the GPL v3 License. See COPYING for further details.\n\n[app-list]: https://developer.spotify.com/dashboard\n[app-create]: https://developer.spotify.com/dashboard/create\n[kbd-conv]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Binding-Conventions.html\n","funding_links":["https://github.com/sponsors/danielfm","https://liberapay.com/danielfm"],"categories":["Emacs Lisp"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielfm%2Fsmudge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielfm%2Fsmudge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielfm%2Fsmudge/lists"}