123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- #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
|