1 /*
2 * Copyright (C) 2000-2019 the xine project
3 *
4 * This file is part of xine, a unix video player.
5 *
6 * xine 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 * xine 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <dirent.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <errno.h>
34
35 #include <X11/keysym.h>
36 #include "common.h"
37
38 #define WINDOW_WIDTH 525
39 #define WINDOW_HEIGHT 270
40
41 static struct {
42 apply_callback_t callback;
43 void *user_data;
44
45 xitk_window_t *xwin;
46
47 xitk_widget_list_t *widget_list;
48
49 xitk_widget_t *mrl;
50 xitk_widget_t *ident;
51 xitk_widget_t *sub;
52 xitk_widget_t *start;
53 xitk_widget_t *end;
54 xitk_widget_t *av_offset;
55 xitk_widget_t *spu_offset;
56
57 mediamark_t **mmk;
58
59 int running;
60 int visible;
61 xitk_register_key_t widget_key;
62 } mmkeditor;
63
64
65 typedef struct {
66 char *data;
67 char **lines;
68 int numl;
69
70 int entries;
71 char *type;
72 } playlist_t;
73
74 typedef mediamark_t **(*playlist_guess_func_t)(playlist_t *, const char *);
75 typedef mediamark_t **(*playlist_xml_guess_func_t)(playlist_t *, const char *, char *xml_content, xml_node_t *);
76
_download_file(const char * filename,int * size)77 static char *_download_file(const char *filename, int *size) {
78 download_t *download;
79 char *buf = NULL;
80
81 if((!filename) || (!strlen(filename))) {
82 fprintf(stderr, "%s(): Empty or NULL filename.\n", __XINE_FUNCTION__);
83 return NULL;
84 }
85
86 download = (download_t *) calloc(1, sizeof(download_t));
87 download->buf = NULL;
88 download->error = NULL;
89 download->size = 0;
90 download->status = 0;
91
92 if((network_download(filename, download))) {
93 *size = download->size;
94 buf = (char *) malloc(*size + 1);
95 if (buf) {
96 memcpy(buf, download->buf, *size);
97 buf[*size] = 0;
98 }
99 }
100 else
101 xine_error("Unable to download '%s': %s", filename, download->error);
102
103 SAFE_FREE(download->buf);
104 SAFE_FREE(download->error);
105 free(download);
106
107 return buf;
108 }
109
110 #if 0
111 static int _file_exist(char *filename) {
112 struct stat st;
113
114 if(filename && (stat(filename, &st) == 0))
115 return 1;
116
117 return 0;
118 }
119 #endif
120
mrl_look_like_playlist(const char * mrl)121 int mrl_look_like_playlist (const char *mrl) {
122 /* TJ. I dont know whether someone really needs to treat
123 * "foo/bar.m3under/the/table" as an m3u playlist.
124 * Lets keep this behaviour for now, but make sure that
125 * hls (.m3u8) goes to xine-lib verbatim. */
126 const char *extension = strrchr (mrl, '.');
127 if (extension) {
128 /* All known playlist ending */
129 if ((!strncasecmp (extension, ".asx", 4)) ||
130 (!strncasecmp (extension, ".smi", 4)) ||
131 /*(!strncasecmp (extension, ".smil", 5)) || caught by ".smi" */
132 (!strncasecmp (extension, ".pls", 4)) ||
133 (!strncasecmp (extension, ".sfv", 4)) ||
134 (!strncasecmp (extension, ".xml", 4)) ||
135 (!strncasecmp (extension, ".tox", 4)) ||
136 (!strncasecmp (extension, ".fxd", 4)))
137 return 1;
138 if ((!strncasecmp (extension, ".m3u", 4)) && (extension[4] != '8'))
139 return 1;
140 }
141 return 0;
142 }
143
_read_file(const char * filename,int * size)144 static char *_read_file(const char *filename, int *size) {
145 struct stat st;
146 char *buf = NULL;
147 int fd, bytes_read;
148
149 if((!filename) || (!strlen(filename))) {
150 fprintf(stderr, "%s(): Empty or NULL filename.\n", __XINE_FUNCTION__);
151 return NULL;
152 }
153
154 if (mrl_look_like_playlist (filename) && is_downloadable ((char *)filename))
155 return _download_file(filename, size);
156
157 if(stat(filename, &st) < 0) {
158 fprintf(stderr, "%s(): Unable to stat() '%s' file: %s.\n",
159 __XINE_FUNCTION__, filename, strerror(errno));
160 return NULL;
161 }
162
163 if((*size = st.st_size) == 0) {
164 fprintf(stderr, "%s(): File '%s' is empty.\n", __XINE_FUNCTION__, filename);
165 return NULL;
166 }
167
168 if((fd = xine_open_cloexec(filename, O_RDONLY)) == -1) {
169 fprintf(stderr, "%s(): open(%s) failed: %s.\n", __XINE_FUNCTION__, filename, strerror(errno));
170 return NULL;
171 }
172
173 if((buf = (char *) malloc(*size + 1)) == NULL) {
174 fprintf(stderr, "%s(): malloc() failed.\n", __XINE_FUNCTION__);
175 close(fd);
176 return NULL;
177 }
178
179 if((bytes_read = read(fd, buf, *size)) != *size) {
180 fprintf(stderr, "%s(): read() return wrong size (%d / %d): %s.\n",
181 __XINE_FUNCTION__, bytes_read, *size, strerror(errno));
182 *size = bytes_read;
183 }
184
185 close(fd);
186
187 buf[*size] = '\0';
188
189 return buf;
190 }
191
mediamark_have_alternates(mediamark_t * mmk)192 int mediamark_have_alternates(mediamark_t *mmk) {
193 if(mmk && mmk->alternates)
194 return 1;
195
196 return 0;
197 }
mediamark_free_alternates(mediamark_t * mmk)198 void mediamark_free_alternates(mediamark_t *mmk) {
199 if(mmk && mediamark_have_alternates(mmk)) {
200 alternate_t *alt = mmk->alternates;
201
202 while(alt) {
203 alternate_t *c_alt = alt;
204
205 free(alt->mrl);
206
207 c_alt = alt;
208 alt = alt->next;
209 free(c_alt);
210 }
211 mmk->alternates = NULL;
212 mmk->cur_alt = NULL;
213 }
214 }
mediamark_get_first_alternate_mrl(mediamark_t * mmk)215 char *mediamark_get_first_alternate_mrl(mediamark_t *mmk) {
216 if(mmk && mmk->alternates) {
217 mmk->cur_alt = mmk->alternates;
218 return mmk->cur_alt->mrl;
219 }
220 return NULL;
221 }
mediamark_get_next_alternate_mrl(mediamark_t * mmk)222 char *mediamark_get_next_alternate_mrl(mediamark_t *mmk) {
223 if(mmk && mmk->cur_alt) {
224 if(mmk->cur_alt->next) {
225 mmk->cur_alt = mmk->cur_alt->next;
226 return mmk->cur_alt->mrl;
227 }
228 }
229 return NULL;
230 }
mediamark_get_current_alternate_mrl(mediamark_t * mmk)231 char *mediamark_get_current_alternate_mrl(mediamark_t *mmk) {
232 if(mmk && mmk->cur_alt)
233 return mmk->cur_alt->mrl;
234
235 return NULL;
236 }
mediamark_append_alternate_mrl(mediamark_t * mmk,const char * mrl)237 void mediamark_append_alternate_mrl(mediamark_t *mmk, const char *mrl) {
238 if(mmk && mrl) {
239 alternate_t *alt;
240
241 alt = (alternate_t *) calloc(1, sizeof(alternate_t));
242 alt->mrl = strdup(mrl);
243 alt->next = NULL;
244
245 if(mmk->alternates) {
246 alternate_t *p_alt = mmk->alternates;
247
248 while(p_alt->next)
249 p_alt = p_alt->next;
250 p_alt->next = alt;
251 }
252 else
253 mmk->alternates = alt;
254
255 }
256 }
mediamark_duplicate_alternates(mediamark_t * s_mmk,mediamark_t * d_mmk)257 void mediamark_duplicate_alternates(mediamark_t *s_mmk, mediamark_t *d_mmk) {
258 if(s_mmk && s_mmk->alternates && d_mmk) {
259 alternate_t *alt;
260
261 if((alt = s_mmk->alternates)) {
262 alternate_t *c_alt, *p_alt = NULL, *t_alt = NULL;
263
264 while(alt) {
265
266 c_alt = (alternate_t *) calloc(1, sizeof(alternate_t));
267 c_alt->mrl = strdup(alt->mrl);
268 c_alt->next = NULL;
269
270 if(!p_alt)
271 t_alt = p_alt = c_alt;
272 else {
273 p_alt->next = c_alt;
274 p_alt = c_alt;
275 }
276
277 alt = alt->next;
278 }
279
280 d_mmk->alternates = t_alt;
281 d_mmk->cur_alt = NULL;
282
283 }
284 }
285 }
mediamark_got_alternate(mediamark_t * mmk)286 int mediamark_got_alternate(mediamark_t *mmk) {
287 if(mmk && mmk->got_alternate)
288 return 1;
289 return 0;
290 }
mediamark_set_got_alternate(mediamark_t * mmk)291 void mediamark_set_got_alternate(mediamark_t *mmk) {
292 if(mmk)
293 mmk->got_alternate = 1;
294 }
mediamark_unset_got_alternate(mediamark_t * mmk)295 void mediamark_unset_got_alternate(mediamark_t *mmk) {
296 if(mmk)
297 mmk->got_alternate = 0;
298 }
mediamark_store_mmk(mediamark_t ** mmk,const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)299 int mediamark_store_mmk(mediamark_t **mmk,
300 const char *mrl, const char *ident, const char *sub,
301 int start, int end, int av_offset, int spu_offset) {
302
303 if(mmk && mrl) {
304
305 (*mmk) = (mediamark_t *) calloc(1, sizeof(mediamark_t));
306 (*mmk)->mrl = strdup(mrl);
307 (*mmk)->ident = strdup((ident != NULL) ? ident : mrl);
308 (*mmk)->sub = (sub != NULL) ? strdup(sub) : NULL;
309 (*mmk)->start = start;
310 (*mmk)->end = end;
311 (*mmk)->av_offset = av_offset;
312 (*mmk)->spu_offset = spu_offset;
313 (*mmk)->played = 0;
314 (*mmk)->got_alternate = 0;
315 (*mmk)->cur_alt = NULL;
316 (*mmk)->alternates = NULL;
317
318 return 1;
319 }
320
321 return 0;
322 }
323
mediamark_clone_mmk(mediamark_t * mmk)324 mediamark_t *mediamark_clone_mmk(mediamark_t *mmk) {
325 mediamark_t *cmmk = NULL;
326
327 if(mmk && mmk->mrl) {
328 cmmk = (mediamark_t *) calloc(1, sizeof(mediamark_t));
329 cmmk->mrl = strdup(mmk->mrl);
330 cmmk->ident = (mmk->ident) ? strdup(mmk->ident) : NULL;
331 cmmk->sub = (mmk->sub) ? strdup(mmk->sub) : NULL;
332 cmmk->start = mmk->start;
333 cmmk->end = mmk->end;
334 cmmk->av_offset = mmk->av_offset;
335 cmmk->spu_offset = mmk->spu_offset;
336 cmmk->played = mmk->played;
337 cmmk->got_alternate = mmk->got_alternate;
338 mediamark_duplicate_alternates(mmk, cmmk);
339 }
340
341 return cmmk;
342 }
343
mediamark_free_mmk(mediamark_t ** mmk)344 int mediamark_free_mmk(mediamark_t **mmk) {
345 if((*mmk) != NULL) {
346 mediamark_free_alternates((*mmk));
347 SAFE_FREE((*mmk)->ident);
348 SAFE_FREE((*mmk)->mrl);
349 SAFE_FREE((*mmk)->sub);
350 SAFE_FREE((*mmk));
351 return 1;
352 }
353 return 0;
354 }
355
356 /*
357 * Split lines from playlist->data
358 */
playlist_split_data(playlist_t * playlist)359 static int playlist_split_data(playlist_t *playlist) {
360
361 if(playlist->data) {
362 char *buf, *pbuf, *p;
363
364 /* strsep modify original string, avoid its corruption */
365 buf = strdup(playlist->data);
366 playlist->numl = 0;
367
368 pbuf = buf;
369 while((p = xine_strsep(&pbuf, "\n")) != NULL) {
370 if(p && (p <= pbuf) && (strlen(p))) {
371
372 playlist->lines = (char **) realloc(playlist->lines, sizeof(char *) * (playlist->numl + 2));
373
374 while((*(p + strlen(p) - 1) == '\n') || (*(p + strlen(p) - 1) == '\r'))
375 *(p + strlen(p) - 1) = '\0';
376
377 playlist->lines[playlist->numl++] = strdup(p);
378 playlist->lines[playlist->numl] = NULL;
379 }
380 }
381
382 free(buf);
383 }
384
385 return (playlist->numl > 0);
386 }
387
set_pos_to_value(char ** p)388 static void set_pos_to_value(char **p) {
389
390 ABORT_IF_NULL(*p);
391
392 while(*(*p) != '\0' && *(*p) != '=' && *(*p) != ':' && *(*p) != '{') ++(*p);
393 while(*(*p) == '=' || *(*p) == ':' || *(*p) == ' ' || *(*p) == '\t') ++(*p);
394 }
395
get_basedir(const char * filename)396 static char *get_basedir(const char *filename) {
397 char *path;
398 char *origin = NULL;
399
400 path = strrchr(filename, '/');
401 if(path && (path > filename)) {
402 origin = (char *) malloc((path - filename) + 2);
403 snprintf(origin, (path-filename)+2, "%s", filename);
404 }
405
406 return origin;
407 }
408
is_mrl(const char * entry)409 static int is_mrl(const char *entry) {
410 char *path;
411
412 path = strstr(entry, ":/");
413 if (path) {
414 while (entry < path) {
415 if (!isalnum(entry[0])) return 0;
416 entry++;
417 }
418 return 1;
419 }
420 return 0;
421 }
422
concat_basedir(char * buffer,size_t size,const char * origin,const char * entry)423 static const char *concat_basedir(char *buffer, size_t size, const char *origin, const char *entry) {
424 if(origin && entry[0] != '/' && !is_mrl(entry)) {
425 snprintf(buffer, size, "%s%s", origin, entry);
426 return buffer;
427 } else {
428 return entry;
429 }
430 }
431
432 /*
433 * Playlists guessing
434 */
guess_pls_playlist(playlist_t * playlist,const char * filename)435 static mediamark_t **guess_pls_playlist(playlist_t *playlist, const char *filename) {
436 mediamark_t **mmk = NULL;
437 char *extension;
438
439 if(filename) {
440 if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
441
442 extension = strrchr(filename, '.');
443
444 if((extension) && (!strncasecmp(extension, ".pls", 4))) {
445 char *pls_content;
446 int size;
447
448 if((pls_content = _read_file(filename, &size)) != NULL) {
449
450 playlist->data = pls_content;
451
452 if(playlist_split_data(playlist)) {
453 int valid_pls = 0;
454 int entries_pls = 0;
455 int found_nument = 0;
456 int stored_nument = 0;
457 int pl_line = 0;
458 int linen = 0;
459 int count = 0;
460 char *ln, *origin;
461 const char *store_mrl;
462 char buffer[_PATH_MAX + _NAME_MAX + 2];
463
464 origin = get_basedir(filename);
465 do {
466
467 while((ln = playlist->lines[linen++]) != NULL) {
468 if(ln) {
469
470 if(valid_pls) {
471
472 if(entries_pls) {
473 int entry;
474
475 if((!strncasecmp(ln, "file", 4)) && ((sscanf(ln + 4, "%d=", &entry)) == 1)) {
476 char *mrl = strchr(ln, '=');
477
478 if(mrl)
479 mrl++;
480
481 if((entry && mrl) &&
482 ((entry) <= entries_pls) &&
483 (mmk && (!mmk[(entry - 1)]))) {
484 stored_nument++;
485 store_mrl = concat_basedir(buffer, sizeof(buffer), origin, mrl);
486 mediamark_store_mmk(&mmk[(entry - 1)], store_mrl, NULL, NULL, 0, -1, 0, 0);
487 }
488
489 }
490
491 }
492 else {
493 if((!strncasecmp(ln, "numberofentries", 15))
494 && ((sscanf(ln + 15, "=%d", &entries_pls)) == 1)) {
495
496 if(!found_nument) {
497 if(entries_pls) {
498 playlist->entries = entries_pls;
499 mmk = (mediamark_t **) calloc((entries_pls + 1), sizeof(mediamark_t *));
500 found_nument = 1;
501 }
502 }
503
504 }
505 }
506
507 }
508 else if((!strcasecmp(ln, "[playlist]"))) {
509 if(!valid_pls) {
510 valid_pls = 1;
511 pl_line = linen;
512 }
513 }
514
515 }
516 }
517
518 count++;
519 linen = pl_line;
520
521 } while((playlist->entries && !stored_nument) && (count < 2));
522 free(origin);
523
524 if(mmk && valid_pls && entries_pls) {
525 int i;
526
527 mmk[entries_pls] = NULL;
528
529 /* Fill missing entries */
530 for(i = 0; i < entries_pls; i++) {
531 if(!mmk[i])
532 mediamark_store_mmk(&mmk[i], _("!!Invalid entry!!"), NULL, NULL, 0, -1, 0, 0);
533 }
534 playlist->type = strdup("PLS");
535 }
536
537 while(playlist->numl) {
538 free(playlist->lines[playlist->numl - 1]);
539 playlist->numl--;
540 }
541
542 if(valid_pls && entries_pls) {
543 SAFE_FREE(playlist->lines);
544 free(pls_content);
545 return mmk;
546 }
547 }
548 SAFE_FREE(playlist->lines);
549 free(pls_content);
550 }
551 }
552 }
553 }
554
555 return NULL;
556 }
557
guess_m3u_playlist(playlist_t * playlist,const char * filename)558 static mediamark_t **guess_m3u_playlist(playlist_t *playlist, const char *filename) {
559 mediamark_t **mmk = NULL;
560
561 if(filename) {
562 if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
563 char *m3u_content;
564 int size;
565
566 if((m3u_content = _read_file(filename, &size)) != NULL) {
567
568 playlist->data = m3u_content;
569
570 if(playlist_split_data(playlist)) {
571 int valid_m3u = 0;
572 int entries_m3u = 0;
573 char *title = NULL;
574 char *origin;
575 int linen = 0;
576 char *ln;
577
578 origin = get_basedir(filename);
579
580 while((ln = playlist->lines[linen++]) != NULL) {
581 if(ln) {
582
583 if(valid_m3u) {
584
585 if(!strncmp(ln, "#EXTINF", 7)) {
586 char *ptitle;
587 if((ptitle = strchr(ln, ',')) != NULL) {
588 ptitle++;
589
590 SAFE_FREE(title);
591
592 if(strlen(ptitle))
593 title = strdup(ptitle);
594 }
595 }
596 else if (ln[0] != '#') {
597 char buffer[_PATH_MAX + _NAME_MAX + 2];
598 const char *entry;
599
600 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_m3u + 2));
601 entry = concat_basedir(buffer, sizeof(buffer), origin, ln);
602
603 mediamark_store_mmk(&mmk[entries_m3u], entry, title, NULL, 0, -1, 0, 0);
604 playlist->entries = ++entries_m3u;
605
606 SAFE_FREE(title);
607 }
608
609 }
610 else if((!strcasecmp(ln, "#EXTM3U")))
611 valid_m3u = 1;
612 }
613 }
614
615 if(valid_m3u && entries_m3u) {
616 mmk[entries_m3u] = NULL;
617 playlist->type = strdup("M3U");
618 }
619
620 free(origin);
621 free(title);
622
623 while(playlist->numl) {
624 free(playlist->lines[playlist->numl - 1]);
625 playlist->numl--;
626 }
627 }
628 SAFE_FREE(playlist->lines);
629
630 free(m3u_content);
631 }
632 }
633 }
634
635 return mmk;
636 }
637
guess_sfv_playlist(playlist_t * playlist,const char * filename)638 static mediamark_t **guess_sfv_playlist(playlist_t *playlist, const char *filename) {
639 mediamark_t **mmk = NULL;
640
641 if(filename) {
642 if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
643 char *extension;
644
645 extension = strrchr(filename, '.');
646
647 if((extension) && (!strncasecmp(extension, ".sfv", 4))) {
648 char *sfv_content;
649 int size;
650
651 if((sfv_content = _read_file(filename, &size)) != NULL) {
652
653 playlist->data = sfv_content;
654
655 if(playlist_split_data(playlist)) {
656 int valid_sfv = 0;
657 int entries_sfv = 0;
658 char *origin;
659 int linen = 0;
660 char *ln;
661
662 origin = get_basedir(filename);
663
664 while((ln = playlist->lines[linen++]) != NULL) {
665
666 if(ln) {
667
668 if(valid_sfv) {
669
670 if(strncmp(ln, ";", 1)) {
671 char buffer[_PATH_MAX + _NAME_MAX + 2];
672 const char *entry;
673 long long int crc = 0;
674 char *p;
675
676 p = ln + strlen(ln) - 1;
677 if(p) {
678
679 while(p && (p > ln) && (*p != ' '))
680 p--;
681
682 if(p > ln) {
683 *p = '\0';
684 p++;
685 }
686
687 if(p && strlen(p)) {
688 crc = strtoll(p, &p, 16);
689
690 if((errno == ERANGE) || (errno == EINVAL))
691 crc = 0;
692
693 }
694
695 if(crc > 0) {
696
697 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_sfv + 2));
698
699 entry = concat_basedir(buffer, sizeof(buffer), origin, ln);
700 mediamark_store_mmk(&mmk[entries_sfv], entry, NULL, NULL, 0, -1, 0, 0);
701 playlist->entries = ++entries_sfv;
702 }
703 }
704 }
705 }
706 else if(strlen(ln) > 1) {
707 long int size;
708 int h, m, s;
709 int Y, M, D;
710 char fn[2];
711 char mon[4];
712
713 if(((sscanf(ln, ";%ld %d:%d.%d %d-%d-%d %1s", &size, &h, &m, &s, &Y, &M, &D, &fn[0])) == 8) ||
714 ((sscanf(ln, ";%ld %3s %d %d:%d:%d %d %1s", &size, &mon[0], &D, &h, &m, &s, &Y, &fn[0])) == 8))
715 valid_sfv = 1;
716
717 }
718 }
719 }
720
721 if(valid_sfv && entries_sfv) {
722 mmk[entries_sfv] = NULL;
723 playlist->type = strdup("SFV");
724 }
725
726 free(origin);
727
728 while(playlist->numl) {
729 free(playlist->lines[playlist->numl - 1]);
730 playlist->numl--;
731 }
732 }
733 SAFE_FREE(playlist->lines);
734 free(sfv_content);
735 }
736 }
737 }
738 }
739
740 return mmk;
741 }
742
guess_raw_playlist(playlist_t * playlist,const char * filename)743 static mediamark_t **guess_raw_playlist(playlist_t *playlist, const char *filename) {
744 mediamark_t **mmk = NULL;
745
746 if(filename) {
747 if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
748 char *raw_content;
749 int size;
750
751 if((raw_content = _read_file(filename, &size)) != NULL) {
752
753 playlist->data = raw_content;
754
755 if(playlist_split_data(playlist)) {
756 char *origin;
757 int entries_raw = 0;
758 int linen = 0;
759 char *ln;
760
761 origin = get_basedir(filename);
762
763 while((ln = playlist->lines[linen++]) != NULL) {
764 if(ln) {
765
766 if((strncmp(ln, ";", 1)) && (strncmp(ln, "#", 1))) {
767 char buffer[_PATH_MAX + _NAME_MAX + 2];
768 const char *entry;
769
770 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_raw + 2));
771
772 entry = concat_basedir(buffer, sizeof(buffer), origin, ln);
773
774 mediamark_store_mmk(&mmk[entries_raw], entry, NULL, NULL, 0, -1, 0, 0);
775 playlist->entries = ++entries_raw;
776 }
777 }
778 }
779
780 if(entries_raw) {
781 mmk[entries_raw] = NULL;
782 playlist->type = strdup("RAW");
783 }
784
785 free(origin);
786
787 while(playlist->numl) {
788 free(playlist->lines[playlist->numl - 1]);
789 playlist->numl--;
790 }
791 }
792 SAFE_FREE(playlist->lines);
793 free(raw_content);
794 }
795 }
796 }
797
798 return mmk;
799 }
800
guess_toxine_playlist(playlist_t * playlist,const char * filename)801 static mediamark_t **guess_toxine_playlist(playlist_t *playlist, const char *filename) {
802 mediamark_t **mmk = NULL;
803
804 if(filename) {
805 if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
806 int entries_tox = 0;
807 char *tox_content;
808 int size;
809
810 if((tox_content = _read_file(filename, &size)) != NULL) {
811
812 playlist->data = tox_content;
813
814 if(playlist_split_data(playlist)) {
815 char buffer[32768], path[_PATH_MAX + _NAME_MAX + 2];
816 char *p, *pp, *origin;
817 int start = 0;
818 int linen = 0;
819 char *ln;
820
821 memset(&buffer, 0, sizeof(buffer));
822 p = buffer;
823 origin = get_basedir(filename);
824
825 while((ln = playlist->lines[linen++]) != NULL) {
826
827 if(!start) {
828 memset(&buffer, 0, sizeof(buffer));
829 p = buffer;
830 }
831
832 if((ln) && (strlen(ln))) {
833
834 if(strncmp(ln, "#", 1)) {
835
836 pp = ln;
837
838 while(*pp != '\0') {
839
840 if(!strncasecmp(pp, "entry {", 7)) {
841 if(!start) {
842 start = 1;
843 pp += 7;
844 memset(&buffer, 0, sizeof(buffer));
845 p = buffer;
846 }
847 else {
848 memset(&buffer, 0, sizeof(buffer));
849 p = buffer;
850 goto __discard;
851 }
852 }
853 if((*pp == '}') && (*(pp + 1) == ';')) {
854 if(start) {
855 start = 0;
856 pp += 2;
857
858 { /* OKAY, check found string */
859 mediamark_t mmkf;
860 int mmkf_members[7];
861 char *line, *pline;
862 char *m;
863
864 mmkf.ident = NULL;
865 mmkf.sub = NULL;
866 mmkf.start = 0;
867 mmkf.end = -1;
868 mmkf.av_offset = 0;
869 mmkf.spu_offset = 0;
870 mmkf.mrl = NULL;
871
872 mmkf_members[0] = 0; /* ident */
873 mmkf_members[1] = -1; /* mrl */
874 mmkf_members[2] = 0; /* start */
875 mmkf_members[3] = 0; /* end */
876 mmkf_members[4] = 0; /* av offset */
877 mmkf_members[5] = 0; /* spu offset */
878 mmkf_members[6] = 0; /* sub */
879
880 line = strdup(atoa(buffer));
881
882 pline = line;
883 while((m = xine_strsep(&pline, ";")) != NULL) {
884 char *key;
885 const char *entry;
886
887 key = atoa(m);
888 if(strlen(key)) {
889
890 if(!strncasecmp(key, "identifier", 10)) {
891 if(mmkf_members[0] == 0) {
892 mmkf_members[0] = 1;
893 set_pos_to_value(&key);
894 mmkf.ident = strdup(key);
895 }
896 }
897 else if(!strncasecmp(key, "subtitle", 8)) {
898 if(mmkf_members[6] == 0) {
899 set_pos_to_value(&key);
900 /* Workaround old toxine playlist version bug */
901 if(strcmp(key, "(null)")) {
902 mmkf_members[6] = 1;
903 entry = concat_basedir(path, sizeof(path), origin, key);
904 mmkf.sub = strdup(entry);
905 }
906 }
907 }
908 else if(!strncasecmp(key, "spu_offset", 10)) {
909 if(mmkf_members[5] == 0) {
910 mmkf_members[5] = 1;
911 set_pos_to_value(&key);
912 mmkf.spu_offset = strtol(key, &key, 10);
913 }
914 }
915 else if(!strncasecmp(key, "av_offset", 9)) {
916 if(mmkf_members[4] == 0) {
917 mmkf_members[4] = 1;
918 set_pos_to_value(&key);
919 mmkf.av_offset = strtol(key, &key, 10);
920 }
921 }
922 else if(!strncasecmp(key, "start", 5)) {
923 if(mmkf_members[2] == 0) {
924 mmkf_members[2] = 1;
925 set_pos_to_value(&key);
926 mmkf.start = strtol(key, &key, 10);
927 }
928 }
929 else if(!strncasecmp(key, "end", 3)) {
930 if(mmkf_members[3] == 0) {
931 mmkf_members[3] = 1;
932 set_pos_to_value(&key);
933 mmkf.end = strtol(key, &key, 10);
934 }
935 }
936 else if(!strncasecmp(key, "mrl", 3)) {
937 if(mmkf_members[1] == -1) {
938 mmkf_members[1] = 1;
939 set_pos_to_value(&key);
940 entry = concat_basedir(path, sizeof(path), origin, key);
941 mmkf.mrl = strdup(entry);
942 }
943 }
944 }
945 }
946
947 if((mmkf_members[1] == 0) || (mmkf_members[1] == -1)) {
948 /* printf("wow, no mrl found\n"); */
949 goto __discard;
950 }
951
952 if(mmkf_members[0] == 0)
953 mmkf.ident = mmkf.mrl;
954
955 /*
956 STORE new mmk;
957 */
958 #if 0
959 printf("DUMP mediamark entry:\n");
960 printf("ident: '%s'\n", mmkf.ident);
961 printf("mrl: '%s'\n", mmkf.mrl);
962 printf("sub: '%s'\n", mmkf.sub);
963 printf("start: %d\n", mmkf.start);
964 printf("end: %d\n", mmkf.end);
965 printf("av_offset: %d\n", mmkf.av_offset);
966 printf("spu_offset: %d\n", mmkf.spu_offset);
967 #endif
968
969 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_tox + 2));
970
971 mediamark_store_mmk(&mmk[entries_tox],
972 mmkf.mrl, mmkf.ident, mmkf.sub,
973 mmkf.start, mmkf.end, mmkf.av_offset, mmkf.spu_offset);
974 playlist->entries = ++entries_tox;
975
976 free(line);
977
978 if(mmkf_members[0])
979 free(mmkf.ident);
980
981 SAFE_FREE(mmkf.sub);
982
983 free(mmkf.mrl);
984 }
985
986 }
987 else {
988 memset(&buffer, 0, sizeof(buffer));
989 p = buffer;
990 goto __discard;
991 }
992 }
993
994 if(*pp != '\0') {
995 /* buffer full? don't copy */
996 if (p - buffer < sizeof (buffer) - 1)
997 *p++ = *pp;
998 pp++;
999 }
1000 }
1001
1002 }
1003 }
1004 __discard: ;
1005 }
1006
1007 free(origin);
1008
1009 if(entries_tox) {
1010 mmk[entries_tox] = NULL;
1011 playlist->type = strdup("TOX");
1012 }
1013
1014 while(playlist->numl) {
1015 free(playlist->lines[playlist->numl - 1]);
1016 playlist->numl--;
1017 }
1018 }
1019 SAFE_FREE(playlist->lines);
1020 free(tox_content);
1021 }
1022 }
1023 }
1024 return mmk;
1025 }
1026
1027 /*
1028 * XML based playlists
1029 */
xml_asx_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)1030 static mediamark_t **xml_asx_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
1031 mediamark_t **mmk = NULL;
1032
1033 if(xml_tree) {
1034 xml_node_t *asx_entry, *asx_ref;
1035 xml_property_t *asx_prop;
1036 int entries_asx = 0;
1037
1038 if(!strcasecmp(xml_tree->name, "ASX")) {
1039
1040 asx_prop = xml_tree->props;
1041
1042 while((asx_prop) && (strcasecmp(asx_prop->name, "VERSION")))
1043 asx_prop = asx_prop->next;
1044
1045 if(asx_prop) {
1046 int version_major, version_minor = 0;
1047
1048 if((((sscanf(asx_prop->value, "%d.%d", &version_major, &version_minor)) == 2) ||
1049 ((sscanf(asx_prop->value, "%d", &version_major)) == 1)) &&
1050 ((version_major == 3) && (version_minor == 0))) {
1051
1052 __parse_anyway:
1053 asx_entry = xml_tree->child;
1054 while(asx_entry) {
1055 if((!strcasecmp(asx_entry->name, "ENTRY")) ||
1056 (!strcasecmp(asx_entry->name, "ENTRYREF"))) {
1057 char *title = NULL;
1058 char *href = NULL;
1059 char *author = NULL;
1060 char *sub = NULL;
1061
1062 asx_ref = asx_entry->child;
1063 while(asx_ref) {
1064
1065 if(!strcasecmp(asx_ref->name, "TITLE")) {
1066
1067 if(!title)
1068 title = asx_ref->data;
1069
1070 }
1071 else if(!strcasecmp(asx_ref->name, "AUTHOR")) {
1072
1073 if(!author)
1074 author = asx_ref->data;
1075
1076 }
1077 else if(!strcasecmp(asx_ref->name, "REF")) {
1078
1079 for(asx_prop = asx_ref->props; asx_prop; asx_prop = asx_prop->next) {
1080
1081 if(!strcasecmp(asx_prop->name, "HREF")) {
1082
1083 if(!href)
1084 href = asx_prop->value;
1085 }
1086 /* This is not part of the ASX specs */
1087 else if(!strcasecmp(asx_prop->name, "SUBTITLE")) {
1088
1089 if(!sub)
1090 sub = asx_prop->value;
1091 }
1092
1093 if(href && sub)
1094 break;
1095 }
1096 }
1097
1098 asx_ref = asx_ref->next;
1099 }
1100
1101 if(href && strlen(href)) {
1102 /* Use the _orig pointers to store the string before
1103 using atoa() that changes the pointer */
1104 char *atitle = NULL, *atitle_orig = NULL;
1105 char *aauthor = NULL, *aauthor_orig = NULL;
1106 char *real_title = NULL;
1107 int len = 0;
1108
1109 if(title && strlen(title)) {
1110 atitle_orig = atitle = strdup(title);
1111 atitle = atoa(atitle);
1112 len = strlen(atitle);
1113
1114 if(author && strlen(author)) {
1115 aauthor_orig = aauthor = strdup(author);
1116 aauthor = atoa(aauthor);
1117 len += strlen(aauthor) + 3;
1118 }
1119
1120 len++;
1121 }
1122
1123 if(atitle && strlen(atitle)) {
1124 real_title = (char *) malloc(len);
1125 strcpy(real_title, atitle);
1126
1127 if(aauthor && strlen(aauthor))
1128 snprintf(real_title+strlen(real_title), len-strlen(real_title), " (%s)", aauthor);
1129 }
1130
1131 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_asx + 2));
1132
1133 mediamark_store_mmk(&mmk[entries_asx], href, real_title, sub, 0, -1, 0, 0);
1134 playlist->entries = ++entries_asx;
1135
1136 SAFE_FREE(real_title);
1137 SAFE_FREE(atitle_orig);
1138 SAFE_FREE(aauthor_orig);
1139 }
1140
1141 href = title = author = NULL;
1142 }
1143 asx_entry = asx_entry->next;
1144 }
1145 }
1146 else
1147 fprintf(stderr, "%s(): Wrong ASX version: %s\n", __XINE_FUNCTION__, asx_prop->value);
1148
1149 }
1150 else {
1151 fprintf(stderr, "%s(): Unable to find VERSION tag.\n", __XINE_FUNCTION__);
1152 fprintf(stderr, "%s(): last chance: try to parse it anyway\n", __XINE_FUNCTION__);
1153 goto __parse_anyway;
1154 }
1155
1156 }
1157 #ifdef DEBUG
1158 else
1159 fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
1160 #endif
1161
1162 /* Maybe it's 'ASF <url> */
1163 if(entries_asx == 0) {
1164
1165 playlist->data = xml_content;
1166
1167 if(playlist_split_data(playlist)) {
1168 int linen = 0;
1169 char *ln;
1170
1171 while((ln = playlist->lines[linen++]) != NULL) {
1172
1173 if(!strncasecmp("ASF", ln, 3)) {
1174 char *p = ln + 3;
1175
1176 while(p && ((*p == ' ') || (*p == '\t')))
1177 p++;
1178
1179 if(p && strlen(p)) {
1180 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_asx + 2));
1181
1182 mediamark_store_mmk(&mmk[entries_asx], p, p, NULL, 0, -1, 0, 0);
1183 playlist->entries = ++entries_asx;
1184 }
1185 }
1186 }
1187 }
1188 SAFE_FREE(playlist->lines);
1189 }
1190
1191 if(entries_asx) {
1192 mmk[entries_asx] = NULL;
1193 playlist->type = strdup("ASX3");
1194 return mmk;
1195 }
1196 }
1197
1198 return NULL;
1199 }
1200
__gx_get_entries(playlist_t * playlist,mediamark_t *** mmk,int * entries,xml_node_t * entry)1201 static void __gx_get_entries(playlist_t *playlist, mediamark_t ***mmk, int *entries, xml_node_t *entry) {
1202 xml_property_t *prop;
1203 xml_node_t *ref;
1204 xml_node_t *node = entry;
1205
1206 while(node) {
1207 if(!strcasecmp(node->name, "SUB"))
1208 __gx_get_entries(playlist, mmk, entries, node->child);
1209 else if(!strcasecmp(node->name, "ENTRY")) {
1210 char *title = NULL;
1211 char *href = NULL;
1212 int start = 0;
1213
1214 ref = node->child;
1215 while(ref) {
1216
1217 if(!strcasecmp(ref->name, "TITLE")) {
1218
1219 if(!title)
1220 title = ref->data;
1221
1222 }
1223 else if(!strcasecmp(ref->name, "REF")) {
1224
1225 for(prop = ref->props; prop; prop = prop->next) {
1226 if(!strcasecmp(prop->name, "HREF")) {
1227
1228 if(!href)
1229 href = prop->value;
1230 }
1231
1232 if(href)
1233 break;
1234 }
1235 }
1236 else if(!strcasecmp(ref->name, "TIME")) {
1237
1238 for(prop = ref->props; prop; prop = prop->next) {
1239 if(!strcasecmp(prop->name, "START")) {
1240
1241 if(prop->value && strlen(prop->value))
1242 start = atoi(prop->value);
1243
1244 }
1245 }
1246 }
1247
1248 ref = ref->next;
1249 }
1250
1251 if(href && strlen(href)) {
1252 char *atitle = NULL;
1253
1254 if(title && strlen(title)) {
1255 atitle = strdup(title);
1256 atitle = atoa(atitle);
1257 }
1258
1259 (*mmk) = (mediamark_t **) realloc((*mmk), sizeof(mediamark_t *) * (*entries + 2));
1260
1261 mediamark_store_mmk(&(*mmk)[*entries], href, (atitle && strlen(atitle)) ? atitle : NULL, NULL, start, -1, 0, 0);
1262 playlist->entries = ++(*entries);
1263
1264 free(atitle);
1265 }
1266
1267 href = title = NULL;
1268 start = 0;
1269 }
1270
1271 node = node->next;
1272 }
1273 }
xml_gx_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)1274 static mediamark_t **xml_gx_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
1275 mediamark_t **mmk = NULL;
1276
1277 if(xml_tree) {
1278 xml_node_t *gx_entry;
1279 xml_property_t *gx_prop;
1280 int entries_gx = 0;
1281
1282 if(!strcasecmp(xml_tree->name, "GXINEMM")) {
1283
1284 gx_prop = xml_tree->props;
1285
1286 while((gx_prop) && (strcasecmp(gx_prop->name, "VERSION")))
1287 gx_prop = gx_prop->next;
1288
1289 if(gx_prop) {
1290 int version_major;
1291
1292 if(((sscanf(gx_prop->value, "%d", &version_major)) == 1) && (version_major == 1)) {
1293
1294 gx_entry = xml_tree->child;
1295 while(gx_entry) {
1296
1297 if(!strcasecmp(gx_entry->name, "SUB")) {
1298 __gx_get_entries(playlist, &mmk, &entries_gx, gx_entry->child);
1299 }
1300 else if(!strcasecmp(gx_entry->name, "ENTRY"))
1301 __gx_get_entries(playlist, &mmk, &entries_gx, gx_entry);
1302
1303 gx_entry = gx_entry->next;
1304 }
1305 }
1306 else
1307 fprintf(stderr, "%s(): Wrong GXINEMM version: %s\n", __XINE_FUNCTION__, gx_prop->value);
1308 }
1309 else
1310 fprintf(stderr, "%s(): Unable to find VERSION tag.\n", __XINE_FUNCTION__);
1311 }
1312 #ifdef DEBUG
1313 else
1314 fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
1315 #endif
1316
1317 if(entries_gx) {
1318 mmk[entries_gx] = NULL;
1319 playlist->type = strdup("GXMM");
1320 return mmk;
1321 }
1322 }
1323
1324 return NULL;
1325 }
1326
xml_noatun_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)1327 static mediamark_t **xml_noatun_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
1328 mediamark_t **mmk = NULL;
1329
1330 if(xml_tree) {
1331 xml_node_t *noa_entry;
1332 xml_property_t *noa_prop;
1333 int entries_noa = 0;
1334
1335 if(!strcasecmp(xml_tree->name, "PLAYLIST")) {
1336 int found = 0;
1337
1338 noa_prop = xml_tree->props;
1339
1340 while(noa_prop) {
1341 if((!strcasecmp(noa_prop->name, "CLIENT")) && (!strcasecmp(noa_prop->value, "NOATUN")))
1342 found++;
1343 else if(!strcasecmp(noa_prop->name, "VERSION")) {
1344 int version_major;
1345
1346 if(((sscanf(noa_prop->value, "%d", &version_major)) == 1) && (version_major == 1))
1347 found++;
1348 }
1349
1350 noa_prop = noa_prop->next;
1351 }
1352
1353 if(found >= 2) {
1354 noa_entry = xml_tree->child;
1355
1356 while(noa_entry) {
1357
1358 if(!strcasecmp(noa_entry->name, "ITEM")) {
1359 char *real_title = NULL;
1360 char *title = NULL;
1361 char *album = NULL;
1362 char *artist = NULL;
1363 char *url = NULL;
1364
1365 for(noa_prop = noa_entry->props; noa_prop; noa_prop = noa_prop->next) {
1366 if(!strcasecmp(noa_prop->name, "TITLE"))
1367 title = noa_prop->value;
1368 else if(!strcasecmp(noa_prop->name, "ALBUM"))
1369 album = noa_prop->value;
1370 else if(!strcasecmp(noa_prop->name, "ARTIST"))
1371 artist = noa_prop->value;
1372 else if(!strcasecmp(noa_prop->name, "URL"))
1373 url = noa_prop->value;
1374 }
1375
1376 if(url) {
1377 int titlen = 0;
1378
1379 /*
1380 title (artist - album)
1381 */
1382 if(title && (titlen = strlen(title))) {
1383 int artlen = 0;
1384 int alblen = 0;
1385
1386 if(artist && (artlen = strlen(artist)) && album && (alblen = strlen(album))) {
1387 int len = titlen + artlen + alblen + 7;
1388
1389 real_title = (char *) malloc(len);
1390 sprintf(real_title, "%s (%s - %s)", title, artist, album);
1391 }
1392 else if(artist && (artlen = strlen(artist))) {
1393 int len = titlen + artlen + 4;
1394
1395 real_title = (char *) malloc(len);
1396 sprintf(real_title, "%s (%s)", title, artist);
1397 }
1398 else if(album && (alblen = strlen(album))) {
1399 int len = titlen + alblen + 4;
1400
1401 real_title = (char *) malloc(len);
1402 sprintf(real_title, "%s (%s)", title, album);
1403 }
1404 else
1405 real_title = strdup(title);
1406
1407 }
1408
1409 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_noa + 2));
1410
1411 mediamark_store_mmk(&mmk[entries_noa], url, real_title, NULL, 0, -1, 0, 0);
1412 playlist->entries = ++entries_noa;
1413
1414 free(real_title);
1415
1416 }
1417 }
1418
1419 noa_entry = noa_entry->next;
1420 }
1421 }
1422 }
1423 #ifdef DEBUG
1424 else
1425 fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
1426 #endif
1427
1428 if(entries_noa) {
1429 mmk[entries_noa] = NULL;
1430 playlist->type = strdup("NOATUN");
1431 return mmk;
1432 }
1433 }
1434
1435 return NULL;
1436 }
1437
1438 /*
1439 * ********************************** SMIL BEGIN ***********************************
1440 */
1441 #undef DEBUG_SMIL
1442
1443 #ifdef DEBUG_SMIL
1444 static int offset = 0;
1445 #define palign do { int i; for(i = 0; i < offset; i++) { printf(" "); } } while(0)
1446 #endif
1447
1448 typedef struct smil_node_s smil_node_t;
1449
1450 typedef struct {
1451 char *anchor;
1452
1453 int repeat;
1454 int begin;
1455 int end;
1456
1457 int clip_begin;
1458 int clip_end;
1459
1460 int duration;
1461
1462 } smil_property_t;
1463
1464 struct smil_node_s {
1465 mediamark_t *mmk;
1466 smil_property_t prop;
1467 smil_node_t *next;
1468 };
1469
1470 typedef struct {
1471 /* Header */
1472 char *title;
1473 char *author;
1474 char *copyright;
1475 char *year;
1476 char *base;
1477 /* End Header */
1478
1479 smil_node_t *first;
1480 smil_node_t *node;
1481
1482 } smil_t;
1483
1484 /* protos */
1485 static void smil_seq(smil_t *, smil_node_t **, xml_node_t *, smil_property_t *);
1486 static void smil_par(smil_t *, smil_node_t **, xml_node_t *, smil_property_t *);
1487 static void smil_switch(smil_t *, smil_node_t **, xml_node_t *, smil_property_t *);
1488 static void smil_repeat(int, smil_node_t *, smil_node_t **, smil_property_t *);
1489
smil_init_smil_property(smil_property_t * sprop)1490 static void smil_init_smil_property(smil_property_t *sprop) {
1491 sprop->anchor = NULL;
1492 sprop->repeat = 1;
1493 sprop->begin = 0;
1494 sprop->clip_begin = 0;
1495 sprop->end = -1;
1496 sprop->clip_end = -1;
1497 sprop->duration = 0;
1498 }
smil_new_node(void)1499 static smil_node_t *smil_new_node(void) {
1500 smil_node_t *node;
1501
1502 node = (smil_node_t *) calloc(1, sizeof(smil_node_t));
1503 node->mmk = NULL;
1504 node->next = NULL;
1505
1506 smil_init_smil_property(&(node->prop));
1507
1508 return node;
1509 }
smil_new_mediamark(void)1510 static mediamark_t *smil_new_mediamark(void) {
1511 mediamark_t *mmk;
1512
1513 mmk = (mediamark_t *) calloc(1, sizeof(mediamark_t));
1514 mmk->mrl = NULL;
1515 mmk->ident = NULL;
1516 mmk->sub = NULL;
1517 mmk->start = 0;
1518 mmk->end = -1;
1519 mmk->played = 0;
1520 mmk->alternates = NULL;
1521 mmk->cur_alt = NULL;
1522
1523 return mmk;
1524 }
smil_duplicate_mmk(mediamark_t * ommk)1525 static mediamark_t *smil_duplicate_mmk(mediamark_t *ommk) {
1526 mediamark_t *mmk = NULL;
1527
1528 if(ommk) {
1529 mmk = smil_new_mediamark();
1530 mmk->mrl = strdup(ommk->mrl);
1531 mmk->ident = strdup(ommk->ident ? ommk->ident : ommk->mrl);
1532 mmk->sub = ommk->sub ? strdup(ommk->sub) : NULL;
1533 mmk->start = ommk->start;
1534 mmk->end = ommk->end;
1535 mmk->played = ommk->played;
1536 mmk->alternates = NULL;
1537 mmk->cur_alt = NULL;
1538 }
1539
1540 return mmk;
1541 }
smil_free_properties(smil_property_t * smil_props)1542 static void smil_free_properties(smil_property_t *smil_props) {
1543 if(smil_props) {
1544 SAFE_FREE(smil_props->anchor);
1545 }
1546 }
1547
1548 #ifdef DEBUG_SMIL
smil_dump_header(smil_t * smil)1549 static void smil_dump_header(smil_t *smil) {
1550 printf("title: %s\n", smil->title);
1551 printf("author: %s\n", smil->author);
1552 printf("copyright: %s\n", smil->copyright);
1553 printf("year: %s\n", smil->year);
1554 printf("base: %s\n", smil->base);
1555 }
1556
smil_dump_tree(smil_node_t * node)1557 static void smil_dump_tree(smil_node_t *node) {
1558 if(node->mmk) {
1559 printf("mrl: %s\n", node->mmk->mrl);
1560 printf(" ident: %s\n", node->mmk->ident);
1561 printf(" sub: %s\n", node->mmk->sub);
1562 printf(" start: %d\n", node->mmk->start);
1563 printf(" end: %d\n", node->mmk->end);
1564 }
1565
1566 if(node->next)
1567 smil_dump_tree(node->next);
1568 }
1569 #endif
1570
smil_get_prop_value(xml_property_t * props,const char * propname)1571 static char *smil_get_prop_value(xml_property_t *props, const char *propname) {
1572 xml_property_t *prop;
1573
1574 for(prop = props; prop; prop = prop->next) {
1575 if(!strcasecmp(prop->name, propname)) {
1576 return prop->value;
1577 }
1578 }
1579 return NULL;
1580 }
1581
smil_get_time_in_seconds(const char * time_str)1582 static int smil_get_time_in_seconds(const char *time_str) {
1583 int retval = 0;
1584 //int hours, mins, secs, msecs;
1585 int unit = 0; /* 1 = ms, 2 = s, 3 = min, 4 = h */
1586
1587 if(strstr(time_str, "ms"))
1588 unit = 1;
1589 else if(strstr(time_str, "s"))
1590 unit = 2;
1591 else if(strstr(time_str, "min"))
1592 unit = 3;
1593 else if(strstr(time_str, "h"))
1594 unit = 4;
1595 else
1596 unit = 2;
1597
1598 /* not reached
1599 if(unit == 0) {
1600 if((sscanf(time_str, "%d:%d:%d.%d", &hours, &mins, &secs, &msecs)) == 4)
1601 retval = (hours * 60 * 60) + (mins * 60) + secs + ((int) msecs / 1000);
1602 else if((sscanf(time_str, "%d:%d:%d", &hours, &mins, &secs)) == 3)
1603 retval = (hours * 60 * 60) + (mins * 60) + secs;
1604 else if((sscanf(time_str, "%d:%d.%d", &mins, &secs, &msecs)) == 3)
1605 retval = (mins * 60) + secs + ((int) msecs / 1000);
1606 else if((sscanf(time_str, "%d:%d", &mins, &secs)) == 2) {
1607 retval = (mins * 60) + secs;
1608 }
1609 }
1610 else*/ {
1611 int val, dec, args;
1612
1613 if(((args = sscanf(time_str, "%d.%d", &val, &dec)) == 2) ||
1614 ((args = sscanf(time_str, "%d", &val)) == 1)) {
1615 switch(unit) {
1616 case 1: /* ms */
1617 retval = (int) val / 1000;
1618 break;
1619 case 2: /* s */
1620 retval = val + ((args == 2) ? ((int)((((float) dec) * 10) / 1000)) : 0);
1621 break;
1622 case 3: /* min */
1623 retval = (val * 60) + ((args == 2) ? (dec * 10 * 60 / 100) : 0);
1624 break;
1625 case 4: /* h */
1626 retval = (val * 60 * 60) + ((args == 2) ? (dec * 10 * 60 / 100) : 0);
1627 break;
1628 }
1629 }
1630 }
1631
1632 return retval;
1633 }
smil_header(smil_t * smil,xml_property_t * props)1634 static void smil_header(smil_t *smil, xml_property_t *props) {
1635 xml_property_t *prop;
1636
1637 #ifdef DEBUG_SMIL
1638 offset += 2;
1639 #endif
1640
1641 for(prop = props; prop; prop = prop->next) {
1642 #ifdef DEBUG_SMIL
1643 palign;
1644 printf("%s(): prop_name '%s', value: '%s'\n", __XINE_FUNCTION__,
1645 prop->name, prop->value ? prop->value : "<NULL>");
1646 #endif
1647
1648 if(!strcasecmp(prop->name, "NAME")) {
1649
1650 if(!strcasecmp(prop->value, "TITLE"))
1651 smil->title = smil_get_prop_value(prop, "CONTENT");
1652 else if(!strcasecmp(prop->value, "AUTHOR"))
1653 smil->author = smil_get_prop_value(prop, "CONTENT");
1654 else if(!strcasecmp(prop->value, "COPYRIGHT"))
1655 smil->copyright = smil_get_prop_value(prop, "CONTENT");
1656 else if(!strcasecmp(prop->value, "YEAR"))
1657 smil->year = smil_get_prop_value(prop, "CONTENT");
1658 else if(!strcasecmp(prop->value, "BASE"))
1659 smil->base = smil_get_prop_value(prop, "CONTENT");
1660
1661 }
1662 }
1663 #ifdef DEBUG_SMIL
1664 palign;
1665 printf("---\n");
1666 offset -= 2;
1667 #endif
1668 }
1669
smil_get_properties(smil_property_t * dprop,xml_property_t * props)1670 static void smil_get_properties(smil_property_t *dprop, xml_property_t *props) {
1671 xml_property_t *prop;
1672
1673 #ifdef DEBUG_SMIL
1674 offset += 2;
1675 #endif
1676 for(prop = props; prop; prop = prop->next) {
1677
1678 if(!strcasecmp(prop->name, "REPEAT")) {
1679 dprop->repeat = strtol(prop->value, &prop->value, 10);
1680 #ifdef DEBUG_SMIL
1681 palign;
1682 printf("REPEAT: %d\n", dprop->repeat);
1683 #endif
1684 }
1685 else if(!strcasecmp(prop->name, "BEGIN")) {
1686 dprop->begin = smil_get_time_in_seconds(prop->value);
1687 #ifdef DEBUG_SMIL
1688 palign;
1689 printf("BEGIN: %d\n", dprop->begin);
1690 #endif
1691 }
1692 else if((!strcasecmp(prop->name, "CLIP-BEGIN")) || (!strcasecmp(prop->name, "CLIPBEGIN"))) {
1693 dprop->clip_begin = smil_get_time_in_seconds(prop->value);
1694 #ifdef DEBUG_SMIL
1695 palign;
1696 printf("CLIP-BEGIN: %d\n", dprop->clip_begin);
1697 #endif
1698 }
1699 else if(!strcasecmp(prop->name, "END")) {
1700 dprop->end = smil_get_time_in_seconds(prop->value);
1701 #ifdef DEBUG_SMIL
1702 palign;
1703 printf("END: %d\n", dprop->end);
1704 #endif
1705 }
1706 else if((!strcasecmp(prop->name, "CLIP-END")) || (!strcasecmp(prop->name, "CLIPEND"))) {
1707 dprop->clip_end = smil_get_time_in_seconds(prop->value);
1708 #ifdef DEBUG_SMIL
1709 palign;
1710 printf("CLIP-END: %d\n", dprop->clip_end);
1711 #endif
1712 }
1713 else if(!strcasecmp(prop->name, "DUR")) {
1714 dprop->duration = smil_get_time_in_seconds(prop->value);
1715 #ifdef DEBUG_SMIL
1716 palign;
1717 printf("DURATION: %d\n", dprop->duration);
1718 #endif
1719 }
1720 else if(!strcasecmp(prop->name, "HREF")) {
1721
1722 SAFE_FREE(dprop->anchor);
1723
1724 dprop->anchor = strdup(prop->value);
1725
1726 #ifdef DEBUG_SMIL
1727 palign;
1728 printf("HREF: %s\n", dprop->anchor);
1729 #endif
1730
1731 }
1732 }
1733 #ifdef DEBUG_SMIL
1734 offset -= 2;
1735 #endif
1736 }
1737
smil_properties(smil_t * smil,smil_node_t ** snode,xml_property_t * props,smil_property_t * sprop)1738 static void smil_properties(smil_t *smil, smil_node_t **snode,
1739 xml_property_t *props, smil_property_t *sprop) {
1740 xml_property_t *prop;
1741 int gotmrl = 0;
1742
1743 #ifdef DEBUG_SMIL
1744 offset += 2;
1745 #endif
1746 for(prop = props; prop; prop = prop->next) {
1747
1748 #ifdef DEBUG_SMIL
1749 palign;
1750 printf("%s(): prop_name '%s', value: '%s'\n", __XINE_FUNCTION__,
1751 prop->name, prop->value ? prop->value : "<NULL>");
1752 #endif
1753
1754 if(prop->name) {
1755 if((!strcasecmp(prop->name, "SRC")) || (!strcasecmp(prop->name, "HREF"))) {
1756
1757 gotmrl++;
1758
1759 if((*snode)->mmk == NULL)
1760 (*snode)->mmk = smil_new_mediamark();
1761
1762 /*
1763 handle BASE, ANCHOR
1764 */
1765 if(sprop && sprop->anchor)
1766 (*snode)->mmk->mrl = strdup(sprop->anchor);
1767 else {
1768 char buffer[((smil->base) ? (strlen(smil->base) + 1) : 0) + strlen(prop->value) + 1];
1769 char *p;
1770
1771 memset(&buffer, 0, sizeof(buffer));
1772 p = buffer;
1773
1774 if(smil->base && (!strstr(prop->value, "://"))) {
1775 strlcat(p, smil->base, sizeof(buffer));
1776
1777 if(buffer[strlen(buffer) - 1] != '/')
1778 strlcat(p, "/", sizeof(buffer));
1779 }
1780
1781 strlcat(p, prop->value, sizeof(buffer));
1782
1783 (*snode)->mmk->mrl = strdup(buffer);
1784 }
1785 }
1786 else if(!strcasecmp(prop->name, "TITLE")) {
1787
1788 gotmrl++;
1789
1790 if((*snode)->mmk == NULL) {
1791 (*snode)->mmk = smil_new_mediamark();
1792 }
1793
1794 (*snode)->mmk->ident = strdup(prop->value);
1795 }
1796 else if(!strcasecmp(prop->name, "REPEAT")) {
1797 (*snode)->prop.repeat = strtol(prop->value, &prop->value, 10);
1798 }
1799 else if(!strcasecmp(prop->name, "BEGIN")) {
1800 (*snode)->prop.begin = smil_get_time_in_seconds(prop->value);
1801 #ifdef DEBUG_SMIL
1802 printf("begin: %d\n",(*snode)->prop.begin);
1803 #endif
1804 }
1805 else if((!strcasecmp(prop->name, "CLIP-BEGIN")) || (!strcasecmp(prop->name, "CLIPBEGIN"))) {
1806 (*snode)->prop.clip_begin = smil_get_time_in_seconds(prop->value);
1807 #ifdef DEBUG_SMIL
1808 printf("clip-begin: %d\n",(*snode)->prop.clip_begin);
1809 #endif
1810 }
1811 else if(!strcasecmp(prop->name, "END")) {
1812 (*snode)->prop.end = smil_get_time_in_seconds(prop->value);
1813 #ifdef DEBUG_SMIL
1814 printf("end: %d\n",(*snode)->prop.end);
1815 #endif
1816 }
1817 else if((!strcasecmp(prop->name, "CLIP-END")) || (!strcasecmp(prop->name, "CLIPEND"))) {
1818 (*snode)->prop.clip_end = smil_get_time_in_seconds(prop->value);
1819 #ifdef DEBUG_SMIL
1820 printf("clip-end: %d\n",(*snode)->prop.clip_end);
1821 #endif
1822 }
1823 else if(!strcasecmp(prop->name, "DUR")) {
1824 (*snode)->prop.duration = smil_get_time_in_seconds(prop->value);
1825 #ifdef DEBUG_SMIL
1826 palign;
1827 printf("DURATION: %d\n", (*snode)->prop.duration);
1828 #endif
1829 }
1830 }
1831 }
1832
1833 if(gotmrl) {
1834 smil_node_t *node = (*snode);
1835 int repeat = node->prop.repeat;
1836
1837 if((*snode)->mmk->ident == NULL)
1838 (*snode)->mmk->ident = strdup((*snode)->mmk->mrl);
1839
1840 if(sprop) {
1841 if(sprop->clip_begin && (node->prop.clip_begin == 0))
1842 node->mmk->start = sprop->clip_begin;
1843 else
1844 node->mmk->start = node->prop.clip_begin;
1845
1846 if(sprop->clip_end && (node->prop.clip_end == -1))
1847 node->mmk->end = sprop->clip_end;
1848 else
1849 node->mmk->end = node->prop.clip_end;
1850
1851 if(sprop->duration && (node->prop.duration == 0))
1852 node->mmk->end = node->mmk->start + sprop->duration;
1853 else
1854 node->mmk->end = node->mmk->start + node->prop.duration;
1855
1856 }
1857 else {
1858 node->mmk->start = node->prop.clip_begin;
1859 node->mmk->end = node->prop.clip_end;
1860 if(node->prop.duration)
1861 node->mmk->end = node->mmk->start + node->prop.duration;
1862 }
1863
1864 smil_repeat((repeat <= 1) ? 1 : repeat, node, snode, sprop);
1865 }
1866
1867 #ifdef DEBUG_SMIL
1868 palign;
1869 printf("---\n");
1870 offset -= 2;
1871 #endif
1872 }
1873
smil_repeat(int times,smil_node_t * from,smil_node_t ** snode,smil_property_t * sprop)1874 static void smil_repeat(int times, smil_node_t *from,
1875 smil_node_t **snode, smil_property_t *sprop) {
1876 int i;
1877
1878 if(times > 1) {
1879 smil_node_t *node, *to;
1880
1881 to = (*snode);
1882
1883 for(i = 0; i < (times - 1); i++) {
1884 int found = 0;
1885
1886 node = from;
1887
1888 while(!found) {
1889 smil_node_t *nnode = smil_new_node();
1890
1891 nnode->mmk = smil_duplicate_mmk(node->mmk);
1892
1893 (*snode)->next = nnode;
1894 (*snode) = nnode;
1895
1896 if(node == to)
1897 found = 1;
1898
1899 node = node->next;
1900 }
1901 }
1902 }
1903 else {
1904 smil_node_t *nnode = smil_new_node();
1905
1906 (*snode)->next = nnode;
1907 (*snode) = (*snode)->next;
1908 }
1909 }
1910 /*
1911 * Sequence playback
1912 */
smil_seq(smil_t * smil,smil_node_t ** snode,xml_node_t * node,smil_property_t * sprop)1913 static void smil_seq(smil_t *smil,
1914 smil_node_t **snode, xml_node_t *node, smil_property_t *sprop) {
1915 xml_node_t *seq;
1916
1917 #ifdef DEBUG_SMIL
1918 offset += 2;
1919 #endif
1920
1921 for(seq = node; seq; seq = seq->next) {
1922
1923 if(!strcasecmp(seq->name, "SEQ")) {
1924 smil_property_t smil_props;
1925 smil_node_t *node = (*snode);
1926
1927 smil_init_smil_property(&smil_props);
1928
1929 #ifdef DEBUG_SMIL
1930 offset += 2;
1931 palign;
1932 printf("seq NEW SEQ\n");
1933 offset -= 2;
1934 #endif
1935
1936 smil_get_properties(&smil_props, seq->props);
1937 smil_seq(smil, snode, seq->child, &smil_props);
1938 smil_repeat(smil_props.repeat, node, snode, &smil_props);
1939 smil_free_properties(&smil_props);
1940 }
1941 else if(!strcasecmp(seq->name, "PAR")) {
1942 smil_property_t smil_props;
1943 smil_node_t *node = (*snode);
1944
1945 smil_init_smil_property(&smil_props);
1946
1947 #ifdef DEBUG_SMIL
1948 offset += 2;
1949 palign;
1950 printf("seq NEW PAR\n");
1951 offset -= 2;
1952 #endif
1953
1954 smil_get_properties(&smil_props, seq->props);
1955 smil_par(smil, snode, seq->child, &smil_props);
1956 smil_repeat(smil_props.repeat, node, snode, &smil_props);
1957 smil_free_properties(&smil_props);
1958 }
1959 else if(!strcasecmp(seq->name, "SWITCH")) {
1960 smil_property_t smil_props;
1961 smil_node_t *node = (*snode);
1962
1963 smil_init_smil_property(&smil_props);
1964
1965 #ifdef DEBUG_SMIL
1966 offset += 2;
1967 palign;
1968 printf("seq NEW SWITCH\n");
1969 offset -= 2;
1970 #endif
1971
1972 smil_get_properties(&smil_props, seq->props);
1973 smil_switch(smil, snode, seq->child, &smil_props);
1974 smil_repeat(smil_props.repeat, node, snode, &smil_props);
1975 smil_free_properties(&smil_props);
1976 }
1977 else if((!strcasecmp(seq->name, "VIDEO")) ||
1978 (!strcasecmp(seq->name, "AUDIO")) ||
1979 (!strcasecmp(seq->name, "A"))) {
1980 smil_property_t smil_props;
1981
1982 smil_init_smil_property(&smil_props);
1983
1984 if(seq->child) {
1985 xml_node_t *child = seq->child;
1986
1987 while(child) {
1988
1989 if(!strcasecmp(child->name, "ANCHOR")) {
1990 #ifdef DEBUG_SMIL
1991 palign;
1992 printf("ANCHOR\n");
1993 #endif
1994 smil_get_properties(&smil_props, child->props);
1995 }
1996
1997 child = child->next;
1998 }
1999 }
2000
2001 smil_properties(smil, snode, seq->props, &smil_props);
2002 smil_free_properties(&smil_props);
2003 }
2004 }
2005 #ifdef DEBUG_SMIL
2006 offset -= 2;
2007 #endif
2008 }
2009 /*
2010 * Parallel playback
2011 */
smil_par(smil_t * smil,smil_node_t ** snode,xml_node_t * node,smil_property_t * sprop)2012 static void smil_par(smil_t *smil, smil_node_t **snode, xml_node_t *node, smil_property_t *sprop) {
2013 xml_node_t *par;
2014
2015 #ifdef DEBUG_SMIL
2016 offset += 2;
2017 #endif
2018
2019 for(par = node; par; par = par->next) {
2020
2021 if(!strcasecmp(par->name, "SEQ")) {
2022 smil_property_t smil_props;
2023 smil_node_t *node = (*snode);
2024
2025 smil_init_smil_property(&smil_props);
2026
2027 #ifdef DEBUG_SMIL
2028 offset += 2;
2029 palign;
2030 printf("par NEW SEQ\n");
2031 offset -= 2;
2032 #endif
2033
2034 smil_get_properties(&smil_props, par->props);
2035 smil_seq(smil, snode, par->child, &smil_props);
2036 smil_repeat(smil_props.repeat, node, snode, &smil_props);
2037 smil_free_properties(&smil_props);
2038 }
2039 else if(!strcasecmp(par->name, "PAR")) {
2040 smil_property_t smil_props;
2041 smil_node_t *node = (*snode);
2042
2043 smil_init_smil_property(&smil_props);
2044
2045 #ifdef DEBUG_SMIL
2046 offset += 2;
2047 palign;
2048 printf("par NEW PAR\n");
2049 offset -= 2;
2050 #endif
2051
2052 smil_get_properties(&smil_props, par->props);
2053 smil_par(smil, snode, par->child, &smil_props);
2054 smil_repeat(smil_props.repeat, node, snode, &smil_props);
2055 smil_free_properties(&smil_props);
2056 }
2057 else if(!strcasecmp(par->name, "SWITCH")) {
2058 smil_property_t smil_props;
2059 smil_node_t *node = (*snode);
2060
2061 smil_init_smil_property(&smil_props);
2062
2063 #ifdef DEBUG_SMIL
2064 offset += 2;
2065 palign;
2066 printf("par NEW SWITCH\n");
2067 offset -= 2;
2068 #endif
2069
2070 smil_get_properties(&smil_props, par->props);
2071 smil_switch(smil, snode, par->child, &smil_props);
2072 smil_repeat(smil_props.repeat, node, snode, &smil_props);
2073 smil_free_properties(&smil_props);
2074 }
2075 else if((!strcasecmp(par->name, "VIDEO")) ||
2076 (!strcasecmp(par->name, "AUDIO")) ||
2077 (!strcasecmp(par->name, "A"))) {
2078 smil_property_t smil_props;
2079
2080 smil_init_smil_property(&smil_props);
2081
2082 if(par->child) {
2083 xml_node_t *child = par->child;
2084
2085 while(child) {
2086 if(!strcasecmp(child->name, "ANCHOR")) {
2087 #ifdef DEBUG_SMIL
2088 palign;
2089 printf("ANCHOR\n");
2090 #endif
2091 smil_get_properties(&smil_props, child->props);
2092 }
2093 child = child->next;
2094 }
2095 }
2096
2097 smil_properties(smil, snode, par->props, &smil_props);
2098 smil_free_properties(&smil_props);
2099 }
2100 }
2101
2102 #ifdef DEBUG_SMIL
2103 offset -= 2;
2104 #endif
2105 }
smil_switch(smil_t * smil,smil_node_t ** snode,xml_node_t * node,smil_property_t * sprop)2106 static void smil_switch(smil_t *smil, smil_node_t **snode, xml_node_t *node, smil_property_t *sprop) {
2107 xml_node_t *nswitch;
2108
2109 #ifdef DEBUG_SMIL
2110 offset += 2;
2111 #endif
2112
2113 for(nswitch = node; nswitch; nswitch = nswitch->next) {
2114
2115 if(!strcasecmp(nswitch->name, "SEQ")) {
2116 smil_property_t smil_props;
2117 smil_node_t *node = (*snode);
2118
2119 smil_init_smil_property(&smil_props);
2120
2121 #ifdef DEBUG_SMIL
2122 offset += 2;
2123 palign;
2124 printf("switch NEW SEQ\n");
2125 offset -= 2;
2126 #endif
2127
2128 smil_get_properties(&smil_props, nswitch->props);
2129 smil_seq(smil, snode, nswitch->child, &smil_props);
2130 smil_repeat(smil_props.repeat, node, snode, &smil_props);
2131 smil_free_properties(&smil_props);
2132 }
2133 else if(!strcasecmp(nswitch->name, "PAR")) {
2134 smil_property_t smil_props;
2135 smil_node_t *node = (*snode);
2136
2137 smil_init_smil_property(&smil_props);
2138
2139 #ifdef DEBUG_SMIL
2140 offset += 2;
2141 palign;
2142 printf("switch NEW PAR\n");
2143 offset -= 2;
2144 #endif
2145
2146 smil_get_properties(&smil_props, nswitch->props);
2147 smil_par(smil, snode, nswitch->child, &smil_props);
2148 smil_repeat(smil_props.repeat, node, snode, &smil_props);
2149 smil_free_properties(&smil_props);
2150 }
2151 else if((!strcasecmp(nswitch->name, "VIDEO")) ||
2152 (!strcasecmp(nswitch->name, "AUDIO")) ||
2153 (!strcasecmp(nswitch->name, "A"))) {
2154 smil_property_t smil_props;
2155
2156 smil_init_smil_property(&smil_props);
2157
2158 if(nswitch->child) {
2159 xml_node_t *child = nswitch->child;
2160
2161 while(child) {
2162
2163 if(!strcasecmp(child->name, "ANCHOR")) {
2164 #ifdef DEBUG_SMIL
2165 palign;
2166 printf("ANCHOR\n");
2167 #endif
2168 smil_get_properties(&smil_props, child->props);
2169 }
2170
2171 child = child->next;
2172 }
2173 }
2174
2175 smil_properties(smil, snode, nswitch->props, &smil_props);
2176 smil_free_properties(&smil_props);
2177 }
2178 }
2179 #ifdef DEBUG_SMIL
2180 offset -= 2;
2181 #endif
2182 }
2183
smil_add_mmk(int * num,mediamark_t *** mmk,smil_node_t * node)2184 static void smil_add_mmk(int *num, mediamark_t ***mmk, smil_node_t *node) {
2185 if(node->mmk) {
2186 (*mmk) = (mediamark_t **) realloc((*mmk), sizeof(mediamark_t *) * (*num + 2));
2187
2188 (*mmk)[*num] = node->mmk;
2189 *num += 1;
2190 }
2191
2192 if(node->next)
2193 smil_add_mmk(num, mmk, node->next);
2194 }
smil_fill_mmk(smil_t * smil,mediamark_t *** mmk)2195 static int smil_fill_mmk(smil_t *smil, mediamark_t ***mmk) {
2196 int num = 0;
2197
2198 if(smil->first)
2199 smil_add_mmk(&num, mmk, smil->first);
2200
2201 return num;
2202 }
2203
smil_free_node(smil_node_t * node)2204 static void smil_free_node(smil_node_t *node) {
2205 if(node->next)
2206 smil_free_node(node->next);
2207
2208 /* mmk shouldn't be fried */
2209 smil_free_properties(&(node->prop));
2210 free(node);
2211 }
smil_free_smil(smil_t * smil)2212 static void smil_free_smil(smil_t *smil) {
2213 if(smil->first)
2214 smil_free_node(smil->first);
2215
2216 SAFE_FREE(smil->title);
2217 SAFE_FREE(smil->author);
2218 SAFE_FREE(smil->copyright);
2219 SAFE_FREE(smil->year);
2220 SAFE_FREE(smil->base);
2221 }
2222
xml_smil_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)2223 static mediamark_t **xml_smil_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
2224 mediamark_t **mmk = NULL;
2225
2226 if(xml_tree) {
2227 xml_node_t *smil_entry, *smil_ref;
2228 smil_t smil;
2229 int entries_smil = 0;
2230
2231 memset(&smil, 0, sizeof(smil_t));
2232
2233 if(!strcasecmp(xml_tree->name, "SMIL")) {
2234
2235 smil.first = smil.node = smil_new_node();
2236 smil_entry = xml_tree->child;
2237
2238 while(smil_entry) {
2239
2240 #ifdef DEBUG_SMIL
2241 printf("smil_entry '%s'\n", smil_entry->name);
2242 #endif
2243 if(!strcasecmp(smil_entry->name, "HEAD")) {
2244 #ifdef DEBUG_SMIL
2245 printf("+---------+\n");
2246 printf("| IN HEAD |\n");
2247 printf("+---------+\n");
2248 #endif
2249 smil_ref = smil_entry->child;
2250 while(smil_ref) {
2251 #ifdef DEBUG_SMIL
2252 printf(" smil_ref '%s'\n", smil_ref->name);
2253 #endif
2254 smil_header(&smil, smil_ref->props);
2255
2256 smil_ref = smil_ref->next;
2257 }
2258 }
2259 else if(!strcasecmp(smil_entry->name, "BODY")) {
2260 #ifdef DEBUG_SMIL
2261 printf("+---------+\n");
2262 printf("| IN BODY |\n");
2263 printf("+---------+\n");
2264 #endif
2265 __kino:
2266 smil_ref = smil_entry->child;
2267 while(smil_ref) {
2268 #ifdef DEBUG_SMIL
2269 printf(" smil_ref '%s'\n", smil_ref->name);
2270 #endif
2271 if(!strcasecmp(smil_ref->name, "SEQ")) {
2272 smil_property_t smil_props;
2273 smil_node_t *node = smil.node;
2274
2275 #ifdef DEBUG_SMIL
2276 palign;
2277 printf("SEQ Found\n");
2278 #endif
2279 smil_init_smil_property(&smil_props);
2280
2281 smil_get_properties(&smil_props, smil_ref->props);
2282 smil_seq(&smil, &(smil.node), smil_ref->child, &smil_props);
2283 smil_repeat(smil_props.repeat, node, &(smil.node), &smil_props);
2284 smil_free_properties(&smil_props);
2285 }
2286 else if(!strcasecmp(smil_ref->name, "PAR")) {
2287 smil_property_t smil_props;
2288 smil_node_t *node = smil.node;
2289
2290 #ifdef DEBUG_SMIL
2291 palign;
2292 printf("PAR Found\n");
2293 #endif
2294
2295 smil_init_smil_property(&smil_props);
2296
2297 smil_get_properties(&smil_props, smil_ref->props);
2298 smil_par(&smil, &(smil.node), smil_ref->child, &smil_props);
2299 smil_repeat(smil_props.repeat, node, &(smil.node), &smil_props);
2300 smil_free_properties(&smil_props);
2301 }
2302 else if(!strcasecmp(smil_ref->name, "A")) {
2303 #ifdef DEBUG_SMIL
2304 palign;
2305 printf(" IN A\n");
2306 #endif
2307 smil_properties(&smil, &(smil.node), smil_ref->props, NULL);
2308 }
2309 else if(!strcasecmp(smil_ref->name, "SWITCH")) {
2310 smil_property_t smil_props;
2311 smil_node_t *node = smil.node;
2312
2313 #ifdef DEBUG_SMIL
2314 palign;
2315 printf("SWITCH Found\n");
2316 #endif
2317
2318 smil_init_smil_property(&smil_props);
2319
2320 smil_get_properties(&smil_props, smil_ref->props);
2321 smil_switch(&smil, &(smil.node), smil_ref->child, &smil_props);
2322 smil_repeat(smil_props.repeat, node, &(smil.node), &smil_props);
2323 smil_free_properties(&smil_props);
2324 }
2325 else {
2326 smil_properties(&smil, &smil.node, smil_ref->props, NULL);
2327 }
2328
2329 smil_ref = smil_ref->next;
2330 }
2331 }
2332 else if(!strcasecmp(smil_entry->name, "SEQ")) { /* smil2, kino format(no body) */
2333 goto __kino;
2334 }
2335
2336 smil_entry = smil_entry->next;
2337 }
2338
2339 #ifdef DEBUG_SMIL
2340 printf("DUMPING TREE:\n");
2341 printf("-------------\n");
2342 smil_dump_tree(smil.first);
2343
2344 printf("DUMPING HEADER:\n");
2345 printf("---------------\n");
2346 smil_dump_header(&smil);
2347 #endif
2348
2349 entries_smil = smil_fill_mmk(&smil, &mmk);
2350 smil_free_smil(&smil);
2351 }
2352 #ifdef DEBUG
2353 else
2354 fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
2355 #endif
2356
2357 if(entries_smil) {
2358 mmk[entries_smil] = NULL;
2359 playlist->entries = entries_smil;
2360 playlist->type = strdup("SMIL");
2361 return mmk;
2362 }
2363 }
2364
2365 return NULL;
2366 }
2367 /*
2368 * ********************************** SMIL END ***********************************
2369 */
2370
xml_freevo_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)2371 static mediamark_t **xml_freevo_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
2372 mediamark_t **mmk = NULL;
2373
2374 if(xml_tree) {
2375 xml_node_t *fvo_entry;
2376 xml_property_t *fvo_prop;
2377 int entries_fvo = 0;
2378
2379 if(!strcasecmp(xml_tree->name, "FREEVO")) {
2380 char *origin;
2381 char *url = NULL;
2382 char *sub = NULL;
2383 char *title = NULL;
2384
2385 origin = get_basedir(filename);
2386
2387 fvo_entry = xml_tree->child;
2388
2389 while(fvo_entry) {
2390 if(!strcasecmp(fvo_entry->name, "MOVIE")) {
2391 xml_node_t *sentry;
2392
2393 for(fvo_prop = fvo_entry->props; fvo_prop; fvo_prop = fvo_prop->next) {
2394 if(!strcasecmp(fvo_prop->name, "TITLE")) {
2395 title = fvo_prop->value;
2396 }
2397 }
2398
2399 sentry = fvo_entry->child;
2400 while(sentry) {
2401
2402 if((!strcasecmp(sentry->name, "VIDEO")) || (!strcasecmp(sentry->name, "AUDIO"))) {
2403 xml_node_t *ssentry = sentry->child;
2404
2405 while(ssentry) {
2406
2407 if(!strcasecmp(ssentry->name, "SUBTITLE")) {
2408 sub = ssentry->data;
2409 }
2410 else if((!strcasecmp(ssentry->name, "URL")) ||
2411 (!strcasecmp(ssentry->name, "DVD")) || (!strcasecmp(ssentry->name, "VCD"))) {
2412 url = strdup(ssentry->data);
2413 }
2414 else if(!strcasecmp(ssentry->name, "FILE")) {
2415
2416 if(origin) {
2417 const size_t urlsize = strlen(origin) + strlen(ssentry->data) + 2;
2418 url = (char *) malloc(urlsize);
2419 strlcat(url, origin, urlsize);
2420
2421 if((url[strlen(url) - 1] == '/') && (*ssentry->data == '/'))
2422 url[strlen(url) - 1] = '\0';
2423
2424 strlcat(url, ssentry->data, urlsize);
2425
2426 }
2427 else
2428 url = strdup(ssentry->data);
2429
2430 }
2431
2432 if(url) {
2433
2434 mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_fvo + 2));
2435
2436 mediamark_store_mmk(&mmk[entries_fvo], url, title, sub, 0, -1, 0, 0);
2437 playlist->entries = ++entries_fvo;
2438
2439 free(url);
2440 url = NULL;
2441 }
2442
2443 sub = NULL;
2444 ssentry = ssentry->next;
2445 }
2446
2447 }
2448
2449 sentry = sentry->next;
2450 }
2451
2452 }
2453
2454 title = NULL;
2455 fvo_entry = fvo_entry->next;
2456 }
2457 free(origin);
2458 }
2459 #ifdef DEBUG
2460 else
2461 fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
2462 #endif
2463
2464 if(entries_fvo) {
2465 mmk[entries_fvo] = NULL;
2466 playlist->type = strdup("FREEVO");
2467 return mmk;
2468 }
2469 }
2470
2471 return NULL;
2472 }
2473
guess_xml_based_playlist(playlist_t * playlist,const char * filename)2474 static mediamark_t **guess_xml_based_playlist(playlist_t *playlist, const char *filename) {
2475 mediamark_t **mmk = NULL;
2476
2477 if(filename) {
2478 char *xml_content;
2479 int size;
2480 int result;
2481 xml_node_t *xml_tree, *top_xml_tree;
2482
2483 if((xml_content = _read_file(filename, &size)) != NULL) {
2484 int i;
2485 playlist_xml_guess_func_t guess_functions[] = {
2486 xml_asx_playlist,
2487 xml_gx_playlist,
2488 xml_noatun_playlist,
2489 xml_smil_playlist,
2490 xml_freevo_playlist,
2491 NULL
2492 };
2493
2494 xml_parser_init_R(xml_parser_t *xml, xml_content, size, XML_PARSER_CASE_INSENSITIVE);
2495 if((result = xml_parser_build_tree_R(xml, &xml_tree)) != XML_PARSER_OK)
2496 goto __failure;
2497
2498 top_xml_tree = xml_tree;
2499
2500 /* Check all playlists */
2501 for(i = 0; guess_functions[i]; i++) {
2502 if((mmk = guess_functions[i](playlist, filename, xml_content, xml_tree)))
2503 break;
2504 }
2505
2506 xml_parser_free_tree(top_xml_tree);
2507 __failure:
2508
2509 xml_parser_finalize_R(xml);
2510 free(xml_content);
2511 }
2512 }
2513
2514 return mmk;
2515 }
2516 /*
2517 * XML based playlists end.
2518 */
2519
2520 /*
2521 * Public
2522 */
mediamark_get_entry_from_id(const char * ident)2523 int mediamark_get_entry_from_id(const char *ident) {
2524 gGui_t *gui = gGui;
2525 if(ident && gui->playlist.num) {
2526 int i;
2527
2528 for(i = 0; i < gui->playlist.num; i++) {
2529 if(!strcasecmp(ident, gui->playlist.mmk[i]->ident))
2530 return i;
2531 }
2532 }
2533 return -1;
2534 }
2535
mediamark_insert_entry(int index,const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)2536 void mediamark_insert_entry(int index, const char *mrl, const char *ident,
2537 const char *sub, int start, int end, int av_offset, int spu_offset) {
2538 gGui_t *gui = gGui;
2539 char autosub[2*XITK_PATH_MAX + XITK_NAME_MAX + 2];
2540 char subpath[XITK_PATH_MAX + XITK_NAME_MAX + 2];
2541 DIR *dir;
2542 struct dirent *dentry;
2543
2544
2545 gui->playlist.mmk = (mediamark_t **) realloc(gui->playlist.mmk, sizeof(mediamark_t *) * (gui->playlist.num + 2));
2546
2547 if(index < gui->playlist.num)
2548 memmove(&gui->playlist.mmk[index+1], &gui->playlist.mmk[index],
2549 (gui->playlist.num - index) * sizeof(gui->playlist.mmk[0]) );
2550
2551 /*
2552 * If subtitle_autoload is enabled and subtitle is NULL
2553 * then try to see if a matching subtitle exist
2554 */
2555 if(mrl && (!sub) && gui->subtitle_autoload) {
2556
2557 if(mrl_look_like_file((char *) mrl)) {
2558 char *know_subs = "sub,srt,asc,smi,ssa,ass,txt";
2559 char *vsubs, *pvsubs;
2560 char *_mrl, *ending, *ext, *path, *d_name;
2561 struct stat pstat;
2562
2563 _mrl = (char *) mrl;
2564 if(!strncasecmp(_mrl, "file:", 5))
2565 _mrl += 5;
2566
2567 strlcpy(autosub, _mrl, sizeof(autosub));
2568
2569 if((ending = strrchr(autosub, '.')))
2570 ending++;
2571 else {
2572 ending = autosub + strlen(autosub);
2573 *ending++ = '.';
2574 }
2575
2576 vsubs = strdup(know_subs);
2577
2578 pvsubs = vsubs;
2579 while((ext = xine_strsep(&pvsubs, ",")) && !sub) {
2580 strcpy(ending, ext);
2581 *(ending + strlen(ext) + 1) = '\0';
2582
2583 if(((stat(autosub, &pstat)) > -1) && (S_ISREG(pstat.st_mode)) && strcmp(autosub, _mrl))
2584 sub = autosub;
2585 }
2586 free(vsubs);
2587
2588 /* try matching "<name>.*.<know_subs>" like used by opensubtitles */
2589 if( !sub ) {
2590 *ending = '\0';
2591
2592 strlcpy(subpath, autosub, sizeof(subpath));
2593
2594 if((d_name = strrchr(subpath, '/'))) {
2595 *d_name++ = '\0';
2596 path = subpath;
2597 } else {
2598 d_name = subpath;
2599 path = ".";
2600 }
2601
2602 if((dir = opendir(path))) {
2603 while((dentry = readdir(dir))) {
2604 if( (strncmp(dentry->d_name, d_name, strlen(d_name)) == 0) &&
2605 (ending = strrchr(dentry->d_name, '.')) ) {
2606
2607 if( strstr(know_subs, ending+1) ) {
2608 snprintf(autosub,sizeof(autosub),"%s/%s",path,dentry->d_name);
2609 autosub[sizeof(autosub)-1]='\0';
2610 if(((stat(autosub, &pstat)) > -1) && (S_ISREG(pstat.st_mode)) && strcmp(autosub, _mrl)) {
2611 sub = autosub;
2612 break;
2613 }
2614 }
2615 }
2616 }
2617 closedir(dir);
2618 }
2619 }
2620 }
2621
2622 }
2623
2624 if(mediamark_store_mmk(&gui->playlist.mmk[index],
2625 mrl, ident, sub, start, end, av_offset, spu_offset))
2626 gui->playlist.num++;
2627 }
2628
mediamark_append_entry(const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)2629 void mediamark_append_entry(const char *mrl, const char *ident,
2630 const char *sub, int start, int end, int av_offset, int spu_offset) {
2631 gGui_t *gui = gGui;
2632
2633 mediamark_insert_entry(gui->playlist.num, mrl, ident, sub, start, end, av_offset, spu_offset);
2634 }
2635
mediamark_free_mediamarks(void)2636 void mediamark_free_mediamarks(void) {
2637 gGui_t *gui = gGui;
2638
2639 if(gui->playlist.num) {
2640 int i;
2641
2642 for(i = 0; i < gui->playlist.num; i++)
2643 mediamark_free_entry(i);
2644
2645 SAFE_FREE(gui->playlist.mmk);
2646 gui->playlist.num = 0;
2647 gui->playlist.cur = -1;
2648 }
2649 }
2650
mediamark_reset_played_state(void)2651 void mediamark_reset_played_state(void) {
2652 gGui_t *gui = gGui;
2653
2654 if(gui->playlist.num) {
2655 int i;
2656
2657 for(i = 0; i < gui->playlist.num; i++)
2658 gui->playlist.mmk[i]->played = 0;
2659 }
2660 }
2661
mediamark_all_played(void)2662 int mediamark_all_played(void) {
2663 gGui_t *gui = gGui;
2664
2665 if(gui->playlist.num) {
2666 int i;
2667
2668 for(i = 0; i < gui->playlist.num; i++) {
2669 if(gui->playlist.mmk[i]->played == 0)
2670 return 0;
2671 }
2672 }
2673
2674 return 1;
2675 }
2676
mediamark_get_shuffle_next(void)2677 int mediamark_get_shuffle_next(void) {
2678 gGui_t *gui = gGui;
2679 int next = 0;
2680
2681 if(gui->playlist.num >= 3 && gui->playlist.cur >= 0) {
2682 int remain = gui->playlist.num;
2683 int entries[remain];
2684 float num = (float) gui->playlist.num;
2685 int found = 0;
2686
2687 memset(&entries, 0, sizeof(int) * remain);
2688
2689 srandom((unsigned int)time(NULL));
2690 entries[gui->playlist.cur] = 1;
2691
2692 do {
2693 next = (int) (num * random() / RAND_MAX);
2694
2695 if(next != gui->playlist.cur) {
2696
2697 if(gui->playlist.mmk[next]->played == 0)
2698 found = 1;
2699 else if(entries[next] == 0) {
2700 entries[next] = 1;
2701 remain--;
2702 }
2703
2704 if((!found) && (!remain))
2705 found = 2; /* No more choice */
2706
2707 }
2708
2709 } while(!found);
2710
2711 if(found == 2)
2712 next = gui->playlist.cur;
2713
2714 }
2715 else if(gui->playlist.num == 2)
2716 next = !gui->playlist.cur;
2717
2718 return next;
2719 }
2720
mediamark_replace_entry(mediamark_t ** mmk,const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)2721 void mediamark_replace_entry(mediamark_t **mmk,
2722 const char *mrl, const char *ident, const char *sub,
2723 int start, int end, int av_offset, int spu_offset) {
2724 SAFE_FREE((*mmk)->mrl);
2725 SAFE_FREE((*mmk)->ident);
2726 SAFE_FREE((*mmk)->sub);
2727
2728 mediamark_free_alternates((*mmk));
2729 (*mmk)->start = 0;
2730 (*mmk)->end = -1;
2731 (*mmk)->av_offset = 0;
2732 (*mmk)->spu_offset = 0;
2733
2734
2735 (void) mediamark_store_mmk(mmk, mrl, ident, sub, start, end, av_offset, spu_offset);
2736 }
2737
mediamark_get_current_mmk(void)2738 mediamark_t *mediamark_get_current_mmk(void) {
2739 gGui_t *gui = gGui;
2740
2741 if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2742 gui->playlist.mmk[gui->playlist.cur])
2743 return gui->playlist.mmk[gui->playlist.cur];
2744
2745 return (mediamark_t *) NULL;
2746 }
2747
mediamark_get_mmk_by_index(int index)2748 mediamark_t *mediamark_get_mmk_by_index(int index) {
2749 gGui_t *gui = gGui;
2750
2751 if(index < gui->playlist.num && index >= 0 && gui->playlist.mmk &&
2752 gui->playlist.mmk[index])
2753 return gui->playlist.mmk[index];
2754
2755 return (mediamark_t *) NULL;
2756 }
2757
mediamark_get_current_mrl(void)2758 const char *mediamark_get_current_mrl(void) {
2759 gGui_t *gui = gGui;
2760
2761 if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2762 gui->playlist.mmk[gui->playlist.cur] &&
2763 gui->playlist.cur < gui->playlist.num)
2764 return gui->playlist.mmk[gui->playlist.cur]->mrl;
2765
2766 return NULL;
2767 }
2768
mediamark_get_current_ident(void)2769 const char *mediamark_get_current_ident(void) {
2770 gGui_t *gui = gGui;
2771
2772 if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2773 gui->playlist.mmk[gui->playlist.cur])
2774 return gui->playlist.mmk[gui->playlist.cur]->ident;
2775
2776 return NULL;
2777 }
2778
mediamark_get_current_sub(void)2779 const char *mediamark_get_current_sub(void) {
2780 gGui_t *gui = gGui;
2781
2782 if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2783 gui->playlist.mmk[gui->playlist.cur])
2784 return gui->playlist.mmk[gui->playlist.cur]->sub;
2785
2786 return NULL;
2787 }
2788
mediamark_free_entry(int offset)2789 void mediamark_free_entry(int offset) {
2790 gGui_t *gui = gGui;
2791
2792 if(offset < gui->playlist.num && offset >= 0 && gui->playlist.mmk &&
2793 gui->playlist.mmk[offset])
2794 if(mediamark_free_mmk(&gui->playlist.mmk[offset]))
2795 gui->playlist.num--;
2796 }
2797
mediamark_concat_mediamarks(const char * _filename)2798 int mediamark_concat_mediamarks(const char *_filename) {
2799 gGui_t *gui = gGui;
2800 playlist_t *playlist;
2801 int i;
2802 mediamark_t **mmk = NULL;
2803 const char *filename = _filename;
2804 playlist_guess_func_t guess_functions[] = {
2805 guess_xml_based_playlist,
2806 guess_toxine_playlist,
2807 guess_pls_playlist,
2808 guess_m3u_playlist,
2809 guess_sfv_playlist,
2810 guess_raw_playlist,
2811 NULL
2812 };
2813
2814 if(_filename) {
2815 if(!strncasecmp("file:/", _filename, 6))
2816 filename = (_filename + 6);
2817 }
2818
2819 playlist = (playlist_t *) calloc(1, sizeof(playlist_t));
2820
2821 for(i = 0; guess_functions[i]; i++) {
2822 if((mmk = guess_functions[i](playlist, filename)))
2823 break;
2824 }
2825
2826 if(mmk) {
2827 #ifdef DEBUG
2828 printf("Playlist file (%s) is valid (%s).\n", filename, playlist->type);
2829 #endif
2830 }
2831 else {
2832 fprintf(stderr, _("Playlist file (%s) is invalid.\n"), filename);
2833 SAFE_FREE(playlist);
2834 return 0;
2835 }
2836
2837 gui->playlist.cur = gui->playlist.num;
2838
2839 for(i = 0; i < playlist->entries; i++)
2840 mediamark_append_entry(mmk[i]->mrl, mmk[i]->ident, mmk[i]->sub,
2841 mmk[i]->start, mmk[i]->end, mmk[i]->av_offset, mmk[i]->spu_offset);
2842
2843 for(i = 0; i < playlist->entries; i++)
2844 (void) mediamark_free_mmk(&mmk[i]);
2845
2846 SAFE_FREE(mmk);
2847 SAFE_FREE(playlist->type);
2848 SAFE_FREE(playlist);
2849
2850 return 1;
2851 }
2852
mediamark_load_mediamarks(const char * _filename)2853 void mediamark_load_mediamarks(const char *_filename) {
2854 gGui_t *gui = gGui;
2855 playlist_t *playlist;
2856 int i, onum;
2857 mediamark_t **mmk = NULL;
2858 mediamark_t **ommk;
2859 const char *filename = _filename;
2860 playlist_guess_func_t guess_functions[] = {
2861 guess_xml_based_playlist,
2862 guess_toxine_playlist,
2863 guess_pls_playlist,
2864 guess_m3u_playlist,
2865 guess_sfv_playlist,
2866 guess_raw_playlist,
2867 NULL
2868 };
2869
2870 if(_filename) {
2871 if(!strncasecmp("file:/", _filename, 6))
2872 filename = (_filename + 6);
2873 }
2874
2875 playlist = (playlist_t *) calloc(1, sizeof(playlist_t));
2876
2877 for(i = 0; guess_functions[i]; i++) {
2878 if((mmk = guess_functions[i](playlist, filename)))
2879 break;
2880 }
2881
2882 if(mmk) {
2883 #ifdef DEBUG
2884 printf("Playlist file (%s) is valid (%s).\n", filename, playlist->type);
2885 #endif
2886 }
2887 else {
2888 fprintf(stderr, _("Playlist file (%s) is invalid.\n"), filename);
2889 SAFE_FREE(playlist);
2890 return;
2891 }
2892
2893 ommk = gui->playlist.mmk;
2894 onum = gui->playlist.num;
2895
2896 gui->playlist.mmk = mmk;
2897 gui->playlist.num = playlist->entries;
2898
2899 if(gui->playlist.loop == PLAYLIST_LOOP_SHUFFLE)
2900 gui->playlist.cur = mediamark_get_shuffle_next();
2901 else
2902 gui->playlist.cur = 0;
2903
2904 for(i = 0; i < onum; i++)
2905 (void) mediamark_free_mmk(&ommk[i]);
2906
2907 SAFE_FREE(ommk);
2908
2909 SAFE_FREE(playlist->type);
2910 SAFE_FREE(playlist);
2911 }
2912
mediamark_save_mediamarks(const char * filename)2913 void mediamark_save_mediamarks(const char *filename) {
2914 gGui_t *gui = gGui;
2915 char *fullfn;
2916 char *pn;
2917 char *fn;
2918 int status = 1;
2919
2920 if(!gui->playlist.num)
2921 return;
2922
2923 fullfn = strdup(filename);
2924
2925 pn = fullfn;
2926
2927 fn = strrchr(fullfn, '/');
2928
2929 if(fn) {
2930 *fn = '\0';
2931 fn++;
2932 status = mkdir_safe(pn);
2933 }
2934 else
2935 fn = pn;
2936
2937 if(status && fn) {
2938 int i;
2939 FILE *fd;
2940 const char *store_item;
2941 char buffer[_PATH_MAX + _NAME_MAX + 2], current_dir[_PATH_MAX];
2942
2943 if (getcwd(current_dir, sizeof(current_dir)) > 0) {
2944 if (current_dir[strlen(current_dir) - 1] != '/')
2945 strlcat(current_dir, "/", sizeof(current_dir));
2946 } else
2947 strcpy(current_dir, "");
2948
2949 if((fd = fopen(filename, "w")) != NULL) {
2950
2951 fprintf(fd, "# toxine playlist\n");
2952
2953 for(i = 0; i < gui->playlist.num; i++) {
2954 fprintf(fd, "\nentry {\n");
2955 fprintf(fd, "\tidentifier = %s;\n", gui->playlist.mmk[i]->ident);
2956 store_item = concat_basedir(buffer, sizeof(buffer), current_dir, gui->playlist.mmk[i]->mrl);
2957 fprintf(fd, "\tmrl = %s;\n", store_item);
2958 if(gui->playlist.mmk[i]->sub) {
2959 store_item = concat_basedir(buffer, sizeof(buffer), current_dir, gui->playlist.mmk[i]->sub);
2960 fprintf(fd, "\tsubtitle = %s;\n", store_item);
2961 }
2962 if(gui->playlist.mmk[i]->start > 0)
2963 fprintf(fd, "\tstart = %d;\n", gui->playlist.mmk[i]->start);
2964 if(gui->playlist.mmk[i]->end != -1)
2965 fprintf(fd, "\tend = %d;\n", gui->playlist.mmk[i]->end);
2966 if(gui->playlist.mmk[i]->av_offset != 0)
2967 fprintf(fd, "\tav_offset = %d;\n", gui->playlist.mmk[i]->av_offset);
2968 if(gui->playlist.mmk[i]->spu_offset != 0)
2969 fprintf(fd, "\tspu_offset = %d;\n", gui->playlist.mmk[i]->spu_offset);
2970 fprintf(fd,"};\n");
2971 }
2972
2973 fprintf(fd, "# END\n");
2974
2975 fclose(fd);
2976 #ifdef DEBUG
2977 printf("Playlist '%s' saved.\n", filename);
2978 #endif
2979 }
2980 else
2981 fprintf(stderr, _("Unable to save playlist (%s): %s.\n"), filename, strerror(errno));
2982
2983 }
2984
2985 free(fullfn);
2986 }
2987
mrl_look_like_file(char * mrl)2988 int mrl_look_like_file(char *mrl) {
2989
2990 if(mrl && strlen(mrl)) {
2991 if((strncasecmp(mrl, "file:", 5)) &&
2992 strstr (mrl, ":/") && (strstr (mrl, ":/") < strchr(mrl, '/')))
2993 return 0;
2994 }
2995
2996 return 1;
2997 }
2998
mediamark_collect_from_directory(char * filepathname)2999 void mediamark_collect_from_directory(char *filepathname) {
3000 DIR *dir;
3001 struct dirent *dentry;
3002
3003 if((dir = opendir(filepathname))) {
3004
3005 while((dentry = readdir(dir))) {
3006 char fullpathname[XITK_PATH_MAX + XITK_NAME_MAX + 2] = "";
3007
3008 snprintf(fullpathname, sizeof(fullpathname) - 1, "%s/%s", filepathname, dentry->d_name);
3009
3010 if(strlen(fullpathname)) {
3011
3012 if(fullpathname[strlen(fullpathname) - 1] == '/')
3013 fullpathname[strlen(fullpathname) - 1] = '\0';
3014
3015 if(is_a_dir(fullpathname)) {
3016 if(!((strlen(dentry->d_name) == 1) && (dentry->d_name[0] == '.'))
3017 && !((strlen(dentry->d_name) == 2) &&
3018 ((dentry->d_name[0] == '.') && dentry->d_name[1] == '.'))) {
3019 mediamark_collect_from_directory(fullpathname);
3020 }
3021 }
3022 else {
3023 char *p, *extension;
3024 char loname[XITK_PATH_MAX + XITK_NAME_MAX + 2] = "";
3025
3026 p = strncat(loname, fullpathname, strlen(fullpathname));
3027 while(*p && (*p != '\0')) {
3028 *p = tolower(*p);
3029 p++;
3030 }
3031
3032 if((extension = strrchr(loname, '.')) && (strlen(extension) > 1)) {
3033 char ext[strlen(extension) + 2];
3034 char *valid_endings =
3035 ".pls .m3u .sfv .tox .asx .smi .smil .xml .fxd " /* Playlists */
3036 ".4xm .ac3 .aif .aiff .asf .wmv .wma .wvx .wax .aud .avi .cin .cpk .cak "
3037 ".film .dv .dif .fli .flc .mjpg .mov .qt .m2p .mp4 .mp3 .mp2 .mpa .mpega .mpg .mpeg "
3038 ".mpv .mve .mv8 .nsf .nsv .ogg .ogm .spx .pes .png .mng .pva .ra .rm "
3039 ".ram .rmvb .roq .snd .au .str .iki .ik2 .dps .dat .xa .xa1 .xa2 .xas .xap .ts .m2t "
3040 ".trp .vob .voc .vox .vqa .wav .wve .y4m ";
3041
3042 snprintf(ext, sizeof(ext), "%s ", extension);
3043
3044 if(strstr(valid_endings, ext)) {
3045 mediamark_append_entry((const char *)fullpathname,
3046 (const char *)fullpathname, NULL, 0, -1, 0, 0);
3047 }
3048 }
3049 }
3050 }
3051 }
3052 closedir(dir);
3053 }
3054 }
3055
3056 /*
3057 * EDITOR
3058 */
mmkeditor_exit(xitk_widget_t * w,void * data)3059 static void mmkeditor_exit(xitk_widget_t *w, void *data) {
3060 gGui_t *gui = gGui;
3061
3062 if(mmkeditor.running) {
3063 window_info_t wi;
3064
3065 mmkeditor.running = 0;
3066 mmkeditor.visible = 0;
3067
3068 if((xitk_get_window_info(mmkeditor.widget_key, &wi))) {
3069 config_update_num ("gui.mmk_editor_x", wi.x);
3070 config_update_num ("gui.mmk_editor_y", wi.y);
3071 WINDOW_INFO_ZERO(&wi);
3072 }
3073
3074 mmkeditor.mmk = NULL;
3075
3076 xitk_unregister_event_handler(&mmkeditor.widget_key);
3077
3078 xitk_destroy_widgets(mmkeditor.widget_list);
3079 xitk_window_destroy_window(gui->imlib_data, mmkeditor.xwin);
3080
3081 mmkeditor.xwin = NULL;
3082 /* xitk_dlist_init (&mmkeditor.widget_list.list); */
3083
3084 gui->x_lock_display (gui->display);
3085 XFreeGC(gui->display, (XITK_WIDGET_LIST_GC(mmkeditor.widget_list)));
3086 gui->x_unlock_display (gui->display);
3087
3088 XITK_WIDGET_LIST_FREE(mmkeditor.widget_list);
3089
3090 playlist_get_input_focus();
3091 }
3092 }
3093
mmkeditor_handle_event(XEvent * event,void * data)3094 static void mmkeditor_handle_event(XEvent *event, void *data) {
3095 switch(event->type) {
3096
3097 case KeyPress:
3098 if(xitk_get_key_pressed(event) == XK_Escape)
3099 mmkeditor_exit(NULL, NULL);
3100 else
3101 gui_handle_event (event, gGui);
3102 }
3103 }
3104
mmk_editor_show_tips(int enabled,unsigned long timeout)3105 void mmk_editor_show_tips(int enabled, unsigned long timeout) {
3106
3107 if(mmkeditor.running) {
3108 if(enabled)
3109 xitk_set_widgets_tips_timeout(mmkeditor.widget_list, timeout);
3110 else
3111 xitk_disable_widgets_tips(mmkeditor.widget_list);
3112 }
3113 }
3114
mmk_editor_update_tips_timeout(unsigned long timeout)3115 void mmk_editor_update_tips_timeout(unsigned long timeout) {
3116 if(mmkeditor.running)
3117 xitk_set_widgets_tips_timeout(mmkeditor.widget_list, timeout);
3118 }
3119
mmk_editor_is_visible(void)3120 int mmk_editor_is_visible(void) {
3121 if(mmkeditor.running)
3122 return mmkeditor.visible;
3123
3124 return 0;
3125 }
3126
mmk_editor_is_running(void)3127 int mmk_editor_is_running(void) {
3128 return mmkeditor.running;
3129 }
3130
mmk_editor_raise_window(void)3131 void mmk_editor_raise_window(void) {
3132 if(mmkeditor.running)
3133 raise_window(xitk_window_get_window(mmkeditor.xwin), mmkeditor.visible, mmkeditor.running);
3134 }
3135
mmk_editor_toggle_visibility(void)3136 void mmk_editor_toggle_visibility(void) {
3137 if(mmkeditor.running)
3138 toggle_window(xitk_window_get_window(mmkeditor.xwin), mmkeditor.widget_list,
3139 &mmkeditor.visible, mmkeditor.running);
3140 }
3141
mmk_editor_end(void)3142 void mmk_editor_end(void) {
3143 mmkeditor_exit(NULL, NULL);
3144 }
3145
mmkeditor_set_mmk(mediamark_t ** mmk)3146 void mmkeditor_set_mmk(mediamark_t **mmk) {
3147
3148 if(mmkeditor.running) {
3149 mmkeditor.mmk = mmk;
3150
3151 xitk_inputtext_change_text(mmkeditor.mrl, (*mmk)->mrl);
3152 xitk_inputtext_change_text(mmkeditor.ident, (*mmk)->ident);
3153 xitk_inputtext_change_text(mmkeditor.sub, (*mmk)->sub);
3154 xitk_intbox_set_value(mmkeditor.start, (*mmk)->start);
3155 xitk_intbox_set_value(mmkeditor.end, (*mmk)->end);
3156 xitk_intbox_set_value(mmkeditor.av_offset, (*mmk)->av_offset);
3157 xitk_intbox_set_value(mmkeditor.spu_offset, (*mmk)->spu_offset);
3158 }
3159 }
3160
mmkeditor_apply(xitk_widget_t * w,void * data)3161 static void mmkeditor_apply(xitk_widget_t *w, void *data) {
3162 const char *sub;
3163 char *ident, *mrl;
3164 int start, end, av_offset, spu_offset;
3165
3166 if(mmkeditor.mmk) {
3167
3168 mrl = atoa(xitk_inputtext_get_text(mmkeditor.mrl));
3169 if (!mrl[0])
3170 mrl = strdup((*mmkeditor.mmk)->mrl);
3171 else
3172 mrl = strdup(mrl);
3173
3174 ident = atoa(xitk_inputtext_get_text(mmkeditor.ident));
3175 if (!ident[0])
3176 ident = strdup(mrl);
3177 else
3178 ident = strdup(ident);
3179
3180 sub = xitk_inputtext_get_text(mmkeditor.sub);
3181 if(sub && (!strlen(sub)))
3182 sub = NULL;
3183
3184 if((start = xitk_intbox_get_value(mmkeditor.start)) < 0)
3185 start = 0;
3186
3187 if((end = xitk_intbox_get_value(mmkeditor.end)) <= -1)
3188 end = -1;
3189 else if (end < start)
3190 end = start + 1;
3191
3192 av_offset = xitk_intbox_get_value(mmkeditor.av_offset);
3193 spu_offset = xitk_intbox_get_value(mmkeditor.spu_offset);
3194
3195 mediamark_replace_entry(mmkeditor.mmk, mrl, ident, sub, start, end, av_offset, spu_offset);
3196 if(mmkeditor.callback)
3197 mmkeditor.callback(mmkeditor.user_data);
3198
3199 free(mrl);
3200 free(ident);
3201 }
3202 }
3203
mmkeditor_ok(xitk_widget_t * w,void * data)3204 static void mmkeditor_ok(xitk_widget_t *w, void *data) {
3205 mmkeditor_apply(NULL, NULL);
3206 mmkeditor_exit(NULL, NULL);
3207 }
3208
mmk_fileselector_callback(filebrowser_t * fb)3209 static void mmk_fileselector_callback(filebrowser_t *fb) {
3210 gGui_t *gui = gGui;
3211 char *file, *dir;
3212
3213 if ((dir = filebrowser_get_current_dir(fb)) != NULL) {
3214 strlcpy(gui->curdir, dir, sizeof(gui->curdir));
3215 free(dir);
3216 }
3217
3218 if((file = filebrowser_get_full_filename(fb)) != NULL) {
3219 if(file)
3220 xitk_inputtext_change_text(mmkeditor.sub, file);
3221 free(file);
3222 }
3223
3224 }
mmkeditor_select_sub(xitk_widget_t * w,void * data)3225 static void mmkeditor_select_sub(xitk_widget_t *w, void *data) {
3226 gGui_t *gui = gGui;
3227 filebrowser_callback_button_t cbb;
3228 char *path, *open_path;
3229
3230 cbb.label = _("Select");
3231 cbb.callback = mmk_fileselector_callback;
3232 cbb.need_a_file = 1;
3233
3234 path = (*mmkeditor.mmk)->sub ? (*mmkeditor.mmk)->sub : (*mmkeditor.mmk)->mrl;
3235
3236 if(mrl_look_like_file(path)) {
3237 char *p;
3238
3239 open_path = strdup(path);
3240
3241 if(!strncasecmp(path, "file:", 5))
3242 path += 5;
3243
3244 p = strrchr(open_path, '/');
3245 if (p && strlen(p))
3246 *p = '\0';
3247 }
3248 else
3249 open_path = strdup(gui->curdir);
3250
3251 create_filebrowser(_("Pick a subtitle file"), open_path, hidden_file_cb, &cbb, NULL, NULL);
3252
3253 free(open_path);
3254 }
3255
mmk_edit_mediamark(mediamark_t ** mmk,apply_callback_t callback,void * data)3256 void mmk_edit_mediamark(mediamark_t **mmk, apply_callback_t callback, void *data) {
3257 gGui_t *gui = gGui;
3258 GC gc;
3259 xitk_labelbutton_widget_t lb;
3260 xitk_label_widget_t lbl;
3261 xitk_checkbox_widget_t cb;
3262 xitk_inputtext_widget_t inp;
3263 xitk_intbox_widget_t ib;
3264 xitk_pixmap_t *bg;
3265 xitk_widget_t *b;
3266 int x, y, w, width, height;
3267
3268 if(mmkeditor.running) {
3269 if(!mmkeditor.visible)
3270 mmkeditor.visible = !mmkeditor.visible;
3271 mmk_editor_raise_window();
3272 mmkeditor_set_mmk(mmk);
3273 return;
3274 }
3275
3276 mmkeditor.callback = callback;
3277 mmkeditor.user_data = data;
3278
3279 x = xine_config_register_num(__xineui_global_xine_instance, "gui.mmk_editor_x",
3280 80,
3281 CONFIG_NO_DESC,
3282 CONFIG_NO_HELP,
3283 CONFIG_LEVEL_DEB,
3284 CONFIG_NO_CB,
3285 CONFIG_NO_DATA);
3286 y = xine_config_register_num(__xineui_global_xine_instance, "gui.mmk_editor_y",
3287 80,
3288 CONFIG_NO_DESC,
3289 CONFIG_NO_HELP,
3290 CONFIG_LEVEL_DEB,
3291 CONFIG_NO_CB,
3292 CONFIG_NO_DATA);
3293
3294 /* Create window */
3295 mmkeditor.xwin = xitk_window_create_dialog_window(gui->imlib_data, _("Mediamark Editor"), x, y,
3296 WINDOW_WIDTH, WINDOW_HEIGHT);
3297
3298 set_window_states_start((xitk_window_get_window(mmkeditor.xwin)));
3299
3300 gui->x_lock_display (gui->display);
3301 gc = XCreateGC(gui->display,
3302 (xitk_window_get_window(mmkeditor.xwin)), None, None);
3303 gui->x_unlock_display (gui->display);
3304
3305 mmkeditor.widget_list = xitk_widget_list_new();
3306 xitk_widget_list_set(mmkeditor.widget_list,
3307 WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(mmkeditor.xwin)));
3308 xitk_widget_list_set(mmkeditor.widget_list, WIDGET_LIST_GC, gc);
3309
3310 XITK_WIDGET_INIT(&lb, gui->imlib_data);
3311 XITK_WIDGET_INIT(&lbl, gui->imlib_data);
3312 XITK_WIDGET_INIT(&cb, gui->imlib_data);
3313 XITK_WIDGET_INIT(&inp, gui->imlib_data);
3314 XITK_WIDGET_INIT(&ib, gui->imlib_data);
3315
3316 xitk_window_get_window_size(mmkeditor.xwin, &width, &height);
3317 bg = xitk_image_create_xitk_pixmap(gui->imlib_data, width, height);
3318 gui->x_lock_display (gui->display);
3319 XCopyArea(gui->display, (xitk_window_get_background(mmkeditor.xwin)), bg->pixmap,
3320 bg->gc, 0, 0, width, height, 0, 0);
3321 gui->x_unlock_display (gui->display);
3322
3323 x = 15;
3324 y = 34 - 6;
3325 w = WINDOW_WIDTH - 30;
3326 draw_outter_frame(gui->imlib_data, bg, _("Identifier"), btnfontname,
3327 x, y, w, 45);
3328
3329 inp.skin_element_name = NULL;
3330 inp.text = NULL;
3331 inp.max_length = 2048;
3332 inp.callback = NULL;
3333 inp.userdata = NULL;
3334 mmkeditor.ident = xitk_noskin_inputtext_create (mmkeditor.widget_list, &inp,
3335 x + 10, y + 16, w - 20, 20, "Black", "Black", fontname);
3336 xitk_add_widget (mmkeditor.widget_list, mmkeditor.ident);
3337 xitk_set_widget_tips_default(mmkeditor.ident, _("Mediamark Identifier"));
3338 xitk_enable_and_show_widget(mmkeditor.ident);
3339
3340 y += 45 + 3;
3341 draw_outter_frame(gui->imlib_data, bg, _("Mrl"), btnfontname,
3342 x, y, w, 45);
3343
3344 inp.skin_element_name = NULL;
3345 inp.text = NULL;
3346 inp.max_length = 2048;
3347 inp.callback = NULL;
3348 inp.userdata = NULL;
3349 mmkeditor.mrl = xitk_noskin_inputtext_create (mmkeditor.widget_list, &inp,
3350 x + 10, y + 16, w - 20, 20, "Black", "Black", fontname);
3351 xitk_add_widget (mmkeditor.widget_list, mmkeditor.mrl);
3352 xitk_set_widget_tips_default(mmkeditor.mrl, _("Mediamark Mrl"));
3353 xitk_enable_and_show_widget(mmkeditor.mrl);
3354
3355 y += 45 + 3;
3356 draw_outter_frame(gui->imlib_data, bg, _("Subtitle"), btnfontname,
3357 x, y, w, 45);
3358
3359 inp.skin_element_name = NULL;
3360 inp.text = NULL;
3361 inp.max_length = 2048;
3362 inp.callback = NULL;
3363 inp.userdata = NULL;
3364 mmkeditor.sub = xitk_noskin_inputtext_create (mmkeditor.widget_list, &inp,
3365 x + 10, y + 16, w - 20 - 100 - 10, 20, "Black", "Black", fontname);
3366 xitk_add_widget (mmkeditor.widget_list, mmkeditor.sub);
3367 xitk_set_widget_tips_default(mmkeditor.sub, _("Subtitle File"));
3368 xitk_enable_and_show_widget(mmkeditor.sub);
3369
3370 lb.button_type = CLICK_BUTTON;
3371 lb.label = _("Sub File");
3372 lb.align = ALIGN_CENTER;
3373 lb.callback = mmkeditor_select_sub;
3374 lb.state_callback = NULL;
3375 lb.userdata = NULL;
3376 lb.skin_element_name = NULL;
3377 b = xitk_noskin_labelbutton_create (mmkeditor.widget_list, &lb,
3378 x + 10 + w - 20 - 100, y + 16, 100, 20, "Black", "Black", "White", btnfontname);
3379 xitk_add_widget (mmkeditor.widget_list, b);
3380 xitk_set_widget_tips_default(b, _("Select a subtitle file to use together with the mrl."));
3381 xitk_enable_and_show_widget(b);
3382
3383 y += 45 + 3;
3384 w = 120;
3385 draw_outter_frame(gui->imlib_data, bg, _("Start at"), btnfontname,
3386 x, y, w, 45);
3387
3388 ib.skin_element_name = NULL;
3389 ib.value = 0;
3390 ib.step = 1;
3391 ib.parent_wlist = mmkeditor.widget_list;
3392 ib.callback = NULL;
3393 ib.userdata = NULL;
3394 mmkeditor.start = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3395 x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3396 xitk_add_widget (mmkeditor.widget_list, mmkeditor.start);
3397 xitk_set_widget_tips_default(mmkeditor.start, _("Mediamark start time (secs)."));
3398 xitk_enable_and_show_widget(mmkeditor.start);
3399
3400 x += w + 5;
3401 draw_outter_frame(gui->imlib_data, bg, _("End at"), btnfontname,
3402 x, y, w, 45);
3403
3404 ib.skin_element_name = NULL;
3405 ib.value = -1;
3406 ib.step = 1;
3407 ib.parent_wlist = mmkeditor.widget_list;
3408 ib.callback = NULL;
3409 ib.userdata = NULL;
3410 mmkeditor.end = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3411 x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3412 xitk_add_widget (mmkeditor.widget_list, mmkeditor.end);
3413 xitk_set_widget_tips_default(mmkeditor.end, _("Mediamark end time (secs)."));
3414 xitk_enable_and_show_widget(mmkeditor.end);
3415
3416 x += w + 5;
3417 draw_outter_frame(gui->imlib_data, bg, _("A/V offset"), btnfontname,
3418 x, y, w, 45);
3419
3420 ib.skin_element_name = NULL;
3421 ib.value = 0;
3422 ib.step = 1;
3423 ib.parent_wlist = mmkeditor.widget_list;
3424 ib.callback = NULL;
3425 ib.userdata = NULL;
3426 mmkeditor.av_offset = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3427 x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3428 xitk_add_widget (mmkeditor.widget_list, mmkeditor.av_offset);
3429 xitk_set_widget_tips_default(mmkeditor.av_offset, _("Offset of Audio and Video."));
3430 xitk_enable_and_show_widget(mmkeditor.av_offset);
3431
3432 x += w + 5;
3433 draw_outter_frame(gui->imlib_data, bg, _("SPU offset"), btnfontname,
3434 x, y, w, 45);
3435
3436 ib.skin_element_name = NULL;
3437 ib.value = 0;
3438 ib.step = 1;
3439 ib.parent_wlist = mmkeditor.widget_list;
3440 ib.callback = NULL;
3441 ib.userdata = NULL;
3442 mmkeditor.spu_offset = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3443 x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3444 xitk_add_widget (mmkeditor.widget_list, mmkeditor.spu_offset);
3445 xitk_set_widget_tips_default(mmkeditor.spu_offset, _("Subpicture offset."));
3446 xitk_enable_and_show_widget(mmkeditor.spu_offset);
3447
3448 y = WINDOW_HEIGHT - (23 + 15);
3449 x = 15;
3450 lb.button_type = CLICK_BUTTON;
3451 lb.label = _("OK");
3452 lb.align = ALIGN_CENTER;
3453 lb.callback = mmkeditor_ok;
3454 lb.state_callback = NULL;
3455 lb.userdata = NULL;
3456 lb.skin_element_name = NULL;
3457 b = xitk_noskin_labelbutton_create (mmkeditor.widget_list,
3458 &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
3459 xitk_add_widget (mmkeditor.widget_list, b);
3460 xitk_set_widget_tips_default(b, _("Apply the changes and close the window."));
3461 xitk_enable_and_show_widget(b);
3462
3463 x = (WINDOW_WIDTH - 100) / 2;
3464 lb.button_type = CLICK_BUTTON;
3465 lb.label = _("Apply");
3466 lb.align = ALIGN_CENTER;
3467 lb.callback = mmkeditor_apply;
3468 lb.state_callback = NULL;
3469 lb.userdata = NULL;
3470 lb.skin_element_name = NULL;
3471 b = xitk_noskin_labelbutton_create (mmkeditor.widget_list,
3472 &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
3473 xitk_add_widget (mmkeditor.widget_list, b);
3474 xitk_set_widget_tips_default(b, _("Apply the changes to the playlist."));
3475 xitk_enable_and_show_widget(b);
3476
3477 x = WINDOW_WIDTH - (100 + 15);
3478 lb.button_type = CLICK_BUTTON;
3479 lb.label = _("Close");
3480 lb.align = ALIGN_CENTER;
3481 lb.callback = mmkeditor_exit;
3482 lb.state_callback = NULL;
3483 lb.userdata = NULL;
3484 lb.skin_element_name = NULL;
3485 b = xitk_noskin_labelbutton_create (mmkeditor.widget_list,
3486 &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
3487 xitk_add_widget (mmkeditor.widget_list, b);
3488 xitk_set_widget_tips_default(b, _("Discard changes and dismiss the window."));
3489 xitk_enable_and_show_widget(b);
3490 mmk_editor_show_tips (panel_get_tips_enable (gui->panel), panel_get_tips_timeout (gui->panel));
3491
3492 xitk_window_change_background(gui->imlib_data, mmkeditor.xwin, bg->pixmap, width, height);
3493 xitk_image_destroy_xitk_pixmap(bg);
3494
3495 mmkeditor.widget_key = xitk_register_event_handler("mmkeditor",
3496 (xitk_window_get_window(mmkeditor.xwin)),
3497 mmkeditor_handle_event,
3498 NULL,
3499 NULL,
3500 mmkeditor.widget_list,
3501 NULL);
3502
3503 mmkeditor.visible = 1;
3504 mmkeditor.running = 1;
3505
3506 mmkeditor_set_mmk(mmk);
3507 mmk_editor_raise_window();
3508
3509 try_to_set_input_focus(xitk_window_get_window(mmkeditor.xwin));
3510 }
3511
3512