Skip to content
This repository was archived by the owner on Sep 22, 2024. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
ed4cafa
Revamped the Playlist and PlaylistTab to work on latest master
defvs Jun 21, 2019
3495225
Added Shuffle and Repeat buttons
defvs Jun 21, 2019
b62cdb7
Skip song as a failsafe if it isn't found
defvs Jun 21, 2019
83ec9ef
Added fork as library for shuffle and repeat icons
defvs Jun 21, 2019
9a24a88
Fix shuffle and repeat not keeping their values after Track changed
defvs Jun 21, 2019
25c7351
Fix pressing next while track is loading returns to first track
defvs Jun 21, 2019
2a4b91f
Fix player not resetting when playlist is cleared
defvs Jun 21, 2019
be22b80
Added keyboard actions to catalog tab.
defvs Jun 21, 2019
e7fc26e
Added keyboard actions to playlist tab
defvs Jun 21, 2019
f76deb5
Added tooltips to player buttons
defvs Jun 22, 2019
3b966ff
Show currently played song in the playlist
defvs Jun 26, 2019
89f294d
Disallow duplicates in playlist to fix currentIndex
defvs Jun 26, 2019
0962c60
Optimize imports for Playlist Tab
defvs Jun 26, 2019
82a9c91
Swapped for-loop for a forEach
defvs Jun 28, 2019
65bf11f
Added a separator and grouped menuItems for TabCatalog
defvs Jun 28, 2019
4a436cc
Added Context Menu for TabDownloader (SongView)
defvs Jun 28, 2019
35b7292
Fix issues with TabPlaylist table, showing weird colors after a scroll
defvs Jun 28, 2019
831d4aa
Change loglevel for context menu in TabCatalog
defvs Jun 28, 2019
4a9aa68
Fix keyboard controls only working for 1 song in TabCatalog
defvs Jun 28, 2019
bb7908f
fix(playlist): Next random Track should not be the same one.
defvs Jul 1, 2019
f3d4c7d
feat(playlist): Added buttons to load/save playlist to Connect
defvs Jul 1, 2019
d48ba9f
feat(playlist-api): Base of ConnectPlaylist
defvs Jul 1, 2019
f6f798a
feat(playlist-api): Playlist loading from account or URL
defvs Jul 2, 2019
69972f7
style(player): Reformat button constructor
defvs Jul 2, 2019
0e7760e
refactor(player): Simplify playTracks code
defvs Jul 2, 2019
c64be58
refactor(downloader): Cleanup menu items of SongView
defvs Jul 2, 2019
dd82e72
feat(playlist): Add helper function to add a list
defvs Jul 2, 2019
602ac97
refactor: Global MenuItem refactoring in Catalog and Playlist tabs
defvs Jul 2, 2019
a7af63f
Merge branch 'master' into playlist
defvs Jul 2, 2019
2f5a7e8
refactor(player): Rename error function when track cannot be loaded
defvs Jul 2, 2019
e2694bf
style(player): Reformat buttons constructors
defvs Jul 2, 2019
978b796
feat(player): Update Util to latest version with icons
defvs Jul 2, 2019
27ab189
Merge remote-tracking branch 'origin/master' into playlist
defvs Jul 3, 2019
7660b50
style(player): Better styling for player controls constructors
defvs Jul 3, 2019
75bbed0
Merge branch 'playlist' into playlist-api
defvs Jul 6, 2019
5fa32ad
fix(playlist-api): Merge conflict with branch playlist
defvs Jul 6, 2019
59473e2
fix(playlist-api): Use tracks from Cache instead of fresh ones
defvs Jul 7, 2019
890afb0
refactor(playlist-api)
defvs Jul 8, 2019
d64b119
Merge branch 'master' into playlist-api
defvs Jul 12, 2019
7798353
feat(api-connection): PUT and POST Http requests
defvs Jul 12, 2019
e3a9e44
feat(api-connection): Playlist POST (create new) and PUT (update)
defvs Jul 12, 2019
adcf49c
feat(playlist-api): Save playlist - Create new one or update existing
defvs Jul 12, 2019
03e2f42
refactor(api-connection): Remove useless logging
defvs Jul 12, 2019
363824a
refactor(playlist)
defvs Jul 21, 2019
2e73539
style(downloader-tab): remove contextMenu indentation
defvs Jul 21, 2019
f5bb7f6
refactor(downloader-tab): remove useless coroutine
defvs Jul 21, 2019
42e81f9
refactor(catalog-tab): remove mouse even clickcount for middle click
defvs Jul 21, 2019
ed46837
refactor(catalog-tab): move repetitive code to an inline function
defvs Jul 21, 2019
844c9bd
refactor(playlist-tab)
defvs Jul 21, 2019
327db23
refactor(playlist-tab): inline ContextMenu
defvs Jul 21, 2019
f325450
refactor(playlist-tab): use custom getter for selectedTrack
defvs Jul 21, 2019
c3065c8
Merge branch 'master' into playlist
defvs Jul 21, 2019
76bcbd1
Merge branch 'playlist' into playlist-api
defvs Jul 21, 2019
348cb5d
fix(playlist-tab): remove and replace problematic code after merge
defvs Jul 21, 2019
dddd1a8
refactor(api-connection): use raw string for JSON
defvs Jul 22, 2019
8271ea0
feat(playlist-api): merge all playlist api functions into one
defvs Jul 23, 2019
c279baa
fix(playlist-api): fix null-pointer when opening ContextMenu on no item
defvs Jul 23, 2019
71761be
refactor(playlist-api): use lambda to run code after helper functions
defvs Jul 23, 2019
9baad4d
fix(playlist-api): use playlist stage as parent for sub-stage
defvs Jul 23, 2019
50c233c
refactor(playlist-api): moved playlist manager window to Playlist.kt
defvs Jul 23, 2019
5a13243
refactor(playlist-api): move playlist functions to companion object
defvs Jul 25, 2019
138d828
refactor(playlist): use apply for track addition
defvs Jul 25, 2019
9a6333e
style(player): use tooltip helper instead of apply
defvs Jul 25, 2019
a3d1881
refactor(playlist-tab): use helper function for table columns
defvs Jul 25, 2019
165cdf2
refactor(playlist-tab): move some parts of table into init
defvs Jul 25, 2019
ed62aa3
style(playlist): remove apply used for two lines
defvs Jul 26, 2019
3794cff
refactor(player): move apply and use addAll for player buttons
defvs Jul 26, 2019
f8eea82
refactor(playlist): use different null checks and more compact code
defvs Jul 26, 2019
ec3ac2d
refactor(playlist): use bidirectional binds for shuffle and random
defvs Jul 26, 2019
c2e4bae
refactor(playlist): change null check
defvs Jul 26, 2019
c5a8533
refactor(playlist): make addAll more efficient and less bulky
defvs Jul 26, 2019
13492d6
refactor(playlist-api): remove useless (?) trimming for playlist object
defvs Jul 29, 2019
820900b
refactor(playlist-api): use helper function TableColumn
defvs Jul 29, 2019
5936a79
fix(playlist-api): correct grammar
defvs Jul 29, 2019
587ecea
style(playlist-api): group playlist manager init code
defvs Jul 29, 2019
0f6dd4f
refactor(playlist-api): use launch instead of async Coroutines
defvs Jul 29, 2019
335b23e
refactor(playlist-api): add logging for cache matching errors
defvs Jul 29, 2019
508576e
refactor(playlist): change next track getter functions names
defvs Jul 30, 2019
7841047
feat(player): disable next and previous buttons when not applicable
defvs Jul 30, 2019
5ef0ab5
Merge branch 'playlist' into playlist-api
defvs Jul 31, 2019
589db1a
fix(playlist-api): close dialog only after playlist is fully loaded
defvs Jul 31, 2019
cd56c64
refactor(playlist-api): use Job to invoke code on substage completion
defvs Aug 1, 2019
031033a
feat(playlist-api): disable buttons, show "Loading" when waiting for API
defvs Aug 1, 2019
d84109a
refactor: use when for Mouse and Keyboard events
defvs Aug 2, 2019
64116fc
style(playlist-tab): indent MenuItem properly
defvs Aug 2, 2019
0e108ae
refactor(player): make skip button listeners smaller
defvs Aug 2, 2019
3be28d6
fix(playlist): add this to fix addAll not working
defvs Aug 4, 2019
42f4237
fix(player): remove duplicate updateCover
defvs Aug 4, 2019
5382a92
style(playlist): reformat code
defvs Aug 4, 2019
5b6ae0e
refactor(player): make skip buttons listeners even better
defvs Aug 5, 2019
a5ee3b8
refactor(playlist): use lastIndex instead of (size - 1)
defvs Aug 6, 2019
b2ac140
refactor(playlist): replace recursive next song random function
defvs Aug 6, 2019
a6e0ae4
refactor(player): remove disabled property on prev / next buttons
defvs Aug 6, 2019
b575ca9
style: reformat code
defvs Aug 6, 2019
f6cbf07
refactor(playlist): move addAll if into function call
defvs Aug 6, 2019
b1979f3
style: reformat code
defvs Aug 6, 2019
c081308
Merge branch 'playlist' into playlist-api
defvs Aug 6, 2019
fa69e2d
fix(playlist): fix addAll returning wrong index is tracklist is empty
defvs Aug 6, 2019
d98c1f7
feat(playlist-tab): allow drag and drop to manually sort the playlist
defvs Aug 6, 2019
c3ab35b
refactor(playlist): remove useless lambda argument
defvs Aug 8, 2019
5312488
refactor(playlist): replace int addition inside of let with add
defvs Aug 8, 2019
ab8c72f
refactor(playlist): simplify next random function
defvs Aug 8, 2019
82b9279
refactor(main): move Playlist tab
defvs Aug 8, 2019
8a998b9
fix(api-connection): fix downloader due to forced content type headers
defvs Aug 9, 2019
44ed6a4
refactor(playlist): filter out random early if only 1 track in playlist
defvs Aug 14, 2019
eb9ae6a
refactor(catalog-tab): use let when matching while adding to playlist
defvs Aug 14, 2019
5899a08
Merge branch 'master' into playlist
defvs Aug 15, 2019
04c8db7
fix(playlist-api): change playlist endpoint and force content type
defvs Sep 8, 2019
33d2793
Merge branch 'master' into playlist
defvs Sep 17, 2019
dd090e1
Merge branch 'playlist' into playlist-api
defvs Sep 17, 2019
3d6d88e
Merge branch 'master' into playlist-api
defvs Oct 4, 2019
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
40 changes: 36 additions & 4 deletions src/main/xerus/monstercat/api/APIConnection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.apache.http.client.config.CookieSpecs
import org.apache.http.client.config.RequestConfig
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPatch
import org.apache.http.client.methods.HttpPost
import org.apache.http.client.methods.HttpUriRequest
import org.apache.http.client.protocol.HttpClientContext
Expand All @@ -26,10 +27,7 @@ import xerus.ktutil.javafx.properties.SimpleObservable
import xerus.ktutil.javafx.properties.listen
import xerus.monstercat.Settings
import xerus.monstercat.Sheets
import xerus.monstercat.api.response.ReleaseResponse
import xerus.monstercat.api.response.Session
import xerus.monstercat.api.response.TrackResponse
import xerus.monstercat.api.response.declaredKeys
import xerus.monstercat.api.response.*
import xerus.monstercat.downloader.CONNECTSID
import xerus.monstercat.downloader.QUALITY
import java.io.IOException
Expand Down Expand Up @@ -80,6 +78,9 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
fun getTracks() =
parseJSON(TrackResponse::class.java)?.results

fun getPlaylists() =
parseJSON(PlaylistResponse::class.java)?.results

private var httpRequest: HttpUriRequest? = null
/** Aborts this connection and thus terminates the InputStream if active */
fun abort() {
Expand Down Expand Up @@ -234,6 +235,37 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
CONNECTSID.clear()
}

private fun convertTracklist(tracks: List<Track>) = tracks.map { HashMap<String, String>().apply { this["trackId"] = it.id; this["releaseId"] = it.release.id } }

fun editPlaylist(id: String, tracks: List<Track>? = null, name: String? = null, public: Boolean? = null, deleted: Boolean? = null) {
val json = HashMap<String, Any>()
tracks?.also { json["tracks"] = convertTracklist(it) }
name?.also { json["name"] = it }
public?.also { json["public"] = it }
deleted?.also { json["deleted"] = it }
val connection = APIConnection("v2", "playlist", id)
val request = HttpPatch(connection.uri).apply {
setHeader("Content-Type", "application/json")
val content = Sheets.JSON_FACTORY.toString(json)
entity = StringEntity(content)
}
connection.execute(request)
}

fun createPlaylist(name: String, tracks: List<Track>, public: Boolean = false) {
val connection = APIConnection("v2", "self", "playlist")
val request = HttpPost(connection.uri).apply {
setHeader("Content-Type", "application/json")
val json = HashMap<String, Any>()
json["name"] = name
json["public"] = public
json["tracks"] = convertTracklist(tracks)
val content = Sheets.JSON_FACTORY.toString(json)
entity = StringEntity(content)
}
connection.execute(request)
}

data class ConnectResult(val connectsid: String, val validity: ConnectValidity, val session: Session?)
}

Expand Down
233 changes: 224 additions & 9 deletions src/main/xerus/monstercat/api/Playlist.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,32 @@ package xerus.monstercat.api
import javafx.beans.property.SimpleBooleanProperty
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.scene.control.*
import javafx.scene.input.MouseButton
import javafx.scene.layout.VBox
import javafx.scene.media.MediaPlayer
import javafx.stage.Modality
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import mu.KotlinLogging
import xerus.ktutil.ifNull
import xerus.ktutil.javafx.*
import xerus.ktutil.javafx.properties.SimpleObservable
import xerus.ktutil.javafx.properties.bindSoft
import xerus.ktutil.javafx.ui.App
import xerus.monstercat.api.response.ConnectPlaylist
import xerus.monstercat.api.response.Track
import xerus.monstercat.monsterUtilities
import java.util.*
import kotlin.random.Random
import kotlin.random.nextInt

private val logger = KotlinLogging.logger {}

object Playlist {
val logger = KotlinLogging.logger { }

val tracks: ObservableList<Track> = FXCollections.observableArrayList()
val history = ArrayDeque<Track>()
val currentIndex = SimpleObservable<Int?>(null).apply {
Expand Down Expand Up @@ -52,6 +67,8 @@ object Playlist {
tracks.add(track)
}

fun addAll(tracks: ArrayList<Track>) = this.tracks.addAll(tracks)

fun removeAt(index: Int?) {
tracks.removeAt(index ?: tracks.size - 1)
}
Expand All @@ -67,13 +84,9 @@ object Playlist {
}

fun getNextTrackRandom(): Track {
return if(tracks.size <= 1) {
tracks[0]
} else {
var index = Random.nextInt(0..tracks.lastIndex)
if(index >= currentIndex.value!!) index++
tracks[index]
}
return if (tracks.size <= 1) tracks[0]
else tracks[
Random.nextInt(0..tracks.lastIndex).let { if(it >= currentIndex.value!!) it + 1 else it } ]
}

fun getNextTrack(): Track? {
Expand All @@ -91,3 +104,205 @@ object Playlist {
this.tracks.addAll(if(asNext) currentIndex.value?.plus(1) ?: 0 else this.tracks.lastIndex.coerceAtLeast(0), tracks)
}
}

object PlaylistManager {
suspend fun loadPlaylist(apiConnection: APIConnection) {
val tracks = apiConnection.getTracks()
tracks?.forEachIndexed { index, track ->
val found = Cache.getTracks().find { it.id == track.id }
if(found != null)
tracks[index] = found
else {
tracks.removeAt(index)
logger.error("Skipped track ${track.artistsTitle} - ${track.title} with id ${track.id}: Not found in the cache.")
}
}
if(tracks != null && tracks.isNotEmpty()) {
if(Player.player?.status != MediaPlayer.Status.DISPOSED)
Player.reset()
Playlist.setTracks(tracks)
}
}

/** Opens the playlist manager dialog
* Allows to load, save, and manage playlists stored on Monstercat.com
*/
fun playlistDialog() {
val connection = APIConnection("api", "playlist").fields(ConnectPlaylist::class)

val parent = VBox()
val stage = App.stage.createStage("Monstercat.com Playlists", parent)
stage.initModality(Modality.WINDOW_MODAL)
val connectTable = TableView<ConnectPlaylist>()

// Common playlist functions
fun load() = GlobalScope.launch { loadPlaylist(APIConnection("api", "playlist", connectTable.selectionModel.selectedItem.id, "tracks")) }

fun loadUrl(): Job {
val subParent = VBox()
val subStage = stage.createStage("Load from URL", subParent)
subStage.initModality(Modality.WINDOW_MODAL)
val textField = TextField().apply { promptText = "URL" }
subParent.add(textField)

var playlistId: String? = null
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
try {
loadPlaylist(APIConnection("api", "playlist", playlistId!!, "tracks"))
onFx { subStage.close() }
} catch(e: Exception) { // FIXME : This breaks everything; if we get in the catch, the error message will show, but job.invokeOnComplete will still work and everything will be closed.
onFx {
monsterUtilities.showAlert(Alert.AlertType.WARNING, "No playlist found", content = "No playlists were found at ${textField.text}.")
}
this.cancel()
}
}

subParent.addRow(createButton("Load") {
playlistId = textField.text.substringAfterLast("/")
if(playlistId!!.length == 24) {
job.start()
(it.source as Button).let { button ->
button.isDisable = true
button.text = "Loading..."
}
} else {
monsterUtilities.showAlert(Alert.AlertType.WARNING, "Playlist URL invalid", content = "${textField.text} is not a valid URL.")
}
}, createButton("Cancel") {
subStage.close()
job.cancel()
})
subStage.show()
return job
}

fun replace() = GlobalScope.launch { APIConnection.editPlaylist(connectTable.selectionModel.selectedItem.id, tracks = Playlist.tracks) }
fun delete() = GlobalScope.launch { APIConnection.editPlaylist(connectTable.selectionModel.selectedItem.id, deleted = true) }
fun new(): Job {
val subParent = VBox()
val subStage = stage.createStage("New Playlist", subParent)
subStage.initModality(Modality.WINDOW_MODAL)
val textField = TextField().apply { promptText = "Name" }
val publicTick = CheckBox("Public")
subParent.children.addAll(textField, publicTick)

val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
APIConnection.createPlaylist(textField.text.let { if(it.isBlank()) "New Playlist" else it }, Playlist.tracks, publicTick.isSelected)
onFx { subStage.close() }
}

subParent.addRow(createButton("Create") {
job.start()
(it.source as Button).let { button ->
button.isDisable = true
button.text = "Loading..."
}
}, createButton("Cancel") {
subStage.close()
job.cancel()
})
subStage.show()
return job
}

fun rename(): Job {
val subParent = VBox()
val subStage = stage.createStage("Rename Playlist", subParent)
subStage.initModality(Modality.WINDOW_MODAL)
val textField = TextField().apply { promptText = "Name" }
subParent.add(textField)

val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
APIConnection.editPlaylist(connectTable.selectionModel.selectedItem.id, name = textField.text.let { if(it.isBlank()) "Unnamed" else it })
onFx { subStage.close() }
}

subParent.addRow(createButton("Rename") {
job.start()
(it.source as Button).let { button ->
button.isDisable = true
button.text = "Loading..."
}
}, createButton("Cancel") {
subStage.close()
job.cancel()
})
subStage.show()
return job
}

val playlists = FXCollections.observableArrayList<ConnectPlaylist>()
fun updatePlaylists() {
playlists.clear()
if(APIConnection.connectValidity.value != ConnectValidity.NOGOLD && APIConnection.connectValidity.value != ConnectValidity.GOLD) {
connectTable.placeholder = Label("Please connect using connect.sid in the downloader tab.")
} else {
connectTable.placeholder = Label("Loading...")
GlobalScope.launch {
val results = connection.getPlaylists()
if(results != null && results.isNotEmpty())
playlists.addAll(results)
else
onFx {
connectTable.placeholder = Label("No playlists were found on your account.")
}
}
}
}

connectTable.apply {
columnResizePolicy = TableView.CONSTRAINED_RESIZE_POLICY
columns.addAll(TableColumn<ConnectPlaylist, String>("Name") { it.value.name },
TableColumn<ConnectPlaylist, String>("Size") { it.value.tracks.size.toString() })
items = playlists
updatePlaylists()

selectionModel.selectionMode = SelectionMode.SINGLE
setOnMouseClicked { if(it.button == MouseButton.PRIMARY && it.clickCount == 2) load() }

val publicMenuItem = CheckMenuItem("Public", {
if(connectTable.selectionModel.selectedItem != null) {
GlobalScope.launch {
APIConnection.editPlaylist(connectTable.selectionModel.selectedItem.id, public = it)
onFx { updatePlaylists() }
}
}
})
contextMenu = ContextMenu(
publicMenuItem,
SeparatorMenuItem(),
MenuItem("Save into") { replace().invokeOnCompletion { onFx { updatePlaylists() } } },
MenuItem("Rename playlist") { rename().invokeOnCompletion { it.ifNull { onFx { updatePlaylists() } } } },
MenuItem("Delete playlist") { delete().invokeOnCompletion { onFx { updatePlaylists() } } })
contextMenu.setOnShown {
contextMenu.items.forEach { it.isDisable = connectTable.selectionModel.selectedItem == null }
publicMenuItem.isSelected = selectionModel.selectedItem?.public ?: false
}
}

parent.add(Label("Tip : You can right-click a playlist to edit it without the window closing each time !"))
parent.addRow(
createButton("Load") {
connectTable.selectionModel.selectedItem ?: return@createButton
load().invokeOnCompletion { onFx { stage.close() } }
(it.source as Button).let { button ->
button.parent.isDisable = true
button.text = "Loading..."
}
},
createButton("From URL...") { loadUrl().invokeOnCompletion { it.ifNull { onFx { stage.close() } } } },
createButton("Save into selected") {
connectTable.selectionModel.selectedItem ?: return@createButton
replace().invokeOnCompletion { onFx { stage.close() } }
(it.source as Button).let { button ->
button.parent.isDisable = true
button.text = "Loading..."
}
},
createButton("Save as new...") { new().invokeOnCompletion { it.ifNull { onFx { stage.close() } } } },
createButton("Cancel") { stage.close() })
parent.fill(connectTable, 0)
stage.show()
}
}
12 changes: 12 additions & 0 deletions src/main/xerus/monstercat/api/response/ConnectPlaylist.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package xerus.monstercat.api.response

import com.google.api.client.util.Key

data class ConnectPlaylist(
@Key("_id") var id: String = "",
@Key var name: String = "",
@Key var public: Boolean = false,
@Key var deleted: Boolean = false,

@Key var tracks: List<Track> = arrayListOf()
)
5 changes: 3 additions & 2 deletions src/main/xerus/monstercat/api/response/ListResponse.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ open class ListResponse<T> {
override fun toString() = "${this.javaClass.simpleName}($total elements): $results"
}

class ReleaseResponse: ListResponse<Release>()
class TrackResponse: ListResponse<Track>()
class ReleaseResponse : ListResponse<Release>()
class TrackResponse : ListResponse<Track>()
class PlaylistResponse : ListResponse<ConnectPlaylist>()

class ReleaseList: ArrayList<Release>()
5 changes: 4 additions & 1 deletion src/main/xerus/monstercat/tabs/TabPlaylist.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import javafx.scene.input.MouseButton
import javafx.scene.input.TransferMode
import xerus.ktutil.javafx.MenuItem
import xerus.ktutil.javafx.TableColumn
import xerus.ktutil.javafx.addButton
import xerus.ktutil.javafx.fill
import xerus.ktutil.javafx.properties.listen
import xerus.monstercat.api.Player
import xerus.monstercat.api.Playlist
import xerus.monstercat.api.PlaylistManager
import xerus.monstercat.api.response.Track


Expand Down Expand Up @@ -104,9 +106,10 @@ class TabPlaylist: VTab() {

init {
table.items = Playlist.tracks
addButton("Playlists from Monstercat.com..."){ PlaylistManager.playlistDialog() }
fill(table)
}

private val selectedTrack: Track
get() = table.selectionModel.selectedItem
private val selectedIndex: Int
Expand Down