diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,16 +1,16 @@
-# Version 0.5.0.0 (December 1, 2017)
+# Release History
 
+## 0.5 (December 1, 2017)
+
   * Add support for track length (thanks to Catherine Galkina)
 
   * Widen dependency versions to latest available.
 
-# Version 0.4.1.0 (February 5, 2017)
-
-  * Remove redundant constraints as reported by GHC 8.X.
+  * Minor updates:
 
-  * Widen dependency versions to latest available.
+    - 0.5.1 (October 29, 2020): Update dependencies
 
-# Version 0.4.0.0 (November 21, 2016)
+## 0.4 (November 21, 2016)
 
   * Added the `resolve` function.  If you have a playlist that
     contains references to remote playlists, this new function will
@@ -24,6 +24,10 @@
     `attoparsec` parser for each playlist type.  This should make it
     easy to write space efficient functions that parse playlists.
 
-# Version 0.3.0.0 (January 24, 2015)
+  * Minor updates:
+
+    - 0.4.1 (February 5, 2017): Update dependencies
+
+## 0.3 (January 24, 2015)
 
   * Update dependencies to recent versions (thanks to Wieland Hoffmann).
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2017 Peter Jones <pjones@devalot.com>
+Copyright (c) 2013-2020 Peter Jones <pjones@devalot.com>
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
 # Haskell Playlists Library and Tool
 
+[![CI](https://github.com/pjones/playlists/workflows/CI/badge.svg)](https://github.com/pjones/playlists/actions)
+[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/pjones/playlists?label=release)](https://github.com/pjones/playlists/releases)
+[![Hackage](https://img.shields.io/hackage/v/playlists)](https://hackage.haskell.org/package/playlists)
+
 Playlists is a library for working with media playlist files.  The
 original motivation for the library was extracting URLs for streaming
 radio stations that use PLS and M3U playlist files.
@@ -9,27 +13,28 @@
 
 ## Supported Formats
 
-  * [PLS] []
-  * [M3U and M3U8] [M3U]
+  * [PLS][]
+  * [M3U and M3U8][M3U]
 
 ## Future Plans
 
 Some playlist files can be really big.  I plan on adding support for
-incremental parsing and generating via [io-streams] [] at some point
-in the future.
+incremental parsing and generating via `pipes` at some point in the
+future.
 
 ## Library Example
 
-    import qualified Data.ByteString as BS
-    import Text.Playlist
-
-    readPlaylist :: Format -> IO Playlist
-    readPlaylist fmt = do
-      content <- BS.getContents
-      case parsePlaylist fmt content of
-        Left err -> fail $ "failed to parse playlist on stdin: " ++ err
-        Right x  -> return x
+```haskell
+import qualified Data.ByteString as BS
+import Text.Playlist
 
+readPlaylist :: Format -> IO Playlist
+readPlaylist fmt = do
+  content <- BS.getContents
+  case parsePlaylist fmt content of
+    Left err -> fail $ "failed to parse playlist on stdin: " ++ err
+    Right x  -> return x
+```
 
 ## Executable Example
 
@@ -39,4 +44,3 @@
 
 [pls]: http://en.wikipedia.org/wiki/PLS_(file_format)
 [m3u]: http://en.wikipedia.org/wiki/M3U
-[io-streams]: http://hackage.haskell.org/package/io-streams
diff --git a/playlists.cabal b/playlists.cabal
--- a/playlists.cabal
+++ b/playlists.cabal
@@ -1,16 +1,17 @@
-name:          playlists
-version:       0.5.0.0
-synopsis:      Library and executable for working with playlist files.
-homepage:      https://github.com/pjones/playlists
-license:       BSD3
-license-file:  LICENSE
-author:        Peter Jones <pjones@devalot.com>
-maintainer:    Peter Jones <pjones@devalot.com>
-copyright:     Copyright (c) 2013-2016 Peter Jones
-category:      Text
-build-type:    Simple
-cabal-version: >= 1.18
-tested-with:   GHC==7.10.3, GHC==8.0.2
+cabal-version:      2.2
+name:               playlists
+version:            0.5.1
+homepage:           https://github.com/pjones/playlists
+bug-reports:        https://github.com/pjones/playlists/issues
+license:            BSD-3-Clause
+license-file:       LICENSE
+author:             Peter Jones <pjones@devalot.com>
+maintainer:         Peter Jones <pjones@devalot.com>
+copyright:          Copyright (c) 2013-2020 Peter Jones
+category:           Text
+build-type:         Simple
+tested-with:        GHC ==8.6.5 || ==8.8.4 || ==8.10.2
+synopsis:           Library and executable for working with playlist files.
 description:
   Playlists is a library for working with media playlist files.  The
   original motivation for the library was extracting URLs for
@@ -50,22 +51,28 @@
   location: https://github.com/pjones/playlists.git
 
 --------------------------------------------------------------------------------
-flag maintainer
-  description: Enable settings for the package maintainer.
-  default: False
-  manual: True
+common dependencies
+  build-depends:
+    , attoparsec  >=0.10 && <0.14
+    , base        >=4.6  && <5
+    , bytestring  >=0.10 && <0.12
+    , filepath    >=1.3  && <1.5
+    , text        >=0.11 && <1.3
+    , word8       >=0.0  && <0.2
 
-------------------------------------------------------------------------------
--- You can disable the doctest test suite with -f-test-doctest
-flag test-doctest
-  description: Run the source code through doctest when testing.
-  default: True
-  manual: True
+--------------------------------------------------------------------------------
+common options
+  default-language: Haskell2010
+  ghc-options:
+    -Wall -Wno-name-shadowing -Werror=incomplete-record-updates
+    -Werror=incomplete-uni-patterns -Werror=missing-home-modules
+    -Widentities -Wmissing-export-lists -Wredundant-constraints
 
 --------------------------------------------------------------------------------
 library
-  exposed-modules:
-    Text.Playlist
+  import:          dependencies, options
+  hs-source-dirs:  src
+  exposed-modules: Text.Playlist
   other-modules:
     Text.Playlist.Internal.Attoparsec
     Text.Playlist.Internal.Format
@@ -77,45 +84,21 @@
     Text.Playlist.PLS.Writer
     Text.Playlist.Types
 
-  default-language: Haskell2010
-  hs-source-dirs: src
-  ghc-options: -Wall
-
-  if flag(maintainer)
-    ghc-options: -Werror
-
-  build-depends: base       >= 4.6   && < 5
-               , attoparsec >= 0.10  && < 1.0
-               , bytestring >= 0.10  && < 1.0
-               , text       >= 0.11  && < 1.3
-               , word8      >= 0.0   && < 1.0
-               , filepath   >= 1.3   && < 2.0
-
 --------------------------------------------------------------------------------
 executable playlist
-  default-language: Haskell2010
+  import:         dependencies, options
   hs-source-dirs: util
-  main-is: playlist.hs
-  ghc-options: -Wall -rtsopts
-
-  if flag(maintainer)
-    ghc-options: -Werror
-    ghc-prof-options: -fprof-auto-top -fprof-cafs
-
-  build-depends: base
-               , bytestring
-               , optparse-applicative >= 0.10 && < 0.15
-               , playlists
-               , text
+  main-is:        playlist.hs
+  build-depends:
+    , optparse-applicative  >=0.10 && <0.17
+    , playlists
 
 --------------------------------------------------------------------------------
-test-suite spec
-  type: exitcode-stdio-1.0
-  default-language: Haskell2010
+test-suite test
+  import:         dependencies, options
+  type:           exitcode-stdio-1.0
   hs-source-dirs: test
-  main-is: Main.hs
-  ghc-options: -Wall
-
+  main-is:        Main.hs
   other-modules:
     Examples
     Helper
@@ -123,26 +106,16 @@
     PLSSpec
     ResolveSpec
 
-  if flag(maintainer)
-    ghc-options: -Werror
-
-  build-depends: base
-               , bytestring
-               , filepath
-               , hspec >= 1.4.0
-               , playlists
-               , text
+  build-depends:
+    , hspec      >=1.4.0
+    , playlists
 
 ------------------------------------------------------------------------------
-test-suite doctests
-  type: exitcode-stdio-1.0
-  default-language: Haskell2010
-  hs-source-dirs: test
-  main-is: doctest.hs
-  ghc-options: -threaded
-
-  if !flag(test-doctest)
-    buildable: False
-  else
-    build-depends: base
-                 , doctest >= 0.9.11
+-- test-suite doctests
+--   import:           dependencies, options
+--   type:             exitcode-stdio-1.0
+--   default-language: Haskell2010
+--   hs-source-dirs:   test
+--   main-is:          doctest.hs
+--   ghc-options:      -threaded
+--   build-depends:    doctest >=0.9.11 && <0.18
diff --git a/src/Text/Playlist/M3U/Reader.hs b/src/Text/Playlist/M3U/Reader.hs
--- a/src/Text/Playlist/M3U/Reader.hs
+++ b/src/Text/Playlist/M3U/Reader.hs
@@ -38,7 +38,7 @@
 parseTrack :: Parser Track
 parseTrack = do
   -- Get the length and title closest to the URL or Nothing.
-  (title, len) <- maybeTitleAndLength . reverse <$> (many' commentOrDirective)
+  (title, len) <- maybeTitleAndLength . reverse <$> many' commentOrDirective
   url   <- parseURL
   return Track { trackURL      = url
                , trackTitle    = title
@@ -73,7 +73,6 @@
       directive = do
         mlen <- (Just . realToFrac <$> signed double) <|> return Nothing -- Parse length.
         skip (== 44)                                                     -- Skip comma.
-        mtext <- (Just . decodeUtf8 <$> takeWhile1 (not . isEOL)) <|> (return Nothing)
+        mtext <- (Just . decodeUtf8 <$> takeWhile1 (not . isEOL)) <|> return Nothing
         skipLine
         return (Just (mtext, mlen))
-
diff --git a/src/Text/Playlist/M3U/Writer.hs b/src/Text/Playlist/M3U/Writer.hs
--- a/src/Text/Playlist/M3U/Writer.hs
+++ b/src/Text/Playlist/M3U/Writer.hs
@@ -18,7 +18,6 @@
 --------------------------------------------------------------------------------
 import Data.ByteString.Lazy.Builder (Builder)
 import qualified Data.ByteString.Lazy.Builder as B
-import Data.Monoid
 import Data.Text (Text)
 import Data.Text.Encoding (encodeUtf8)
 import Text.Playlist.Types
@@ -53,4 +52,3 @@
 writeTitle :: Maybe Text -> Builder
 writeTitle Nothing = mempty
 writeTitle (Just x) = B.byteString (encodeUtf8 x)
-
diff --git a/src/Text/Playlist/PLS/Writer.hs b/src/Text/Playlist/PLS/Writer.hs
--- a/src/Text/Playlist/PLS/Writer.hs
+++ b/src/Text/Playlist/PLS/Writer.hs
@@ -20,7 +20,6 @@
 --------------------------------------------------------------------------------
 import Data.ByteString.Lazy.Builder (Builder)
 import qualified Data.ByteString.Lazy.Builder as B
-import Data.Monoid
 import Data.Text.Encoding (encodeUtf8)
 import Text.Playlist.Types
 
@@ -67,4 +66,3 @@
                B.charUtf8 '='        <>
                B.stringUtf8 (show l) <>
                B.charUtf8 '\n'
-
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -10,7 +10,7 @@
 -}
 
 --------------------------------------------------------------------------------
-module Main where
+module Main (main) where
 
 --------------------------------------------------------------------------------
 import Test.Hspec
diff --git a/test/doctest.hs b/test/doctest.hs
deleted file mode 100644
--- a/test/doctest.hs
+++ /dev/null
@@ -1,36 +0,0 @@
-{-
-
-This file is part of the Haskell package playlists. It is subject to
-the license terms in the LICENSE file found in the top-level directory
-of this distribution and at git://pmade.com/playlists/LICENSE. No part
-of playlists package, including this file, may be copied, modified,
-propagated, or distributed except according to the terms contained in
-the LICENSE file.
-
--}
-
---------------------------------------------------------------------------------
--- | Wrapper around doctest to make sure examples in the source code work.
-module Main (main) where
-
---------------------------------------------------------------------------------
-import Control.Applicative
-import System.Environment
-import Test.DocTest
-
---------------------------------------------------------------------------------
--- Totally stupid right now.  Will search the directory later.
-files :: IO [String]
-files = fmap ("util/playlist.hs" :) files' where
-  files' = return ["src/Text/Playlist.hs"]
-
---------------------------------------------------------------------------------
--- | GHC flags needed by doctest.  Would be better to fetch these from
--- cabal directly though.
-flags :: [String]
-flags = ["-isrc"]
-
---------------------------------------------------------------------------------
--- | Check the docs.
-main :: IO ()
-main = doctest =<< (++) <$> getArgs <*> fmap (flags ++) files
diff --git a/util/playlist.hs b/util/playlist.hs
--- a/util/playlist.hs
+++ b/util/playlist.hs
@@ -10,12 +10,11 @@
 -}
 
 --------------------------------------------------------------------------------
-module Main where
+module Main (main) where
 
 --------------------------------------------------------------------------------
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BL
-import Data.Monoid
 import qualified Data.Text as T
 import Options.Applicative
 import Text.Playlist
