packages feed

htaglib-1.2.0: Sound/HTagLib/Setter.hs

-- |
-- Module      :  Sound.HTagLib.Setter
-- Copyright   :  © 2015–2018 Mark Karpov
-- License     :  BSD 3 clause
--
-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
-- Stability   :  experimental
-- Portability :  portable
--
-- A high-level interface for writing audio meta data. You don't need to
-- import this module directly, import "Sound.HTagLib" instead.

{-# LANGUAGE CPP             #-}
{-# LANGUAGE RecordWildCards #-}

module Sound.HTagLib.Setter
  ( -- * High-level API
    TagSetter
  , setTags
  , setTags'
    -- * Built-in setters
  , titleSetter
  , artistSetter
  , albumSetter
  , commentSetter
  , genreSetter
  , yearSetter
  , trackNumberSetter )
where

import Control.Applicative ((<|>))
import Control.Monad.IO.Class
import Data.Foldable (forM_)
import Data.Semigroup (Semigroup (..))
import Sound.HTagLib.Type
import qualified Sound.HTagLib.Internal as I

#if !MIN_VERSION_base(4,8,0)
import Data.Monoid hiding ((<>))
#endif

-- | A composable entity that can be used together with the 'setTags' or the
-- 'setTags'' functions to write meta data to an audio file.
--
-- Note that in case of (for example):
--
-- > titleSetter "foo" <> titleSetter "bar"
--
-- The first value wins.

data TagSetter = TagSetter
  { sdTitle       :: Maybe Title
  , sdArtist      :: Maybe Artist
  , sdAlbum       :: Maybe Album
  , sdComment     :: Maybe Comment
  , sdGenre       :: Maybe Genre
  , sdYear        :: Maybe (Maybe Year)
  , sdTrackNumber :: Maybe (Maybe TrackNumber) }

-- | @since 1.2.0

instance Semigroup TagSetter where
  x <> y = let f g = g x <|> g y in TagSetter
    { sdTitle       = f sdTitle
    , sdArtist      = f sdArtist
    , sdAlbum       = f sdAlbum
    , sdComment     = f sdComment
    , sdGenre       = f sdGenre
    , sdYear        = f sdYear
    , sdTrackNumber = f sdTrackNumber }

instance Monoid TagSetter where
  mempty = TagSetter
    { sdTitle       = Nothing
    , sdArtist      = Nothing
    , sdAlbum       = Nothing
    , sdComment     = Nothing
    , sdGenre       = Nothing
    , sdYear        = Nothing
    , sdTrackNumber = Nothing }
  mappend = (<>)

-- | Set tags in specified file using the given setter.
--
-- In the case of trouble 'I.HTagLibException' will be thrown.

setTags :: MonadIO m
  => FilePath          -- ^ Path to audio file
  -> Maybe ID3v2Encoding -- ^ Encoding for ID3v2 frames
  -> TagSetter         -- ^ Setter
  -> m ()
setTags path enc = execSetter path enc Nothing

-- | Similar to 'setTags', but you can also specify type of audio file
-- explicitly (otherwise it's guessed from file extension).

setTags' :: MonadIO m
  => FilePath          -- ^ Path to audio file
  -> Maybe ID3v2Encoding -- ^ Encoding for ID3v2 frames
  -> FileType          -- ^ Type of audio file
  -> TagSetter         -- ^ Setter
  -> m ()
setTags' path enc t = execSetter path enc (Just t)

-- | The most general way to set meta data. 'setTags' and 'setTags'' are
-- just wrappers around this function.

execSetter :: MonadIO m
  => FilePath          -- ^ Path to audio file
  -> Maybe ID3v2Encoding -- ^ Encoding for ID3v2 frames
  -> Maybe FileType    -- ^ Type of audio file (if known)
  -> TagSetter         -- ^ Setter
  -> m ()
execSetter path enc t TagSetter {..} = liftIO . I.withFile path t $ \fid -> do
  forM_ enc I.id3v2SetEncoding
  let writeTag x f = forM_ x (`f` fid)
  writeTag sdTitle       I.setTitle
  writeTag sdArtist      I.setArtist
  writeTag sdAlbum       I.setAlbum
  writeTag sdComment     I.setComment
  writeTag sdGenre       I.setGenre
  writeTag sdYear        I.setYear
  writeTag sdTrackNumber I.setTrackNumber
  I.saveFile path fid

-- | Setter for track title.

titleSetter :: Title -> TagSetter
titleSetter x = mempty { sdTitle = Just x }

-- | Setter for track artist.

artistSetter :: Artist -> TagSetter
artistSetter x = mempty { sdArtist = Just x }

-- | Setter for track album.

albumSetter :: Album -> TagSetter
albumSetter x = mempty { sdAlbum = Just x }

-- | Setter for track comment.

commentSetter :: Comment -> TagSetter
commentSetter x = mempty { sdComment = Just x }

-- | Setter for track genre.

genreSetter :: Genre -> TagSetter
genreSetter x = mempty { sdGenre = Just x }

-- | Setter for year tag, use 'Nothing' to clear the field.

yearSetter :: Maybe Year -> TagSetter
yearSetter x = mempty { sdYear = Just x }

-- | Setter for track number, use 'Nothing' to clear the field.

trackNumberSetter :: Maybe TrackNumber -> TagSetter
trackNumberSetter x = mempty { sdTrackNumber = Just x }