Show HN: Tired of the non-portability of my playlists, I wrote my own format
universalplaylist.stavros.io
universalplaylist.stavros.io
Playlist and metadata are separate things, and coupling them is not good from an architectural perspective (redundancy/denormalization). The two can still be related, even in a single file: relational database. (plug: I'm working on http://jstimpfle.de/projects/wsl/main.html)
Sketch to make more clear what I mean:
{
"metadata": {
"buss_und_reu": {
"filepath": "Bach/Matthäuspassion/Buss_und_Reu.mp3"
"composer": "Johann Sebastian Bach"
},
"highway_to_hell": {
"filepath": "ACDC/Highway_to_Hell.mp3",
"artist": "ACDC",
"sha512": ...
}
},
"playlists": {
"mypl": [
"buss_und_reu",
"highway_to_hell"
],
"mypl2": [
"buss_und_reu"
]
}
}
This is more normalized, less redundant. Of course other schemes are thinkable, for example storing each playlist in its own file, but this way metadata and playlists will more quickly get out of sync.The library I'm working on allows automated conversions of such hierarchical data to/from relational databases, so data can be viewed, and even stored, as JSON, while integrity is checked based on an relational schema - something like
% TABLE Track trackid
% TABLE Filepath trackid filepath
% TABLE SHA512 trackid sha512
% TABLE Title trackid title
% TABLE Composer trackid composer
% [Declare also domains, keys, and foreign keys here...]It's definitely a better approach for a library, though, and it's how one would model the data in a database as well.
BUT... you have a defined schema, so it's probably to your advantage to use a storage format with a defined schema: ProtoBuf or Thrift. That would mean somebody trying to use your code would already have generated objects in their language.
As for the hashing algorithm, this is a good use for MD5 -- cheap and fast. You're unlikely to be concerned about somebody actively trying to generate the same checksum for two music files. For non-security (integrity verification) purposes, MD5 is still very appropriate.
About MD5, I was worried about a case where a service that serves user-submitted files would be exploited by MD5 collisions, leading users to open files that might exploit decoder bugs to execute code. Far-fetched, I know, but the tradeoff didn't seem worth it. I'm not married to that decision, though.
The downside of course is this requires pre-calculation of the p-hash for every track to use. But I can't think of a music application that doesn't require some kind of "library loading" step, so perhaps this could be accomplished then?
Of course none of this mitigates your concern with decoder bugs resulting in RCE, but I think that's probably best handled elsewhere (for example, sandboxed upload validation in your hypothetical user-uploaded-files service).
Example: I just moved all mp3, m4a files to the microSD card on my android phone, keeping names and folders identical, but my playlists are all empty now. Thank you Samsung! grr..
Making a path independent, p-hash independent (but utilizing, if available, or requested) playlist format is what I'd really want. The metadata should always be saved inside the file because metadata get's lost when you change the program. Filling up an sqlite db with all the metadata saved in your audio files would only speed up meta-data management and sync, but remove it's control from you.
Features I think make sense to expect from a perfect music player (without vendor-lock-in), be it run on mobile, web, desktop, cli or as a daemon:
• Path independant, file-name independant (thanks to p-hash) playlist import & export
• Playlist export options for ie. Samsung Music Player, iTunes or whatever crap we're locked-in currently
• Save Metadata incl. rating always inside audio files, because metadata get's lost when you change the program
• Extract Metdata from audio files into a database for speed, management and easier sync into the files
• Audio-Fingerprint may allow detection of: duplicates (hash independant), similars, classify genre and map mood-maps
• Batch-Convert between flac,mp3,ogg,mp4,m4a if user wishes without stupid dialogues. 128Kbit LAME-encoded MP3s don't sound converted to "best quality ogg"..
• Create P-Hashes (or another perceptual hash) initially, when idle, periodically or when requested
Example playlist format:
estelle.mp3, audio-fingerprint, House-Playlist
wu-tang.mp3, audio-fingerprint, PartyNightCan you hear us underneath that pile of bike sheds?
It's only bikeshedding when the decision has to be made by community!
https://en.m.wikipedia.org/wiki/XML_Shareable_Playlist_Forma...
I would really like the MBID to be the main way of identifying songs throughout the industry, and possibly the AcoustID fingerprint, as that's more specific. I think it would be fantastic if each song had its own UUID that computers could refer to, and this spec tries to incorporate that.
Since I don't really gain anything from reusing its format (most players won't support the strong IDs), I thought I might as well create a new format that was better suited to this task.
The UPF "ids" field maps to the XSPF "identifier" field: http://xspf.org/xspf-v1.html#rfc.section.4.1.1.2.14.1.1.1.2
XSPF is not M3U in any way. From the spec: http://xspf.org/xspf-v1.html#rfc.section.3.4
3.4 Content resolver
On a surface level you can use XSPF like any other playlist format. Drop a bunch of filenames into an XSPF document, prepend "file://" to each, and you're ready to go. Under the surface there is much more.
The guiding design principle was to separate the functionality of a catalog of files from the functionality of a list of songs. Most software music players on the PC have some sort of cache for file information. This cache stores a list, or catalog, of available files and metadata from ID3 tags and other sources. XSPF is not a catalog format. XSPF exists only to say which songs to play. Almost everything in XSPF is for the purpose of answering the question which resource, rather than the question what is this resource.
If XSPF is not a catalog format, what is it? XSPF is an intermediate format. We expected a new kind of software called a content resolver to do the job of converting XSPF to a plain old list of files or URIs. A content resolver would be smart enough to keep your playlists from breaking when you move your media from /oggs to /music/ogg. It would be able to figure out that a playlist entry by the artist "Hank Williams" with the title "Your Cheating Heart" could be satisfied by the file /vorbis/hankwilliams/yourcheatingheart.ogg. It might even know how to query the iTunes music store or another online provider to locate and download a missing song.
The content resolver maintains the catalog of your songs in whatever format it prefers. It might use a flatfile, a file in the Berkeley DB format, or a SQL database. It might use only ID3 metadata, but it might also know how to query MusicBrainz or another metadata service.
All XSPF user agents are content resolvers, in that they have complete leeway to turn the contents of a track element into a specific set of bytes.
3.5 Fuzzy names
Any given track can be identified in a number of ways. We provided means for absolute identifiers like URIs, filesystem paths and secure hashes, but also for query-based identifiers — free text fields like artist and work title and numeric fields for song length, all of which together should be enough for a good content resolver to turn into files.
format-standardized-algorithm audio fingerprint
Assuming that's not available on stock Android/iOS/etc a quick hash like md5 should suffice.The trade off would seem to be if the audio analysis hash needs to scrape though a huge playlist and it's running on a slow arm processor than it would be impractical and no one would want to use it.
MD5 might not be ideal, but how often do people edit their files after making one of these playlists?
If that's true, then nothing has to happen on export. You just dump the hashes you already know.
And then, when you try to load someone else's playlist, all you're doing is a bunch of hash-table lookups against the index you already have, to see if you have tracks with matching fingerprints.
(And the initial generation can also be made cheaper, if online music stores also adopt the fingerprint format, such that tracks you buy come with their fingerprints pre-calculated and embedded into the ID3 metadata. Then you can just dump those straight into your index on import.)
For example a fingerprint might or might not need to draw these distinctions:
- Clean ("Walmart") and dirty (profanity) mixes of the same recording
- Amount of opening and closing silence (often varies across releases)
- Different masterings
- Fidelity of the source recording (e.g. 64K Real Audio vs uncompressed PCM)
In addition, fingerprint developers are constantly improving on their algorithms.
We have enough playlists formats to support already (and most of them are half-baked/half-broken already). There is no good reason to not reuse and extend xspf.
(Last time I counted 16 major playlists formats in VLC...)
In version 0 of XSPF, dates were specified as an ISO 8601 date. In version 1 dates were specified as xsd:dateTime. This is the same thing (with better documentation) for almost every date in history, and as there are no playlist creation dates that might be different, there are no real world playlists that would be incompatible.
These single file media items generally have 1 or more Album Artists (e.g. the DJ) with each track in the performance blending into the next. Each track may have an associated Artist and Track name. Conventionally the best approach to solving these is to use a Cue sheet and a plugin for whatever media player that can load these simultaneously. Alternatively (though rarely) a container format that supports chapters / sub streams (e.g. mka) can be used.
I don't see a secondary (CUE sheet) format as being the right solution to this problem. To me a playlist and a list of tracks played in a performance are kind of the same thing and should be handled by the playlist.
My proposed extension to the UPL format is to allow each playlist entry to specify a part of the identified file. Now there's two possible ways of doing this:
1. Sub items under the entry (e.g. chapters)
2. Each entry specifies a start and end time (or duration)
You mention at [1] that this format is about display, I feel this fits within that idea by being something that is otherwise difficult to display using any other available info (ID3 tags etc.)
[1]: https://gitlab.com/universal-playlist/universal-playlist-spe...
- Pink Floyd, Brain Damage followed by Eclipse
- Queen, We Will Rock You followed by We Are the Champions
- Yes, Long Distance Runaround followed by The Fish (Schindleria Praematurus)
- the different movements of Beethoven's Fifth Symphony
Copying the actual songs into a directory that becomes "the playlist".
It's very inefficient and inelegant but after the 100th car stereo I encountered that couldn't make sense of the most basic m3u file, I just gave up ... my "playlists" are just more copies of the actual songs.
Problem solved.
You can represent hashes using the named-information URI scheme (https://tools.ietf.org/html/rfc6920), e.g. ni:///sha-256;u88lYWn4xAlto-6Bs79KHHYDAu28US71ui5Be6C-ZVw.
Your filepath could be file:///Anciients/Following%20the%20Voice.mp3; your mbtrackid could be musicbrainz:b00a2b97-53f1-485a-9121-1fe76b55e651 (since I don't think an authority makes sense in this case).
This would also permit multiple entries for certain types of identifier, which doesn't make so much sense for hashes (although it's possible for the same recording to have two different MP3 encodings, so … maybe it's not crazy), but would be nice when e.g. a song is found in multiple places in the filesystem, or can be retrieved from multiple locations.
Notice that the cardinality is "zero or more."
I do wonder about what you said, though: Would it be useful to be able to specify two different file paths or URIs for the same item? I'm leaning towards no, because, when clicking on an entry, you want it to start playing, so the choice is made when adding the file, rather than when playing it. The problem with multiple file paths is that both have the same weight, so how do you choose?
I guess you'd choose the same way you choose between the formats already, based on whatever is available/found.
Yup, which I think is good, since URI schemes already exist for many identifiers, and more can be added.
> Would it be useful to be able to specify two different file paths or URIs for the same item?
I think so. Maybe one is one removable medium and the other is on another.
> The problem with multiple file paths is that both have the same weight, so how do you choose?
If they have equal weight, does it matter which you choose?
Probably in this day and age it would have better chance at adoption if it would be a JSON file. It's just network effects.
YAML is a superset of JSON, so the file can look like JSON if you want, although that's not mandatory. I don't think there are many languages that don't support YAML these days, and one of my goals was to make the format not-entirely-unreadable by humans.
YAML is a big and complex format though, libyaml's header is bigger than the entirety of libtoml.
I think hand-editable playlists are a good idea, just not a fan of yaml.
Ie yaml isn't nearly as supported as it looks once you start actually trying to use it between languages with anything more than 1.0 as most of the purported library support seemed to be roughly 2009 abandonware.
Yaml also precludes easy https://www.sqlite.org/json1.html and postgresql jsonb etc. Not to mention trivial js consumption of the file.
Superset or not, Yaml automagically introduces bugs to and from json through the magic powers of more lines of code involved.
Most projects would have a hard time not having a json library these days? I've not done a survey of music apps though.
In practice, as someone that has been vim/emacs for a few decades I'd put a low priority on yaml vs json text editability. They are about the same for changing a few characters and I'd probably use orgmode+ python to do anything non-trivial but not full program(and probably drive the transform via json data from somewhere...).
In 2017 yaml doesn't seem tasteful for this format.
However, with all the apprehension in the thread, I'm rethinking this decision. Maybe TOML would be better?
Toml is nice for simple configs/Cargo.toml but I think everything I said basically applies to it as well. Toml has recent work on it in 2017 for some (but not broad) library support but what does it look like in 2024?
While you are thinking maybe look at this
https://stedolan.github.io/jq/
and how people could leverage it when using your format.
TOML is a much simpler format than YAML, and is a dialect/formalisation of INI files which have yet to go out of style. Direct database storage seems like a red herring (why would you want to store a playlist as a json blob?), so does "trivial JS consumption of the file".
[0] the entire spec is 10 pages, and that's with lots of examples, YAML 1.2 is ~80 pages
And here I was feeling badly for thinking that it'd be better as S-expressions! I wasn't going to post that, but since you already did, here's the same example in the original YAML:
---
format: UPL1
name: Favorites
id: 2b43009f-d6a6-4f00-8533-09a9a73d8b54
entries:
- artist: Anciients
title: Following the Voice
duration: 408.764081632
ids:
sha2: e577cce68a69735acccd5d8603b3e663f6aa5bc9
sha3: e577cce68a69735acccd5d8603b3e663f6aa5bc9
mbtrackid: b00a2b97-53f1-485a-9121-1fe76b55e651
filepath: Anciients/Following the Voice.mp3
uri: nfs://example.com/music/ftv.mp3
as JSON: {
"format": "UPL1",
"name": "Favorites",
"id": "2b43009f-d6a6-4f00-8533-09a9a73d8b54",
"entries": [
{
"artist": "Anciients",
"title": "Following the Voice",
"duration": 408.764081632,
"ids": {
"sha2": "e577cce68a69735acccd5d8603b3e663f6aa5bc9",
"sha3": "e577cce68a69735acccd5d8603b3e663f6aa5bc9",
"mbtrackid": "b00a2b97-53f1-485a-9121-1fe76b55e651",
"filepath": "Anciients/Following the Voice.mp3",
"uri": "nfs://example.com/music/ftv.mp3"
}
}
]
}
And as S-expressions: (playlist
(format UPL1)
(name Favorites)
(id 2b43009f-d6a6-4f00-8533-09a9a73d8b54)
(entries
(entry
(artist Anciients)
(title "Following the Voice")
(duration 408.764081632)
(ids
(sha2 e577cce68a69735acccd5d8603b3e663f6aa5bc9)
(sha3 e577cce68a69735acccd5d8603b3e663f6aa5bc9)
(mbtrackid b00a2b97-53f1-485a-9121-1fe76b55e651)
(filepath "Anciients/Following the Voice.mp3")
(uri nfs://example.com/music/ftv.mp3)))))
I wonder which in general people think are prettier.The opposite step would be a bit harder, I'd have to index all music files in a library and convert the UPL to PLS, which might be too much work for a simple tool, but I'll see if it can be done quickly and dirtily. That would be a nice idea, `upl2pls -i favorites.upl -o favorites.pls` and your favorite player is ready to play the files.
https://gitlab.com/universal-playlist/pls2upl
You can also star the repo of the spec website itself:
https://gitlab.com/universal-playlist/universal-playlist-spe...
I was planning on making a cli around some parsing logic and convert/export from various services (wherever I can get an API from).
Here's the Python converter I'm playing with:
For example, download credit card transactions as csv. Import into Quicken. Quicken decides that skookumchuck is the payee on all the transactions. Arggh. This is 2017 fer cryin out loud.
magnet:?dn=Anciients%2FFollowing%20the%20Voice&xt.1=urn:sha1: e577cce68a69735acccd5d8603b3e663f6aa5bc9&xs=nfs://example.com/music/ftv.mp3&x.dur=408.764081632
Magnet already does this.
> it's better suited to torrents and things like that.
I don't understand what you mean by this.
Why exactly is it more suited?
> I don't see how it can be applied here, or what benefit it has over a simple list.
Winamp supported playlists containing http urls.
If it supported magnet urls you would have every feature of your proposal and tooling to build other applications on.
You could build this in about an hour and see a portable durable playlist.
Any help would be appreciated!
Subtitle: "The Universal Playlist Format"
Analysis: https://xkcd.com/927/
With a good plugin support an ecosystem to allow interoperability of playlists becomes possible.
> The Universal Playlist Format (UPF) is a data format for music playlists. It aims to be more flexible and complete than existing formats. A Universal Playlist (UPL) will allow better description of the tracks it contains, as well as enable added functionality in music players.
I am really curious about moving from Google Play to a local library, though - can you say more about that? GP has been losing tracks, slowly but steadily, or mislabeling them horribly.
It aims to be more flexible, but it doesn't specifically aim to replace them. I don't mind players using M3U, PLS, or whatever else, I just want the extra features that UPL would provide. I guess it's a fine distinction.
> I am really curious about moving from Google Play to a local library, though - can you say more about that?
Well, if Google Play allowed me to export my playlists as UPL, my local foobar2000 could easily find the tracks in my local library (I already have them all) and correlate which tracks on Google Play are which tracks locally. As it stands, I have to go into my Google Play playlist, find the thousands of songs in my playlists one by one locally, and add them to my local playlists as I find them.
However, it has been said, that there are no new stories; they've all be written, and that everything worth inventing has already been invented.
If we actually believed that, there'd be no new books, and stuff like graphene, and CRISPR wouldn't exist.
I say, let everyone re-invent the wheel, one day someone might actually get anti-grav ones working - You never know.