Supporters update.
[dcpomatic.git] / src / lib / json_server.cc
1 /*
2     Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "film.h"
23 #include "job.h"
24 #include "job_manager.h"
25 #include "json_server.h"
26 #include "transcode_job.h"
27 #include "util.h"
28 #include <dcp/raw_convert.h>
29 #include <boost/asio.hpp>
30 #include <boost/bind/bind.hpp>
31 #include <boost/thread.hpp>
32 #include <iostream>
33
34
35 using std::cout;
36 using std::dynamic_pointer_cast;
37 using std::list;
38 using std::make_shared;
39 using std::map;
40 using std::shared_ptr;
41 using std::string;
42 using boost::asio::ip::tcp;
43 using boost::thread;
44 using dcp::raw_convert;
45
46
47 #define MAX_LENGTH 512
48
49
50 enum State {
51         AWAITING_G,
52         AWAITING_E,
53         AWAITING_T,
54         AWAITING_SPACE,
55         READING_URL,
56 };
57
58
59 JSONServer::JSONServer (int port)
60 {
61 #ifdef DCPOMATIC_LINUX
62         auto t = new thread (boost::bind (&JSONServer::run, this, port));
63         pthread_setname_np (t->native_handle(), "json-server");
64 #else
65         new thread (boost::bind (&JSONServer::run, this, port));
66 #endif
67 }
68
69
70 void
71 JSONServer::run (int port)
72 try
73 {
74         boost::asio::io_service io_service;
75         tcp::acceptor a (io_service, tcp::endpoint (tcp::v4 (), port));
76         while (true) {
77                 try {
78                         auto s = make_shared<tcp::socket>(io_service);
79                         a.accept (*s);
80                         handle (s);
81                 }
82                 catch (...) {
83
84                 }
85         }
86 }
87 catch (...)
88 {
89
90 }
91
92
93 void
94 JSONServer::handle (shared_ptr<tcp::socket> socket)
95 {
96         string url;
97         State state = AWAITING_G;
98
99         while (true) {
100                 char data[MAX_LENGTH];
101                 boost::system::error_code error;
102                 size_t len = socket->read_some (boost::asio::buffer (data), error);
103                 if (error) {
104                         cout << "error.\n";
105                         break;
106                 }
107
108                 auto p = data;
109                 auto e = data + len;
110                 while (p != e) {
111
112                         auto old_state = state;
113                         switch (state) {
114                         case AWAITING_G:
115                                 if (*p == 'G') {
116                                         state = AWAITING_E;
117                                 }
118                                 break;
119                         case AWAITING_E:
120                                 if (*p == 'E') {
121                                         state = AWAITING_T;
122                                 }
123                                 break;
124                         case AWAITING_T:
125                                 if (*p == 'T') {
126                                         state = AWAITING_SPACE;
127                                 }
128                                 break;
129                         case AWAITING_SPACE:
130                                 if (*p == ' ') {
131                                         state = READING_URL;
132                                 }
133                                 break;
134                         case READING_URL:
135                                 if (*p == ' ') {
136                                         request (url, socket);
137                                         state = AWAITING_G;
138                                         url = "";
139                                 } else {
140                                         url += *p;
141                                 }
142                                 break;
143                         }
144
145                         if (state == old_state && state != READING_URL) {
146                                 state = AWAITING_G;
147                         }
148
149                         ++p;
150                 }
151         }
152 }
153
154
155 void
156 JSONServer::request (string url, shared_ptr<tcp::socket> socket)
157 {
158         cout << "request: " << url << "\n";
159
160         auto r = split_get_request (url);
161         for (auto const& i: r) {
162                 cout << i.first << " => " << i.second << "\n";
163         }
164
165         string action;
166         if (r.find ("action") != r.end ()) {
167                 action = r["action"];
168         }
169
170         string json;
171         if (action == "status") {
172
173                 auto jobs = JobManager::instance()->get();
174
175                 json += "{ \"jobs\": [";
176                 for (auto i = jobs.cbegin(); i != jobs.cend(); ++i) {
177                         json += "{ ";
178
179                         if ((*i)->film()) {
180                                 json += "\"dcp\": \"" + (*i)->film()->dcp_name() + "\", ";
181                         }
182
183                         json += "\"name\": \"" + (*i)->json_name() + "\", ";
184                         if ((*i)->progress()) {
185                                 json += "\"progress\": " + raw_convert<string>((*i)->progress().get()) + ", ";
186                         } else {
187                                 json += "\"progress\": unknown, ";
188                         }
189                         json += "\"status\": \"" + (*i)->json_status() + "\"";
190                         json += " }";
191
192                         auto j = i;
193                         ++j;
194                         if (j != jobs.end ()) {
195                                 json += ", ";
196                         }
197                 }
198                 json += "] }";
199         }
200
201         string reply = "HTTP/1.1 200 OK\r\n"
202                 "Content-Length: " + raw_convert<string>(json.length()) + "\r\n"
203                 "Content-Type: application/json\r\n"
204                 "\r\n"
205                 + json + "\r\n";
206         cout << "reply: " << json << "\n";
207         boost::asio::write (*socket, boost::asio::buffer(reply.c_str(), reply.length()));
208 }