aboutsummaryrefslogtreecommitdiff
path: root/mpdwatch/parse.ml
blob: bd7f0a044213a569511efd9b11e9874149058eee (plain) (blame)
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
open Angstrom

(**
   We only care about tags in the intersection of those known
   by MPD and ListenBrainz. They are (LB <-> MPD)
   + artist_name <-> artist, performer, composer
   + track_name <-> title
   + tracknumber <-> track
   + artist_mbids <-> musicbrainz_artistid
   + release_mbid <-> musicbrainz_albumid
   + recording_mbid <-> musicbrainz_trackid
   MPD may return multiple entries for each tag.
   See https://picard-docs.musicbrainz.org/downloads/MusicBrainz_Picard_Tag_Map.html
   for the correspondence between LB and MPD tag names.
*)
type currentsong = {
  artist: string list;
  performer: string list;
  composer: string list;
  title: string option;
  track: int option;
  musicbrainz_albumid: string option;
  musicbrainz_artistid: string list;
  musicbrainz_trackid: string option;
}

exception ParseError of string

let parse_string p s =
  match Angstrom.parse_string ~consume:All (p >>= return) s with
  | Ok parsed -> parsed
  | Error m -> raise (ParseError m)

let take_till_eol =
  let is_eol = function
    | '\n' | '\r' -> true
    | _ -> false in
  take_till is_eol <* end_of_line

let is_whitespace = function
  | ' ' | '\t' -> true
  | _ -> false

let skip_spaces = skip_while is_whitespace

let currentsong_parser : currentsong Angstrom.t =
  let open Angstrom in
  fix (fun currentsong ->
      lift2 (fun artist current -> { current with artist = artist :: current.artist })
        (string "Artist:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> lift2 (fun performer current -> { current with performer = performer :: current.performer })
        (string "Performer:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> lift2 (fun composer current -> { current with composer = composer :: current.composer })
        (string "Composer:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> lift2 (fun title current -> { current with title = Some title })
        (string "Title:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> lift2 (fun track current -> { current with track = Some (int_of_string track) })
        (string "Track:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> lift2 (fun albumid current -> { current with musicbrainz_albumid = Some albumid })
        (string "MUSICBRAINZ_ALBUMID:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> lift2 (fun artistid current ->
          { current with musicbrainz_artistid = artistid :: current.musicbrainz_artistid })
        (string "MUSICBRAINZ_ARTISTID:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> lift2 (fun trackid current -> { current with musicbrainz_trackid = Some trackid })
        (string "MUSICBRAINZ_TRACKID:" *> skip_spaces *> take_till_eol)
        currentsong
      <|> ((string "OK") *> return {
          artist = [];
          performer = [];
          composer = [];
          title = None;
          track = None;
          musicbrainz_albumid = None;
          musicbrainz_artistid = [];
          musicbrainz_trackid = None;
        })
      <|> (take_till_eol *> currentsong))
  <?> "currentsong"