Skip to content
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

Persistent player #6

Merged
merged 2 commits into from
Mar 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
[org.clojure/tools.cli "0.3.5"]
[org.clojure/tools.logging "0.3.1"]
[org.postgresql/postgresql "9.4.1212"]
[org.webjars.bower/tether "1.3.7"]
[com.mpatric/mp3agic "0.9.0"] ;; ID3 parser
[ring-middleware-format "0.7.0"]
[ring-webjars "0.1.1"]
Expand All @@ -24,6 +23,7 @@
;; Clojurescript
[org.webjars/bootstrap "4.0.0-alpha.5"]
[org.webjars/font-awesome "4.7.0"]
[org.webjars.bower/tether "1.3.7"]
[org.webjars/webjars-locator-jboss-vfs "0.1.0"]
[org.clojure/clojurescript "1.9.293" :scope "provided"]
[cljs-ajax "0.5.8"]
Expand Down
23 changes: 22 additions & 1 deletion resources/public/css/screen.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/*
* Colours:
* Primary - #000000
* Secondary - #00aeef
*/

html,
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
height: 100%;
}

Expand Down Expand Up @@ -40,3 +46,18 @@ body {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

/* Persistent player */
#player {
z-index: 100;
position: fixed;
bottom: 0;
width: 100%;
height: 50px;
padding: 10px;
margin: 0;
background: #000;
border-top: 1.5px solid #00aeef;
/* May cause other colours other than text to change */
color: #fff;
}
19 changes: 10 additions & 9 deletions resources/public/css/sidebar.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ body {

#wrapper {
padding-left: 0;
padding-bottom: 25px; /* half the size of the player */
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
Expand All @@ -19,7 +20,7 @@ body {
padding-left: 250px;
}

#sidebar-wrapper {
#sidebar {
z-index: 100;
position: fixed;
left: 250px;
Expand All @@ -34,17 +35,17 @@ body {
transition: all 0.5s ease;
}

#wrapper.toggled #sidebar-wrapper {
#wrapper.toggled #sidebar {
width: 250px;
}

#page-content-wrapper {
#page-content {
width: 100%;
position: absolute;
padding: 15px;
}

#wrapper.toggled #page-content-wrapper {
#wrapper.toggled #page-content {
position: absolute;
margin-right: -250px;
}
Expand Down Expand Up @@ -104,21 +105,21 @@ body {
padding-left: 0;
}

#sidebar-wrapper {
#sidebar {
width: 250px;
}

#wrapper.toggled #sidebar-wrapper {
#wrapper.toggled #sidebar {
width: 0;
}

#page-content-wrapper {
#page-content {
padding: 20px;
position: relative;
}

#wrapper.toggled #page-content-wrapper {
#wrapper.toggled #page-content {
position: relative;
margin-right: 0;
}
}
}
50 changes: 0 additions & 50 deletions src/cljs/channel/play_queue.cljs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
(ns channel.play-queue
(:require [clojure.zip :as z]
[channel.audio :as audio]
[channel.events :refer [handle-event dispatch!]]
[channel.utils :refer [maybe]]))

(defn make-play-queue
Expand Down Expand Up @@ -42,51 +40,3 @@
(def track-id
"Returns the ID of the song in the current play queue."
(maybe z/node))

;;
;; Handlers
;;
(defn- song->audio
"Create an audio object using the given `song`, setting up
all callbacks."
[song]
(audio/make-audio song {:on-ended #(dispatch! [:songs/next])}))

(defmethod handle-event :songs/play
[{:keys [songs player] :as db} [_ song]]
(if (:queue player)
(do
(audio/play!)
(assoc-in db [:player :status] :playing))
(do
(when song
(-> song
song->audio
audio/play!))
;; TODO: Reorganize the queue so that the first item is `song`
(assoc db :player {:queue (make-play-queue songs)
:status :playing}))))

(defmethod handle-event :songs/pause
[db _]
(audio/pause!)
(assoc-in db [:player :status] :paused))

(defmethod handle-event :songs/next
[{:keys [songs player] :as db} _]
(if-let [pq (next-track (:queue player))]
(do
(audio/pause!)
(audio/play! (-> (get songs (track-id pq))
song->audio))
(assoc-in db [:player :queue] pq))
;; ensure that status is updated when the queue is depleted.
(assoc db :player {:queue nil, :status nil})))

(defmethod handle-event :songs/prev
[{:keys [songs player] :as db} [ev-name]]
(let [pq (previous-track (:queue player))]
(audio/pause!)
(audio/play! (-> (get songs (track-id pq))
song->audio))
(assoc-in db [:player :queue] pq)))
2 changes: 1 addition & 1 deletion src/cljs/channel/views/components.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[:a#menu-toggle text])

(rum/defc sidebar < rum/static [links]
[:#sidebar-wrapper
[:#sidebar
[:ul.sidebar-nav
[:li.sidebar-brand
{:key (hash "root")} ;; TODO: find more robust method
Expand Down
13 changes: 7 additions & 6 deletions src/cljs/channel/views/core.cljs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns channel.views.core
(:require [channel.views.components :as c]
[channel.views.player :refer [audio-player]]
[channel.views.songs :refer [songs-page]]
[channel.views.upload :refer [upload-page]]
[goog.events :as events]
Expand Down Expand Up @@ -29,12 +30,12 @@
[:#wrapper
(c/sidebar [["Songs" (songs-path)]
["Upload" (upload-path)]])
[:#page-content-wrapper
[:.row
[:.col-lg-12
(current-page
(:page (rum/react db))
db)]]]])
[:#page-content
(current-page
(:page (rum/react db))
db)]
(let [{:keys [player songs]} (rum/react db)]
(audio-player songs player))])

(defn- hook-browser-navigation!
"Hook browser history in to secretary config."
Expand Down
92 changes: 92 additions & 0 deletions src/cljs/channel/views/player.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
(ns channel.views.player
(:require [channel.audio :as audio]
[channel.events :refer [handle-event dispatch!]]
[channel.play-queue :as pq]
[rum.core :as rum]))

;;
;; Handlers
;;
(defn- song->audio
"Create an audio object using the given `song`, setting up
all callbacks."
[song]
(audio/make-audio song {:on-ended #(dispatch! [:songs/next])}))

(defmethod handle-event :songs/play
[{:keys [songs player] :as db} [_ song]]
(if (:queue player)
(do
(audio/play!)
(assoc-in db [:player :status] :playing))
(do
(when song
(-> song
song->audio
audio/play!))
;; TODO: Reorganize the queue so that the first item is `song`
(assoc db :player {:queue (pq/make-play-queue songs)
:status :playing}))))

(defmethod handle-event :songs/pause
[db _]
(audio/pause!)
(assoc-in db [:player :status] :paused))

(defmethod handle-event :songs/next
[{:keys [songs player] :as db} _]
(if-let [pq (pq/next-track (:queue player))]
(do
(audio/pause!)
(audio/play! (-> (get songs (pq/track-id pq))
song->audio))
(update db :player merge {:queue pq, :status :playing}))
;; ensure that status is updated when the queue is depleted.
(assoc db :player {:queue nil, :status nil})))

(defmethod handle-event :songs/prev
[{:keys [songs player] :as db} [ev-name]]
(let [pq (pq/previous-track (:queue player))]
(audio/pause!)
(audio/play! (-> (get songs (pq/track-id pq))
song->audio))
(update db :player merge {:queue pq, :status :playing})))

;;
;; Views
;;

(defn current-song [songs play-queue]
(->> play-queue pq/track-id (get songs)))

;; TODO: Make a mixin for updating player song progress
;; *hint* use requestAnimationFrame or something
;; TODO: Define a player progress component

(defn- song-title-display
"Returns a human readable song title. This joins the title,
album and artist."
[{:keys [title album artist] :as song}]
(if song
(clojure.string/join " - " [title album artist])
"Not Playing..."))

(rum/defc audio-player [songs player]
[:.row#player
[:.col-md-3
[:.btn-group {:role "group"}
[:button.btn.btn-default {:on-click
#(dispatch! [:songs/prev])}
[:i.fa.fa-backward]]
(if (= (:status player) :playing)
[:button.btn.btn-default {:on-click
#(dispatch! [:songs/pause])}
[:i.fa.fa-pause]]
[:button.btn.btn-default {:on-click
#(dispatch! [:songs/play])}
[:i.fa.fa-play]])
[:butto.btn.btn-default {:on-click
#(dispatch! [:songs/next])}
[:i.fa.fa-forward]]]]
[:.col-md-9
[:p (song-title-display (current-song songs (:queue player)))]]])
86 changes: 22 additions & 64 deletions src/cljs/channel/views/songs.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,27 @@
[channel.play-queue :as pq]
[rum.core :as rum]))

(defn current-song [songs play-queue]
(->> play-queue pq/track-id (get songs)))

;; TODO: Make a mixin for updating player song progress
;; *hint* use requestAnimationFrame or something
;; TODO: Define a player progress component

(defn- song-title-display
"Returns a human readable song title. This joins the title,
album and artist."
[{:keys [title album artist] :as song}]
(if song
(clojure.string/join " - " [title album artist])
"Not Playing..."))

(rum/defc audio-player [songs player]
[:.row
[:.col-md-3
[:.btn-group {:role "group"}
[:button.btn.btn-default {:on-click
#(events/dispatch! [:songs/prev])}
[:i.fa.fa-backward]]
(if (= (:status player) :playing)
[:button.btn.btn-default {:on-click
#(events/dispatch! [:songs/pause])}
[:i.fa.fa-pause]]
[:button.btn.btn-default {:on-click
#(events/dispatch! [:songs/play])}
[:i.fa.fa-play]])
[:butto.btn.btn-default {:on-click
#(events/dispatch! [:songs/next])}
[:i.fa.fa-forward]]]]
[:.col-md-9
[:p (song-title-display (current-song songs (:queue player)))]]])

(rum/defc song-list [songs]
[:table.table.table-striped
[:thead
[:tr
[:th "#"]
[:th "Title"]
[:th "Artist"]
[:th "Album"]
[:th {:col-span 2}]]]
[:tbody
(for [s (sort-by (juxt :artist :album :track) songs)]
[:tr
{:key (:id s)}
[:th (:track s)]
[:td (:title s)]
[:td (:artist s)]
[:td (:album s)]
[:td [:button {:on-click
#(events/dispatch! [:songs/play s])}
[:i.fa.fa-play]]]])]])

(rum/defc songs-page < rum/reactive
[db]
[:div#songs
[:.row
[:.col-md-12
(song-list (vals (:songs (rum/react db))))]]
[:.row
[:.col-md-12
(let [{:keys [player songs]} (rum/react db)]
(audio-player songs player))]]])
(let [songs (vals (:songs (rum/react db)))]
[:.row
[:.col-lg-12
[:table.table.table-striped
[:thead
[:tr
[:th "#"]
[:th "Title"]
[:th "Artist"]
[:th "Album"]
[:th {:col-span 2}]]]
[:tbody
(for [s (sort-by (juxt :artist :album :track) songs)]
[:tr
{:key (:id s)}
[:th (:track s)]
[:td (:title s)]
[:td (:artist s)]
[:td (:album s)]
[:td [:button {:on-click
#(events/dispatch! [:songs/play s])}
[:i.fa.fa-play]]]])]]]]))