xref: /haiku/src/apps/mediaplayer/playlist/PlaylistFileReader.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2  * PlaylistFileReader.cpp - Media Player for the Haiku Operating System
3  *
4  * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5  * Copyright (C) 2007-2009 Stephan Aßmus <superstippi@gmx.de> (MIT ok)
6  * Copyright (C) 2008-2009 Fredrik Modéen <[FirstName]@[LastName].se> (MIT ok)
7  *
8  * Released under the terms of the MIT license.
9  */
10 
11 #include "PlaylistFileReader.h"
12 
13 #include <cctype>
14 #include <new>
15 #include <stdio.h>
16 #include <strings.h>
17 
18 #include <Path.h>
19 #include <Url.h>
20 
21 #include "FileReadWrite.h"
22 #include "Playlist.h"
23 
24 using std::isdigit;
25 using std::nothrow;
26 
27 
28 /*static*/ PlaylistFileReader*
29 PlaylistFileReader::GenerateReader(const entry_ref& ref)
30 {
31 	PlaylistFileType type = _IdentifyType(ref);
32 	PlaylistFileReader* reader = NULL;
33 	if (type == m3u)
34 		reader = new (nothrow) M3uReader;
35 	else if (type == pls)
36 		reader = new (nothrow) PlsReader;
37 	return reader;
38 }
39 
40 
41 int32
42 PlaylistFileReader::_AppendItemToPlaylist(const BString& entry, Playlist* playlist)
43 {
44 	bool validItem = true;
45 	int32 assignedIndex = -1;
46 	BPath path(entry.String());
47 	entry_ref refPath;
48 	status_t err = get_ref_for_path(path.Path(), &refPath);
49 	PlaylistItem* item = NULL;
50 
51 	// Create a PlaylistItem if entry is a valid file path or URL
52 	if (err == B_OK)
53 		item = new (nothrow) FilePlaylistItem(refPath);
54 	else {
55 		BUrl url(entry);
56 		if (url.IsValid())
57 			item = new (nothrow) UrlPlaylistItem(url);
58 		else {
59 			printf("Error - %s: [%" B_PRIx32 "]\n", strerror(err), err);
60 			validItem = false;
61 		}
62 	}
63 
64 	// If creation of a PlaylistItem was attempted, try to add it to the Playlist
65 	if (validItem) {
66 		if (item == NULL)
67 			delete item;
68 		else {
69 			bool itemAdded = playlist->AddItem(item);
70 			if (!itemAdded)
71 				delete item;
72 			else
73 				assignedIndex = playlist->IndexOf(item);
74 		}
75 	}
76 
77 	return assignedIndex;
78 }
79 
80 
81 /*static*/ PlaylistFileType
82 PlaylistFileReader::_IdentifyType(const entry_ref& ref)
83 {
84 	BString path(BPath(&ref).Path());
85 	PlaylistFileType type = unknown;
86 	if (path.FindLast(".m3u") == path.CountChars() - 4
87 		|| path.FindLast(".m3u8") == path.CountChars() - 5)
88 		type = m3u;
89 	else if (path.FindLast(".pls") == path.CountChars() - 4)
90 		type = pls;
91 	return type;
92 }
93 
94 
95 /*virtual*/ void
96 M3uReader::AppendToPlaylist(const entry_ref& ref, Playlist* playlist)
97 {
98 	BFile file(&ref, B_READ_ONLY);
99 	FileReadWrite lineReader(&file);
100 
101 	BString line;
102 	while (lineReader.Next(line)) {
103 		if (line.FindFirst("#") != 0)
104 			// Current version of this function ignores all comment lines
105 			_AppendItemToPlaylist(line, playlist);
106 		line.Truncate(0);
107 	}
108 }
109 
110 
111 /*virtual*/ void
112 PlsReader::AppendToPlaylist(const entry_ref& ref, Playlist* playlist)
113 {
114 	BString plsEntries;
115 		// The total number of tracks in the PLS file, taken from the NumberOfEntries line.
116 		// This variable is not used for anything at this time.
117 	BString plsVersion;
118 		// The version of the PLS standard used in this playlist file, taken from the Version line.
119 		// This variable is not used for anything at this time.
120 	int32 lastAssignedIndex = -1;
121 		// The index that MediaPlayer assigned to the most recently added PlaylistItem.
122 		// If an attempted assignment fails, this will be set to -1 again.
123 
124 	BFile file(&ref, B_READ_ONLY);
125 	FileReadWrite lineReader(&file);
126 	BString line;
127 
128 	// Check for the "[playlist]" header on the first line
129 	lineReader.Next(line);
130 	if (line != "[playlist]") {
131 		printf("Error - Invalid .pls file\n");
132 		return;
133 	}
134 	line.Truncate(0);
135 
136 	while (true) {
137 		bool lineRead = lineReader.Next(line);
138 		if (lineRead == false)
139 			break;
140 
141 		// All valid PLS lines after the header contain an equal sign
142 		int32 equalIndex = line.FindFirst("=");
143 		if (equalIndex == B_ERROR) {
144 			line.Truncate(0);
145 			continue;
146 		}
147 
148 		BString lineType;
149 			// Will be set for each line to one of: File, Title, Length, NumberOfEntries, Version
150 		BString trackNumber;
151 			// Number of the track being processed, using the (one-based) explicit numbering
152 			// from the .pls file
153 		if (isdigit(line[equalIndex - 1])) {
154 			// Distinguish between lines that specify a track number, and those that don't
155 			int32 trackIndex = equalIndex - 1;
156 				// The string index where the track number begins
157 			while (isdigit(line[trackIndex - 1]))
158 				trackIndex--;
159 			line.CopyInto(lineType, 0, trackIndex);
160 			line.CopyInto(trackNumber, trackIndex, equalIndex - trackIndex);
161 		} else {
162 			line.CopyInto(lineType, 0, equalIndex);
163 		}
164 
165 		BString lineContent;
166 			// Everything after the equal sign
167 		line.CopyInto(lineContent, equalIndex + 1, line.Length() - (equalIndex + 1));
168 
169 		if (lineType == "File") {
170 			lastAssignedIndex = _AppendItemToPlaylist(lineContent, playlist);
171 		// A File line may be followed by optional Title and/or Length lines.
172 		} else if (lineType == "Title") {
173 			_ParseTitleLine(lineContent, playlist, lastAssignedIndex);
174 		} else if (lineType == "Length") {
175 			_ParseLengthLine(lineContent, playlist, lastAssignedIndex);
176 		// The file should include one NumberOfEntries line and one Version line.
177 		} else if (lineType == "NumberOfEntries") {
178 			plsEntries = lineContent;
179 		} else if (lineType == "Version") {
180 			plsVersion = lineContent;
181 		} else {
182 			// Ignore the line
183 		}
184 
185 	line.Truncate(0);
186 	}
187 }
188 
189 
190 status_t
191 PlsReader::_ParseTitleLine(const BString& title, Playlist* playlist,
192 	const int32 lastAssignedIndex)
193 {
194 	status_t err;
195 	if (lastAssignedIndex != -1) {
196 		// Only use this Title if most recent File was successfully added to the playlist.
197 		err = playlist->ItemAt(lastAssignedIndex)->SetAttribute(
198 			PlaylistItem::ATTR_STRING_TITLE, title);
199 	} else
200 		err = B_CANCELED;
201 
202 	if (err != B_OK)
203 		printf("Error - %s: [%" B_PRIx32 "]\n", strerror(err), err);
204 
205 	return err;
206 }
207 
208 
209 status_t
210 PlsReader::_ParseLengthLine(const BString& length, Playlist* playlist,
211 	const int32 lastAssignedIndex)
212 {
213 	status_t err;
214 	if (lastAssignedIndex != -1)
215 	{
216 		int64 lengthInt = strtoll(length.String(), NULL, 10);
217 			// Track length in seconds, or -1 for an infinite streaming track.
218 
219 		err = playlist->ItemAt(lastAssignedIndex)->SetAttribute(
220 			PlaylistItem::ATTR_INT64_DURATION, lengthInt);
221 			// This does nothing if the track in question is streaming, because
222 			// UrlPlaylistItem::SetAttribute(const Attribute&, const int32&) is not implemented.
223 	} else
224 		err = B_CANCELED;
225 
226 	if (err != B_OK)
227 		printf("Error - %s: [%" B_PRIx32 "]\n", strerror(err), err);
228 
229 	return err;
230 }
231