livefootballtv-tizen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,94 @@
1
+ # LiveFootballTV C++ Proxy Service - Tizen Studio Setup
2
+
3
+ This directory contains the source code for the native C++ proxy service.
4
+ You need to create a Tizen Studio project and import these files.
5
+
6
+ ## Quick Setup in Tizen Studio
7
+
8
+ ### Step 1: Create New Native Service Project
9
+
10
+ 1. Open Tizen Studio
11
+ 2. File → New → Tizen Project
12
+ 3. Select **Template** → **TV** → **Native Service**
13
+ 4. Name it: `proxyservice`
14
+ 5. Package ID: `LIVEFTV001` (must match the web app!)
15
+
16
+ ### Step 2: Configure C++11
17
+
18
+ 1. Right-click project → **Properties**
19
+ 2. Go to: C/C++ Build → Settings → Tool Settings → C++ Compiler → Dialect
20
+ 3. Set Language standard to: **ISO C++11 (-std=c++0x)**
21
+ 4. Click **Apply and Close**
22
+
23
+ ### Step 3: Copy Source Files
24
+
25
+ Copy these files into your Tizen Studio project:
26
+
27
+ ```
28
+ From: d:\IDE AI\webapp\tizen\native_service\
29
+ ├── tizen-manifest.xml → [project root]/tizen-manifest.xml
30
+ ├── src/proxy_service.cpp → [project root]/src/proxy_service.cpp
31
+ └── inc/proxy_service.h → [project root]/inc/proxy_service.h
32
+ ```
33
+
34
+ ### Step 4: Add Required Libraries
35
+
36
+ In project Properties → C/C++ Build → Settings → Tool Settings → C++ Linker → Libraries:
37
+
38
+ Add these libraries (`-l` flags):
39
+ - `curl`
40
+ - `pthread`
41
+
42
+ ### Step 5: Build the Native Service
43
+
44
+ 1. Right-click project → **Build Project**
45
+ 2. Ensure no compilation errors
46
+
47
+ ### Step 6: Create Hybrid Package
48
+
49
+ To package both web app and native service together:
50
+
51
+ 1. Right-click the **Web App** project (livefootballtv)
52
+ 2. Select: **Export to TPK**
53
+ 3. In the dialog, check "Include Native Service"
54
+ 4. Select the `proxyservice` native project
55
+ 5. Export the combined `.wgt` or `.tpk` file
56
+
57
+ ### Step 7: Deploy and Test
58
+
59
+ 1. Connect TV via SDB
60
+ 2. Install the hybrid package
61
+ 3. Launch the app
62
+ 4. Check if the Proxy indicator turns green
63
+
64
+ ## Endpoints
65
+
66
+ The service runs on `http://localhost:8888` with these endpoints:
67
+
68
+ | Endpoint | Description |
69
+ |----------|-------------|
70
+ | `/health` | Health check - returns `{"status":"ok"}` |
71
+ | `/proxy?url=...` | Proxy request with custom headers |
72
+
73
+ ## Custom Headers
74
+
75
+ Pass these headers to add custom headers to proxied requests:
76
+
77
+ - `X-Proxy-Referer: https://example.com` → Sends `Referer: https://example.com`
78
+ - `X-Proxy-Origin: https://example.com` → Sends `Origin: https://example.com`
79
+ - `X-Proxy-User-Agent: Custom UA` → Sends `User-Agent: Custom UA`
80
+
81
+ ## Troubleshooting
82
+
83
+ ### Service Not Starting
84
+ - Check Tizen Studio console for errors
85
+ - Verify package ID matches between web app and native service
86
+ - Ensure all privileges are granted
87
+
88
+ ### Port Already in Use
89
+ - Another instance may be running
90
+ - Restart the TV and try again
91
+
92
+ ### CURL Errors
93
+ - Check internet connectivity
94
+ - Verify target URL is accessible
@@ -0,0 +1,22 @@
1
+ /**
2
+ * LiveFootballTV Proxy Service - Header
3
+ */
4
+
5
+ #ifndef __PROXY_SERVICE_H__
6
+ #define __PROXY_SERVICE_H__
7
+
8
+ #include <service_app.h>
9
+ #include <dlog.h>
10
+
11
+ #ifdef __cplusplus
12
+ extern "C" {
13
+ #endif
14
+
15
+ #define LOG_TAG "PROXY_SERVICE"
16
+ #define PROXY_PORT 8888
17
+
18
+ #ifdef __cplusplus
19
+ }
20
+ #endif
21
+
22
+ #endif /* __PROXY_SERVICE_H__ */
@@ -0,0 +1,453 @@
1
+ /**
2
+ * LiveFootballTV C++ Proxy Service
3
+ *
4
+ * This native service runs on localhost:8888 and proxies HTTP requests
5
+ * with custom headers for streaming video content.
6
+ *
7
+ * Build with Tizen Studio -> TV Native Service Project
8
+ * Enable C++11 in Project Settings
9
+ */
10
+
11
+ #include <service_app.h>
12
+ #include <dlog.h>
13
+ #include <curl/curl.h>
14
+ #include <sys/socket.h>
15
+ #include <netinet/in.h>
16
+ #include <arpa/inet.h>
17
+ #include <unistd.h>
18
+ #include <pthread.h>
19
+ #include <cstring>
20
+ #include <string>
21
+ #include <sstream>
22
+ #include <map>
23
+ #include <vector>
24
+
25
+ #define LOG_TAG "PROXY_SERVICE"
26
+ #define PROXY_PORT 8888
27
+ #define BUFFER_SIZE 65536
28
+ #define MAX_CLIENTS 10
29
+
30
+ static int server_fd = -1;
31
+ static volatile bool running = false;
32
+ static pthread_t server_thread;
33
+
34
+ // ============================================================================
35
+ // CURL Helpers
36
+ // ============================================================================
37
+
38
+ struct CurlResponse {
39
+ std::vector<char> data;
40
+ std::string content_type;
41
+ long http_code;
42
+ };
43
+
44
+ static size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
45
+ CurlResponse* response = static_cast<CurlResponse*>(userp);
46
+ size_t total_size = size * nmemb;
47
+ const char* data = static_cast<char*>(contents);
48
+ response->data.insert(response->data.end(), data, data + total_size);
49
+ return total_size;
50
+ }
51
+
52
+ static size_t header_callback(char* buffer, size_t size, size_t nitems, void* userp) {
53
+ CurlResponse* response = static_cast<CurlResponse*>(userp);
54
+ size_t total_size = size * nitems;
55
+ std::string header(buffer, total_size);
56
+
57
+ // Parse Content-Type header
58
+ if (header.find("Content-Type:") == 0 || header.find("content-type:") == 0) {
59
+ size_t start = header.find(':') + 1;
60
+ size_t end = header.find('\r');
61
+ if (end == std::string::npos) end = header.find('\n');
62
+ if (end != std::string::npos) {
63
+ response->content_type = header.substr(start, end - start);
64
+ // Trim whitespace
65
+ size_t first = response->content_type.find_first_not_of(" \t");
66
+ if (first != std::string::npos) {
67
+ response->content_type = response->content_type.substr(first);
68
+ }
69
+ }
70
+ }
71
+ return total_size;
72
+ }
73
+
74
+ // ============================================================================
75
+ // HTTP Parsing Helpers
76
+ // ============================================================================
77
+
78
+ std::string url_decode(const std::string& encoded) {
79
+ CURL* curl = curl_easy_init();
80
+ if (!curl) return encoded;
81
+
82
+ int out_len;
83
+ char* decoded = curl_easy_unescape(curl, encoded.c_str(), encoded.length(), &out_len);
84
+ std::string result = decoded ? std::string(decoded, out_len) : encoded;
85
+ if (decoded) curl_free(decoded);
86
+ curl_easy_cleanup(curl);
87
+ return result;
88
+ }
89
+
90
+ std::string parse_url_param(const std::string& request) {
91
+ // Find ?url= or &url= in the request
92
+ size_t url_start = request.find("url=");
93
+ if (url_start == std::string::npos) return "";
94
+
95
+ url_start += 4; // Skip "url="
96
+
97
+ // Find the end of the URL parameter (space or &)
98
+ size_t url_end = request.find_first_of(" &\r\n", url_start);
99
+ if (url_end == std::string::npos) url_end = request.length();
100
+
101
+ std::string url = request.substr(url_start, url_end - url_start);
102
+ return url_decode(url);
103
+ }
104
+
105
+ std::map<std::string, std::string> parse_custom_headers(const std::string& request) {
106
+ std::map<std::string, std::string> headers;
107
+
108
+ // Parse headers that start with X-Proxy- prefix
109
+ // These will be forwarded without the prefix
110
+ const char* prefixes[] = {"X-Proxy-Referer:", "X-Proxy-Origin:", "X-Proxy-User-Agent:"};
111
+ const char* header_names[] = {"Referer", "Origin", "User-Agent"};
112
+
113
+ for (int i = 0; i < 3; i++) {
114
+ size_t pos = request.find(prefixes[i]);
115
+ if (pos != std::string::npos) {
116
+ size_t value_start = pos + strlen(prefixes[i]);
117
+ // Skip leading whitespace
118
+ while (value_start < request.length() &&
119
+ (request[value_start] == ' ' || request[value_start] == '\t')) {
120
+ value_start++;
121
+ }
122
+ size_t value_end = request.find("\r\n", value_start);
123
+ if (value_end == std::string::npos) value_end = request.find("\n", value_start);
124
+ if (value_end != std::string::npos) {
125
+ headers[header_names[i]] = request.substr(value_start, value_end - value_start);
126
+ }
127
+ }
128
+ }
129
+
130
+ return headers;
131
+ }
132
+
133
+ // ============================================================================
134
+ // HTTP Response Helpers
135
+ // ============================================================================
136
+
137
+ void send_response(int client_fd, int status_code, const char* status_text,
138
+ const char* content_type, const char* body, size_t body_len) {
139
+ std::ostringstream response;
140
+ response << "HTTP/1.1 " << status_code << " " << status_text << "\r\n";
141
+ response << "Access-Control-Allow-Origin: *\r\n";
142
+ response << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n";
143
+ response << "Access-Control-Allow-Headers: *\r\n";
144
+ response << "Access-Control-Expose-Headers: *\r\n";
145
+ response << "Connection: close\r\n";
146
+ if (content_type) {
147
+ response << "Content-Type: " << content_type << "\r\n";
148
+ }
149
+ response << "Content-Length: " << body_len << "\r\n";
150
+ response << "\r\n";
151
+
152
+ std::string headers = response.str();
153
+ send(client_fd, headers.c_str(), headers.length(), 0);
154
+ if (body && body_len > 0) {
155
+ send(client_fd, body, body_len, 0);
156
+ }
157
+ }
158
+
159
+ void send_json_response(int client_fd, int status_code, const char* status_text, const char* json) {
160
+ send_response(client_fd, status_code, status_text, "application/json", json, strlen(json));
161
+ }
162
+
163
+ // ============================================================================
164
+ // Request Handlers
165
+ // ============================================================================
166
+
167
+ void handle_health_check(int client_fd) {
168
+ const char* json = "{\"status\":\"ok\",\"service\":\"LiveFootballTV Proxy\",\"port\":8888}";
169
+ send_json_response(client_fd, 200, "OK", json);
170
+ dlog_print(DLOG_DEBUG, LOG_TAG, "Health check: OK");
171
+ }
172
+
173
+ void handle_cors_preflight(int client_fd) {
174
+ send_response(client_fd, 200, "OK", NULL, "", 0);
175
+ dlog_print(DLOG_DEBUG, LOG_TAG, "CORS preflight handled");
176
+ }
177
+
178
+ void handle_proxy_request(int client_fd, const std::string& request) {
179
+ // Extract target URL
180
+ std::string target_url = parse_url_param(request);
181
+ if (target_url.empty()) {
182
+ const char* error = "{\"error\":\"Missing 'url' parameter\"}";
183
+ send_json_response(client_fd, 400, "Bad Request", error);
184
+ return;
185
+ }
186
+
187
+ dlog_print(DLOG_INFO, LOG_TAG, "Proxying: %s", target_url.c_str());
188
+
189
+ // Parse custom headers from request
190
+ auto custom_headers = parse_custom_headers(request);
191
+
192
+ // Initialize CURL
193
+ CURL* curl = curl_easy_init();
194
+ if (!curl) {
195
+ const char* error = "{\"error\":\"Failed to initialize HTTP client\"}";
196
+ send_json_response(client_fd, 500, "Internal Server Error", error);
197
+ return;
198
+ }
199
+
200
+ CurlResponse response;
201
+ response.http_code = 0;
202
+
203
+ // Build headers list
204
+ struct curl_slist* curl_headers = NULL;
205
+
206
+ // Add custom headers from request
207
+ for (const auto& h : custom_headers) {
208
+ std::string header = h.first + ": " + h.second;
209
+ curl_headers = curl_slist_append(curl_headers, header.c_str());
210
+ dlog_print(DLOG_DEBUG, LOG_TAG, "Adding header: %s", header.c_str());
211
+ }
212
+
213
+ // Default User-Agent if not provided
214
+ if (custom_headers.find("User-Agent") == custom_headers.end()) {
215
+ curl_headers = curl_slist_append(curl_headers,
216
+ "User-Agent: Mozilla/5.0 (SMART-TV; Tizen 4.0) AppleWebKit/538.1 (KHTML, like Gecko)");
217
+ }
218
+
219
+ // Configure CURL
220
+ curl_easy_setopt(curl, CURLOPT_URL, target_url.c_str());
221
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
222
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
223
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
224
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
225
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response);
226
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
227
+ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L);
228
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
229
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
230
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
231
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
232
+
233
+ // Perform request
234
+ CURLcode res = curl_easy_perform(curl);
235
+
236
+ if (res == CURLE_OK) {
237
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.http_code);
238
+
239
+ dlog_print(DLOG_INFO, LOG_TAG, "Proxy response: %ld, size: %zu",
240
+ response.http_code, response.data.size());
241
+
242
+ // Determine content type
243
+ const char* ct = response.content_type.empty() ?
244
+ "application/octet-stream" : response.content_type.c_str();
245
+
246
+ // Send response
247
+ send_response(client_fd, (int)response.http_code, "OK", ct,
248
+ response.data.data(), response.data.size());
249
+ } else {
250
+ dlog_print(DLOG_ERROR, LOG_TAG, "CURL error: %s", curl_easy_strerror(res));
251
+
252
+ std::ostringstream error_json;
253
+ error_json << "{\"error\":\"" << curl_easy_strerror(res) << "\"}";
254
+ std::string error_str = error_json.str();
255
+ send_json_response(client_fd, 502, "Bad Gateway", error_str.c_str());
256
+ }
257
+
258
+ // Cleanup
259
+ if (curl_headers) curl_slist_free_all(curl_headers);
260
+ curl_easy_cleanup(curl);
261
+ }
262
+
263
+ // ============================================================================
264
+ // Client Handler
265
+ // ============================================================================
266
+
267
+ void handle_client(int client_fd) {
268
+ char buffer[BUFFER_SIZE];
269
+ ssize_t bytes_read = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
270
+
271
+ if (bytes_read <= 0) {
272
+ close(client_fd);
273
+ return;
274
+ }
275
+
276
+ buffer[bytes_read] = '\0';
277
+ std::string request(buffer);
278
+
279
+ dlog_print(DLOG_DEBUG, LOG_TAG, "Request received (%zd bytes)", bytes_read);
280
+
281
+ // Route request
282
+ if (request.find("GET /health") != std::string::npos) {
283
+ handle_health_check(client_fd);
284
+ }
285
+ else if (request.find("OPTIONS ") == 0) {
286
+ handle_cors_preflight(client_fd);
287
+ }
288
+ else if (request.find("/proxy?") != std::string::npos ||
289
+ request.find("/proxy?") != std::string::npos) {
290
+ handle_proxy_request(client_fd, request);
291
+ }
292
+ else {
293
+ const char* error = "{\"error\":\"Unknown endpoint. Use /health or /proxy?url=...\"}";
294
+ send_json_response(client_fd, 404, "Not Found", error);
295
+ }
296
+
297
+ close(client_fd);
298
+ }
299
+
300
+ // ============================================================================
301
+ // Server Thread
302
+ // ============================================================================
303
+
304
+ void* server_loop(void* arg) {
305
+ dlog_print(DLOG_INFO, LOG_TAG, "Server thread started");
306
+
307
+ while (running) {
308
+ struct sockaddr_in client_addr;
309
+ socklen_t client_len = sizeof(client_addr);
310
+
311
+ // Use select with timeout to allow checking 'running' flag
312
+ fd_set read_fds;
313
+ FD_ZERO(&read_fds);
314
+ FD_SET(server_fd, &read_fds);
315
+
316
+ struct timeval timeout;
317
+ timeout.tv_sec = 1;
318
+ timeout.tv_usec = 0;
319
+
320
+ int ready = select(server_fd + 1, &read_fds, NULL, NULL, &timeout);
321
+
322
+ if (ready > 0 && FD_ISSET(server_fd, &read_fds)) {
323
+ int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
324
+ if (client_fd >= 0) {
325
+ handle_client(client_fd);
326
+ }
327
+ }
328
+ }
329
+
330
+ dlog_print(DLOG_INFO, LOG_TAG, "Server thread exiting");
331
+ return NULL;
332
+ }
333
+
334
+ // ============================================================================
335
+ // Server Start/Stop
336
+ // ============================================================================
337
+
338
+ bool start_server() {
339
+ server_fd = socket(AF_INET, SOCK_STREAM, 0);
340
+ if (server_fd < 0) {
341
+ dlog_print(DLOG_ERROR, LOG_TAG, "Socket creation failed: %s", strerror(errno));
342
+ return false;
343
+ }
344
+
345
+ // Allow address reuse
346
+ int opt = 1;
347
+ if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
348
+ dlog_print(DLOG_WARN, LOG_TAG, "setsockopt SO_REUSEADDR failed");
349
+ }
350
+
351
+ // Bind to all interfaces on PROXY_PORT
352
+ struct sockaddr_in server_addr;
353
+ memset(&server_addr, 0, sizeof(server_addr));
354
+ server_addr.sin_family = AF_INET;
355
+ server_addr.sin_addr.s_addr = INADDR_ANY;
356
+ server_addr.sin_port = htons(PROXY_PORT);
357
+
358
+ if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
359
+ dlog_print(DLOG_ERROR, LOG_TAG, "Bind failed on port %d: %s", PROXY_PORT, strerror(errno));
360
+ close(server_fd);
361
+ server_fd = -1;
362
+ return false;
363
+ }
364
+
365
+ if (listen(server_fd, MAX_CLIENTS) < 0) {
366
+ dlog_print(DLOG_ERROR, LOG_TAG, "Listen failed: %s", strerror(errno));
367
+ close(server_fd);
368
+ server_fd = -1;
369
+ return false;
370
+ }
371
+
372
+ running = true;
373
+
374
+ if (pthread_create(&server_thread, NULL, server_loop, NULL) != 0) {
375
+ dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create server thread");
376
+ close(server_fd);
377
+ server_fd = -1;
378
+ running = false;
379
+ return false;
380
+ }
381
+
382
+ dlog_print(DLOG_INFO, LOG_TAG, "=== Proxy Server Started on port %d ===", PROXY_PORT);
383
+ return true;
384
+ }
385
+
386
+ void stop_server() {
387
+ dlog_print(DLOG_INFO, LOG_TAG, "Stopping proxy server...");
388
+
389
+ running = false;
390
+
391
+ if (server_fd >= 0) {
392
+ shutdown(server_fd, SHUT_RDWR);
393
+ close(server_fd);
394
+ server_fd = -1;
395
+ }
396
+
397
+ pthread_join(server_thread, NULL);
398
+
399
+ dlog_print(DLOG_INFO, LOG_TAG, "=== Proxy Server Stopped ===");
400
+ }
401
+
402
+ // ============================================================================
403
+ // Service Lifecycle Callbacks
404
+ // ============================================================================
405
+
406
+ bool service_app_create(void* data) {
407
+ dlog_print(DLOG_INFO, LOG_TAG, "=== Service Creating ===");
408
+
409
+ // Initialize CURL globally
410
+ CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
411
+ if (res != CURLE_OK) {
412
+ dlog_print(DLOG_ERROR, LOG_TAG, "CURL global init failed: %s", curl_easy_strerror(res));
413
+ return false;
414
+ }
415
+
416
+ // Start the proxy server
417
+ if (!start_server()) {
418
+ curl_global_cleanup();
419
+ return false;
420
+ }
421
+
422
+ dlog_print(DLOG_INFO, LOG_TAG, "=== Service Created Successfully ===");
423
+ return true;
424
+ }
425
+
426
+ void service_app_terminate(void* data) {
427
+ dlog_print(DLOG_INFO, LOG_TAG, "=== Service Terminating ===");
428
+
429
+ stop_server();
430
+ curl_global_cleanup();
431
+
432
+ dlog_print(DLOG_INFO, LOG_TAG, "=== Service Terminated ===");
433
+ }
434
+
435
+ void service_app_control(app_control_h app_control, void* data) {
436
+ dlog_print(DLOG_INFO, LOG_TAG, "Service app_control received");
437
+ // Handle any incoming app control requests here
438
+ }
439
+
440
+ // ============================================================================
441
+ // Main Entry Point
442
+ // ============================================================================
443
+
444
+ int main(int argc, char* argv[]) {
445
+ service_app_lifecycle_callback_s event_callback;
446
+ memset(&event_callback, 0, sizeof(event_callback));
447
+
448
+ event_callback.create = service_app_create;
449
+ event_callback.terminate = service_app_terminate;
450
+ event_callback.app_control = service_app_control;
451
+
452
+ return service_app_main(argc, argv, &event_callback, NULL);
453
+ }
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <manifest xmlns="http://tizen.org/ns/packages"
3
+ api-version="4.0"
4
+ package="LIVEFTV001"
5
+ version="1.0.0">
6
+ <profile name="tv"/>
7
+
8
+ <!-- Native Service Application -->
9
+ <service-application appid="LIVEFTV001.proxyservice"
10
+ exec="proxyservice"
11
+ type="capp"
12
+ nodisplay="true"
13
+ multiple="false"
14
+ auto-restart="true"
15
+ on-boot="false">
16
+ <label>Proxy Service</label>
17
+ </service-application>
18
+
19
+ <privileges>
20
+ <privilege>http://tizen.org/privilege/internet</privilege>
21
+ <privilege>http://tizen.org/privilege/network.connection</privilege>
22
+ </privileges>
23
+ </manifest>