630 lines
17 KiB
C++
630 lines
17 KiB
C++
#include <strsafe.h>
|
|
#include <shlwapi.h>
|
|
#include <algorithm>
|
|
|
|
|
|
#include "main.h"
|
|
#include "resource.h"
|
|
#include "PlaylistManager.h"
|
|
#include "ifc_playlistloader.h"
|
|
#include "M3ULoader.h"
|
|
#include "M3UWriter.h"
|
|
#include "PLSWriter.h"
|
|
#include "M3U8Writer.h"
|
|
#include "B4SWriter.h"
|
|
#include "../nu/AutoChar.h"
|
|
#include "Playlist.h"
|
|
#include "../playlist/svc_playlisthandler.h"
|
|
#include "../playlist/Handler.h"
|
|
#include "../nu/AutoWide.h"
|
|
#include "Playlist.h"
|
|
#include "api/service/services.h"
|
|
#include "api__playlist.h"
|
|
#include "api/service/waservicefactory.h"
|
|
#include "PlaylistCounter.h"
|
|
#include "ifc_playlistloadercallback.h"
|
|
#include "ifc_playlistdirectorycallback.h"
|
|
|
|
#include "..\WAT\WAT.h"
|
|
|
|
class NoRecurseCallback : public ifc_playlistdirectorycallback
|
|
{
|
|
public:
|
|
NoRecurseCallback( ifc_playlistdirectorycallback *_callback ) : callback( _callback ) {}
|
|
bool ShouldRecurse( const wchar_t *path ) { return false; }
|
|
bool ShouldLoad( const wchar_t *filename ) { return callback->ShouldLoad( filename ); }
|
|
|
|
ifc_playlistdirectorycallback *callback;
|
|
|
|
protected:
|
|
RECVS_DISPATCH;
|
|
};
|
|
|
|
#define CBCLASS NoRecurseCallback
|
|
START_DISPATCH;
|
|
CB( IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDRECURSE, ShouldRecurse )
|
|
CB( IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDLOAD, ShouldLoad )
|
|
END_DISPATCH;
|
|
#undef CBCLASS
|
|
|
|
|
|
static void MakeRelativePathName( const wchar_t *filename, wchar_t *outFile, size_t cch, const wchar_t *path )
|
|
{
|
|
wchar_t outPath[ MAX_PATH ] = { 0 };
|
|
|
|
int common = PathCommonPrefixW( path, filename, outPath );
|
|
if ( common && common == wcslen( path ) )
|
|
{
|
|
PathAddBackslashW( outPath );
|
|
const wchar_t *p = filename + wcslen( outPath );
|
|
lstrcpynW( outFile, p, (int)cch );
|
|
}
|
|
else if ( !PathIsUNCW( filename ) && PathIsSameRootW( filename, path ) )
|
|
{
|
|
if ( outFile[ 1 ] == ':' )
|
|
lstrcpynW( outFile, filename + 2, (int)cch );
|
|
}
|
|
}
|
|
|
|
static void PlayList_makerelative( const wchar_t *base, wchar_t *filename, size_t cch )
|
|
{
|
|
MakeRelativePathName( filename, filename, cch, base );
|
|
}
|
|
|
|
|
|
PlaylistManager playlistManager;
|
|
|
|
struct LoaderPair
|
|
{
|
|
ifc_playlistloader *loader;
|
|
svc_playlisthandler *handler;
|
|
};
|
|
|
|
static LoaderPair CreateLoader( const wchar_t *filename )
|
|
{
|
|
LoaderPair ret = { 0, 0 };
|
|
int n = 0;
|
|
waServiceFactory *sf = 0;
|
|
|
|
while ( sf = WASABI_API_SVC->service_enumService( WaSvc::PLAYLISTHANDLER, n++ ) )
|
|
{
|
|
svc_playlisthandler *handler = static_cast<svc_playlisthandler *>( sf->getInterface() );
|
|
if ( handler )
|
|
{
|
|
if ( handler->SupportedFilename( filename ) == SVC_PLAYLISTHANDLER_SUCCESS )
|
|
{
|
|
ret.loader = handler->CreateLoader( filename );
|
|
ret.handler = handler;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
sf->releaseInterface( handler );
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: sniff file if no one claims it
|
|
return ret;
|
|
}
|
|
|
|
void DestroyLoader( LoaderPair &loader )
|
|
{
|
|
loader.handler->ReleaseLoader( loader.loader );
|
|
}
|
|
|
|
// a simple loader...
|
|
int PlaylistManager::Load( const wchar_t *filename, ifc_playlistloadercallback *playlist )
|
|
{
|
|
LoaderPair loaderPair = CreateLoader( filename );
|
|
ifc_playlistloader *loader = loaderPair.loader;
|
|
|
|
if ( !loader )
|
|
return PLAYLISTMANAGER_LOAD_NO_LOADER; // failed to find a loader
|
|
|
|
// TODO: make our own ifc_playlistloadercallback, so we can handle nested playlists
|
|
int res = loader->Load( filename, playlist );
|
|
DestroyLoader( loaderPair );
|
|
|
|
if ( res != IFC_PLAYLISTLOADER_SUCCESS ) // TODO: switch on the error code and return a more specific error
|
|
return PLAYLISTMANAGER_LOAD_LOADER_OPEN_FAILED;
|
|
|
|
return PLAYLISTMANAGER_SUCCESS;
|
|
}
|
|
|
|
int PlaylistManager::LoadAs( const wchar_t *filename, const wchar_t *ext, ifc_playlistloadercallback *playlist )
|
|
{
|
|
LoaderPair loaderPair = CreateLoader( ext );
|
|
ifc_playlistloader *loader = loaderPair.loader;
|
|
|
|
if ( !loader )
|
|
return PLAYLISTMANAGER_LOAD_NO_LOADER; // failed to find a loader
|
|
|
|
// TODO: make our own ifc_playlistloadercallback, so we can handle nested playlists
|
|
int res = loader->Load( filename, playlist );
|
|
DestroyLoader( loaderPair );
|
|
|
|
if ( res != IFC_PLAYLISTLOADER_SUCCESS ) // TODO: switch on the error code and return a more specific error
|
|
return PLAYLISTMANAGER_LOAD_LOADER_OPEN_FAILED;
|
|
|
|
return PLAYLISTMANAGER_SUCCESS;
|
|
}
|
|
|
|
int PlaylistManager::LoadFromDialog( const wchar_t *fns, ifc_playlistloadercallback *playlist )
|
|
{
|
|
wchar_t buf[ MAX_PATH ] = { 0 };
|
|
const wchar_t *path = fns;
|
|
fns += wcslen( fns ) + 1;
|
|
|
|
while ( fns && *fns )
|
|
{
|
|
if ( *path )
|
|
PathCombineW( buf, path, fns );
|
|
else
|
|
StringCchCopyW( buf, MAX_PATH, fns );
|
|
|
|
if ( Load( buf, playlist ) != PLAYLISTMANAGER_SUCCESS )
|
|
{
|
|
if ( playlist->OnFile( buf, 0, -1, 0 ) != ifc_playlistloadercallback::LOAD_CONTINUE )
|
|
return PLAYLIST_SUCCESS;
|
|
}
|
|
|
|
fns += wcslen( fns ) + 1;
|
|
}
|
|
return PLAYLIST_SUCCESS;
|
|
}
|
|
|
|
int PlaylistManager::LoadFromANSIDialog( const char *fns, ifc_playlistloadercallback *playlist )
|
|
{
|
|
char buf[ MAX_PATH ] = { 0 };
|
|
const char *path = fns;
|
|
fns += lstrlenA( fns ) + 1;
|
|
|
|
while ( fns && *fns )
|
|
{
|
|
if ( *path )
|
|
PathCombineA( buf, path, fns );
|
|
else
|
|
lstrcpynA( buf, fns, MAX_PATH );
|
|
|
|
AutoWide wideFn( buf );
|
|
if ( Load( wideFn, playlist ) != PLAYLISTMANAGER_SUCCESS )
|
|
{
|
|
if ( playlist->OnFile( wideFn, 0, -1, 0 ) != ifc_playlistloadercallback::LOAD_CONTINUE )
|
|
return PLAYLIST_SUCCESS;
|
|
}
|
|
|
|
fns += lstrlenA( fns ) + 1;
|
|
}
|
|
return PLAYLIST_SUCCESS;
|
|
}
|
|
|
|
|
|
int PlaylistManager::Save( const wchar_t *filename, ifc_playlist *playlist )
|
|
{
|
|
const wchar_t *ext = PathFindExtensionW( filename );
|
|
PlaylistWriter *writer = 0;
|
|
|
|
if ( !lstrcmpiW( ext, L".M3U" ) )
|
|
writer = new M3UWriter;
|
|
else if ( !lstrcmpiW( ext, L".M3U8" ) )
|
|
writer = new M3U8Writer;
|
|
else if ( !lstrcmpiW( ext, L".PLS" ) )
|
|
writer = new PLSWriter;
|
|
else if ( !lstrcmpiW( ext, L".B4S" ) )
|
|
writer = new B4SWriter;
|
|
else
|
|
return PLAYLISTMANAGER_FAILED;
|
|
|
|
wchar_t base[ MAX_PATH ] = { 0 };
|
|
StringCchCopyW( base, MAX_PATH, filename );
|
|
PathRemoveFileSpecW( base );
|
|
PathRemoveBackslashW( base );
|
|
|
|
if ( !writer->Open( filename ) )
|
|
{
|
|
delete writer;
|
|
|
|
return PLAYLISTMANAGER_FAILED;
|
|
}
|
|
|
|
size_t numItems = playlist->GetNumItems();
|
|
wchar_t itemname[ FILENAME_SIZE ] = { 0 };
|
|
wchar_t title[ FILETITLE_SIZE ] = { 0 };
|
|
wchar_t cloud_info[ 512 ] = { 0 };
|
|
int length = 0;
|
|
|
|
wchar_t l_tvg_id[ 10 ] = { 0 };
|
|
wchar_t l_tvg_name[ FILETITLE_SIZE ] = { 0 };
|
|
wchar_t l_tvg_logo[ 512 ] = { 0 };
|
|
wchar_t l_group_title[ 64 ] = { 0 };
|
|
|
|
wchar_t l_ext[ 10 ] = { 0 };
|
|
|
|
wa::strings::wa_string l_extented_infos_line( "" );
|
|
|
|
for ( size_t i = 0; i != numItems; i++ )
|
|
{
|
|
if ( playlist->GetItem( i, itemname, FILENAME_SIZE ) )
|
|
{
|
|
//PlayList_makerelative( base, itemname, FILENAME_SIZE );
|
|
|
|
// this is used to preserve 'cloud' specific data in playlists
|
|
// and should only get a response from a cloud-based ml_playlist
|
|
if ( playlist->GetItemExtendedInfo( i, L"cloud", cloud_info, 512 ) )
|
|
{
|
|
writer->Write( cloud_info );
|
|
}
|
|
|
|
|
|
l_extented_infos_line.clear();
|
|
|
|
if ( playlist->GetItemExtendedInfo( i, L"tvg-name", l_tvg_name, FILETITLE_SIZE ) )
|
|
{
|
|
playlist->GetItemExtendedInfo( i, L"tvg-id", l_tvg_id, 10 );
|
|
playlist->GetItemExtendedInfo( i, L"tvg-logo", l_tvg_logo, 512 );
|
|
playlist->GetItemExtendedInfo( i, L"group-title", l_group_title, 64 );
|
|
|
|
l_extented_infos_line = L"tvg-id";
|
|
l_extented_infos_line.append( L"=\"" );
|
|
l_extented_infos_line.append( l_tvg_id );
|
|
l_extented_infos_line.append( L"\" " );
|
|
|
|
l_extented_infos_line.append( L"tvg-name" );
|
|
l_extented_infos_line.append( L"=\"" );
|
|
l_extented_infos_line.append( l_tvg_name );
|
|
l_extented_infos_line.append( L"\" " );
|
|
|
|
l_extented_infos_line.append( L"tvg-logo" );
|
|
l_extented_infos_line.append( L"=\"" );
|
|
l_extented_infos_line.append( l_tvg_logo );
|
|
l_extented_infos_line.append( L"\" " );
|
|
|
|
l_extented_infos_line.append( L"group-title" );
|
|
l_extented_infos_line.append( L"=\"" );
|
|
l_extented_infos_line.append( l_group_title );
|
|
l_extented_infos_line.append( L"\" " );
|
|
}
|
|
|
|
wa::strings::wa_string l_item_name( itemname );
|
|
|
|
if ( l_item_name.contains( "://" ) && playlist->GetItemExtendedInfo(i, L"ext", l_ext, 10) )
|
|
{
|
|
l_extented_infos_line = L"ext";
|
|
l_extented_infos_line.append( L"=\"" );
|
|
l_extented_infos_line.append( l_ext );
|
|
l_extented_infos_line.append( L"\"" );
|
|
}
|
|
|
|
if ( playlist->GetItemTitle( i, title, FILETITLE_SIZE ) )
|
|
{
|
|
length = playlist->GetItemLengthMilliseconds( i );
|
|
|
|
if ( l_extented_infos_line.empty() )
|
|
writer->Write( itemname, title, length / 1000 );
|
|
else
|
|
writer->Write( itemname, title, l_extented_infos_line.GetW().c_str(), length / 1000);
|
|
}
|
|
else
|
|
writer->Write( itemname );
|
|
}
|
|
}
|
|
|
|
writer->Close();
|
|
delete writer;
|
|
|
|
return PLAYLISTMANAGER_SUCCESS;
|
|
}
|
|
|
|
|
|
size_t PlaylistManager::Copy( const wchar_t *destFn, const wchar_t *srcFn )
|
|
{
|
|
Playlist copy;
|
|
|
|
Load( srcFn, © );
|
|
Save( destFn, © );
|
|
|
|
return copy.GetNumItems();
|
|
}
|
|
|
|
|
|
size_t PlaylistManager::CountItems( const wchar_t *filename )
|
|
{
|
|
LoaderPair loaderPair = CreateLoader( filename );
|
|
ifc_playlistloader *loader = loaderPair.loader;
|
|
|
|
if ( !loader )
|
|
return 0;
|
|
|
|
PlaylistCounter counter;
|
|
loader->Load( filename, &counter );
|
|
|
|
DestroyLoader( loaderPair );
|
|
return counter.count;
|
|
}
|
|
|
|
|
|
int PlaylistManager::GetLengthMilliseconds( const wchar_t *filename )
|
|
{
|
|
LoaderPair loaderPair = CreateLoader( filename );
|
|
ifc_playlistloader *loader = loaderPair.loader;
|
|
|
|
if ( !loader )
|
|
return 0;
|
|
|
|
PlaylistCounter counter;
|
|
loader->Load( filename, &counter );
|
|
DestroyLoader( loaderPair );
|
|
return (int)counter.length;
|
|
}
|
|
|
|
uint64_t PlaylistManager::GetLongLengthMilliseconds( const wchar_t *filename )
|
|
{
|
|
LoaderPair loaderPair = CreateLoader( filename );
|
|
ifc_playlistloader *loader = loaderPair.loader;
|
|
|
|
if ( !loader )
|
|
return 0;
|
|
|
|
PlaylistCounter counter;
|
|
loader->Load( filename, &counter );
|
|
DestroyLoader( loaderPair );
|
|
return counter.length;
|
|
}
|
|
|
|
|
|
void PlaylistManager::Randomize( ifc_playlist *playlist )
|
|
{
|
|
if ( playlist->Randomize( warand ) == PLAYLIST_UNIMPLEMENTED )
|
|
{
|
|
// TODO: do it the hard way
|
|
}
|
|
}
|
|
|
|
void PlaylistManager::Reverse( ifc_playlist *playlist )
|
|
{
|
|
if ( playlist->Reverse() == PLAYLIST_UNIMPLEMENTED )
|
|
{
|
|
// TODO: do it the hard way
|
|
}
|
|
}
|
|
|
|
|
|
void PlaylistManager::LoadDirectory( const wchar_t *directory, ifc_playlistloadercallback *callback, ifc_playlistdirectorycallback *dirCallback )
|
|
{
|
|
WIN32_FIND_DATAW found = { 0 };
|
|
wchar_t filespec[ MAX_PATH ] = { 0 };
|
|
PathCombineW( filespec, directory, L"*.*" );
|
|
|
|
HANDLE i = FindFirstFileW( filespec, &found );
|
|
if ( i != INVALID_HANDLE_VALUE )
|
|
{
|
|
do
|
|
{
|
|
// if it's another folder, then we might want to recurse into it
|
|
if ( ( found.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) // if it's a directory
|
|
&& wcscmp( found.cFileName, L"." ) && wcscmp( found.cFileName, L".." ) // but not . or ..
|
|
&& ( !dirCallback || dirCallback->ShouldRecurse( found.cFileName ) ) ) // and we're allowed to recurse
|
|
{
|
|
if ( PathCombineW( filespec, directory, found.cFileName ) )
|
|
{
|
|
LoadDirectory( filespec, callback, dirCallback );
|
|
}
|
|
}
|
|
|
|
if ( !( found.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
|
|
{
|
|
const wchar_t *ext = PathFindExtensionW( found.cFileName );
|
|
if ( ext[ 0 ] )
|
|
{
|
|
if ( !_wcsicmp( ext, L".lnk" ) )
|
|
{
|
|
wchar_t thisf[ MAX_PATH ] = { 0 };
|
|
wchar_t temp2[ MAX_PATH ] = { 0 };
|
|
PathCombineW( temp2, directory, found.cFileName );
|
|
if ( ResolveShortCut( NULL, temp2, thisf ) && GetLongPathNameW( thisf, temp2, MAX_PATH ) && lstrcmpiW( temp2, directory ) )
|
|
{
|
|
WIN32_FIND_DATAW d2 = { 0 };
|
|
if ( IsUrl( temp2 ) && ( !dirCallback || dirCallback->ShouldLoad( temp2 ) ) )
|
|
{
|
|
if ( callback->OnFile( temp2, 0, -1, 0 ) != ifc_playlistloadercallback::LOAD_CONTINUE )
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
HANDLE h2 = FindFirstFileW( temp2, &d2 );
|
|
if ( h2 != INVALID_HANDLE_VALUE )
|
|
{
|
|
if ( !( d2.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
|
|
{
|
|
if ( !dirCallback || dirCallback->ShouldLoad( temp2 ) )
|
|
{
|
|
if ( callback->OnFile( temp2, 0, -1, 0 ) != ifc_playlistloadercallback::LOAD_CONTINUE )
|
|
{
|
|
FindClose( h2 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// recursively load a shortcut w/o fear of infinite recursion
|
|
NoRecurseCallback noRecurse( dirCallback );
|
|
LoadDirectory( temp2, callback, &noRecurse );
|
|
}
|
|
FindClose( h2 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // !shortcut
|
|
{
|
|
|
|
if ( PathCombineW( filespec, directory, found.cFileName ) &&
|
|
( !dirCallback || dirCallback->ShouldLoad( filespec ) ) )
|
|
{
|
|
if ( callback->OnFile( filespec, 0, -1, 0 ) != ifc_playlistloadercallback::LOAD_CONTINUE )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while ( FindNextFileW( i, &found ) );
|
|
FindClose( i );
|
|
}
|
|
}
|
|
|
|
bool PlaylistManager::CanLoad( const wchar_t *filename )
|
|
{
|
|
int n = 0;
|
|
waServiceFactory *sf = 0;
|
|
while ( sf = WASABI_API_SVC->service_enumService( WaSvc::PLAYLISTHANDLER, n++ ) )
|
|
{
|
|
svc_playlisthandler *handler = static_cast<svc_playlisthandler *>( sf->getInterface() );
|
|
if ( handler )
|
|
{
|
|
if ( handler->SupportedFilename( filename ) == SVC_PLAYLISTHANDLER_SUCCESS )
|
|
{
|
|
sf->releaseInterface( handler );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
sf->releaseInterface( handler );
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PlaylistManager::GetExtensionList( wchar_t *extensionList, size_t extensionListCch )
|
|
{
|
|
extensionList[ 0 ] = 0;
|
|
|
|
bool first = true;
|
|
|
|
int n = 0, extListCch = (int)extensionListCch;
|
|
wchar_t *extList = extensionList;
|
|
waServiceFactory *sf = 0;
|
|
while ( sf = WASABI_API_SVC->service_enumService( WaSvc::PLAYLISTHANDLER, n++ ) )
|
|
{
|
|
svc_playlisthandler *handler = static_cast<svc_playlisthandler *>( sf->getInterface() );
|
|
if ( handler )
|
|
{
|
|
const wchar_t *ext = 0;
|
|
int k = 0;
|
|
while ( ext = handler->EnumerateExtensions( k++ ) )
|
|
{
|
|
if ( first )
|
|
StringCchCatExW( extensionList, extensionListCch, L"*.", &extensionList, &extensionListCch, 0 );
|
|
else
|
|
StringCchCatExW( extensionList, extensionListCch, L";*.", &extensionList, &extensionListCch, 0 );
|
|
|
|
first = false;
|
|
|
|
StringCchCatExW( extensionList, extensionListCch, ext, &extensionList, &extensionListCch, 0 );
|
|
}
|
|
sf->releaseInterface( handler );
|
|
}
|
|
}
|
|
CharUpperBuffW( extList, extListCch );
|
|
}
|
|
|
|
void PlaylistManager::GetFilterList( wchar_t *extensionList, size_t extensionListCch )
|
|
{
|
|
extensionListCch--; // this needs to be DOUBLE null terminated, so we'll make sure there's room
|
|
|
|
StringCchCopyExW( extensionList, extensionListCch, WASABI_API_LNGSTRINGW( IDS_ALL_PLAYLIST_TYPES ), &extensionList, &extensionListCch, 0 );
|
|
extensionListCch--;
|
|
extensionList++;
|
|
|
|
GetExtensionList( extensionList, extensionListCch );
|
|
|
|
extensionListCch -= ( wcslen( extensionList ) + 1 );
|
|
extensionList += wcslen( extensionList ) + 1;
|
|
|
|
int n = 0;
|
|
waServiceFactory *sf = 0;
|
|
while ( sf = WASABI_API_SVC->service_enumService( WaSvc::PLAYLISTHANDLER, n++ ) )
|
|
{
|
|
svc_playlisthandler *handler = static_cast<svc_playlisthandler *>( sf->getInterface() );
|
|
if ( handler )
|
|
{
|
|
const wchar_t *name = handler->GetName();
|
|
if ( !name )
|
|
name = WASABI_API_LNGSTRINGW( IDS_PLAYLIST );
|
|
|
|
StringCchCopyExW( extensionList, extensionListCch, name, &extensionList, &extensionListCch, 0 );
|
|
extensionList++;
|
|
extensionListCch--;
|
|
|
|
bool first = true;
|
|
const wchar_t *ext = 0;
|
|
int k = 0;
|
|
while ( ext = handler->EnumerateExtensions( k++ ) )
|
|
{
|
|
if ( first )
|
|
StringCchCopyExW( extensionList, extensionListCch, L"*.", &extensionList, &extensionListCch, 0 );
|
|
else
|
|
StringCchCatExW( extensionList, extensionListCch, L";*.", &extensionList, &extensionListCch, 0 );
|
|
|
|
first = false;
|
|
|
|
StringCchCatExW( extensionList, extensionListCch, ext, &extensionList, &extensionListCch, 0 );
|
|
}
|
|
extensionList++;
|
|
extensionListCch--;
|
|
|
|
sf->releaseInterface( handler );
|
|
}
|
|
}
|
|
|
|
extensionList[ 0 ] = 0; // ok because we reserved the room for it above
|
|
}
|
|
|
|
const wchar_t *PlaylistManager::EnumExtensions( size_t num )
|
|
{
|
|
int n = 0;
|
|
int total = 0;
|
|
waServiceFactory *sf = 0;
|
|
while ( sf = WASABI_API_SVC->service_enumService( WaSvc::PLAYLISTHANDLER, n++ ) )
|
|
{
|
|
svc_playlisthandler *handler = static_cast<svc_playlisthandler *>( sf->getInterface() );
|
|
if ( handler )
|
|
{
|
|
const wchar_t *ext = 0;
|
|
int k = 0;
|
|
while ( ext = handler->EnumerateExtensions( k++ ) )
|
|
{
|
|
if ( total++ == num )
|
|
return ext;
|
|
}
|
|
|
|
sf->releaseInterface( handler );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define CBCLASS PlaylistManager
|
|
START_DISPATCH;
|
|
CB( API_PLAYLISTMANAGER_LOAD, Load )
|
|
CB( API_PLAYLISTMANAGER_LOADAS, LoadAs )
|
|
CB( API_PLAYLISTMANAGER_LOADNULLDELIMITED, LoadFromDialog )
|
|
CB( API_PLAYLISTMANAGER_LOADNULLDELIMITED_ANSI, LoadFromANSIDialog )
|
|
CB( API_PLAYLISTMANAGER_SAVE, Save )
|
|
CB( API_PLAYLISTMANAGER_COPY, Copy )
|
|
CB( API_PLAYLISTMANAGER_COUNT, CountItems )
|
|
CB( API_PLAYLISTMANAGER_GETLENGTH, GetLengthMilliseconds )
|
|
CB( API_PLAYLISTMANAGER_GETLONGLENGTH, GetLongLengthMilliseconds )
|
|
VCB( API_PLAYLISTMANAGER_RANDOMIZE, Randomize )
|
|
VCB( API_PLAYLISTMANAGER_REVERSE, Reverse )
|
|
VCB( API_PLAYLISTMANAGER_LOADDIRECTORY, LoadDirectory )
|
|
CB( API_PLAYLISTMANAGER_CANLOAD, CanLoad )
|
|
VCB( API_PLAYLISTMANAGER_GETEXTENSIONLIST, GetExtensionList )
|
|
VCB( API_PLAYLISTMANAGER_GETFILTERLIST, GetFilterList )
|
|
CB( API_PLAYLISTMANAGER_ENUMEXTENSION, EnumExtensions )
|
|
END_DISPATCH;
|
|
#undef CBCLASS |