summaryrefslogtreecommitdiff
path: root/lib/Ultramarine/Controller.pm6
blob: 0167dd0f8749da1e627ef1dd834a330319ad2264 (plain)
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
use v6.d.PREVIEW;
use Cro::HTTP::Router;
use Cro::HTTP::Request;
use Cro::HTTP::Response;
use Cro::Transform;
use Cro;
 
class Ultramarine::Controller {
    has $.license is required;
    has $.authorisation is required;
    has $.collection is required;
 
    sub respond(*@body{
        response.status = 200;
        response.set-body(@body);
    }
 
    my class URLRewrite does Cro::Transform {
        method consumes() { Cro::HTTP::Request }
        method produces() { Cro::HTTP::Request }
 
        my token prefix { '/rest' }
        my token api-method { '/' \w+ }
        my token suffix { '.view' }
        my token query { '?' .* }
        my token api-url { ^ <prefix> <api-method> <suffix>? <query> $ }
 
        method transformer(Supply $requests --> Supply{
            supply whenever $requests -> $request {
                with $request {
                    if (.target ~~ /<api-url>/{
                        emit .clone(
                            original-target => .original-target // .target,
                            target => ($<api-url><prefix api-method query>).join,
                        );
                    }
                    else {
                        emit $_;
                    }
                }
            }
        }
    }
 
    method inner-routes() {
        return route {
            # this needs to be here, not in the Server because its 
            # short-circuited "unauthorised" response will be emited 
            # in an 'after' middleware, but the 
            # ResponseSerializerExtension is applied just before those 
            # middlewares, so our serialised won't be seen by the 
            # "unauthorised" response 
            before $!authorisation;
 
            post my $ping = -> 'ping' { respond [] }
            get $ping;
 
            post my $getLicense = -> 'getLicense' {
                my $expires = $.license.expires-at.DateTime.truncated-to('second');
                respond [
                    :status<ok>,
                    license => [
                                :valid($.license.is-valid.Str.lc),
                                :email($.license.email),
                                :licenseExpires($expires.yyyy-mm-dd ~ ':' ~ $expires.hh-mm-ss),
                            ],
                ],
            }
            get $getLicense;
 
            post my $getMusicFolders = -> 'getMusicFolders' {
                respond [
                    :status<ok>,
                    musicFolders => [
                                     $.collection.top-folders.map:
                                     { musicFolder => [ id => $^d.idname => $^d.name ] },
                                 ]
                ],
            }
            get $getMusicFolders;
 
            post my $getIndexes = -> 'getIndexes':$musicFolderId is query:$ifModifiedSince is query {
                await $.collection.is-ready;
 
                # when musicFolderId is not passed, we get a Mu, which 
                # breaks the call (it wants an Any) 
                my $indexes = $.collection.dir-children-of(id=>$musicFolderId)
                .classify*.name.substr(0,1).uc ).pairs.map: -> (:key($initial),:value($dirs)) {
                    index => [
                        name => $initial,
                        |($dirs.map: { artist => [ id => $^d.idname => $^d.name ] })
                    ]
                };
                my $songs = $.collection.songs-children-of(id=>$musicFolderId)
                .map: -> $song {
                    my $c = [
                        id => $song.id,
                        path => $song.path,
                        isDir => 'false',
                        isVideo => 'false',
                        type => 'music',
                        contentType => 'audio/mpeg'# faking it 
                        |($song.metadata<tags><album artist genre track title>:p),
                        year => $song.metadata<tags><date>,
                        bitRate => $song.metadata<bit_rate>.Int,
                        duration => ($song.metadata<duration>*60).Int,
                        suffix => $song.metadata<format_name># I'm guessing 
                        albumId => $song.album_id,
                        artistId => 1# real artistId to be fetched 
                        size => 10000# we don't event store this 
                        created => Instant.from-posix($song.mtime).DateTime.Str,
                    ];
                    warn $c.perl;
                    child => $c;
                }
 
                respond [
                    :status<ok>,
                    indexes => [
                                lastModified => now.to-posix[0].Int,
                                ignoredArticles => '',
                                |$indexes,
                                |$songs,
                            ],
                ];
            }
            get $getIndexes;
 
            # this is clearly not a Subsonic method 
            get -> 'dakkarList' {
                await $.collection.is-ready;
                respond [
                    :status<ok>,
                    songs => $.collection.all-songs,
                ];
            }
        }
    }
 
    method routes() {
        return Cro.compose(
            URLRewrite,
            route { include 'rest' => self.inner-routes() },
        );
    }
}