egoist/mygo-musicPublic

A native music player you extend with plugins

Stars
903
Forks
144
Watchers
1
Open issues
61
Open PRs
3
Contributors
~2
Commits
115
Branches
8

GoAGPL-3.0Created Nov 4, 2026Last push todayLatest release v0.1.22

Quick answers

What is mygo-music?
A native music player you extend with plugins
How popular is mygo-music on GitHub?
egoist/mygo-music has 903 stars and 144 forks on GitHub.
What license does mygo-music use?
egoist/mygo-music is released under the AGPL-3.0 license.

Star history

since Nov 8, 2026

Star history is still being collected for this repository.

Contribution activity

commits per day, last 52 weeks
NovDecJanFebMarAprMayJunJulAugSepOctNovMonWedFri2025-11-16: 0 commits2025-11-17: 0 commits2025-11-18: 0 commits2025-11-19: 0 commits2025-11-20: 0 commits2025-11-21: 0 commits2025-11-22: 0 commits2025-11-23: 0 commits2025-11-24: 0 commits2025-11-25: 0 commits2025-11-26: 0 commits2025-11-27: 0 commits2025-11-28: 0 commits2025-11-29: 0 commits2025-11-30: 0 commits2025-12-01: 0 commits2025-12-02: 0 commits2025-12-03: 0 commits2025-12-04: 0 commits2025-12-05: 0 commits2025-12-06: 0 commits2025-12-07: 0 commits2025-12-08: 0 commits2025-12-09: 0 commits2025-12-10: 0 commits2025-12-11: 0 commits2025-12-12: 0 commits2025-12-13: 0 commits2025-12-14: 0 commits2025-12-15: 0 commits2025-12-16: 0 commits2025-12-17: 0 commits2025-12-18: 0 commits2025-12-19: 0 commits2025-12-20: 0 commits2025-12-21: 0 commits2025-12-22: 0 commits2025-12-23: 0 commits2025-12-24: 0 commits2025-12-25: 0 commits2025-12-26: 0 commits2025-12-27: 0 commits2025-12-28: 0 commits2025-12-29: 0 commits2025-12-30: 0 commits2025-12-31: 0 commits2026-01-01: 0 commits2026-01-02: 0 commits2026-01-03: 0 commits2026-01-04: 0 commits2026-01-05: 0 commits2026-01-06: 0 commits2026-01-07: 0 commits2026-01-08: 0 commits2026-01-09: 0 commits2026-01-10: 0 commits2026-01-11: 0 commits2026-01-12: 0 commits2026-01-13: 0 commits2026-01-14: 0 commits2026-01-15: 0 commits2026-01-16: 0 commits2026-01-17: 0 commits2026-01-18: 0 commits2026-01-19: 0 commits2026-01-20: 0 commits2026-01-21: 0 commits2026-01-22: 0 commits2026-01-23: 0 commits2026-01-24: 0 commits2026-01-25: 0 commits2026-01-26: 0 commits2026-01-27: 0 commits2026-01-28: 0 commits2026-01-29: 0 commits2026-01-30: 0 commits2026-01-31: 0 commits2026-02-01: 0 commits2026-02-02: 0 commits2026-02-03: 0 commits2026-02-04: 0 commits2026-02-05: 0 commits2026-02-06: 0 commits2026-02-07: 0 commits2026-02-08: 0 commits2026-02-09: 0 commits2026-02-10: 0 commits2026-02-11: 0 commits2026-02-12: 0 commits2026-02-13: 0 commits2026-02-14: 0 commits2026-02-15: 0 commits2026-02-16: 0 commits2026-02-17: 0 commits2026-02-18: 0 commits2026-02-19: 0 commits2026-02-20: 0 commits2026-02-21: 0 commits2026-02-22: 0 commits2026-02-23: 0 commits2026-02-24: 0 commits2026-02-25: 0 commits2026-02-26: 0 commits2026-02-27: 0 commits2026-02-28: 0 commits2026-03-01: 0 commits2026-03-02: 0 commits2026-03-03: 0 commits2026-03-04: 0 commits2026-03-05: 0 commits2026-03-06: 0 commits2026-03-07: 0 commits2026-03-08: 0 commits2026-03-09: 0 commits2026-03-10: 0 commits2026-03-11: 0 commits2026-03-12: 0 commits2026-03-13: 0 commits2026-03-14: 0 commits2026-03-15: 0 commits2026-03-16: 0 commits2026-03-17: 0 commits2026-03-18: 0 commits2026-03-19: 0 commits2026-03-20: 0 commits2026-03-21: 0 commits2026-03-22: 0 commits2026-03-23: 0 commits2026-03-24: 0 commits2026-03-25: 0 commits2026-03-26: 0 commits2026-03-27: 0 commits2026-03-28: 0 commits2026-03-29: 0 commits2026-03-30: 0 commits2026-03-31: 0 commits2026-04-01: 0 commits2026-04-02: 0 commits2026-04-03: 0 commits2026-04-04: 0 commits2026-04-05: 0 commits2026-04-06: 0 commits2026-04-07: 0 commits2026-04-08: 0 commits2026-04-09: 0 commits2026-04-10: 0 commits2026-04-11: 0 commits2026-04-12: 0 commits2026-04-13: 0 commits2026-04-14: 0 commits2026-04-15: 0 commits2026-04-16: 0 commits2026-04-17: 0 commits2026-04-18: 0 commits2026-04-19: 0 commits2026-04-20: 0 commits2026-04-21: 0 commits2026-04-22: 0 commits2026-04-23: 0 commits2026-04-24: 0 commits2026-04-25: 0 commits2026-04-26: 0 commits2026-04-27: 0 commits2026-04-28: 0 commits2026-04-29: 0 commits2026-04-30: 0 commits2026-05-01: 0 commits2026-05-02: 0 commits2026-05-03: 0 commits2026-05-04: 0 commits2026-05-05: 0 commits2026-05-06: 0 commits2026-05-07: 0 commits2026-05-08: 0 commits2026-05-09: 0 commits2026-05-10: 0 commits2026-05-11: 0 commits2026-05-12: 0 commits2026-05-13: 0 commits2026-05-14: 0 commits2026-05-15: 0 commits2026-05-16: 0 commits2026-05-17: 0 commits2026-05-18: 0 commits2026-05-19: 0 commits2026-05-20: 0 commits2026-05-21: 0 commits2026-05-22: 0 commits2026-05-23: 0 commits2026-05-24: 0 commits2026-05-25: 0 commits2026-05-26: 0 commits2026-05-27: 0 commits2026-05-28: 0 commits2026-05-29: 0 commits2026-05-30: 0 commits2026-05-31: 0 commits2026-06-01: 0 commits2026-06-02: 0 commits2026-06-03: 0 commits2026-06-04: 0 commits2026-06-05: 0 commits2026-06-06: 0 commits2026-06-07: 0 commits2026-06-08: 0 commits2026-06-09: 0 commits2026-06-10: 0 commits2026-06-11: 0 commits2026-06-12: 0 commits2026-06-13: 0 commits2026-06-14: 0 commits2026-06-15: 0 commits2026-06-16: 0 commits2026-06-17: 0 commits2026-06-18: 0 commits2026-06-19: 0 commits2026-06-20: 0 commits2026-06-21: 0 commits2026-06-22: 0 commits2026-06-23: 0 commits2026-06-24: 0 commits2026-06-25: 0 commits2026-06-26: 0 commits2026-06-27: 0 commits2026-06-28: 0 commits2026-06-29: 0 commits2026-06-30: 0 commits2026-07-01: 0 commits2026-07-02: 0 commits2026-07-03: 0 commits2026-07-04: 0 commits2026-07-05: 0 commits2026-07-06: 0 commits2026-07-07: 0 commits2026-07-08: 0 commits2026-07-09: 0 commits2026-07-10: 0 commits2026-07-11: 0 commits2026-07-12: 0 commits2026-07-13: 0 commits2026-07-14: 0 commits2026-07-15: 0 commits2026-07-16: 0 commits2026-07-17: 0 commits2026-07-18: 0 commits2026-07-19: 0 commits2026-07-20: 0 commits2026-07-21: 0 commits2026-07-22: 0 commits2026-07-23: 0 commits2026-07-24: 0 commits2026-07-25: 0 commits2026-07-26: 0 commits2026-07-27: 0 commits2026-07-28: 0 commits2026-07-29: 0 commits2026-07-30: 0 commits2026-07-31: 0 commits2026-08-01: 0 commits2026-08-02: 0 commits2026-08-03: 0 commits2026-08-04: 0 commits2026-08-05: 0 commits2026-08-06: 0 commits2026-08-07: 0 commits2026-08-08: 0 commits2026-08-09: 0 commits2026-08-10: 0 commits2026-08-11: 0 commits2026-08-12: 0 commits2026-08-13: 0 commits2026-08-14: 0 commits2026-08-15: 0 commits2026-08-16: 0 commits2026-08-17: 0 commits2026-08-18: 0 commits2026-08-19: 0 commits2026-08-20: 0 commits2026-08-21: 0 commits2026-08-22: 0 commits2026-08-23: 0 commits2026-08-24: 0 commits2026-08-25: 0 commits2026-08-26: 0 commits2026-08-27: 0 commits2026-08-28: 0 commits2026-08-29: 0 commits2026-08-30: 0 commits2026-08-31: 0 commits2026-09-01: 0 commits2026-09-02: 0 commits2026-09-03: 0 commits2026-09-04: 0 commits2026-09-05: 0 commits2026-09-06: 0 commits2026-09-07: 0 commits2026-09-08: 0 commits2026-09-09: 0 commits2026-09-10: 0 commits2026-09-11: 0 commits2026-09-12: 0 commits2026-09-13: 0 commits2026-09-14: 0 commits2026-09-15: 0 commits2026-09-16: 0 commits2026-09-17: 0 commits2026-09-18: 0 commits2026-09-19: 0 commits2026-09-20: 0 commits2026-09-21: 0 commits2026-09-22: 0 commits2026-09-23: 0 commits2026-09-24: 0 commits2026-09-25: 0 commits2026-09-26: 0 commits2026-09-27: 0 commits2026-09-28: 0 commits2026-09-29: 0 commits2026-09-30: 0 commits2026-10-01: 0 commits2026-10-02: 0 commits2026-10-03: 0 commits2026-10-04: 0 commits2026-10-05: 0 commits2026-10-06: 0 commits2026-10-07: 0 commits2026-10-08: 0 commits2026-10-09: 0 commits2026-10-10: 0 commits2026-10-11: 0 commits2026-10-12: 0 commits2026-10-13: 0 commits2026-10-14: 0 commits2026-10-15: 0 commits2026-10-16: 0 commits2026-10-17: 0 commits2026-10-18: 0 commits2026-10-19: 0 commits2026-10-20: 0 commits2026-10-21: 0 commits2026-10-22: 0 commits2026-10-23: 0 commits2026-10-24: 0 commits2026-10-25: 0 commits2026-10-26: 0 commits2026-10-27: 0 commits2026-10-28: 0 commits2026-10-29: 0 commits2026-10-30: 0 commits2026-10-31: 0 commits2026-11-01: 0 commits2026-11-02: 0 commits2026-11-03: 0 commits2026-11-04: 13 commits2026-11-05: 5 commits2026-11-06: 49 commits2026-11-07: 47 commits2026-11-08: 0 commits2026-11-09: 0 commits2026-11-10: 0 commits2026-11-11: 0 commits2026-11-12: 0 commits2026-11-13: 0 commits2026-11-14: 0 commits
114 commits in the last yearLessMore

Signals and awards

derived from tracked data
  • Actively maintained

    Pushed within 48 hours

  • Continuous integration

    Automated checks passing

README

main branch

MyGo Music

A native desktop music player written in Go with MyGo, no WebView. It plays your local files out of the box, and everything else comes from plugins.

Features

  • Local files (MP3, FLAC, Ogg Vorbis, WAV, and ALAC, FLAC or AAC in .m4a and .aac) with tags, covers and .lrc lyrics
  • Playlists, liked songs, search, albums and artists
  • Now Playing with lyric stage styles, cover wall, queue, shuffle and repeat
  • Notch player on macOS, tray, media keys
  • Audio cache, fades on pause and seek, loudness normalization
  • Session restore
  • Podcast captions, written down on the device as episodes play
  • English and Simplified Chinese
  • Plugins can add accounts, catalogs, podcasts, stations, comments and pages of their own

Build

go tool mygo dev     # run in development
go tool mygo build   # package into build/
go test ./internal/...

Podcast captions

Episodes are written down on the device as they play, with sherpa-onnx and Qwen3-ASR, which the app downloads when the listener asks for them. The libraries come from this repository's sherpa-onnx-<version> prerelease, which only changes with that version:

go run ./scripts/sherpa -sign "Developer ID Application: …"
gh release create sherpa-onnx-2.0.1 --prerelease --latest=false build/sherpa/*

Plugins

A plugin is a folder in the app's plugins directory (Settings > Plugins > + > Show Plugins Folder) with a plugin.json and one JavaScript or TypeScript file. Install one with the + in Settings > Plugins from GitHub, by its repository or its address, or from its folder or a zip of it on this computer, or drop the folder in by hand. Remove one from its ⋯ menu there. There's nothing to build, and the same plugin runs on every platform. examples/demo-plugin does everything a plugin can with made-up data: copy it to start.

{
  "id": "example",
  "name": "Example",
  "version": "1.0.0",
  "minAppVersion": "0.1.8",
  "main": "index.js",
  "playback": "stream",
  "permissions": { "network": ["api.example.com"] },
  "settings": [{ "key": "apiKey", "label": "API Key", "help": "From your account page", "link": "https://example.com/keys" }],
  "navigation": [
    { "builtin": "home" },
    { "title": "Charts", "page": "charts", "icon": "chart" },
    { "title": "Stations", "builtin": "stations" }
  ]
}
export async function search({ query, kind, offset }) {
  const res = await fetch(`https://api.example.com/search?q=${encodeURIComponent(query)}&type=${kind}&offset=${offset}`, {
    headers: { authorization: `Bearer ${host.setting("apiKey")}` },
  });
  return { tracks: (await res.json()).items.map(toTrack), more: false };
}

export function stream({ id, quality }) {
  return { url: `https://api.example.com/audio/${id}.mp3`, type: "mp3" };
}

Export a function for each thing the plugin does. Each takes one object of parameters and returns a value or a promise. What it doesn't export, the app leaves out of its pages.

Export Parameters Returns
signIn, signOut, account {id, name, imageUrl, page}; account returns null when signed out. page is a browse page of the listener's own, which the account menu opens as My Page
setSetting {key, value}
home Browse
browse {page} Browse
search {query, kind, offset}, where kind is tracks, albums, artists, playlists or shows {tracks or albums or ..., more}
suggest {query} [string]
recent [Track]
played {id}
liked {offset} {tracks, more}
isLiked {ids} {id: bool}
like, likeEpisode {id, on} like likes a song, and likeEpisode an episode: without it, episodes show no heart
album {id} {album, tracks}
artist {id} {artist, tracks, albums}
saveAlbum, followArtist, savePlaylist, subscribeShow {id, on}
savedAlbums {offset} {albums, more}
playlists [Playlist]
playlist {id} Playlist with tracks
createPlaylist {name} Playlist
renamePlaylist {id, name}
deletePlaylist {id}
addToPlaylist, removeFromPlaylist {id, trackIds}
movePlaylistTrack {id, from, to}
shows [Show]
show {id, offset} {show, episodes, more}
stations [Station]
stationNext {id} [Track]
comments {trackId, offset} {comments, total, more}
likeComment {trackId, commentId, on}
postComment {trackId, text} Comment
lyrics {id} {lrc, translation}, or {lines} with timed words
stream {id, quality} {url, type, headers, size, gain, peak}, where type is mp3, flac, ogg, wav, m4a (ALAC, FLAC or AAC-LC, whole or in fragments as a DASH stream serves it) or aac (AAC-LC in ADTS)
remotePlay {ids, start, position}
remotePause, remoteResume
remoteSeek {position}
remoteVolume {percent}
remoteState {playing, trackId, position, duration, volume, device} or null
remoteDevices [{id, name, active}]
remoteUse {deviceId}

The shapes are in internal/music/music.go by their JSON names. Times are in milliseconds. A Browse is {title, subtitle, imageUrl, description, sections}: with an imageUrl, such as a listener's avatar, it's headed round, as an artist's page is, and description goes under the title a line to each line. A section can hold tracks, albums, artists, playlists, shows, stations, links ({title, subtitle, imageUrl, page}), a morePage, and empty, what it says while it holds nothing. A source's first section of tracks leads its Home as a card of the day's songs, which opens a page of all of them, headed by the section's pageTitle if it has one, else its title, and its subtitle after the count of songs. A show's publisherPage is a browse page of who publishes it, which their name on the show's page opens.

An imageUrl can say where its size goes, with {size}, for an image server that makes pictures the size asked: https://img.example.com/cover.jpg?param={size}y{size} or https://img.example.com/{size}/cover.jpg. The app fills in how many pixels across it shows the picture at, 40 in a row and 600 in Now Playing, or 600 where nothing asks for a size. %7Bsize%7D, as JavaScript's URL writes it in a query, counts too. An app older than 0.1.20 fetches {size} as it is, so a plugin that gives it wants a minAppVersion of 0.1.20. Without it, give pictures at about 800 pixels if the service can, rather than as large as they were made: the app fetches and decodes the whole of each, a few megabytes for one thousands of pixels across, to show it in a row.

Of the playlists, those with mine are the listener's own, listed under My Playlists, and the rest are ones they saved. One that is mine and also fixed, such as the songs they liked, is kept by the source itself: the app doesn't offer to rename it, delete it, add songs to it, take them out or move them.

navigation is the plugin's sidebar, in its order. An entry opens a browse page of its own by page, or one of the app's by builtin: home, liked, savedAlbums, stations or shows, which can be given a title. Without navigation, the sidebar has an entry for each of the app's pages the plugin can do. Up to four stations show as a switch over one card, and more as a grid.

minAppVersion is the oldest version of MyGo Music the plugin works with. An older one won't install it, and says to update first. Versions before 0.1.8 don't read it.

Translating a plugin

Put the translations in locales/<language>.json next to plugin.json: locales/zh-CN.json for Simplified Chinese, the language the app speaks besides English. Each one maps the English text to the translation:

{ "Example": "示例", "API Key": "API 密钥", "From your account page": "在你的账号页面获取", "Charts": "排行榜" }

The app translates the plugin's name, its settings' label and help and its navigation titles with them. For what the plugin says itself, such as a page's titles or an error's message, host.t(text) answers the translation, or text when there's none, and host.language() the listener's language, en or zh-CN. Both follow the listener as they change it, and the page they're on loads again.

With "playback": "stream", the app plays what stream answers. With "playback": "remote", the plugin makes the sound itself, or has another program do so, and the app calls the remote functions and polls remoteState.

Throw an error with a code to say what's wrong: signIn prompts the listener to sign in, setup sends them to the plugin's settings, unavailable means this can't be played, and unsupported means the plugin doesn't do this. Any other error shows its message.

Publishing a plugin

Put it in a public GitHub repository, with plugin.json at the top. The app takes the latest release: a .zip asset named for the platform, such as plugin-darwin-arm64.zip, or one named for none, or else the source. Without a release it takes the latest commit of the default branch. A new tag or commit is a new version, which shows as an update in the settings. An update asking for more permissions asks the listener again.

Add the topic mygo-music-plugin to the repository to have it listed on the plugins page.

Link to mygo-music://install-plugin?repo=owner/repo for an install button: it opens the app, which downloads the plugin and asks the listener before installing it. The development app (mygo dev) answers mygo-music-dev:// instead.

What a plugin can reach

A plugin has the JavaScript standard library, timers, fetch and its relatives, URL, console and host:

host.platform, host.arch such as darwin, arm64
host.dir, host.dataDir the plugin's folder, and one for its data
host.storage.get(key), .set(key, value), .remove(key) JSON values that outlive the app
host.setting(key) what the listener filled in
host.t(text), host.language() the text in the listener's language, from the plugin's locales, and that language: en or zh-CN
host.removeDataDir(name) removes a folder inside host.dataDir
host.crypto.md5(text) its hash, in hex
host.crypto.aesEcbEncrypt(key, text) AES-ECB with PKCS#7 padding, in hex, for services whose requests are made that way
host.openURL(url) opens a web page in the listener's browser
host.signInPrompt({qr, text}) what the sign-in window shows while signIn goes on: a QR code to scan, and what to do
host.waitForRedirect({port, path, timeout, page}) the end of a browser sign-in: listens on 127.0.0.1 and answers the query it's redirected back with. Needs the network permission
host.webSignIn({url, cookiesOf, until, userAgent}) a sign-in on the service's own page, for one that has no other way in: the app shows url in a window of its own, apart from every other page and forgotten after, and once the cookies named in until are all there, closes it and answers {cookie}, the Cookie header a request to cookiesOf would send. cookiesOf has to be a host the network permission reaches. userAgent is what the page is told it's shown in
host.spawn(command, args) starts a program, by a path from the plugin's folder or absolute, or a name on PATH. Needs the process permission to name it. Returns {pid, write(text), closeStdin(), readLine(), wait(), kill(), errorOutput()}

Everything else takes a permission in plugin.json:

  • "network": ["api.example.com", "*.example.com"] lists the hosts fetch can reach, or ["*"] for any. Without it, fetch reaches nothing.

  • "requestHeaders" are headers the app sends with what it fetches itself for the plugin, its pictures (image) and its songs' audio (audio), for a service that keeps those behind its session as well as its API:

    "requestHeaders": [
      { "hosts": ["{setting:server}"], "kinds": ["image", "audio"], "headers": { "cookie": "music-token={storage:token}" } },
      { "hosts": ["img.example.com"], "kinds": ["image"], "headers": { "referer": "https://example.com/" } }
    ]

    hosts are hosts or *.domain, which the network permission has to reach, or {setting:key}, the host of the server a setting names, such as https://nas.local:5666, while the network permission reaches it. * isn't one. Without kinds, they go with both. In headers, {setting:key} is what the listener filled in and {storage:key} what the plugin keeps with host.storage, as each is when the request goes out: a token the plugin stores anew goes with the next one, and none is kept in a URL. A header whose value isn't there yet, as before signing in, is left out. The headers a stream answers come first. Neither goes on to another host a request is redirected to.

  • "process": ["bin/helper-*", "ffmpeg"] lists the programs host.spawn can start, for what JavaScript can't do: a path from the plugin's folder or absolute, or a name looked up on PATH. A path only matches a path and a name only a name, and * matches within one folder, as in a glob. ["*"], or true, asks for any program, which the listener is warned about. The listener has to allow it in Settings first, as a program can do anything they can. Programs end when the plugin does, and once allowed, fetch can reach this machine too, to talk to them. Ship one binary per platform and pick it with host.platform and host.arch.

Credits

License

AGPL-3.0. See LICENSE.

View on GitHub

Recent activity

commits and pull requests

Releases and announcements

20 total
  1. v0.1.22v0.1.22Nov 7, 20267.6K downloads

    ### Fixed - The Shuffle button in a list now lets any song come first, instead of always keeping the first one first. A song you pick to start still stays first. - Seeking in AAC audio in an MP4, including a fragmented file as streaming sites serve it, lands on the same frame as playing straight through, not a second or so off after the first short packet. - The end of a fragmented MP4 no longer plays the silence padding of its last packet. - Now Playing's lyrics start at the top in a tall window, instead of leaving the top of the window empty and cutting off the lines before the one sung. - Japanese lyrics are drawn in a Japanese font, kanji and kana alike, rather than their kanji in the font of the app's language. - On English Windows, Chinese text is drawn in one Chinese font, not with characters in different fonts. - A plugin that needs setting up says why, in its settings and in the player, instead of "Fill these in for it to work" whatever it said. The message goes away once the program is allowed or the setting is saved. - A plugin's `id` and `main` must now stay inside the plugin's folder, so a plugin dropped into the plugins folder can't read another plugin's saved setti

  2. v0.1.21v0.1.21Nov 7, 20265.3K downloads

    ### Added - Audio in MP4s cut into fragments, as streaming sites serve it, plays as it comes in, and so does FLAC in MP4. A plugin for such a site can give the app the stream's address as it is, without converting it first. ### Fixed - With Grow From the Cover on, Now Playing no longer takes hundreds of megabytes more memory each time it opens out of the cover, as it did on Windows.

  3. v0.1.20v0.1.20Nov 7, 20263.2K downloads

    ### Added - A plugin's picture URLs can say where the size goes with `{size}`, such as `?param={size}y{size}`, and the app fetches each cover the size it shows, so rows of covers take much less memory and come in faster. - Settings > Experiments has Grow From the Cover, for Now Playing to open out of the player bar's cover as before. ### Changed - Now Playing slides up from the bottom of the window as it opens, and back down as it closes. ### Fixed - A plugin's page that changes as you play, such as songs played lately, shows what's new when you open it again a while later, without restarting the app. - Big covers that come only large are decoded within a memory budget, and the app gives memory back once they're shown and when the window hides. - A plugin that reads its storage or settings at its top level no longer hangs as it starts. - The button that saves a playlist's new name reads 保存 in Chinese, not 收藏.

  4. v0.1.19v0.1.19Nov 7, 20262.9K downloads

    ### [0.1.19] - 2026-11-07 #### Added - Plugins can list `requestHeaders` in plugin.json: headers, such as a session's cookie, that the app sends with the plugin's pictures and audio, so covers kept behind a sign-in, as fnOS Music's are, show. #### Fixed - Headers a plugin gives for a stream no longer go on to another host the stream redirects to. ### [0.1.18] - 2026-11-07 #### Added - ALAC, AAC and WAV files play, from local folders and from plugins. WAV and ALAC show the Lossless badge. #### Fixed - Big covers take much less memory to show as small thumbnails, and a cover shown at several sizes at once is fetched and decoded only once. - Long audio, such as a ten-hour audiobook, no longer takes a lot of memory for its spectrum, and streams that don't say how long they are get levels all the way through. ### [0.1.17] - 2026-11-07 #### Added - Plugins can head the page of a feature card's songs, such as a daily mix, with their own title and subtitle, and the page is headed as the card is. - Plugins can mark a playlist of yours as fixed, such as NetEase's liked songs. It stays under My Playlists but can't be renamed, deleted or changed. #### Fixed - Playlists and Liked

  5. v0.1.16v0.1.16Nov 6, 20261.3K downloads

    ### Fixed - On macOS, the app no longer takes more and more memory while something on screen moves, such as the lyrics in Now Playing.

Code frequency

additions and deletions
+49.2K-49.2KWeek of 2026-11-01: +49,182 linesWeek of 2026-11-01: -3,620 linesWeek of 2026-11-08: +0 linesWeek of 2026-11-08: -0 linesNov 1, 2026Nov 8, 2026
+49.2K lines added, -3.6K removed over the last year.

Commits per week

last 52 weeks
1140Week of 2025-11-16: 0 commitsWeek of 2025-11-23: 0 commitsWeek of 2025-11-30: 0 commitsWeek of 2025-12-07: 0 commitsWeek of 2025-12-14: 0 commitsWeek of 2025-12-21: 0 commitsWeek of 2025-12-28: 0 commitsWeek of 2026-01-04: 0 commitsWeek of 2026-01-11: 0 commitsWeek of 2026-01-18: 0 commitsWeek of 2026-01-25: 0 commitsWeek of 2026-02-01: 0 commitsWeek of 2026-02-08: 0 commitsWeek of 2026-02-15: 0 commitsWeek of 2026-02-22: 0 commitsWeek of 2026-03-01: 0 commitsWeek of 2026-03-08: 0 commitsWeek of 2026-03-15: 0 commitsWeek of 2026-03-22: 0 commitsWeek of 2026-03-29: 0 commitsWeek of 2026-04-05: 0 commitsWeek of 2026-04-12: 0 commitsWeek of 2026-04-19: 0 commitsWeek of 2026-04-26: 0 commitsWeek of 2026-05-03: 0 commitsWeek of 2026-05-10: 0 commitsWeek of 2026-05-17: 0 commitsWeek of 2026-05-24: 0 commitsWeek of 2026-05-31: 0 commitsWeek of 2026-06-07: 0 commitsWeek of 2026-06-14: 0 commitsWeek of 2026-06-21: 0 commitsWeek of 2026-06-28: 0 commitsWeek of 2026-07-05: 0 commitsWeek of 2026-07-12: 0 commitsWeek of 2026-07-19: 0 commitsWeek of 2026-07-26: 0 commitsWeek of 2026-08-02: 0 commitsWeek of 2026-08-09: 0 commitsWeek of 2026-08-16: 0 commitsWeek of 2026-08-23: 0 commitsWeek of 2026-08-30: 0 commitsWeek of 2026-09-06: 0 commitsWeek of 2026-09-13: 0 commitsWeek of 2026-09-20: 0 commitsWeek of 2026-09-27: 0 commitsWeek of 2026-10-04: 0 commitsWeek of 2026-10-11: 0 commitsWeek of 2026-10-18: 0 commitsWeek of 2026-10-25: 0 commitsWeek of 2026-11-01: 114 commitsWeek of 2026-11-08: 0 commitsNov 16, 2025Nov 8, 2026
114 commits in the last 52 weeks.

When work happens

weekday and hour
SunMonTueWedThuFriSat036912151821Sun 0:00 — 0 commitsSun 1:00 — 0 commitsSun 2:00 — 0 commitsSun 3:00 — 0 commitsSun 4:00 — 0 commitsSun 5:00 — 0 commitsSun 6:00 — 0 commitsSun 7:00 — 0 commitsSun 8:00 — 0 commitsSun 9:00 — 0 commitsSun 10:00 — 0 commitsSun 11:00 — 0 commitsSun 12:00 — 0 commitsSun 13:00 — 0 commitsSun 14:00 — 0 commitsSun 15:00 — 0 commitsSun 16:00 — 0 commitsSun 17:00 — 0 commitsSun 18:00 — 0 commitsSun 19:00 — 0 commitsSun 20:00 — 0 commitsSun 21:00 — 0 commitsSun 22:00 — 0 commitsSun 23:00 — 0 commitsMon 0:00 — 0 commitsMon 1:00 — 0 commitsMon 2:00 — 0 commitsMon 3:00 — 0 commitsMon 4:00 — 0 commitsMon 5:00 — 0 commitsMon 6:00 — 0 commitsMon 7:00 — 0 commitsMon 8:00 — 0 commitsMon 9:00 — 0 commitsMon 10:00 — 0 commitsMon 11:00 — 0 commitsMon 12:00 — 0 commitsMon 13:00 — 0 commitsMon 14:00 — 0 commitsMon 15:00 — 0 commitsMon 16:00 — 0 commitsMon 17:00 — 0 commitsMon 18:00 — 0 commitsMon 19:00 — 0 commitsMon 20:00 — 0 commitsMon 21:00 — 0 commitsMon 22:00 — 0 commitsMon 23:00 — 0 commitsTue 0:00 — 0 commitsTue 1:00 — 0 commitsTue 2:00 — 0 commitsTue 3:00 — 0 commitsTue 4:00 — 0 commitsTue 5:00 — 0 commitsTue 6:00 — 0 commitsTue 7:00 — 0 commitsTue 8:00 — 0 commitsTue 9:00 — 0 commitsTue 10:00 — 0 commitsTue 11:00 — 0 commitsTue 12:00 — 0 commitsTue 13:00 — 0 commitsTue 14:00 — 0 commitsTue 15:00 — 0 commitsTue 16:00 — 0 commitsTue 17:00 — 0 commitsTue 18:00 — 0 commitsTue 19:00 — 0 commitsTue 20:00 — 0 commitsTue 21:00 — 0 commitsTue 22:00 — 0 commitsTue 23:00 — 0 commitsWed 0:00 — 0 commitsWed 1:00 — 0 commitsWed 2:00 — 0 commitsWed 3:00 — 0 commitsWed 4:00 — 0 commitsWed 5:00 — 0 commitsWed 6:00 — 0 commitsWed 7:00 — 0 commitsWed 8:00 — 0 commitsWed 9:00 — 0 commitsWed 10:00 — 0 commitsWed 11:00 — 0 commitsWed 12:00 — 0 commitsWed 13:00 — 0 commitsWed 14:00 — 0 commitsWed 15:00 — 0 commitsWed 16:00 — 0 commitsWed 17:00 — 0 commitsWed 18:00 — 0 commitsWed 19:00 — 0 commitsWed 20:00 — 0 commitsWed 21:00 — 4 commitsWed 22:00 — 5 commitsWed 23:00 — 4 commitsThu 0:00 — 1 commitsThu 1:00 — 2 commitsThu 2:00 — 0 commitsThu 3:00 — 0 commitsThu 4:00 — 0 commitsThu 5:00 — 0 commitsThu 6:00 — 0 commitsThu 7:00 — 0 commitsThu 8:00 — 0 commitsThu 9:00 — 0 commitsThu 10:00 — 0 commitsThu 11:00 — 0 commitsThu 12:00 — 0 commitsThu 13:00 — 0 commitsThu 14:00 — 0 commitsThu 15:00 — 0 commitsThu 16:00 — 0 commitsThu 17:00 — 2 commitsThu 18:00 — 0 commitsThu 19:00 — 0 commitsThu 20:00 — 0 commitsThu 21:00 — 0 commitsThu 22:00 — 0 commitsThu 23:00 — 0 commitsFri 0:00 — 0 commitsFri 1:00 — 0 commitsFri 2:00 — 0 commitsFri 3:00 — 0 commitsFri 4:00 — 0 commitsFri 5:00 — 0 commitsFri 6:00 — 0 commitsFri 7:00 — 0 commitsFri 8:00 — 0 commitsFri 9:00 — 0 commitsFri 10:00 — 0 commitsFri 11:00 — 0 commitsFri 12:00 — 0 commitsFri 13:00 — 0 commitsFri 14:00 — 0 commitsFri 15:00 — 2 commitsFri 16:00 — 4 commitsFri 17:00 — 5 commitsFri 18:00 — 4 commitsFri 19:00 — 7 commitsFri 20:00 — 5 commitsFri 21:00 — 10 commitsFri 22:00 — 8 commitsFri 23:00 — 4 commitsSat 0:00 — 8 commitsSat 1:00 — 7 commitsSat 2:00 — 4 commitsSat 3:00 — 3 commitsSat 4:00 — 2 commitsSat 5:00 — 1 commitsSat 6:00 — 0 commitsSat 7:00 — 0 commitsSat 8:00 — 1 commitsSat 9:00 — 1 commitsSat 10:00 — 0 commitsSat 11:00 — 1 commitsSat 12:00 — 2 commitsSat 13:00 — 4 commitsSat 14:00 — 1 commitsSat 15:00 — 0 commitsSat 16:00 — 2 commitsSat 17:00 — 1 commitsSat 18:00 — 0 commitsSat 19:00 — 0 commitsSat 20:00 — 0 commitsSat 21:00 — 0 commitsSat 22:00 — 0 commitsSat 23:00 — 10 commits
Commit volume by weekday and hour (UTC). Larger dots mean more commits.

Who is committing

last 52 weeks
Maintainer commits110 (96%)
Community commits5 (4%)

115 commits in total over the last year.

DateListRankStars gained
Nov 8, 2026daily#15n/a
  • avelino/awesome-go

    A curated list of awesome Go frameworks, libraries and software

    192.7K stars · Go

  • kubernetes/kubernetes

    Production-Grade Container Scheduling and Management

    128.9K stars · Go

  • JuliusBrussee/caveman

    🪨 why use many token when few token do trick. Viral skill + proxy for coding agents that cuts 65% of tokens by talking like a caveman.

    118.3K stars · Go

  • microsoft/TypeScript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    111.9K stars · Go

  • infiniflow/ragflow

    RAGFlow is an agent-native database: a Retrieval-Augmented Generation (RAG) engine that grew into context infrastructure.

    93.8K stars · Go

  • caddyserver/caddy

    Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

    78.9K stars · Go