Skip to content

Commit 100f123

Browse files
committed
Remove Sonos support (not needed anymore)
1 parent f28b742 commit 100f123

File tree

8 files changed

+66
-127
lines changed

8 files changed

+66
-127
lines changed

Diff for: src/main/scala/com/kubukoz/next/ConfigLoader.scala

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import java.nio.file.NoSuchFileException
99
import com.kubukoz.next.util.ConsoleRead
1010
import cats.effect._
1111
import cats.implicits._
12-
import cats.Show
1312
import cats.Applicative
1413
import cats.effect.std.Console
1514
import fs2.io.file.Files

Diff for: src/main/scala/com/kubukoz/next/LoginProcess.scala

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.kubukoz.next
2+
3+
import cats.implicits._
4+
import com.kubukoz.next.util.Config
5+
import cats.Monad
6+
7+
trait LoginProcess[F[_]] {
8+
def login: F[Unit]
9+
def refreshUserToken(refreshToken: Config.RefreshToken): F[Unit]
10+
}
11+
12+
object LoginProcess {
13+
def apply[F[_]](implicit F: LoginProcess[F]): LoginProcess[F] = F
14+
15+
def instance[F[_]: UserOutput: Login: ConfigLoader: Monad]: LoginProcess[F] = new LoginProcess[F] {
16+
17+
def login: F[Unit] = for {
18+
tokens <- Login[F].server
19+
config <- ConfigLoader[F].loadConfig
20+
newConfig = config.copy(token = tokens.access.some, refreshToken = tokens.refresh.some)
21+
_ <- ConfigLoader[F].saveConfig(newConfig)
22+
_ <- UserOutput[F].print(UserMessage.SavedToken)
23+
} yield ()
24+
25+
def refreshUserToken(refreshToken: Config.RefreshToken): F[Unit] = for {
26+
newToken <- Login[F].refreshToken(refreshToken)
27+
config <- ConfigLoader[F].loadConfig
28+
newConfig = config.copy(token = newToken.some)
29+
_ <- ConfigLoader[F].saveConfig(newConfig)
30+
_ <- UserOutput[F].print(UserMessage.RefreshedToken)
31+
} yield ()
32+
33+
}
34+
35+
}

Diff for: src/main/scala/com/kubukoz/next/Main.scala

+8-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.kubukoz.next
22

3-
import cats.Monad
43
import cats.data.NonEmptyList
54
import cats.effect.ExitCode
65
import cats.effect.IO
@@ -47,35 +46,29 @@ object Main extends CommandIOApp(name = "spotify-next", header = "spotify-next:
4746

4847
import Program._
4948

50-
def runApp[F[_]: Spotify: ConfigLoader: Login: UserOutput: Monad]: Choice => F[Unit] = {
51-
case Choice.Login => loginUser[F]
52-
case Choice.SkipTrack => Spotify[F].skipTrack
53-
case Choice.DropTrack => Spotify[F].dropTrack
54-
case Choice.FastForward(p) => Spotify[F].fastForward(p)
55-
}
56-
57-
def makeProgram[F[_]: Async: Console]: Resource[F, Choice => F[Unit]] = {
49+
def makeProgram[F[_]: Async: Console]: Resource[F, Runner[F]] = {
5850
implicit val userOutput: UserOutput[F] = UserOutput.toConsole
5951

6052
Resource
6153
.eval(makeLoader[F])
6254
.flatMap { implicit loader =>
6355
implicit val configAsk: Config.Ask[F] = loader.configAsk
6456

65-
makeBasicClient[F].evalMap { rawClient =>
57+
makeBasicClient[F].map { rawClient =>
6658
implicit val login: Login[F] = Login.blaze[F](rawClient)
59+
implicit val loginProcess: LoginProcess[F] = LoginProcess.instance
60+
61+
implicit val spotify = makeSpotify(apiClient[F].apply(rawClient))
6762

68-
makeSpotify(apiClient[F].apply(rawClient)).map { implicit spotify =>
69-
runApp[F]
70-
}
63+
Runner.instance[F]
7164
}
7265
}
7366
}
7467

7568
val mainOpts: Opts[IO[Unit]] = Choice
7669
.opts
7770
.map { choice =>
78-
makeProgram[IO].use(_.apply(choice))
71+
makeProgram[IO].use(_.run(choice))
7972
}
8073

8174
val runRepl: IO[Unit] = {
@@ -99,7 +92,7 @@ object Main extends CommandIOApp(name = "spotify-next", header = "spotify-next:
9992
.Stream
10093
.resource(makeProgram[IO])
10194
.evalTap(_ => IO.println("Welcome to the spotify-next REPL! Type in a command to begin"))
102-
.map(Command("", "")(Choice.opts).map(_))
95+
.map(runner => Command("", "")(Choice.opts).map(runner.run))
10396
.flatMap { command =>
10497
input
10598
.map(command.parse(_, sys.env).leftMap(_.toString))

Diff for: src/main/scala/com/kubukoz/next/Program.scala

+6-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.kubukoz.next
22

3-
import cats.Monad
4-
import cats.MonadError
53
import cats.effect.Concurrent
64
import cats.effect.MonadCancelThrow
75
import cats.effect.Resource
@@ -10,7 +8,6 @@ import cats.effect.kernel.Ref
108
import cats.effect.std.Console
119
import cats.implicits._
1210
import com.kubukoz.next.util.Config
13-
import com.kubukoz.next.util.Config.RefreshToken
1411
import com.kubukoz.next.util.Config.Token
1512
import com.kubukoz.next.util.middlewares
1613
import fs2.io.file.Files
@@ -43,7 +40,7 @@ object Program {
4340
.map(RequestLogger(logHeaders = true, logBody = true))
4441
.map(ResponseLogger(logHeaders = true, logBody = true))
4542

46-
def apiClient[F[_]: UserOutput: Console: ConfigLoader: Login: MonadCancelThrow](
43+
def apiClient[F[_]: Console: ConfigLoader: LoginProcess: MonadCancelThrow](
4744
implicit SC: fs2.Compiler[F, F]
4845
): Client[F] => Client[F] = {
4946
implicit val configAsk: Config.Ask[F] = ConfigLoader[F].configAsk
@@ -54,8 +51,8 @@ object Program {
5451
.ask[F]
5552
.map(_.refreshToken)
5653
.flatMap {
57-
case None => loginUser
58-
case Some(refreshToken) => refreshUserToken(refreshToken)
54+
case None => LoginProcess[F].login
55+
case Some(refreshToken) => LoginProcess[F].refreshUserToken(refreshToken)
5956
}
6057

6158
middlewares
@@ -64,35 +61,11 @@ object Program {
6461
.compose(middlewares.withToken[F])
6562
}
6663

67-
// Do NOT move this into Spotify, it'll vastly increase the range of its responsibilities!
68-
def loginUser[F[_]: UserOutput: Login: ConfigLoader: Monad]: F[Unit] =
69-
for {
70-
tokens <- Login[F].server
71-
config <- ConfigLoader[F].loadConfig
72-
newConfig = config.copy(token = tokens.access.some, refreshToken = tokens.refresh.some)
73-
_ <- ConfigLoader[F].saveConfig(newConfig)
74-
_ <- UserOutput[F].print(UserMessage.SavedToken)
75-
} yield ()
76-
77-
def refreshUserToken[F[_]: UserOutput: Login: ConfigLoader: MonadError[*[_], Throwable]](
78-
refreshToken: RefreshToken
79-
): F[Unit] =
80-
for {
81-
newToken <- Login[F].refreshToken(refreshToken)
82-
config <- ConfigLoader[F].loadConfig
83-
newConfig = config.copy(token = newToken.some)
84-
_ <- ConfigLoader[F].saveConfig(newConfig)
85-
_ <- UserOutput[F].print(UserMessage.RefreshedToken)
86-
} yield ()
87-
88-
def makeSpotify[F[_]: UserOutput: Concurrent](client: Client[F]): F[Spotify[F]] = {
64+
def makeSpotify[F[_]: UserOutput: Concurrent](client: Client[F]): Spotify[F] = {
8965
implicit val theClient = client
66+
implicit val playback = Spotify.Playback.spotify[F](client)
9067

91-
import org.http4s.syntax.all._
92-
93-
Spotify.Playback.build[F](uri"http://localhost:5005", client).map { implicit playback =>
94-
Spotify.instance[F]
95-
}
68+
Spotify.instance[F]
9669
}
9770

9871
}

Diff for: src/main/scala/com/kubukoz/next/Runner.scala

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.kubukoz.next
2+
3+
trait Runner[F[_]] {
4+
def run(choice: Choice): F[Unit]
5+
}
6+
7+
object Runner {
8+
def apply[F[_]](implicit F: Runner[F]): Runner[F] = F
9+
10+
def instance[F[_]: Spotify: LoginProcess]: Runner[F] = {
11+
case Choice.Login => LoginProcess[F].login
12+
case Choice.SkipTrack => Spotify[F].skipTrack
13+
case Choice.DropTrack => Spotify[F].dropTrack
14+
case Choice.FastForward(p) => Spotify[F].fastForward(p)
15+
}
16+
17+
}

Diff for: src/main/scala/com/kubukoz/next/Spotify.scala

-36
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package com.kubukoz.next
22

3-
import java.time.Duration
4-
53
import cats.data.Kleisli
64
import cats.effect.Concurrent
75
import cats.implicits._
8-
import com.kubukoz.next.api.sonos
96
import com.kubukoz.next.api.spotify.Item
107
import com.kubukoz.next.api.spotify.Player
118
import com.kubukoz.next.api.spotify.PlayerContext
@@ -15,7 +12,6 @@ import org.http4s.Method.POST
1512
import org.http4s.Method.PUT
1613
import org.http4s.Request
1714
import org.http4s.Status
18-
import org.http4s.Uri
1915
import org.http4s.circe.CirceEntityCodec._
2016
import org.http4s.client.Client
2117

@@ -110,38 +106,6 @@ object Spotify {
110106

111107
}
112108

113-
def localSonos[F[_]: Concurrent](baseUrl: Uri, room: String, client: Client[F]): Playback[F] = new Playback[F] {
114-
val nextTrack: F[Unit] =
115-
client.expect[api.spotify.Anything](baseUrl / room / "next").void
116-
117-
def seek(ms: Int): F[Unit] = {
118-
val seconds = Duration.ofMillis(ms.toLong).toSeconds().toString
119-
120-
client.expect[api.spotify.Anything](baseUrl / room / "timeseek" / seconds).void
121-
}
122-
123-
}
124-
125-
def build[F[_]: UserOutput: Concurrent](sonosBaseUrl: Uri, client: Client[F]): F[Playback[F]] =
126-
UserOutput[F].print(UserMessage.CheckingSonos(sonosBaseUrl)) *>
127-
client
128-
.get(sonosBaseUrl / "zones") {
129-
case response if response.status.isSuccess => response.as[sonos.SonosZones].map(_.some)
130-
case _ => none[sonos.SonosZones].pure[F]
131-
}
132-
.handleError(_ => None)
133-
.flatMap {
134-
case None =>
135-
UserOutput[F].print(UserMessage.SonosNotFound).as(spotify(client))
136-
137-
case Some(zones) =>
138-
val roomName = zones.zones.head.coordinator.roomName
139-
140-
UserOutput[F]
141-
.print(UserMessage.SonosFound(zones, roomName))
142-
.as(localSonos(sonosBaseUrl, roomName, client))
143-
}
144-
145109
}
146110

147111
private object methods {

Diff for: src/main/scala/com/kubukoz/next/UserMessage.scala

-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import cats.Show
44
import cats.effect.std
55
import cats.implicits._
66
import cats.~>
7-
import com.kubukoz.next.api.sonos.SonosZones
87
import com.kubukoz.next.api.spotify.Item
98
import com.kubukoz.next.api.spotify.Player
109
import com.kubukoz.next.api.spotify.PlayerContext
@@ -26,11 +25,6 @@ object UserMessage {
2625
final case class RemovingCurrentTrack(player: Player[PlayerContext.playlist, Item.track]) extends UserMessage
2726
case object TooCloseToEnd extends UserMessage
2827
final case class Seeking(desiredProgressPercent: Int) extends UserMessage
29-
30-
// setup
31-
final case class CheckingSonos(url: Uri) extends UserMessage
32-
case object SonosNotFound extends UserMessage
33-
final case class SonosFound(zones: SonosZones, roomName: String) extends UserMessage
3428
}
3529

3630
trait UserOutput[F[_]] {
@@ -57,9 +51,6 @@ object UserOutput {
5751
show"""Removing track "${player.item.name}" (${player.item.uri}) from playlist ${player.context.uri.playlist}"""
5852
case TooCloseToEnd => "Too close to song's ending, rewinding to beginning"
5953
case Seeking(desiredProgressPercent) => show"Seeking to $desiredProgressPercent%"
60-
case CheckingSonos(url) => show"Checking if Sonos API is available at $url..."
61-
case SonosNotFound => "Sonos not found, will access Spotify API directly"
62-
case SonosFound(zones, roomName) => show"Found ${zones.zones.size} zone(s), will use room $roomName"
6354
}
6455

6556
msg => std.Console[F].println(stringify(msg))

Diff for: src/main/scala/com/kubukoz/next/api/sonos.scala

-33
This file was deleted.

0 commit comments

Comments
 (0)