Updated package version
[lms] / lightmediascanner / src / plugins / m3u / m3u.c
1 /**
2  * Copyright (C) 2007 by INdT
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
19  */
20
21 /**
22  * @brief
23  *
24  * m3u playlist parser.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #define _XOPEN_SOURCE 600
32 #include <lightmediascanner_plugin.h>
33 #include <lightmediascanner_db.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42
43 static int
44 _m3u_get_n_entries(int fd, struct lms_playlist_info *info)
45 {
46     char buf[1024];
47     enum {
48         IS_EMPTY,
49         IS_ENTRY,
50         IS_COMMENT
51     } state;
52
53     state = IS_EMPTY;
54     do {
55         ssize_t r;
56         int i;
57
58         r = read(fd, buf, sizeof(buf));
59         if (r < 0) {
60             perror("read");
61             return -1;
62         } else if (r == 0)
63             goto done;
64
65         for (i = 0; i < r; i++) {
66             char c;
67
68             c = buf[i];
69             if (c == '\n') {
70                 if (state == IS_ENTRY)
71                     info->n_entries++;
72                 state = IS_EMPTY;
73             } else if (state == IS_EMPTY) {
74                 if (c == '#')
75                     state = IS_COMMENT;
76                 else if (!isspace(c))
77                     state = IS_ENTRY;
78             }
79         }
80     } while (1);
81
82   done:
83     if (state == IS_ENTRY)
84         info->n_entries++;
85     return 0;
86 }
87
88 static const char _name[] = "m3u";
89 static const struct lms_string_size _exts[] = {
90     LMS_STATIC_STRING_SIZE(".m3u")
91 };
92
93 struct plugin {
94     struct lms_plugin plugin;
95     lms_db_playlist_t *playlist_db;
96 };
97
98 static void *
99 _match(struct plugin *p, const char *path, int len, int base)
100 {
101     int i;
102
103     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
104     if (i < 0)
105       return NULL;
106     else
107       return (void*)(i + 1);
108 }
109
110 static int
111 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
112 {
113     struct lms_playlist_info info = {0};
114     int fd, r, ext_idx;
115
116     fd = open(finfo->path, O_RDONLY);
117     if (fd < 0) {
118         perror("open");
119         return -1;
120     }
121
122     if (_m3u_get_n_entries(fd, &info) != 0)
123         fprintf(stderr,
124                 "WARNING: could not get number of entries in playlist '%s'.\n",
125                 finfo->path);
126
127     ext_idx = ((int)match) - 1;
128     info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
129     info.title.str = malloc((info.title.len + 1) * sizeof(char));
130     memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
131     info.title.str[info.title.len] = '\0';
132     lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
133
134     info.id = finfo->id;
135     r = lms_db_playlist_add(plugin->playlist_db, &info);
136
137     if (info.title.str)
138         free(info.title.str);
139     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
140     close(fd);
141
142     return r;
143 }
144
145 static int
146 _setup(struct plugin *plugin, struct lms_context *ctxt)
147 {
148     plugin->playlist_db = lms_db_playlist_new(ctxt->db);
149     if (!plugin->playlist_db)
150         return -1;
151
152     return 0;
153 }
154
155 static int
156 _start(struct plugin *plugin, struct lms_context *ctxt)
157 {
158     return lms_db_playlist_start(plugin->playlist_db);
159 }
160
161 static int
162 _finish(struct plugin *plugin, struct lms_context *ctxt)
163 {
164     if (plugin->playlist_db)
165         return lms_db_playlist_free(plugin->playlist_db);
166
167     return 0;
168 }
169
170
171 static int
172 _close(struct plugin *plugin)
173 {
174     free(plugin);
175     return 0;
176 }
177
178 API struct lms_plugin *
179 lms_plugin_open(void)
180 {
181     struct plugin *plugin;
182
183     plugin = malloc(sizeof(*plugin));
184     plugin->plugin.name = _name;
185     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
186     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
187     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
188     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
189     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
190     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
191
192     return (struct lms_plugin *)plugin;
193 }