From d7b8cbcc4e08276412645c5008711fd2f66391af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20Schl=C3=BCter?= Date: Tue, 7 Aug 2018 15:15:13 +0200 Subject: [PATCH] Add skipping for linear playlists with all repeat modes --- src/cljs/airsonic_ui/audio/playlist.cljs | 80 +++++++++++++------ src/cljs/airsonic_ui/events.cljs | 3 +- .../cljs/airsonic_ui/audio/playlist_test.cljs | 46 +++++++++-- 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/src/cljs/airsonic_ui/audio/playlist.cljs b/src/cljs/airsonic_ui/audio/playlist.cljs index 3f842ff..0e8b4a7 100644 --- a/src/cljs/airsonic_ui/audio/playlist.cljs +++ b/src/cljs/airsonic_ui/audio/playlist.cljs @@ -11,38 +11,39 @@ (defmulti ->playlist "Creates a new playlist that behaves according to the given playback- and repeat-mode parameters." - (fn [queue play-idx playback-mode repeat-mode] playback-mode)) + (fn [queue playing-idx playback-mode repeat-mode] playback-mode)) (defn- playlist-queue - [queue play-idx] - (concat (take play-idx queue) - [(assoc (nth queue play-idx) :currently-playing? true)] - (drop (inc play-idx) queue))) + [queue playing-idx] + (concat (take playing-idx queue) + [(assoc (nth queue playing-idx) :currently-playing? true)] + (drop (inc playing-idx) queue))) -(defmethod ->playlist - :playback-mode/linear - [queue play-idx playback-mode repeat-mode] - (let [queue (->> (playlist-queue queue play-idx) - (map (fn [order song] (assoc song :order order)) - (range (count queue))))] +(defmethod ->playlist :playback-mode/linear + [queue playing-idx playback-mode repeat-mode] + (let [queue (->> (playlist-queue queue playing-idx) + (mapv (fn [order song] (assoc song :order order)) + (range (count queue))))] (->Playlist queue playback-mode repeat-mode))) -(defmethod ->playlist - :playback-mode/shuffle - [queue play-idx playback-mode repeat-mode] - (let [queue (->> (playlist-queue queue play-idx) - (map (fn [order song] (assoc song :order order)) - (shuffle (range (count queue)))))] +(defmethod ->playlist :playback-mode/shuffle + [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)))))] (->Playlist queue playback-mode repeat-mode))) +(defn- current-idx [playlist] + (first (keep-indexed (fn [idx item] + (when (:currently-playing? item) + idx)) + (:queue playlist)))) + (defn set-playback-mode - "Changes the playback mode of a playlist and re-suffles it if necessary" + "Changes the playback mode of a playlist and re-shuffles it if necessary" [playlist playback-mode] - (let [current-idx (first (keep-indexed (fn [idx item] - (when (:currently-playing? item) - idx)) - (:queue playlist)))] - (->playlist (:queue playlist) current-idx playback-mode (:repeat-mode playlist)))) + (->playlist (:queue playlist) (current-idx playlist) + playback-mode (:repeat-mode playlist))) (defn set-repeat-mode "Allows to change the way the next and previous song of a playlist is selected" @@ -56,5 +57,38 @@ (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 (juxt :playback-mode :repeat-mode)) + +(defmethod next-song [:playback-mode/linear :repeat-mode/none] + [playlist] + (let [current-idx (current-idx 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))))))) + +(defmethod next-song [:playback-mode/linear :repeat-mode/single] + [playlist] + playlist) + +(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)))))) + +(defmethod next-song [:playback-mode/shuffle :repeat-mode/single] + [playlist] + playlist) + (defmulti previous-song (juxt :playback-mode :repeat-mode)) diff --git a/src/cljs/airsonic_ui/events.cljs b/src/cljs/airsonic_ui/events.cljs index dd3e619..1d91b6d 100644 --- a/src/cljs/airsonic_ui/events.cljs +++ b/src/cljs/airsonic_ui/events.cljs @@ -3,8 +3,7 @@ [ajax.core :as ajax] [airsonic-ui.routes :as routes] [airsonic-ui.db :as db] - [airsonic-ui.utils.api :as api] - [day8.re-frame.tracing :refer-macros [fn-traced defn-traced]])) ; <- useful to debug handlers + [airsonic-ui.utils.api :as api])) (re-frame/reg-fx ;; a simple effect to keep println statements out of our event handlers diff --git a/test/cljs/airsonic_ui/audio/playlist_test.cljs b/test/cljs/airsonic_ui/audio/playlist_test.cljs index d03d22d..7196cae 100644 --- a/test/cljs/airsonic_ui/audio/playlist_test.cljs +++ b/test/cljs/airsonic_ui/audio/playlist_test.cljs @@ -2,7 +2,8 @@ (:require [cljs.test :refer [deftest testing is]] [airsonic-ui.audio.playlist :as playlist] [airsonic-ui.fixtures :as fixtures] - [airsonic-ui.test-helpers :as helpers])) + [airsonic-ui.test-helpers :as helpers] + [debux.cs.core :refer-macros [dbg]])) (enable-console-print!) @@ -90,14 +91,47 @@ (str "from " repeat-mode " to " next-repeat-mode))))))) (deftest linear-next-song - (testing "Should follow the same order as the queue used for creation") - (testing "Should go back to the first song when repeat-mode is all") - (testing "Should always give the same track when repeat-mode is single")) + (testing "Should follow the same order as the queue used for creation" + (doseq [repeat-mode [:repeat-mode/none :repeat-mode/all]] + (let [queue (playing-queue 5) + playlist (playlist/->playlist queue 0 :playback-mode/linear repeat-mode)] + (is (same-song? (nth queue 1) (-> (playlist/next-song playlist) + (playlist/peek))) + (str repeat-mode ", skipped once")) + (is (same-song? (nth queue 2) (-> (playlist/next-song playlist) + (playlist/next-song) + (playlist/peek))) + (str repeat-mode ", skipped twice"))))) + (testing "Should go back to the first song when repeat-mode is all and we played the last song") + (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"))) + (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 +#_(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 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 linear-previous-song) #_(deftest shuffled-previous-song)