module MusicScroll.LyricsPipeline
  ( SongByOrigin (..),
    SearchResult (..),
    ErrorCause (..),
    noRepeatedSongs,
    getLyricsFromAnywhere,
    getLyricsOnlyFromWeb,
    saveOnDb,
  )
where

--- | Discriminate between getting the lyrics from SQLite or the web.

import Control.Applicative (Alternative (..))
import Control.Concurrent.MVar
import Control.Monad.Trans.Reader (ReaderT, runReaderT)
import Database.SQLite.Simple
import MusicScroll.DatabaseUtils
import MusicScroll.Providers.AZLyrics (azLyricsInstance)
import MusicScroll.Providers.MusiXMatch (musiXMatchInstance)
import MusicScroll.Providers.Utils (Lyrics (..))
import MusicScroll.TrackInfo
import MusicScroll.Web
import Pipes
import qualified Pipes.Prelude as PP

data SongByOrigin = DB | Web deriving (Int -> SongByOrigin -> ShowS
[SongByOrigin] -> ShowS
SongByOrigin -> String
(Int -> SongByOrigin -> ShowS)
-> (SongByOrigin -> String)
-> ([SongByOrigin] -> ShowS)
-> Show SongByOrigin
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SongByOrigin -> ShowS
showsPrec :: Int -> SongByOrigin -> ShowS
$cshow :: SongByOrigin -> String
show :: SongByOrigin -> String
$cshowList :: [SongByOrigin] -> ShowS
showList :: [SongByOrigin] -> ShowS
Show)

data SearchResult
  = GotLyric SongByOrigin TrackInfo Lyrics
  | ErrorOn ErrorCause
  deriving (Int -> SearchResult -> ShowS
[SearchResult] -> ShowS
SearchResult -> String
(Int -> SearchResult -> ShowS)
-> (SearchResult -> String)
-> ([SearchResult] -> ShowS)
-> Show SearchResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SearchResult -> ShowS
showsPrec :: Int -> SearchResult -> ShowS
$cshow :: SearchResult -> String
show :: SearchResult -> String
$cshowList :: [SearchResult] -> ShowS
showList :: [SearchResult] -> ShowS
Show)

data ErrorCause = NotOnDB TrackByPath | NoLyricsOnWeb TrackInfo | ENoSong
  deriving (Int -> ErrorCause -> ShowS
[ErrorCause] -> ShowS
ErrorCause -> String
(Int -> ErrorCause -> ShowS)
-> (ErrorCause -> String)
-> ([ErrorCause] -> ShowS)
-> Show ErrorCause
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ErrorCause -> ShowS
showsPrec :: Int -> ErrorCause -> ShowS
$cshow :: ErrorCause -> String
show :: ErrorCause -> String
$cshowList :: [ErrorCause] -> ShowS
showList :: [ErrorCause] -> ShowS
Show)

noRepeatedSongs :: Functor m => Pipe TrackIdentifier TrackIdentifier m a
noRepeatedSongs :: forall (m :: * -> *) a.
Functor m =>
Pipe TrackIdentifier TrackIdentifier m a
noRepeatedSongs = do
  TrackIdentifier
firstSong <- Proxy () TrackIdentifier () TrackIdentifier m TrackIdentifier
Consumer' TrackIdentifier m TrackIdentifier
forall (m :: * -> *) a. Functor m => Consumer' a m a
await
  TrackIdentifier -> Proxy () TrackIdentifier () TrackIdentifier m ()
forall (m :: * -> *) a x' x. Functor m => a -> Proxy x' x () a m ()
yield TrackIdentifier
firstSong
  TrackIdentifier -> Pipe TrackIdentifier TrackIdentifier m a
forall {m :: * -> *} {b}.
Functor m =>
TrackIdentifier -> Proxy () TrackIdentifier () TrackIdentifier m b
loop TrackIdentifier
firstSong
  where
    loop :: TrackIdentifier -> Proxy () TrackIdentifier () TrackIdentifier m b
loop TrackIdentifier
prevSong = do
      TrackIdentifier
newSong <- Proxy () TrackIdentifier () TrackIdentifier m TrackIdentifier
Consumer' TrackIdentifier m TrackIdentifier
forall (m :: * -> *) a. Functor m => Consumer' a m a
await
      if (TrackIdentifier -> TrackIdentifierWithEq
TIWE TrackIdentifier
newSong) TrackIdentifierWithEq -> TrackIdentifierWithEq -> Bool
forall a. Eq a => a -> a -> Bool
/= (TrackIdentifier -> TrackIdentifierWithEq
TIWE TrackIdentifier
prevSong)
        then TrackIdentifier -> Proxy () TrackIdentifier () TrackIdentifier m ()
forall (m :: * -> *) a x' x. Functor m => a -> Proxy x' x () a m ()
yield TrackIdentifier
newSong Proxy () TrackIdentifier () TrackIdentifier m ()
-> Proxy () TrackIdentifier () TrackIdentifier m b
-> Proxy () TrackIdentifier () TrackIdentifier m b
forall a b.
Proxy () TrackIdentifier () TrackIdentifier m a
-> Proxy () TrackIdentifier () TrackIdentifier m b
-> Proxy () TrackIdentifier () TrackIdentifier m b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> TrackIdentifier -> Proxy () TrackIdentifier () TrackIdentifier m b
loop TrackIdentifier
newSong
        else TrackIdentifier -> Proxy () TrackIdentifier () TrackIdentifier m b
loop TrackIdentifier
prevSong

getLyricsFromAnywhere :: MVar Connection -> Pipe TrackIdentifier SearchResult IO a
getLyricsFromAnywhere :: forall a. MVar Connection -> Pipe TrackIdentifier SearchResult IO a
getLyricsFromAnywhere MVar Connection
connMvar = (TrackIdentifier -> IO SearchResult)
-> Pipe TrackIdentifier SearchResult IO a
forall (m :: * -> *) a b r. Monad m => (a -> m b) -> Pipe a b m r
PP.mapM TrackIdentifier -> IO SearchResult
go
  where
    go :: TrackIdentifier -> IO SearchResult
    go :: TrackIdentifier -> IO SearchResult
go TrackIdentifier
ident = ReaderT (MVar Connection) IO SearchResult
-> MVar Connection -> IO SearchResult
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT ((TrackByPath -> ReaderT (MVar Connection) IO SearchResult)
-> (TrackInfo -> ReaderT (MVar Connection) IO SearchResult)
-> TrackIdentifier
-> ReaderT (MVar Connection) IO SearchResult
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either TrackByPath -> ReaderT (MVar Connection) IO SearchResult
caseByPath TrackInfo -> ReaderT (MVar Connection) IO SearchResult
caseByInfoGeneral TrackIdentifier
ident) MVar Connection
connMvar

getLyricsOnlyFromWeb :: Pipe TrackInfo SearchResult IO a
getLyricsOnlyFromWeb :: forall a. Pipe TrackInfo SearchResult IO a
getLyricsOnlyFromWeb = (TrackInfo -> IO SearchResult) -> Pipe TrackInfo SearchResult IO a
forall (m :: * -> *) a b r. Monad m => (a -> m b) -> Pipe a b m r
PP.mapM TrackInfo -> IO SearchResult
forall (m :: * -> *).
(MonadIO m, Alternative m) =>
TrackInfo -> m SearchResult
caseByInfoWeb

caseByInfoGeneral :: TrackInfo -> ReaderT (MVar Connection) IO SearchResult
caseByInfoGeneral :: TrackInfo -> ReaderT (MVar Connection) IO SearchResult
caseByInfoGeneral TrackInfo
track =
  let local :: ReaderT (MVar Connection) IO SearchResult
local = (TrackInfo -> Lyrics -> SearchResult)
-> (TrackInfo, Lyrics) -> SearchResult
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (SongByOrigin -> TrackInfo -> Lyrics -> SearchResult
GotLyric SongByOrigin
DB) ((TrackInfo, Lyrics) -> SearchResult)
-> ReaderT (MVar Connection) IO (TrackInfo, Lyrics)
-> ReaderT (MVar Connection) IO SearchResult
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> ReaderT (MVar Connection) IO (TrackInfo, Lyrics)
getDBSong (TrackInfo -> String
tUrl TrackInfo
track)
      web :: ReaderT (MVar Connection) IO SearchResult
web = TrackInfo -> ReaderT (MVar Connection) IO SearchResult
forall (m :: * -> *).
(MonadIO m, Alternative m) =>
TrackInfo -> m SearchResult
caseByInfoWeb TrackInfo
track
      err :: ReaderT (MVar Connection) IO SearchResult
err = SearchResult -> ReaderT (MVar Connection) IO SearchResult
forall a. a -> ReaderT (MVar Connection) IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ErrorCause -> SearchResult
ErrorOn (TrackInfo -> ErrorCause
NoLyricsOnWeb TrackInfo
track))
   in ReaderT (MVar Connection) IO SearchResult
local ReaderT (MVar Connection) IO SearchResult
-> ReaderT (MVar Connection) IO SearchResult
-> ReaderT (MVar Connection) IO SearchResult
forall a.
ReaderT (MVar Connection) IO a
-> ReaderT (MVar Connection) IO a -> ReaderT (MVar Connection) IO a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ReaderT (MVar Connection) IO SearchResult
web ReaderT (MVar Connection) IO SearchResult
-> ReaderT (MVar Connection) IO SearchResult
-> ReaderT (MVar Connection) IO SearchResult
forall a.
ReaderT (MVar Connection) IO a
-> ReaderT (MVar Connection) IO a -> ReaderT (MVar Connection) IO a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ReaderT (MVar Connection) IO SearchResult
err

caseByInfoWeb :: (MonadIO m, Alternative m) => TrackInfo -> m SearchResult
caseByInfoWeb :: forall (m :: * -> *).
(MonadIO m, Alternative m) =>
TrackInfo -> m SearchResult
caseByInfoWeb TrackInfo
track =
  SongByOrigin -> TrackInfo -> Lyrics -> SearchResult
GotLyric SongByOrigin
Web TrackInfo
track
    (Lyrics -> SearchResult) -> m Lyrics -> m SearchResult
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ( Provider -> TrackInfo -> m Lyrics
forall (m :: * -> *).
(MonadIO m, Alternative m) =>
Provider -> TrackInfo -> m Lyrics
getLyricsFromWeb Provider
azLyricsInstance TrackInfo
track
            m Lyrics -> m Lyrics -> m Lyrics
forall a. m a -> m a -> m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Provider -> TrackInfo -> m Lyrics
forall (m :: * -> *).
(MonadIO m, Alternative m) =>
Provider -> TrackInfo -> m Lyrics
getLyricsFromWeb Provider
musiXMatchInstance TrackInfo
track
        )

caseByPath :: TrackByPath -> ReaderT (MVar Connection) IO SearchResult
caseByPath :: TrackByPath -> ReaderT (MVar Connection) IO SearchResult
caseByPath TrackByPath
track =
  (((TrackInfo -> Lyrics -> SearchResult)
-> (TrackInfo, Lyrics) -> SearchResult
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (SongByOrigin -> TrackInfo -> Lyrics -> SearchResult
GotLyric SongByOrigin
DB)) ((TrackInfo, Lyrics) -> SearchResult)
-> ReaderT (MVar Connection) IO (TrackInfo, Lyrics)
-> ReaderT (MVar Connection) IO SearchResult
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> ReaderT (MVar Connection) IO (TrackInfo, Lyrics)
getDBSong (TrackByPath -> String
tpPath TrackByPath
track))
    ReaderT (MVar Connection) IO SearchResult
-> ReaderT (MVar Connection) IO SearchResult
-> ReaderT (MVar Connection) IO SearchResult
forall a.
ReaderT (MVar Connection) IO a
-> ReaderT (MVar Connection) IO a -> ReaderT (MVar Connection) IO a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> SearchResult -> ReaderT (MVar Connection) IO SearchResult
forall a. a -> ReaderT (MVar Connection) IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ErrorCause -> SearchResult
ErrorOn (TrackByPath -> ErrorCause
NotOnDB TrackByPath
track))

saveOnDb :: MVar Connection -> InsertStategy -> Pipe SearchResult SearchResult IO a
saveOnDb :: forall a.
MVar Connection
-> InsertStategy -> Pipe SearchResult SearchResult IO a
saveOnDb MVar Connection
mconn InsertStategy
strat = (SearchResult -> IO ()) -> Pipe SearchResult SearchResult IO a
forall (m :: * -> *) a r. Monad m => (a -> m ()) -> Pipe a a m r
PP.chain SearchResult -> IO ()
go
  where
    go :: SearchResult -> IO ()
    go :: SearchResult -> IO ()
go (GotLyric SongByOrigin
Web TrackInfo
info Lyrics
lyr) = ReaderT (MVar Connection) IO () -> MVar Connection -> IO ()
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (InsertStategy
strat TrackInfo
info Lyrics
lyr) MVar Connection
mconn
    go SearchResult
_otherwise = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()