692 lines
17 KiB
Ucode
692 lines
17 KiB
Ucode
//==============================================================================
|
|
// Created on: 10/14/2003
|
|
// Manages custom playlists
|
|
//
|
|
// Written by Ron Prestenback
|
|
// © 2003, Epic Games, Inc. All Rights Reserved
|
|
//==============================================================================
|
|
class StreamPlaylistManager extends StreamBase
|
|
Config(UPlaylists);
|
|
|
|
var() editconst protected config int CurrentPlaylist; // Index of current playlist
|
|
var() editinline editconst noexport StreamInterface FileManager;
|
|
var() editinline editconstarray protected array<StreamPlaylist> Playlists;
|
|
var() protected bool bDisableNotification;
|
|
var() transient noexport protected bool bDirty;
|
|
|
|
var() protected config bool bShuffle, bShuffleAll, bRepeat, bRepeatAll;
|
|
var() localized string DefaultPlaylistName;
|
|
|
|
struct PlaylistParser
|
|
{
|
|
var() EStreamPlaylistType Type;
|
|
var() string ParserClass;
|
|
};
|
|
|
|
var() config array<PlaylistParser> ParserType;
|
|
var() editconst noexport editinline editconstarray array<PlaylistParserBase> Parsers;
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Initialization
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
delegate ChangedActivePlaylist( StreamPlaylist NewPlaylist );
|
|
|
|
function Initialize( StreamInterface InFileManager )
|
|
{
|
|
FileManager = InFileManager;
|
|
InitializePlaylists();
|
|
InitializeParsers();
|
|
}
|
|
|
|
protected function InitializePlaylists()
|
|
{
|
|
local array<string> PlaylistNames;
|
|
local StreamPlaylist NewList;
|
|
local int i;
|
|
|
|
PlaylistNames = GetPerObjectNames("UPlaylists", "StreamPlaylist");
|
|
for ( i = 0; i < PlaylistNames.Length; i++ )
|
|
{
|
|
if ( PlaylistNames[i] == "" )
|
|
continue;
|
|
|
|
NewList = CreatePlaylist(PlaylistNames[i]);
|
|
AppendPlaylist(NewList);
|
|
}
|
|
|
|
if ( Playlists.Length == 0 )
|
|
CreateDefaultPlaylist();
|
|
|
|
else if ( !ActivatePlaylist(CurrentPlaylist) )
|
|
ActivatePlaylist(0);
|
|
}
|
|
|
|
protected function InitializeParsers()
|
|
{
|
|
local int i, j;
|
|
local class<PlaylistParserBase> ParseClass;
|
|
|
|
for ( i = 0; i < ParserType.Length; i++ )
|
|
{
|
|
if ( ParserType[i].ParserClass != "" )
|
|
{
|
|
ParseClass = class<PlaylistParserBase>(DynamicLoadObject(ParserType[i].ParserClass, class'Class'));
|
|
if ( ParseClass != None )
|
|
{
|
|
j = Parsers.Length;
|
|
Parsers[j] = new(Self) ParseClass;
|
|
Parsers[j].SetType(ParserType[i].Type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function StreamPlaylist CreateDefaultPlaylist()
|
|
{
|
|
local int i;
|
|
local string Nothing;
|
|
|
|
Nothing = DefaultPlaylistName;
|
|
i = AddPlaylist(Nothing);
|
|
if ( ValidIndex(i) && ActivatePlaylist(i) )
|
|
return Playlists[CurrentPlaylist];
|
|
|
|
return None;
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Playlist Management
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function bool ActivatePlaylist( int ListIndex, optional bool bNoFail )
|
|
{
|
|
if ( !ValidIndex(ListIndex) )
|
|
{
|
|
if ( bNoFail )
|
|
return False;
|
|
|
|
// log("Wasn't a valid index:"$ListIndex,'MusicPlayer');
|
|
if ( GetRepeatAll() )
|
|
{
|
|
if ( ListIndex < 0 )
|
|
ListIndex = Playlists.Length - 1;
|
|
else if ( ListIndex >= Playlists.Length )
|
|
ListIndex = 0;
|
|
|
|
if ( !ValidIndex(ListIndex) )
|
|
return false;
|
|
}
|
|
|
|
else return false;
|
|
}
|
|
|
|
bDirty = bDirty || ListIndex != CurrentPlaylist;
|
|
Playlists[ListIndex].InitializePlaylist(FileManager);
|
|
CurrentPlaylist = ListIndex;
|
|
|
|
if ( !bDisableNotification )
|
|
ChangedActivePlaylist(Playlists[ListIndex]);
|
|
|
|
return true;
|
|
}
|
|
|
|
function int AddPlaylist( out string NewPlaylistName )
|
|
{
|
|
local string Str;
|
|
local int i;
|
|
|
|
if ( NewPlaylistName == "" )
|
|
NewPlaylistName = DefaultPlaylistName;
|
|
|
|
Str = NewPlaylistName;
|
|
while ( ValidName(NewPlaylistName) )
|
|
NewPlaylistName = Str $ i++;
|
|
|
|
return AppendPlaylist( CreatePlaylist(NewPlaylistName) );
|
|
}
|
|
|
|
protected function int AppendPlaylist( StreamPlaylist Playlist )
|
|
{
|
|
local int i;
|
|
|
|
if ( Playlist == None )
|
|
return -1;
|
|
|
|
i = FindPlaylistIndex(Playlist);
|
|
if ( i == -1 )
|
|
i = Playlists.Length;
|
|
|
|
// log("Appending new playlist...playlist number "$i);
|
|
Playlists[i] = Playlist;
|
|
bDirty = True;
|
|
return i;
|
|
}
|
|
|
|
function bool RemovePlaylist( string PlaylistName )
|
|
{
|
|
local int i;
|
|
|
|
// log("Removing playlist "$PlaylistName);
|
|
i = FindNameIndex(PlaylistName);
|
|
if ( i != -1 )
|
|
return RemovePlaylistAt(i);
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool RemovePlaylistAt( int Index )
|
|
{
|
|
local bool bWasActive;
|
|
|
|
if ( !ValidIndex(Index) )
|
|
return false;
|
|
|
|
// log("Removing playlist at "$index);
|
|
bWasActive = CurrentPlaylist == Index;
|
|
|
|
// Check if this is the active playlist - if so, set the default active
|
|
Playlists[Index].ClearConfig();
|
|
Playlists.Remove(Index,1);
|
|
|
|
if ( bWasActive )
|
|
ActivatePlaylist(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
function bool RenamePlaylist( int Index, out string NewName )
|
|
{
|
|
local StreamPlaylist Temp;
|
|
local string Str;
|
|
local int i;
|
|
|
|
// log("RenamePlaylist() Index:"$Index@"CurrentName:"$Playlists[Index].GetTitle()@"NewName:"$NewName);
|
|
if ( !ValidIndex(Index) || NewName == "" )
|
|
return false;
|
|
|
|
Str = NewName;
|
|
while ( ValidName(NewName) )
|
|
NewName = Str $ i++;
|
|
|
|
Temp = CreatePlaylist(NewName);
|
|
if ( Temp == None )
|
|
return false;
|
|
|
|
Temp.ReplaceWith(Playlists[Index]);
|
|
Playlists[Index].ClearConfig();
|
|
|
|
Playlists[Index] = Temp;
|
|
Playlists[Index].Save();
|
|
|
|
if ( !bDisableNotification )
|
|
ChangedActivePlaylist(Playlists[Index]);
|
|
|
|
return true;
|
|
}
|
|
|
|
function bool AddToPlaylist( int PlaylistIndex, string Path, optional bool bSkipNotification )
|
|
{
|
|
if ( !ValidIndex(PlaylistIndex) )
|
|
{
|
|
if ( !ValidIndex(CurrentPlaylist) )
|
|
return false;
|
|
|
|
PlaylistIndex = CurrentPlaylist;
|
|
}
|
|
|
|
return InsertInPlaylist( PlaylistIndex, -1, Path, bSkipNotification );
|
|
}
|
|
|
|
function bool InsertInPlaylist( int PlaylistIndex, int InsertPosition, string Path, optional bool bSkipNotification )
|
|
{
|
|
local EFileType Type;
|
|
local bool bResult;
|
|
|
|
if ( !ValidIndex(PlaylistIndex) )
|
|
return false;
|
|
|
|
Type = ConvertToFileType(Path);
|
|
Playlists[PlaylistIndex].InitializePlaylist(FileManager);
|
|
|
|
// log("PlaylistManager.AddToPlaylist() PlaylistIndex:"$PlaylistIndex@"InsertPosition:"$InsertPosition@"Path:"$Path@" Type:"$GetEnum(enum'EFileType',Type)@"Path:"$Path);
|
|
switch ( Type )
|
|
{
|
|
case FILE_None: // directory
|
|
bDisableNotification = True;
|
|
bResult = AddDirectory( PlaylistIndex, InsertPosition, Path, True );
|
|
bDisableNotification = False;
|
|
|
|
if ( bResult && !bSkipNotification )
|
|
Playlists[PlaylistIndex].OnPlaylistChanged();
|
|
|
|
return bResult;
|
|
|
|
case FILE_Playlist: // importing a playlist
|
|
bDisableNotification = True;
|
|
bResult = ImportPlaylist(PlaylistIndex, InsertPosition, Path);
|
|
bDisableNotification = False;
|
|
|
|
if ( bResult && !bSkipNotification )
|
|
Playlists[PlaylistIndex].OnPlaylistChanged();
|
|
|
|
return bResult;
|
|
|
|
case FILE_Stream: // adding a song
|
|
return Playlists[PlaylistIndex].InsertSong(InsertPosition, Path, bDisableNotification || bSkipNotification) != -1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool RemoveFromCurrentPlaylist( string Path, optional bool bSkipNotification )
|
|
{
|
|
// log("RemoveFromCurrentPlaylist CurrentPlaylist:"$CurrentPlaylist@"Path:"$Path);
|
|
if ( ValidIndex(CurrentPlaylist) )
|
|
return Playlists[CurrentPlaylist].RemoveSong(Path, bSkipNotification);
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool ClearCurrentPlaylist()
|
|
{
|
|
if ( !ValidIndex(CurrentPlaylist) )
|
|
return false;
|
|
|
|
return Playlists[CurrentPlaylist].ClearPlaylist();
|
|
}
|
|
|
|
function EStreamPlaylistType GetPlaylistType( string Path )
|
|
{
|
|
local string Ext;
|
|
|
|
Ext = ParseExtension(Path);
|
|
switch ( Locs(Ext) )
|
|
{
|
|
case "m3u": return SPT_M3U;
|
|
case "b4s": return SPT_B4S;
|
|
case "pls": return SPT_PLS;
|
|
}
|
|
|
|
return SPT_None;
|
|
}
|
|
|
|
|
|
// TODO hook up non-recursive
|
|
function bool AddDirectory( int PlaylistIndex, int InsertPosition, string Path, bool bRecurseDirectories )
|
|
{
|
|
local int i;
|
|
local bool bValue;
|
|
local array<string> Results;
|
|
|
|
// First, check for any directories
|
|
if ( bRecurseDirectories )
|
|
{
|
|
if ( FileManager.GetDirectoryContents(Results,Path,FILE_Directory) )
|
|
{
|
|
for ( i = Results.Length - 1; i >= 0; i-- )
|
|
bValue = AddDirectory(PlaylistIndex,InsertPosition, Path $ Results[i] $ GetPathSeparator(), True) || bValue;
|
|
}
|
|
}
|
|
|
|
Results.Remove(0, Results.Length);
|
|
if ( FileManager.GetDirectoryContents(Results,Path,FILE_Stream) )
|
|
{
|
|
if ( !Playlists[PlaylistIndex].ValidStreamIndex(InsertPosition) )
|
|
InsertPosition = Playlists[PlaylistIndex].GetPlaylistLength();
|
|
|
|
for ( i = Results.Length - 1; i >= 0; i-- )
|
|
bValue = Playlists[PlaylistIndex].InsertSong(InsertPosition, Path $ Results[i], bDisableNotification) != -1 || bValue;
|
|
}
|
|
|
|
return bValue;
|
|
}
|
|
|
|
function bool ImportPlaylist( int PlaylistIndex, int InsertPosition, string Path )
|
|
{
|
|
local int i;
|
|
local EStreamPlaylistType Type;
|
|
|
|
// log("ImportPlaylist() PlaylistIndex:"$PlaylistIndex@"InsertPosition:"$InsertPosition@"Path:"$Path);
|
|
|
|
Type = GetPlaylistType(Path);
|
|
for ( i = 0; i < Parsers.Length; i++ )
|
|
if ( Parsers[i].GetType() == Type )
|
|
return Parsers[i].Import(PlaylistIndex, InsertPosition, Path);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Returns the next song in the playlist
|
|
function string NextSong( optional bool bForce )
|
|
{
|
|
local StreamPlaylist List;
|
|
local Stream S;
|
|
local string Str;
|
|
|
|
if ( GetShuffleAll() || GetShuffle() )
|
|
return GetRandomSong();
|
|
|
|
List = GetCurrentPlaylist();
|
|
if ( List == None )
|
|
{
|
|
log("PlaylistManager.NextSong() - no playlists found!",'MusicPlayer');
|
|
return "";
|
|
}
|
|
|
|
if ( GetRepeat() )
|
|
{
|
|
S = List.GetCurrentStream();
|
|
if ( S != None )
|
|
return S.GetPath();
|
|
}
|
|
else
|
|
{
|
|
Str = List.NextSong( bForce || GetRepeat() );
|
|
if ( Str == "" && GetRepeatAll() && ActivatePlaylist(CurrentPlaylist+1) )
|
|
Str = Playlists[CurrentPlaylist].NextSong(bForce);
|
|
|
|
if ( Str != "" )
|
|
return Str;
|
|
}
|
|
|
|
// log("PlaylistManager.NextSong() - last song reached.",'MusicPlayer');
|
|
return "";
|
|
}
|
|
|
|
function string PrevSong( optional bool bForce )
|
|
{
|
|
local StreamPlaylist List;
|
|
local Stream S;
|
|
local string str;
|
|
|
|
if ( GetShuffleAll() || GetShuffle() )
|
|
return GetRandomSong();
|
|
|
|
List = GetCurrentPlaylist();
|
|
if ( List == None )
|
|
{
|
|
log("PlaylistManager.PrevSong() - no playlists found!",'MusicPlayer');
|
|
return "";
|
|
}
|
|
|
|
if ( GetRepeat() )
|
|
{
|
|
S = List.GetCurrentStream();
|
|
if ( S != None )
|
|
return S.GetPath();
|
|
}
|
|
|
|
else
|
|
{
|
|
Str = List.PrevSong( bForce || GetRepeat() );
|
|
if ( Str == "" && GetRepeatAll() && ActivatePlaylist(CurrentPlaylist-1) )
|
|
Str = Playlists[CurrentPlaylist].PrevSong(bForce);
|
|
|
|
if ( Str != "" )
|
|
return Str;
|
|
}
|
|
// log("PlaylistManager.PrevSong() - last song reached.",'MusicPlayer');
|
|
return "";
|
|
}
|
|
|
|
function string GetRandomSong()
|
|
{
|
|
local StreamPlaylist List;
|
|
|
|
if ( GetShuffle() )
|
|
List = GetCurrentPlaylist();
|
|
|
|
else if ( GetShuffleAll() )
|
|
List = GetRandomPlayList();
|
|
|
|
if ( List != None )
|
|
return List.GetRandomSong();
|
|
|
|
return "";
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Playlist Query
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function int GetCurrentIndex()
|
|
{
|
|
if ( ValidIndex(CurrentPlaylist) )
|
|
return CurrentPlaylist;
|
|
|
|
return -1;
|
|
}
|
|
|
|
function StreamPlaylist GetCurrentPlaylist()
|
|
{
|
|
if ( ValidIndex(CurrentPlaylist) )
|
|
return Playlists[CurrentPlaylist];
|
|
|
|
if ( ActivatePlaylist(0) )
|
|
return Playlists[0];
|
|
|
|
return CreateDefaultPlaylist();
|
|
}
|
|
|
|
function StreamPlaylist GetRandomPlaylist()
|
|
{
|
|
local int i, idx;
|
|
|
|
if ( Playlists.Length == 0 )
|
|
return None;
|
|
|
|
idx = Rand(Playlists.Length - 1);
|
|
while ( ++i < 10 )
|
|
{
|
|
if ( ActivatePlaylist(idx) )
|
|
return Playlists[CurrentPlaylist];
|
|
|
|
idx = Rand(Playlists.Length - 1);
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
function StreamPlaylist GetPlaylistAt( int idx )
|
|
{
|
|
if ( !ValidIndex(idx) )
|
|
return None;
|
|
|
|
return Playlists[idx];
|
|
}
|
|
|
|
function int GetPlaylistCount()
|
|
{
|
|
return Playlists.Length;
|
|
}
|
|
|
|
// Current playlist interaction
|
|
|
|
function bool ValidIndex( int Index )
|
|
{
|
|
return Index >= 0 && Index < Playlists.Length;
|
|
}
|
|
|
|
function int FindNameIndex( string PlaylistName )
|
|
{
|
|
local int i;
|
|
|
|
if ( PlaylistName == "" )
|
|
return -1;
|
|
|
|
for ( i = 0; i < Playlists.Length; i++ )
|
|
if ( Playlists[i].GetTitle() ~= PlaylistName )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
function int FindPlaylistIndex( StreamPlaylist Playlist )
|
|
{
|
|
local int i;
|
|
|
|
if ( Playlist == None )
|
|
return -1;
|
|
|
|
for ( i = 0; i < Playlists.Length; i++ )
|
|
if ( Playlists[i] == Playlist )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
function bool ValidName( string Test )
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < Playlists.Length; i++ )
|
|
if ( Test ~= Playlists[i].GetTitle() )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool GetShuffle()
|
|
{
|
|
return bShuffle;
|
|
}
|
|
|
|
function bool GetShuffleAll()
|
|
{
|
|
return bShuffleAll;
|
|
}
|
|
|
|
function bool GetRepeat()
|
|
{
|
|
return bRepeat;
|
|
}
|
|
|
|
function bool GetRepeatAll()
|
|
{
|
|
return bRepeatAll;
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Internal
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function SetShuffle( bool bEnable )
|
|
{
|
|
bDirty = bDirty || bEnable != bShuffle;
|
|
bShuffle = bEnable;
|
|
|
|
if ( bEnable && bShuffleAll )
|
|
SetShuffleAll(False);
|
|
}
|
|
|
|
function SetShuffleAll( bool bEnable )
|
|
{
|
|
bDirty = bDirty || bEnable != bShuffleAll;
|
|
bShuffleAll = bEnable;
|
|
if ( bEnable && bShuffle )
|
|
SetShuffle( False );
|
|
}
|
|
|
|
function SetRepeat( bool bEnable )
|
|
{
|
|
bDirty = bDirty || bEnable != bRepeat;
|
|
bRepeat = bEnable;
|
|
|
|
if ( bEnable && bRepeatAll )
|
|
SetRepeatAll(False);
|
|
}
|
|
|
|
function SetRepeatAll( bool bEnable )
|
|
{
|
|
bDirty = bDirty || bEnable != bRepeatAll;
|
|
bRepeatAll = bEnable;
|
|
|
|
if ( bEnable && bRepeat )
|
|
SetRepeat(False);
|
|
}
|
|
|
|
protected function StreamPlaylist CreatePlaylist(string PlaylistName)
|
|
{
|
|
local StreamPlaylist List;
|
|
|
|
if ( PlaylistName == "" )
|
|
return None;
|
|
|
|
List = new(None, Repl(PlaylistName, " ", Chr(27))) class'StreamPlaylist';
|
|
if ( List != None )
|
|
{
|
|
List.SetTitle( Repl(PlaylistName, Chr(27), " ") );
|
|
List.Save();
|
|
}
|
|
|
|
return List;
|
|
}
|
|
|
|
function bool HandleDebugExec( string Command, string Param )
|
|
{
|
|
local int i;
|
|
local bool result;
|
|
|
|
if ( Super.HandleDebugExec(Command,Param) )
|
|
return true;
|
|
|
|
switch ( Locs(command) )
|
|
{
|
|
|
|
case "activate":
|
|
if ( Playlists.Length > 0 )
|
|
ActivatePlaylist(0);
|
|
else ActivatePlaylist(int(Param));
|
|
|
|
log("Active Playlist:"$Playlists[CurrentPlaylist].GetTitle()@"Tracks:"$Playlists[CurrentPlaylist].GetPlaylistLength(),'MusicPlayer');
|
|
break;
|
|
|
|
case "lists":
|
|
log(" === All existing playlists === ",'MusicPlayer');
|
|
for ( i = 0; i < Playlists.Length; i++ )
|
|
log(" Playlist"@i@" '"$Playlists[i].GetTitle(),'MusicPlayer');
|
|
}
|
|
|
|
for (i = 0; i < Playlists.Length; i++ )
|
|
result = Playlists[i].HandleDebugExec(Command,Param) || result;
|
|
|
|
return result;
|
|
}
|
|
|
|
function Save()
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < Playlists.Length; i++ )
|
|
Playlists[i].Save();
|
|
|
|
if ( !bDirty )
|
|
return;
|
|
|
|
SaveConfig();
|
|
bDirty = False;
|
|
}
|
|
|
|
function string GetCurrentTitle()
|
|
{
|
|
return Playlists[GetCurrentIndex()].GetTitle();
|
|
}
|
|
|
|
DefaultProperties
|
|
{
|
|
DefaultPlaylistName="New Playlist"
|
|
bRepeatAll=True
|
|
ParserType(0)=(Type=SPT_B4S,ParserClass="Engine.B4SParser")
|
|
ParserType(1)=(Type=SPT_M3U,ParserClass="Engine.M3UParser")
|
|
ParserType(2)=(Type=SPT_PLS,ParserClass="Engine.PLSParser")
|
|
}
|