{"id":16495080,"url":"https://github.com/arlodotexe/strix-music","last_synced_at":"2025-03-16T18:32:13.286Z","repository":{"id":36956126,"uuid":"354057742","full_name":"Arlodotexe/strix-music","owner":"Arlodotexe","description":"Combine any music sources into a single library. It's your music. Play it your way.","archived":false,"fork":false,"pushed_at":"2023-11-20T22:15:06.000Z","size":77962,"stargazers_count":142,"open_issues_count":35,"forks_count":4,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-16T04:41:19.358Z","etag":null,"topics":["dotnet","dotnet-standard","music","music-library","plugins","sdk","uno-platform"],"latest_commit_sha":null,"homepage":"http://www.strixmusic.com","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Arlodotexe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"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},"funding":{"github":"Arlodotexe","patreon":"arlodotexe","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":"http://paypal.me/arlogodfrey"}},"created_at":"2021-04-02T15:24:05.000Z","updated_at":"2025-03-10T12:06:41.000Z","dependencies_parsed_at":"2024-04-09T11:52:46.278Z","dependency_job_id":null,"html_url":"https://github.com/Arlodotexe/strix-music","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arlodotexe%2Fstrix-music","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arlodotexe%2Fstrix-music/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arlodotexe%2Fstrix-music/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arlodotexe%2Fstrix-music/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Arlodotexe","download_url":"https://codeload.github.com/Arlodotexe/strix-music/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243910675,"owners_count":20367546,"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":["dotnet","dotnet-standard","music","music-library","plugins","sdk","uno-platform"],"created_at":"2024-10-11T14:27:28.561Z","updated_at":"2025-03-16T18:32:08.252Z","avatar_url":"https://github.com/Arlodotexe.png","language":"C#","funding_links":["https://github.com/sponsors/Arlodotexe","https://patreon.com/arlodotexe","http://paypal.me/arlogodfrey"],"categories":[],"sub_categories":[],"readme":"# \u003ca href=\"https://strixmusic.com/\" target=\"_blank\"\u003e\u003cimg align=\"left\" src=\"www/favicon.ico\" height=\"100\" /\u003e\u003c/a\u003e Strix Music\nThe Strix Music App is the music player that adapts to you. Combine your libraries from multiple sources and services, and reskin the app to your taste. Or, try building with the Strix Music SDK. Seamlessly integrate any music source or Strix plugin into your app.\n\n## Open source alpha\n\nWe spent the last 2 years designing and refining the SDK, making sure it could cleanly handle anything we threw at it.\n\nThe foundation has been laid, and we've entered the next stage in development: **open source alpha**.\n\n✔ Use the SDK in your own project to easily interface with any music service.\n\n✔ Extend any strix-compatible app with new music sources and plugins.\n\n✔ Provide feedback, feature suggestions or bug reports to improve the SDK for everyone.\n\n❌ Build WinUI applications using our inbox control library (not ready)\n\n❌ Download the Strix Music App, our incubation and demo project for the SDK (not ready)\n \n\n## **The Strix Music SDK**\n  - Our incubation project where we define and refine the infrastructure before going cross-language.\n  - Easily bring the power of Strix to your own projects. \n  - Rapidly interface with any available music services.\n  - Merge multiple music services together into a single, transparent API.\n   - Bring new music services to the Strix ecosystem.\n\n## **The Strix Music App**\n  - Our incubation and demo app for the Strix SDK.\n  - You rely on the SDK for your apps, so we built our own app to catch bugs, test new features, and push boundaries. Dogfooding ftw!\n  - Much like how Surface is Microsoft's vision for making the most of Windows, this app is _our_ vision for making the most of the SDK.\n  - Multi-service, thanks to the Strix Music SDK.\n  - Multi-platform, powered by the Uno Platform.\n  - Multi-skinnable, thanks to the highly refined data structure in the SDK and the MVVM architecture built on top.\n\n## **The Cores: interchangeable music sources**\nA core is any music source implemented with our CoreModel APIs - a standardized, flexible and refined data structure designed to fit the needs of different types of music sources.\n\n### Basic core usage\n```csharp\n// Core method signature may vary.\nvar localFilesCore = new LocalFilesCore(id, folderAbstraction);\n\n// Perform any async setup needed before usage, such as login or database init.\nawait localFilesCore.InitAsync();\n\n// Get tracks from the library\nvar tracks = await localFilesCore.Library.GetTracksAsync(limit: 20, offset: 0).ToListAsync();\n```\n\n### Merge them together!\nThe Strix Music SDK allows you to merge multiple sources together. Libraries, search results, recently played, devices, etc. are all seamelessly combined under a single data structure. \n\n```csharp\n// Assumes that `config` contains all the info needed to function without user interaction.\n// Spotify and YouTube cores are examples only.\n\n// Create a few cores.\nvar onedrive = new OneDriveCore(id, config);\nvar youtube = new YouTubeCore(id, config);\nvar sideloaded = new RemoteCore(id, config);\n\n// Merge them together\nvar mergedLayer = new MergedCore(mergedConfig, onedrive, spotify, youtube, sideloaded);\nawait mergedLayer.InitAsync();\n\n// Get albums in all libraries\nvar albums = await mergedLayer.Library.GetAlbumItemsAsync(limit: 20, offset: 0).ToListAsync();\n\n// and play one\nawait mergedLayer.Library.PlayAlbumCollectionAsync(startWith: albums[5]);\n\n// Search everywhere\nvar searchResults = await mergedLayer.Search.GetSearchResultsAsync(\"Zombie by Jamie T\").ToListAsync();\nvar tracks = await searchResults.GetTracksAsync(limit: 100, offset: 0).ToListAsync();\nvar artists = await searchResults.GetArtistItemsAsync(limit: 10, offset: 0).ToListAsync();\n\n// Get all the sources that were combined to create the track\nvar sourceTracks = tracks[0].Sources; // FilesCoreTrack, YouTubeTrack\n```\n\n### The sky is the limit\n\n#### Check out our [docs](https://strixmusic.com/docs/) to get started\n\nAnyone can create a core, and a core can be anything that satisfies the CoreModel interfaces.\n\nHere are some ideas for cores we'd like to see join the Strix ecosystem (click to expand):\n\n- \u003cdetails\u003e\n  \u003csummary\u003eFile based (90%+ code sharing)\u003c/summary\u003e\n\n  - [x] Local Files\n  - [x] OneDrive\n  - [ ] Google Drive\n  - [ ] Dropbox\n  - [ ] [IPFS](https://ipfs.io/)\n  - [ ] FTP\n  \n  \u003c/details\u003e\n\n- \u003cdetails\u003e\n  \u003csummary\u003eStreaming based (all possible but not promised)\u003c/summary\u003e\n\n  - [ ] YouTube\n  - [ ] SoundCloud\n  - [ ] Spotify\n  - [ ] Pandora\n  - [ ] Audible\n  - [ ] Deezer\n  \n  \u003c/details\u003e\n\n- \u003cdetails\u003e\n  \u003csummary\u003eHardware based\u003c/summary\u003e\n\n  - [ ] CDs\n  - [ ] Zune\n\n  \u003c/details\u003e\n  \n- \u003cdetails\u003e\n  \u003csummary\u003eRemote (out of process / on another machine)\u003c/summary\u003e\n\n  - [ ] [OwlCore.Remoting](https://arlo.site/owlcore/articles/OwlCore.Remoting/index.html) (see [#103](https://github.com/Arlodotexe/strix-music/issues/103))\n  - [ ] gRPC\n  - [ ] Standardized REST API\n\n  \u003c/details\u003e\n\n## Model Plugins: Easy customization\n[Model plugins](https://strixmusic.com/docs/reference/api/StrixMusic.Sdk.Plugins.Model.html) are an _extremely_ modular and flexible way to customize the SDK. \n\nIn short, a model plugin modifies data or behavior for any [AppModel](https://strixmusic.com/docs/reference/api/StrixMusic.Sdk.AppModels.html) in the SDK by wrapping around it and selectively overriding members, then taking the place of the original model.\n\nOnce you have at least one model plugin, use the [PluginModels](https://strixmusic.com/docs/reference/api/StrixMusic.Sdk.PluginModels.html) layer to wrap around an existing [data root](https://strixmusic.com/docs/reference/api/StrixMusic.Sdk.AppModels.IStrixDataRoot.html), and provide plugins that you want applied to all interface implementations in the data structure. For example, an [ImageCollection](http://strixmusic.com/docs/reference/api/StrixMusic.Sdk.Plugins.Model.ImageCollectionPluginBase.html) plugin is applied to `IAlbum`, `IArtist`, `IPlaylist`, etc..\n\nThen, simply take the place of the original data root.\n\n### Applying model plugins\n\n```csharp\n// Create the AppModels with one or more sources\nvar mergedLayer = new MergedCore(mergedConfig, onedrive, spotify, youtube, sideloaded);\n\n// Add plugins\nvar pluginLayer = new StrixDataRootPluginWrapper(mergedLayer,\n    new FallbackImagePlugin(fallbackImage),\n\n    // Handle playback locally, add start/stop flair, bring your own shuffle logic, whatever you want.\n    new PlaybackHandlerPlugin(_playbackHandler),\n\n    // Other ideas that are possible\n    new LastFmPlugin(),\n    new MissingMetadataFromMusicBrainzPlugin(),\n    new MusixmatchSyncedLyricsPlugin(),\n    new CacheEverythingOfflinePlugin(),\n);\n\n// Optionally wrap with ViewModels for MVVM.\nvar viewModel = new StrixDataRootViewModel(mergedLayer); // without plugins\nvar viewModel = new StrixDataRootViewModel(pluginLayer); // with plugins\n\n```\n\n### Example: fallback images\nIn this example, we create a plugin that can inject a fallback image into any empty `IImageCollection`.\n\n```csharp\n// Implement the PluginBase that corresponds to the interface you want to affect.\npublic class AddFallbackToImageCollectionPlugin : ImageCollectionPluginBase\n{\n    private readonly IImage _fallbackImage;\n\n    public AddFallbackToImageCollectionPlugin(ModelPluginMetadata metadata, IImageCollection inner, IImage fallbackImage)\n      : base(metadata, inner)\n    {\n        _fallbackImage = fallbackImage;\n    }\n\n    public override int TotalImageCount =\u003e base.TotalImageCount \u003e 0 ? base.TotalImageCount : 1;\n\n    public override async IAsyncEnumerable\u003cIImage\u003e GetImagesAsync(int limit, int offset, CancellationToken cancellationToken = default)\n    {\n        if (base.TotalImageCount == 0)\n            yield return _fallbackImage;\n\n        await foreach (var item in base.GetImagesAsync(limit, offset, cancellationToken))\n            yield return item;\n    }\n}\n\n// Create a container that identifies your plugin and tells us how to construct the pieces:\npublic class FallbackImagePlugin : SdkModelPlugin\n{\n    private static readonly ModelPluginMetadata _metadata = new(\n        id: nameof(FallbackImagePlugin),\n        displayName: \"Fallback images\",\n        description: \"When an image collection is empty, this plugin injects a fallback image.\",\n        new Version(0, 0, 0));\n\n    public FallbackImagePlugin(IImage fallbackImage)\n        : base(_metadata)\n    {\n        ImageCollection.Add(x =\u003e new StrixDataRootPlaybackHandlerPlugin(_metadata, x, fallbackImage));\n    }\n}\n```\n\n# Our pledge to you\nThis project was built on these core values, and they serve as a promise to our community.\n\n## Free and open standard\nThat means community focused and no paywalls. You'll never be charged to build with or use the Strix Music standard, Strix Music SDK or the Strix Music App. \n \nTo drive our efforts, we rely on donations and contributions from users like you.\n\n## Privacy focused\nNo logs are generated and no servers are contacted unless you say so. Your data is exclusively put into your hands.\n\n## Perpetually preserved\nApps that work standalone and offline are a lost art.\n\nWe want to build software where a given version will always work, whether on Day 1 or Day 10,000\n\nThe entire project (docs, website, source code, build scripts, dependencies and releases) are perpetually preserved in every release, and hosted on [IPFS](https://ipfs.io/). If _anyone_ has these things on their local node, you'll be able to access it.\n\nOur project will never break from a server outage, and cannot be taken down or censored.\n\nNot even an apocalypse could ruin our hard work.\n\n---\n\nCopyright 2022 © Arlo Godfrey\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farlodotexe%2Fstrix-music","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farlodotexe%2Fstrix-music","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farlodotexe%2Fstrix-music/lists"}