diff --git a/shadow-cljs.edn b/shadow-cljs.edn index e14759a..621b22a 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -11,8 +11,9 @@ ;; debugging [day8.re-frame/re-frame-10x "0.3.3-react16"] [day8.re-frame/tracing "0.5.1"] + [philoskim/debux "0.4.11"] ;; for CIDER - [cider/cider-nrepl "0.18.0-SNAPSHOT"]] + [cider/cider-nrepl "0.18.0"]] :nrepl {:port 9000} diff --git a/src/cljs/airsonic_ui/audio/playlist.cljs b/src/cljs/airsonic_ui/audio/playlist.cljs index 0e8b4a7..bf530c1 100644 --- a/src/cljs/airsonic_ui/audio/playlist.cljs +++ b/src/cljs/airsonic_ui/audio/playlist.cljs @@ -26,23 +26,25 @@ (range (count queue))))] (->Playlist queue playback-mode repeat-mode))) -(defmethod ->playlist :playback-mode/shuffle +(defn- -shuffle-songs [queue] + (mapv (fn [song order] (assoc song :order order)) queue (shuffle (range (count queue))))) + +(defmethod ->playlist :playback-mode/shuffled [queue playing-idx playback-mode repeat-mode] (let [queue (->> (playlist-queue queue playing-idx) - (mapv (fn [order song] (assoc song :order order)) - (shuffle (range (count queue)))))] + (-shuffle-songs))] (->Playlist queue playback-mode repeat-mode))) -(defn- current-idx [playlist] - (first (keep-indexed (fn [idx item] - (when (:currently-playing? item) - idx)) +(defn- -current-song [playlist] + (first (keep-indexed (fn [idx song] + (when (:currently-playing? song) + [idx song])) (:queue playlist)))) (defn set-playback-mode "Changes the playback mode of a playlist and re-shuffles it if necessary" [playlist playback-mode] - (->playlist (:queue playlist) (current-idx playlist) + (->playlist (:queue playlist) (first (-current-song playlist)) playback-mode (:repeat-mode playlist))) (defn set-repeat-mode @@ -57,38 +59,45 @@ (filter :currently-playing?) (first))) -(defn- ?assoc - "Like assoc, but returns coll unchanged if (pref coll) is false" - [coll pred & assog-args] - (if (pred coll) (apply assoc coll assog-args) coll)) +(defmulti next-song :repeat-mode) -(defmulti next-song (juxt :playback-mode :repeat-mode)) - -(defmethod next-song [:playback-mode/linear :repeat-mode/none] +(defmethod next-song :repeat-mode/none [playlist] - (let [current-idx (current-idx playlist) + ;; this is pretty easy; get the next song and stop playing at the at + (let [[current-idx _] (-current-song playlist) next-idx (inc current-idx)] - (println "next-idx" next-idx "current-idx" current-idx "(< next-idx (count playlist))" (< next-idx (count playlist))) (update playlist :queue - (fn [queue] (-> (update queue current-idx dissoc :currently-playing?) - (?assoc #(< next-idx (count playlist)) - next-idx (assoc (get queue next-idx) - :currently-playing? true))))))) + (fn [queue] (cond-> queue + :always (update current-idx dissoc :currently-playing?) + (< next-idx (count playlist)) (assoc-in [next-idx :currently-playing?] true)))))) -(defmethod next-song [:playback-mode/linear :repeat-mode/single] +(defmethod next-song :repeat-mode/single [playlist] playlist) + +(defmethod next-song :repeat-mode/all [playlist] - playlist) + (let [[current-idx _] (-current-song playlist)] + (-> (update-in playlist [:queue current-idx] dissoc :currently-playing?) + (update :queue + (fn [queue] + ;; we need special treatment here if we're playing the last song and + ;; have a shuffled playlist because we need to re-shuffle + (if (and (= current-idx (dec (count playlist))) + (= :playback-mode/shuffled (:playback-mode playlist))) + (->> (-shuffle-songs queue) + (mapv #(if (= (:order %) 0) (assoc % :currently-playing? true) %))) + (let [next-idx (mod (inc current-idx) (count playlist))] + (assoc-in queue [next-idx :currently-playing?] true)))))))) -(defmethod next-song [:playback-mode/linear :repeat-mode/all] - [playlist] - (let [current-idx (current-idx playlist) - next-idx (mod (inc current-idx) (count playlist))] - (update playlist :queue - (fn [queue] (-> (update queue current-idx dissoc :currently-playing?) - (assoc-in [next-idx :currently-playing?] true)))))) +(defmulti previous-song :repeat-mode) -(defmethod next-song [:playback-mode/shuffle :repeat-mode/single] - [playlist] - playlist) +(defn set-current-song [playlist playing-idx] + ;; TODO: Implementation + ) -(defmulti previous-song (juxt :playback-mode :repeat-mode)) +(defn add-song-to-end [playlist] + ;; TODO: Implementation + ) + +(defn add-next-song [playlist] + ;; TODO: Implementation + ) diff --git a/test/cljs/airsonic_ui/audio/playlist_test.cljs b/test/cljs/airsonic_ui/audio/playlist_test.cljs index 7196cae..d8f12fc 100644 --- a/test/cljs/airsonic_ui/audio/playlist_test.cljs +++ b/test/cljs/airsonic_ui/audio/playlist_test.cljs @@ -30,7 +30,7 @@ (testing "should give us the correct current song" (let [queue (playing-queue 10) start-idx (rand-int 10)] - (doseq [playback-mode [:playback-mode/linear, :playback-mode/shuffle] + (doseq [playback-mode [:playback-mode/linear, :playback-mode/shuffled] repeat-mode [:repeat-mode/none, :repeat-mode/single, :repeat-mode/all]] (is (same-song? (nth queue start-idx) (-> (playlist/->playlist queue start-idx playback-mode repeat-mode) @@ -39,7 +39,7 @@ (testing "should give us a playlist with the correct number of tracks" (let [queue (playing-queue 100) start-idx (rand-int 100)] - (doseq [playback-mode [:playback-mode/linear, :playback-mode/shuffle] + (doseq [playback-mode [:playback-mode/linear, :playback-mode/shuffled] repeat-mode [:repeat-mode/none, :repeat-mode/single, :repeat-mode/all]] (is (= (count queue) (count (playlist/->playlist queue start-idx playback-mode repeat-mode))) @@ -51,7 +51,7 @@ (let [queue (playing-queue 10) start-idx (rand-int 10) linear (playlist/->playlist queue start-idx :playback-mode/linear :repeat-mode/node) - shuffled (playlist/set-playback-mode linear :playback-mode/shuffle)] + shuffled (playlist/set-playback-mode linear :playback-mode/shuffled)] (testing "should re-order the tracks" (is (not= (map :order (:queue shuffled)) (map :order (:queue linear))))) (testing "should not change the currently playing track" @@ -61,7 +61,8 @@ (testing "from shuffled to linear" (let [queue (playing-queue 10) start-idx (rand-int 10) - shuffled (playlist/->playlist queue start-idx :playback-mode/shuffle :repeat-mode/none) + shuffled (playlist/->playlist queue start-idx + :playback-mode/shuffled :repeat-mode/none) linear (playlist/set-playback-mode shuffled :playback-mode/linear)] (testing "should set the correct order for tracks" (is (every? #(apply same-song? %) @@ -81,10 +82,11 @@ (deftest chaging-repeat-mode (testing "Changing the repeat mode" (testing "should not change the playback mode" - (doseq [playback-mode [:playback-mode/linear, :playback-mode/shuffle] + (doseq [playback-mode [:playback-mode/linear, :playback-mode/shuffled] repeat-mode [:repeat-mode/none, :repeat-mode/single, :repeat-mode/all] next-repeat-mode [:repeat-mode/none, :repeat-mode/single, :repeat-mode/all]] - (let [playlist (-> (playlist/->playlist (playing-queue 1) 0 playback-mode repeat-mode) + (let [playlist (-> (playlist/->playlist (playing-queue 1) 0 + playback-mode repeat-mode) (playlist/set-repeat-mode next-repeat-mode))] (is (= playback-mode (:playback-mode playlist))) (is (= next-repeat-mode (:repeat-mode playlist)) @@ -106,32 +108,51 @@ (testing "Should always give the same track when repeat-mode is single" (let [queue (playing-queue 3) playing-idx (rand-int 3) - playlist (playlist/->playlist queue playing-idx :playback-mode/linear :repeat-mode/single)] - (is (same-song? (nth queue playing-idx) (playlist/peek playlist))) - (is (same-song? (nth queue playing-idx) - (-> (playlist/next-song playlist) - (playlist/peek)))) - (is (same-song? (nth queue playing-idx) - (-> (playlist/next-song playlist) - (playlist/next-song) - (playlist/peek)))) - (is (same-song? (nth queue playing-idx) - (-> (playlist/next-song playlist) - (playlist/next-song) - (playlist/next-song) - (playlist/peek))) - "wrapping around"))) + playlist (playlist/->playlist queue playing-idx + :playback-mode/linear :repeat-mode/single) + played-back (map playlist/peek (iterate playlist/next-song playlist))] + (is (same-song? (nth queue playing-idx) (nth played-back 0))) + (is (same-song? (nth queue playing-idx) (nth played-back 1))) + (is (same-song? (nth queue playing-idx) (nth played-back 2))) + (is (same-song? (nth queue playing-idx) (nth played-back 3)) "wrapping around"))) (testing "Should stop playing at the end of the queue when repeat-mode is none" (is (nil? (-> (playing-queue 1) (playlist/->playlist 0 :playback-mode/linear :repeat-mode/none) (playlist/next-song) (playlist/peek)))))) -#_(deftest shuffled-next-song - (testing "Should play every track once when called for the entire queue") - (testing "Should re-shuffle the playlist when wrapping around and repeat-mode is all") - (testing "Should always give the same track when repeat-mode is single") - (testing "Should stop playing at the end of the queue when repeat-mode is none")) +(deftest shuffled-next-song + (testing "Should play every track once when called for the entire queue" + (doseq [repeat-mode '(:repeat-mode/none :repeat-mode/all)] + (let [length 10 + playlist (playlist/->playlist (playing-queue length) 0 + :playback-mode/shuffled repeat-mode) + played-tracks (->> (iterate playlist/next-song playlist) + (map playlist/peek) + (take length))] + (is (= (count played-tracks) (count (set played-tracks))) + (str repeat-mode))))) + (testing "Should re-shuffle the playlist when wrapping around and repeat-mode is all" + (let [length 100 + playlist (playlist/->playlist (playing-queue length) (dec length) + :playback-mode/shuffled :repeat-mode/all)] + (is (not= (map :order (:queue playlist)) + (map :order (:queue (playlist/next-song playlist))))))) + (testing "Should always give the same track when repeat-mode is single" + (let [queue (playing-queue 3) + playing-idx (rand-int 3) + playlist (playlist/->playlist queue playing-idx + :playback-mode/shuffled :repeat-mode/single) + played-back (map playlist/peek (iterate playlist/next-song playlist))] + (is (same-song? (nth queue playing-idx) (nth played-back 0))) + (is (same-song? (nth queue playing-idx) (nth played-back 1))) + (is (same-song? (nth queue playing-idx) (nth played-back 2))) + (is (same-song? (nth queue playing-idx) (nth played-back 3)) "wrapping around"))) + (testing "Should stop playing at the end of the queue when repeat-mode is none" + (is (nil? (-> (playing-queue 1) + (playlist/->playlist 0 :playback-mode/linear :repeat-mode/none) + (playlist/next-song) + (playlist/peek)))))) #_(deftest linear-previous-song) #_(deftest shuffled-previous-song)