FLAC Cue Sheet Support

I ripped my entire CD collection into .flac files with separate .cue files, choosing this format because many of my favorite albums do not neatly separate into tracks. I was tired of Plex not supporting this correctly and put together a Plex Scanner and a python based FUSE filesystem to allow Plex to work with my FLAC music library. It’s fairly basic and currently fails on a small number of my albums but I’m happy enough with it now that I wanted to share it with anyone else that needs this. It’s written for a Linux Plex server (specifically a Synology x86_64 v6.2 server, needing the FFMPEG and Python3 packages installed)–it may require some modification to work on other systems.

You can find the files on GitHub.

As a brief overview:
Scanner:

  • The Scanner looks for any .cue files in the folder.
  • The Scanner reads the file and track information from the .cue.
  • The Scanner creates a Track entry for each track in the .cue, giving them a filename that includes the track start and end times: /flaccue/{path to .flac file}.{start time}.{end time}. The start and end times are the same format as in the .cue file: MM:SS:FF, with MM being minutes, SS seconds, and FF frames (1/75th of a second).
  • The Scanner removes the .flac file from the list of files to parse.
  • If it fails in this process, the .flac file is left in the list of files to parse.
  • Anything not handled by the .cue parsing is sent to the normal Plex Music Scanner.

FUSE filesystem:

  • The basic filesystem is a read-only mirror of your root directory. You can access anything the user running the code can by simply prepending /flaccue. Don’t run it as root! Ideally run it as the same user running the Plex server.
  • When a filename includes .flac.{start time}.{end time}, it is handled differently.
  • Basic file properties are pulled from the .flac file, with file size adjusted to account for the shorter length.
  • When one of these special “files” is opened, I use FFMPEG to extract the requested portion of the .flac file and store it as a wave file in a byte array in memory.
  • When accessed, I return the appropriate bytes from the byte array in memory.
  • When all references to the special “file” are closed and no access has happened for about a minute, I remove references to the byte array and allow normal garbage collection to clean up.

I hope this helps you until they finally add proper support for these files to Plex.

6 Likes

Great work!

This is really cool.

I make dj mixes and record them to long high-bit-rate .mp3, and the mix sotware I use spits out a .cue file showing when each track is crossfaded >= 51% of the audio. I would love it if Plex player would recognize this metadata and present it on screen so that each new mixed track is shown with artist and song name instead of just the long form mix track’s metadata.

For example, mixcloud.com does an excellent job of this. When I upload my audio, I can then upload my .cue file which immediately becomes the tracklist shown on screen, changing as each new song mixes in. (They also parse the audio with some kazaa-type algorithm and electronic-music database, but when that doesn’t recognize a track, the .cue-inspired playlist seems to be a fall back)

Some of my mixes:

Notice if you scrub through a mix, the song data on the player changes… That’s what I want in Plex.

–Do you have a way for your plugin to work for mp3?
–Can it be relegated to certain subfolders? I just want my dj mixes parsed…
–I do some coding: can you tell me the best way to get started writing Plex media server / Synology NAS integrations as you have? Just a point to to a best practices style guide or anything similar would be inspirational and help cut research time.

Making it work with MP3 (or any music format) should be straightforward. The hardest part would be parsing the metadata in the scanner code as that is currently done with FLAC specific code (more time consuming than hard, and I probably should rely on the cue sheet information more). The actual file handling is done with ffmpeg, which appears agnostic to file types. It would just need to check for ‘.mp3.’ and similar instead of only ‘.flac.’. I’ll see what I can do when I have some time. While I’m at it, can you think of any other formats that would be useful? Right now, .flac, .mp3, and .wav come to mind.

Making it work just like Mixcloud is unlikely as a Plex scanner–my code fools Plex into seeing multiple separate files and provides a pseudo file system to parse the files Plex requests. Plex appears to need separate files for separate display entries with the way the underlying structure is built (trying to achieve this without a fake filesystem failed before I moved to this approach). The way Plex buffers data on my primary system, this solution still gives gapless playback. But it’s critical to have a player with gapless playback support if you want this to work right for your needs–I have my system running on a Raspberry Pi using Rasplex and it works great (with the occasional reboot). Speaking of which…I should also change the “Full album” feature to instead indicate the disc number and use disc 0 for file ordering (multi disc playback would work better this way, both with and without the album split). This feature was added to handle non-gapless playback of albums where it matters on systems that don’t support it.

Related to showing title and artist info, my code currently assumes everything but the track title and time info stays the same for all tracks in the cue file. Changing this wouldn’t be difficult but I’ll need to make sure Plex keeps the albums together. I’ll need to play with this a bit as well.

Plex itself provides the ability to handle directories differently using multiple top-level libraries. That said, this plugin falls back to the default Plex scanner if the special parsing fails. I have a mix of formats (including FLAC files without cue sheets) and they get parsed without issue. Are you seeing issues with other files in your music folders when using this code for FLAC cuesheets?

As far as Plex coding goes, learning how all the underlying structure works was probably the hardest part… Overall, I found sample source code the most useful, combined with google searching for specific issues. I didn’t find any real documentation (I did find complaints from others that Plex doesn’t provide an up-to-date guide for plugin/scanner/agent development…). You can find the built in scanner source here. They also have lots of other plugins and such in the plexinc-plugins repositories.

For Synology packaging, the hard part is finding the documentation . Things are documented quite well once you find it.

Try the latest version and let me know how it works for you. It should work with any file supported by ffmpeg that doesn’t somehow already include “.flaccuesplit.” in the filename. I also made album artists and track artists work better, as well as cleaner full disc vs track selection. Make sure to use a gapless playback client to avoid track splitting.

I sadly didn’t find an easy way to have it work with a premium Plex music library, which does audio database lookups like Mixcloud–if anyone knows a good way to add to that, I’d be happy to hear.

Cenko,

sorry I haven’t logged into Plex forum for a while–I will check this out. I need to get into my mixing software and set it to record and export FLAC instead of mp3 to test it out, sometime in the next couple weeks.

I’ll report back when I get it done and tested.

Seems like you took a lot of time into this, as I read it seems great but how would I be able to install / enable thin on a Ubuntu PMS? any step by step thing I can follow?

Thanks.

There isn’t any sort of manual for this but installation shouldn’t be too difficult. You can look through the Synology package for some details (particularly the postinst and start-stop-status scripts) but I’ll briefly explain things below.

The code comprises two main parts, a Plex Scanner and a FUSE filesystem.

The Plex Scanner just needs to be dropped into your Plex folder system as it is in the Git repository (Plex Media Server\Scanners\Music). “FLAC Cue Scanner” should then show up as an option when you create a new Music library in Plex. Effectively, this scanner creates a new “file” for every entry in the cue sheet, which is then parsed by the FUSE filesystem to create the track upon request (keeping a temporary copy in RAM). Note that the Scanner installation is NOT done by the Synology package–it’s pretty trivial but still is a manual process.

The FUSE filesystem is also fairly easy to run. It requires Python, libfuse, and ffmpeg, as well as python-fuse, ffmpeg, and mutagen Python libraries. It essentially mirrors your root directory under a /flaccue/ directory, which you’ll need to create. Once all that is ready, you can just run the Python script from the command line, specifying that you want to mirror / -> /flaccue (flaccue.py / /flaccue/). Then, when Plex requests a “file” from this filesystem, it parses the filename to determine the start and end times from which to extract music from the FLAC file. You may need to configure FUSE to allow things to run correctly–my Synology package drops fuse.conf into /etc/.

The FUSE filesystem can be run from a command line but it helps to automatically load it. There are lots of ways to handle this–I used Synology package management but it wouldn’t be hard to make a cron job or a service daemon. Note that I do not recommend running the filesystem as root as it is primarily based on a mirroring filesystem and I didn’t spend any effort on access control (anything without “.flaccuesplit.” in the filename is simply mirrored, although it should be read-only). I run it as the same user as the Plex server, which restricts access to everything the Plex server could already access.

Hope this helps!

Thanks a lot, I’ll try to set it up, so then at the end you must create a new Library to be able to include all these CUE+FLAC files, instead of adding them to your existing Music Library right?
Also, when adding the files to the library does it mirror every file? or is this only done when you open a specific file?
When you say FUSE filesystem do you reffer to this as a new partition on your hard drive? sorry for all these questions, but I don’t want to mess up my PMS

You may be able to change the Scanner for your current music library but you should probably create a new one. This is especially true if you are using a Premium library–I couldn’t find an obvious way to add this with the Premium Scanner. So I actually have two music libraries, one with the premium scanner and one with this scanner (I tend to only use the one with this scanner, though).

The FUSE “mirroring” does not create any files or write anything to disc. Nothing on your disc should change. FUSE is a mechanism to automatically process file system requests with custom code. This specific FUSE filesystem presents a folder structure that looks identical to your root file structure (i.e., mirrored). Any file you try to access through normal means simply passes through the actual file, with the exception of writing to disc (writes should not work). Only when you make a filesystem request for a file with “.flaccuesplit.” in the name (which I expect will never exist on anyone’s actual filesystem), the FUSE code treats that request differently. Instead of simply passing through the file, it loads the file into memory and extracts the music segment specified by the filename (the full filename will be something like /flaccue/music/random_album.flaccuesplit.10:25:17.12:55:20.flac, which would access /music/random_album.flac). It creates and stores an uncompressed copy of the extracted music in RAM and returns that data when requested through the filesystem. It stores the data in RAM for a few minutes after the file is closed and reuses the data if requested again shortly thereafter, then forgets it. In practice, you can use this same filesystem to extract music into separate files using a simple copy command (although you lose any compression–it’s basically a .wav file at that point).

All that said, backups are your friend. I have not had any issues with this code but I’ve only tested on my machine with my Plex server. No one has explicitly told me that this successfully works on their machine, although replies in this thread and the number of link clicks suggest people have. It’s been working great for me for months now but that’s a sample size of one.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.