From cda71ed852f40c582dec6b9ce268903d10e1b83e Mon Sep 17 00:00:00 2001 From: daladim Date: Sat, 22 Apr 2023 22:46:27 +0200 Subject: [PATCH 1/6] [minor] typo --- .../main/java/com/simplecityapps/mediaprovider/MediaImporter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/MediaImporter.kt b/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/MediaImporter.kt index 13338cf06..d3810f9da 100644 --- a/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/MediaImporter.kt +++ b/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/MediaImporter.kt @@ -81,7 +81,7 @@ class MediaImporter( return } - Timber.v("Starting import..") + Timber.v("Starting import...") val time = System.currentTimeMillis() isImporting = true From 37372630aa28a0481514ae9ea658f6d767ca9bba Mon Sep 17 00:00:00 2001 From: daladim Date: Fri, 24 Feb 2023 17:43:54 +0100 Subject: [PATCH 2/6] m3u playlists use their path as `externalId` This will be useful when exporting update playlists to their original path --- .../local/provider/taglib/TaglibMediaProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/provider/taglib/TaglibMediaProvider.kt b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/provider/taglib/TaglibMediaProvider.kt index f4ea48e74..16bd87d8d 100644 --- a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/provider/taglib/TaglibMediaProvider.kt +++ b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/provider/taglib/TaglibMediaProvider.kt @@ -150,7 +150,7 @@ class TaglibMediaProvider( mediaProviderType = type, name = m3uPlaylist.name, songs = songs, - externalId = m3uPlaylist.name + externalId = m3uPlaylist.path ) updateData } else { From 69ceac8219686da21b3b1bd010b5d04c30580a81 Mon Sep 17 00:00:00 2001 From: daladim Date: Thu, 23 Feb 2023 22:09:33 +0100 Subject: [PATCH 3/6] Ability to update a playlist M3U file --- .../playlists/PlaylistRepository.kt | 3 +++ .../repository/LocalPlaylistRepository.kt | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt b/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt index eb30effb3..bbba47f8e 100644 --- a/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt +++ b/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt @@ -6,6 +6,7 @@ import com.simplecityapps.shuttle.model.PlaylistSong import com.simplecityapps.shuttle.model.SmartPlaylist import com.simplecityapps.shuttle.model.Song import com.simplecityapps.shuttle.sorting.PlaylistSongSortOrder +import java.io.OutputStream import java.io.Serializable import kotlinx.coroutines.flow.Flow @@ -70,6 +71,8 @@ interface PlaylistRepository { playlist: Playlist, externalId: String? ) + + suspend fun updateM3uFile(playlist: Playlist) } enum class PlaylistSortOrder : Serializable { diff --git a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt index ca848e738..fd945a462 100644 --- a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt +++ b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt @@ -1,6 +1,7 @@ package com.simplecityapps.localmediaprovider.local.repository import android.content.Context +import android.net.Uri import com.simplecityapps.localmediaprovider.local.data.room.dao.PlaylistDataDao import com.simplecityapps.localmediaprovider.local.data.room.dao.PlaylistSongJoinDao import com.simplecityapps.localmediaprovider.local.data.room.entity.PlaylistData @@ -159,6 +160,32 @@ class LocalPlaylistRepository( ) ) + override suspend fun updateM3uFile(playlist: Playlist) { + val outputStream = playlist.externalId?.let { path -> + context.contentResolver.openOutputStream(Uri.parse(playlist.externalId), "wt") + } + + if (outputStream == null) { + Timber.w("Unable to open M3U file at ${playlist.externalId} for playlist ${playlist.name}") + } else { + val playlistPath = Uri.decode(playlist.externalId?: "") + val playlistFolder = playlistPath.substringBeforeLast("/") + "/" + + getSongsForPlaylist(playlist) + .firstOrNull() + .orEmpty() + .forEach { plSong -> + // Quick-and-dirty way to relativize the song path to the m3u folder + // Note that paths can be content:// URIs, for which there is no proper .relativize() method + // We'll use absolute values (paths or URIs, whatever is in database) for files that are not stored in a sub-folder relative to the M3U file + val songPath = Uri.decode(plSong.song.path) + val relative = songPath.substringAfter(playlistFolder) + val line = relative.toByteArray() + /* CRLF */ 0x0d.toByte() + 0x0A.toByte() + outputStream.write(line) + } + } + } + override suspend fun updatePlaylistSortOder( playlist: Playlist, sortOrder: PlaylistSongSortOrder From 00dd108cf3065b6631ab2fd61f8904ed5bfdbb70 Mon Sep 17 00:00:00 2001 From: daladim Date: Mon, 27 Feb 2023 16:17:28 +0100 Subject: [PATCH 4/6] Automatically update M3U files --- .../repository/LocalPlaylistRepository.kt | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt index fd945a462..65656f1be 100644 --- a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt +++ b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt @@ -110,31 +110,40 @@ class LocalPlaylistRepository( override suspend fun addToPlaylist( playlist: Playlist, songs: List - ) = playlistSongJoinDao.insert( - songs.mapIndexed { i, song -> - PlaylistSongJoin( - playlistId = playlist.id, - songId = song.id, - sortOrder = (playlist.songCount + i).toLong() - ) - } - ) + ) { + playlistSongJoinDao.insert( + songs.mapIndexed { i, song -> + PlaylistSongJoin( + playlistId = playlist.id, + songId = song.id, + sortOrder = (playlist.songCount + i).toLong() + ) + } + ) + updateM3uFile(playlist) + } override suspend fun removeFromPlaylist( playlist: Playlist, playlistSongs: List - ) = playlistSongJoinDao.delete( - playlistId = playlist.id, - playlistSongIds = playlistSongs.map { playlistSong -> playlistSong.id }.toTypedArray() - ) + ) { + playlistSongJoinDao.delete( + playlistId = playlist.id, + playlistSongIds = playlistSongs.map { playlistSong -> playlistSong.id }.toTypedArray() + ) + updateM3uFile(playlist) + } override suspend fun removeSongsFromPlaylist( playlist: Playlist, songs: List - ) = playlistSongJoinDao.deleteSongs( - playlistId = playlist.id, - songIds = songs.map { it.id }.toTypedArray() - ) + ) { + playlistSongJoinDao.deleteSongs( + playlistId = playlist.id, + songIds = songs.map { it.id }.toTypedArray() + ) + updateM3uFile(playlist) + } override fun getSongsForPlaylist(playlist: Playlist): Flow> = playlistSongJoinDao.getSongsForPlaylist(playlist.id) .map { playlistSong -> @@ -145,7 +154,10 @@ class LocalPlaylistRepository( override suspend fun deleteAll(mediaProviderType: MediaProviderType) = playlistDataDao.deleteAll(mediaProviderType) - override suspend fun clearPlaylist(playlist: Playlist) = playlistDataDao.clear(playlist.id) + override suspend fun clearPlaylist(playlist: Playlist) { + playlistDataDao.clear(playlist.id) + updateM3uFile(playlist) + } override suspend fun renamePlaylist( playlist: Playlist, @@ -216,6 +228,7 @@ class LocalPlaylistRepository( } } ) + updateM3uFile(playlist) } override suspend fun updatePlaylistMediaProviderType( From 7136fc8ea9d339605a315fb40535b9d4846329ff Mon Sep 17 00:00:00 2001 From: daladim Date: Thu, 5 Jun 2025 22:51:36 +0200 Subject: [PATCH 5/6] [lint] --- .../mediaprovider/repository/playlists/PlaylistRepository.kt | 1 - .../local/repository/LocalPlaylistRepository.kt | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt b/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt index bbba47f8e..787445a42 100644 --- a/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt +++ b/android/mediaprovider/core/src/main/java/com/simplecityapps/mediaprovider/repository/playlists/PlaylistRepository.kt @@ -6,7 +6,6 @@ import com.simplecityapps.shuttle.model.PlaylistSong import com.simplecityapps.shuttle.model.SmartPlaylist import com.simplecityapps.shuttle.model.Song import com.simplecityapps.shuttle.sorting.PlaylistSongSortOrder -import java.io.OutputStream import java.io.Serializable import kotlinx.coroutines.flow.Flow diff --git a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt index 65656f1be..450928406 100644 --- a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt +++ b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt @@ -180,7 +180,7 @@ class LocalPlaylistRepository( if (outputStream == null) { Timber.w("Unable to open M3U file at ${playlist.externalId} for playlist ${playlist.name}") } else { - val playlistPath = Uri.decode(playlist.externalId?: "") + val playlistPath = Uri.decode(playlist.externalId ?: "") val playlistFolder = playlistPath.substringBeforeLast("/") + "/" getSongsForPlaylist(playlist) @@ -192,7 +192,8 @@ class LocalPlaylistRepository( // We'll use absolute values (paths or URIs, whatever is in database) for files that are not stored in a sub-folder relative to the M3U file val songPath = Uri.decode(plSong.song.path) val relative = songPath.substringAfter(playlistFolder) - val line = relative.toByteArray() + /* CRLF */ 0x0d.toByte() + 0x0A.toByte() + val crlf = 0x0d.toByte() + 0x0A.toByte() + val line = relative.toByteArray() + crlf outputStream.write(line) } } From a8584f491010291238bd5bbd6be7c6768b91a409 Mon Sep 17 00:00:00 2001 From: daladim Date: Thu, 5 Jun 2025 23:14:02 +0200 Subject: [PATCH 6/6] Working around Room not able to infer the schema type --- .../local/repository/LocalPlaylistRepository.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt index 450928406..1e6d8cf2f 100644 --- a/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt +++ b/android/mediaprovider/local/src/main/java/com/simplecityapps/localmediaprovider/local/repository/LocalPlaylistRepository.kt @@ -191,10 +191,9 @@ class LocalPlaylistRepository( // Note that paths can be content:// URIs, for which there is no proper .relativize() method // We'll use absolute values (paths or URIs, whatever is in database) for files that are not stored in a sub-folder relative to the M3U file val songPath = Uri.decode(plSong.song.path) - val relative = songPath.substringAfter(playlistFolder) - val crlf = 0x0d.toByte() + 0x0A.toByte() - val line = relative.toByteArray() + crlf - outputStream.write(line) + val crlf = "\r\n" + val relative = songPath.substringAfter(playlistFolder) + crlf + outputStream.write(relative.toByteArray()) } } }