package shakuhachi
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
A music collection manager
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.0_1.tar.gz
sha256=541a7ac078a520730a71edf4d2fe0f17cc98f0b9ad82060db62120a7d2519ae5
sha512=37b98cb52dab8293adb92f71e91394cf1c5d0d481d03893c320cc8aa550459a30dabdfdc0018f512d3a7a547f2899b493a20d86f450bc084b78e30b40234a911
doc/src/shakuhachi.query/queryMusic.ml.html
Source file queryMusic.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233(*****************************************************************************) (* *) (* Copyright (C) 2026 Yves Ndiaye *) (* *) (* This Source Code Form is subject to the terms of the Mozilla Public *) (* License, v. 2.0. If a copy of the MPL was not distributed with this *) (* file, You can obtain one at https://mozilla.org/MPL/2.0/. *) (* *) (*****************************************************************************) module Metadata = SkhcMetadata.Metadata module Ids = SkhcDb.Ids module Roles = SkhcDb.Roles include QueryUtil let matches_node substrings (music_file, album, participants, properties) node = let metadata = SkhcMetadata.Metadata.init None [||] properties in let album_name = SkhcDb.Album.name album in let id = SkhcDb.MusicFile.id music_file in let path = SkhcDb.MusicFile.path music_file in let extension = Filename.extension path in let size = SkhcDb.MusicFile.size music_file in let play_count = SkhcDb.MusicFile.play_count music_file in let flag = SkhcDb.MusicFile.flag music_file in let rating = SkhcDb.MusicFile.rating music_file in let duration = SkhcDb.MusicFile.duration music_file in let has_cover = SkhcDb.MusicFile.has_cover music_file in let title = Metadata.title metadata in let sort_name = Metadata.title_sort metadata in let track_number = Metadata.track metadata in let disc_number = Metadata.disc metadata in let year = Metadata.year metadata in let genre = Metadata.genre metadata in match node with | Node.Substring s -> let fields = match_participants participants in let fields = Substrings.( fields |> add_field_if_some Title title |> add_field_if_some SortName sort_name |> add Album album_name |> add_field_if_some Genre genre |> add FileExtension extension |> add Path path |> filter (fun key _ -> List.mem key substrings) |> bindings |> List.map snd ) in List.exists (matches ~insensible:true false s) fields | Field (FString s) -> ( let regex = Field.String.regex s in let insensible = Field.String.insensible s in let field = Field.String.key s in let value = Field.String.value s in match field with | Title -> omatches ~insensible regex value title | SortName -> omatches ~insensible regex value sort_name | Artist -> Ids.Map.exists (fun _ (artist_name, roles) -> Roles.mem Artist roles && matches ~insensible regex value artist_name ) participants | Album -> matches ~insensible regex value album_name | AlbumArtist -> Ids.Map.exists (fun _ (artist_name, roles) -> Roles.mem AlbumArtist roles && matches ~insensible regex value artist_name ) participants | Composer -> Ids.Map.exists (fun _ (artist_name, roles) -> Roles.mem Composer roles && matches ~insensible regex value artist_name ) participants | Genre -> omatches ~insensible regex value genre | Path -> matches ~insensible regex value path | FileExtension -> matches ~insensible regex value extension | Arbitrary key -> let key = String.uppercase_ascii key in let mvalues = Metadata.property_list key metadata in List.exists (matches ~insensible regex value) mvalues ) | Field (FBool b) -> ( let field = Field.Bool.key b in let b = Field.Bool.value b in match field with Art -> b = has_cover | Flag -> flag = b ) | Field (FInt64 i64) -> ( let field = Field.Int64.key i64 in let frange = Field.Int64.value i64 in match field with | Id -> Range.matches frange id | Size -> Range.matches frange (Int64.of_int size) ) | Field (FInt i) -> ( let field = Field.Int.key i in let frange = Field.Int.value i in match field with | Track -> track_number |> Option.map (Range.matches frange) |> Option.value ~default:false | Year -> year |> Option.map (Range.matches frange) |> Option.value ~default:false | Disc -> Option.fold ~none:false ~some:(Range.matches frange) disc_number | Plays -> Range.matches frange play_count | Length -> Range.matches frange duration | Rating -> rating |> Option.map (Range.matches frange) |> Option.value ~default:false | AlbumCount -> Range.matches frange 1 ) | Field (FFloat f) -> ( let field = Field.Float.key f in let frange = Field.Float.value f in match field with | AvgDuration -> Range.matches frange (Float.of_int duration) | AvgRating -> rating |> Option.map (fun rating -> let rating = Float.of_int rating in Range.matches frange rating ) |> Option.value ~default:false | AvgSize -> Range.matches frange (Int.to_float size) ) let matches_metadata bmetadata ex_metadata = let module M = SkhcMetadata.Metadata in let ostre ?(eq = Stdlib.( = )) f lhs rhs = match (f lhs, f rhs) with | None, None -> false | Some lhs, Some rhs -> eq lhs rhs | _ -> false in function | Field.FString s -> ( match Field.String.key s with | Title -> ostre M.title bmetadata ex_metadata | SortName -> ostre M.title bmetadata ex_metadata | Artist -> ostre ~eq:M.Values.subset (M.property M.Keys.artist) bmetadata ex_metadata | Album -> ostre M.album bmetadata ex_metadata | AlbumArtist -> ostre ~eq:M.Values.subset (M.property M.Keys.album_artist) bmetadata ex_metadata | Composer -> ostre ~eq:M.Values.subset (M.property M.Keys.composer) bmetadata ex_metadata | Genre -> ostre ~eq:M.Values.subset (M.property M.Keys.artist) bmetadata ex_metadata | Path -> false | FileExtension -> ostre (M.property_min M.Keys.skhc_file_extension) bmetadata ex_metadata | Arbitrary s -> let s = String.uppercase_ascii s in ostre ~eq:M.Values.subset (M.property s) bmetadata ex_metadata ) | Field.FBool b -> ( match Field.Bool.key b with Art -> false | Flag -> false ) | Field.FInt64 i -> ( match Field.Int64.key i with | Id -> false | Size -> ostre (M.property_min M.Keys.skhc_file_size) bmetadata ex_metadata ) | Field.FInt i -> ( match Field.Int.key i with | Track -> ostre M.track bmetadata ex_metadata | Year -> ostre M.year bmetadata ex_metadata | Disc -> ostre M.disc bmetadata ex_metadata | Plays -> false | Length -> ( let lhs = M.audioproperties bmetadata in let rhs = M.audioproperties ex_metadata in let f (a : Otaglibc.audioproperties) = a.length in match (lhs, rhs) with | Some lhs, Some rhs -> f lhs = f rhs | _ -> false ) | Rating -> false | AlbumCount -> true ) | Field.FFloat f -> ( match Field.Float.key f with AvgDuration | AvgRating | AvgSize -> false )
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>