packages feed

musicScroll-0.3.3: src/MusicScroll/LyricsPipeline.hs

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 (Show)

data SearchResult
  = GotLyric SongByOrigin TrackInfo Lyrics
  | ErrorOn ErrorCause
  deriving (Show)

data ErrorCause = NotOnDB TrackByPath | NoLyricsOnWeb TrackInfo | ENoSong
  deriving (Show)

noRepeatedSongs :: Functor m => Pipe TrackIdentifier TrackIdentifier m a
noRepeatedSongs = do
  firstSong <- await
  yield firstSong
  loop firstSong
  where
    loop prevSong = do
      newSong <- await
      if (TIWE newSong) /= (TIWE prevSong)
        then yield newSong *> loop newSong
        else loop prevSong

getLyricsFromAnywhere :: MVar Connection -> Pipe TrackIdentifier SearchResult IO a
getLyricsFromAnywhere connMvar = PP.mapM go
  where
    go :: TrackIdentifier -> IO SearchResult
    go ident = runReaderT (either caseByPath caseByInfoGeneral ident) connMvar

getLyricsOnlyFromWeb :: Pipe TrackInfo SearchResult IO a
getLyricsOnlyFromWeb = PP.mapM caseByInfoWeb

caseByInfoGeneral :: TrackInfo -> ReaderT (MVar Connection) IO SearchResult
caseByInfoGeneral track =
  let local = uncurry (GotLyric DB) <$> getDBSong (tUrl track)
      web = caseByInfoWeb track
      err = pure (ErrorOn (NoLyricsOnWeb track))
   in local <|> web <|> err

caseByInfoWeb :: (MonadIO m, Alternative m) => TrackInfo -> m SearchResult
caseByInfoWeb track =
  GotLyric Web track
    <$> ( getLyricsFromWeb azLyricsInstance track
            <|> getLyricsFromWeb musiXMatchInstance track
        )

caseByPath :: TrackByPath -> ReaderT (MVar Connection) IO SearchResult
caseByPath track =
  ((uncurry (GotLyric DB)) <$> getDBSong (tpPath track))
    <|> pure (ErrorOn (NotOnDB track))

saveOnDb :: MVar Connection -> InsertStategy -> Pipe SearchResult SearchResult IO a
saveOnDb mconn strat = PP.chain go
  where
    go :: SearchResult -> IO ()
    go (GotLyric Web info lyr) = runReaderT (strat info lyr) mconn
    go _otherwise = pure ()