func (i *Info) snapshotPosition() {
	now := timing.Now()
	elapsed := now.Sub(i.lastUpdated)
	i.lastPosition += time.Duration(float64(elapsed) * i.rate)
	i.lastUpdated = now
}
snapshotPosition snapshots the playback position,
useful when updates to rate or playback status would yield incorrect results.
package media
import (
	"reflect"
	"strings"
	"time"
	"barista.run/timing"
	"github.com/godbus/dbus/v5"
)
func (i Info) Paused() bool { return i.PlaybackStatus == Paused }
func (i Info) Playing() bool { return i.PlaybackStatus == Playing }
func (i Info) Stopped() bool { return i.PlaybackStatus == Stopped }
func (i Info) Connected() bool { return i.PlaybackStatus != Disconnected }
func (i Info) Position() time.Duration {
	if i.PlaybackStatus == Paused {
		return i.lastPosition
	}
	elapsed := timing.Now().Sub(i.lastUpdated)
	return i.lastPosition + time.Duration(float64(elapsed)*i.rate)
}
func (i Info) TruncatedPosition(unit string) string {
	dur, err := time.ParseDuration("1" + unit)
	if err != nil {
		dur = time.Second
	}
	rounded := (i.Position() + dur/2) / dur * dur
	s := rounded.String()
	if i := strings.LastIndex(s, unit); i != -1 {
		return s[0 : i+len(unit)]
	}
	return s
}
func (i Info) Play() { i.call("Play") }
func (i Info) Pause() { i.call("Pause") }
func (i Info) PlayPause() { i.call("PlayPause") }
func (i Info) Stop() { i.call("Stop") }
func (i Info) Next() { i.call("Next") }
func (i Info) Previous() { i.call("Previous") }
func (i Info) Seek(offset time.Duration) {
	i.call("Seek", int64(offset/time.Microsecond))
}
func (i *Info) set(key string, value interface{}) {
	switch key {
	case "Rate":
		i.snapshotPosition()
		i.rate = getDouble(value)
	case "Position":
		i.lastUpdated = timing.Now()
		i.lastPosition = time.Duration(getDouble(value)) * time.Microsecond
	case "Shuffle":
		i.Shuffle, _ = value.(bool)
	case "PlaybackStatus":
		status, _ := value.(string)
		i.updatePlaybackStatus(status)
	case "Metadata":
		metadata, _ := value.(map[string]dbus.Variant)
		i.updateMetadata(metadata)
	}
}