Post Wed Apr 10, 2019 8:32 pm

Audio Playlist Issues, Samsung, Solution and Workaround

INTRO:
There are Playlist issues with Serviio and Samsung.
Nothing can be done here concerning Samsung issues (that Samsung
seems to want to display Playlist entries alphabetically rather
than in the order sent by Serviio).
The Serviio issues will be addressed here.
Also, a partial workaround I use for Serviio Playlists and Samsung TV
will be described.

SERVIIO ISSUES:
Serviio has the following tables related to Playlists
[
below is not sql
found in file:script-1.0.sql
]:

# playlist columns
TABLE PLAYLIST {
ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY
(START WITH 1, INCREMENT BY 1) CONSTRAINT playlist_pk PRIMARY KEY,
FILE_TYPES VARCHAR(20),
TITLE VARCHAR(128) NOT NULL,
FILE_PATH VARCHAR(4096) NOT NULL,
DATE_UPDATED TIMESTAMP,
REPOSITORY_ID INT NOT NULL CONSTRAINT pl_rep_foreign_key
REFERENCES REPOSITORY,
ALL_ITEMS_FOUND SMALLINT NOT NULL DEFAULT 0
}

# playlist_item columns
TABLE PLAYLIST_ITEM {
ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY
(START WITH 1, INCREMENT BY 1) CONSTRAINT playlist_item_pk PRIMARY KEY,
ITEM_ORDER INT NOT NULL DEFAULT 1,
PLAYLIST_ID INT NOT NULL CONSTRAINT pi_playlist_foreign_key
REFERENCES PLAYLIST,
MEDIA_ITEM_ID INT CONSTRAINT pi_item_foreign_key REFERENCES MEDIA_ITEM
}

# media_item columns
TABLE MEDIA_ITEM {
FILE_PATH VARCHAR(4096) NOT NULL DEFAULT '',
LC_FILE_PATH GENERATED ALWAYS AS (LOWER(FILE_PATH)),
ONLINE_IDENTIFIERS VARCHAR(256),
REPOSITORY_ID INT CONSTRAINT mi_repository_foreign_key REFERENCES REPOSITORY,
SAR VARCHAR(20),
SUBSAMPLING VARCHAR(15),
MKV_HEADER_COMPRESSION SMALLINT
}

UPDATE MEDIA_ITEM mi SET mi.REPOSITORY_ID =
(SELECT REPOSITORY_ID FROM FOLDER WHERE ID =mi.FOLDER_ID)
ALTER TABLE MEDIA_ITEM ALTER COLUMN REPOSITORY_ID NOT NULL
CREATE INDEX ON MEDIA_ITEM(FILE_PATH)
CREATE INDEX ON MEDIA_ITEM(LC_FILE_PATH)

Note the cardinality relationships are what one expect:
repositor 1-to-Many playlist
repositor 1-to-Many media_item
playlist 1-to-Many playlist_item
playlist_item Many-to-1 media_item


Java files of interest.

org/serviio/library/dao/PlaylistDAOImpl.java
db io
some methods:
long create(Playlist newInstance)
calls:
"INSERT INTO playlist (
file_types, # newInstance.getFileTypes as String
title, # newInstance.getTitle
file_path, # newInstance.getFilePath
date_updated, # newInstance.getDateUpdated
repository_id, # newInstance.getRepositoryId
all_items_found # newInstance.isAllItemsFound
) VALUES (?,?,?,?,?,?)", 1);

void delete(Long id)
Playlist read(Long id)
void update(Playlist transientObject)
boolean isPlaylistPresent(String playlistFilePath)
List<Playlist> getPlaylistsInFolder(String folderPath)
Playlist findPlaylistInLibrary(String playlistFilePath)
...
List<Integer> getPlaylistItemIndices(Long playlistId)


org/serviio/library/local/service/PlaylistService.java
access playlist DAO
some methods:
...
getAllPlaylists
addPlaylistItem
getPlaylistItemIndices
getListOfPlaylistsWithMedia
getNumberOfPlaylistsWithMedia
...
void addPlaylistToLibrary(ParsedPlaylist parsedPlaylist, Repository repository, String filePath)
calls: PlaylistParser.getInstance().parse(playlistFilePath)
calls: DAOFactory.getPlaylistDAO().create(new new Playlist(title, emptyHashSet<MediaFileType>, filePath, null, repid))
...

supported playlist types found in:
org/serviio/library/library/playlist/PlaylistType.java
axs, wax, wvx
m3u, m3u8
pls
wpl

org/serviio/library/library/playlist/PlaylistParser.java
some methods:
ParsedPlaylist parse(String playlistLocation)
byte[] playlistBytes = this.getPlaylistBytes(playlistLocation);
PlaylistParserStrategy strategy = PlaylistStrategyFactory.getStrategy(playlistBytes, playlistLocation);
return strategy.parsePlaylist(playlistBytes, playlistLocation);

There is a strategy for each playlist type: m3u, pls, etc.


org/serviio/library/library/playlist/M3UParserStrategy.java
NOTE:
ignores ALL lines starting with "#"
returns all other non-empty lines as absolute file paths
for anything else on a line an exception is thrown
REASON SERVIIO FAILS TO HANDLE m3u PLAYLISTS CORRECTLY

org/serviio/library/library/playlist/PlsParserStrategy.java
NOTE:
returns all lines start with "File" trimmed up to the "="
ignores all other lines including "Title*" lines
REASON SERVIIO FAILS TO HANDLE pls PLAYLISTS CORRECTLY

org/serviio/library/local/indexing/PlaylistFileIndexer.java
some methods:
void addNewPlaylistFile(Repository repository, File file)
calls: PlaylistService.addPlaylistToLibrary
void removePlaylist(Playlist playlist, File playlistFile)
void updatePlaylist(Playlist playlist, File playlistFile)

org/serviio/library/local/metadata/extractor/embedded/MP3ExtractionStrategy.java

some methods:
void extractMetadata(AudioMetadata metadata, AudioFile audioFile, AudioHeader header, Tag tag)

org/serviio/library.metadata/FFmpegMetadataRetriever.java
used if MP3ExtractionStrategy fails
some methods:
void retrieveAudioMetadata(AudioMetadata metadata, String filePath, DeliveryContext context)
get "title" tag from mp3 file.

You will see that:
1) Serviio throws away the user preference for what each media
file should be listed as, that is, the title appearing in the pls
files and the extended m3p comment title field.
2) Serviio does not use the user entered file name for each media
file as title.
3) Serviio response to Samsung playlist request with the audio media
metadata, the tags, not the user supplied title (or filename as title).
4) There is no column in the playlist_item table to include a title
alias which would allow for the storage of the user's preferred name.

The abstraction should have been to ask the PlaylistItem object for
its title value and, if it had a user preferred value, return that and,
otherwise, return its associate MediaItem's title value.
But, the current abstraction is to ask the PlaylistItem's MediaItem
for its title value.
The pls and m3u formats allowing for user defined track titles are
completely ignored because of the abstraction layers used.

SERVIIO SOLUTION:
So, to fix this:
1) Add a new nullable field to the PLAYLIST_ITEM table which would be
the user's desired name for the entry,
2) When parsing the pls files, keep the "Title" line entries,
3) When parsing the m3u files, keep the file names,
4) When parsing the extended m3u files, keep the title found in
the lines starting with "#EXTINF".
5) Someone else can figure out what to do about the axs, wax, wvx
and wpl files,
6) Store this user preferred name in the new column of the PLAYLIST_ITEM
table, and
7) When the TV request the playlist items, return the user preferred
name rather than the mp3 (flac, etc.) metadata tag "title" or "TIT2" entry.

SERVIIO EXTRA CREDIT:
It would be great for Serviio to support embedded playlists, that is,
allow m3u's to have not only media files but also other m3u files.
This would allow one to have a playlist for each album and then
playlist that could combine such per-album playlist.
This would make audio playlist very flexible and actually useful for
long playing playlists.
It would require adding a table such as:

# playlist_playlist columns
TABLE PLAYLIST_PLAYLIST {
ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY
(START WITH 1, INCREMENT BY 1) CONSTRAINT playlist_item_pk PRIMARY KEY,
ITEM_ORDER INT NOT NULL DEFAULT 1,
PARENT_PLAYLIST_ID INT NOT NULL CONSTRAINT pi_playlist_foreign_key
REFERENCES PLAYLIST
CHILD_PLAYLIST_ID INT NOT NULL CONSTRAINT pi_playlist_foreign_key
REFERENCES PLAYLIST
}

which would be a sibling of PLAYLIST_ITEM and whenever one wants a
playlist's "items" one would have to query both the PLAYLIST_ITEM
and PLAYLIST_PLAYLIST tables.
Also, one have to be careful about recursive/nesting of playlist files.

SERVIIO WORKAROUND:
I have tested this workaround with both m3u and pls files

I simply change the value for the "title" tag within the mp3 file.
As an example, using my ripped mp3s from Yes Fragile album:
'01 - Roundabout.mp3'
'02 - Cans And Brahms.mp3'
'03 - We Have Heaven.mp3'
'04 - South Side Of The Sky.mp3'
'05 - Five Per Cent For Nothing.mp3'
'06 - Long Distance Runaround.mp3'
'07 - The Fish.mp3'
'08 - Mood For A Day.mp3'
'09 - Heart Of The Sunrise.mp3'
'10 - America.mp3'

For each of the mp3, I create a new tag call "realtitle" into which
I store the mp3's current "title" tag value (if I might someday want to
restore the mp3 with its proper title), and then set the
"title" tag to the mp3's file name (minus the ".mp3" of course).
This way, when Samsung sorts the entries alphabetically, the show up
and play the way I would expect.

Yea, to do this by hand is crazy.

But, if you can write scripts you can use something like mid3v2 to
extra/set the "track" and "title" tags (and "disc" tag value for
multi-disc albums) for each mp3 file,
create the desired name: {disc-}?{track}-{title} (making sure to add
a '0' before each track < 10) and, finally,
setting the "title" and "realtitle" tags on the mp3.
You apply yours script recursively over the mp3 collection.

That said....
I switched from Kodi to Serviio about a year ago.
This playlist issue is the only problem I've encountered.
Serviio, basically, beats Kodi on all points.
Serviio works better, can handle larger list of items, has a
better storage hierarchy, the metadata storage is much more
natural and Web GUI is far more intuitive (the Kodi folks
seem to pride themselves for their awkwardness).