Skip to content

Instantly share code, notes, and snippets.

@einsteinx2
Last active January 3, 2020 22:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save einsteinx2/8070b893457f83c5f643 to your computer and use it in GitHub Desktop.
Save einsteinx2/8070b893457f83c5f643 to your computer and use it in GitHub Desktop.
Hey that's great! Here's a super quick overview to get you started, but if you have some time later, I can give you a more in depth walkthrough over a google hangout.
While it's coded in C#, we're targeting the Mono 3 runtime, not necessarily .NET. For all intents and purposes, these are the same, but we do occasionally use some Mono additions (like dllmaps for different platforms).
Basically the project it split into 2 assemblies: Core and Server.
Core contains the data model and basically any logic that isn't specific to the actual server part (i.e. could also be used in a client).
This is:
- Model objects (Song, Album, etc) that are basically just a set of properties with maybe a few relevant methods
- Repository objects (SongRepository, AlbumRepository, etc) which contain only database access methods for those model objects
- ApiResponse objects which are small model objects used by the Api handlers (more on those in a bit) to convert to JSON and return to the client
- And a few Utility/Extension classes
Server contains everything relevant to the actual API and media server.
This is:
- The WaveBoxMain and WaveBoxService classes which are the entry points (we use the Mono service wrapper to allow for it to be installed as a daemon on Linux or an actual Windows service on Windows — however it also works as a standalone terminal application on Windows).
- Services, which are dynamically loaded services to handle things like device syncing, http serving, nat, zeroconf, etc. These are created by simply implementing the IService interface and then enabling the service by name in the wavebox.conf file
- ApiHandler, which are dynamically loaded (as of the pull request I’m about to merge) and handle each API call. So one ApiHandler class corresponds to each API entry point. They generally call some methods on the data model objects or repositories in the Core library, create an ApiResponse model object, serialize to JSON, and return.
- Transcoding, which handles the various media format transcoding that is supported
- Folder scanning, which keeps the media collection up to date
- And some utility and extension classes
Couple of things to be aware of:
1. The Core assembly is re-used in a range of clients that we're building for iOS, Android, Mac, Windows, and Linux, using Xamarin's tools. I'm currently refactoring the Core assembly to remove any non PCL compliant namespaces, but that is not complete yet.
2. We're currently refactoring the whole project to use the Ninject IoC container for DI. You'll see some places where this is completed (like the Repository classes) and others where it isn't and the Ninject Kernel is being references statically. We're working towards full DI.
3. We're still refactoring the ApiHandlers. I'm about to merge in a big pull request, but after that we're working to decouple the HttpProcessor class and the handlers.
4. We're currently not using async and await much in the Core and Server as it wasn't supported in Mono 2. It is supported in Mono 3, so we'll be using more of that in the future.
5. Our database schema is a bit unusual. Instead of using primary keys for each table, we use a single autoincrementing value for all ids. This allows for API calls that can accept an id for different object types to take only an id and know what to do. So you'll see that the db tables have unique indexed fields for id rather than primary key.
6. Another unusual thing about the design is that the native clients are meant to download the server's actual sqlite database, to run local queries rather than using the server (the HTML5 client uses the API calls only). This allows for a lot of otherwise impossible features, like playlist creation in offline mode, etc. So you'll see the code we use to do db backups and we keep a query log to allow the clients to apply db changes without completely resyncing.
7. We don't currently have any unit or integration tests, this is a big problem and if you're experienced in that area, this would be the most useful contribution we could get right now. Since the project was not built with unit testing in mind, the best bang for the buck at the beginning would probably be to write integration tests that actually do API calls on a running server using a known library, parse the JSON, and check for expected responses. That way all systems from the back end to the user are tested in one shot, rather than doing integration tests on just the ApiHandler response objects or something. I'm pretty sure we could do this using C# and NUnit, but if you have any suggestions they would be most welcome.
8. Another big area that we could use some help is performance and memory profiling. If you're experienced in this area and could help speed up our scanning process or improve any other CPU/Disk/Memory intensive area, that would be awesome.
Hopefully that was a thorough enough intro. I had meant to keep it short but I didn't want to leave anything out. I'll probably add this to the WaveBox Wiki for others as well. Let me know if you have any questions about anything. I'm available on Hangouts and iMessage at einsteinx2@gmail.com pretty much any time, though currently I'm living in Amsterdam until about 3 weeks from now, so I'm in GMT+2 time zone at the moment. After that I'll be back in San Francisco.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment