Seeking in liquidsoap

Starting with Liquidsoap 1.0.0-beta2, it is now possible to seek within sources! Not all sources support seeking though: currently, they are mostly file-based sources such as request.queue, playlist, request.dynamic.list etc..

The basic function to seek within a source is source.seek. It has the following type:

(source('a),float)->float

The parameters are:

  • The source to seek.
  • The duration in seconds to seek from current position.

The function returns the duration actually seeked.

Please note that seeking is done to a position relative to the current position. For instance, source.seek(s,3.) will seek 3 seconds forward in source s and source.seek(s,(-4.)) will seek 4 seconds backward.

Since seeking is currently only supported by request-based sources, it is recommended to hook the function as close as possible to the original source. Here is an example that implements a server/telnet seek function:

# A playlist source
s = playlist("/path/to/music")

# The server seeking function
def seek(t) =
  t = float_of_string(default=0., t)
  log(
    "Seeking #{t} sec"
  )
  ret = source.seek(s, t)
  "Seeked #{ret} seconds."
end

# Register the function
server.register(
  namespace=source.id(s),
  description=
    "Seek to a relative position in source #{source.id(s)}",
  usage=
    "seek <duration>",
  "seek",
  seek
)

Cue points

File-based sources support cue-points to cut the beginning and end of tracks The values of cue-in and cue-out points are given in absolute position through the source’s metadata. For instance, the following source will cue-in at 10 seconds and cue-out at 45 seconds on all its tracks:

s = playlist(prefix="annotate:liq_cue_in=\"10.\",liq_cue_out=\"45\":",
             "/path/to/music")

As in the above example, you may use the annotate protocol to pass custom cue points along with the files passed to Liquidsoap. This is particularly useful in combination with request.dynamic as an external script can build-up the appropriate URI, including cue-points, based on information from your own scheduling back-end.

Alternatively, you may use metadata.map to add those metadata. The operator metadata.map supports seeking and passes it to its underlying source.