1//=========================================================================
2// FILENAME	: playlist.c
3// DESCRIPTION	: Playlist
4//=========================================================================
5// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6//=========================================================================
7
8/*
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <stdio.h>
24#include <string.h>
25#include <ctype.h>
26
27#include "misc.h"
28#include "tagutils.h"
29#include "textutils.h"
30#include "log.h"
31
32
33#define MAX_BUF 4096
34
35static FILE *fp = 0;
36static int _utf8bom = 0;
37static int _trackno;
38
39static int (*_next_track)(struct song_metadata*, struct stat*, char*, char*);
40static int _m3u_pls_next_track(struct song_metadata*, struct stat*, char*, char*);
41
42int
43start_plist(const char *path, struct song_metadata *psong, struct stat *stat, char *lang, char *type)
44{
45	char *fname, *suffix;
46
47	_next_track = 0;
48	_utf8bom = 0;
49	_trackno = 0;
50
51	if(strcasecmp(type, "m3u") == 0)
52		_next_track = _m3u_pls_next_track;
53	else if(strcasecmp(type, "pls") == 0)
54		_next_track = _m3u_pls_next_track;
55
56	if(!_next_track)
57	{
58		DPRINTF(E_ERROR, L_SCANNER, "Unsupported playlist type <%s> (%s)\n", type, path);
59		return -1;
60	}
61
62	if(!(fp = fopen(path, "rb")))
63	{
64		DPRINTF(E_ERROR, L_SCANNER, "Cannot open %s\n", path);
65		return -1;
66	}
67
68	if(!psong)
69		return 0;
70
71	memset((void*)psong, 0, sizeof(struct song_metadata));
72	psong->is_plist = 1;
73	psong->path = strdup(path);
74	psong->type = type;
75
76	fname = strrchr(psong->path, '/');
77	psong->basename = fname ? fname + 1 : psong->path;
78
79	psong->title = strdup(psong->basename);
80	suffix = strrchr(psong->title, '.');
81	if(suffix) *suffix = '\0';
82
83	if(stat)
84	{
85		if(!psong->time_modified)
86			psong->time_modified = stat->st_mtime;
87		psong->file_size = stat->st_size;
88	}
89
90	return 0;
91}
92
93int
94_m3u_pls_next_track(struct song_metadata *psong, struct stat *stat, char *lang, char *type)
95{
96	char buf[MAX_BUF], *p;
97	int len;
98
99	memset((void*)psong, 0, sizeof(struct song_metadata));
100
101	// read first line
102	p = fgets(buf, MAX_BUF, fp);
103	if(!p)
104	{
105		fclose(fp);
106		return 1;
107	}
108
109	if(strcasecmp(type, "m3u") == 0)
110	{
111		// check BOM
112		if(!_utf8bom && p[0] == '\xef' && p[1] == '\xbb' && p[2] == '\xbf')
113		{
114			_utf8bom = 1;
115			p += 3;
116		}
117	}
118
119	while(p)
120	{
121		while(isspace(*p)) p++;
122
123		if(!(*p) || *p == '#')
124			goto next_line;
125
126		if(!isprint(*p))
127		{
128			DPRINTF(E_ERROR, L_SCANNER, "Playlist looks bad (unprintable characters)\n");
129			fclose(fp);
130			return 2;
131		}
132
133		if(strcasecmp(type, "pls") == 0)
134		{
135			// verify that it's a valid pls playlist
136			if(!_trackno)
137			{
138				if(strncmp(p, "[playlist]", 10))
139					break;
140				_trackno++;
141				goto next_line;
142			}
143
144			if(strncmp(p, "File", 4) != 0)
145				goto next_line;
146
147			psong->track = strtol(p+4, &p, 10);
148			if(!psong->track || !p++)
149				goto next_line;
150			_trackno = psong->track;
151		}
152		else if(strcasecmp(type, "m3u") == 0)
153			psong->track = ++_trackno;
154
155		len = strlen(p);
156		while(p[len-1] == '\r' || p[len-1] == '\n')
157			p[--len] = '\0';
158		psong->path = strdup(p);
159		return 0;
160next_line:
161		p = fgets(buf, MAX_BUF, fp);
162	}
163
164	fclose(fp);
165	return 1;
166}
167
168int
169next_plist_track(struct song_metadata *psong, struct stat *stat, char *lang, char *type)
170{
171	if(_next_track)
172		return _next_track(psong, stat, lang, type);
173	return -1;
174}
175