I'm creating this issue to gather information and discuss about enhancing playlist management in Airsonic, particularly for the purpose of creating smart playlists.
In the "More" page, "Shuffle Radio" can shuffle through a collection based on a combination of the following parameters, provided through HTTP form data:
Single-parameter shuffle is present on many pages for these items:
The following changes can be easily added without changing the architecture a lot:
The "Shuffle Radio" feature is a first workaround for the absence of smart playlists, but it suffers from the following issues.
We're going to need a new way to search through the database in order to implement smart playlists in a maintainable way. The way parameters are passed to Shuffle Radio is not going to be very easy to maintain.
I want to add a new search query syntax, that can be used as a base for smart playlists (this means we can just record the search query in a field in the db and use that). Some ideas already discussed in https://github.com/airsonic/airsonic/issues/803#issuecomment-570927765.
The search query syntax can be:
Running the search query can be:
I think adding a new table/DAO is the correct thing to do. Existing PLAYLIST table is not made for automatically-updated playlists, so anything we bolt on top of that is not going to be very clean.
So, this means adding a new SMART_PLAYLIST table to Airsonic, with the following fields:
idusernameis_publicnamecommentquerycached_file_count ?cached_duration ?Search results can be populated on demand (I think this is the easiest solution, hopefully we won't have too much trouble with performance).
Some attributes could be cached (e.g. an estimate of the file_count and duration) so that they could be displayed in the playlist page, optionally updated during media scans.
We should add a way to browse the resulting search results/playlist. Here are a few suggestions, in order of difficulty:
datatables)datatables everywhere in Airsonic)A few things could be added to make it easier to use:
Some ideas on the subject:
=)The most basic query syntax I can imagine is a space-separated sequence of search terms in the form, where a field only appears once:
field:value (if value doesn't have spaces)field:"value with spaces" (if value has spaces)The following column types can be supported (mostly borrowed from Beets, with a small subset of the features):
field:true, field:t, field:1, field:y, field:yes all evaluate to "field is true"field:false, field:f, field:0, field:n, field:no all evaluate to "field is false"field:value means that field contains valuefield:value means that field is exactly equal to valuefield:yyyy-mm-ddfield:yyyy-MM-dd hh:mm:ssfield:hh:mm:ssThe following columns can be supported:
path: media_file.pathtitle: media_file.titlealbum: media_file.albumartist: media_file.artistalbumartist: media_file.album_artistdiscnumber: media_file.disc_numbertracknumber: media_file.track_numberbitrate: media_file.bit_rateduration: media_file.variable_bit_ratefilesize: media_file.file_sizecomment: media_file.commentcreated: media_file.createdchanged: media_file.changedlastscanned: media_file.last_scannedmb_release_id: media_file.mb_release_idmb_recording_id: media_file.mb_recording_idgenre: media_file.genreyear: media_file.yearfolder: media_file.folderlastplayed: media_file.last_playedformat: media_file.formatplaycount: media_file.play_countalbumrating: user_rating.ratingstarred: starred_media_file.idThe following range queries can help a lot to narrow down media files, for both numerical and date types:
field:..value means that field is at most valuefield:value.. means that field is at least valuefield:a..b means that field is between a and bRelative values can be supported:
-1d (1 day ago)-2w (2 weeks ago)-3m (3 months ago)-1y (1 year ago)A search term can be negated by prefixing it with - or ^.
If a field appears multiple times, these occurrences are grouped together with an OR boolean operator ; otherwise fields are linked with the AND boolean operator.
Negated fields would be grouped together separately, for example:
^artist:who ^artist:beatles artist:the
..is equivalent to artist contains 'the' AND artist contains neither "who" nor or "beatles".
Note that this is not compatible with how Beets does things (which is, default to "AND" everywhere).
We could add support for partial dates (yyyy[-mm[-dd[ hh[:mm[:ss]]]).
We can also add field:=value, meaning that field is exactly equal to value.
Regular expressions support can be added to the existing string comparison operators using something like field::regexp.
It is unclear to me now if SQL support on different databases is good enough, or if we should use our own regular expression engine so that things are consistent. From what I hear we may have to use our the latter.
Boolean operators can work with the following priorities:
(field:value field2:value2))field:value field:value2)field:value field2:value2)This way, the basic syntax keep on working, but boolean operators add additional possibilities.
Basic query:
genre:Rock year:2018 starred:true albumrating:5
More complex query with year and rating ranges, and multiple genre selection:
genre:Pop genre:Rock year:2010..2018 starred:true albumrating:3..
Find songs that are not starred or rated and were not recently played:
lastplayed:..-2m starred:false albumrating:0
Let's roughly design the whole picture.
By doing so, you can divide your requirements into stages, right?
OK.
I will reconsider according to your plan
It's a good idea to create a new, completely new advanced search screen, and then delete the legacy incomplete advanced search once it's complete. To be honest, it's not worth the reuse.
Sounds like a good idea. That's what I was thinking of doing too.
In terms of interface design order, the new Criteria and Results placed in the domain package will be the top priority. Since they already exist, a name like DetailsSearchCriteria and DetailsSearchResults might make sense. Criteria are necessary for design consideration of search, and Results are necessary for design consideration of screen design.
:+1:
You can think of SMART_PLAYLIST as a master and the old playlist table as a snapshot of SMART_PLAYLIST.
So this would need an additional column (dynamic?) in PLAYLIST to indicate that it is in fact dynamic?
Caching the results in the PLAYLIST table could be needed to improve performance, but I'm going to measure first.
Another thing I'm thinking about : I use a music collection management tool called Beets, which has a fairly nice query syntax for music management. How about reusing it, or implementing a subset?
Some ideas on the subject:
Add ability to use the new search query syntax from API (with a prefix, e.g. =)
We recommend that you start with a cautious approach to regular expressions, SQL, exact string matches and prefixes. Many music software features are based on specifications and implementations about 20 years ago, and backward compatibility cannot be removed. We need to find out if they are really good examples.
The ideas for regular expressions, SQL, exact string matches, and prefixes are based on a basic knowledge of databases. Airsonic uses a more sophisticated search engine, so I think it's a simpler solution.
https://github.com/jpsonic/jpsonic/releases/tag/v109.0.0
You can see an example of phrase search in my clone. Unlike previous methods, word order is considered, reducing the need for exact matches and prefixes.
Note that for large specifications like UPnP, there are only "contains" and "not contains" in the string condition. The reason why the guidelines did not recommend any other method was that they were logically less needed.
If you need to mimic some software, you can optionally add basic functions after creation. If you start by imitating some software, you will inherit the problems that the software has.
I imagine a flow like this for queries.
It is assumed that the following are required from the various demands currently issued.

Serializer and Deserializer can be created with ANTLR. The role of serializers and deserializers is simple. Same as serializer and deserializer that are often used to register dates and enums in the database with ORM.
Regarding this Query. I often see opinions equating to LuceneQuery, but that is not possible.
To be precise, it is possible only in limited circumstances. (For example, if we do not need a deserializer by design, and only use English.)
So you have to design the query as part of your application. Like most products that do things like this. Most of the smart lists are proprietary for each product. The smart list design mechanism is not very smart, because there is no such thing as a guideline.
If you want to avoid relying on the specific product specifications, UPnP's search query specifications are helpful. If you want a somewhat abstract and simple query. Most existing query specifications for existing products are completely dependent on the search impl spec, so it is important to be careful whether or not they are worth reference.
How you tie the query to the search functionality is a matter of future design. It could be converted to a SQL query or a LuceneQuery. We do not object to the recommendation of Beets, but it may add administrative overhead.
Serializer and Deserializer can be created with ANTLR.
[...] LuceneQuery, but that is not possible.
I think ANTLR is a good solution, re-using the Lucene query parser means we're effectively tied to it and its limitations.
I have a proof of concept up and running, so I know I can use it now.
UPnP's search query specifications are helpful.
Do you have a link to such a specification? I wasn't able to find it with a quick search.
I added a proposal for the query syntax to the top comment, let me know what you think.
Exact compatibility is explicitly _not_ a goal (we can change it if there is a better idea), but I was heavily inspired by Beets.
It should however be compatible with the basic "field" syntax used by Lucene, which is nice.
Note that for completeness I added boolean operators, but I think it's probably not needed.
For me the priority would be (in parentheses those that are probably not going to be in the first implementation):
Do you have a link to such a specification? I wasn't able to find it with a quick search.
There is a huge amount of data, but I think the following will help you with your search syntax.
I think it's much more convenient than referring to the external specifications of other products.
let me know what you think.
For now, I think it's a good idea to start with something simple. As you may have noticed, I take a rather conservative position. But that doesn't mean we are pessimistic about incorporating new ideas. I just think that evolution requires some steps.
A realistic idea is to distinguish between progressive goals and relay points. First, create what is practical with as few requirements as possible. It's a little more engineer's perspective than user's perspective. Expansion is easy, but the opposite is difficult. We may be careful not to carry a negative heritage.
mostly borrowed from [Beets]
I think the specification is redundant because it is written for explanation of Beets. It's a little simpler to create something similar. We should also avoid some of them.
relOp. relOp ::= ‘=’ | ‘!=’ | ‘<’ | ‘<=’ | ‘>’ | ‘>=’**Op should be defined if needed.(Eg relative date)**Op used for thembeets also supports regular expression matching for more advanced queries.(quoted from Beets)
The high-level and good features of regular expressions are for limited uses. Not everywhere gives great results ... I think we should redesign phrases and regular expressions.
As with UPnP, we recommend that string fields be one of the following:
stringOp ::= ‘contains’ | ‘doesNotContain’
I think that practical use can be secured by performing an approximate phrase search in the background. At least at first. (Verification should be separate. Just because UPnP didn't support extra **Op doesn't mean it wasn't well thought out.)
Regarding Boolean operators and nesting, I think that we can consider introducing it if we can express it well in GUI. (We don't enter the query directly... right?) And if analysis is possible with ANTLR.
Finally, I think the query length is finite (DB). We need to think about it a little.
It can support the following columns:
There is no opposition to the fact that there are many items to deal with. The main concern is the string search method. contains | doesNotContain can guarantee a certain degree of practicality and there is no technical lock-in.
Another thing I'm thinking about : I use a music collection management tool called Beets, which has a fairly nice query syntax for music management. How about reusing it, or implementing a subset?
I can understand my interest in a library that looks more useful. But search libraries are very complex. Update risk and bug risk are high. (This is the risk I'm afraid or have experienced.) The current DB + Lucene configuration is fairly typical and reliable in the long run. Even if not the best in a particular perspective.
I've confirmed issues of Beets, but the bug is primitive, such as a specific string crash. If you plan to use it directly, you should be prepared to encounter many defects that you have never encountered. I think that it is possible to some extent in the sense of referring to the query syntax.
By the way, if there is no limit, the development policy of phrase search with postgres is simple. Development is easy and performance is easy to maintain. Of course, that is not possible, so background options are limited. I think Beat is a little uncertain about its reliability. I'm not claiming that Lucene + DB is the best configuration!
There is already such a GUI :

Are you assuming an advanced version of such an input? If significantly simplified, the design will change. This seems to need confirmation first.