⛏️ index : haiku.git

/*
 * PlaylistFileReader.cpp - Media Player for the Haiku Operating System
 *
 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
 * Copyright (C) 2007-2009 Stephan Aßmus <superstippi@gmx.de> (MIT ok)
 * Copyright (C) 2008-2009 Fredrik ModΓ©en <[FirstName]@[LastName].se> (MIT ok)
 *
 * Released under the terms of the MIT license.
 */

#include "PlaylistFileReader.h"

#include <cctype>
#include <new>
#include <stdio.h>
#include <strings.h>

#include <Path.h>
#include <Url.h>

#include "FileReadWrite.h"
#include "Playlist.h"

using std::isdigit;
using std::nothrow;


/*static*/ PlaylistFileReader*
PlaylistFileReader::GenerateReader(const entry_ref& ref)
{
	PlaylistFileType type = _IdentifyType(ref);
	PlaylistFileReader* reader = NULL;
	if (type == m3u)
		reader = new (nothrow) M3uReader;
	else if (type == pls)
		reader = new (nothrow) PlsReader;
	return reader;
}


int32
PlaylistFileReader::_AppendItemToPlaylist(const BString& entry, Playlist* playlist)
{
	bool validItem = true;
	int32 assignedIndex = -1;
	BPath path(entry.String());
	entry_ref refPath;
	status_t err = get_ref_for_path(path.Path(), &refPath);
	PlaylistItem* item = NULL;

	// Create a PlaylistItem if entry is a valid file path or URL
	if (err == B_OK)
		item = new (nothrow) FilePlaylistItem(refPath);
	else {
		BUrl url(entry, true);
		if (url.IsValid())
			item = new (nothrow) UrlPlaylistItem(url);
		else {
			printf("Error - %s: [%" B_PRIx32 "]\n", strerror(err), err);
			validItem = false;
		}
	}

	// If creation of a PlaylistItem was attempted, try to add it to the Playlist
	if (validItem) {
		if (item == NULL)
			delete item;
		else {
			bool itemAdded = playlist->AddItem(item);
			if (!itemAdded)
				delete item;
			else
				assignedIndex = playlist->IndexOf(item);
		}
	}

	return assignedIndex;
}


/*static*/ PlaylistFileType
PlaylistFileReader::_IdentifyType(const entry_ref& ref)
{
	BString path(BPath(&ref).Path());
	PlaylistFileType type = unknown;
	if (path.FindLast(".m3u") == path.CountChars() - 4
		|| path.FindLast(".m3u8") == path.CountChars() - 5)
		type = m3u;
	else if (path.FindLast(".pls") == path.CountChars() - 4)
		type = pls;
	return type;
}


/*virtual*/ void
M3uReader::AppendToPlaylist(const entry_ref& ref, Playlist* playlist)
{
	BFile file(&ref, B_READ_ONLY);
	FileReadWrite lineReader(&file);

	BString line;
	while (lineReader.Next(line)) {
		if (line.FindFirst("#") != 0)
			// Current version of this function ignores all comment lines
			_AppendItemToPlaylist(line, playlist);
		line.Truncate(0);
	}
}


/*virtual*/ void
PlsReader::AppendToPlaylist(const entry_ref& ref, Playlist* playlist)
{
	BString plsEntries;
		// The total number of tracks in the PLS file, taken from the NumberOfEntries line.
		// This variable is not used for anything at this time.
	BString plsVersion;
		// The version of the PLS standard used in this playlist file, taken from the Version line.
		// This variable is not used for anything at this time.
	int32 lastAssignedIndex = -1;
		// The index that MediaPlayer assigned to the most recently added PlaylistItem.
		// If an attempted assignment fails, this will be set to -1 again.

	BFile file(&ref, B_READ_ONLY);
	FileReadWrite lineReader(&file);
	BString line;

	// Check for the "[playlist]" header on the first line
	lineReader.Next(line);
	if (line != "[playlist]") {
		printf("Error - Invalid .pls file\n");
		return;
	}
	line.Truncate(0);

	while (true) {
		bool lineRead = lineReader.Next(line);
		if (lineRead == false)
			break;

		// All valid PLS lines after the header contain an equal sign
		int32 equalIndex = line.FindFirst("=");
		if (equalIndex == B_ERROR) {
			line.Truncate(0);
			continue;
		}

		BString lineType;
			// Will be set for each line to one of: File, Title, Length, NumberOfEntries, Version
		BString trackNumber;
			// Number of the track being processed, using the (one-based) explicit numbering
			// from the .pls file
		if (isdigit(line[equalIndex - 1])) {
			// Distinguish between lines that specify a track number, and those that don't
			int32 trackIndex = equalIndex - 1;
				// The string index where the track number begins
			while (isdigit(line[trackIndex - 1]))
				trackIndex--;
			line.CopyInto(lineType, 0, trackIndex);
			line.CopyInto(trackNumber, trackIndex, equalIndex - trackIndex);
		} else {
			line.CopyInto(lineType, 0, equalIndex);
		}

		BString lineContent;
			// Everything after the equal sign
		line.CopyInto(lineContent, equalIndex + 1, line.Length() - (equalIndex + 1));

		if (lineType == "File") {
			lastAssignedIndex = _AppendItemToPlaylist(lineContent, playlist);
		// A File line may be followed by optional Title and/or Length lines.
		} else if (lineType == "Title") {
			_ParseTitleLine(lineContent, playlist, lastAssignedIndex);
		} else if (lineType == "Length") {
			_ParseLengthLine(lineContent, playlist, lastAssignedIndex);
		// The file should include one NumberOfEntries line and one Version line.
		} else if (lineType == "NumberOfEntries") {
			plsEntries = lineContent;
		} else if (lineType == "Version") {
			plsVersion = lineContent;
		} else {
			// Ignore the line
		}

	line.Truncate(0);
	}
}


status_t
PlsReader::_ParseTitleLine(const BString& title, Playlist* playlist,
	const int32 lastAssignedIndex)
{
	status_t err;
	if (lastAssignedIndex != -1) {
		// Only use this Title if most recent File was successfully added to the playlist.
		err = playlist->ItemAt(lastAssignedIndex)->SetAttribute(
			PlaylistItem::ATTR_STRING_TITLE, title);
	} else
		err = B_CANCELED;

	if (err != B_OK)
		printf("Error - %s: [%" B_PRIx32 "]\n", strerror(err), err);

	return err;
}


status_t
PlsReader::_ParseLengthLine(const BString& length, Playlist* playlist,
	const int32 lastAssignedIndex)
{
	status_t err;
	if (lastAssignedIndex != -1)
	{
		int64 lengthInt = strtoll(length.String(), NULL, 10);
			// Track length in seconds, or -1 for an infinite streaming track.

		err = playlist->ItemAt(lastAssignedIndex)->SetAttribute(
			PlaylistItem::ATTR_INT64_DURATION, lengthInt);
			// This does nothing if the track in question is streaming, because
			// UrlPlaylistItem::SetAttribute(const Attribute&, const int32&) is not implemented.
	} else
		err = B_CANCELED;

	if (err != B_OK)
		printf("Error - %s: [%" B_PRIx32 "]\n", strerror(err), err);

	return err;
}