├── .gitignore ├── .travis.yml ├── ChangeLog ├── LICENSE ├── MAINTAINERS ├── Makefile ├── README.md ├── dune-project ├── lib ├── album.atd ├── artist.atd ├── dune ├── external_ids.atd ├── external_urls.atd ├── followers.atd ├── image.atd ├── paging.atd ├── spotify_web_api.ml ├── spotify_web_api.mli └── track.atd ├── spotify-web-api.opam └── test ├── data ├── album_search.json ├── artist_search.json └── track_search.json ├── dune ├── test_href.ml ├── test_main.ml └── test_parse.ml /.gitignore: -------------------------------------------------------------------------------- 1 | lib/*_j.ml* 2 | lib/*_t.ml* 3 | *.swp 4 | _build 5 | *.merlin 6 | *.install 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: required 3 | service: docker 4 | install: 5 | - wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-docker.sh 6 | script: 7 | - bash -ex .travis-docker.sh 8 | env: 9 | global: 10 | - PINS="spotify-web-api:." 11 | - PACKAGE=spotify-web-api 12 | matrix: 13 | - DISTRO="debian-9-ocaml-4.03" 14 | - DISTRO="debian-9-ocaml-4.04" 15 | - DISTRO="debian-9-ocaml-4.05" 16 | - DISTRO="debian-9-ocaml-4.06" 17 | - DISTRO="debian-9-ocaml-4.07" 18 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 0.2.1 (28-Dec-2018): 2 | * Convert to dune 3 | 4 | 0.2.0 (26-Feb-2015): 5 | * first public release 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 John Else 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | How to submit changes to this project 2 | ===================================== 3 | 4 | Please submit changes as pull requests to the repository on github. 5 | Please ensure that all changes have descriptive commit comments and 6 | include a Signed-off-by: line. 7 | 8 | Maintainers list 9 | ---------------- 10 | 11 | * John Else 12 | 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | ATDGEN_FILES=$(wildcard lib/*.atd) 4 | ATDGEN_J_FILES=$(ATDGEN_FILES:.atd=_j.ml) 5 | ATDGEN_T_FILES=$(ATDGEN_FILES:.atd=_t.ml) 6 | 7 | lib/%_j.ml: lib/%.atd 8 | atdgen -j $< 9 | 10 | lib/%_t.ml: lib/%.atd 11 | atdgen -t $< 12 | 13 | build: $(ATDGEN_J_FILES) $(ATDGEN_T_FILES) 14 | dune build @install 15 | 16 | build-install: $(ATDGEN_J_FILES) $(ATDGEN_T_FILES) 17 | dune build -p $(PACKAGE) @install 18 | 19 | test: build 20 | rm -rf _build/default/test/data 21 | mkdir -p _build/default/test/data 22 | cp -r test/data/* _build/default/test/data/ 23 | dune runtest --force 24 | 25 | install: build 26 | dune install 27 | 28 | uninstall: 29 | dune uninstall 30 | 31 | clean: 32 | dune clean 33 | $(foreach FILE, $(ATDGEN_FILES), rm -f `atdgen -list $(FILE)`) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ocaml-spotify-web-api [![Build status](https://travis-ci.org/johnelse/ocaml-spotify-web-api.png?branch=master)](https://travis-ci.org/johnelse/ocaml-spotify-web-api) 2 | ===================== 3 | 4 | OCaml bindings for the [spotify web API](https://developer.spotify.com/web-api/). 5 | 6 | Dependencies: 7 | 8 | * [lwt](http://ocsigen.org/lwt/) 9 | * [cohttp](https://github.com/avsm/ocaml-cohttp) 10 | * [biniou](https://github.com/mjambon/biniou) 11 | * [atdgen](https://github.com/mjambon/atdgen) 12 | * [uri](https://github.com/avsm/ocaml-uri) 13 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.6) 2 | -------------------------------------------------------------------------------- /lib/album.atd: -------------------------------------------------------------------------------- 1 | type external_urls = abstract 2 | type image = abstract 3 | type 'a paging = abstract 4 | 5 | type album_simplified = { 6 | album_type: string; 7 | available_markets: string list; 8 | external_urls: external_urls; 9 | href: string; 10 | id: string; 11 | images: image list; 12 | name: string; 13 | uri: string; 14 | } 15 | 16 | type search_wrapper = { 17 | albums: album_simplified paging; 18 | } 19 | -------------------------------------------------------------------------------- /lib/artist.atd: -------------------------------------------------------------------------------- 1 | type external_urls = abstract 2 | type followers = abstract 3 | type image = abstract 4 | type 'a paging = abstract 5 | 6 | type artist = { 7 | external_urls: external_urls; 8 | followers: followers; 9 | genres: string list; 10 | href: string; 11 | id: string; 12 | images: image list; 13 | name: string; 14 | popularity: int; 15 | uri: string; 16 | } 17 | 18 | type artist_simplified = { 19 | external_urls: external_urls; 20 | href: string; 21 | id: string; 22 | name: string; 23 | uri: string; 24 | } 25 | 26 | type search_wrapper = { 27 | artists: artist paging; 28 | } 29 | -------------------------------------------------------------------------------- /lib/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name spotify_web_api) 3 | (public_name spotify-web-api) 4 | (libraries atdgen biniou yojson uri cohttp-lwt-unix) 5 | (modules album_t album_j 6 | artist_t artist_j 7 | external_ids_t external_ids_j 8 | external_urls_t external_urls_j 9 | followers_t followers_j 10 | image_t image_j 11 | paging_t paging_j 12 | track_t track_j 13 | spotify_web_api) 14 | (wrapped false)) 15 | -------------------------------------------------------------------------------- /lib/external_ids.atd: -------------------------------------------------------------------------------- 1 | type external_ids = (string * string) list 2 | -------------------------------------------------------------------------------- /lib/external_urls.atd: -------------------------------------------------------------------------------- 1 | type external_urls = (string * string) list 2 | -------------------------------------------------------------------------------- /lib/followers.atd: -------------------------------------------------------------------------------- 1 | type followers = { 2 | href: string nullable; 3 | total: int; 4 | } 5 | -------------------------------------------------------------------------------- /lib/image.atd: -------------------------------------------------------------------------------- 1 | type image = { 2 | height: int; 3 | url: string; 4 | width: int; 5 | } 6 | -------------------------------------------------------------------------------- /lib/paging.atd: -------------------------------------------------------------------------------- 1 | type 'a paging = { 2 | href: string; 3 | items: 'a list; 4 | limit: int; 5 | offset: int; 6 | total: int; 7 | } 8 | -------------------------------------------------------------------------------- /lib/spotify_web_api.ml: -------------------------------------------------------------------------------- 1 | module Common = struct 2 | let base_uri = "https://api.spotify.com/v1" 3 | 4 | let uid_length = 22 5 | 6 | type mode = [ `album | `artist | `track ] 7 | 8 | let string_of_mode = function 9 | | `album -> "album" 10 | | `artist -> "artist" 11 | | `track -> "track" 12 | 13 | exception Invalid_href 14 | 15 | let check_href mode href = 16 | try 17 | Scanf.sscanf href "spotify:%s@:%s" 18 | (fun mode_string uid -> 19 | if mode_string <> (string_of_mode mode) 20 | then raise Invalid_href; 21 | if (String.length uid) <> uid_length 22 | then raise Invalid_href) 23 | with Scanf.Scan_failure _ -> 24 | raise Invalid_href 25 | end 26 | 27 | module Remote = struct 28 | module C = Cohttp_lwt_unix 29 | 30 | let read_uri uri parse_fn = 31 | let open Cohttp_lwt_unix.IO in 32 | C.Client.call ~chunked:false `GET uri 33 | >>= (fun (_, body) -> 34 | Cohttp_lwt__Body.to_string body 35 | >>= (fun data -> return (parse_fn data))) 36 | end 37 | 38 | module Search = struct 39 | open Lwt 40 | 41 | let search mode query parse_fn = 42 | let uri = Uri.of_string 43 | (Printf.sprintf "%s/search?type=%s&q=%s" 44 | Common.base_uri (Common.string_of_mode mode) query) 45 | in 46 | Remote.read_uri uri parse_fn 47 | 48 | let search_albums query = 49 | search `album query Album_j.search_wrapper_of_string 50 | >|= (fun wrapper -> wrapper.Album_t.albums) 51 | 52 | let search_artists query = 53 | search `artist query Artist_j.search_wrapper_of_string 54 | >|= (fun wrapper -> wrapper.Artist_t.artists) 55 | 56 | let search_tracks query = 57 | search `track query Track_j.search_wrapper_of_string 58 | >|= (fun wrapper -> wrapper.Track_t.tracks) 59 | end 60 | -------------------------------------------------------------------------------- /lib/spotify_web_api.mli: -------------------------------------------------------------------------------- 1 | module Common : sig 2 | val base_uri : string 3 | 4 | exception Invalid_href 5 | 6 | type mode = [ `album | `artist | `track ] 7 | 8 | val string_of_mode : mode -> string 9 | 10 | val check_href : mode -> string -> unit 11 | end 12 | 13 | module Search : sig 14 | val search_albums: string -> Album_t.album_simplified Paging_t.paging Lwt.t 15 | val search_artists: string -> Artist_t.artist Paging_t.paging Lwt.t 16 | val search_tracks: string -> Track_t.track Paging_t.paging Lwt.t 17 | end 18 | -------------------------------------------------------------------------------- /lib/track.atd: -------------------------------------------------------------------------------- 1 | type album_simplified = abstract 2 | type artist_simplified = abstract 3 | type external_ids = abstract 4 | type external_urls = abstract 5 | type 'a paging = abstract 6 | 7 | type track = { 8 | album: album_simplified; 9 | artists: artist_simplified list; 10 | available_markets: string list; 11 | disc_number: int; 12 | duration_ms: int; 13 | explicit: bool; 14 | external_ids: external_ids; 15 | external_urls: external_urls; 16 | href: string; 17 | id: string; 18 | name: string; 19 | popularity: int; 20 | preview_url: string; 21 | track_number: int; 22 | uri: string; 23 | } 24 | 25 | type track_simplified = { 26 | artists: artist_simplified list; 27 | available_markets: string list; 28 | disc_number: int; 29 | duration_ms: int; 30 | explicit: bool; 31 | href: string; 32 | id: string; 33 | name: string; 34 | preview_url: string; 35 | track_number: int; 36 | uri: string; 37 | } 38 | 39 | type search_wrapper = { 40 | tracks: track paging; 41 | } 42 | -------------------------------------------------------------------------------- /spotify-web-api.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | synopsis: "OCaml bindings to the Spotify web API" 3 | maintainer: "john.else@gmail.com" 4 | authors: ["John Else"] 5 | homepage: "https://github.com/johnelse/ocaml-spotify-web-api" 6 | bug-reports: "https://github.com/johnelse/ocaml-spotify-web-api/issues" 7 | depends: [ 8 | "dune" {build} 9 | "atdgen" 10 | "biniou" 11 | "yojson" 12 | "uri" 13 | "ssl" 14 | "cohttp-lwt-unix" {>= "1.0.0"} 15 | "ounit" {with-test} 16 | ] 17 | build: [ 18 | [make "build-install" "PACKAGE=%{name}%"] 19 | ["dune" "build" "@doc" "-p" name] {with-doc} 20 | ["dune" "runtest" "-p" name] {with-test} 21 | ] 22 | dev-repo: "git://github.com/johnelse/ocaml-spotify-web-api" 23 | -------------------------------------------------------------------------------- /test/data/album_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "albums" : { 3 | "href" : "https://api.spotify.com/v1/search?query=cavalcade+of+dadaist&offset=0&limit=20&type=album", 4 | "items" : [ { 5 | "album_type" : "album", 6 | "available_markets" : [ "AR", "BO", "BR", "CA", "CL", "CO", "CR", "DO", "EC", "GT", "HN", "MX", "NI", "PA", "PE", "PY", "SV", "US", "UY" ], 7 | "external_urls" : { 8 | "spotify" : "https://open.spotify.com/album/5v4vDmq8pNO3TbegZmK5bL" 9 | }, 10 | "href" : "https://api.spotify.com/v1/albums/5v4vDmq8pNO3TbegZmK5bL", 11 | "id" : "5v4vDmq8pNO3TbegZmK5bL", 12 | "images" : [ { 13 | "height" : 640, 14 | "url" : "https://i.scdn.co/image/d2c396974cd76ab68e95deb6c996eb2c34033341", 15 | "width" : 640 16 | }, { 17 | "height" : 300, 18 | "url" : "https://i.scdn.co/image/2e4646f11b710bae7802d1dd85c634c6aa5f5ff0", 19 | "width" : 300 20 | }, { 21 | "height" : 64, 22 | "url" : "https://i.scdn.co/image/a389ad01fe08f0d1d994f55ac28d8a417c047158", 23 | "width" : 64 24 | } ], 25 | "name" : "Cavalcade Of Glee & Dadaist Happy Hardcore Pom Poms", 26 | "type" : "album", 27 | "uri" : "spotify:album:5v4vDmq8pNO3TbegZmK5bL" 28 | } ], 29 | "limit" : 20, 30 | "next" : null, 31 | "offset" : 0, 32 | "previous" : null, 33 | "total" : 1 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/data/artist_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "artists" : { 3 | "href" : "https://api.spotify.com/v1/search?query=tania+bowra&offset=0&limit=20&type=artist", 4 | "items" : [ { 5 | "external_urls" : { 6 | "spotify" : "https://open.spotify.com/artist/08td7MxkoHQkXnWAYD8d6Q" 7 | }, 8 | "followers" : { 9 | "href" : null, 10 | "total" : 30 11 | }, 12 | "genres" : [ ], 13 | "href" : "https://api.spotify.com/v1/artists/08td7MxkoHQkXnWAYD8d6Q", 14 | "id" : "08td7MxkoHQkXnWAYD8d6Q", 15 | "images" : [ { 16 | "height" : 640, 17 | "url" : "https://i.scdn.co/image/f2798ddab0c7b76dc2d270b65c4f67ddef7f6718", 18 | "width" : 640 19 | }, { 20 | "height" : 300, 21 | "url" : "https://i.scdn.co/image/b414091165ea0f4172089c2fc67bb35aa37cfc55", 22 | "width" : 300 23 | }, { 24 | "height" : 64, 25 | "url" : "https://i.scdn.co/image/8522fc78be4bf4e83fea8e67bb742e7d3dfe21b4", 26 | "width" : 64 27 | } ], 28 | "name" : "Tania Bowra", 29 | "popularity" : 3, 30 | "type" : "artist", 31 | "uri" : "spotify:artist:08td7MxkoHQkXnWAYD8d6Q" 32 | } ], 33 | "limit" : 20, 34 | "next" : null, 35 | "offset" : 0, 36 | "previous" : null, 37 | "total" : 1 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/data/track_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "tracks" : { 3 | "href" : "https://api.spotify.com/v1/search?query=will_smith&offset=0&limit=20&type=track", 4 | "items" : [ { 5 | "album" : { 6 | "album_type" : "album", 7 | "available_markets" : [ "AR", "AT", "BE", "BO", "BR", "BG", "CA", "CL", "CO", "CR", "CY", "CZ", "DK", "DO", "DE", "EC", "EE", "SV", "FI", "FR", "GR", "GT", "HN", "HK", "HU", "IS", "IE", "IT", "LV", "LT", "LU", "MY", "MT", "MX", "NL", "NI", "NO", "PA", "PY", "PE", "PH", "PL", "PT", "SG", "SK", "ES", "SE", "CH", "TW", "TR", "UY", "US", "GB", "AD", "LI", "MC", "ID" ], 8 | "external_urls" : { 9 | "spotify" : "https://open.spotify.com/album/1eMrRFFbPPC2zgKGojsVxX" 10 | }, 11 | "href" : "https://api.spotify.com/v1/albums/1eMrRFFbPPC2zgKGojsVxX", 12 | "id" : "1eMrRFFbPPC2zgKGojsVxX", 13 | "images" : [ { 14 | "height" : 640, 15 | "url" : "https://i.scdn.co/image/a50c2eae749b0f4c5125ca639e4b34dda5f8db72", 16 | "width" : 640 17 | }, { 18 | "height" : 300, 19 | "url" : "https://i.scdn.co/image/9ab898ff26fd366b925a34185cbd180f6a840b49", 20 | "width" : 300 21 | }, { 22 | "height" : 64, 23 | "url" : "https://i.scdn.co/image/a947b4743b6fd4c36be1256811ea62c3e5853ff9", 24 | "width" : 64 25 | } ], 26 | "name" : "OCTOPUS4", 27 | "type" : "album", 28 | "uri" : "spotify:album:1eMrRFFbPPC2zgKGojsVxX" 29 | }, 30 | "artists" : [ { 31 | "external_urls" : { 32 | "spotify" : "https://open.spotify.com/artist/14u4KXVp0iXQil79EpxXGc" 33 | }, 34 | "href" : "https://api.spotify.com/v1/artists/14u4KXVp0iXQil79EpxXGc", 35 | "id" : "14u4KXVp0iXQil79EpxXGc", 36 | "name" : "The Algorithm", 37 | "type" : "artist", 38 | "uri" : "spotify:artist:14u4KXVp0iXQil79EpxXGc" 39 | } ], 40 | "available_markets" : [ "AR", "AT", "BE", "BO", "BR", "BG", "CA", "CL", "CO", "CR", "CY", "CZ", "DK", "DO", "DE", "EC", "EE", "SV", "FI", "FR", "GR", "GT", "HN", "HK", "HU", "IS", "IE", "IT", "LV", "LT", "LU", "MY", "MT", "MX", "NL", "NI", "NO", "PA", "PY", "PE", "PH", "PL", "PT", "SG", "SK", "ES", "SE", "CH", "TW", "TR", "UY", "US", "GB", "AD", "LI", "MC", "ID" ], 41 | "disc_number" : 1, 42 | "duration_ms" : 261346, 43 | "explicit" : false, 44 | "external_ids" : { 45 | "isrc" : "UK2D51400006" 46 | }, 47 | "external_urls" : { 48 | "spotify" : "https://open.spotify.com/track/7FPxCA9KaWXV18r1Za878d" 49 | }, 50 | "href" : "https://api.spotify.com/v1/tracks/7FPxCA9KaWXV18r1Za878d", 51 | "id" : "7FPxCA9KaWXV18r1Za878d", 52 | "name" : "will_smith", 53 | "popularity" : 30, 54 | "preview_url" : "https://p.scdn.co/mp3-preview/860ab99fe6dfe6b0455b91f5a8039d08308fae0f", 55 | "track_number" : 4, 56 | "type" : "track", 57 | "uri" : "spotify:track:7FPxCA9KaWXV18r1Za878d" 58 | } ], 59 | "limit" : 20, 60 | "next" : null, 61 | "offset" : 0, 62 | "previous" : null, 63 | "total" : 1 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name test_main) 3 | (libraries oUnit spotify_web_api)) 4 | 5 | (alias 6 | (name runtest) 7 | (action 8 | (chdir %{workspace_root}/test/ 9 | (run ./test_main.exe)))) 10 | -------------------------------------------------------------------------------- /test/test_href.ml: -------------------------------------------------------------------------------- 1 | open OUnit 2 | open Spotify_web_api 3 | 4 | type check_href_output = 5 | | Exception of exn 6 | | Ok 7 | 8 | let test_check_href = 9 | let test ~msg ~mode ~input ~expected_output () = 10 | match expected_output with 11 | | Exception e -> 12 | assert_raises ~msg e (fun () -> Common.check_href mode input) 13 | | Ok -> 14 | Common.check_href mode input 15 | in 16 | "test_check_href" >::: 17 | [ 18 | "test_valid_album" >:: 19 | test ~msg:"Checking a valid album href" 20 | ~mode:`album 21 | ~input:"spotify:album:0123456789012345678901" 22 | ~expected_output:Ok; 23 | "test_valid_artist" >:: 24 | test ~msg:"Checking a valid artist href" 25 | ~mode:`artist 26 | ~input:"spotify:artist:0123456789012345678901" 27 | ~expected_output:Ok; 28 | "test_valid_track" >:: 29 | test ~msg:"Checking a valid track href" 30 | ~mode:`track 31 | ~input:"spotify:track:0123456789012345678901" 32 | ~expected_output:Ok; 33 | "test_garbage_album" >:: 34 | test ~msg:"Checking that garbage fails to parse as a album href" 35 | ~mode:`album 36 | ~input:"spotify:wmnermfg:diobvdfjk" 37 | ~expected_output:(Exception Common.Invalid_href); 38 | "test_garbage_artist" >:: 39 | test ~msg:"Checking that garbage fails to parse as a artist href" 40 | ~mode:`artist 41 | ~input:"spotify:wmnermfg:diobvdfjk" 42 | ~expected_output:(Exception Common.Invalid_href); 43 | "test_garbage_track" >:: 44 | test ~msg:"Checking that garbage fails to parse as a track href" 45 | ~mode:`track 46 | ~input:"spotify:wmnermfg:diobvdfjk" 47 | ~expected_output:(Exception Common.Invalid_href); 48 | ] 49 | 50 | let suite = 51 | "test_href" >::: 52 | [ 53 | test_check_href; 54 | ] 55 | -------------------------------------------------------------------------------- /test/test_main.ml: -------------------------------------------------------------------------------- 1 | open OUnit 2 | 3 | let base_suite = 4 | "base_suite" >::: 5 | [ 6 | Test_href.suite; 7 | Test_parse.suite; 8 | ] 9 | 10 | let () = OUnit2.run_test_tt_main (ounit2_of_ounit1 base_suite) 11 | -------------------------------------------------------------------------------- /test/test_parse.ml: -------------------------------------------------------------------------------- 1 | open OUnit 2 | 3 | let data_dir = "../../../test/data" 4 | 5 | let string_of_file filename = 6 | let path = Filename.concat data_dir filename in 7 | let chan = open_in path in 8 | try 9 | let length = in_channel_length chan in 10 | let buffer = Bytes.create length in 11 | really_input chan buffer 0 length; 12 | close_in chan; 13 | Bytes.to_string buffer 14 | with e -> 15 | close_in chan; 16 | raise e 17 | 18 | let test_parse_album_search () = 19 | let results = 20 | string_of_file "album_search.json" 21 | |> Album_j.search_wrapper_of_string 22 | in 23 | assert_equal 24 | results 25 | Album_j.({ 26 | albums = Paging_j.({ 27 | href = "https://api.spotify.com/v1/search?query=cavalcade+of+dadaist&offset=0&limit=20&type=album"; 28 | items = [ 29 | { 30 | album_type = "album"; 31 | available_markets = ["AR"; "BO"; "BR"; "CA"; "CL"; "CO"; "CR"; "DO"; "EC"; "GT"; "HN"; "MX"; "NI"; "PA"; "PE"; "PY"; "SV"; "US"; "UY"]; 32 | external_urls = [ 33 | "spotify", "https://open.spotify.com/album/5v4vDmq8pNO3TbegZmK5bL"; 34 | ]; 35 | href = "https://api.spotify.com/v1/albums/5v4vDmq8pNO3TbegZmK5bL"; 36 | id = "5v4vDmq8pNO3TbegZmK5bL"; 37 | images = Image_j.([ 38 | { 39 | height = 640; 40 | url = "https://i.scdn.co/image/d2c396974cd76ab68e95deb6c996eb2c34033341"; 41 | width = 640; 42 | }; 43 | { 44 | height = 300; 45 | url = "https://i.scdn.co/image/2e4646f11b710bae7802d1dd85c634c6aa5f5ff0"; 46 | width = 300; 47 | }; 48 | { 49 | height = 64; 50 | url = "https://i.scdn.co/image/a389ad01fe08f0d1d994f55ac28d8a417c047158"; 51 | width = 64; 52 | }; 53 | ]); 54 | name = "Cavalcade Of Glee & Dadaist Happy Hardcore Pom Poms"; 55 | uri = "spotify:album:5v4vDmq8pNO3TbegZmK5bL"; 56 | }; 57 | ]; 58 | limit = 20; 59 | offset = 0; 60 | total = 1; 61 | }) 62 | }) 63 | 64 | let test_parse_artist_search () = 65 | let results = 66 | string_of_file "artist_search.json" 67 | |> Artist_j.search_wrapper_of_string 68 | in 69 | assert_equal 70 | results 71 | Artist_j.({ 72 | artists = Paging_j.({ 73 | href = "https://api.spotify.com/v1/search?query=tania+bowra&offset=0&limit=20&type=artist"; 74 | items = [ 75 | { 76 | external_urls = [ 77 | "spotify", "https://open.spotify.com/artist/08td7MxkoHQkXnWAYD8d6Q" 78 | ]; 79 | followers = Followers_j.({ 80 | href = None; 81 | total = 30; 82 | }); 83 | genres = []; 84 | href = "https://api.spotify.com/v1/artists/08td7MxkoHQkXnWAYD8d6Q"; 85 | id = "08td7MxkoHQkXnWAYD8d6Q"; 86 | images = Image_j.([ 87 | { 88 | height = 640; 89 | url = "https://i.scdn.co/image/f2798ddab0c7b76dc2d270b65c4f67ddef7f6718"; 90 | width = 640; 91 | }; 92 | { 93 | height = 300; 94 | url = "https://i.scdn.co/image/b414091165ea0f4172089c2fc67bb35aa37cfc55"; 95 | width = 300; 96 | }; 97 | { 98 | height = 64; 99 | url = "https://i.scdn.co/image/8522fc78be4bf4e83fea8e67bb742e7d3dfe21b4"; 100 | width = 64; 101 | }; 102 | ]); 103 | name = "Tania Bowra"; 104 | popularity = 3; 105 | uri = "spotify:artist:08td7MxkoHQkXnWAYD8d6Q"; 106 | } 107 | ]; 108 | limit = 20; 109 | offset = 0; 110 | total = 1; 111 | }) 112 | }) 113 | 114 | let test_parse_track_search () = 115 | let results = 116 | string_of_file "track_search.json" 117 | |> Track_j.search_wrapper_of_string 118 | in 119 | assert_equal 120 | results 121 | Track_j.({ 122 | tracks = Paging_j.({ 123 | href = "https://api.spotify.com/v1/search?query=will_smith&offset=0&limit=20&type=track"; 124 | items = [ 125 | { 126 | album = Album_j.({ 127 | album_type = "album"; 128 | available_markets = ["AR"; "AT"; "BE"; "BO"; "BR"; "BG"; "CA"; "CL"; "CO"; "CR"; "CY"; "CZ"; "DK"; "DO"; "DE"; "EC"; "EE"; "SV"; "FI"; "FR"; "GR"; "GT"; "HN"; "HK"; "HU"; "IS"; "IE"; "IT"; "LV"; "LT"; "LU"; "MY"; "MT"; "MX"; "NL"; "NI"; "NO"; "PA"; "PY"; "PE"; "PH"; "PL"; "PT"; "SG"; "SK"; "ES"; "SE"; "CH"; "TW"; "TR"; "UY"; "US"; "GB"; "AD"; "LI"; "MC"; "ID"]; 129 | external_urls = [ 130 | "spotify", "https://open.spotify.com/album/1eMrRFFbPPC2zgKGojsVxX"; 131 | ]; 132 | href = "https://api.spotify.com/v1/albums/1eMrRFFbPPC2zgKGojsVxX"; 133 | id = "1eMrRFFbPPC2zgKGojsVxX"; 134 | images = Image_j.([ 135 | { 136 | height = 640; 137 | url = "https://i.scdn.co/image/a50c2eae749b0f4c5125ca639e4b34dda5f8db72"; 138 | width = 640; 139 | }; 140 | { 141 | height = 300; 142 | url = "https://i.scdn.co/image/9ab898ff26fd366b925a34185cbd180f6a840b49"; 143 | width = 300; 144 | }; 145 | { 146 | height = 64; 147 | url = "https://i.scdn.co/image/a947b4743b6fd4c36be1256811ea62c3e5853ff9"; 148 | width = 64; 149 | }; 150 | ]); 151 | name = "OCTOPUS4"; 152 | uri = "spotify:album:1eMrRFFbPPC2zgKGojsVxX"; 153 | }); 154 | artists = Artist_j.([ 155 | { 156 | external_urls = [ 157 | "spotify", "https://open.spotify.com/artist/14u4KXVp0iXQil79EpxXGc"; 158 | ]; 159 | href = "https://api.spotify.com/v1/artists/14u4KXVp0iXQil79EpxXGc"; 160 | id = "14u4KXVp0iXQil79EpxXGc"; 161 | name = "The Algorithm"; 162 | uri = "spotify:artist:14u4KXVp0iXQil79EpxXGc"; 163 | }; 164 | ]); 165 | available_markets = ["AR"; "AT"; "BE"; "BO"; "BR"; "BG"; "CA"; "CL"; "CO"; "CR"; "CY"; "CZ"; "DK"; "DO"; "DE"; "EC"; "EE"; "SV"; "FI"; "FR"; "GR"; "GT"; "HN"; "HK"; "HU"; "IS"; "IE"; "IT"; "LV"; "LT"; "LU"; "MY"; "MT"; "MX"; "NL"; "NI"; "NO"; "PA"; "PY"; "PE"; "PH"; "PL"; "PT"; "SG"; "SK"; "ES"; "SE"; "CH"; "TW"; "TR"; "UY"; "US"; "GB"; "AD"; "LI"; "MC"; "ID"]; 166 | disc_number = 1; 167 | duration_ms = 261346; 168 | explicit = false; 169 | external_ids = [ 170 | "isrc", "UK2D51400006"; 171 | ]; 172 | external_urls = [ 173 | "spotify", "https://open.spotify.com/track/7FPxCA9KaWXV18r1Za878d"; 174 | ]; 175 | href = "https://api.spotify.com/v1/tracks/7FPxCA9KaWXV18r1Za878d"; 176 | id = "7FPxCA9KaWXV18r1Za878d"; 177 | name = "will_smith"; 178 | popularity = 30; 179 | preview_url = "https://p.scdn.co/mp3-preview/860ab99fe6dfe6b0455b91f5a8039d08308fae0f"; 180 | track_number = 4; 181 | uri = "spotify:track:7FPxCA9KaWXV18r1Za878d"; 182 | }; 183 | ]; 184 | limit = 20; 185 | offset = 0; 186 | total = 1; 187 | }) 188 | }) 189 | 190 | let suite = 191 | "test_parse" >::: 192 | [ 193 | "test_parse_album_search" >:: test_parse_album_search; 194 | "test_parse_artist_search" >:: test_parse_artist_search; 195 | "test_parse_track_search" >:: test_parse_track_search; 196 | ] 197 | --------------------------------------------------------------------------------