Skip to content

Commit

Permalink
Working on ability to import playlists to shows.
Browse files Browse the repository at this point in the history
  • Loading branch information
brunchboy committed May 15, 2024
1 parent b98b8f0 commit 74efb6c
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 16 deletions.
31 changes: 24 additions & 7 deletions src/beat_link_trigger/show.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2081,22 +2081,39 @@
the show map."
[show]
(seesaw/action :handler (fn [_]
(loop [show (latest-show show)]
(loop [show (latest-show show)
playlist? false]
(let [^Database database (:import-database show)
result (loader/choose-local-track (:frame show) database
"Change Media")]
(if (string? result) ; User wants to change media
result (if playlist?
(loader/choose-local-playlist (:frame show) database
"Change Media" "Import Track")
(loader/choose-local-track (:frame show) database
"Change Media" "Import Playlist"))]
(cond
(= "Change Media" result) ; User wants to change media.
(do
(try
(.close database)
(catch Throwable t
(timbre/error t "Problem closing offline media database.")))
(swap-show! show dissoc :import-database)
(recur (latest-show show)))
(when-let [[database track-row] result]
(recur (latest-show show) playlist?))

(= "Import Track" result) ; User wants to switch to importing a single track.
(recur show false)

(= "Import Playlist" result) ; User wants to switch to importing a playlist.
(recur show true)

:else
(when-let [[database chosen] result]
(swap-show! show assoc :import-database database)
(try
(import-from-media (latest-show show) database track-row)
(if (instance? RekordboxPdb$TrackRow chosen)
(import-from-media (latest-show show) database chosen)
(doseq [id chosen]
(when-let [track (.. database trackIndex (get (long id)))]
(import-from-media (latest-show show) database track))))
(catch Throwable t
(timbre/error t "Problem importing from offline media.")
(seesaw/alert (:frame show) (str "<html>Unable to Import.<br><br>" t)
Expand Down
158 changes: 149 additions & 9 deletions src/beat_link_trigger/track_loader.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2321,9 +2321,9 @@
[^File pdb-file]
(.. pdb-file getParentFile getParentFile getParentFile getAbsolutePath))

(defn offline-file-node
(defn- offline-file-track-node
"Creates the root node for working with an offline rekordbox exported
database file."
database file and choosing tracks."
[^Database database]
(let [node (DefaultMutableTreeNode.
(proxy [Object IMenuEntry] []
Expand All @@ -2335,7 +2335,56 @@
(add-file-node-children database node nil)
node))

(defn- create-chooser-dialog
(defn offline-playlist-only-node
"Creates a node that represents a playlist available in an exported
rekordbox database file when the only thing that can be loaded is
playlists themselves."
[^Database _database id playlist-name]
(DefaultMutableTreeNode.
(proxy [Object IMenuEntry] []
(toString [] playlist-name)
(getId [] (int id))
(getSlot [] nil)
(getTrackType [] nil)
(loadChildren [_]))
false))

(defn- offline-playlist-only-folder-node
"Creates a node that represents a playlist folder available in an
exported rekordbox database file when the only thing that can be
chosen is playlists themselves."
[^Database database id folder-name]
(DefaultMutableTreeNode.
(proxy [Object IMenuEntry] []
(toString [] folder-name)
(getId [] (int id))
(getSlot [] nil)
(getTrackType [] nil)
(loadChildren [^DefaultMutableTreeNode node]
(when (unloaded? node)
(doseq [^Database$PlaylistFolderEntry entry (.. database playlistFolderIndex (get id))]
(when entry
(if (.isFolder entry)
(.add node (offline-playlist-only-folder-node database (.id entry) (.name entry)))
(.add node (offline-playlist-only-node database (.id entry) (.name entry))))))
(mark-if-still-empty node))))
true))

(defn- offline-file-playlist-node
"Creates the root node for working with an offline rekordbox exported
database file and choosing playlists."
[^Database database]
(let [node (DefaultMutableTreeNode.
(proxy [Object IMenuEntry] []
(toString [] (str "Choose Playlist from:"))
(getId [] (int 0))
(getSlot [] nil)
(getTrackType [] nil)
(loadChildren [_])))]
(.add node (offline-playlist-only-folder-node database 0 "Playlists"))
node))

(defn- create-local-track-chooser-dialog
"Builds an interface in which the user can choose a track from a
locally mounted media filesystem for offline inclusion into a show.
Returns the frame if creation succeeded. If `parent` is not nil, the
Expand All @@ -2347,11 +2396,11 @@
the usual [database track] tuple.
This function must be invoked on the Swing Event Dispatch thread."
[^JFrame parent ^Database pdb extra-labels]
[^JFrame parent ^Database pdb extra-labels]
(try
(let [selected-track (atom nil)
searches (atom {})
file-model (DefaultTreeModel. (offline-file-node pdb) true)
file-model (DefaultTreeModel. (offline-file-track-node pdb) true)
^JTree file-tree (seesaw/tree :model file-model :id :tree)
file-scroll (seesaw/scrollable file-tree)
choose-button (seesaw/button :text "Choose Track" :enabled? false)
Expand All @@ -2361,7 +2410,7 @@
(seesaw/config! choose-button :enabled? (some? @selected-track)))
search-label (seesaw/label :text "Search:")
search-field (seesaw/text "")
search-partial (seesaw/label "") ; Not used in this dialog variant, expected by search UI.
search-partial (seesaw/label "") ; Not used in this dialog variant, expected by search UI.
search-button (seesaw/button :text "Load All") ; Also not used but expected by search UI.
search-panel (mig/mig-panel :background "#eee"
:items [[search-label] [search-field "pushx, growx"]])
Expand Down Expand Up @@ -2399,7 +2448,7 @@
^IMenuEntry selected-entry (when search-node (.. search-node getUserObject))
selected-search (when selected-entry (.getSlot selected-entry))]
(when (not= selected-search (:current @searches))
(swap! searches dissoc :current) ; Suppress UI responses during switch to new search.
(swap! searches dissoc :current) ; Suppress UI responses during switch to new search.
(if selected-search
(do
(swap! searches assoc-in [selected-search :path] search-path)
Expand Down Expand Up @@ -2429,6 +2478,73 @@
"<br><br>See the log file for more details.")
:title "Problem Choosing Track" :type :error))))

(defn- create-local-playlist-chooser-dialog
"Builds an interface in which the user can choose a playlist from a
locally mounted media filesystem for offline inclusion into a show.
Returns the frame if creation succeeded. If `parent` is not nil, the
dialog will be centered on it rather than in the middle of the
screen. `pdb` must be a Database object that contains a parsed
rekordbox `export.pdb` database. If `extra-labels` are provided,
they are used to create additional buttons at the bottom of the
dialog which, when clicked, return the text of the label rather than
the usual [database playlist] tuple.
This function must be invoked on the Swing Event Dispatch thread."
[^JFrame parent ^Database pdb extra-labels]
(try
(let [selected-playlist (atom nil)
file-model (DefaultTreeModel. (offline-file-playlist-node pdb) true)
^JTree file-tree (seesaw/tree :model file-model :id :tree)
file-scroll (seesaw/scrollable file-tree)
choose-button (seesaw/button :text "Choose Playlist" :enabled? false)
cancel-button (seesaw/button :text "Cancel")
extra-buttons (map (fn [text] (seesaw/button :text text)) extra-labels)
update-choose-ui (fn []
(seesaw/config! choose-button :enabled? (some? @selected-playlist)))
layout (seesaw/border-panel :center file-scroll)
^JDialog dialog (seesaw/dialog :content layout :options (concat [choose-button cancel-button] extra-buttons)
:title (str "Choose Playlist from " (describe-pdb-media (.sourceFile pdb)))
:default-option choose-button :modal? true)
mouse-listener (proxy [java.awt.event.MouseAdapter] []
(mousePressed [^java.awt.event.MouseEvent e]
(when (and @selected-playlist (= 2 (.getClickCount e)))
(.doClick ^JButton choose-button))))]
(.setSelectionMode (.getSelectionModel file-tree) javax.swing.tree.TreeSelectionModel/SINGLE_TREE_SELECTION)
(.setSize dialog 800 600)
(.setLocationRelativeTo dialog parent)
(seesaw/listen file-tree
:tree-will-expand
(fn [^javax.swing.event.TreeExpansionEvent e]
(let [^DefaultMutableTreeNode node (.. e (getPath) (getLastPathComponent))
^IMenuEntry entry (.getUserObject node)]
(.loadChildren entry node)))
:selection
(fn [^javax.swing.event.TreeSelectionEvent e]
(try
(reset! selected-playlist
(when (.isAddedPath e)
(let [^DefaultMutableTreeNode node (.. e (getPath) (getLastPathComponent))
^IMenuEntry entry (.getUserObject node)]
(when-not (.getAllowsChildren node) (.getId entry)))))
(update-choose-ui)
(catch Throwable t
(timbre/error t "Problem responding to file tree click.")))))
(.addMouseListener file-tree mouse-listener)
(seesaw/listen choose-button :action-performed
(fn [_]
(seesaw/return-from-dialog dialog [pdb (.. pdb playlistIndex (get (long @selected-playlist)))])))
(seesaw/listen cancel-button :action-performed (fn [_] (seesaw/return-from-dialog dialog nil)))
(doseq [button extra-buttons]
(let [text (seesaw/text button)]
(seesaw/listen button :action-performed (fn [_] (seesaw/return-from-dialog dialog text)))))
(.expandPath file-tree (.getPathForRow file-tree 1))
(seesaw/show! dialog))
(catch Exception e
(timbre/error e "Problem Choosing Playlist")
(seesaw/alert (str "<html>Unable to Choose Playlist from Media Export:<br><br>" (.getMessage e)
"<br><br>See the log file for more details.")
:title "Problem Choosing Playlist" :type :error))))

(defn choose-media-export
"Presents a modal dialog allowing the selection of a locally mounted
rekordbox media export filesystem. If one is successfully chosen,
Expand Down Expand Up @@ -2479,12 +2595,36 @@
([parent ^Database database & extra-labels]
(seesaw/invoke-now
(if (and database (.. database sourceFile canRead)) ; Trying to reuse a database, make sure file is still there.
(create-chooser-dialog parent database extra-labels)
(create-local-track-chooser-dialog parent database extra-labels)
(if-let [pdb (choose-media-export parent)]
(create-chooser-dialog parent pdb extra-labels)
(create-local-track-chooser-dialog parent pdb extra-labels)
(seesaw/alert parent "Could not find exported rekordbox database."
:title "Nowhere to Load Tracks From" :type :error))))))

(defn choose-local-playlist
"Presents a modal dialog allowing the selection of a playlist from a
locally mounted media filesystem. If `parent` is supplied, the
dialogs will be centered on it rather than in the middle of the
screen. If `database` is supplied, uses that already-parsed
rekordbox export file; otherwise starts by prompting the user to
choose a media volume to parse. Returns a tuple of the database and
the chosen playlist object, or `nil` if the user canceled. If
`extra-labels` are provided, they are used to create additional
buttons at the bottom of the dialog which, when clicked, return the
text of the label rather than the usual [database playlist] tuple."
([]
(choose-local-playlist nil))
([parent]
(choose-local-playlist parent nil))
([parent ^Database database & extra-labels]
(seesaw/invoke-now
(if (and database (.. database sourceFile canRead)) ; Trying to reuse a database, make sure file is still there.
(create-local-playlist-chooser-dialog parent database extra-labels)
(if-let [pdb (choose-media-export parent)]
(create-local-playlist-chooser-dialog parent pdb extra-labels)
(seesaw/alert parent "Could not find exported rekordbox database."
:title "Nowhere to Load Playlists From" :type :error))))))

(defn create-metadata-archive
"Prompt the user to select some mounted rekordbox media, then for a
file in which to archive its metadata for use with the Opus Quad.
Expand Down

0 comments on commit 74efb6c

Please sign in to comment.