diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,11 @@
+## Version 0.49 (April, 2025)
+
+- New plugins: `ArchUpdates` and `Accordion` (thanks, Enrico Maria)
+- New template variable `weatherS` for `WeatherX`.
+- New Nix flake (thanks, refaelsh)
+- `MPRIS`: Fix for duration reported by Spotify (thanks, Claudio)
+- Base dep up to 4.20 (thanks, Alexander)
+
 ## Version 0.48.1 (May, 2024)
 
 - Removing for now mouse support for `Kbd`, which can be recovered by
diff --git a/doc/plugins.org b/doc/plugins.org
--- a/doc/plugins.org
+++ b/doc/plugins.org
@@ -1268,9 +1268,11 @@
     - Works in the same way as =Weather=, but takes an additional argument,
       a list of pairs from sky conditions to their replacement (typically a
       unicode string or an icon specification).
-    - Use the variable =skyConditionS= to display the replacement of the
-      corresponding sky condition. All other =Weather= template variables
-      are available as well.
+    - Use the variable =skyConditionS= or =weatherS= to display the replacement of
+      the corresponding sky condition. =weatherS= uses the string returned by
+      =weather= to look up a replacement, and, if that one is not found, it
+      looks up the value of =skyConditionS=. All other =Weather= template
+      variables are available as well.
 
       For example:
 
@@ -1321,7 +1323,7 @@
       #+begin_src haskell
         Run UVMeter "Brisbane" ["-H", "3", "-L", "3", "--low", "green", "--high", "red"] 900
       #+end_src
-** Other monitors
+** Other monitors and plugins
 *** =CatInt n filename=
 
     - Reads and displays an integer from the file whose path is =filename=
@@ -1351,6 +1353,43 @@
       the display of those numeric fields.
     - Default template: =Up: <days>d <hours>h <minutes>m=
 
+*** =ArchUpdates (Zero, One, Many) Rate=
+
+  - Aliases to =arch=
+  - =Zero=: a =String= to use when the system is up to date.
+  - =One=: a =String= to use when only one update is available.
+  - =Many=: a =String= to use when several updates are available; it must contain
+    a =?= character as a placeholder for the number of updates.
+  - Example:
+    #+begin_src haskell
+      ArchUpdates ("<fc=green>up to date</fc>",
+                   "<fc=yellow>1 update</fc>,
+                   "<fc=red>? updates</fc>")
+                   600
+    #+end_src
+
+*** =makeAccordion Tuning [Runnable]=
+
+  - Wraps other =Runnable= plugins and makes them all collapsible to a single string:
+
+    [[file:accordion.gif]]
+
+  - Aliases to =alias' tuning=, being =tuning= of type =Tuning=, so one can use multiple such "accordions"
+  - **Disclaimer**: This only works for Haskell =xmobar.hs=
+  - =Tuning=: the "settings", for which a default value is provided,
+    #+begin_src haskell
+      defaultTuning = Tuning {
+          alias' = "accordion"
+        , initial = True
+        , expand = "<>"
+        , shrink = "><"
+      }
+    #+end_src
+  - =expand=: =String= shown when the accordion is contracted (defaults to ="<>"=).
+  - =shrink=: =String= shown when the accordion is expanded (defaults to ="><"=).
+  - =initial=: =Bool= to tell whether the accordion is initially expanded (defaults to =True=).
+  - =[Runnable]=: a list of =Runnable= plugins
+
 * Interfacing with window managers
   :PROPERTIES:
   :CUSTOM_ID: interfacing-with-window-managers
@@ -1574,8 +1613,6 @@
       updates. It is thus necessary to enable =-threaded= when compiling
       xmonad configuration (=xmonad.hs=), e.g. by using a custom
       =~/.xmonad/build= script.
-
-
 
 * Executing external commands
 
diff --git a/readme.org b/readme.org
--- a/readme.org
+++ b/readme.org
@@ -5,9 +5,6 @@
   <a href="http://hackage.haskell.org/package/xmobar">
     <img src="https://img.shields.io/hackage/v/xmobar.svg" alt="hackage"/>
   </a>
-  <a href="https://ci.codeberg.org/xmobar/xmobar">
-    <img src="https://ci.codeberg.org/api/badges/xmobar/xmobar/status.svg" alt="ci"/>
-  </a>
 </p>
 #+end_export
 
@@ -203,6 +200,6 @@
   This software is released under a BSD-style license. See [[https://codeberg.org/xmobar/xmobar/src/branch/master/license][license]] for more
   details.
 
-  Copyright © 2010-2024 Jose Antonio Ortega Ruiz
+  Copyright © 2010-2025 Jose Antonio Ortega Ruiz
 
   Copyright © 2007-2010 Andrea Rossato
diff --git a/src/Xmobar.hs b/src/Xmobar.hs
--- a/src/Xmobar.hs
+++ b/src/Xmobar.hs
@@ -26,6 +26,8 @@
               , SignalType (..)
               , module Xmobar.Config.Types
               , module Xmobar.Config.Parse
+              , module Xmobar.Plugins.Accordion
+              , module Xmobar.Plugins.ArchUpdates
               , module Xmobar.Plugins.BufferedPipeReader
               , module Xmobar.Plugins.CommandReader
               , module Xmobar.Plugins.Date
@@ -53,6 +55,8 @@
 import Xmobar.Run.Exec
 import Xmobar.Config.Types
 import Xmobar.Config.Parse
+import Xmobar.Plugins.Accordion
+import Xmobar.Plugins.ArchUpdates
 import Xmobar.Plugins.Command
 import Xmobar.Plugins.BufferedPipeReader
 import Xmobar.Plugins.CommandReader
diff --git a/src/Xmobar/App/Opts.hs b/src/Xmobar/App/Opts.hs
--- a/src/Xmobar/App/Opts.hs
+++ b/src/Xmobar/App/Opts.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------------
 -- |
 -- Module: Xmobar.App.Opts
--- Copyright: (c) 2018, 2019, 2020, 2022, 2023, 2024 Jose Antonio Ortega Ruiz
+-- Copyright: (c) 2018, 2019, 2020, 2022, 2023, 2024, 2025 Jose Antonio Ortega Ruiz
 -- License: BSD3-style (see LICENSE)
 --
 -- Maintainer: jao@gnu.org
@@ -116,7 +116,7 @@
 
 info :: String
 info = "xmobar " ++ showVersion version
-        ++ "\n (C) 2010 - 2024 Jose A Ortega Ruiz"
+        ++ "\n (C) 2010 - 2025 Jose A Ortega Ruiz"
         ++ "\n (C) 2007 - 2010 Andrea Rossato\n "
         ++ mail ++ "\n" ++ license ++ "\n"
 
diff --git a/src/Xmobar/Plugins/Accordion.hs b/src/Xmobar/Plugins/Accordion.hs
new file mode 100644
--- /dev/null
+++ b/src/Xmobar/Plugins/Accordion.hs
@@ -0,0 +1,97 @@
+{-# LANGUAGE TupleSections, FlexibleContexts #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Plugins.Monitors.Accordion
+-- Copyright   :  (c) 2024 Enrico Maria De Angelis
+-- License     :  BSD-style (see LICENSE)
+--
+-- Maintainer  :  Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com>
+-- Stability   :  unstable
+-- Portability :  unportable
+--
+-- A plugin to group adjacent plugins and make them, as a whole, shrinkable to
+-- an alternate text upon clicking.
+--
+-----------------------------------------------------------------------------
+
+module Xmobar.Plugins.Accordion (defaultTuning, makeAccordion, Tuning(..)) where
+
+import Control.Concurrent.Async (withAsync)
+import Control.Exception (finally)
+import Control.Monad (forever, join, when)
+import Control.Monad.IO.Class (liftIO)
+import Control.Monad.Reader (runReaderT, ask)
+import Control.Monad.State.Strict (evalStateT, get, modify')
+import Data.IORef (atomicModifyIORef', newIORef, readIORef)
+import Data.Maybe (isJust)
+import System.Directory (removeFile)
+import System.Exit (ExitCode(..))
+import System.Process (readProcessWithExitCode)
+import Xmobar.Run.Exec (Exec(..), tenthSeconds)
+
+-- TODO: Ideally, I'd have just `Accordion`, and not `Tuning`, but since
+-- `Accordion` is polymorphic, I can't have a `defaultAccordion` constructor
+-- with `plugins = []`, because that leaves  `a` undetermined.
+-- So I have move all non-polymorphic typed members in `Tuning`, allowing for
+-- default values at least for those members.
+data Accordion a = Accordion {
+    tuning :: Tuning
+  , plugins :: [a]
+} deriving (Show, Read)
+
+makeAccordion :: Exec a => Tuning -> [a] -> Accordion a
+makeAccordion t rs = Accordion { tuning = t, plugins = rs }
+
+data Tuning = Tuning {
+    alias' :: String
+  , initial :: Bool
+  , expand :: String
+  , shrink :: String
+} deriving (Read, Show)
+
+defaultTuning :: Tuning
+defaultTuning = Tuning {
+    alias' = "accordion"
+  , initial = True
+  , expand = "<>"
+  , shrink = "><"
+}
+
+instance (Exec a, Read a, Show a) => Exec (Accordion a) where
+  alias (Accordion Tuning { alias' = name } _) = name
+  start (Accordion Tuning { initial = initial'
+                          , expand = expand'
+                          , shrink = shrink' }
+                   runnables)
+        cb = do
+    clicked <- newIORef Nothing
+    (_, n, _) <- readProcessWithExitCode "uuidgen" [] ""
+    let pipe = "/tmp/accordion-" ++ removeLinebreak n
+    (_, _, _) <- readProcessWithExitCode "mkfifo" [pipe] ""
+    withAsync (forever $ do (ret, _, _) <- readProcessWithExitCode "cat" [pipe] ""
+                            case ret of
+                              ExitSuccess -> atomicModifyIORef' clicked (const (Just (), ()))
+                              ExitFailure _ -> error "how is this possible?")
+              (const $ do
+                  srefs <- mapM (newIORef . const "") runnables
+                  foldr (\(runnable, sref) acc -> withAsync (start runnable (writeToRef sref)) (const acc))
+                        (forever (do liftIO (tenthSeconds 1)
+                                     clicked' <- liftIO $ readIORef clicked
+                                     when (isJust clicked')
+                                          (do liftIO $ clear clicked
+                                              modify' not)
+                                     b <- get
+                                     if b then loop pipe else liftIO $ cb (click pipe expand'))
+                                 `runReaderT` srefs `evalStateT` initial')
+                        (zip runnables srefs))
+      `finally` removeFile pipe
+    where
+      click file icon = "<action=`echo 1 > " ++ file ++ "`>" ++ icon ++ "</action>"
+      clear = (`atomicModifyIORef'` const (Nothing, ()))
+      removeLinebreak = init
+      writeToRef strRef = atomicModifyIORef' strRef . const . (,())
+      loop p = do
+        srefs <- ask
+        text <- join <$> mapM (liftIO . readIORef) srefs
+        liftIO $ cb $ text ++ click p shrink'
diff --git a/src/Xmobar/Plugins/ArchUpdates.hs b/src/Xmobar/Plugins/ArchUpdates.hs
new file mode 100644
--- /dev/null
+++ b/src/Xmobar/Plugins/ArchUpdates.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE CPP #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Plugins.Monitors.ArchUpdates
+-- Copyright   :  (c) 2024 Enrico Maria De Angelis
+-- License     :  BSD-style (see LICENSE)
+--
+-- Maintainer  :  Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com>
+-- Stability   :  unstable
+-- Portability :  unportable
+--
+-- An ArchLinux updates availablility plugin for Xmobar
+--
+-----------------------------------------------------------------------------
+
+module Xmobar.Plugins.ArchUpdates (ArchUpdates(..)) where
+
+import System.Exit (ExitCode(..))
+import System.Process (readProcessWithExitCode)
+import Xmobar.Run.Exec
+import Xmobar.Plugins.Command (Rate)
+
+data ArchUpdates = ArchUpdates (String, String, String) Rate
+  deriving (Read, Show)
+
+instance Exec ArchUpdates where
+    alias (ArchUpdates _ _) = "arch"
+    rate (ArchUpdates _ r) = r
+    run (ArchUpdates (z, o, m) _) = do
+      (exit, stdout, _) <- readProcessWithExitCode "checkupdates" [] ""
+      return $ case exit of
+        ExitFailure 2 -> z--ero updates
+        ExitFailure 1 -> "pacman: Unknown cause of failure."
+        ExitSuccess -> case length $ lines stdout of
+          0 -> impossible
+          1 -> o
+          n -> m >>= \c -> if c == '?' then show n else pure c
+        _ -> impossible
+        where
+          impossible = error "This is impossible based on pacman manpage"
diff --git a/src/Xmobar/Plugins/MarqueePipeReader.hs b/src/Xmobar/Plugins/MarqueePipeReader.hs
--- a/src/Xmobar/Plugins/MarqueePipeReader.hs
+++ b/src/Xmobar/Plugins/MarqueePipeReader.hs
@@ -60,7 +60,7 @@
         Nothing -> tenthSeconds rate >> writer (drop 1 txt) sep len rate chan cb
 
 toInfTxt :: String -> String -> String
-toInfTxt line sep = concat (repeat $ line ++ " " ++ sep ++ " ")
+toInfTxt line sep = cycle (line ++ " " ++ sep ++ " ")
 
 checkPipe :: FilePath -> IO ()
 checkPipe file = handle (\(SomeException _) -> waitForPipe) $ do
diff --git a/src/Xmobar/Plugins/Monitors/Mpris.hs b/src/Xmobar/Plugins/Monitors/Mpris.hs
--- a/src/Xmobar/Plugins/Monitors/Mpris.hs
+++ b/src/Xmobar/Plugins/Monitors/Mpris.hs
@@ -28,7 +28,7 @@
 import Control.Arrow ((***))
 import Data.Maybe ( fromJust )
 import Data.Int ( Int32, Int64 )
-import Data.Word ( Word32 )
+import Data.Word ( Word32, Word64 )
 import System.IO.Unsafe ( unsafePerformIO )
 
 import Control.Exception (try)
@@ -136,17 +136,17 @@
                              "xesam:trackNumber" -> printf "%02d" num
                              _ -> (show::Int32 -> String) num
             pw32 v = printf "%02d" (fromVar v::Word32)
-            plen str v = let num = fromVar v in
-                           case str of
+            plen str num = case str of
                              "mpris:length" -> formatTime (num `div` 1000000)
-                             _ -> (show::Int64 -> String) num
+                             _ -> show num
             getStr str = case lookup str md of
                 Nothing -> ""
                 Just v -> case variantType v of
                             TypeString -> fromVar v
                             TypeInt32 -> pInt str v
                             TypeWord32 -> pw32 v
-                            TypeInt64 -> plen str v
+                            TypeWord64 -> plen str (fromVar v :: Word64)
+                            TypeInt64 -> plen str (fromVar v :: Int64)
                             TypeArray TypeString ->
                               let x = arrayItems (fromVar v) in
                                 if null x then "" else fromVar (head x)
diff --git a/src/Xmobar/Plugins/Monitors/Top.hs b/src/Xmobar/Plugins/Monitors/Top.hs
--- a/src/Xmobar/Plugins/Monitors/Top.hs
+++ b/src/Xmobar/Plugins/Monitors/Top.hs
@@ -3,7 +3,7 @@
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Plugins.Monitors.Top
--- Copyright   :  (c) 2010, 2011, 2012, 2013, 2014, 2018, 2022 Jose A Ortega Ruiz
+-- Copyright   :  (c) 2010-2014, 2018, 2022, 2025 Jose A Ortega Ruiz
 -- License     :  BSD-style (see LICENSE)
 --
 -- Maintainer  :  Jose A Ortega Ruiz <jao@gnu.org>
@@ -20,7 +20,7 @@
 
 import Data.IORef (newIORef, readIORef, writeIORef)
 import Data.List (sortBy)
-import Data.Ord (comparing)
+import Data.Ord (comparing, Down (..))
 import Data.Time.Clock (getCurrentTime, diffUTCTime)
 
 import Xmobar.Plugins.Monitors.Top.Common (
@@ -66,7 +66,7 @@
 
 
 sortTop :: [(String, Float)] -> [(String, Float)]
-sortTop =  sortBy (flip (comparing snd))
+sortTop =  sortBy (comparing (Down . snd))
 
 showMemInfo :: Float -> MemInfo -> Monitor [String]
 showMemInfo scale (nm, rss) =
diff --git a/src/Xmobar/Plugins/Monitors/Weather.hs b/src/Xmobar/Plugins/Monitors/Weather.hs
--- a/src/Xmobar/Plugins/Monitors/Weather.hs
+++ b/src/Xmobar/Plugins/Monitors/Weather.hs
@@ -66,6 +66,7 @@
        , "skyCondition"
        , "skyConditionS"
        , "weather"
+       , "weatherS"
        , "tempC"
        , "tempF"
        , "dewPointC"
@@ -221,23 +222,23 @@
     errHandler :: CE.SomeException -> IO String
     errHandler _ = return "<Could not retrieve data>"
 
-formatSk :: Eq p => [(p, p)] -> p -> p
-formatSk ((a,b):sks) sk = if a == sk then b else formatSk sks sk
-formatSk [] sk = sk
-
 formatWeather
     :: WeatherOpts        -- ^ Formatting options from the cfg file
     -> [(String,String)]  -- ^ 'SkyConditionS' for 'WeatherX'
     -> [WeatherInfo]      -- ^ The actual weather info
     -> Monitor String
-formatWeather opts sks [WI st ss y m d h (WindInfo wc wa wm wk wkh wms) v sk we tC tF dC dF r p] =
-    do cel <- showWithColors show tC
+formatWeather opts sks [WI st ss y m d h wind v sk we tC tF dC dF r p] =
+    do let WindInfo wc wa wm wk wkh wms = wind
+       cel <- showWithColors show tC
        far <- showWithColors show tF
-       let sk' = formatSk sks (map toLower sk)
-           we' = showWeather (weatherString opts) we
+       let we' = showWeather (weatherString opts) we
+           sk' = findSk sks (map toLower sk) we'
+           we'' = findSk sks (map toLower we') sk'
        parseTemplate [st, ss, y, m, d, h, wc, wa, wm, wk, wkh
-                     , wms, v, sk, sk', we', cel, far
+                     , wms, v, sk, sk', we', we'', cel, far
                      , show dC, show dF, show r , show p ]
+       where findSk ((a,b):xs) x df = if a == x then b else findSk xs x df
+             findSk [] _ df = df
 formatWeather _ _ _ = getConfigValue naString
 
 -- | Show the 'weather' field with a default string in case it was empty.
diff --git a/src/Xmobar/Run/Types.hs b/src/Xmobar/Run/Types.hs
--- a/src/Xmobar/Run/Types.hs
+++ b/src/Xmobar/Run/Types.hs
@@ -19,6 +19,7 @@
 module Xmobar.Run.Types(runnableTypes) where
 
 import {-# SOURCE #-} Xmobar.Run.Runnable()
+import Xmobar.Plugins.ArchUpdates
 import Xmobar.Plugins.Command
 import Xmobar.Plugins.Monitors
 import Xmobar.Plugins.Date
@@ -59,6 +60,7 @@
 runnableTypes :: Command :*: Monitors :*: Date :*: PipeReader :*:
                  BufferedPipeReader :*: CommandReader :*: StdinReader :*:
                  XMonadLog :*: EWMH :*: Kbd :*: Locks :*: NotmuchMail :*:
+                 ArchUpdates :*:
 #ifdef INOTIFY
                  Mail :*: MBox :*:
 #endif
diff --git a/src/Xmobar/X11/Loop.hs b/src/Xmobar/X11/Loop.hs
--- a/src/Xmobar/X11/Loop.hs
+++ b/src/Xmobar/X11/Loop.hs
@@ -170,7 +170,7 @@
 runActions :: D.Actions -> A.Button -> X11.Position -> IO ()
 runActions actions button pos =
   mapM_ A.runAction $
-   filter (\(A.Spawn b _) -> button `elem` b) $
-   concatMap (\(a,_,_) -> a) $
-   filter (\(_, from, to) -> pos' >= from && pos' <= to) actions
+   concatMap
+    (filter (\ (A.Spawn b _) -> button `elem` b) . (\ (a, _, _) -> a))
+    (filter (\ (_, from, to) -> pos' >= from && pos' <= to) actions)
   where pos' = fromIntegral pos
diff --git a/xmobar.cabal b/xmobar.cabal
--- a/xmobar.cabal
+++ b/xmobar.cabal
@@ -1,5 +1,5 @@
 name:               xmobar
-version:            0.48.1
+version:            0.49
 homepage:           https://codeberg.org/xmobar/xmobar
 synopsis:           A Minimalistic Text Based Status Bar
 description: 	    Xmobar is a minimalistic text based status bar.
@@ -105,6 +105,7 @@
     hs-source-dirs:  src
 
     exposed-modules: Xmobar,
+                     Xmobar.Plugins.Accordion,
                      Xmobar.Plugins.Monitors.Common.Types,
                      Xmobar.Plugins.Monitors.Common.Run,
                      Xmobar.Plugins.Monitors.Common,
@@ -149,6 +150,7 @@
                    Xmobar.X11.Text,
                    Xmobar.X11.Types,
                    Xmobar.X11.Window,
+                   Xmobar.Plugins.ArchUpdates,
                    Xmobar.Plugins.Command,
                    Xmobar.Plugins.BufferedPipeReader,
                    Xmobar.Plugins.CommandReader,
@@ -198,7 +200,7 @@
     build-depends:
                   aeson >= 1.4.7.1,
                   async,
-                  base >= 4.11.0 && < 4.20,
+                  base >= 4.11.0 && < 4.21,
                   bytestring >= 0.10.8.2,
                   cairo >= 0.13,
                   colour >= 2.3.6,
