Skip to content

Update m3u files #168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class MediaImporter(
return
}

Timber.v("Starting import..")
Timber.v("Starting import...")
val time = System.currentTimeMillis()

isImporting = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -70,6 +71,8 @@ interface PlaylistRepository {
playlist: Playlist,
externalId: String?
)

suspend fun updateM3uFile(playlist: Playlist)
}

enum class PlaylistSortOrder : Serializable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class TaglibMediaProvider(
mediaProviderType = type,
name = m3uPlaylist.name,
songs = songs,
externalId = m3uPlaylist.name
externalId = m3uPlaylist.path
)
updateData
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -109,31 +110,40 @@ class LocalPlaylistRepository(
override suspend fun addToPlaylist(
playlist: Playlist,
songs: List<Song>
) = 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<PlaylistSong>
) = 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<Song>
) = 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<List<PlaylistSong>> = playlistSongJoinDao.getSongsForPlaylist(playlist.id)
.map { playlistSong ->
Expand All @@ -144,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,
Expand All @@ -159,6 +172,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
Expand Down Expand Up @@ -189,6 +228,7 @@ class LocalPlaylistRepository(
}
}
)
updateM3uFile(playlist)
}

override suspend fun updatePlaylistMediaProviderType(
Expand Down
Loading