Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add media library operator. #3115

Merged
merged 49 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
3907db4
First mockup for a media library.
smimram May 31, 2023
17a5210
Add file.mtime.
smimram May 31, 2023
9fcc81b
Quote.
smimram May 31, 2023
76e16cd
Peristency and mtime support.
smimram May 31, 2023
2b8ca49
More predicates.
smimram May 31, 2023
0212613
More modular code.
smimram Jun 1, 2023
9ddd8c0
Add list.filter_map.
smimram Jun 1, 2023
bc78544
Add log.startup.
smimram Jun 1, 2023
ef1d507
Working medialib.
smimram Jun 1, 2023
4acbef0
Style.
smimram Jun 1, 2023
92776f1
Refresh.
smimram Jun 1, 2023
be79cd4
Revert "Add log.startup."
smimram Jun 1, 2023
e44c306
Fix update.
smimram Jun 1, 2023
700eccb
Filename.
smimram Jun 1, 2023
75d16ea
Export useful methods.
smimram Jun 1, 2023
5526615
Add list.flatten.
smimram Jun 1, 2023
6a9c630
Polish.
smimram Jun 1, 2023
58d3c81
Year.
smimram Jun 1, 2023
fa3907c
Set a clock in append, unify frame types.
toots May 31, 2023
36db5f3
Fix typo.
toots May 31, 2023
1e11fed
Use a record to pass let arguments.
toots Jun 1, 2023
0d61c97
Better doc for file.basename.
smimram Jun 1, 2023
6034a1e
Merge branch 'main' into medialib
smimram Jun 1, 2023
0a4bc38
Sanitize.
smimram Jun 1, 2023
20f4eae
filename.
smimram Jun 1, 2023
a933f51
Better name.
smimram Jun 1, 2023
6dc56e8
Single and multiple dirs.
smimram Jun 1, 2023
277a724
Regenerate dune.inc.
smimram Jun 1, 2023
ab8e0e3
Changelog entry.
smimram Jun 1, 2023
c31f9bc
Refresh every hour by default.
smimram Jun 1, 2023
b97e1b4
Add some doc.
smimram Jun 1, 2023
a285083
Use native decoder only.
smimram Jun 1, 2023
a2ac5ec
Normalize.
smimram Jun 1, 2023
5aeeed4
Merge branch 'main' into medialib
smimram Jun 1, 2023
cdbe754
Show entries.
smimram Jun 1, 2023
4777cac
Cosmetic.
smimram Jun 1, 2023
1bccc30
Don't refresh on startup.
smimram Jun 1, 2023
e4d1415
Add log.
smimram Jun 2, 2023
e69c242
Standardization function.
smimram Jun 2, 2023
4dac518
Reuse code.
smimram Jun 2, 2023
e735fcd
Year comparison.
smimram Jun 2, 2023
11a5e22
BPM.
smimram Jun 2, 2023
edac876
Cleaner.
smimram Jun 2, 2023
d20f4cb
Don't refresh by default.
smimram Jun 12, 2023
76ceba6
Update doc/content/cookbook.md
smimram Jun 12, 2023
2be45af
Update doc/content/cookbook.md
smimram Jun 12, 2023
02f8be7
More doc.
smimram Jun 12, 2023
84b8f5e
Merge branch 'main' into medialib
smimram Jun 12, 2023
4bcdf82
Version persistent file.
smimram Jun 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ New:
[Pico TTS](https://github.com/naggety/picotts) (#2934).
- Added `"metadata_url"` to the default list of exported metadata (#2946)
- Added log colors!
- Added `list.filter_map` and `list.flatten`.
- Added `medialib` in order to store metadata of files in a folder and query
them (#3115).

Changed:

Expand Down
18 changes: 18 additions & 0 deletions doc/content/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,24 @@ fallback([playlist("http://my/playlist"),
switch([ ({0h-7h}, night), ({7h-24h}, day) ])
```

## Generating playlists from a media library

In order to store all the metadata of the files in a given directory and use
those to generate playlists, you can use the `medialib` operator which takes as
argument the directory to index. On first run, it will index all the files of
the given folder, which can take some time (you are advised to use the
`persistency` parameter in order to specify a file where metadata will be
stored, in order to avoid reindexing at each run). The resulting object can then
smimram marked this conversation as resolved.
Show resolved Hide resolved
be queried with the `find` method in order to return all files matching given
smimram marked this conversation as resolved.
Show resolved Hide resolved
conditions and thus generate a playlist:

```liquidsoap
m = medialib(persistency="/tmp/medialib.json", "~/Music/")
l = m.find(artist_contains="Brassens")
l = list.shuffle(l)
output(playlist.list(l))
```

smimram marked this conversation as resolved.
Show resolved Hide resolved
## Force a file/playlist to be played at least every XX minutes

It can be useful to have a special playlist that is played at least every 20 minutes for instance (3 times per hour).
Expand Down
34 changes: 17 additions & 17 deletions doc/dune.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1566,27 +1566,27 @@
(section doc)
(package liquidsoap)
(files
(orig/fosdem2020/clock.png as html/fosdem2020/clock.png)
(orig/fosdem2020/remark.js as html/fosdem2020/remark.js)
(orig/fosdem2020/radio.gif as html/fosdem2020/radio.gif)
(orig/fosdem2020/logo.png as html/fosdem2020/logo.png)
(orig/fosdem2020/index.html as html/fosdem2020/index.html)
(orig/images/grab.png as html/images/grab.png)
(orig/images/schema-webradio-inkscape.png as html/images/schema-webradio-inkscape.png)
(orig/images/liqgraph.png as html/images/liqgraph.png)
(orig/images/stream.png as html/images/stream.png)
(orig/images/graph_clocks.png as html/images/graph_clocks.png)
(orig/images/design/background_page.png as html/images/design/background_page.png)
(orig/images/design/logo.png as html/images/design/logo.png)
(orig/images/grab.png as html/images/grab.png)
(orig/images/design/background.png as html/images/design/background.png)
(orig/images/basic-radio-graph.png as html/images/basic-radio-graph.png)
(orig/images/design/logo.png as html/images/design/logo.png)
(orig/images/design/background_page.png as html/images/design/background_page.png)
(orig/images/graph_clocks.png as html/images/graph_clocks.png)
(orig/images/stream.png as html/images/stream.png)
(orig/images/schema-webradio-inkscape.png as html/images/schema-webradio-inkscape.png)
smimram marked this conversation as resolved.
Show resolved Hide resolved
(orig/images/tabs/tab_API.png as html/images/tabs/tab_API.png)
(orig/images/tabs/tab_developers.png as html/images/tabs/tab_developers.png)
(orig/images/tabs/tab_snippets.png as html/images/tabs/tab_snippets.png)
(orig/images/tabs/tab_docs.png as html/images/tabs/tab_docs.png)
(orig/images/tabs/tab_about.png as html/images/tabs/tab_about.png)
(orig/css/style.css as html/css/style.css)
(orig/images/tabs/tab_docs.png as html/images/tabs/tab_docs.png)
(orig/images/tabs/tab_snippets.png as html/images/tabs/tab_snippets.png)
(orig/images/tabs/tab_developers.png as html/images/tabs/tab_developers.png)
(orig/images/liqgraph.png as html/images/liqgraph.png)
(orig/images/basic-radio-graph.png as html/images/basic-radio-graph.png)
(orig/fosdem2020/logo.png as html/fosdem2020/logo.png)
(orig/fosdem2020/index.html as html/fosdem2020/index.html)
(orig/fosdem2020/clock.png as html/fosdem2020/clock.png)
(orig/fosdem2020/radio.gif as html/fosdem2020/radio.gif)
(orig/fosdem2020/remark.js as html/fosdem2020/remark.js)
(orig/css/homepage.css as html/css/homepage.css)
(orig/css/style.css as html/css/style.css)
(protocols.html as html/protocols.html)
(reference.html as html/reference.html)
(reference-extras.html as html/reference-extras.html)
Expand Down
24 changes: 24 additions & 0 deletions src/core/builtins/builtins_files.ml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ let _ =
Lang.int ret
with _ -> Lang.int 0)

let _ =
Lang.add_builtin ~base:file "mtime" ~category:`File
~descr:"Last modification time."
[("", Lang.string_t, None, None)]
Lang.float_t
(fun p ->
let fname = List.assoc "" p |> Lang.to_string in
try Lang.float (Unix.stat fname).st_mtime with _ -> Lang.float 0.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we return the whole stat record? Do we need user-friendly human readable attributes on the result?


let _ =
Lang.add_builtin ~base:file "mkdir" ~category:`File
~descr:"Create a directory."
Expand Down Expand Up @@ -486,6 +495,21 @@ let () =
in
Lang.metadata (Frame.metadata_of_list m)))))

let _ =
Lang.add_builtin ~base:file_metadata "native" ~category:`File
[
( "",
Lang.string_t,
None,
Some "File from which the metadata should be read." );
]
Lang.metadata_t ~descr:"Read metadata from a file using the native decoder."
(fun p ->
let file = List.assoc "" p |> Lang.to_string in
let m = try Metadata.parse_file file with _ -> [] in
let m = List.map (fun (k, v) -> (String.lowercase_ascii k, v)) m in
Lang.metadata (Frame.metadata_of_list m))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the decoder is registered as a mresolvers plugin, this should already be declared here: https://github.com/savonet/liquidsoap/blob/main/src/core/builtins/builtins_files.ml#L473


let _ =
Lang.add_builtin ~base:file "which" ~category:`File
~descr:
Expand Down
75 changes: 38 additions & 37 deletions src/js/dune.inc
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,52 @@
(:stdlib_js ./stdlib_js.liq)
(source_tree ../libs))
(action
(run js_of_ocaml build-fs -I . -I ../libs -o %{target} %{stdlib_js} visualization.liq
resolvers.liq
(run js_of_ocaml build-fs -I . -I ../libs -o %{target} %{stdlib_js} profiler.liq
externals.liq
gstreamer.liq
log.liq
io.liq
native.liq
audio.liq
http.liq
utils.liq
thread.liq
source.liq
process.liq
ref.liq
fades.liq
profiler.liq
null.liq
math.liq
socket.liq
error.liq
request.liq
string.liq
stdlib.liq
deprecations.liq
liquidsoap.liq
audio.liq
clock.liq
protocols.liq
source.liq
video.liq
ffmpeg.liq
native.liq
interactive.liq
http_codes.liq
predicate.liq
file.liq
log.liq
string.liq
replaygain.liq
process.liq
telnet.liq
getter.liq
io.liq
ffmpeg.liq
http_extra.liq
deprecations.liq
socket.liq
hls.liq
switches.liq
list.liq
telnet.liq
replaygain.liq
http.liq
metadata.liq
lastfm.liq
playlist.liq
fades.liq
ref.liq
settings.liq
lastfm.liq
testing.liq
hls.liq
runtime.liq
icecast.liq
http_extra.liq
interactive.liq
stdlib.liq
request.liq
list.liq
file.liq
liquidsoap.liq
medialib.liq
server.liq
settings.liq)))
metadata.liq
predicate.liq
math.liq
visualization.liq
resolvers.liq
clock.liq
thread.liq
protocols.liq
runtime.liq
error.liq
gstreamer.liq)))
75 changes: 38 additions & 37 deletions src/libs/dune.inc
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,52 @@
(liquidsoap-lang libs)))
(package liquidsoap-libs)
(files
visualization.liq
resolvers.liq
profiler.liq
externals.liq
gstreamer.liq
log.liq
io.liq
native.liq
audio.liq
http.liq
utils.liq
thread.liq
source.liq
process.liq
ref.liq
fades.liq
profiler.liq
null.liq
math.liq
socket.liq
error.liq
request.liq
string.liq
stdlib.liq
deprecations.liq
liquidsoap.liq
audio.liq
clock.liq
protocols.liq
source.liq
video.liq
ffmpeg.liq
native.liq
interactive.liq
http_codes.liq
predicate.liq
file.liq
log.liq
string.liq
replaygain.liq
process.liq
telnet.liq
getter.liq
io.liq
ffmpeg.liq
http_extra.liq
deprecations.liq
socket.liq
hls.liq
switches.liq
list.liq
telnet.liq
replaygain.liq
http.liq
metadata.liq
lastfm.liq
playlist.liq
fades.liq
ref.liq
settings.liq
lastfm.liq
testing.liq
hls.liq
runtime.liq
icecast.liq
http_extra.liq
interactive.liq
stdlib.liq
request.liq
list.liq
file.liq
liquidsoap.liq
medialib.liq
server.liq
settings.liq))
metadata.liq
predicate.liq
math.liq
visualization.liq
resolvers.liq
clock.liq
thread.liq
protocols.liq
runtime.liq
error.liq
gstreamer.liq))
33 changes: 32 additions & 1 deletion src/libs/list.liq
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ def list.fold.right(f, x, l)
list.ind(l, x, fun (e, _, r) -> f(e, r))
end

# Concatenate all the elements of a list of lists.
# @category List
def list.flatten(l)
list.fold(fun(l,s) -> list.append(l,s), [], l)
end

# Filter a list according to a predicate. The order in which elements are
# handled is not specified (and is currently implemented from the right).
# @category List
Expand All @@ -121,6 +127,23 @@ def list.filter(~remove=fun(_)->(), p, l)
list.ind(l, [], fun(x, _, l) -> if p(x) then list.cons(x, l) else (remove(x):unit); l end)
end

# Map a function on a list (like `list.map`) excepting that the value is removed
# if the function returns `null`.
# @category List
# @param f Function called on every element of the list.
# @param l The list.
def list.filter_map(f, l)
def f(x, _, l)
y = f(x)
if null.defined(y) then
list.cons(null.get(y), l)
else
l
end
end
list.ind(l, [], f)
end

# Associate a value to a key in an association list. This functions raises
# `error.not_found` if no default value is specified.
# @category List
Expand All @@ -147,6 +170,14 @@ def list.assoc.filter(p, l)
list.filter(p, l)
end

# Map a function of every element of the associative list, removing the entry if
# the function returns `null`.
# @category List
def list.assoc.filter_map(f, l)
def f(kv) = f(fst(kv), snd(kv)) end
list.filter_map(f, l)
end

# Remove the first pair from an associative list.
# @category List
# @param key Key of pair to be removed.
Expand Down Expand Up @@ -237,7 +268,7 @@ def list.index(p, l)
list.ind(l, 0, fun (x, _, r) -> if p(x) then 0 else r+1 end)
end

# list.assoc.mem(key,l) returns true if l contains a pair (key,value).
# `list.assoc.mem(key,l)` returns `true` if `l` contains a pair (key,value).
# @category List
# @param a Key to look for
# @param l List of pairs (key,value)
Expand Down
Loading