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.
package/app/styles.css ADDED
@@ -0,0 +1,539 @@
1
+ /* styles.css – YouTube-style Grid Layout with Samsung TV Compatibility + D-Pad Navigation */
2
+
3
+ * {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ html,
10
+ body {
11
+ height: 100%;
12
+ margin: 0;
13
+ background: #0f0f0f;
14
+ /* YouTube dark background */
15
+ color: #f1f1f1;
16
+ font-family: 'Inter', sans-serif;
17
+ font-weight: 400;
18
+ display: -webkit-box;
19
+ display: -ms-flexbox;
20
+ display: flex;
21
+ -webkit-box-orient: vertical;
22
+ -webkit-box-direction: normal;
23
+ -ms-flex-direction: column;
24
+ flex-direction: column;
25
+ overflow: hidden;
26
+ }
27
+
28
+ /* Header */
29
+ .header {
30
+ display: -webkit-box;
31
+ display: -ms-flexbox;
32
+ display: flex;
33
+ -webkit-box-pack: justify;
34
+ -ms-flex-pack: justify;
35
+ justify-content: space-between;
36
+ -webkit-box-align: center;
37
+ -ms-flex-align: center;
38
+ align-items: center;
39
+ padding: 0 16px;
40
+ height: 56px;
41
+ background: #0f0f0f;
42
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
43
+ -webkit-flex-shrink: 0;
44
+ -ms-flex-negative: 0;
45
+ flex-shrink: 0;
46
+ z-index: 100;
47
+ }
48
+
49
+ .header h1 {
50
+ font-size: 1.2rem;
51
+ color: #fff;
52
+ font-weight: 600;
53
+ letter-spacing: -0.5px;
54
+ }
55
+
56
+ #search {
57
+ padding: 0 16px;
58
+ height: 40px;
59
+ border-radius: 20px;
60
+ border: 1px solid #303030;
61
+ background: #121212;
62
+ color: #fff;
63
+ outline: none;
64
+ width: 100%;
65
+ max-width: 400px;
66
+ font-size: 16px;
67
+ }
68
+
69
+ #search:focus {
70
+ border-color: #1c62b9;
71
+ background: #000;
72
+ outline: 3px solid #3ea6ff;
73
+ outline-offset: 2px;
74
+ }
75
+
76
+ /* Main Layout */
77
+ .main {
78
+ display: -webkit-box;
79
+ display: -ms-flexbox;
80
+ display: flex;
81
+ -webkit-box-flex: 1;
82
+ -ms-flex: 1;
83
+ flex: 1;
84
+ overflow: hidden;
85
+ }
86
+
87
+ /* Sidebar (Categories) */
88
+ .sidebar {
89
+ width: 240px;
90
+ background: #0f0f0f;
91
+ overflow-y: auto;
92
+ padding: 12px;
93
+ display: -webkit-box;
94
+ display: -ms-flexbox;
95
+ display: flex;
96
+ -webkit-box-orient: vertical;
97
+ -webkit-box-direction: normal;
98
+ -ms-flex-direction: column;
99
+ flex-direction: column;
100
+ -webkit-flex-shrink: 0;
101
+ -ms-flex-negative: 0;
102
+ flex-shrink: 0;
103
+ scrollbar-width: thin;
104
+ scrollbar-color: #717171 transparent;
105
+ }
106
+
107
+ .sidebar::-webkit-scrollbar {
108
+ width: 8px;
109
+ }
110
+
111
+ .sidebar::-webkit-scrollbar-thumb {
112
+ background-color: #717171;
113
+ border-radius: 4px;
114
+ }
115
+
116
+ .category {
117
+ display: -webkit-box;
118
+ display: -ms-flexbox;
119
+ display: flex;
120
+ -webkit-box-align: center;
121
+ -ms-flex-align: center;
122
+ align-items: center;
123
+ padding: 0 12px;
124
+ height: 40px;
125
+ border-radius: 10px;
126
+ cursor: pointer;
127
+ margin-bottom: 4px;
128
+ transition: background 0.2s;
129
+ }
130
+
131
+ .category:hover,
132
+ .category:focus {
133
+ background: #272727;
134
+ outline: none;
135
+ }
136
+
137
+ /* Enhanced D-pad focus for categories */
138
+ .category:focus {
139
+ background: #3ea6ff !important;
140
+ outline: 3px solid #fff !important;
141
+ outline-offset: -3px;
142
+ }
143
+
144
+ .category.active {
145
+ background: #272727;
146
+ font-weight: 600;
147
+ }
148
+
149
+ .category img {
150
+ width: 24px;
151
+ height: 24px;
152
+ margin-right: 24px;
153
+ border-radius: 50%;
154
+ object-fit: cover;
155
+ }
156
+
157
+ .category span {
158
+ font-size: 14px;
159
+ white-space: nowrap;
160
+ overflow: hidden;
161
+ text-overflow: ellipsis;
162
+ }
163
+
164
+ /* Content Area (Posts Grid) */
165
+ .content {
166
+ -webkit-box-flex: 1;
167
+ -ms-flex: 1;
168
+ flex: 1;
169
+ overflow-y: auto;
170
+ padding: 24px;
171
+ background: #0f0f0f;
172
+ scrollbar-width: thin;
173
+ scrollbar-color: #717171 transparent;
174
+ }
175
+
176
+ .content::-webkit-scrollbar {
177
+ width: 8px;
178
+ }
179
+
180
+ .content::-webkit-scrollbar-thumb {
181
+ background-color: #717171;
182
+ border-radius: 4px;
183
+ }
184
+
185
+ /* Grid Layout using Flexbox for max compatibility */
186
+ .posts-grid {
187
+ display: -webkit-box;
188
+ display: -ms-flexbox;
189
+ display: flex;
190
+ -webkit-flex-wrap: wrap;
191
+ -ms-flex-wrap: wrap;
192
+ flex-wrap: wrap;
193
+ margin: -8px;
194
+ }
195
+
196
+ .post {
197
+ display: -webkit-box;
198
+ display: -ms-flexbox;
199
+ display: flex;
200
+ -webkit-box-orient: vertical;
201
+ -webkit-box-direction: normal;
202
+ -ms-flex-direction: column;
203
+ flex-direction: column;
204
+ width: 25%;
205
+ padding: 8px;
206
+ cursor: pointer;
207
+ border: none;
208
+ background: transparent;
209
+ outline: none;
210
+ }
211
+
212
+ /* Responsive Grid */
213
+ @media (max-width: 1400px) {
214
+ .post {
215
+ width: 33.333%;
216
+ }
217
+ }
218
+
219
+ @media (max-width: 1000px) {
220
+ .post {
221
+ width: 50%;
222
+ }
223
+ }
224
+
225
+ @media (max-width: 600px) {
226
+ .post {
227
+ width: 100%;
228
+ }
229
+ }
230
+
231
+ .post:hover .thumbnail-container,
232
+ .post:focus .thumbnail-container {
233
+ transform: scale(1.02);
234
+ box-shadow: 0 0 0 2px #3ea6ff;
235
+ }
236
+
237
+ /* Enhanced D-pad focus for posts */
238
+ .post:focus {
239
+ background: #1a1a1a;
240
+ border-radius: 12px;
241
+ outline: 3px solid #3ea6ff !important;
242
+ outline-offset: 4px;
243
+ }
244
+
245
+ /* Prevent outline on mouse focus, keep for keyboard */
246
+ .post:focus:not(:focus-visible) {
247
+ outline: none;
248
+ background: transparent;
249
+ }
250
+
251
+ .thumbnail-container {
252
+ position: relative;
253
+ width: 100%;
254
+ padding-bottom: 56.25%;
255
+ border-radius: 12px;
256
+ overflow: hidden;
257
+ background: #202020;
258
+ margin-bottom: 12px;
259
+ transition: transform 0.2s;
260
+ }
261
+
262
+ .post img {
263
+ position: absolute;
264
+ top: 0;
265
+ left: 0;
266
+ width: 100%;
267
+ height: 100%;
268
+ object-fit: cover;
269
+ }
270
+
271
+ .post .info {
272
+ display: -webkit-box;
273
+ display: -ms-flexbox;
274
+ display: flex;
275
+ -webkit-box-orient: vertical;
276
+ -webkit-box-direction: normal;
277
+ -ms-flex-direction: column;
278
+ flex-direction: column;
279
+ padding: 0 4px;
280
+ }
281
+
282
+ .post .info h3 {
283
+ font-size: 16px;
284
+ line-height: 1.4rem;
285
+ font-weight: 600;
286
+ color: #f1f1f1;
287
+ margin-bottom: 4px;
288
+ display: -webkit-box;
289
+ -webkit-line-clamp: 2;
290
+ -webkit-box-orient: vertical;
291
+ overflow: hidden;
292
+ }
293
+
294
+ .post .info p {
295
+ font-size: 14px;
296
+ color: #aaa;
297
+ white-space: nowrap;
298
+ overflow: hidden;
299
+ text-overflow: ellipsis;
300
+ }
301
+
302
+ /* Pagination */
303
+ .pagination {
304
+ display: -webkit-box;
305
+ display: -ms-flexbox;
306
+ display: flex;
307
+ -webkit-box-pack: center;
308
+ -ms-flex-pack: center;
309
+ justify-content: center;
310
+ margin-top: 32px;
311
+ padding-bottom: 32px;
312
+ }
313
+
314
+ .pagination button {
315
+ padding: 8px 16px;
316
+ background: #272727;
317
+ border: none;
318
+ border-radius: 18px;
319
+ color: #f1f1f1;
320
+ cursor: pointer;
321
+ font-size: 14px;
322
+ font-weight: 500;
323
+ margin: 0 8px;
324
+ transition: background 0.2s;
325
+ }
326
+
327
+ .pagination button:hover:not(:disabled),
328
+ .pagination button:focus {
329
+ background: #3ea6ff;
330
+ color: #0f0f0f;
331
+ outline: none;
332
+ }
333
+
334
+ .pagination button:disabled {
335
+ opacity: 0.5;
336
+ cursor: not-allowed;
337
+ }
338
+
339
+ /* Loader */
340
+ #loader {
341
+ position: fixed;
342
+ top: 50%;
343
+ left: 50%;
344
+ transform: translate(-50%, -50%);
345
+ border: 4px solid rgba(255, 255, 255, 0.1);
346
+ border-top: 4px solid #3ea6ff;
347
+ border-radius: 50%;
348
+ width: 48px;
349
+ height: 48px;
350
+ animation: spin 1s linear infinite;
351
+ z-index: 2000;
352
+ }
353
+
354
+ #loader[aria-hidden="true"] {
355
+ display: none;
356
+ }
357
+
358
+ @keyframes spin {
359
+ to {
360
+ transform: translate(-50%, -50%) rotate(360deg);
361
+ }
362
+ }
363
+
364
+ /* Video Player Modal */
365
+ .video-modal {
366
+ position: fixed;
367
+ top: 0;
368
+ left: 0;
369
+ width: 100%;
370
+ height: 100%;
371
+ background: #000;
372
+ z-index: 3000;
373
+ display: -webkit-box;
374
+ display: -ms-flexbox;
375
+ display: flex;
376
+ -webkit-box-align: center;
377
+ -ms-flex-align: center;
378
+ align-items: center;
379
+ -webkit-box-pack: center;
380
+ -ms-flex-pack: center;
381
+ justify-content: center;
382
+ }
383
+
384
+ .video-modal[hidden] {
385
+ display: none;
386
+ }
387
+
388
+ .video-modal-content {
389
+ width: 100%;
390
+ height: 100%;
391
+ display: -webkit-box;
392
+ display: -ms-flexbox;
393
+ display: flex;
394
+ -webkit-box-orient: vertical;
395
+ -webkit-box-direction: normal;
396
+ -ms-flex-direction: column;
397
+ flex-direction: column;
398
+ }
399
+
400
+ .video-container {
401
+ -webkit-box-flex: 1;
402
+ -ms-flex: 1;
403
+ flex: 1;
404
+ position: relative;
405
+ background: #000;
406
+ display: -webkit-box;
407
+ display: -ms-flexbox;
408
+ display: flex;
409
+ -webkit-box-align: center;
410
+ -ms-flex-align: center;
411
+ align-items: center;
412
+ -webkit-box-pack: center;
413
+ -ms-flex-pack: center;
414
+ justify-content: center;
415
+ }
416
+
417
+ .video-container video,
418
+ .video-container iframe {
419
+ width: 100%;
420
+ height: 100%;
421
+ max-width: 100%;
422
+ max-height: 100%;
423
+ border: none;
424
+ display: none;
425
+ }
426
+
427
+ .video-container video.active,
428
+ .video-container iframe.active {
429
+ display: block;
430
+ }
431
+
432
+ .video-controls {
433
+ padding: 16px;
434
+ background: rgba(0, 0, 0, 0.8);
435
+ display: -webkit-box;
436
+ display: -ms-flexbox;
437
+ display: flex;
438
+ -webkit-box-pack: center;
439
+ -ms-flex-pack: center;
440
+ justify-content: center;
441
+ -webkit-flex-wrap: wrap;
442
+ -ms-flex-wrap: wrap;
443
+ flex-wrap: wrap;
444
+ }
445
+
446
+ .control-btn {
447
+ padding: 8px 16px;
448
+ background: transparent;
449
+ border: none;
450
+ color: #fff;
451
+ cursor: pointer;
452
+ font-size: 14px;
453
+ font-weight: 500;
454
+ margin: 0 8px;
455
+ border-radius: 4px;
456
+ }
457
+
458
+ .control-btn:hover,
459
+ .control-btn:focus {
460
+ background: rgba(255, 255, 255, 0.1);
461
+ outline: 3px solid #3ea6ff !important;
462
+ outline-offset: 2px;
463
+ }
464
+
465
+ .close-btn {
466
+ position: absolute;
467
+ top: 16px;
468
+ right: 16px;
469
+ background: rgba(0, 0, 0, 0.5);
470
+ border: none;
471
+ color: #fff;
472
+ font-size: 24px;
473
+ width: 48px;
474
+ height: 48px;
475
+ border-radius: 50%;
476
+ cursor: pointer;
477
+ z-index: 3001;
478
+ display: -webkit-box;
479
+ display: -ms-flexbox;
480
+ display: flex;
481
+ -webkit-box-align: center;
482
+ -ms-flex-align: center;
483
+ align-items: center;
484
+ -webkit-box-pack: center;
485
+ -ms-flex-pack: center;
486
+ justify-content: center;
487
+ }
488
+
489
+ .close-btn:hover,
490
+ .close-btn:focus {
491
+ background: rgba(255, 255, 255, 0.2);
492
+ outline: 3px solid #3ea6ff !important;
493
+ outline-offset: 2px;
494
+ }
495
+
496
+ /* Mobile adjustments */
497
+ @media (max-width: 768px) {
498
+ .main {
499
+ -webkit-box-orient: vertical;
500
+ -webkit-box-direction: normal;
501
+ -ms-flex-direction: column;
502
+ flex-direction: column;
503
+ }
504
+
505
+ .sidebar {
506
+ width: 100%;
507
+ height: auto;
508
+ max-height: 60px;
509
+ -webkit-box-orient: horizontal;
510
+ -webkit-box-direction: normal;
511
+ -ms-flex-direction: row;
512
+ flex-direction: row;
513
+ overflow-x: auto;
514
+ overflow-y: hidden;
515
+ padding: 8px;
516
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
517
+ }
518
+
519
+ .category {
520
+ margin-right: 8px;
521
+ margin-bottom: 0;
522
+ background: #272727;
523
+ border-radius: 8px;
524
+ padding: 0 12px;
525
+ height: 32px;
526
+ }
527
+
528
+ .category img {
529
+ display: none;
530
+ }
531
+
532
+ .sidebar.hidden-mobile {
533
+ display: flex;
534
+ }
535
+
536
+ .content.hidden-mobile {
537
+ display: block;
538
+ }
539
+ }
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "livefootballtv-tizen",
3
+ "packageType": "app",
4
+ "appName": "LiveFootballTV",
5
+ "appPath": "app/index.html",
6
+ "version": "1.0.0",
7
+ "serviceFile": "service.js",
8
+ "description": "LiveFootballTV webapp with header modification for Tizen TVs",
9
+ "author": "midozalouk",
10
+ "license": "MIT"
11
+ }
package/service.js ADDED
@@ -0,0 +1,121 @@
1
+ /**
2
+ * LiveFootballTV TizenBrew Service
3
+ * Provides header modification for streams requiring CORS bypass
4
+ */
5
+
6
+ const http = require('http');
7
+ const https = require('https');
8
+ const { URL } = require('url');
9
+
10
+ const PORT = 8888;
11
+
12
+ // Domain-specific header configuration
13
+ const HEADER_CONFIG = {
14
+ 'merichunidya.com': {
15
+ referer: 'https://vividmosaica.com/',
16
+ origin: 'https://vividmosaica.com'
17
+ },
18
+ 'vividmosaica.com': {
19
+ referer: 'https://vividmosaica.com/',
20
+ origin: 'https://vividmosaica.com'
21
+ }
22
+ };
23
+
24
+ function getHeaders(targetUrl) {
25
+ for (const domain in HEADER_CONFIG) {
26
+ if (targetUrl.includes(domain)) {
27
+ return HEADER_CONFIG[domain];
28
+ }
29
+ }
30
+ return {};
31
+ }
32
+
33
+ const server = http.createServer((req, res) => {
34
+ // CORS headers
35
+ res.setHeader('Access-Control-Allow-Origin', '*');
36
+ res.setHeader('Access-Control-Allow-Headers', '*');
37
+ res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS');
38
+
39
+ if (req.method === 'OPTIONS') {
40
+ res.writeHead(200);
41
+ return res.end();
42
+ }
43
+
44
+ // Health check endpoint
45
+ if (req.url === '/health') {
46
+ res.writeHead(200, { 'Content-Type': 'application/json' });
47
+ return res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));
48
+ }
49
+
50
+ // Proxy endpoint: ?url=<encoded_url>
51
+ const parsed = new URL(req.url, `http://localhost:${PORT}`);
52
+ const target = parsed.searchParams.get('url');
53
+
54
+ if (!target) {
55
+ res.writeHead(400);
56
+ return res.end('Missing url parameter');
57
+ }
58
+
59
+ let targetUrl;
60
+ try {
61
+ targetUrl = new URL(decodeURIComponent(target));
62
+ } catch {
63
+ res.writeHead(400);
64
+ return res.end('Invalid URL');
65
+ }
66
+
67
+ const proto = targetUrl.protocol === 'https:' ? https : http;
68
+ const customHeaders = getHeaders(targetUrl.href);
69
+
70
+ const options = {
71
+ protocol: targetUrl.protocol,
72
+ hostname: targetUrl.hostname,
73
+ port: targetUrl.port || (targetUrl.protocol === 'https:' ? 443 : 80),
74
+ path: targetUrl.pathname + targetUrl.search,
75
+ method: req.method,
76
+ headers: {
77
+ 'User-Agent': 'Mozilla/5.0 (SMART-TV; Tizen)',
78
+ 'Accept': '*/*',
79
+ 'Connection': 'close'
80
+ },
81
+ rejectUnauthorized: false
82
+ };
83
+
84
+ // Add Range header support for AVPlay
85
+ if (req.headers.range) {
86
+ options.headers['Range'] = req.headers.range;
87
+ }
88
+
89
+ // Add custom headers (Referer, Origin)
90
+ if (customHeaders.referer) options.headers['Referer'] = customHeaders.referer;
91
+ if (customHeaders.origin) options.headers['Origin'] = customHeaders.origin;
92
+
93
+ const proxyReq = proto.request(options, proxyRes => {
94
+ // Forward status and headers (important for 206 partial content)
95
+ res.writeHead(proxyRes.statusCode, proxyRes.headers);
96
+
97
+ if (req.method === 'HEAD') return res.end();
98
+
99
+ proxyRes.pipe(res);
100
+ });
101
+
102
+ proxyReq.on('error', err => {
103
+ res.writeHead(502);
104
+ res.end('Proxy error: ' + err.message);
105
+ });
106
+
107
+ proxyReq.setTimeout(30000, () => {
108
+ proxyReq.destroy();
109
+ res.writeHead(504);
110
+ res.end('Timeout');
111
+ });
112
+
113
+ proxyReq.end();
114
+ });
115
+
116
+ server.listen(PORT, () => {
117
+ console.log('====================================');
118
+ console.log('LiveFootballTV Service (Node.js)');
119
+ console.log('Proxy listening on port', PORT);
120
+ console.log('====================================');
121
+ });