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