diff options
Diffstat (limited to 'Src/playlist')
80 files changed, 8544 insertions, 0 deletions
diff --git a/Src/playlist/B4SLoader.cpp b/Src/playlist/B4SLoader.cpp new file mode 100644 index 00000000..735cdfe8 --- /dev/null +++ b/Src/playlist/B4SLoader.cpp @@ -0,0 +1,253 @@ +#include "main.h" +#include "B4SLoader.h" +#include "../nu/AutoChar.h" +#include "..\Components\wac_network\wac_network_http_receiver_api.h" +#include <strsafe.h> + +B4SLoader::B4SLoader() : parser(0), parserFactory(0) +{ + parserFactory = WASABI_API_SVC->service_getServiceByGuid(obj_xmlGUID); + if (parserFactory) + parser = (obj_xml *)parserFactory->getInterface(); + + if (parser) + { + parser->xmlreader_registerCallback(L"WasabiXML\fplaylist\fentry\fname", &b4sXml.title); + parser->xmlreader_registerCallback(L"WinampXML\fplaylist\fentry\fname", &b4sXml.title); + + parser->xmlreader_registerCallback(L"WasabiXML\fplaylist\fentry\flength", &b4sXml.length); + parser->xmlreader_registerCallback(L"WinampXML\fplaylist\fentry\flength", &b4sXml.length); + + //parser->xmlreader_registerCallback(L"WasabiXML\fplaylist", this); + //parser->xmlreader_registerCallback(L"WinampXML\fplaylist", this); + parser->xmlreader_registerCallback(L"WasabiXML\fplaylist\fentry", &b4sXml); + parser->xmlreader_registerCallback(L"WinampXML\fplaylist\fentry", &b4sXml); + parser->xmlreader_open(); + //parser->xmlreader_setEncoding(L"UTF-8"); + } +} + +B4SXML::B4SXML() +{ + filename[0]=0; + playlist = 0; +} + +B4SLoader::~B4SLoader() +{ + if (parser) + { + parser->xmlreader_unregisterCallback(&b4sXml); + parser->xmlreader_unregisterCallback(&b4sXml.length); + parser->xmlreader_unregisterCallback(&b4sXml.title); + parser->xmlreader_close(); + } + + if (parserFactory && parser) + parserFactory->releaseInterface(parser); + + parserFactory = 0; + parser = 0; +} + +#define HTTP_BUFFER_SIZE 1024 +static int FeedXMLHTTP(api_httpreceiver *http, obj_xml *parser, bool *noData) +{ + char downloadedData[HTTP_BUFFER_SIZE] = {0}; + int xmlResult = API_XML_SUCCESS; + int downloadSize = http->get_bytes(downloadedData, HTTP_BUFFER_SIZE); + if (downloadSize) + { + xmlResult = parser->xmlreader_feed((void *)downloadedData, downloadSize); + *noData=false; + } + else + *noData = true; + + return xmlResult; +} + +static void RunXMLDownload(api_httpreceiver *http, obj_xml *parser) +{ + int ret; + bool noData; + do + { + Sleep(50); + ret = http->run(); + if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS) + return ; + } + while (ret == HTTPRECEIVER_RUN_OK); + + // finish off the data + do + { + if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS) + return ; + } while (!noData); + + parser->xmlreader_feed(0, 0); +} + +void B4SXML::StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + if (!_wcsicmp(xmltag, L"entry")) + { + const wchar_t *track = params->getItemValue(L"playstring"); + if (!_wcsnicmp(track, L"file://", 7)) + track+=7; + else if (!_wcsnicmp(track, L"file:", 5)) + track+=5; + StringCchCopyW(filename, FILENAME_SIZE, track); + } +} + +void B4SXML::EndTag(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (!_wcsicmp(xmltag, L"entry") && filename[0]) + { + int lengthSeconds=-1; + const wchar_t *trackLength = length.GetString(); + if (trackLength && *trackLength) + lengthSeconds = _wtoi(trackLength) / 1000; + + const wchar_t *trackTitle = title.GetString(); + // TODO: deal with relative pathnames + if (trackTitle && *trackTitle) + playlist->OnFile(filename, trackTitle, lengthSeconds, 0); // TODO: extended info + else + playlist->OnFile(filename, 0, lengthSeconds, 0);// TODO: extended info + + filename[0]=0; + length.Reset(); + title.Reset(); + } +} +#if 0 // TOOD: reimplement +void B4SLoader::LoadURL(const char *url) +{ + if (!parser) + return ; // no sense in continuing if there's no parser available + + api_httpreceiver *http = 0; + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID); + if (sf) + http = (api_httpreceiver *)sf->getInterface(); + + if (!http) + return ; + http->AllowCompression(); + http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, GetProxy()); + SetUserAgent(http); + http->connect(url); + int ret; + bool first = true; + + do + { + Sleep(50); + ret = http->run(); + if (ret == -1) // connection failed + break; + + // ---- check our reply code ---- + int replycode = http->GetReplyCode(); + switch (replycode) + { + case 0: + case 100: + break; + case 200: + { + RunXMLDownload(http, parser); + sf->releaseInterface(http); + return ; + } + break; + default: + sf->releaseInterface(http); + return ; + } + } + while (ret == HTTPRECEIVER_RUN_OK); + const char *er = http->geterrorstr(); + sf->releaseInterface(http); + return ; +} + +void B4SLoader::LoadFile(const char *filename) +{ + if (!parser) + return ; // no sense in continuing if there's no parser available + + HANDLE file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); + + if (file == INVALID_HANDLE_VALUE) + return ; + + char data[1024] = {0}; + + while (true) + { + DWORD bytesRead = 0; + if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead) + { + parser->xmlreader_feed(data, bytesRead); + } + else + break; + } + + CloseHandle(file); + parser->xmlreader_feed(0, 0); +} +#endif +int B4SLoader::Load(const wchar_t *filename, ifc_playlistloadercallback *playlist) +{ + if (!parser) + return IFC_PLAYLISTLOADER_FAILED; // no sense in continuing if there's no parser available + + b4sXml.playlist=playlist; + + HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); + + if (file == INVALID_HANDLE_VALUE) + return IFC_PLAYLISTLOADER_FAILED; + + while (true) + { + char data[1024] = {0}; + DWORD bytesRead = 0; + if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead) + { + parser->xmlreader_feed(data, bytesRead); + } + else + break; + } + + CloseHandle(file); + parser->xmlreader_feed(0, 0); + return IFC_PLAYLISTLOADER_SUCCESS; +} + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS B4SXML +START_DISPATCH; +VCB(ONSTARTELEMENT, StartTag) +VCB(ONENDELEMENT, EndTag) +END_DISPATCH; + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS B4SLoader +START_DISPATCH; +CB(IFC_PLAYLISTLOADER_LOAD, Load) + +END_DISPATCH;
\ No newline at end of file diff --git a/Src/playlist/B4SLoader.h b/Src/playlist/B4SLoader.h new file mode 100644 index 00000000..0c8e4bf5 --- /dev/null +++ b/Src/playlist/B4SLoader.h @@ -0,0 +1,46 @@ +#ifndef NULLSOFT_WINAMP_B4S_H +#define NULLSOFT_WINAMP_B4S_H + +#include "../xml/obj_xml.h" +#include "../xml/ifc_xmlreadercallback.h" +#include "api__playlist.h" +#include <api/service/waServiceFactory.h> +#include "XMLString.h" +#include "main.h" +#include "ifc_playlistloader.h" + +class B4SXML : public ifc_xmlreadercallback +{ +public: + B4SXML(); + ifc_playlistloadercallback *playlist; + //private: + + /* XML callbacks */ + void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag); + + XMLString /*name, artist, */title/*, album*/, length; // wa2 only supports title and length in the playlist anyway + RECVS_DISPATCH; + wchar_t filename[FILENAME_SIZE]; +}; + +class B4SLoader : public ifc_playlistloader +{ +public: + B4SLoader(); + ~B4SLoader(); + + void LoadFile(const char *filename); + void LoadURL(const char *url); + + int Load(const wchar_t *filename, ifc_playlistloadercallback *playlist); + + RECVS_DISPATCH; + + obj_xml *parser; + waServiceFactory *parserFactory; + B4SXML b4sXml; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/B4SWriter.cpp b/Src/playlist/B4SWriter.cpp new file mode 100644 index 00000000..a23a0b96 --- /dev/null +++ b/Src/playlist/B4SWriter.cpp @@ -0,0 +1,40 @@ +#include "B4SWriter.h" + +/* +TODO: escape XML shit +*/ + +B4SWriter::B4SWriter() : fp(0) +{ +} + +int B4SWriter::Open(const wchar_t *filename) +{ + fp = _wfopen(filename, L"wt"); + if (!fp) + return 0; + + fwprintf(fp, L"<playlist>\n"); + + return 1; +} + +void B4SWriter::Write(const wchar_t *filename) +{ + fwprintf(fp, L"<entry playstring=\"%s\"/>\n", filename); +} + +void B4SWriter::Write(const wchar_t *filename, const wchar_t *title, int length) +{ + fwprintf(fp, L"<entry playstring=\"%s\">\n", filename); + fwprintf(fp, L"<name>%s</name>\n", title); + fwprintf(fp, L"<length>%d</length>\n", length); + fwprintf(fp, L"</entry>\n"); +} + +void B4SWriter::Close() +{ + fputs("</playlist>", fp); + fclose(fp); + +}
\ No newline at end of file diff --git a/Src/playlist/B4SWriter.h b/Src/playlist/B4SWriter.h new file mode 100644 index 00000000..ef2d656f --- /dev/null +++ b/Src/playlist/B4SWriter.h @@ -0,0 +1,24 @@ +#ifndef NULLSOFT_PLAYLIST_B4SWRITER_H +#define NULLSOFT_PLAYLIST_B4SWRITER_H + +#include <stdio.h> +#include "PlaylistWriter.h" + +class B4SWriter : public PlaylistWriter +{ +public: + B4SWriter(); + + int Open( const wchar_t *filename ) override; + void Write( const wchar_t *filename ) override; + void Write( const wchar_t *filename, const wchar_t *title, int length ) override; + void Write( const wchar_t *p_filename, const wchar_t *p_title, const wchar_t *p_extended_infos, int p_length ) override + {}; + + void Close() override; + +private: + FILE *fp; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/Handler.cpp b/Src/playlist/Handler.cpp new file mode 100644 index 00000000..755f68fa --- /dev/null +++ b/Src/playlist/Handler.cpp @@ -0,0 +1,210 @@ +#include "Handler.h" +#include "resource.h" +#include "M3ULoader.h" +#include "PLSLoader.h" +#include "B4SLoader.h" +#include <shlwapi.h> + +void GetExtension(const wchar_t *filename, wchar_t *ext, size_t extCch) +{ + const wchar_t *s = PathFindExtensionW(filename); + if (!PathIsURLW(filename) + || (!wcsstr(s, L"?") && !wcsstr(s, L"&") && !wcsstr(s, L"=") && *s)) + { + lstrcpynW(ext, s, (int)extCch); + return ; + } + // s is not a terribly good extension, let's try again + { + wchar_t *copy = _wcsdup(filename); + s = L""; + again: + { + wchar_t *p = StrRChrW(copy,NULL, L'?'); + if (p) + { + *p = 0; + s = PathFindExtensionW(copy); + if (!*s) goto again; + } + lstrcpynW(ext, s, (int)extCch); + } + free(copy); + } +} + +const wchar_t *M3UHandler::enumExtensions(size_t n) +{ + switch(n) + { + case 0: + return L"M3U"; + case 1: + return L"M3U8"; + default: + return 0; + } +} + +int M3UHandler::SupportedFilename(const wchar_t *filename) +{ + wchar_t ext[16] = {0}; + GetExtension(filename, ext, 16); + + if (!lstrcmpiW(ext, L".M3U")) + return SVC_PLAYLISTHANDLER_SUCCESS; + if (!lstrcmpiW(ext, L".M3U8")) + return SVC_PLAYLISTHANDLER_SUCCESS; + + return SVC_PLAYLISTHANDLER_FAILED; +} + +ifc_playlistloader *M3UHandler::CreateLoader(const wchar_t *filename) +{ + return new M3ULoader; +} + +void M3UHandler::ReleaseLoader(ifc_playlistloader *loader) +{ + M3ULoader *m3u; + + m3u = static_cast<M3ULoader *>(loader); + delete m3u; +} + +const wchar_t *M3UHandler::GetName() +{ + static wchar_t m3upl[64]; + // no point re-loading this all of the time since it won't change once we've been loaded + return (!m3upl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_M3U_PLAYLIST,m3upl,64):m3upl); +} +/* ----------------------------------------------------- */ + +const wchar_t *PLSHandler::enumExtensions(size_t n) +{ + switch(n) + { + case 0: + return L"PLS"; + default: + return 0; + } +} + +int PLSHandler::SupportedFilename(const wchar_t *filename) +{ + wchar_t ext[16] = {0}; + GetExtension(filename, ext, 16); + + if (!lstrcmpiW(ext, L".PLS")) + return SVC_PLAYLISTHANDLER_SUCCESS; + + // TODO: open file and sniff it for file signature + return SVC_PLAYLISTHANDLER_FAILED; +} + +ifc_playlistloader *PLSHandler::CreateLoader(const wchar_t *filename) +{ + return new PLSLoader; +} + +void PLSHandler::ReleaseLoader(ifc_playlistloader *loader) +{ + PLSLoader *pls; + + pls = static_cast<PLSLoader *>(loader); + delete pls; +} + + +const wchar_t *PLSHandler::GetName() +{ + static wchar_t plspl[64]; + // no point re-loading this all of the time since it won't change once we've been loaded + return (!plspl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_PLS_PLAYLIST,plspl,64):plspl); +} + +/* --- B4S --- */ +const wchar_t *B4SHandler::enumExtensions(size_t n) +{ + switch(n) + { + case 0: + return L"B4S"; + //case 1: + //return L"BPL"; + default: + return 0; + } +} + +int B4SHandler::SupportedFilename(const wchar_t *filename) +{ + wchar_t ext[16] = {0}; + GetExtension(filename, ext, 16); + + if (!lstrcmpiW(ext, L".B4S")) + return SVC_PLAYLISTHANDLER_SUCCESS; + if (!lstrcmpiW(ext, L".BPL")) + return SVC_PLAYLISTHANDLER_SUCCESS; + + // TODO: open file and sniff it for file signature + return SVC_PLAYLISTHANDLER_FAILED; +} + +ifc_playlistloader *B4SHandler::CreateLoader(const wchar_t *filename) +{ + return new B4SLoader; +} + +void B4SHandler::ReleaseLoader(ifc_playlistloader *loader) +{ + B4SLoader *pls; + + pls = static_cast<B4SLoader *>(loader); + delete pls; +} + +const wchar_t *B4SHandler::GetName() +{ + static wchar_t wa3pl[64]; + // no point re-loading this all of the time since it won't change once we've been loaded + return (!wa3pl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_WINAMP3_PLAYLIST,wa3pl,64):wa3pl); +} + + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS M3UHandler +START_DISPATCH; +CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, enumExtensions) +CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename) +CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader) +VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader) +CB(SVC_PLAYLISTHANDLER_GETNAME, GetName) +CB(SVC_PLAYLISTHANDLER_HASWRITER, HasWriter) +END_DISPATCH; +#undef CBCLASS + +#define CBCLASS PLSHandler +START_DISPATCH; +CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, enumExtensions) +CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename) +CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader) +VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader) +CB(SVC_PLAYLISTHANDLER_GETNAME, GetName) +CB(SVC_PLAYLISTHANDLER_HASWRITER, HasWriter) +END_DISPATCH; +#undef CBCLASS + +#define CBCLASS B4SHandler +START_DISPATCH; +CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, enumExtensions) +CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename) +CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader) +VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader) +CB(SVC_PLAYLISTHANDLER_GETNAME, GetName) +CB(SVC_PLAYLISTHANDLER_HASWRITER, HasWriter) +END_DISPATCH;
\ No newline at end of file diff --git a/Src/playlist/Handler.h b/Src/playlist/Handler.h new file mode 100644 index 00000000..aabd8a7d --- /dev/null +++ b/Src/playlist/Handler.h @@ -0,0 +1,32 @@ +#ifndef NULLSOFT_PLAYLISTS_HANDLER_H +#define NULLSOFT_PLAYLISTS_HANDLER_H + +#include "svc_playlisthandler.h" +#include <bfc/platform/types.h> + +#define DECLARE_HANDLER(className, IS_WRITER) class className ## Handler : public svc_playlisthandler {\ +public:\ + const wchar_t *enumExtensions(size_t n); \ + int SupportedFilename(const wchar_t *filename); \ + ifc_playlistloader *CreateLoader(const wchar_t *filename);\ + void ReleaseLoader(ifc_playlistloader *loader);\ + const wchar_t *GetName(); \ + int HasWriter() { return IS_WRITER; }\ +protected: RECVS_DISPATCH;} + +DECLARE_HANDLER(M3U, 1); +DECLARE_HANDLER(PLS, 1); +DECLARE_HANDLER(B4S, 0); + +// {8D031378-4209-4bfe-AC94-03C57C896214} +static const GUID m3uHandlerGUID = +{ 0x8d031378, 0x4209, 0x4bfe, { 0xac, 0x94, 0x3, 0xc5, 0x7c, 0x89, 0x62, 0x14 } }; + +// {4FF33CC0-F82C-4550-8E4A-DDF90036BE85} +static const GUID plsHandlerGUID = +{ 0x4ff33cc0, 0xf82c, 0x4550, { 0x8e, 0x4a, 0xdd, 0xf9, 0x0, 0x36, 0xbe, 0x85 } }; + +static const GUID b4sHandlerGUID = +{ 0x6f62cbb8, 0x7e1f, 0x43eb, { 0xb3, 0xf6, 0x1, 0xc2, 0x60, 0x10, 0x29, 0xa3 } }; + +#endif
\ No newline at end of file diff --git a/Src/playlist/JSAPI2_Creator.cpp b/Src/playlist/JSAPI2_Creator.cpp new file mode 100644 index 00000000..c621f07b --- /dev/null +++ b/Src/playlist/JSAPI2_Creator.cpp @@ -0,0 +1,102 @@ +#include "JSAPI2_Creator.h" +#include "JSAPI2_Playlists.h" +#include "api__playlist.h" +#include "main.h" +#include "resource.h" + +IDispatch *JSAPI2_Creator::CreateAPI(const wchar_t *name, const wchar_t *key, JSAPI::ifc_info *info) +{ + if (!wcscmp(name, L"Playlists")) + return new JSAPI2::PlaylistsAPI(key, info); + else + return 0; +} + +int JSAPI2_Creator::PromptForAuthorization(HWND parent, const wchar_t *group, const wchar_t *action, const wchar_t *authorization_key, JSAPI2::api_security::AuthorizationData *data) +{ + if (group && !wcscmp(group, L"playlists")) + { + const wchar_t *title_str = AGAVE_API_JSAPI2_SECURITY->GetAssociatedName(authorization_key); + if (action && !wcscmp(action, L"read")) + { + return AGAVE_API_JSAPI2_SECURITY->SecurityPrompt(parent, title_str, L"This service is requesting access to the playlists in your Library.", 0); + } + else if (action && !wcscmp(action, L"write")) + { + return AGAVE_API_JSAPI2_SECURITY->SecurityPrompt(parent, title_str, L"This service is trying modify one of the playlists in your Library.", 0); + } + } + + return JSAPI2::svc_apicreator::AUTHORIZATION_UNDEFINED; +} + +#define CBCLASS JSAPI2_Creator +START_DISPATCH; +CB(JSAPI2_SVC_APICREATOR_CREATEAPI, CreateAPI); +CB(JSAPI2_SVC_APICREATOR_PROMPTFORAUTHORIZATION, PromptForAuthorization); +END_DISPATCH; +#undef CBCLASS + +static JSAPI2_Creator jsapi2_svc; +static const char serviceName[] = "Playlist Javascript Objects"; + +// {CF057176-A819-4bc5-8723-6C072BB28BAA} +static const GUID jsapi2_factory_guid = +{ 0xcf057176, 0xa819, 0x4bc5, { 0x87, 0x23, 0x6c, 0x7, 0x2b, 0xb2, 0x8b, 0xaa } }; + + +FOURCC JSAPI2Factory::GetServiceType() +{ + return jsapi2_svc.getServiceType(); +} + +const char *JSAPI2Factory::GetServiceName() +{ + return serviceName; +} + +GUID JSAPI2Factory::GetGUID() +{ + return jsapi2_factory_guid; +} + +void *JSAPI2Factory::GetInterface(int global_lock) +{ + // if (global_lock) + // WASABI_API_SVC->service_lock(this, (void *)ifc); + return &jsapi2_svc; +} + +int JSAPI2Factory::SupportNonLockingInterface() +{ + return 1; +} + +int JSAPI2Factory::ReleaseInterface(void *ifc) +{ + //WASABI_API_SVC->service_unlock(ifc); + return 1; +} + +const char *JSAPI2Factory::GetTestString() +{ + return 0; +} + +int JSAPI2Factory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#define CBCLASS JSAPI2Factory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/JSAPI2_Creator.h b/Src/playlist/JSAPI2_Creator.h new file mode 100644 index 00000000..fd85a3f0 --- /dev/null +++ b/Src/playlist/JSAPI2_Creator.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../Winamp/JSAPI2_svc_apicreator.h" + +#include <api/service/waservicefactory.h> +#include <api/service/services.h> + +class JSAPI2Factory : public waServiceFactory +{ +public: + FOURCC GetServiceType(); + const char *GetServiceName(); + GUID GetGUID(); + void *GetInterface(int global_lock); + int SupportNonLockingInterface(); + int ReleaseInterface(void *ifc); + const char *GetTestString(); + int ServiceNotify(int msg, int param1, int param2); + +protected: + RECVS_DISPATCH; +}; + + +class JSAPI2_Creator : public JSAPI2::svc_apicreator +{ + IDispatch *CreateAPI(const wchar_t *name, const wchar_t *key, JSAPI::ifc_info *info); + int PromptForAuthorization(HWND parent, const wchar_t *group, const wchar_t *action, const wchar_t *authorization_key, JSAPI2::api_security::AuthorizationData *data); +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/playlist/JSAPI2_Playlist.cpp b/Src/playlist/JSAPI2_Playlist.cpp new file mode 100644 index 00000000..924c4d70 --- /dev/null +++ b/Src/playlist/JSAPI2_Playlist.cpp @@ -0,0 +1,328 @@ +#include "JSAPI2_Playlist.h" +#include "../Winamp/JSAPI.h" +#include "api__playlist.h" +#include "PlaylistManager.h" + +JSAPI2::PlaylistObject::PlaylistObject(const wchar_t *_key) +{ + key = _key; +} + +#define DISP_TABLE \ + CHECK_ID(Clear)\ + CHECK_ID(AppendURL)\ + CHECK_ID(GetItemFilename)\ + CHECK_ID(GetItemTitle)\ + CHECK_ID(GetItemLength)\ + CHECK_ID(GetItemExtendedInfo)\ + CHECK_ID(Reverse)\ + CHECK_ID(SwapItems)\ + CHECK_ID(Randomize)\ + CHECK_ID(RemoveItem)\ + CHECK_ID(SortByTitle)\ + CHECK_ID(SortByFilename)\ + CHECK_ID(SetItemFilename)\ + CHECK_ID(SetItemTitle)\ + CHECK_ID(SetItemLength)\ + CHECK_ID(InsertURL)\ + CHECK_ID(numitems)\ + + +#define CHECK_ID(str) JSAPI_DISP_ENUMIFY(str), +enum { + DISP_TABLE +}; + +#undef CHECK_ID +#define CHECK_ID(str) if (wcscmp(rgszNames[i], L## #str) == 0) { rgdispid[i] = JSAPI_DISP_ENUMIFY(str); continue; } +HRESULT JSAPI2::PlaylistObject::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + DISP_TABLE + + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::PlaylistObject::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::PlaylistObject::Clear(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.Clear(); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::AppendURL(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 1, 3); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_BSTR, puArgErr); + JSAPI_VERIFY_PARAMTYPE_OPTIONAL(pdispparams, 2, VT_BSTR, puArgErr); + JSAPI_VERIFY_PARAMTYPE_OPTIONAL(pdispparams, 3, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + const wchar_t *filename = JSAPI_PARAM(pdispparams, 1).bstrVal; + const wchar_t *title = JSAPI_PARAM_OPTIONAL(pdispparams, 2, bstrVal, 0); + int length = JSAPI_PARAM_OPTIONAL(pdispparams, 3, lVal, -1); + playlist.AppendWithInfo(filename, title, length); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::InsertURL(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 2, 4); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 2, VT_BSTR, puArgErr); + JSAPI_VERIFY_PARAMTYPE_OPTIONAL(pdispparams, 3, VT_BSTR, puArgErr); + JSAPI_VERIFY_PARAMTYPE_OPTIONAL(pdispparams, 4, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + const wchar_t *filename = JSAPI_PARAM(pdispparams, 2).bstrVal; + const wchar_t *title = JSAPI_PARAM_OPTIONAL(pdispparams, 3, bstrVal, 0); + int length = JSAPI_PARAM_OPTIONAL(pdispparams, 4, lVal, -1); + playlist.Insert(JSAPI_PARAM(pdispparams, 1).lVal, filename, title, length); + return S_OK; +} + + +HRESULT JSAPI2::PlaylistObject::GetItemFilename(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BSTR); + JSAPI_SET_RESULT(pvarResult, bstrVal, SysAllocString(playlist.ItemName(JSAPI_PARAM(pdispparams, 1).lVal))); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::GetItemTitle(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BSTR); + JSAPI_SET_RESULT(pvarResult, bstrVal, SysAllocString(playlist.ItemTitle(JSAPI_PARAM(pdispparams, 1).lVal))); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::GetItemLength(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_I4); + JSAPI_SET_RESULT(pvarResult, lVal, playlist.GetItemLengthMilliseconds(JSAPI_PARAM(pdispparams, 1).lVal)); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::GetItemExtendedInfo(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 2); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 2, VT_BSTR, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BSTR); + wchar_t metadata[1024]=L""; + playlist.GetItemExtendedInfo(JSAPI_PARAM(pdispparams, 1).lVal, JSAPI_PARAM(pdispparams, 2).bstrVal, metadata, sizeof(metadata)/sizeof(*metadata)); + JSAPI_SET_RESULT(pvarResult, bstrVal, SysAllocString(metadata)); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::Reverse(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.Reverse(); + return S_OK; +} + + +HRESULT JSAPI2::PlaylistObject::SwapItems(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 2); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 2, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.Swap(JSAPI_PARAM(pdispparams, 1).lVal, JSAPI_PARAM(pdispparams, 2).lVal); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::Randomize(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlistManager.Randomize(&playlist); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::RemoveItem(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.Remove(JSAPI_PARAM(pdispparams, 1).lVal); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::SortByTitle(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.SortByTitle(); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::SortByFilename(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.SortByFilename(); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::SetItemFilename(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 2); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 2, VT_BSTR, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.SetItemFilename(JSAPI_PARAM(pdispparams, 1).lVal, JSAPI_PARAM(pdispparams, 2).bstrVal); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::SetItemTitle(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 2); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 2, VT_BSTR, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.SetItemTitle(JSAPI_PARAM(pdispparams, 1).lVal, JSAPI_PARAM(pdispparams, 2).bstrVal); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::SetItemLength(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 2); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_I4, puArgErr); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 2, VT_I4, puArgErr); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE); + playlist.SetItemLengthMilliseconds(JSAPI_PARAM(pdispparams, 1).lVal, JSAPI_PARAM(pdispparams, 2).lVal); + return S_OK; +} + +HRESULT JSAPI2::PlaylistObject::numitems(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ +// JSAPI_VERIFY_METHOD(wFlags); +// JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0); + if (wFlags & DISPATCH_PROPERTYGET) + { + JSAPI_INIT_RESULT(pvarResult, VT_I4); + JSAPI_SET_RESULT(pvarResult, lVal, (LONG)playlist.GetNumItems()); + return S_OK; + } + else + return DISP_E_MEMBERNOTFOUND; +} + + +#undef CHECK_ID +#define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): return str(wFlags, pdispparams, pvarResult, puArgErr); +HRESULT JSAPI2::PlaylistObject::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch (dispid) + { + DISP_TABLE + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP JSAPI2::PlaylistObject::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else if (IsEqualIID(riid, IID_PlaylistObject)) + *ppvObject = (PlaylistObject *)this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG JSAPI2::PlaylistObject::AddRef(void) +{ + return this->_refCount.fetch_add( 1 ); +} + + +ULONG JSAPI2::PlaylistObject::Release( void ) +{ + std::size_t l_Ref = this->_refCount.fetch_sub( 1 ); + if ( l_Ref == 0 ) + delete this; + + return l_Ref; +} diff --git a/Src/playlist/JSAPI2_Playlist.h b/Src/playlist/JSAPI2_Playlist.h new file mode 100644 index 00000000..c6f946b7 --- /dev/null +++ b/Src/playlist/JSAPI2_Playlist.h @@ -0,0 +1,51 @@ +#pragma once + +#include <ocidl.h> +#include <atomic> + +#include "Playlist.h" + +namespace JSAPI2 +{ + // {8535DB01-7630-45df-9429-2640A29B9468} + static const GUID IID_PlaylistObject = + { 0x8535db01, 0x7630, 0x45df, { 0x94, 0x29, 0x26, 0x40, 0xa2, 0x9b, 0x94, 0x68 } }; + + + class PlaylistObject : public IDispatch + { + public: + PlaylistObject( const wchar_t *_key ); + STDMETHOD( QueryInterface )( REFIID riid, PVOID *ppvObject ); + STDMETHOD_( ULONG, AddRef )( void ); + STDMETHOD_( ULONG, Release )( void ); + // *** IDispatch Methods *** + STDMETHOD( GetIDsOfNames )( REFIID riid, OLECHAR FAR *FAR *rgszNames, unsigned int cNames, LCID lcid, DISPID FAR *rgdispid ); + STDMETHOD( GetTypeInfo )( unsigned int itinfo, LCID lcid, ITypeInfo FAR *FAR *pptinfo ); + STDMETHOD( GetTypeInfoCount )( unsigned int FAR *pctinfo ); + STDMETHOD( Invoke )( DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR *pexecinfo, unsigned int FAR *puArgErr ); + + Playlist playlist; + private: + const wchar_t *key; + volatile std::atomic<std::size_t> _refCount = 1; + + STDMETHOD( Clear )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( AppendURL )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( GetItemFilename )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( GetItemTitle )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( GetItemLength )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( GetItemExtendedInfo )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( Reverse )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( SwapItems )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( Randomize )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( RemoveItem )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( SortByTitle )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( SortByFilename )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( SetItemFilename )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( SetItemTitle )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( SetItemLength )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( InsertURL )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + STDMETHOD( numitems )( WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr ); + }; +} diff --git a/Src/playlist/JSAPI2_Playlists.cpp b/Src/playlist/JSAPI2_Playlists.cpp new file mode 100644 index 00000000..c01a87f2 --- /dev/null +++ b/Src/playlist/JSAPI2_Playlists.cpp @@ -0,0 +1,221 @@ +#include "JSAPI2_Playlists.h" +#include "../Winamp/JSAPI.h" +#include "api__playlist.h" +#include "../Winamp/JSAPI_ObjectArray.h" +#include "Playlists.h" +#include "JSAPI2_Playlist.h" +#include "PlaylistManager.h" +#include "../Winamp/JSAPI_CallbackParameters.h" + +extern Playlists playlists; + +JSAPI2::PlaylistsAPI::PlaylistsAPI(const wchar_t *_key, JSAPI::ifc_info *_info) +{ + info = _info; + key = _key; +} + +#define DISP_TABLE \ + CHECK_ID(GetPlaylists)\ + CHECK_ID(OpenPlaylist)\ + CHECK_ID(SavePlaylist)\ + +#define CHECK_ID(str) JSAPI_DISP_ENUMIFY(str), +enum { + DISP_TABLE +}; + +#undef CHECK_ID +#define CHECK_ID(str) if (wcscmp(rgszNames[i], L## #str) == 0) { rgdispid[i] = JSAPI_DISP_ENUMIFY(str); continue; } +HRESULT JSAPI2::PlaylistsAPI::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + DISP_TABLE + + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT JSAPI2::PlaylistsAPI::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::PlaylistsAPI::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::PlaylistsAPI::GetPlaylists(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0); + + VariantInit(pvarResult); + V_VT(pvarResult) = VT_DISPATCH; + if (AGAVE_API_JSAPI2_SECURITY->GetActionAuthorization(L"playlists", L"read", key, info, JSAPI2::api_security::ACTION_PROMPT) == JSAPI2::api_security::ACTION_ALLOWED) + { + JSAPI::ObjectArray *objectArray = new JSAPI::ObjectArray; + playlists.Lock(); + + size_t count = playlists.GetCount(); + + for (size_t i=0;i!=count;i++) + { + const PlaylistInfo &info = playlists.GetPlaylistInfo(i); + JSAPI::CallbackParameters *playlistParams = new JSAPI::CallbackParameters; + playlistParams->AddString(L"filename", info.filename); + playlistParams->AddString(L"title", info.title); + wchar_t guid_str[40]; + nsGUID::toCharW(info.guid, guid_str); + playlistParams->AddString(L"playlistId", guid_str); + playlistParams->AddLong(L"length", info.length); + playlistParams->AddLong(L"numitems", info.numItems); + objectArray->AddObject(playlistParams); + playlistParams->Release(); + } + playlists.Unlock(); + V_DISPATCH(pvarResult) = objectArray; + return S_OK; + } + else + { + V_DISPATCH(pvarResult) = 0; + return S_OK; + } + + return E_FAIL; +} + +HRESULT JSAPI2::PlaylistsAPI::OpenPlaylist(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_BSTR, puArgErr); + + VariantInit(pvarResult); + V_VT(pvarResult) = VT_DISPATCH; + V_DISPATCH(pvarResult) = 0; + if (AGAVE_API_JSAPI2_SECURITY->GetActionAuthorization(L"playlists", L"read", key, info, JSAPI2::api_security::ACTION_PROMPT) == JSAPI2::api_security::ACTION_ALLOWED) + { + GUID playlist_guid = nsGUID::fromCharW(JSAPI_PARAM(pdispparams, 1).bstrVal); + playlists.Lock(); + size_t index; + if (playlists.GetPosition(playlist_guid, &index) == API_PLAYLISTS_SUCCESS) + { + const wchar_t *filename = playlists.GetFilename(index); + if (filename) + { + PlaylistObject *playlist = new PlaylistObject(key); + if (playlistManager.Load(filename, &playlist->playlist) == PLAYLISTMANAGER_SUCCESS) + { + V_DISPATCH(pvarResult) = playlist; + } + else + { + delete playlist; + } + } + } + playlists.Unlock(); + return S_OK; + + } + else + { + return S_OK; + } + + return E_FAIL; +} + +HRESULT JSAPI2::PlaylistsAPI::SavePlaylist(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 2); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_BSTR, puArgErr); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 2, VT_DISPATCH, puArgErr); + + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = FALSE; + if (AGAVE_API_JSAPI2_SECURITY->GetActionAuthorization(L"playlists", L"write", key, info, JSAPI2::api_security::ACTION_PROMPT) == JSAPI2::api_security::ACTION_ALLOWED) + { + GUID playlist_guid = nsGUID::fromCharW(JSAPI_PARAM(pdispparams, 1).bstrVal); + playlists.Lock(); + size_t index; + if (playlists.GetPosition(playlist_guid, &index) == API_PLAYLISTS_SUCCESS) + { + const wchar_t *filename = playlists.GetFilename(index); + if (filename) + { + IDispatch *dispPlaylist = JSAPI_PARAM(pdispparams, 2).pdispVal; + PlaylistObject *playlist = 0; + dispPlaylist->QueryInterface(JSAPI2::IID_PlaylistObject, (void **)&playlist); + if (playlistManager.Save(filename, &(playlist->playlist)) == PLAYLISTMANAGER_SUCCESS) + V_BOOL(pvarResult) = TRUE; + } + } + playlists.Unlock(); + return S_OK; + + } + else + { + return S_OK; + } + + return E_FAIL; +} + +#undef CHECK_ID +#define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): return str(wFlags, pdispparams, pvarResult, puArgErr); +HRESULT JSAPI2::PlaylistsAPI::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch (dispid) + { + DISP_TABLE + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP JSAPI2::PlaylistsAPI::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG JSAPI2::PlaylistsAPI::AddRef(void) +{ + return this->_refCount.fetch_add( 1 ); +} + +ULONG JSAPI2::PlaylistsAPI::Release( void ) +{ + std::size_t l_Ref = this->_refCount.fetch_sub( 1 ); + if ( l_Ref == 0 ) + delete this; + + return l_Ref; +} diff --git a/Src/playlist/JSAPI2_Playlists.h b/Src/playlist/JSAPI2_Playlists.h new file mode 100644 index 00000000..0afd48f2 --- /dev/null +++ b/Src/playlist/JSAPI2_Playlists.h @@ -0,0 +1,30 @@ +#pragma once + +#include <ocidl.h> +#include <atomic> +#include "../Winamp/JSAPI_Info.h" + +namespace JSAPI2 +{ + class PlaylistsAPI : public IDispatch + { + public: + PlaylistsAPI(const wchar_t *_key, JSAPI::ifc_info *info); + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); + private: + const wchar_t *key; + volatile std::atomic<std::size_t> _refCount = 1; + JSAPI::ifc_info *info; + + STDMETHOD (GetPlaylists)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr); + STDMETHOD (OpenPlaylist)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr); + STDMETHOD (SavePlaylist)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr); + }; +} diff --git a/Src/playlist/M3U8Writer.cpp b/Src/playlist/M3U8Writer.cpp new file mode 100644 index 00000000..72972216 --- /dev/null +++ b/Src/playlist/M3U8Writer.cpp @@ -0,0 +1,39 @@ +#include "M3U8Writer.h" +#include "../nu/AutoChar.h" +#include <bfc/platform/types.h> + +M3U8Writer::M3U8Writer() : fp(0) +{ +} + +int M3U8Writer::Open(const wchar_t *filename) +{ + fp = _wfopen(filename, L"wt"); + if (!fp) + return 0; + + fputs( "\xEF\xBB\xBF", fp ); + fprintf(fp,"#EXTM3U\n"); + + return 1; +} + +void M3U8Writer::Write(const wchar_t *filename) +{ + fwprintf(fp,L"%s\n", filename); +} + +void M3U8Writer::Write(const wchar_t *filename, const wchar_t *title, int length) +{ + fprintf( fp, "#EXTINF:%d,%s\n%s\n", length, (char *) AutoChar( title, CP_UTF8 ), (char *) AutoChar( filename, CP_UTF8 ) ); +} + +void M3U8Writer::Write( const wchar_t *p_filename, const wchar_t *p_title, const wchar_t *p_extended_infos, int p_length ) +{ + fprintf( fp, "#EXTINF:%d %s,%s\n%s\n", p_length, (char *)AutoChar( p_extended_infos, CP_UTF8 ), (char *)AutoChar( p_title, CP_UTF8 ), (char *)AutoChar( p_filename, CP_UTF8 ) ); +} + +void M3U8Writer::Close() +{ + fclose(fp); +}
\ No newline at end of file diff --git a/Src/playlist/M3U8Writer.h b/Src/playlist/M3U8Writer.h new file mode 100644 index 00000000..7f251ef6 --- /dev/null +++ b/Src/playlist/M3U8Writer.h @@ -0,0 +1,21 @@ +#ifndef NULLSOFT_M3U8WRITERH +#define NULLSOFT_M3U8WRITERH + +#include <stdio.h> +#include "PlaylistWriter.h" +class M3U8Writer : public PlaylistWriter +{ +public: + M3U8Writer(); + + int Open( const wchar_t *filename ) override; + void Write( const wchar_t *filename ) override; + void Write( const wchar_t *filename, const wchar_t *title, int length ) override; + void Write( const wchar_t *p_filename, const wchar_t *p_title, const wchar_t* p_extended_infos, int p_length ) override; + void Close() override; + +private: + FILE *fp; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/M3ULoader.cpp b/Src/playlist/M3ULoader.cpp new file mode 100644 index 00000000..ebab35da --- /dev/null +++ b/Src/playlist/M3ULoader.cpp @@ -0,0 +1,466 @@ +#include <stdio.h> +#include <shlwapi.h> +#include <strsafe.h> +#include <fstream> +#include <string> + +#include "M3ULoader.h" +#include "../nu/ns_wc.h" + +#include "../WAT/WAT.h" + + +M3ULoader::M3ULoader() : _utf8( false ) +{ + wideTitle[ 0 ] = wideFilename[ 0 ] = 0; +} + +M3ULoader::~M3ULoader( void ) +{ + //Close(); +} + +struct cmpWchar_t { + bool operator()(const wchar_t* a, const wchar_t* b) const { + return wcscmp(a, b) < 0; + } +}; + +class M3UInfo : public ifc_plentryinfo +{ +public: + M3UInfo() {} + M3UInfo( wchar_t *_mediahash, wchar_t *_metahash, wchar_t *_cloud_id, wchar_t *_cloud_status, wchar_t *_cloud_devices ) + { + _extended_infos.emplace( _wcsdup( _INFO_NAME_MEDIA_HASH ), _wcsdup( _mediahash) ); + _extended_infos.emplace( _wcsdup( _INFO_NAME_META_HASH ), _wcsdup( _metahash ) ); + + _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_ID ), _wcsdup( _cloud_id ) ); + _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_STATUS ), _wcsdup( _cloud_status ) ); + _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_DEVICES ), _wcsdup( _cloud_devices ) ); + } + + ~M3UInfo() + { + for ( auto l_extended_infos_iterator = _extended_infos.begin(); l_extended_infos_iterator != _extended_infos.end(); ++l_extended_infos_iterator ) + { + free( ( *l_extended_infos_iterator ).first ); + free( ( *l_extended_infos_iterator ).second ); + } + + _extended_infos.clear(); + } + + void SetExtendedInfo( const wchar_t *p_parameter_name, const wchar_t *p_parameter_value ) + { + _extended_infos.emplace( _wcsdup( p_parameter_name ), _wcsdup( p_parameter_value ) ); + } + + const wchar_t *GetExtendedInfo( wchar_t *parameter ) + { + //for ( auto l_extended_infos_iterator = _extended_infos.begin(); l_extended_infos_iterator != _extended_infos.end(); ++l_extended_infos_iterator ) + //{ + // wchar_t *l_key = _wcsdup( ( *l_extended_infos_iterator ).first ); + // if ( wcscmp( l_key, parameter ) == 0 ) + // return _wcsdup( ( *l_extended_infos_iterator ).second ); + //} + + // OLD + //std::map<wchar_t *, wchar_t *>::iterator l_extended_infos_iterator = _extended_infos.find( parameter ); + + //if ( l_extended_infos_iterator != _extended_infos.end() ) + // return _wcsdup( ( *l_extended_infos_iterator ).second ); + + auto it = _extended_infos.find(parameter); + if (_extended_infos.end() != it) + { + return it->second; + } + + return 0; + } + +private: + RECVS_DISPATCH; + + std::map<wchar_t *, wchar_t *, cmpWchar_t> _extended_infos; +}; + +#define CBCLASS M3UInfo +START_DISPATCH; +CB( IFC_PLENTRYINFO_GETEXTENDEDINFO, GetExtendedInfo ) +END_DISPATCH; +#undef CBCLASS + + +int M3ULoader::OnFileHelper( ifc_playlistloadercallback *playlist, const wchar_t *trackName, const wchar_t *title, int length, const wchar_t *rootPath, ifc_plentryinfo *extraInfo ) +{ + if ( length == -1000 ) + length = -1; + + wcsncpy( wideFilename, trackName, FILENAME_SIZE ); + + int ret; + + if ( wcsstr( wideFilename, L"://" ) || PathIsRootW( wideFilename ) ) + { + ret = playlist->OnFile( wideFilename, title, length, extraInfo ); + } + else + { + wchar_t fullPath[ MAX_PATH ] = { 0 }; + if ( PathCombineW( fullPath, rootPath, wideFilename ) ) + { + wchar_t canonicalizedPath[ MAX_PATH ] = { 0 }; + PathCanonicalizeW( canonicalizedPath, fullPath ); + ret = playlist->OnFile( canonicalizedPath, title, length, extraInfo ); + } + else + { + ret = ifc_playlistloadercallback::LOAD_CONTINUE; + } + } + + return ret; +} + +static bool StringEnds( const wchar_t *a, const wchar_t *b ) +{ + size_t aLen = wcslen( a ); + size_t bLen = wcslen( b ); + + if ( aLen < bLen ) + return false; // too short + + if ( !_wcsicmp( a + aLen - bLen, b ) ) + return true; + + return false; +} + +int M3ULoader::Load( const wchar_t *p_filename, ifc_playlistloadercallback *playlist ) +{ + // TODO: download temp file if it's a URL + // TODO - WDP2-198 + + FILE *fp = _wfopen( p_filename, L"rt,ccs=UNICODE" ); + if ( !fp ) + return IFC_PLAYLISTLOADER_FAILED; + + fseek( fp, 0, SEEK_END ); + int size = ftell( fp ); + fseek( fp, 0, SEEK_SET ); + + if ( size == -1 ) + { + fclose( fp ); + fp = 0; + + return IFC_PLAYLISTLOADER_FAILED; + } + + if ( StringEnds( p_filename, L".m3u8" ) ) + _utf8 = true; + + int ext = 0; + + wchar_t *p; + + const int l_linebuf_size = 2048; + wchar_t linebuf[ l_linebuf_size ] = { 0 }; + + wchar_t ext_title[ MAX_PATH ] = { 0 }; + + wchar_t ext_mediahash[ 128 ] = { 0 }; + wchar_t ext_metahash[ 128 ] = { 0 }; + + wchar_t ext_cloud_id[ 128 ] = { 0 }; + wchar_t ext_cloud_status[ 16 ] = { 0 }; + wchar_t ext_cloud_devices[ 128 ] = { 0 }; + + int ext_len = -1; + + wchar_t rootPath[ MAX_PATH ] = { 0 }; + const wchar_t *callbackPath = playlist->GetBasePath(); + if ( callbackPath ) + StringCchCopyW( rootPath, MAX_PATH, callbackPath ); + else + { + StringCchCopyW( rootPath, MAX_PATH, p_filename ); + PathRemoveFileSpecW( rootPath ); + } + + unsigned char BOM[ 3 ] = { 0, 0, 0 }; + if ( fread( BOM, 3, 1, fp ) == 1 && BOM[ 0 ] == 0xEF && BOM[ 1 ] == 0xBB && BOM[ 2 ] == 0xBF ) + _utf8 = true; + else + fseek( fp, 0, SEEK_SET ); + + + std::wstring l_separator = L"\" "; + std::wstring l_key_separator = L"="; + + + const wchar_t _ASF[] = L"ASF "; + const wchar_t _DIRECTIVE_EXTINF[] = L"#EXTINF:"; + const wchar_t _DIRECTIVE_EXTM3U[] = L"#EXTM3U"; + const wchar_t _DIRECTIVE_EXT_X_NS_CLOUD[] = L"#EXT-X-NS-CLOUD:"; + const wchar_t _DIRECTIVE_UTF8[] = L"#UTF8"; + const wchar_t _END_LINE[] = L"\r\n"; + + const int l_move_size = sizeof( wchar_t ); + + + wa::strings::wa_string l_key_value_pair = ""; + wa::strings::wa_string l_key = ""; + wa::strings::wa_string l_value = ""; + + std::map<std::wstring, std::wstring> l_extended_infos; + + while ( 1 ) + { + if ( feof( fp ) ) + break; + + linebuf[ 0 ] = 0; + fgetws( linebuf, l_linebuf_size - 1, fp ); + + linebuf[ wcscspn( linebuf, _END_LINE ) ] = 0; + if ( wcslen( linebuf ) == 0 ) + continue; + + if ( ext == 0 && wcsstr( linebuf, _DIRECTIVE_EXTM3U ) ) + { + ext = 1; + + continue; + } + + if ( !wcsncmp( linebuf, _DIRECTIVE_UTF8, 5 ) ) + { + _utf8 = true; + + continue; + } + + p = linebuf; + + while ( p && *p == ' ' || *p == '\t' ) + p = CharNextW( p ); + + if ( *p != '#' && *p != '\n' && *p != '\r' && *p ) + { + wchar_t buf[ 4096 ] = { 0 }; + + wchar_t *p2 = CharPrevW( linebuf, linebuf + wcslen( linebuf ) ); //GetLastCharacter(linebuf); + if ( p2 && *p2 == '\n' ) + *p2 = 0; + + if ( !wcsncmp( p, _ASF, 4 ) && wcslen( p ) > 4 ) + p += 4; + + if ( wcsncmp( p, L"\\\\", 2 ) && wcsncmp( p + 1, L":\\", 2 ) && wcsncmp( p + 1, L":/", 2 ) && !wcsstr( p, L"://" ) ) + { + if ( p[ 0 ] == '\\' ) + { + buf[ 0 ] = rootPath[ 0 ]; + buf[ 1 ] = rootPath[ 1 ]; + + StringCchCopyW( buf + 2, 4093, p ); + + //buf[ wcslen( buf ) - 1 ] = 0; + buf[ wcscspn( buf, _END_LINE ) ] = 0; + p = buf; + } + } + + int ret; + + // generate extra info from the cloud specific values (if present) + M3UInfo info( ext_mediahash, ext_metahash, ext_cloud_id, ext_cloud_status, ext_cloud_devices ); + + + if ( !l_extended_infos.empty() ) + { + for ( auto l_extended_infos_iterator = l_extended_infos.begin(); l_extended_infos_iterator != l_extended_infos.end(); ++l_extended_infos_iterator ) + { + info.SetExtendedInfo( ( *l_extended_infos_iterator ).first.c_str(), ( *l_extended_infos_iterator ).second.c_str() ); + } + } + + l_extended_infos.clear(); + + if ( ext_title[ 0 ] ) + { + wcsncpy( wideTitle, ext_title, FILETITLE_SIZE ); + ret = OnFileHelper( playlist, p, wideTitle, ext_len * 1000, rootPath, &info ); + } + else + { + ret = OnFileHelper( playlist, p, 0, -1, rootPath, &info ); + } + + if ( ret != ifc_playlistloadercallback::LOAD_CONTINUE ) + break; + + ext_len = -1; + ext_title[ 0 ] = 0; + } + else + { + if ( ext && !wcsncmp( p, _DIRECTIVE_EXTINF, 8 ) ) + { + p += 8; + ext_len = _wtoi( p ); + + int l_track_length = ext_len; + int l_digits = ( l_track_length < 0 ? 1 : 0 ); + while ( l_track_length ) + { + l_track_length /= 10; + ++l_digits; + } + + p += l_digits; + + + if ( p && *p ) + { + wchar_t *p2 = CharPrevW( p, p + wcslen( p ) ); // GetLastCharacter(p); + if ( p2 && *p2 == '\n' ) + *p2 = 0; + + while ( p && *p == ' ' ) + p = CharNextW( p ); + + std::wstring l_string( p ); + + int l_pos = l_string.find_first_of( L"," ); + + if ( l_pos > 0 ) + { + int l_key_separator_pos = 0; + + wa::strings::wa_string l_line_trail( l_string.substr( 0, l_pos ) ); + + while ( !l_line_trail.empty() ) + { + int l_separator_pos = l_line_trail.find( l_separator ); + + if ( l_separator_pos > 0 ) + l_key_value_pair = l_line_trail.mid( 0, l_separator_pos + 1 ); + else + l_key_value_pair = l_line_trail; + + + l_key_separator_pos = l_key_value_pair.find( l_key_separator ); + + l_key = l_key_value_pair.mid( 0, l_key_separator_pos ); + l_value = l_key_value_pair.mid( l_key_separator_pos + 1, l_key_value_pair.lengthS() - l_key_separator_pos + 1 ); + + l_value.replaceAll( "\"", "" ); + + l_extended_infos.emplace( l_key.GetW(), l_value.GetW() ); + + if ( l_separator_pos > 0 ) + l_line_trail = l_line_trail.mid( l_separator_pos + l_move_size, l_line_trail.lengthS() - l_separator_pos + 1 ); + else + l_line_trail.clear(); + } + + + l_string = l_string.substr( l_pos + 1, l_string.size() - l_pos ); + + StringCchCopyW( ext_title, MAX_PATH, l_string.c_str() ); + } + else + StringCchCopyW( ext_title, MAX_PATH, CharNextW( p ) ); + } + else + { + ext_len = -1; + ext_title[ 0 ] = 0; + } + } + // cloud specific playlist line for holding information about the entry + else if ( ext && !wcsncmp( p, _DIRECTIVE_EXT_X_NS_CLOUD, 16 ) ) + { + p += 16; + wchar_t *pt = wcstok( p, L"," ); + while ( pt != NULL ) + { + int end = (int)wcscspn( pt, L"=" ); + + if ( !wcsncmp( pt, _INFO_NAME_MEDIA_HASH, end ) ) + { + if ( ( lstrcpynW( ext_mediahash, pt + end + 1, 128 ) ) == NULL ) + return IFC_PLAYLISTLOADER_FAILED; + } + else if ( !wcsncmp( pt, _INFO_NAME_META_HASH, end ) ) + { + if ( ( lstrcpynW( ext_metahash, pt + end + 1, 128 ) ) == NULL ) + return IFC_PLAYLISTLOADER_FAILED; + } + else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_ID, end ) ) + { + if ( ( lstrcpynW( ext_cloud_id, pt + end + 1, 128 ) ) == NULL ) + return IFC_PLAYLISTLOADER_FAILED; + } + else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_STATUS, end ) ) + { + if ( ( lstrcpynW( ext_cloud_status, pt + end + 1, 16 ) ) == NULL ) + return IFC_PLAYLISTLOADER_FAILED; + } + else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_DEVICES, end ) ) + { + wchar_t *p2 = pt + end + 1; + while ( p2 && *p2 != '\n' ) + p2 = CharNextW( p2 ); + + if ( p2 && *p2 == '\n' ) + *p2 = 0; + + if ( ( lstrcpynW( ext_cloud_devices, pt + end + 1, 128 ) ) == NULL ) + return IFC_PLAYLISTLOADER_FAILED; + } + + pt = wcstok( NULL, L"," ); + } + } + else + { + ext_len = -1; + ext_title[ 0 ] = 0; + ext_mediahash[ 0 ] = 0; + ext_metahash[ 0 ] = 0; + ext_cloud_id[ 0 ] = 0; + ext_cloud_status[ 0 ] = 0; + ext_cloud_devices[ 0 ] = 0; + } + } + } + + if ( fp ) + fclose( fp ); + + return IFC_PLAYLISTLOADER_SUCCESS; +} + + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS M3ULoader + +START_DISPATCH; +CB( IFC_PLAYLISTLOADER_LOAD, Load ) +#if 0 +VCB( IFC_PLAYLISTLOADER_CLOSE, Close ) +CB( IFC_PLAYLISTLOADER_GETITEM, GetItem ) +CB( IFC_PLAYLISTLOADER_GETITEMTITLE, GetItemTitle ) +CB( IFC_PLAYLISTLOADER_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds ) +CB( IFC_PLAYLISTLOADER_GETITEMEXTENDEDINFO, GetItemExtendedInfo ) +CB( IFC_PLAYLISTLOADER_NEXTITEM, NextItem ) +#endif +END_DISPATCH;
\ No newline at end of file diff --git a/Src/playlist/M3ULoader.h b/Src/playlist/M3ULoader.h new file mode 100644 index 00000000..9dd93edf --- /dev/null +++ b/Src/playlist/M3ULoader.h @@ -0,0 +1,31 @@ +#ifndef NULLSOFT_PLAYLIST_M3U_LOADER_H +#define NULLSOFT_PLAYLIST_M3U_LOADER_H + +#include "ifc_playlistloader.h" +#include "ifc_playlistloadercallback.h" +#include <stdio.h> + +static wchar_t *_INFO_NAME_MEDIA_HASH = L"mediahash"; +static wchar_t *_INFO_NAME_META_HASH = L"metahash"; +static wchar_t *_INFO_NAME_CLOUD_ID = L"cloud_id"; +static wchar_t *_INFO_NAME_CLOUD_STATUS = L"cloud_status"; +static wchar_t *_INFO_NAME_CLOUD_DEVICES = L"cloud_devices"; + +class M3ULoader : public ifc_playlistloader +{ +public: + M3ULoader(); + virtual ~M3ULoader( void ); + + int Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ); + int OnFileHelper( ifc_playlistloadercallback *playlist, const wchar_t *trackName, const wchar_t *title, int length, const wchar_t *rootPath, ifc_plentryinfo *extraInfo ); + + +protected: + RECVS_DISPATCH; + + bool _utf8; + wchar_t wideFilename[ FILENAME_SIZE ]; + wchar_t wideTitle[ FILETITLE_SIZE ]; +}; +#endif
\ No newline at end of file diff --git a/Src/playlist/M3UWriter.cpp b/Src/playlist/M3UWriter.cpp new file mode 100644 index 00000000..07110c81 --- /dev/null +++ b/Src/playlist/M3UWriter.cpp @@ -0,0 +1,42 @@ +#include "M3UWriter.h" +#include "../nu/AutoChar.h" + +M3UWriter::~M3UWriter() +{ + Close(); +} + +int M3UWriter::Open( const wchar_t *filename ) +{ + if ( fp != NULL ) + return 0; + + fp = _wfopen( filename, L"wt" ); + if ( !fp ) + return 0; + + fprintf( fp, "#EXTM3U\n" ); + + return 1; +} + +void M3UWriter::Write( const wchar_t *filename ) +{ + if ( fp != NULL ) + fprintf( fp, "%s\n", (char *)AutoChar( filename ) ); +} + +void M3UWriter::Write( const wchar_t *filename, const wchar_t *title, int length ) +{ + if ( fp != NULL ) + fprintf( fp, "#EXTINF:%d,%s\n%s\n", length, (char *)AutoChar( title ), (char *)AutoChar( filename ) ); +} + +void M3UWriter::Close() +{ + if ( fp != NULL ) + { + fclose( fp ); + fp = NULL; + } +}
\ No newline at end of file diff --git a/Src/playlist/M3UWriter.h b/Src/playlist/M3UWriter.h new file mode 100644 index 00000000..1b53d646 --- /dev/null +++ b/Src/playlist/M3UWriter.h @@ -0,0 +1,23 @@ +#ifndef NULLSOFT_M3UWRITERH +#define NULLSOFT_M3UWRITERH + +#include <stdio.h> +#include "PlaylistWriter.h" +class M3UWriter : public PlaylistWriter +{ +public: + M3UWriter() {} + virtual ~M3UWriter(); + + int Open( const wchar_t *filename ) override; + void Write( const wchar_t *filename ) override; + void Write( const wchar_t *filename, const wchar_t *title, int length ) override; + void Write( const wchar_t *p_filename, const wchar_t *p_title, const wchar_t *p_extended_infos, int p_length ) override + {}; + void Close() override; + +private: + FILE *fp = NULL; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/PLSLoader.cpp b/Src/playlist/PLSLoader.cpp new file mode 100644 index 00000000..736b5240 --- /dev/null +++ b/Src/playlist/PLSLoader.cpp @@ -0,0 +1,159 @@ +#include "PLSLoader.h" + +#include "../nu/AutoChar.h" +#include "../nu/AutoWide.h" +#include <shlwapi.h> +#include <strsafe.h> + + +class PLSInfo : public ifc_plentryinfo +{ +public: + PLSInfo( const wchar_t *_filename, int _entryNum ) : filename( _filename ), entryNum( _entryNum ) + {} + + const wchar_t *GetExtendedInfo( const wchar_t *parameter ) + { + static wchar_t data[ 1024 ]; + wchar_t fieldbuf[ 100 ] = { 0 }; + + StringCchPrintfW( fieldbuf, 100, L"%s%d", parameter, entryNum ); + + GetPrivateProfileStringW( L"playlist", fieldbuf, L"", data, 1024, filename ); + + if ( data[ 0 ] ) + return data; + else + return 0; + } + +private: + RECVS_DISPATCH; + + const wchar_t *filename; + int entryNum; +}; + +#define CBCLASS PLSInfo +START_DISPATCH; +CB( IFC_PLENTRYINFO_GETEXTENDEDINFO, GetExtendedInfo ) +END_DISPATCH; +#undef CBCLASS + +int PLSLoader::OnFileHelper( ifc_playlistloadercallback *playlist, const wchar_t *trackName, const wchar_t *title, int length, const wchar_t *rootPath, ifc_plentryinfo *extraInfo ) +{ + if ( length == -1000 ) + length = -1; + + int ret; + if ( wcsstr( trackName, L"://" ) || PathIsRootW( trackName ) ) + { + ret = playlist->OnFile( trackName, title, length, extraInfo ); + } + else + { + wchar_t fullPath[ MAX_PATH ] = { 0 }; + if ( PathCombineW( fullPath, rootPath, trackName ) ) + { + wchar_t canonicalizedPath[ MAX_PATH ] = { 0 }; + PathCanonicalizeW( canonicalizedPath, fullPath ); + ret = playlist->OnFile( canonicalizedPath, title, length, extraInfo ); + } + else + { + ret = ifc_playlistloadercallback::LOAD_CONTINUE; + } + } + + return ret; +} + +int PLSLoader::Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ) +{ + int x, numfiles; + int ext = 0; + char fieldbuf[ 100 ] = { 0 }; + char fnbuf[ FILENAME_SIZE ] = { 0 }; + char tmp[ MAX_PATH ] = { 0 }; + + wchar_t rootPath[ MAX_PATH ] = { 0 }; + const wchar_t *callbackPath = playlist->GetBasePath(); + if ( callbackPath ) + lstrcpynW( rootPath, callbackPath, MAX_PATH ); + else + { + lstrcpynW( rootPath, filename, MAX_PATH ); + PathRemoveFileSpecW( rootPath ); + } + + tmp[ 0 ] = (char)rootPath[ 0 ]; + tmp[ 1 ] = (char)rootPath[ 1 ]; + tmp[ 2 ] = (char)rootPath[ 2 ]; + + AutoChar fn( filename ); + + numfiles = GetPrivateProfileIntA( "playlist", "NumberOfEntries", 0, fn ); + ext = GetPrivateProfileIntA( "playlist", "Version", 1, fn ); + if ( numfiles == 0 ) + return IFC_PLAYLISTLOADER_FAILED; + + for ( x = 1; x <= numfiles; x++ ) + { + int flen = -1; + char ftitle[ FILETITLE_SIZE ] = ""; + StringCchPrintfA( fieldbuf, 100, "File%d", x ); + GetPrivateProfileStringA( "playlist", fieldbuf, "", fnbuf, FILENAME_SIZE, fn ); + if ( ext ) + { + StringCchPrintfA( fieldbuf, 100, "Title%d", x ); + GetPrivateProfileStringA( "playlist", fieldbuf, "", ftitle, FILETITLE_SIZE, fn ); + StringCchPrintfA( fieldbuf, 100, "Length%d", x ); + flen = GetPrivateProfileIntA( "playlist", fieldbuf, -1, fn ); + } + + if ( *fnbuf ) + { + char *p; + char buf[ 512 ] = { 0 }; + + p = fnbuf; + + if ( strncmp( p, "\\\\", 2 ) && strncmp( p + 1, ":\\", 2 ) && !strstr( p, ":/" ) ) + { + if ( p[ 0 ] == '\\' ) + { + buf[ 0 ] = tmp[ 0 ]; + buf[ 1 ] = tmp[ 1 ]; + lstrcpynA( buf + 2, p, 510 ); + buf[ 511 ] = 0; + p = buf; + } + } + + PLSInfo info( filename, x ); + + int ret; + if ( ftitle[ 0 ] ) + { + ret = OnFileHelper( playlist, AutoWide( p ), AutoWide( ftitle ), flen * 1000, rootPath, &info ); + } + else + { + ret = OnFileHelper( playlist, AutoWide( p ), 0, -1, rootPath, &info ); + } + + if ( ret != ifc_playlistloadercallback::LOAD_CONTINUE ) + { + break; + } + } + } + + return IFC_PLAYLISTLOADER_SUCCESS; +} + +#define CBCLASS PLSLoader +START_DISPATCH; +CB( IFC_PLAYLISTLOADER_LOAD, Load ) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/PLSLoader.h b/Src/playlist/PLSLoader.h new file mode 100644 index 00000000..ebff25a0 --- /dev/null +++ b/Src/playlist/PLSLoader.h @@ -0,0 +1,22 @@ +#ifndef NULLSOFT_PLAYLIST_PLSLOADER_H +#define NULLSOFT_PLAYLIST_PLSLOADER_H + +#include "ifc_playlistloader.h" +#include <windows.h> + +class PLSLoader : public ifc_playlistloader +{ +public: + PLSLoader() {} + virtual ~PLSLoader() {} + + int Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ); + +protected: + RECVS_DISPATCH; + +private: + int OnFileHelper( ifc_playlistloadercallback *playlist, const wchar_t *trackName, const wchar_t *title, int length, const wchar_t *rootPath, ifc_plentryinfo *extraInfo ); +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/PLSWriter.cpp b/Src/playlist/PLSWriter.cpp new file mode 100644 index 00000000..e3f35ed5 --- /dev/null +++ b/Src/playlist/PLSWriter.cpp @@ -0,0 +1,36 @@ +#include "PLSWriter.h" +#include "../nu/AutoChar.h" + +PLSWriter::PLSWriter() : fp(0), numEntries(0) +{ +} + +int PLSWriter::Open(const wchar_t *filename) +{ + fp = _wfopen(filename, L"wt"); + if (!fp) + return 0; + + fprintf(fp, "[playlist]\r\n"); + + return 1; +} + +void PLSWriter::Write(const wchar_t *filename) +{ + fwprintf(fp, L"File%d=%s\r\n", (int)++numEntries, filename); +} + +void PLSWriter::Write(const wchar_t *filename, const wchar_t *title, int length) +{ + Write(filename); + fwprintf(fp, L"Title%d=%s\r\n", (int)numEntries, title); + fprintf(fp, "Length%d=%d\r\n", (int)numEntries, length); +} + +void PLSWriter::Close() +{ + fprintf(fp, "NumberOfEntries=%d\r\n", (int)numEntries); + fprintf(fp, "Version=2\r\n"); + fclose(fp); +}
\ No newline at end of file diff --git a/Src/playlist/PLSWriter.h b/Src/playlist/PLSWriter.h new file mode 100644 index 00000000..868a3ac8 --- /dev/null +++ b/Src/playlist/PLSWriter.h @@ -0,0 +1,21 @@ +#ifndef NULLSOFT_PLSWRITERH +#define NULLSOFT_PLSWRITERH + +#include <stdio.h> +#include "PlaylistWriter.h" +class PLSWriter : public PlaylistWriter +{ +public: + PLSWriter(); + int Open(const wchar_t *filename); + void Write(const wchar_t *filename); + void Write(const wchar_t *filename, const wchar_t *title, int length); + void Write( const wchar_t *p_filename, const wchar_t *p_title, const wchar_t *p_extended_infos, int p_length ) override + {}; + void Close(); +private: + size_t numEntries; + FILE *fp; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/Playlist.cpp b/Src/playlist/Playlist.cpp new file mode 100644 index 00000000..4c0dd564 --- /dev/null +++ b/Src/playlist/Playlist.cpp @@ -0,0 +1,230 @@ +#include "main.h" +#include "Playlist.h" +#include <algorithm> +#include "../nu/AutoChar.h" +#include "../Winamp/strutil.h" + +void Playlist::Clear() +{ + for ( pl_entry *entry : entries ) + delete entry; + + entries.clear(); +} + +int Playlist::OnFile( const wchar_t *p_filename, const wchar_t *p_title, int p_lengthInMS, ifc_plentryinfo *p_info ) +{ + entries.push_back( new pl_entry( p_filename, p_title, p_lengthInMS, p_info ) ); + + return ifc_playlistloadercallback::LOAD_CONTINUE; +} + +void Playlist::AppendWithInfo( const wchar_t *p_filename, const wchar_t *p_title, int p_lengthInMS ) +{ + entries.push_back( new pl_entry( p_filename, p_title, p_lengthInMS ) ); +} + +void Playlist::Insert( size_t p_index, const wchar_t *p_filename, const wchar_t *p_title, int p_lengthInMS ) +{ + entries.insert( entries.begin() + p_index, new pl_entry( p_filename, p_title, p_lengthInMS ) ); +} + +Playlist::~Playlist() +{ + Clear(); +} + + +size_t Playlist::GetNumItems() +{ + return entries.size(); +} + +size_t Playlist::GetItem( size_t item, wchar_t *filename, size_t filenameCch ) +{ + if ( item >= entries.size() ) + return 0; + + return entries[ item ]->GetFilename( filename, filenameCch ); +} + +size_t Playlist::GetItemTitle( size_t item, wchar_t *title, size_t titleCch ) +{ + if ( item >= entries.size() ) + return 0; + + return entries[ item ]->GetTitle( title, titleCch ); +} + +const wchar_t *Playlist::ItemTitle( size_t item ) +{ + if ( item >= entries.size() ) + return 0; + + return entries[ item ]->filetitle; +} + +const wchar_t *Playlist::ItemName( size_t item ) +{ + if ( item >= entries.size() ) + return 0; + + return entries[ item ]->filename; +} + +int Playlist::GetItemLengthMilliseconds( size_t item ) +{ + if ( item >= entries.size() ) + return -1; + + return entries[ item ]->GetLengthInMilliseconds(); +} + +size_t Playlist::GetItemExtendedInfo( size_t item, const wchar_t *metadata, wchar_t *info, size_t infoCch ) +{ + if ( item >= entries.size() ) + return 0; + + return entries[ item ]->GetExtendedInfo( metadata, info, infoCch ); +} + +int Playlist::Reverse() +{ + // TODO: keep a bool flag and just do size-item-1 every time a GetItem* function is called + std::reverse( entries.begin(), entries.end() ); + + return PLAYLIST_SUCCESS; +} + +int Playlist::Swap( size_t item1, size_t item2 ) +{ + std::swap( entries[ item1 ], entries[ item2 ] ); + + return PLAYLIST_SUCCESS; +} + +class RandMod +{ +public: + RandMod( int ( *_generator )( ) ) : generator( _generator ) {} + int operator ()( int n ) { return generator() % n; } + int ( *generator )( ); +}; + +int Playlist::Randomize( int ( *generator )( ) ) +{ + RandMod randMod( generator ); + std::random_shuffle( entries.begin(), entries.end(), randMod ); + + return PLAYLIST_SUCCESS; +} + +void Playlist::Remove( size_t item ) +{ + if ( entries.size() > item ) + entries.erase( entries.begin() + item ); +} + +void Playlist::SetItemFilename( size_t item, const wchar_t *filename ) +{ + if ( item < entries.size() ) + entries[ item ]->SetFilename( filename ); +} + +void Playlist::SetItemTitle( size_t item, const wchar_t *title ) +{ + if ( item < entries.size() ) + entries[ item ]->SetTitle( title ); +} + +void Playlist::SetItemLengthMilliseconds( size_t item, int length ) +{ + if ( item < entries.size() ) + entries[ item ]->SetLengthMilliseconds( length ); +} + +static bool PlayList_sortByTitle( pl_entry *&a, pl_entry *&b ) +{ + int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE /*|NORM_IGNOREKANATYPE*/ | NORM_IGNOREWIDTH, a->filetitle, -1, b->filetitle, -1 ); + + return comp == CSTR_LESS_THAN; + // TODO: grab this function from winamp - return CompareStringLogical(a.strTitle, b.strTitle)<0; +} + +static bool PlayList_sortByFile( pl_entry *&a, pl_entry *&b ) //const void *a, const void *b) +{ + const wchar_t *file1 = PathFindFileNameW( a->filename ); + const wchar_t *file2 = PathFindFileNameW( b->filename ); + + int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | /*NORM_IGNOREKANATYPE |*/ NORM_IGNOREWIDTH, file1, -1, file2, -1 ); + + return comp == CSTR_LESS_THAN; + // TODO: grab this function from winamp - return FileCompareLogical(file1, file2)<0; +} + +static bool PlayList_sortByDirectory( pl_entry *&a, pl_entry *&b ) // by dir, then by p_title +{ + const wchar_t *directory1 = a->filename; + const wchar_t *directory2 = b->filename; + + const wchar_t *directoryEnd1 = scanstr_backcW( directory1, L"\\", 0 ); + const wchar_t *directoryEnd2 = scanstr_backcW( directory2, L"\\", 0 ); + + int dirLen1 = (int)( directoryEnd1 - directory1 ); + int dirLen2 = (int)( directoryEnd2 - directory2 ); + + if ( !dirLen1 && !dirLen2 ) // both in the current directory? + return PlayList_sortByFile( a, b ); // not optimized, because the function does another scanstr_back, but easy for now :) + + if ( !dirLen1 ) // only the first dir is empty? + return true; // sort it first + + if ( !dirLen2 ) // only the second dir empty? + return false; // empty dirs go first + +#if 0 // TODO: grab this function from winamp + int comp = FileCompareLogicalN( directory1, dirLen1, directory2, dirLen2 ); + if ( comp == 0 ) + return PlayList_sortByFile( a, b ); + else + return comp < 0; +#endif + + int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | /*NORM_IGNOREKANATYPE | */NORM_IGNOREWIDTH, directory1, dirLen1, directory2, dirLen2 ); + if ( comp == CSTR_EQUAL ) // same dir + return PlayList_sortByFile( a, b ); // do second sort + else // different dirs + return comp == CSTR_LESS_THAN; +} + +int Playlist::SortByTitle() +{ + std::sort( entries.begin(), entries.end(), PlayList_sortByTitle ); + + return 1; +} + +int Playlist::SortByFilename() +{ + std::sort( entries.begin(), entries.end(), PlayList_sortByFile ); + + return 1; +} + +int Playlist::SortByDirectory() +{ + std::sort( entries.begin(), entries.end(), PlayList_sortByDirectory ); + + return 1; +} +/* +int Playlist::Move(size_t itemSrc, size_t itemDest) +{ + if (itemSrc < itemDest) + std::rotate(&entries[itemSrc], &entries[itemSrc], &entries[itemDest]); + else + if (itemSrc > itemDest) + std::rotate(&entries[itemDest], &entries[itemSrc], &entries[itemSrc]); + return 1; +}*/ + diff --git a/Src/playlist/Playlist.h b/Src/playlist/Playlist.h new file mode 100644 index 00000000..0368b57c --- /dev/null +++ b/Src/playlist/Playlist.h @@ -0,0 +1,48 @@ +#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLIST_H +#define NULLSOFT_ML_PLAYLISTS_PLAYLIST_H + +#include "ifc_playlist.h" + +#include <windows.h> // for MAX_PATH +#include "pl_entry.h" +#include "ifc_playlistT.h" +#include "ifc_playlistloadercallbackT.h" +#include <vector> + + +class Playlist : public ifc_playlistloadercallbackT<Playlist>, public ifc_playlistT<Playlist> +{ +public: + virtual ~Playlist(); + + void Clear(); + int OnFile( const wchar_t *p_filename, const wchar_t *p_title, int p_lengthInMS, ifc_plentryinfo *p_info ); + void AppendWithInfo( const wchar_t *p_filename, const wchar_t *p_title, int p_lengthInMS ); + void Insert( size_t p_index, const wchar_t *p_filename, const wchar_t *p_title, int p_lengthInMS ); + + size_t GetNumItems(); + + size_t GetItem( size_t item, wchar_t *filename, size_t filenameCch ); + size_t GetItemTitle( size_t item, wchar_t *title, size_t titleCch ); + const wchar_t *ItemTitle( size_t item ); + const wchar_t *ItemName( size_t item ); + int GetItemLengthMilliseconds( size_t item ); // TODO: maybe microsecond for better resolution? + size_t GetItemExtendedInfo( size_t item, const wchar_t *metadata, wchar_t *info, size_t infoCch ); + + void SetItemFilename( size_t item, const wchar_t *filename ); + void SetItemTitle( size_t item, const wchar_t *title ); + void SetItemLengthMilliseconds( size_t item, int length ); + + int Reverse(); + int Swap( size_t item1, size_t item2 ); + int Randomize( int ( *generator )( ) ); + void Remove( size_t item ); + int SortByTitle(); + int SortByFilename(); + int SortByDirectory(); //sorts by directory and then by filename + +private: + typedef std::vector<pl_entry*> PlaylistEntries; + PlaylistEntries entries; +}; +#endif
\ No newline at end of file diff --git a/Src/playlist/PlaylistCounter.cpp b/Src/playlist/PlaylistCounter.cpp new file mode 100644 index 00000000..413c133b --- /dev/null +++ b/Src/playlist/PlaylistCounter.cpp @@ -0,0 +1,18 @@ +#include "PlaylistCounter.h" + +int PlaylistCounter::OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info ) +{ + // TODO: recursive load? + ++count; + if ( lengthInMS > 0 ) + length += lengthInMS; + + return LOAD_CONTINUE; +} + + +#define CBCLASS PlaylistCounter +START_DISPATCH; +CB( IFC_PLAYLISTLOADERCALLBACK_ONFILE_RET, OnFile ) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/PlaylistCounter.h b/Src/playlist/PlaylistCounter.h new file mode 100644 index 00000000..0d3fb72d --- /dev/null +++ b/Src/playlist/PlaylistCounter.h @@ -0,0 +1,21 @@ +#ifndef NULLSOFT_PLAYLIST_PLAYLISTCOUNTER_H +#define NULLSOFT_PLAYLIST_PLAYLISTCOUNTER_H + +#include "ifc_playlistloadercallback.h" + +class PlaylistCounter : public ifc_playlistloadercallback +{ +public: + PlaylistCounter() {} + + int OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info ); + + size_t count = 0; + uint64_t length = 0; + +protected: + RECVS_DISPATCH; + +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/PlaylistManager.cpp b/Src/playlist/PlaylistManager.cpp new file mode 100644 index 00000000..948048c8 --- /dev/null +++ b/Src/playlist/PlaylistManager.cpp @@ -0,0 +1,630 @@ +#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
\ No newline at end of file diff --git a/Src/playlist/PlaylistManager.h b/Src/playlist/PlaylistManager.h new file mode 100644 index 00000000..0ac53a35 --- /dev/null +++ b/Src/playlist/PlaylistManager.h @@ -0,0 +1,42 @@ +#ifndef NULLSOFT_ML_PLAYLISTS_PLAYLIST_MANAGER_H +#define NULLSOFT_ML_PLAYLISTS_PLAYLIST_MANAGER_H + +#include "api_playlistmanager.h" + +class PlaylistManager : public api_playlistmanager +{ +public: + int Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ); + int LoadAs( const wchar_t *filename, const wchar_t *ext, ifc_playlistloadercallback *playlist ); + int LoadFromDialog( const wchar_t *fns, ifc_playlistloadercallback *playlist ); + int LoadFromANSIDialog( const char *fns, ifc_playlistloadercallback *playlist ); + + int Save( const wchar_t *filename, ifc_playlist *playlist ); + + size_t Copy( const wchar_t *destFn, const wchar_t *srcFn ); // returns number of items copied + + size_t CountItems( const wchar_t *filename ); + + int GetLengthMilliseconds( const wchar_t *filename ); + uint64_t GetLongLengthMilliseconds( const wchar_t *filename ); + + void Randomize( ifc_playlist *playlist ); + void Reverse( ifc_playlist *playlist ); + + void LoadDirectory( const wchar_t *directory, ifc_playlistloadercallback *callback, ifc_playlistdirectorycallback *dirCallback ); + + bool CanLoad( const wchar_t *filename ); + + void GetExtensionList( wchar_t *extensionList, size_t extensionListCch ); + void GetFilterList( wchar_t *extensionList, size_t extensionListCch ); + + const wchar_t *EnumExtensions( size_t num ); + +protected: + RECVS_DISPATCH; + +}; + +extern PlaylistManager playlistManager; + +#endif
\ No newline at end of file diff --git a/Src/playlist/PlaylistWriter.h b/Src/playlist/PlaylistWriter.h new file mode 100644 index 00000000..74d4b9f0 --- /dev/null +++ b/Src/playlist/PlaylistWriter.h @@ -0,0 +1,17 @@ +#ifndef NULLSOFT_PLAYLIST_PLAYLISTWRITER_H +#define NULLSOFT_PLAYLIST_PLAYLISTWRITER_H + +// probably not the final interface, so we won't dispatch it yet + +class PlaylistWriter +{ +public: + virtual ~PlaylistWriter() {} + virtual int Open( const wchar_t *filename ) = 0; + virtual void Write( const wchar_t *filename ) = 0; + virtual void Write( const wchar_t *filename, const wchar_t *title, int length ) = 0; + virtual void Write( const wchar_t *filename, const wchar_t *title, const wchar_t *p_extended_infos, int length ) = 0; + virtual void Close() = 0; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/Playlists.cpp b/Src/playlist/Playlists.cpp new file mode 100644 index 00000000..71fd1a0e --- /dev/null +++ b/Src/playlist/Playlists.cpp @@ -0,0 +1,746 @@ +#include <algorithm> +#include "playlists.h" +#include "api__playlist.h" +#include "PlaylistsXML.h" +#include <shlwapi.h> +#include <limits.h> +#include <strsafe.h> +#pragma comment(lib, "Rpcrt4") +using namespace Nullsoft::Utility; + +/* +benski> Notes to maintainers +be sure to call DelayLoad() before doing anything. +This is mainly done because the XML parsing service isn't guaranteed to be registered before this service. +It also improves load time. +*/ + +/* --------------------------------------------- */ + +PlaylistInfo::PlaylistInfo() +{ + filename[0] = 0; + title[0] = 0; + length = 0; + numItems = 0; + iTunesID = 0; + cloud = 0; + + UuidCreate(&guid); +} + +PlaylistInfo::PlaylistInfo( const wchar_t *_filename, const wchar_t *_title, GUID playlist_guid ) +{ + StringCbCopyW( filename, sizeof( filename ), _filename ); + if ( _title ) + StringCbCopyW( title, sizeof( title ), _title ); + else + title[ 0 ] = 0; + + length = 0; + numItems = 0; + + if ( playlist_guid == INVALID_GUID ) + UuidCreate( &guid ); + else + guid = playlist_guid; + + iTunesID = 0; + cloud = 0; +} + +PlaylistInfo::PlaylistInfo( const PlaylistInfo © ) +{ + StringCbCopyW( filename, sizeof( filename ), copy.filename ); + StringCbCopyW( title, sizeof( title ), copy.title ); + + length = copy.length; + numItems = copy.numItems; + guid = copy.guid; + iTunesID = copy.iTunesID; + cloud = copy.cloud; +} + +/* --------------------------------------------- */ +Playlists::Playlists() +{ + iterator = 0; + triedLoaded = false; + loaded = false; + dirty = false; +} + +bool Playlists::DelayLoad() +{ + if ( triedLoaded ) + return loaded; + + PlaylistsXML loader( this ); + + const wchar_t *g_path = WASABI_API_APP->path_getUserSettingsPath(); + wchar_t playlistsFilename[ MAX_PATH ] = { 0 }; + wchar_t oldPlaylistsFilename[ MAX_PATH ] = { 0 }; + wchar_t newPlaylistsFolder[ MAX_PATH ] = { 0 }; + + PathCombineW( playlistsFilename, g_path, L"plugins" ); + PathAppendW( playlistsFilename, L"ml" ); + PathAppendW( playlistsFilename, L"playlists" ); + CreateDirectoryW( playlistsFilename, NULL ); + lstrcpynW( newPlaylistsFolder, playlistsFilename, MAX_PATH ); + PathAppendW( playlistsFilename, L"playlists.xml" ); + + PathCombineW( oldPlaylistsFilename, g_path, L"plugins" ); + PathAppendW( oldPlaylistsFilename, L"ml" ); + PathAppendW( oldPlaylistsFilename, L"playlists.xml" ); + + bool migrated = false; + if ( PathFileExistsW( oldPlaylistsFilename ) && !PathFileExistsW( playlistsFilename ) ) + { + if ( MoveFileW( oldPlaylistsFilename, playlistsFilename ) ) + { + migrated = true; + PathRemoveFileSpecW( oldPlaylistsFilename ); + } + } + + switch ( loader.LoadFile( playlistsFilename ) ) + { + case PLAYLISTSXML_SUCCESS: + loaded = true; + triedLoaded = true; + if ( AGAVE_API_STATS ) + AGAVE_API_STATS->SetStat( api_stats::PLAYLIST_COUNT, (int)playlists.size() ); + + if ( playlists.size() && migrated ) + { + for ( PlaylistInfo l_playlist : playlists ) + { + wchar_t path[ MAX_PATH ] = { 0 }, file[ MAX_PATH ] = { 0 }; + lstrcpynW( file, l_playlist.filename, MAX_PATH ); + PathStripPathW( file ); + PathCombineW( path, oldPlaylistsFilename, file ); + if ( PathFileExistsW( path ) ) + { + wchar_t new_path[ MAX_PATH ] = { 0 }; + PathCombineW( new_path, newPlaylistsFolder, file ); + MoveFileW( path, new_path ); + } + } + dirty = true; + Flush(); + } + + break; + case PLAYLISTSXML_NO_PARSER: + // if there's XML parser, we'll try again on the off-chance it eventually gets loaded (we might still be in the midst of loading the w5s/wac components) + break; + default: + loaded = true; + triedLoaded = true; + break; + } + + return loaded; +} + +void Playlists::Lock() +{ + playlistsGuard.Lock(); +} + +void Playlists::Unlock() +{ + playlistsGuard.Unlock(); +} + +size_t Playlists::GetIterator() +{ + return iterator; +} + +static void WriteEscaped( FILE *fp, const wchar_t *str ) +{ + // TODO: for speed optimization, + // we should wait until we hit a special character + // and write out everything else so before it, + // like how ASX loader does it + while ( str && *str ) + { + switch ( *str ) + { + case L'&': + fputws( L"&", fp ); + break; + case L'>': + fputws( L">", fp ); + break; + case L'<': + fputws( L"<", fp ); + break; + case L'\'': + fputws( L"'", fp ); + break; + case L'\"': + fputws( L""", fp ); + break; + default: + fputwc( *str, fp ); + break; + } + + // write out the whole UTF-16 character + wchar_t *next = CharNextW( str ); + while ( ++str != next ) + fputwc( *str, fp ); + } +} + +bool TitleSortAsc( PlaylistInfo &item1, PlaylistInfo &item2 ) +{ + int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | NORM_IGNOREWIDTH, item1.title, -1, item2.title, -1 ); + + return comp == CSTR_LESS_THAN; +} + +bool TitleSortDesc( PlaylistInfo &item1, PlaylistInfo &item2 ) +{ + int comp = CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE | NORM_IGNOREWIDTH, item1.title, -1, item2.title, -1 ); + + return comp == CSTR_GREATER_THAN; +} + +bool NumberOfEntrySortAsc( PlaylistInfo &item1, PlaylistInfo &item2 ) +{ + return !!( item1.numItems < item2.numItems ); +} + +bool NumberOfEntrySortDesc( PlaylistInfo &item1, PlaylistInfo &item2 ) +{ + return !!( item1.numItems > item2.numItems ); +} + +int Playlists::Sort( size_t sort_type ) +{ + if ( !DelayLoad() ) + return 0; + + int sorted = 1; + + switch ( sort_type ) + { + case SORT_TITLE_ASCENDING: + std::sort( playlists.begin(), playlists.end(), TitleSortAsc ); + break; + case SORT_TITLE_DESCENDING: + std::sort( playlists.begin(), playlists.end(), TitleSortDesc ); + break; + case SORT_NUMBER_ASCENDING: + std::sort( playlists.begin(), playlists.end(), NumberOfEntrySortAsc ); + break; + case SORT_NUMBER_DESCENDING: + std::sort( playlists.begin(), playlists.end(), NumberOfEntrySortDesc ); + break; + default: + sorted = 0; + break; + } + + dirty = true; + + if ( sorted ) + Flush(); + + return sorted; +} + +void Playlists::Flush() +{ + AutoLockT<Playlists> lock( this ); + + if ( !triedLoaded && !loaded ) // if the playlists.xml file was never even attempted to be loaded, don't overwrite + return; + + if ( !dirty ) // if we've not seen any changes then no need to re-save + return; + + const wchar_t *g_path = WASABI_API_APP->path_getUserSettingsPath(); + + wchar_t rootPath[ MAX_PATH ] = { 0 }; + wchar_t playlistsBackupFilename[ MAX_PATH ] = { 0 }; + wchar_t playlistsDestination[ MAX_PATH ] = { 0 }; + + PathCombineW( rootPath, g_path, L"plugins" ); + CreateDirectoryW( rootPath, NULL ); + PathAppendW( rootPath, L"ml" ); + CreateDirectoryW( rootPath, NULL ); + PathAppendW( rootPath, L"playlists" ); + CreateDirectoryW( rootPath, NULL ); + + int g_path_size = wcslen( rootPath ); + PathCombineW( playlistsBackupFilename, rootPath, L"playlists.xml.backup" ); + PathCombineW( playlistsDestination, rootPath, L"playlists.xml" ); + + CopyFileW( playlistsDestination, playlistsBackupFilename, FALSE ); + + FILE *fp = _wfopen( playlistsDestination, L"wb" ); + if ( !fp ) // bah + { + dirty = false; + return; + } + + fseek( fp, 0, SEEK_SET ); + fputwc( L'\xFEFF', fp ); + fwprintf( fp, L"<?xml version=\"1.0\" encoding=\"UTF-16\"?>" ); + fwprintf( fp, L"<playlists playlists=\"%u\">", (unsigned int)playlists.size() ); + + if ( AGAVE_API_STATS ) + AGAVE_API_STATS->SetStat( api_stats::PLAYLIST_COUNT, (int)playlists.size() ); + + for ( PlaylistInfo &l_play_list_info : playlists ) + { + fputws( L"<playlist filename=\"", fp ); + const wchar_t *fn = l_play_list_info.filename; + + if ( !_wcsnicmp( rootPath, fn, g_path_size ) ) + { + fn += g_path_size; + + if ( *fn == L'\\' ) + ++fn; + } + + WriteEscaped( fp, fn ); + + fputws( L"\" title=\"", fp ); + WriteEscaped( fp, l_play_list_info.title ); + + GUID guid = l_play_list_info.guid; + + fwprintf( fp, L"\" id=\"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\"", + (int)guid.Data1, (int)guid.Data2, (int)guid.Data3, + (int)guid.Data4[ 0 ], (int)guid.Data4[ 1 ], + (int)guid.Data4[ 2 ], (int)guid.Data4[ 3 ], + (int)guid.Data4[ 4 ], (int)guid.Data4[ 5 ], + (int)guid.Data4[ 6 ], (int)guid.Data4[ 7 ] ); + + fwprintf( fp, L" songs=\"%u\" seconds=\"%u\"", l_play_list_info.numItems, l_play_list_info.length ); + + if ( l_play_list_info.iTunesID ) + fwprintf( fp, L" iTunesID=\"%I64u\"", l_play_list_info.iTunesID ); + + if ( l_play_list_info.cloud ) + fwprintf( fp, L" cloud=\"1\"" ); + + fwprintf( fp, L"/>" ); + } + + fwprintf( fp, L"</playlists>" ); + fclose( fp ); + dirty = false; +} + +size_t Playlists::GetCount() +{ + DelayLoad(); + + return playlists.size(); +} + +const wchar_t *Playlists::GetFilename( size_t index ) +{ + if ( !DelayLoad() || index >= playlists.size() ) + return 0; + + return playlists[ index ].filename; +} + +const wchar_t *Playlists::GetName( size_t index ) +{ + if ( !DelayLoad() || index >= playlists.size() ) + return 0; + + return playlists[ index ].title; +} + +GUID Playlists::GetGUID( size_t index ) +{ + if ( !DelayLoad() || index >= playlists.size() ) + return INVALID_GUID; + + return playlists[ index ].guid; +} + +int Playlists::GetPosition( GUID playlist_guid, size_t *index ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + size_t indexCount = 0; + for ( PlaylistInfo &l_play_list_info : playlists ) + { + if ( l_play_list_info.guid == playlist_guid ) + { + *index = indexCount; + return API_PLAYLISTS_SUCCESS; + } + + ++indexCount; + } + + return API_PLAYLISTS_FAILURE; +} + +template <class val_t> +static int GetWithSize( void *data, size_t dataLen, val_t value ) +{ + switch ( dataLen ) + { + case 1: + { + if ( value > _UI8_MAX ) // check for overflow + return API_PLAYLISTS_BAD_SIZE; + + *(uint8_t *)data = (uint8_t)value; + + return API_PLAYLISTS_SUCCESS; + } + case 2: + { + if ( value > _UI16_MAX ) // check for overflow + return API_PLAYLISTS_BAD_SIZE; + + *(uint16_t *)data = (uint16_t)value; + + return API_PLAYLISTS_SUCCESS; + } + case 4: + { + if ( value > _UI32_MAX ) + return API_PLAYLISTS_BAD_SIZE; + + *(uint32_t *)data = (uint32_t)value; + + return API_PLAYLISTS_SUCCESS; + } + case 8: + { + if ( value > _UI64_MAX ) + return API_PLAYLISTS_BAD_SIZE; + + *(uint64_t *)data = (uint64_t)value; + + return API_PLAYLISTS_SUCCESS; + } + } + + return API_PLAYLISTS_BAD_SIZE; +} + +template <class val_t> +static int SetWithSize( void *data, size_t dataLen, val_t *value ) +{ + switch ( dataLen ) + { + case 1: + { + *value = ( val_t ) * (uint8_t *)data; + return API_PLAYLISTS_SUCCESS; + } + case 2: + { + *value = ( val_t ) * (uint16_t *)data; + return API_PLAYLISTS_SUCCESS; + } + case 4: + { + *value = ( val_t ) * (uint32_t *)data; + return API_PLAYLISTS_SUCCESS; + } + case 8: + { + *value = ( val_t ) * (uint64_t *)data; + return API_PLAYLISTS_SUCCESS; + } + } + + return API_PLAYLISTS_BAD_SIZE; +} + +int Playlists::GetInfo( size_t index, GUID info, void *data, size_t dataLen ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + if ( index >= playlists.size() ) + return API_PLAYLISTS_INVALID_INDEX; + + if ( info == api_playlists_itemCount ) + return GetWithSize( data, dataLen, playlists[ index ].numItems ); + else if ( info == api_playlists_totalTime ) + return GetWithSize( data, dataLen, playlists[ index ].length ); + else if ( info == api_playlists_iTunesID ) + return GetWithSize( data, dataLen, playlists[ index ].iTunesID ); + else if ( info == api_playlists_cloud ) + return GetWithSize( data, dataLen, playlists[ index ].cloud ); + + return API_PLAYLISTS_UNKNOWN_INFO_GUID; +} + +int Playlists::MoveBefore( size_t index1, size_t index2 ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + if ( index1 >= playlists.size() ) + return API_PLAYLISTS_INVALID_INDEX; + + PlaylistInfo copy = playlists[ index1 ]; + if ( index2 >= playlists.size() ) + { + playlists.push_back( copy ); + playlists.erase(playlists.begin() + index1 ); + } + else + { + playlists.insert(playlists.begin() + index2, copy ); + if ( index1 >= index2 ) + index1++; + playlists.erase(playlists.begin() + index1 ); + } + + dirty = true; + ++iterator; + + return API_PLAYLISTS_SUCCESS; +} + +size_t Playlists::AddPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid ) +{ + if ( !DelayLoad() ) + return -1; + + AutoLockT<Playlists> lock( this ); + + if ( playlist_guid != INVALID_GUID ) + { + for ( size_t index = 0; index < playlists.size(); index++ ) + { + if ( playlists[ index ].guid == playlist_guid ) + { + if ( lstrcmpiW( playlists[ index ].title, playlistName ) ) + { + dirty = true; + StringCbCopyW( playlists[ index ].title, sizeof( playlists[ index ].title ), playlistName ); + WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_RENAMED, index, 0 ); + } + + return -2; + } + } + } + + size_t newIndex = AddPlaylist_NoCallback( filename, playlistName, playlist_guid ); + WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_ADDED, newIndex, 0 ); + + return newIndex; +} + +size_t Playlists::AddCloudPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid ) +{ + if ( !DelayLoad() ) + return -1; + + AutoLockT<Playlists> lock( this ); + + if ( playlist_guid != INVALID_GUID ) + { + for ( size_t index = 0; index < playlists.size(); index++ ) + { + if ( playlists[ index ].guid == playlist_guid ) + { + // we make sure that this playlist has a 'cloud' flag + // as without it, our detection of thing isn't ideal. + if ( !playlists[ index ].cloud ) + playlists[ index ].cloud = 1; + + if ( lstrcmpW( playlists[ index ].title, playlistName ) ) + { + dirty = true; + StringCbCopyW( playlists[ index ].title, sizeof( playlists[ index ].title ), playlistName ); + WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_RENAMED, index, 0 ); + } + + return -2; + } + } + } + + size_t newIndex = AddPlaylist_NoCallback( filename, playlistName, playlist_guid ); + int cloud = 1; + SetInfo( newIndex, api_playlists_cloud, &cloud, sizeof( cloud ) ); + WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_ADDED, newIndex, 0 ); + + return newIndex; +} + +size_t Playlists::AddPlaylist_internal( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid, size_t numItems, size_t length, uint64_t iTunesID, size_t cloud ) +{ + PlaylistInfo newPlaylist( filename, playlistName, playlist_guid ); + newPlaylist.numItems = (int)numItems; + newPlaylist.length = (int)length; + newPlaylist.iTunesID = iTunesID; + newPlaylist.cloud = (int)cloud; + + playlists.push_back( newPlaylist ); + size_t newIndex = playlists.size() - 1; + + return newIndex; +} + +size_t Playlists::AddPlaylist_NoCallback( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid ) +{ + AutoLockT<Playlists> lock( this ); + PlaylistInfo newPlaylist( filename, playlistName, playlist_guid ); + + dirty = true; + playlists.push_back( newPlaylist ); + + ++iterator; + + size_t newIndex = playlists.size() - 1; + + return newIndex; +} + +int Playlists::SetGUID( size_t index, GUID playlist_guid ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + if ( index >= playlists.size() ) + return API_PLAYLISTS_INVALID_INDEX; + + dirty = true; + playlists[ index ].guid = playlist_guid; + + return API_PLAYLISTS_SUCCESS; +} + +int Playlists::RenamePlaylist( size_t index, const wchar_t *name ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + if ( index >= playlists.size() ) + return API_PLAYLISTS_INVALID_INDEX; + + dirty = true; + + if ( lstrcmpW( playlists[ index ].title, name ) ) + { + StringCbCopyW( playlists[ index ].title, sizeof( playlists[ index ].title ), name ); + WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_RENAMED, index, 0 ); + } + + return API_PLAYLISTS_SUCCESS; +} + +int Playlists::MovePlaylist( size_t index, const wchar_t *filename ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + if ( index >= playlists.size() ) + return API_PLAYLISTS_INVALID_INDEX; + + dirty = true; + StringCbCopyW( playlists[ index ].filename, sizeof( playlists[ index ].filename ), filename ); + iterator++; + + return API_PLAYLISTS_SUCCESS; +} + +int Playlists::SetInfo( size_t index, GUID info, void *data, size_t dataLen ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + if ( index >= playlists.size() ) + return API_PLAYLISTS_INVALID_INDEX; + + dirty = true; + if ( info == api_playlists_itemCount ) + return SetWithSize( data, dataLen, &playlists[ index ].numItems ); + else if ( info == api_playlists_totalTime ) + return SetWithSize( data, dataLen, &playlists[ index ].length ); + else if ( info == api_playlists_iTunesID ) + return SetWithSize( data, dataLen, &playlists[ index ].iTunesID ); + else if ( info == api_playlists_cloud ) + return SetWithSize( data, dataLen, &playlists[ index ].cloud ); + + dirty = false; + + return API_PLAYLISTS_UNKNOWN_INFO_GUID; +} + +int Playlists::RemovePlaylist( size_t index ) +{ + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + if ( index >= playlists.size() ) + return API_PLAYLISTS_INVALID_INDEX; + + dirty = true; + WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_REMOVED_PRE, index, 0 ); + playlists.erase(playlists.begin() + index ); + iterator++; + WASABI_API_SYSCB->syscb_issueCallback( api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_REMOVED_POST, index, 0 ); + + return API_PLAYLISTS_SUCCESS; +} + +int Playlists::ClearPlaylists() +{ + AutoLockT<Playlists> lock( this ); + + if ( !DelayLoad() ) + return API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS; + + dirty = true; + playlists.clear(); + + return API_PLAYLISTS_SUCCESS; +} + +const PlaylistInfo &Playlists::GetPlaylistInfo(size_t i) +{ + return playlists[i]; +} + +#define CBCLASS Playlists +START_DISPATCH; +VCB( API_PLAYLISTS_LOCK, Lock ); +VCB( API_PLAYLISTS_UNLOCK, Unlock ); +CB( API_PLAYLISTS_GETITERATOR, GetIterator ); +VCB( API_PLAYLISTS_FLUSH, Flush ); +CB( API_PLAYLISTS_GETCOUNT, GetCount ); +CB( API_PLAYLISTS_GETFILENAME, GetFilename ); +CB( API_PLAYLISTS_GETNAME, GetName ); +CB( API_PLAYLISTS_GETGUID, GetGUID ); +CB( API_PLAYLISTS_GETPOSITION, GetPosition ); +CB( API_PLAYLISTS_GETINFO, GetInfo ); +CB( API_PLAYLISTS_MOVEBEFORE, MoveBefore ); +CB( API_PLAYLISTS_ADDPLAYLIST, AddPlaylist ); +CB( API_PLAYLISTS_ADDPLAYLISTNOCB, AddPlaylist_NoCallback ); +CB( API_PLAYLISTS_ADDCLOUDPLAYLIST, AddCloudPlaylist ); +CB( API_PLAYLISTS_SETGUID, SetGUID ); +CB( API_PLAYLISTS_RENAMEPLAYLIST, RenamePlaylist ); +CB( API_PLAYLISTS_MOVEPLAYLIST, MovePlaylist ); +CB( API_PLAYLISTS_SETINFO, SetInfo ); +CB( API_PLAYLISTS_REMOVEPLAYLIST, RemovePlaylist ); +CB( API_PLAYLISTS_CLEARPLAYLISTS, ClearPlaylists ); +CB( API_PLAYLISTS_SORT, Sort ); +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/Playlists.h b/Src/playlist/Playlists.h new file mode 100644 index 00000000..7b873b2d --- /dev/null +++ b/Src/playlist/Playlists.h @@ -0,0 +1,74 @@ +#ifndef NULLSOFT_PLAYLIST_PLAYLISTS_H +#define NULLSOFT_PLAYLIST_PLAYLISTS_H + +#include "api_playlists.h" +#include <vector> +#include "../nu/AutoLock.h" + +class PlaylistInfo +{ +public: + PlaylistInfo(); + PlaylistInfo(const wchar_t *_filename, const wchar_t *_title, GUID playlist_guid = INVALID_GUID); + PlaylistInfo(const PlaylistInfo ©); + wchar_t filename[MAX_PATH]; + wchar_t title[1024]; + int length; // in seconds + int numItems; + GUID guid; + uint64_t iTunesID; // this is used by ml_impex + int cloud; // this is used by ml_playlists +}; + +class Playlists : public api_playlists +{ +public: + Playlists(); + bool DelayLoad(); + + // api_playlists implementations + void Lock(); + void Unlock(); + + size_t GetIterator(); + int Sort(size_t sort_type); + void Flush(); + + // get information about playlists + size_t GetCount(); // returns number of playlists + const wchar_t *GetFilename(size_t index); // returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE. only valid until you Unlock() + const wchar_t *GetName(size_t index); // returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE. only valid until you Unlock() + GUID GetGUID(size_t index); // retrieves a unique ID which identifies this playlist + int GetPosition(GUID playlist_guid, size_t *index); // retrieves the index where a particular playlist ID lives. returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE + int GetInfo(size_t index, GUID info, void *data, size_t dataLen); // This is for getting "extra" data, see list of GUIDs below. returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE + + // manipulating playlists + int MoveBefore(size_t index1, size_t index2); // moves playlist at position index1 to before index2. setting index2 to anything larger than GetCount() moves to end + size_t AddPlaylist(const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid = INVALID_GUID); // adds a new playlist, returns new index. Generates a GUID if you don't pass third parameter. + size_t AddPlaylist_NoCallback(const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid = INVALID_GUID); + size_t AddCloudPlaylist(const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid = INVALID_GUID); // adds a new playlist, returns new index. Generates a GUID if you don't pass third parameter. + int SetGUID(size_t index, GUID playlist_guid); // sets (overrides) a playlist ID. Don't use unless you have some very specific need + int RenamePlaylist(size_t index, const wchar_t *name); + int MovePlaylist(size_t index, const wchar_t *filename); // sets a new filename. NOTE: IT'S UP TO YOU TO PHYSICALLY MOVE/RENAME/CREATE THE NEW FILENAME. + int SetInfo(size_t index, GUID info, void *data, size_t dataLen); // returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE + int RemovePlaylist(size_t index); // removes a particular playlist + int ClearPlaylists(); // [*] clears the entire playlist. Use at your own risk :) + + size_t AddPlaylist_internal(const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid, size_t numItems, size_t length, uint64_t iTunesID, size_t cloud); + + // for SPlaylists to use + const PlaylistInfo &GetPlaylistInfo(size_t i); +private: + bool loaded; // whether or not playlists.xml has been loaded + bool triedLoaded; // whether or not we tried to load + bool dirty; // whether a flush should save on Flush() + size_t iterator; // a counter to help clients determine if the list data has changed + typedef std::vector<PlaylistInfo> PlaylistsList; + PlaylistsList playlists; + + Nullsoft::Utility::LockGuard playlistsGuard; + + RECVS_DISPATCH; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/PlaylistsXML.cpp b/Src/playlist/PlaylistsXML.cpp new file mode 100644 index 00000000..5db3b8dd --- /dev/null +++ b/Src/playlist/PlaylistsXML.cpp @@ -0,0 +1,149 @@ +#include "PlaylistsXML.h" +#include "api__playlist.h" +#include "../nu/AutoLock.h" +#include "Playlists.h" +#include <shlwapi.h> +#include <stdint.h> + +using namespace Nullsoft::Utility; + +void PlaylistsXML::StartTag( const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params ) +{ + const wchar_t *filename = params->getItemValue( L"filename" ); + const wchar_t *title = params->getItemValue( L"title" ); + const wchar_t *countString = params->getItemValue( L"songs" ); + + size_t numItems = 0; + + if ( countString && *countString ) + numItems = _wtoi( countString ); + + const wchar_t *lengthString = params->getItemValue( L"seconds" ); + size_t length = 0; + if ( lengthString && *lengthString ) + length = _wtoi( lengthString ); + + const wchar_t *iTunesIDString = params->getItemValue( L"iTunesID" ); + uint64_t iTunesID = 0; + if ( iTunesIDString && *iTunesIDString ) + iTunesID = _wtoi64( iTunesIDString ); + + const wchar_t *cloudString = params->getItemValue( L"cloud" ); + size_t cloud = 0; + if ( cloudString && *cloudString ) + cloud = _wtoi( cloudString ); + + // parse GUID + GUID guid = INVALID_GUID; + const wchar_t *guidString = params->getItemValue( L"id" ); + if ( guidString && *guidString ) + { + int Data1, Data2, Data3; + int Data4[ 8 ]; + + int n = swscanf( guidString, L" { %08x - %04x - %04x - %02x%02x - %02x%02x%02x%02x%02x%02x } ", + &Data1, &Data2, &Data3, Data4 + 0, Data4 + 1, + Data4 + 2, Data4 + 3, Data4 + 4, Data4 + 5, Data4 + 6, Data4 + 7 ); + + if ( n == 11 ) // GUID was + { + // Cross assign all the values + guid.Data1 = Data1; + guid.Data2 = Data2; + guid.Data3 = Data3; + guid.Data4[ 0 ] = Data4[ 0 ]; + guid.Data4[ 1 ] = Data4[ 1 ]; + guid.Data4[ 2 ] = Data4[ 2 ]; + guid.Data4[ 3 ] = Data4[ 3 ]; + guid.Data4[ 4 ] = Data4[ 4 ]; + guid.Data4[ 5 ] = Data4[ 5 ]; + guid.Data4[ 6 ] = Data4[ 6 ]; + guid.Data4[ 7 ] = Data4[ 7 ]; + } + } + + if ( PathIsFileSpecW( filename ) ) + { + wchar_t playlistFilename[ MAX_PATH ] = { 0 }; + PathCombineW( playlistFilename, rootPath, filename ); + playlists->AddPlaylist_internal( playlistFilename, title, guid, numItems, length, iTunesID, cloud ); + } + else + { + playlists->AddPlaylist_internal( filename, title, guid, numItems, length, iTunesID, cloud ); + } +} + +int PlaylistsXML::LoadFile( const wchar_t *filename ) +{ + if ( !parser ) + return PLAYLISTSXML_NO_PARSER; // no sense in continuing if there's no parser available + + HANDLE file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL ); + + if ( file == INVALID_HANDLE_VALUE ) + return PLAYLISTSXML_NO_FILE; + + while ( true ) + { + int8_t data[ 1024 ] = { 0 }; + DWORD bytesRead = 0; + if ( ReadFile( file, data, 1024, &bytesRead, NULL ) && bytesRead ) + { + if ( parser->xmlreader_feed( data, bytesRead ) != API_XML_SUCCESS ) + { + CloseHandle( file ); + return PLAYLISTSXML_XML_PARSE_ERROR; + } + } + else + break; + } + + CloseHandle( file ); + parser->xmlreader_feed( 0, 0 ); + + return PLAYLISTSXML_SUCCESS; +} + +PlaylistsXML::PlaylistsXML( Playlists *_playlists ) : playlists( _playlists ) +{ + const wchar_t *g_path = WASABI_API_APP->path_getUserSettingsPath(); + PathCombineW( rootPath, g_path, L"plugins\\ml\\playlists" ); + + playlists->AddRef(); + + parserFactory = WASABI_API_SVC->service_getServiceByGuid( obj_xmlGUID ); + if ( parserFactory ) + parser = (obj_xml *)parserFactory->getInterface(); + + if ( parser ) + { + parser->xmlreader_setCaseSensitive(); + parser->xmlreader_registerCallback( L"Winamp:Playlists\fplaylist", this ); + parser->xmlreader_registerCallback( L"playlists\fplaylist", this ); + parser->xmlreader_open(); + } +} + +PlaylistsXML::~PlaylistsXML() +{ + playlists->Release(); + if ( parser ) + { + parser->xmlreader_unregisterCallback( this ); + parser->xmlreader_close(); + } + + if ( parserFactory && parser ) + parserFactory->releaseInterface( parser ); + + parserFactory = 0; + parser = 0; +} + +#define CBCLASS PlaylistsXML +START_DISPATCH; +VCB( ONSTARTELEMENT, StartTag ) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/PlaylistsXML.h b/Src/playlist/PlaylistsXML.h new file mode 100644 index 00000000..8207c653 --- /dev/null +++ b/Src/playlist/PlaylistsXML.h @@ -0,0 +1,38 @@ +#ifndef NULLSOFT_AGAVE_PLAYLISTSXML_H +#define NULLSOFT_AGAVE_PLAYLISTSXML_H + +#include "../xml/obj_xml.h" +#include "../xml/ifc_xmlreadercallback.h" +#include <api/service/waServiceFactory.h> +class Playlists; + +enum +{ + PLAYLISTSXML_SUCCESS = 0, + PLAYLISTSXML_FAILURE = 1, + PLAYLISTSXML_NO_PARSER = 2, + PLAYLISTSXML_NO_FILE = 3, + PLAYLISTSXML_XML_PARSE_ERROR = 4, +}; + +class PlaylistsXML : public ifc_xmlreadercallback +{ +public: + PlaylistsXML( Playlists *_playlists ); + ~PlaylistsXML(); + + int LoadFile( const wchar_t *filename ); + +private: + RECVS_DISPATCH; + /* XML callbacks */ + void StartTag( const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params ); + + obj_xml *parser = 0; + waServiceFactory *parserFactory = 0; + Playlists *playlists = 0; + + wchar_t rootPath[ MAX_PATH ]; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/SPlaylist.cpp b/Src/playlist/SPlaylist.cpp new file mode 100644 index 00000000..0777840d --- /dev/null +++ b/Src/playlist/SPlaylist.cpp @@ -0,0 +1,270 @@ +#include "SPlaylist.h" +#include "api__playlist.h" +#include "PlaylistManager.h" + +extern ScriptObjectController *script_root; +extern PlaylistScriptController playlistController; + + +// -- Functions table ------------------------------------- +function_descriptor_struct PlaylistScriptController::exportedFunction[] = { + {L"Clear", 0, (void*)SPlaylist::script_vcpu_Clear }, + {L"GetNumItems", 0, (void*)SPlaylist::script_vcpu_GetNumItems }, + {L"GetItem", 1, (void*)SPlaylist::script_vcpu_GetItem }, + {L"GetItemTitle", 1, (void*)SPlaylist::script_vcpu_GetItemTitle }, + {L"GetItemLength", 1, (void*)SPlaylist::script_vcpu_GetItemLength }, + {L"GetItemExtendedInfo", 2, (void*)SPlaylist::script_vcpu_GetItemExtendedInfo }, + {L"Reverse", 0, (void*)SPlaylist::script_vcpu_Reverse }, + {L"Swap", 2, (void*)SPlaylist::script_vcpu_Swap }, + {L"Randomize", 0, (void*)SPlaylist::script_vcpu_Randomize }, + {L"Remove", 1, (void*)SPlaylist::script_vcpu_Remove }, + {L"SortByTitle", 0, (void*)SPlaylist::script_vcpu_SortByTitle }, + {L"SortByFilename", 0, (void*)SPlaylist::script_vcpu_SortByFilename }, + /* TODO: + {L"Append", 3, (void*)SPlaylist::script_vcpu_Append }, + {L"Insert", 4, (void*)SPlaylist::script_vcpu_Insert }, + {L"SetItemFilename", 2, (void*)SPlaylist::script_vcpu_SetItemFilename }, + {L"SetItemTitle", 2, (void*)SPlaylist::script_vcpu_SetItemTitle }, + {L"SetItemLengthMilliseconds", 2, (void*)SPlaylist::script_vcpu_SetItemLengthMilliseconds }, + {L"SortByDirectory", 0, (void*)SPlaylist::script_vcpu_SortByDirectory }, + */ + +}; +// -------------------------------------------------------- + +const wchar_t *PlaylistScriptController::getClassName() +{ + return L"Playlist"; +} + +const wchar_t *PlaylistScriptController::getAncestorClassName() +{ + return L"Object"; +} + +ScriptObjectController *PlaylistScriptController::getAncestorController() +{ + return script_root; +} + +ScriptObject *PlaylistScriptController::instantiate() +{ + SPlaylist *xd = new SPlaylist; + + ASSERT( xd != NULL ); + + return xd->getScriptObject(); +} + +void PlaylistScriptController::destroy( ScriptObject *o ) +{ + SPlaylist *xd = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + + ASSERT( xd != NULL ); + + delete xd; +} + +void *PlaylistScriptController::encapsulate( ScriptObject *o ) +{ + return NULL; +} + +void PlaylistScriptController::deencapsulate( void *o ) +{} + +int PlaylistScriptController::getNumFunctions() +{ + return sizeof( exportedFunction ) / sizeof( function_descriptor_struct ); +} + +const function_descriptor_struct *PlaylistScriptController::getExportedFunctions() +{ + return exportedFunction; +} + +GUID PlaylistScriptController::getClassGuid() +{ + return makiPlaylistGUID; +} + + +/* ---- */ +SPlaylist::SPlaylist() +{ + getScriptObject()->vcpu_setInterface( makiPlaylistGUID, static_cast<SPlaylist *>( this ) ); + getScriptObject()->vcpu_setClassName( L"Playlist" ); + getScriptObject()->vcpu_setController( &playlistController ); +} + +scriptVar SPlaylist::script_vcpu_Clear( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + playlist->playlist.Clear(); + } + RETURN_SCRIPT_VOID +} + +scriptVar SPlaylist::script_vcpu_GetNumItems( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + size_t count = playlist->playlist.GetNumItems(); + + return MAKE_SCRIPT_INT( (int)count ); + } + RETURN_SCRIPT_ZERO +} + +static wchar_t splaylist_string_return[ 1024 ]; +static wchar_t *splaylist_string_empty = L""; + +scriptVar SPlaylist::script_vcpu_GetItem( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber ) +{ + SCRIPT_FUNCTION_INIT; + + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + int item = GET_SCRIPT_INT( itemNumber ); + size_t cch = playlist->playlist.GetItem( item, splaylist_string_return, sizeof( splaylist_string_return ) / sizeof( *splaylist_string_return ) ); + + if ( cch == 0 ) + return MAKE_SCRIPT_STRING( splaylist_string_empty ); + else + return MAKE_SCRIPT_STRING( splaylist_string_return ); + } + + return MAKE_SCRIPT_STRING( splaylist_string_empty ); +} + +scriptVar SPlaylist::script_vcpu_GetItemTitle( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber ) +{ + SCRIPT_FUNCTION_INIT; + + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + int item = GET_SCRIPT_INT( itemNumber ); + size_t cch = playlist->playlist.GetItemTitle( item, splaylist_string_return, sizeof( splaylist_string_return ) / sizeof( *splaylist_string_return ) ); + + if ( cch == 0 ) + return MAKE_SCRIPT_STRING( splaylist_string_empty ); + else + return MAKE_SCRIPT_STRING( splaylist_string_return ); + } + + return MAKE_SCRIPT_STRING( splaylist_string_empty ); +} + +scriptVar SPlaylist::script_vcpu_GetItemLength( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber ) +{ + SCRIPT_FUNCTION_INIT; + + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + int item = GET_SCRIPT_INT( itemNumber ); + int length = playlist->playlist.GetItemLengthMilliseconds( item ); + + return MAKE_SCRIPT_INT( length ); + } + + return MAKE_SCRIPT_INT( -1 ); +} + +scriptVar SPlaylist::script_vcpu_GetItemExtendedInfo( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber, scriptVar metadata ) +{ + SCRIPT_FUNCTION_INIT; + + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + int item = GET_SCRIPT_INT( itemNumber ); + const wchar_t *tag = GET_SCRIPT_STRING( metadata ); + size_t cch = playlist->playlist.GetItemExtendedInfo( item, tag, splaylist_string_return, sizeof( splaylist_string_return ) / sizeof( *splaylist_string_return ) ); + + if ( cch == 0 ) + return MAKE_SCRIPT_STRING( splaylist_string_empty ); + else + return MAKE_SCRIPT_STRING( splaylist_string_return ); + } + + return MAKE_SCRIPT_STRING( splaylist_string_empty ); +} + +scriptVar SPlaylist::script_vcpu_Reverse( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + playlist->playlist.Reverse(); + } + RETURN_SCRIPT_VOID +} + +scriptVar SPlaylist::script_vcpu_Swap( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar position1, scriptVar position2 ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + int item1 = GET_SCRIPT_INT( position1 ); + int item2 = GET_SCRIPT_INT( position2 ); + + playlist->playlist.Swap( item1, item2 ); + } + RETURN_SCRIPT_VOID +} + +scriptVar SPlaylist::script_vcpu_Randomize( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + playlistManager.Randomize( &playlist->playlist ); + } + RETURN_SCRIPT_VOID +} + + +scriptVar SPlaylist::script_vcpu_Remove( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemIndex ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + int item = GET_SCRIPT_INT( itemIndex ); + playlist->playlist.Remove( item ); + } + RETURN_SCRIPT_VOID +} + +scriptVar SPlaylist::script_vcpu_SortByTitle( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + playlist->playlist.SortByTitle(); + } + RETURN_SCRIPT_VOID +} + +scriptVar SPlaylist::script_vcpu_SortByFilename( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylist *playlist = static_cast<SPlaylist *>( o->vcpu_getInterface( makiPlaylistGUID ) ); + if ( playlist ) + { + playlist->playlist.SortByFilename(); + } + RETURN_SCRIPT_VOID +} diff --git a/Src/playlist/SPlaylist.h b/Src/playlist/SPlaylist.h new file mode 100644 index 00000000..8689b1ec --- /dev/null +++ b/Src/playlist/SPlaylist.h @@ -0,0 +1,52 @@ +#pragma once + +#include "api/script/objcontroller.h" +#include "api/script/objects/rootobj.h" +#include "Playlist.h" + +class PlaylistScriptController : public ScriptObjectControllerI +{ +public: + virtual const wchar_t *getClassName(); + virtual const wchar_t *getAncestorClassName(); + virtual ScriptObjectController *getAncestorController(); + virtual int getNumFunctions(); + virtual const function_descriptor_struct *getExportedFunctions(); + virtual GUID getClassGuid(); + virtual ScriptObject *instantiate(); + virtual void destroy( ScriptObject *o ); + virtual void *encapsulate( ScriptObject *o ); + virtual void deencapsulate( void *o ); + +private: + static function_descriptor_struct exportedFunction[]; +}; + +class SPlaylist : public RootObjectInstance +{ +public: + SPlaylist(); + + /* ifc_playlist wrapper */ + static scriptVar script_vcpu_Clear( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ); + static scriptVar script_vcpu_GetNumItems( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ); + static scriptVar script_vcpu_GetItem( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber ); + static scriptVar script_vcpu_GetItemTitle( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber ); + static scriptVar script_vcpu_GetItemLength( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber ); + static scriptVar script_vcpu_GetItemExtendedInfo( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber, scriptVar metadata ); + static scriptVar script_vcpu_Reverse( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ); + static scriptVar script_vcpu_Swap( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar position1, scriptVar position2 ); + static scriptVar script_vcpu_Randomize( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ); + static scriptVar script_vcpu_Remove( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemIndex ); + static scriptVar script_vcpu_SortByTitle( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ); + static scriptVar script_vcpu_SortByFilename( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ); + + /* extra functions */ +//private: + Playlist playlist; +}; + + +// {632883FC-159F-4330-B193-CFD62CA47EC1} +static const GUID makiPlaylistGUID = +{ 0x632883fc, 0x159f, 0x4330, { 0xb1, 0x93, 0xcf, 0xd6, 0x2c, 0xa4, 0x7e, 0xc1 } }; diff --git a/Src/playlist/SPlaylistManager.cpp b/Src/playlist/SPlaylistManager.cpp new file mode 100644 index 00000000..896039d4 --- /dev/null +++ b/Src/playlist/SPlaylistManager.cpp @@ -0,0 +1,117 @@ +#include "SPlaylistManager.h" +#include "SPlaylist.h" +#include "api__playlist.h" +#include "PlaylistManager.h" + +extern ScriptObjectController *script_root; +extern PlaylistManagerScriptController playlistManagerController; + +// {C6207729-2600-4bb8-B562-2E0BC04E4416} +static const GUID makePlaylistManagerGUID = +{ 0xc6207729, 0x2600, 0x4bb8, { 0xb5, 0x62, 0x2e, 0xb, 0xc0, 0x4e, 0x44, 0x16 } }; + + +// -- Functions table ------------------------------------- +function_descriptor_struct PlaylistManagerScriptController::exportedFunction[] = { + {L"OpenPlaylist", 1, (void*)SPlaylistManager::script_vcpu_OpenPlaylist }, + {L"SavePlaylist", 2, (void*)SPlaylistManager::script_vcpu_SavePlaylist }, +}; +// -------------------------------------------------------- + +const wchar_t *PlaylistManagerScriptController::getClassName() { + return L"PlaylistManager"; +} + +const wchar_t *PlaylistManagerScriptController::getAncestorClassName() { + return L"Object"; +} + +ScriptObjectController *PlaylistManagerScriptController::getAncestorController() +{ + return script_root; +} + +int PlaylistManagerScriptController::getInstantiable() +{ + return 0; +} + +int PlaylistManagerScriptController::getReferenceable() +{ + return 0; +} + +ScriptObject *PlaylistManagerScriptController::instantiate() { + SPlaylistManager *xd = new SPlaylistManager; + ASSERT(xd != NULL); + return xd->getScriptObject(); +} + +void PlaylistManagerScriptController::destroy(ScriptObject *o) { + SPlaylistManager *xd = static_cast<SPlaylistManager *>(o->vcpu_getInterface(makePlaylistManagerGUID)); + ASSERT(xd != NULL); + delete xd; +} + +void *PlaylistManagerScriptController::encapsulate(ScriptObject *o) { + return NULL; +} + +void PlaylistManagerScriptController::deencapsulate(void *o) { +} + +int PlaylistManagerScriptController::getNumFunctions() { + return sizeof(exportedFunction) / sizeof(function_descriptor_struct); +} + +const function_descriptor_struct *PlaylistManagerScriptController::getExportedFunctions() { + return exportedFunction; +} + +GUID PlaylistManagerScriptController::getClassGuid() { + return makePlaylistManagerGUID; +} + +/* --- */ + +SPlaylistManager::SPlaylistManager() +{ + getScriptObject()->vcpu_setInterface(makePlaylistManagerGUID, static_cast<SPlaylistManager *>(this)); + getScriptObject()->vcpu_setClassName(L"PlaylistManager"); + getScriptObject()->vcpu_setController(&playlistManagerController); +} + +scriptVar SPlaylistManager::script_vcpu_OpenPlaylist(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar scriptFilename) +{ + SCRIPT_FUNCTION_INIT; + const wchar_t *filename = GET_SCRIPT_STRING(scriptFilename); + if (filename && *filename) + { + SPlaylist *playlist = new SPlaylist; + if (playlistManager.Load(filename, &playlist->playlist) == PLAYLISTMANAGER_SUCCESS) + { + return MAKE_SCRIPT_OBJECT(playlist->getScriptObject()); + } + else + { + delete playlist; + return MAKE_SCRIPT_OBJECT(0); + } + } + + return MAKE_SCRIPT_OBJECT(0); +} + +scriptVar SPlaylistManager::script_vcpu_SavePlaylist(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar scriptFilename, scriptVar scriptPlaylist) +{ + SCRIPT_FUNCTION_INIT; + const wchar_t *filename = GET_SCRIPT_STRING(scriptFilename); + SPlaylist *playlist = static_cast<SPlaylist *>(GET_SCRIPT_OBJECT_AS(scriptPlaylist, makiPlaylistGUID)); + if (filename && *filename && playlist) + { + playlistManager.Save(filename, &playlist->playlist); + } + + RETURN_SCRIPT_VOID; +} + diff --git a/Src/playlist/SPlaylistManager.h b/Src/playlist/SPlaylistManager.h new file mode 100644 index 00000000..4917deb5 --- /dev/null +++ b/Src/playlist/SPlaylistManager.h @@ -0,0 +1,38 @@ +#pragma once + +#include <api/script/objcontroller.h> +#include <api/script/objects/rootobj.h> + +class PlaylistManagerScriptController : public ScriptObjectControllerI +{ +public: + const wchar_t *getClassName(); + const wchar_t *getAncestorClassName(); + ScriptObjectController *getAncestorController(); + int getNumFunctions(); + const function_descriptor_struct *getExportedFunctions(); + GUID getClassGuid(); + + ScriptObject *instantiate(); + void destroy( ScriptObject *o ); + + void *encapsulate( ScriptObject *o ); + void deencapsulate( void *o ); + + int getInstantiable(); + int getReferenceable(); + +private: + static function_descriptor_struct exportedFunction[]; +}; + +class SPlaylistManager : public RootObjectInstance +{ +public: + SPlaylistManager(); + + static scriptVar script_vcpu_OpenPlaylist( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar filename ); + static scriptVar script_vcpu_SavePlaylist( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar filename, scriptVar playlist ); + +private: +};
\ No newline at end of file diff --git a/Src/playlist/SPlaylists.cpp b/Src/playlist/SPlaylists.cpp new file mode 100644 index 00000000..953b8ed2 --- /dev/null +++ b/Src/playlist/SPlaylists.cpp @@ -0,0 +1,157 @@ +#include "SPlaylists.h" +#include "api__playlist.h" +#include "Playlists.h" +#include "SPlaylistsEnumerator.h" +#include "SPlaylist.h" +#include "PlaylistManager.h" + +extern Playlists playlists; + +extern ScriptObjectController *script_root; +extern PlaylistsScriptController playlistsController; + +// {5829EE15-3648-4c6e-B2FE-8736CBBF39DB} +static const GUID makiPlaylistsGUID = +{ 0x5829ee15, 0x3648, 0x4c6e, { 0xb2, 0xfe, 0x87, 0x36, 0xcb, 0xbf, 0x39, 0xdb } }; + +// -- Functions table ------------------------------------- +function_descriptor_struct PlaylistsScriptController::exportedFunction[] = { + {L"GetEnumerator", 0, (void*)SPlaylists::script_vcpu_GetEnumerator }, + {L"OpenPlaylist", 1, (void*)SPlaylists::script_vcpu_OpenPlaylist }, + {L"SavePlaylist", 2, (void*)SPlaylists::script_vcpu_SavePlaylist }, +}; +// -------------------------------------------------------- + +const wchar_t *PlaylistsScriptController::getClassName() +{ + return L"Playlists"; +} + +const wchar_t *PlaylistsScriptController::getAncestorClassName() +{ + return L"Object"; +} + +ScriptObjectController *PlaylistsScriptController::getAncestorController() +{ + return script_root; +} + +int PlaylistsScriptController::getInstantiable() +{ + return 0; +} + +int PlaylistsScriptController::getReferenceable() +{ + return 0; +} + +ScriptObject *PlaylistsScriptController::instantiate() +{ + SPlaylists *xd = new SPlaylists; + ASSERT(xd != NULL); + return xd->getScriptObject(); +} + +void PlaylistsScriptController::destroy(ScriptObject *o) +{ + SPlaylists *xd = static_cast<SPlaylists *>(o->vcpu_getInterface(makiPlaylistsGUID)); + ASSERT(xd != NULL); + delete xd; +} + +void *PlaylistsScriptController::encapsulate(ScriptObject *o) +{ + return NULL; +} + +void PlaylistsScriptController::deencapsulate(void *o) +{} + +int PlaylistsScriptController::getNumFunctions() +{ + return sizeof(exportedFunction) / sizeof(function_descriptor_struct); +} + +const function_descriptor_struct *PlaylistsScriptController::getExportedFunctions() +{ + return exportedFunction; +} + +GUID PlaylistsScriptController::getClassGuid() +{ + return makiPlaylistsGUID; +} + +/* --- */ + +SPlaylists::SPlaylists() +{ + getScriptObject()->vcpu_setInterface(makiPlaylistsGUID, static_cast<SPlaylists *>(this)); + getScriptObject()->vcpu_setClassName(L"Playlists"); + getScriptObject()->vcpu_setController(&playlistsController); +} + +scriptVar SPlaylists::script_vcpu_GetEnumerator(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) +{ + SCRIPT_FUNCTION_INIT; + SPlaylistsEnumerator *enumerator = new SPlaylistsEnumerator(); + playlists.Lock(); + size_t count = playlists.GetCount(); + enumerator->Reserve(count); + for (size_t i=0;i!=count;i++) + { + const PlaylistInfo &info = playlists.GetPlaylistInfo(i); + enumerator->AppendPlaylist(info); + } + playlists.Unlock(); + return MAKE_SCRIPT_OBJECT(enumerator->getScriptObject()); +} + +scriptVar SPlaylists::script_vcpu_OpenPlaylist( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar scriptPlaylistGUID ) +{ + SCRIPT_FUNCTION_INIT; + GUID playlist_guid = nsGUID::fromCharW( GET_SCRIPT_STRING( scriptPlaylistGUID ) ); + SPlaylist *playlist = 0; + + playlists.Lock(); + size_t index; + if ( playlists.GetPosition( playlist_guid, &index ) == API_PLAYLISTS_SUCCESS ) + { + const wchar_t *filename = playlists.GetFilename( index ); + if ( filename ) + { + playlist = new SPlaylist; + if ( playlistManager.Load( filename, &playlist->playlist ) != PLAYLISTMANAGER_SUCCESS ) + { + delete playlist; + playlist = 0; + } + } + } + playlists.Unlock(); + + return MAKE_SCRIPT_OBJECT( playlist ? playlist->getScriptObject() : 0 ); +} + +scriptVar SPlaylists::script_vcpu_SavePlaylist( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar scriptPlaylistGUID, scriptVar scriptPlaylist ) +{ + SCRIPT_FUNCTION_INIT; + GUID playlist_guid = nsGUID::fromCharW( GET_SCRIPT_STRING( scriptPlaylistGUID ) ); + SPlaylist *playlist = static_cast<SPlaylist *>( GET_SCRIPT_OBJECT_AS( scriptPlaylist, makiPlaylistGUID ) ); + if ( playlist ) + { + playlists.Lock(); + size_t index; + if ( playlists.GetPosition( playlist_guid, &index ) == API_PLAYLISTS_SUCCESS ) + { + const wchar_t *filename = playlists.GetFilename( index ); + if ( filename ) + playlistManager.Save( filename, &playlist->playlist ); + } + playlists.Unlock(); + } + + return MAKE_SCRIPT_OBJECT( playlist ? playlist->getScriptObject() : 0 ); +} diff --git a/Src/playlist/SPlaylists.h b/Src/playlist/SPlaylists.h new file mode 100644 index 00000000..53d55c49 --- /dev/null +++ b/Src/playlist/SPlaylists.h @@ -0,0 +1,40 @@ +#pragma once + +#include <api/script/objcontroller.h> +#include <api/script/objects/rootobj.h> + +class PlaylistsScriptController : public ScriptObjectControllerI +{ +public: + const wchar_t *getClassName(); + const wchar_t *getAncestorClassName(); + ScriptObjectController *getAncestorController(); + int getNumFunctions(); + const function_descriptor_struct *getExportedFunctions(); + GUID getClassGuid(); + + ScriptObject *instantiate(); + void destroy( ScriptObject *o ); + + void *encapsulate( ScriptObject *o ); + void deencapsulate( void *o ); + + int getInstantiable(); + int getReferenceable(); + +private: + static function_descriptor_struct exportedFunction[]; +}; + +class SPlaylists : public RootObjectInstance +{ +public: + SPlaylists(); + + static scriptVar script_vcpu_GetEnumerator( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ); + static scriptVar script_vcpu_OpenPlaylist( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar scriptPlaylistGUID ); + static scriptVar script_vcpu_SavePlaylist( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar scriptPlaylistGUID, scriptVar scriptPlaylist ); + +private: + +};
\ No newline at end of file diff --git a/Src/playlist/SPlaylistsEnumerator.cpp b/Src/playlist/SPlaylistsEnumerator.cpp new file mode 100644 index 00000000..376c64f2 --- /dev/null +++ b/Src/playlist/SPlaylistsEnumerator.cpp @@ -0,0 +1,184 @@ +#include "SPlaylistsEnumerator.h" +#include "api__playlist.h" +#include <bfc/nsguid.h> +#include "Playlists.h" + +extern ScriptObjectController *script_root; +extern PlaylistsEnumeratorScriptController playlistsEnumeratorController; + +// {C18F8E50-2C81-4001-9F46-FD942B07ECCD} +static const GUID makiPlaylistsEnumeratorGUID = +{ 0xc18f8e50, 0x2c81, 0x4001, { 0x9f, 0x46, 0xfd, 0x94, 0x2b, 0x7, 0xec, 0xcd } }; + +// -- Functions table ------------------------------------- +function_descriptor_struct PlaylistsEnumeratorScriptController::exportedFunction[] = { + {L"GetCount", 0, (void *)SPlaylistsEnumerator::script_vcpu_GetCount }, + {L"GetFilename", 1, (void *)SPlaylistsEnumerator::script_vcpu_GetFilename }, + {L"GetTitle", 1, (void *)SPlaylistsEnumerator::script_vcpu_GetTitle }, + {L"GetLength", 1, (void *)SPlaylistsEnumerator::script_vcpu_GetLength }, + {L"GetNumItems", 1, (void *)SPlaylistsEnumerator::script_vcpu_GetNumItems }, + {L"GetGUID", 1, (void *)SPlaylistsEnumerator::script_vcpu_GetGUID }, + +}; +// -------------------------------------------------------- + +const wchar_t *PlaylistsEnumeratorScriptController::getClassName() +{ + return L"PlaylistsEnumerator"; +} + +const wchar_t *PlaylistsEnumeratorScriptController::getAncestorClassName() +{ + return L"Object"; +} + +ScriptObjectController *PlaylistsEnumeratorScriptController::getAncestorController() +{ + return script_root; +} + +ScriptObject *PlaylistsEnumeratorScriptController::instantiate() +{ + SPlaylistsEnumerator *xd = new SPlaylistsEnumerator; + ASSERT(xd != NULL); + return xd->getScriptObject(); +} + +void PlaylistsEnumeratorScriptController::destroy( ScriptObject *o ) +{ + SPlaylistsEnumerator *xd = static_cast<SPlaylistsEnumerator *>( o->vcpu_getInterface( makiPlaylistsEnumeratorGUID ) ); + ASSERT( xd != NULL ); + delete xd; +} + +void *PlaylistsEnumeratorScriptController::encapsulate( ScriptObject *o ) +{ + return NULL; +} + +void PlaylistsEnumeratorScriptController::deencapsulate( void *o ) +{} + +int PlaylistsEnumeratorScriptController::getNumFunctions() +{ + return sizeof( exportedFunction ) / sizeof( function_descriptor_struct ); +} + +const function_descriptor_struct *PlaylistsEnumeratorScriptController::getExportedFunctions() +{ + return exportedFunction; +} + +GUID PlaylistsEnumeratorScriptController::getClassGuid() +{ + return makiPlaylistsEnumeratorGUID; +} + +/* --- */ + +SPlaylistsEnumerator::SPlaylistsEnumerator() +{ + getScriptObject()->vcpu_setInterface( makiPlaylistsEnumeratorGUID, static_cast<SPlaylistsEnumerator *>( this ) ); + getScriptObject()->vcpu_setClassName( L"PlaylistsEnumerator" ); + getScriptObject()->vcpu_setController( &playlistsEnumeratorController ); +} + +SPlaylistsEnumerator::~SPlaylistsEnumerator() +{ + //info.deleteAll(); + for (auto obj : info) + { + delete obj; + } + info.clear(); +} + +scriptVar SPlaylistsEnumerator::script_vcpu_GetCount( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylistsEnumerator *enumerator = static_cast<SPlaylistsEnumerator *>( o->vcpu_getInterface( makiPlaylistsEnumeratorGUID ) ); + if ( enumerator ) + { + return MAKE_SCRIPT_INT( (int)enumerator->info.size() ); + } + + return MAKE_SCRIPT_INT( 0 ); +} + +static const wchar_t *static_splaylistsenumerator_empty_string = L""; +scriptVar SPlaylistsEnumerator::script_vcpu_GetFilename( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar playlistNumber ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylistsEnumerator *enumerator = static_cast<SPlaylistsEnumerator *>( o->vcpu_getInterface( makiPlaylistsEnumeratorGUID ) ); + size_t i = GET_SCRIPT_INT( playlistNumber ); + if ( enumerator && i < enumerator->info.size() ) + { + return MAKE_SCRIPT_STRING( enumerator->info[ i ]->filename ); + } + + return MAKE_SCRIPT_STRING( static_splaylistsenumerator_empty_string ); +} + +scriptVar SPlaylistsEnumerator::script_vcpu_GetTitle( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar playlistNumber ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylistsEnumerator *enumerator = static_cast<SPlaylistsEnumerator *>( o->vcpu_getInterface( makiPlaylistsEnumeratorGUID ) ); + size_t i = GET_SCRIPT_INT( playlistNumber ); + if ( enumerator && i < enumerator->info.size() ) + { + return MAKE_SCRIPT_STRING( enumerator->info[ i ]->title ); + } + + return MAKE_SCRIPT_STRING( static_splaylistsenumerator_empty_string ); +} + +scriptVar SPlaylistsEnumerator::script_vcpu_GetLength( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar playlistNumber ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylistsEnumerator *enumerator = static_cast<SPlaylistsEnumerator *>( o->vcpu_getInterface( makiPlaylistsEnumeratorGUID ) ); + size_t i = GET_SCRIPT_INT( playlistNumber ); + if ( enumerator && i < enumerator->info.size() ) + { + return MAKE_SCRIPT_INT( enumerator->info[ i ]->length ); + } + + return MAKE_SCRIPT_INT( -1 ); +} + +scriptVar SPlaylistsEnumerator::script_vcpu_GetNumItems( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar playlistNumber ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylistsEnumerator *enumerator = static_cast<SPlaylistsEnumerator *>( o->vcpu_getInterface( makiPlaylistsEnumeratorGUID ) ); + size_t i = GET_SCRIPT_INT( playlistNumber ); + if ( enumerator && i < enumerator->info.size() ) + { + return MAKE_SCRIPT_INT( enumerator->info[ i ]->numItems ); + } + + return MAKE_SCRIPT_INT( 0 ); +} + +static wchar_t static_splaylistsenumerator_guid_string[39]; +scriptVar SPlaylistsEnumerator::script_vcpu_GetGUID( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar playlistNumber ) +{ + SCRIPT_FUNCTION_INIT; + SPlaylistsEnumerator *enumerator = static_cast<SPlaylistsEnumerator *>( o->vcpu_getInterface( makiPlaylistsEnumeratorGUID ) ); + size_t i = GET_SCRIPT_INT( playlistNumber ); + if ( enumerator && i < enumerator->info.size() ) + { + nsGUID::toCharW( enumerator->info[ i ]->guid, static_splaylistsenumerator_guid_string ); + return MAKE_SCRIPT_STRING( static_splaylistsenumerator_guid_string ); + } + + return MAKE_SCRIPT_STRING( static_splaylistsenumerator_empty_string ); +} + +void SPlaylistsEnumerator::Reserve( size_t count ) +{ + info.reserve( count ); +} + +void SPlaylistsEnumerator::AppendPlaylist( const PlaylistInfo &newPlaylist ) +{ + info.push_back( new PlaylistInfo( newPlaylist ) ); +}
\ No newline at end of file diff --git a/Src/playlist/SPlaylistsEnumerator.h b/Src/playlist/SPlaylistsEnumerator.h new file mode 100644 index 00000000..9ad12a96 --- /dev/null +++ b/Src/playlist/SPlaylistsEnumerator.h @@ -0,0 +1,45 @@ + +#pragma once + +#include <api/script/objcontroller.h> +#include <api/script/objects/rootobj.h> +#include "Playlists.h" +#include <vector> + +class PlaylistsEnumeratorScriptController : public ScriptObjectControllerI +{ + public: + const wchar_t *getClassName(); + const wchar_t *getAncestorClassName(); + ScriptObjectController *getAncestorController(); + int getNumFunctions(); + const function_descriptor_struct *getExportedFunctions(); + GUID getClassGuid(); + ScriptObject *instantiate(); + void destroy(ScriptObject *o); + void *encapsulate(ScriptObject *o); + void deencapsulate(void *o); + + private: + static function_descriptor_struct exportedFunction[]; +}; + +class SPlaylistsEnumerator : public RootObjectInstance +{ +public: + SPlaylistsEnumerator(); + ~SPlaylistsEnumerator(); + + void Reserve(size_t count); + void AppendPlaylist(const PlaylistInfo &newPlaylist); + + static scriptVar script_vcpu_GetCount(SCRIPT_FUNCTION_PARAMS, ScriptObject *o); + static scriptVar script_vcpu_GetFilename(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber); + static scriptVar script_vcpu_GetTitle(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber); + static scriptVar script_vcpu_GetLength(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber); + static scriptVar script_vcpu_GetNumItems(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber); + static scriptVar script_vcpu_GetGUID(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemNumber); + +private: + std::vector<PlaylistInfo*> info; +};
\ No newline at end of file diff --git a/Src/playlist/ScriptObjectFactory.cpp b/Src/playlist/ScriptObjectFactory.cpp new file mode 100644 index 00000000..5e947b68 --- /dev/null +++ b/Src/playlist/ScriptObjectFactory.cpp @@ -0,0 +1,67 @@ +#include "ScriptObjectFactory.h" +#include "api__playlist.h" +#include "ScriptObjectService.h" + +ScriptObjectService svc; +static const char serviceName[] = "Playlist Maki Objects"; + +// {2406A46C-9740-4af7-A0EB-A544AAB8F00C} +static const GUID playlist_script_object_guid = +{ 0x2406a46c, 0x9740, 0x4af7, { 0xa0, 0xeb, 0xa5, 0x44, 0xaa, 0xb8, 0xf0, 0xc } }; + + +FOURCC ScriptObjectFactory::GetServiceType() +{ + return svc.getServiceType(); +} + +const char *ScriptObjectFactory::GetServiceName() +{ + return serviceName; +} + +GUID ScriptObjectFactory::GetGUID() +{ + return playlist_script_object_guid; +} + +void *ScriptObjectFactory::GetInterface(int global_lock) +{ +// if (global_lock) +// WASABI_API_SVC->service_lock(this, (void *)ifc); + return &svc; +} + +int ScriptObjectFactory::SupportNonLockingInterface() +{ + return 1; +} + +int ScriptObjectFactory::ReleaseInterface(void *ifc) +{ + //WASABI_API_SVC->service_unlock(ifc); + return 1; +} + +const char *ScriptObjectFactory::GetTestString() +{ + return 0; +} + +int ScriptObjectFactory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#define CBCLASS ScriptObjectFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/ScriptObjectFactory.h b/Src/playlist/ScriptObjectFactory.h new file mode 100644 index 00000000..8209082e --- /dev/null +++ b/Src/playlist/ScriptObjectFactory.h @@ -0,0 +1,23 @@ +#pragma once + +#include "api__playlist.h" +#include <api/service/waservicefactory.h> +#include <api/service/services.h> + +class ScriptObjectFactory : public waServiceFactory +{ +public: + FOURCC GetServiceType(); + const char *GetServiceName(); + GUID GetGUID(); + void *GetInterface(int global_lock); + int SupportNonLockingInterface(); + int ReleaseInterface(void *ifc); + const char *GetTestString(); + int ServiceNotify(int msg, int param1, int param2); + +protected: + RECVS_DISPATCH; +}; + + diff --git a/Src/playlist/ScriptObjectService.cpp b/Src/playlist/ScriptObjectService.cpp new file mode 100644 index 00000000..01c64410 --- /dev/null +++ b/Src/playlist/ScriptObjectService.cpp @@ -0,0 +1,42 @@ +#include "ScriptObjectService.h" +#include <api/script/objects/rootobjcontroller.h> +#include "SPlaylist.h" +#include "SPlaylists.h" +#include "SPlaylistsEnumerator.h" +#include "SPlaylistManager.h" + +ScriptObjectController *script_root=0; +PlaylistScriptController playlistController; +PlaylistsScriptController playlistsController; +PlaylistsEnumeratorScriptController playlistsEnumeratorController; +PlaylistManagerScriptController playlistManagerController; + +ScriptObjectController *ScriptObjectService::getController(int n) +{ + switch (n) + { + case 0: + return &playlistController; + case 1: + return &playlistsController; + case 2: + return &playlistsEnumeratorController; + case 3: + return &playlistManagerController; + } + + return 0; +} + + +void ScriptObjectService::onRegisterClasses(ScriptObjectController *rootController) +{ + script_root = rootController; +} + +#define CBCLASS ScriptObjectService +START_DISPATCH; + CB(GETCONTROLLER, getController); + VCB(ONREGISTER, onRegisterClasses); +END_DISPATCH; +#undef CBCLASS diff --git a/Src/playlist/ScriptObjectService.h b/Src/playlist/ScriptObjectService.h new file mode 100644 index 00000000..872657ac --- /dev/null +++ b/Src/playlist/ScriptObjectService.h @@ -0,0 +1,11 @@ +#pragma once +#include <api/service/svcs/svc_scriptobj.h> + +class ScriptObjectService : public svc_scriptObject +{ +public: + ScriptObjectController *getController(int n); + void onRegisterClasses(ScriptObjectController *rootController); +protected: + RECVS_DISPATCH; +}; diff --git a/Src/playlist/XMLString.cpp b/Src/playlist/XMLString.cpp new file mode 100644 index 00000000..ea4d0cbf --- /dev/null +++ b/Src/playlist/XMLString.cpp @@ -0,0 +1,42 @@ +#include "main.h" +#include "XMLString.h" +#include "../nu/strsafe.h" + +XMLString::XMLString() +{ + data[ 0 ] = 0; +} + +void XMLString::Reset() +{ + data[ 0 ] = 0; +} + +const wchar_t *XMLString::GetString() +{ + return data; +} + +void XMLString::StartTag( const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params ) +{ + data[ 0 ] = 0; +} + + +void XMLString::TextHandler( const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str ) +{ + StringCchCatW( data, 256, str ); +} + + +void XMLString::ManualSet( const wchar_t *string ) +{ + StringCchCatW( data, 256, string ); +} + +#define CBCLASS XMLString +START_DISPATCH; +VCB( ONSTARTELEMENT, StartTag ) +VCB( ONCHARDATA, TextHandler ) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/XMLString.h b/Src/playlist/XMLString.h new file mode 100644 index 00000000..b80380eb --- /dev/null +++ b/Src/playlist/XMLString.h @@ -0,0 +1,29 @@ +#ifndef NULLSOFT_WINAMP_XMLSTRING_H +#define NULLSOFT_WINAMP_XMLSTRING_H + + +#include "../xml/ifc_xmlreadercallback.h" +/* +this one is an xml callback that just saves the last encountered string +*/ + + +class XMLString : public ifc_xmlreadercallback +{ +public: + XMLString(); + void Reset(); + const wchar_t *GetString(); + void ManualSet(const wchar_t *string); +private: + /* XML callbacks */ + void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag); + void TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str); + + wchar_t data[256]; // for now, we'll make it dynamic later + + RECVS_DISPATCH; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/_ifc_playlistentry.h b/Src/playlist/_ifc_playlistentry.h new file mode 100644 index 00000000..71dee76f --- /dev/null +++ b/Src/playlist/_ifc_playlistentry.h @@ -0,0 +1,49 @@ +#ifndef NULLSOFT_IFC_PLAYLIST_ENTRY_H +#define NULLSOFT_IFC_PLAYLIST_ENTRY_H + +#include <bfc/dispatch.h> + +class ifc_playlistentry : public Dispatchable +{ +protected: + ifc_playlistentry() {} + ~ifc_playlistentry() {} + + +public: + DISPATCH_CODES + { + IFC_PLAYLISTENTRY_GETFILENAME = 10, + IFC_PLAYLISTENTRY_GETTITLE = 20, + IFC_PLAYLISTENTRY_GETLENGTHMS = 30, + IFC_PLAYLISTENTRY_GETEXTENDEDINFO = 40, + }; + + + size_t GetFilename( wchar_t *filename, size_t filenameCch ); + size_t GetTitle( wchar_t *title, size_t titleCch ); + int GetLengthInMilliseconds(); + size_t GetExtendedInfo( const wchar_t *metadata, wchar_t *info, size_t infoCch ); +}; + +inline size_t ifc_playlistentry::GetFilename( wchar_t *filename, size_t filenameCch ) +{ + return _call( IFC_PLAYLISTENTRY_GETFILENAME, (size_t)0, filename, filenameCch ); +} + +inline size_t ifc_playlistentry::GetTitle( wchar_t *title, size_t titleCch ) +{ + return _call( IFC_PLAYLISTENTRY_GETTITLE, (size_t)0, title, titleCch ); +} + +inline int ifc_playlistentry::GetLengthInMilliseconds() +{ + return _call( IFC_PLAYLISTENTRY_GETLENGTHMS, (int)-1 ); +} + +inline size_t ifc_playlistentry::GetExtendedInfo( const wchar_t *metadata, wchar_t *info, size_t infoCch ) +{ + return _call( IFC_PLAYLISTENTRY_GETEXTENDEDINFO, (size_t)0, metadata, info, infoCch ); +} + +#endif
\ No newline at end of file diff --git a/Src/playlist/api__playlist.h b/Src/playlist/api__playlist.h new file mode 100644 index 00000000..eae25572 --- /dev/null +++ b/Src/playlist/api__playlist.h @@ -0,0 +1,58 @@ +#ifndef NULLSOFT_PLAYLIST_API_H +#define NULLSOFT_PLAYLIST_API_H + +#include <api/service/api_service.h> + +#include <api/application/api_application.h> +#define WASABI_API_APP applicationApi + +#include <api/syscb/api_syscb.h> +#define WASABI_API_SYSCB sysCallbackApi + +#ifndef ML_PLG_EXPORTS +#include "../Agave/Config/api_config.h" +#endif + +#include <api/script/api_maki.h> +#define WASABI_API_MAKI makiApi + +#include "../Winamp/JSAPI2_api_security.h" +extern JSAPI2::api_security *jsapi2_security; +#define AGAVE_API_JSAPI2_SECURITY jsapi2_security + +#include "../Winamp/api_stats.h" +extern api_stats *statsApi; +#define AGAVE_API_STATS statsApi + +#include <api/service/waservicefactory.h> + +template <class api_t> +class QuickService +{ +public: + QuickService( GUID p_serviceGUID ) + { + _sf = WASABI_API_SVC->service_getServiceByGuid( p_serviceGUID ); + if ( _sf ) + _api = (api_t *)_sf->getInterface(); + } + ~QuickService() + { + _sf->releaseInterface( _api ); + _api = 0; + } + bool OK() + { + return _api != 0; + } + api_t *operator ->() + { + return _api; + } + waServiceFactory *_sf = NULL; + api_t *_api = 0; +}; + +#include "../Agave/Language/api_language.h" + +#endif // !NULLSOFT_PLAYLIST_API_H
\ No newline at end of file diff --git a/Src/playlist/api_playlistmanager.h b/Src/playlist/api_playlistmanager.h new file mode 100644 index 00000000..f04df555 --- /dev/null +++ b/Src/playlist/api_playlistmanager.h @@ -0,0 +1,161 @@ +#ifndef NULLSOFT_API_PLAYLISTMANAGER_H +#define NULLSOFT_API_PLAYLISTMANAGER_H + +#include <bfc/dispatch.h> + +class ifc_playlistloadercallback; +class ifc_playlist; +class ifc_playlistdirectorycallback; +enum +{ + PLAYLISTMANAGER_SUCCESS = 0, + + PLAYLISTMANAGER_FAILED = 1, + PLAYLISTMANAGER_LOAD_NO_LOADER = 1, + PLAYLISTMANAGER_LOAD_LOADER_OPEN_FAILED = 2, +}; + +class api_playlistmanager : public Dispatchable +{ +protected: + api_playlistmanager() {} + ~api_playlistmanager() {} + +public: + int Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ); + int LoadAs( const wchar_t *filename, const wchar_t *ext, ifc_playlistloadercallback *playlist ); // call with ext in the format ".pls" + int LoadFromDialog( const wchar_t *fns, ifc_playlistloadercallback *playlist ); + int LoadFromANSIDialog( const char *fns, ifc_playlistloadercallback *playlist ); + + int Save( const wchar_t *filename, ifc_playlist *playlist ); + + size_t Copy( const wchar_t *destFn, const wchar_t *srcFn ); // returns number of items copied + + size_t CountItems( const wchar_t *filename ); + + int GetLengthMilliseconds( const wchar_t *filename ); + uint64_t GetLongLengthMilliseconds( const wchar_t *filename ); + + void Randomize( ifc_playlist *playlist ); + void Reverse( ifc_playlist *playlist ); + + void LoadDirectory( const wchar_t *directory, ifc_playlistloadercallback *callback, ifc_playlistdirectorycallback *dirCallback ); + + bool CanLoad( const wchar_t *filename ); + + void GetExtensionList( wchar_t *extensionList, size_t extensionListCch ); + void GetFilterList( wchar_t *extensionList, size_t extensionListCch ); + + const wchar_t *EnumExtension( size_t num ); + +public: + DISPATCH_CODES + { + API_PLAYLISTMANAGER_LOAD = 10, + API_PLAYLISTMANAGER_LOADNULLDELIMITED = 11, + API_PLAYLISTMANAGER_LOADNULLDELIMITED_ANSI = 12, + API_PLAYLISTMANAGER_LOADAS = 13, + API_PLAYLISTMANAGER_SAVE = 20, + API_PLAYLISTMANAGER_COPY = 30, + API_PLAYLISTMANAGER_COUNT = 40, + API_PLAYLISTMANAGER_GETLENGTH = 50, + API_PLAYLISTMANAGER_GETLONGLENGTH = 51, + API_PLAYLISTMANAGER_LOADDIRECTORY = 60, + API_PLAYLISTMANAGER_RANDOMIZE = 100, + API_PLAYLISTMANAGER_REVERSE = 110, + API_PLAYLISTMANAGER_CANLOAD = 120, + API_PLAYLISTMANAGER_GETEXTENSIONLIST = 130, + API_PLAYLISTMANAGER_GETFILTERLIST = 140, + API_PLAYLISTMANAGER_ENUMEXTENSION = 150, + }; +}; + +inline void api_playlistmanager::GetFilterList( wchar_t *extensionList, size_t extensionListCch ) +{ + extensionList[ 0 ] = 0; // just in case no one implements it + extensionList[ 1 ] = 0; + + _voidcall( API_PLAYLISTMANAGER_GETFILTERLIST, extensionList, extensionListCch ); +} + +inline int api_playlistmanager::LoadAs( const wchar_t *filename, const wchar_t *ext, ifc_playlistloadercallback *playlist ) +{ + return _call( API_PLAYLISTMANAGER_LOADAS, (int)PLAYLISTMANAGER_FAILED, filename, ext, playlist ); +} + +inline void api_playlistmanager::GetExtensionList( wchar_t *extensionList, size_t extensionListCch ) +{ + extensionList[ 0 ] = 0; // just in case no one implements it + _voidcall( API_PLAYLISTMANAGER_GETEXTENSIONLIST, extensionList, extensionListCch ); +} + +inline int api_playlistmanager::Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ) +{ + return _call( API_PLAYLISTMANAGER_LOAD, (int)PLAYLISTMANAGER_FAILED, filename, playlist ); +} + +inline int api_playlistmanager::LoadFromDialog( const wchar_t *filename, ifc_playlistloadercallback *playlist ) +{ + return _call( API_PLAYLISTMANAGER_LOADNULLDELIMITED, (int)PLAYLISTMANAGER_FAILED, filename, playlist ); +} + +inline int api_playlistmanager::LoadFromANSIDialog( const char *filename, ifc_playlistloadercallback *playlist ) +{ + return _call( API_PLAYLISTMANAGER_LOADNULLDELIMITED_ANSI, (int)PLAYLISTMANAGER_FAILED, filename, playlist ); +} + +inline int api_playlistmanager::Save( const wchar_t *filename, ifc_playlist *playlist ) +{ + return _call( API_PLAYLISTMANAGER_SAVE, (int)PLAYLISTMANAGER_FAILED, filename, playlist ); +} + +inline size_t api_playlistmanager::Copy( const wchar_t *destFn, const wchar_t *srcFn ) +{ + return _call( API_PLAYLISTMANAGER_COPY, (size_t)0, destFn, srcFn ); +} + +inline size_t api_playlistmanager::CountItems( const wchar_t *filename ) +{ + return _call( API_PLAYLISTMANAGER_COUNT, (size_t)0, filename ); +} + +inline int api_playlistmanager::GetLengthMilliseconds( const wchar_t *filename ) +{ + return _call( API_PLAYLISTMANAGER_GETLENGTH, (int)0, filename ); +} + +inline uint64_t api_playlistmanager::GetLongLengthMilliseconds( const wchar_t *filename ) +{ + return _call( API_PLAYLISTMANAGER_GETLONGLENGTH, (uint64_t)0, filename ); +} + +inline void api_playlistmanager::Randomize( ifc_playlist *playlist ) +{ + _voidcall( API_PLAYLISTMANAGER_RANDOMIZE, playlist ); +} + +inline void api_playlistmanager::Reverse( ifc_playlist *playlist ) +{ + _voidcall( API_PLAYLISTMANAGER_REVERSE, playlist ); +} + +inline void api_playlistmanager::LoadDirectory( const wchar_t *directory, ifc_playlistloadercallback *callback, ifc_playlistdirectorycallback *dirCallback ) +{ + _voidcall( API_PLAYLISTMANAGER_LOADDIRECTORY, directory, callback, dirCallback ); +} + +inline bool api_playlistmanager::CanLoad( const wchar_t *filename ) +{ + return _call( API_PLAYLISTMANAGER_CANLOAD, (bool)true, filename ); +} + +inline const wchar_t *api_playlistmanager::EnumExtension( size_t num ) +{ + return _call( API_PLAYLISTMANAGER_ENUMEXTENSION, (const wchar_t *)0, num ); +} + +// {C5618774-7177-43aa-9906-933C9F40EBDC} +static const GUID api_playlistmanagerGUID = +{ 0xc5618774, 0x7177, 0x43aa, { 0x99, 0x6, 0x93, 0x3c, 0x9f, 0x40, 0xeb, 0xdc } }; + +#endif diff --git a/Src/playlist/api_playlists.h b/Src/playlist/api_playlists.h new file mode 100644 index 00000000..6e205b0f --- /dev/null +++ b/Src/playlist/api_playlists.h @@ -0,0 +1,290 @@ +#ifndef NULLSOFT_PLAYLIST_API_PLAYLISTS_H +#define NULLSOFT_PLAYLIST_API_PLAYLISTS_H + +#include <bfc/dispatch.h> +#include <bfc/platform/guid.h> +#include <bfc/std_mkncc.h> +// manages Winamp's master list of playlists + +/* Important note to users of this API: + This API does not actually parse or in any way read the contents of the playlist files themselves. + It only manages the master "list" of playlists, used in e.g. ml_playlists. + + --- important --- + This also means that some information retrieved through this API can be inaccurate, + such as playlist item length or total time. These values are provided as a cache, + to speed up display of UI. They are not to be relied on for determining how many items + are actually in the playlist. Don't use this value to allocate memory for data structures, + unless it's just an initial guess and you allow for realloc'ing if the count is higher. + ----------------- + + It is recommended (but not required) that you call SetInfo to update calculated values, + such as playlist item length, whenever you parse the playlist and have accurate information. + + If you need playlist parsing, use api_playlistmanager. + + This API is thread-safe, as long as you properly call Lock/Unlock + Methods which don't require external locking are marked with [*] + Note that these methods still lock internally + */ + +enum +{ + API_PLAYLISTS_SUCCESS = 0, + API_PLAYLISTS_FAILURE = 1, // general purpose failure + API_PLAYLISTS_UNKNOWN_INFO_GUID = 2, // bad GUID passed to Set/GetInfo + API_PLAYLISTS_UNABLE_TO_LOAD_PLAYLISTS = 3, // take that variable name, FORTRAN77! + API_PLAYLISTS_INVALID_INDEX = 4, // index you passed was out of range + API_PLAYLISTS_BAD_SIZE = 5, // bad dataLen passed to Set/GetInfo +}; + +class api_playlists : public Dispatchable +{ +protected: + api_playlists() {} + virtual ~api_playlists() {} + +public: + // call these to lock the list of playlists so no one changes in the middle of an operation. be careful with this! + // you can use AutoLockT<api_playlists> to help you out + // indices are only valid between these two calls. call GetGUID() if you need session-persistent identifiers + void Lock(); + void Unlock(); + + size_t GetIterator(); + /* this value changes each time a modification is made that would invalidate indices previously retrieved. + It does not change when information is changed + Use it to test if your index is out of date. + example: + size_t playlistIterator = playlists->GetIterator(); + playlists->GetPosition(myGUID, &index); + // ... do a bunch of stuff + if (playlistIterator != playlists->GetIterator()) + playlists->GetPosition(myGUID, &index); + + This is meant as a tool to aid implementations that want to cache indices to prevent too many GetPosition() lookups + you don't need this function for casual usage of the API + */ + + int Sort( size_t sort_type ); + + void Flush(); // [*] flushes playlists to disk. avoid usage - mainly useful when some program is parsing the playlists.xml file externally + + // get information about playlists + size_t GetCount(); // returns number of playlists + + const wchar_t *GetFilename( size_t index ); // returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE. only valid until you Unlock() + const wchar_t *GetName( size_t index ); // returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE. only valid until you Unlock() + + GUID GetGUID( size_t index ); // retrieves a unique ID which identifies this playlist + + int GetPosition( GUID playlist_guid, size_t *index ); // retrieves the index where a particular playlist ID lives. returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE + int GetInfo( size_t index, GUID info, void *data, size_t dataLen ); // This is for getting "extra" data, see list of GUIDs below. returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE + + // manipulating playlists + // at this time, it is not recommended that you use this API. It is reserved for ml_playlists. + int MoveBefore( size_t index1, size_t index2 ); // moves playlist at position index1 to before index2. setting index2 to anything larger than GetCount() moves to end + + size_t AddPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid = INVALID_GUID ); // [*] adds a new playlist, returns new index. Generates a GUID if you don't pass third parameter. returns (size_t)-1 on error and (size_t)-2 if already exists + // note: AddPlaylist locks internally, but you need to lock externally if you want to trust the return value + + size_t AddCloudPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid = INVALID_GUID ); // [*] adds a new playlist, returns new index. Generates a GUID if you don't pass third parameter. returns (size_t)-1 on error and (size_t)-2 if already exists + // note: AddCloudPlaylist locks internally, but you need to lock externally if you want to trust the return value + + size_t AddPlaylist_NoCallback( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid = INVALID_GUID ); + // same as AddPlaylist, but doesn't do a syscallback, use when you want to make a few SetInfo calls, + // when you are done, call WASABI_API_SYSCB->syscb_issueCallback(api_playlists::SYSCALLBACK, api_playlists::PLAYLIST_REMOVED_POST, index, 0); yourself + + int SetGUID( size_t index, GUID playlist_guid ); // sets (overrides) a playlist ID. Don't use unless you have some very specific need + int RenamePlaylist( size_t index, const wchar_t *name ); + int MovePlaylist( size_t index, const wchar_t *filename ); // sets a new filename. NOTE: IT'S UP TO YOU TO PHYSICALLY MOVE/RENAME/CREATE THE NEW FILENAME. + int SetInfo( size_t index, GUID info, void *data, size_t dataLen ); // returns API_PLAYLISTS_SUCCESS or API_PLAYLISTS_FAILURE + int RemovePlaylist( size_t index ); // removes a particular playlist + int ClearPlaylists(); // [*] clears the entire list of playlists. Use at your own risk :) + + /* + callbacks. these are sent through api_syscb. + callbacks are typically done within the api_playlists Lock, + so be careful not to deadlock by waiting on another thread that is also using api_playlists + TODO + probably want move, remove, add + need to think through adding, though. Someone might add a playlist and then SetInfo, so don't want to call syscb too early + */ + + enum + { + SYSCALLBACK = MK4CC( 'p', 'l', 'a', 'y' ), + PLAYLIST_ADDED = 10, // param1 = index + PLAYLIST_REMOVED_PRE = 20, // param1 = index, called BEFORE it's removed internally, so you can still query for data (Get* function calls) + PLAYLIST_REMOVED_POST = 30, // no parameters, called after it's removed internally + PLAYLIST_RENAMED = 40, // param1 = index + /* These two callbacks are made by you (not api_playlists) + * pass some unique ID as param2 (e.g. some function pointer or pointer to a global variable) + * so that you can identify your own callbacks if you also listen for these events + */ + PLAYLIST_SAVED = 50, // param1 = index. You should send this when you save a playlist. Surround with Lock()/Unlock() so that the index is valid + PLAYLIST_FLUSH_REQUEST = 60, // param1 = index. Call before you load a playlist to request anyone who might be currently modifying the same playlist to save + }; + + enum + { + SORT_TITLE_ASCENDING, + SORT_TITLE_DESCENDING, + SORT_NUMBER_ASCENDING, + SORT_NUMBER_DESCENDING, + }; + + DISPATCH_CODES + { + API_PLAYLISTS_LOCK = 10, + API_PLAYLISTS_UNLOCK = 20, + API_PLAYLISTS_GETITERATOR = 30, + API_PLAYLISTS_FLUSH = 40, + API_PLAYLISTS_GETCOUNT = 50, + API_PLAYLISTS_GETFILENAME = 60, + API_PLAYLISTS_GETNAME = 70, + API_PLAYLISTS_GETGUID = 80, + API_PLAYLISTS_GETPOSITION = 90, + API_PLAYLISTS_GETINFO = 100, + API_PLAYLISTS_MOVEBEFORE = 110, + API_PLAYLISTS_ADDPLAYLIST = 120, + API_PLAYLISTS_ADDPLAYLISTNOCB = 121, + API_PLAYLISTS_ADDCLOUDPLAYLIST = 122, + API_PLAYLISTS_SETGUID = 130, + API_PLAYLISTS_RENAMEPLAYLIST = 140, + API_PLAYLISTS_MOVEPLAYLIST = 150, + API_PLAYLISTS_SETINFO = 160, + API_PLAYLISTS_REMOVEPLAYLIST = 170, + API_PLAYLISTS_CLEARPLAYLISTS = 180, + API_PLAYLISTS_SORT = 190, + }; +}; + +// Info GUIDS + +// {C4FAD6CE-DA38-47b0-AAA9-E966D8E8E7C5} +static const GUID api_playlists_itemCount = +{ 0xc4fad6ce, 0xda38, 0x47b0, { 0xaa, 0xa9, 0xe9, 0x66, 0xd8, 0xe8, 0xe7, 0xc5 } }; + +// {D4E0E000-A3F5-4f18-ADA5-F2BA40689593} +static const GUID api_playlists_totalTime = +{ 0xd4e0e000, 0xa3f5, 0x4f18, { 0xad, 0xa5, 0xf2, 0xba, 0x40, 0x68, 0x95, 0x93 } }; + +// {F6E1AB19-6931-4cc9-BCBA-4B40DE2A959F} +static const GUID api_playlists_iTunesID = +{ 0xf6e1ab19, 0x6931, 0x4cc9, { 0xbc, 0xba, 0x4b, 0x40, 0xde, 0x2a, 0x95, 0x9f } }; + +// {B83AD244-7CD3-4a24-B2C5-41F42CA37F14} +static const GUID api_playlists_cloud = +{ 0xb83ad244, 0x7cd3, 0x4a24, { 0xb2, 0xc5, 0x41, 0xf4, 0x2c, 0xa3, 0x7f, 0x14 } }; + + +inline void api_playlists::Lock() +{ + _voidcall( API_PLAYLISTS_LOCK ); +} +inline void api_playlists::Unlock() +{ + _voidcall( API_PLAYLISTS_UNLOCK ); +} + +inline size_t api_playlists::GetIterator() +{ + return _call( API_PLAYLISTS_GETITERATOR, 0 ); +} + +inline void api_playlists::Flush() +{ + _voidcall( API_PLAYLISTS_FLUSH ); +} + +inline int api_playlists::Sort( size_t sort_type ) +{ + return _call( API_PLAYLISTS_SORT, 0, sort_type ); +} + +inline size_t api_playlists::GetCount() +{ + return _call( API_PLAYLISTS_GETCOUNT, 0 ); +} + +inline const wchar_t *api_playlists::GetFilename( size_t index ) +{ + return _call( API_PLAYLISTS_GETFILENAME, (const wchar_t *)0, index ); +} + +inline const wchar_t *api_playlists::GetName( size_t index ) +{ + return _call( API_PLAYLISTS_GETNAME, (const wchar_t *)0, index ); +} + +inline GUID api_playlists::GetGUID( size_t index ) +{ + return _call( API_PLAYLISTS_GETGUID, INVALID_GUID, index ); +} + +inline int api_playlists::GetPosition( GUID playlist_guid, size_t *index ) +{ + return _call( API_PLAYLISTS_GETPOSITION, API_PLAYLISTS_FAILURE, playlist_guid, index ); +} + +inline int api_playlists::GetInfo( size_t index, GUID info, void *data, size_t dataLen ) +{ + return _call( API_PLAYLISTS_GETINFO, API_PLAYLISTS_FAILURE, index, info, data, dataLen ); +} + +inline int api_playlists::MoveBefore( size_t index1, size_t index2 ) +{ + return _call( API_PLAYLISTS_MOVEBEFORE, API_PLAYLISTS_FAILURE, index1, index2 ); +} + +inline size_t api_playlists::AddPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid ) +{ + return _call( API_PLAYLISTS_ADDPLAYLIST, (size_t)-1, filename, playlistName, playlist_guid ); +} + +inline size_t api_playlists::AddPlaylist_NoCallback( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid ) +{ + return _call( API_PLAYLISTS_ADDPLAYLISTNOCB, (size_t)-1, filename, playlistName, playlist_guid ); +} + +inline size_t api_playlists::AddCloudPlaylist( const wchar_t *filename, const wchar_t *playlistName, GUID playlist_guid ) +{ + return _call( API_PLAYLISTS_ADDCLOUDPLAYLIST, (size_t)-1, filename, playlistName, playlist_guid ); +} + +inline int api_playlists::SetGUID( size_t index, GUID playlist_guid ) +{ + return _call( API_PLAYLISTS_SETGUID, API_PLAYLISTS_FAILURE, index, playlist_guid ); +} + +inline int api_playlists::RenamePlaylist( size_t index, const wchar_t *name ) +{ + return _call( API_PLAYLISTS_RENAMEPLAYLIST, API_PLAYLISTS_FAILURE, index, name ); +} + +inline int api_playlists::MovePlaylist( size_t index, const wchar_t *filename ) +{ + return _call( API_PLAYLISTS_MOVEPLAYLIST, API_PLAYLISTS_FAILURE, index, filename ); +} + +inline int api_playlists::SetInfo( size_t index, GUID info, void *data, size_t dataLen ) +{ + return _call( API_PLAYLISTS_SETINFO, API_PLAYLISTS_FAILURE, index, info, data, dataLen ); +} + +inline int api_playlists::RemovePlaylist( size_t index ) +{ + return _call( API_PLAYLISTS_REMOVEPLAYLIST, API_PLAYLISTS_FAILURE, index ); +} + +inline int api_playlists::ClearPlaylists() +{ + return _call( API_PLAYLISTS_CLEARPLAYLISTS, API_PLAYLISTS_FAILURE ); +} + +// {2DC3C390-D9B8-4a49-B230-EF240ADDDCDB} +static const GUID api_playlistsGUID = +{ 0x2dc3c390, 0xd9b8, 0x4a49, { 0xb2, 0x30, 0xef, 0x24, 0xa, 0xdd, 0xdc, 0xdb } }; + +#endif
\ No newline at end of file diff --git a/Src/playlist/factory_Handler.cpp b/Src/playlist/factory_Handler.cpp new file mode 100644 index 00000000..199dd1ad --- /dev/null +++ b/Src/playlist/factory_Handler.cpp @@ -0,0 +1,97 @@ +#include "api__playlist.h" +#include "factory_Handler.h" +#include "handler.h" +#include "api/service/services.h" + +M3UHandler m3uHandler; +PLSHandler plsHandler; +B4SHandler b4sHandler; + +static const char m3uServiceName[] = "M3U Playlist Handler"; +static const char plsServiceName[] = "PLS Playlist Handler"; + +#define DEFINE_HANDLER_FACTORY(CLASSNAME, className) const char *CLASSNAME ## HandlerFactory::GetServiceName() { return #CLASSNAME ## "Playlist Handler"; }\ + GUID CLASSNAME ## HandlerFactory::GetGUID(){ return className ## HandlerGUID;}\ + void *CLASSNAME ## HandlerFactory::GetInterface(int global_lock){ return &className ## Handler;} + +DEFINE_HANDLER_FACTORY(M3U, m3u); +DEFINE_HANDLER_FACTORY(PLS, pls); +DEFINE_HANDLER_FACTORY(B4S, b4s); + +/* --------------------------------------------------------------------- */ +FOURCC CommonHandlerFactory::GetServiceType() +{ + return WaSvc::PLAYLISTHANDLER; +} + +int CommonHandlerFactory::SupportNonLockingInterface() +{ + return 1; +} + +int CommonHandlerFactory::ReleaseInterface(void *ifc) +{ + return 1; +} + +const char *CommonHandlerFactory::GetTestString() +{ + return NULL; +} + +int CommonHandlerFactory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS M3UHandlerFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +#undef CBCLASS +#define CBCLASS CommonHandlerFactory +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; + +#undef CBCLASS +#define CBCLASS PLSHandlerFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +#undef CBCLASS +#define CBCLASS CommonHandlerFactory +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; + + + +#undef CBCLASS +#define CBCLASS B4SHandlerFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +#undef CBCLASS +#define CBCLASS CommonHandlerFactory +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; + + diff --git a/Src/playlist/factory_Handler.h b/Src/playlist/factory_Handler.h new file mode 100644 index 00000000..c952e233 --- /dev/null +++ b/Src/playlist/factory_Handler.h @@ -0,0 +1,30 @@ +#ifndef NULLSOFT_FACTORY_HANDLER_H +#define NULLSOFT_FACTORY_HANDLER_H + +#include <api/service/waservicefactory.h> +#include <api/service/services.h> + +class CommonHandlerFactory : public waServiceFactory +{ +public: + + FOURCC GetServiceType(); + int SupportNonLockingInterface(); + int ReleaseInterface(void *ifc); + const char *GetTestString(); + int ServiceNotify(int msg, int param1, int param2); + +}; + +#define DECLARE_HANDLER_FACTORY(CLASSNAME) class CLASSNAME : public CommonHandlerFactory {\ +public:\ + const char *GetServiceName();\ + GUID GetGUID();\ + void *GetInterface(int global_lock);\ +protected:\ + RECVS_DISPATCH;} + +DECLARE_HANDLER_FACTORY(M3UHandlerFactory); +DECLARE_HANDLER_FACTORY(PLSHandlerFactory); +DECLARE_HANDLER_FACTORY(B4SHandlerFactory); +#endif
\ No newline at end of file diff --git a/Src/playlist/factory_playlistmanager.cpp b/Src/playlist/factory_playlistmanager.cpp new file mode 100644 index 00000000..6de39fb8 --- /dev/null +++ b/Src/playlist/factory_playlistmanager.cpp @@ -0,0 +1,65 @@ +#include "main.h" +#include "api__playlist.h" +#include "factory_playlistmanager.h" +#include "PlaylistManager.h" + +static const char serviceName[] = "Playlist Manager"; + +FOURCC PlaylistManagerFactory::GetServiceType() +{ + return WaSvc::UNIQUE; +} + +const char *PlaylistManagerFactory::GetServiceName() +{ + return serviceName; +} + +GUID PlaylistManagerFactory::GetGUID() +{ + return api_playlistmanagerGUID; +} + +void *PlaylistManagerFactory::GetInterface(int global_lock) +{ +// if (global_lock) +// WASABI_API_SVC->service_lock(this, (void *)ifc); + return &playlistManager; +} + +int PlaylistManagerFactory::SupportNonLockingInterface() +{ + return 1; +} + +int PlaylistManagerFactory::ReleaseInterface(void *ifc) +{ + //WASABI_API_SVC->service_unlock(ifc); + return 1; +} + +const char *PlaylistManagerFactory::GetTestString() +{ + return 0; +} + +int PlaylistManagerFactory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS PlaylistManagerFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; diff --git a/Src/playlist/factory_playlistmanager.h b/Src/playlist/factory_playlistmanager.h new file mode 100644 index 00000000..50dc893c --- /dev/null +++ b/Src/playlist/factory_playlistmanager.h @@ -0,0 +1,28 @@ +#ifndef NULLSOFT_FACTORY_PLAYLISTMANAGER_H +#define NULLSOFT_FACTORY_PLAYLISTMANAGER_H + +#include "api__playlist.h" +#include "api/service/waservicefactory.h" +#include "api/service/services.h" + +class PlaylistManagerFactory : public waServiceFactory +{ +public: + FOURCC GetServiceType(); + const char *GetServiceName(); + GUID GetGUID(); + void *GetInterface( int global_lock ); + + int SupportNonLockingInterface(); + int ReleaseInterface( void *ifc ); + + const char *GetTestString(); + + int ServiceNotify( int msg, int param1, int param2 ); + +protected: + RECVS_DISPATCH; +}; + + +#endif
\ No newline at end of file diff --git a/Src/playlist/factory_playlists.cpp b/Src/playlist/factory_playlists.cpp new file mode 100644 index 00000000..841bac6d --- /dev/null +++ b/Src/playlist/factory_playlists.cpp @@ -0,0 +1,63 @@ +#include "main.h" +#include "api__playlist.h" +#include "factory_playlists.h" +#include "Playlists.h" + +Playlists playlists; +static const char serviceName[] = "Playlists"; + +FOURCC PlaylistsFactory::GetServiceType() +{ + return WaSvc::UNIQUE; +} + +const char *PlaylistsFactory::GetServiceName() +{ + return serviceName; +} + +GUID PlaylistsFactory::GetGUID() +{ + return api_playlistsGUID; +} + +void *PlaylistsFactory::GetInterface(int global_lock) +{ +// if (global_lock) +// WASABI_API_SVC->service_lock(this, (void *)ifc); + return &playlists; +} + +int PlaylistsFactory::SupportNonLockingInterface() +{ + return 1; +} + +int PlaylistsFactory::ReleaseInterface(void *ifc) +{ + //WASABI_API_SVC->service_unlock(ifc); + return 1; +} + +const char *PlaylistsFactory::GetTestString() +{ + return 0; +} + +int PlaylistsFactory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#define CBCLASS PlaylistsFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/factory_playlists.h b/Src/playlist/factory_playlists.h new file mode 100644 index 00000000..d31d16da --- /dev/null +++ b/Src/playlist/factory_playlists.h @@ -0,0 +1,25 @@ +#ifndef NULLSOFT_FACTORY_PLAYLISTS_H +#define NULLSOFT_FACTORY_PLAYLISTS_H + +#include "api__playlist.h" +#include "api/service/waservicefactory.h" +#include "api/service/services.h" + +class PlaylistsFactory : public waServiceFactory +{ +public: + FOURCC GetServiceType(); + const char *GetServiceName(); + GUID GetGUID(); + void *GetInterface( int global_lock ); + int SupportNonLockingInterface(); + int ReleaseInterface( void *ifc ); + const char *GetTestString(); + int ServiceNotify( int msg, int param1, int param2 ); + +protected: + RECVS_DISPATCH; +}; + + +#endif
\ No newline at end of file diff --git a/Src/playlist/ifc_playlist.h b/Src/playlist/ifc_playlist.h new file mode 100644 index 00000000..656623c3 --- /dev/null +++ b/Src/playlist/ifc_playlist.h @@ -0,0 +1,139 @@ +#ifndef NULLSOFT_IFC_PLAYLIST_H +#define NULLSOFT_IFC_PLAYLIST_H + +#include "bfc/dispatch.h" +#include "bfc/platform/types.h" + +enum +{ + PLAYLIST_SUCCESS = 0, + PLAYLIST_UNIMPLEMENTED = 1, +}; + +class ifc_playlist : public Dispatchable +{ +protected: + ifc_playlist() {} + ~ifc_playlist() {} + +public: + DISPATCH_CODES + { + IFC_PLAYLIST_CLEAR = 10, + //IFC_PLAYLIST_APPENDWITHINFO = 20, + //IFC_PLAYLIST_APPEND = 30, + IFC_PLAYLIST_GETNUMITEMS = 40, + IFC_PLAYLIST_GETITEM = 50, + IFC_PLAYLIST_GETITEMTITLE = 60, + IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS = 70, + IFC_PLAYLIST_GETITEMEXTENDEDINFO = 80, + IFC_PLAYLIST_REVERSE = 90, + IFC_PLAYLIST_SWAP = 100, + IFC_PLAYLIST_RANDOMIZE = 110, + IFC_PLAYLIST_REMOVE = 120, + IFC_PLAYLIST_SORTBYTITLE = 130, + IFC_PLAYLIST_SORTBYFILENAME = 140, + IFC_PLAYLIST_SORTBYDIRECTORY = 150, + }; + + void Clear(); + //void AppendWithInfo(const wchar_t *filename, const char *title, int lengthInMS); + //void Append(const wchar_t *filename); + + size_t GetNumItems(); + + size_t GetItem( size_t item, wchar_t *filename, size_t filenameCch ); + + size_t GetItemTitle( size_t item, wchar_t *title, size_t titleCch ); + + int GetItemLengthMilliseconds( size_t item ); // TODO: maybe microsecond for better resolution? + + size_t GetItemExtendedInfo( size_t item, const wchar_t *metadata, wchar_t *info, size_t infoCch ); + + int Reverse(); // optional, return 1 to indicate that you did the reversal (otherwise, caller must perform manually) + int Swap( size_t item1, size_t item2 ); + int Randomize( int ( *generator )( ) ); // optional, return 1 to indicate that you did the randomization (otherwise, caller must perform manually) + void Remove( size_t item ); + + int SortByTitle(); // optional, return 1 to indicate that you did the sort (otherwise, caller must perform manually) + int SortByFilename(); // optional, return 1 to indicate that you did the sort (otherwise, caller must perform manually) + int SortByDirectory(); +}; + +inline void ifc_playlist::Clear() +{ + _voidcall( IFC_PLAYLIST_CLEAR ); +} +/* +inline void ifc_playlist::AppendWithInfo(const wchar_t *filename, const char *title, int lengthInMS) +{ +_voidcall(IFC_PLAYLIST_APPENDWITHINFO, filename, title, lengthInMS); +} +*/ +/* +inline void ifc_playlist::Append(const wchar_t *filename) +{ +_voidcall(IFC_PLAYLIST_APPEND, filename); +}*/ + +inline size_t ifc_playlist::GetNumItems() +{ + return _call( IFC_PLAYLIST_GETNUMITEMS, (size_t)0 ); +} + +inline size_t ifc_playlist::GetItem( size_t item, wchar_t *filename, size_t filenameCch ) +{ + return _call( IFC_PLAYLIST_GETITEM, (size_t)0, item, filename, filenameCch ); +} + +inline size_t ifc_playlist::GetItemTitle( size_t item, wchar_t *title, size_t titleCch ) +{ + return _call( IFC_PLAYLIST_GETITEMTITLE, (size_t)0, item, title, titleCch ); +} + +inline int ifc_playlist::GetItemLengthMilliseconds( size_t item ) +{ + return _call( IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS, (int)-1, item ); +} + +inline size_t ifc_playlist::GetItemExtendedInfo( size_t item, const wchar_t *metadata, wchar_t *info, size_t infoCch ) +{ + return _call( IFC_PLAYLIST_GETITEMEXTENDEDINFO, (size_t)0, item, metadata, info, infoCch ); +} + +inline int ifc_playlist::Reverse() +{ + return _call( IFC_PLAYLIST_REVERSE, (int)PLAYLIST_UNIMPLEMENTED ); +} + +inline int ifc_playlist::Swap( size_t item1, size_t item2 ) +{ + return _call( IFC_PLAYLIST_SWAP, (int)PLAYLIST_UNIMPLEMENTED, item1, item2 ); +} + +inline int ifc_playlist::Randomize( int ( *generator )( ) ) +{ + return _call( IFC_PLAYLIST_RANDOMIZE, (int)PLAYLIST_UNIMPLEMENTED, generator ); +} + +inline void ifc_playlist::Remove( size_t item ) +{ + _voidcall( IFC_PLAYLIST_REMOVE, item ); +} + +inline int ifc_playlist::SortByTitle() +{ + return _call( IFC_PLAYLIST_SORTBYTITLE, (int)0 ); +} + +inline int ifc_playlist::SortByFilename() +{ + return _call( IFC_PLAYLIST_SORTBYFILENAME, (int)0 ); +} + +inline int ifc_playlist::SortByDirectory() +{ + return _call( IFC_PLAYLIST_SORTBYDIRECTORY, (int)0 ); +} + +#endif
\ No newline at end of file diff --git a/Src/playlist/ifc_playlistT.h b/Src/playlist/ifc_playlistT.h new file mode 100644 index 00000000..7b9d96d5 --- /dev/null +++ b/Src/playlist/ifc_playlistT.h @@ -0,0 +1,48 @@ +#pragma once +#include "ifc_playlist.h" +template <class T> +class ifc_playlistT : public ifc_playlist +{ +protected: + ifc_playlistT() {} + ~ifc_playlistT() {} + + void Clear(); + //void AppendWithInfo(const wchar_t *filename, const char *title, int lengthInMS); + //void Append(const wchar_t *filename); + + size_t GetNumItems() { return 0; } + size_t GetItem( size_t item, wchar_t *filename, size_t filenameCch ) { return 0; } + size_t GetItemTitle( size_t item, wchar_t *title, size_t titleCch ) { return 0; } + int GetItemLengthMilliseconds( size_t item ) { return -1; } + size_t GetItemExtendedInfo( size_t item, const wchar_t *metadata, wchar_t *info, size_t infoCch ) { return 0; } + int Reverse() { return PLAYLIST_UNIMPLEMENTED; } + int Swap( size_t item1, size_t item2 ) { return PLAYLIST_UNIMPLEMENTED; } + int Randomize( int ( *generator )( ) ) { return PLAYLIST_UNIMPLEMENTED; } + void Remove( size_t item ) {} + int SortByTitle() { return 0; } + int SortByFilename() { return 0; } + int SortByDirectory() { return 0; } + +#define CBCLASS T +#define CBCLASST ifc_playlistT<T> + START_DISPATCH_INLINE; + VCBT( IFC_PLAYLIST_CLEAR, Clear ) + //M_VCB( IFC_PLAYLIST_APPENDWITHINFO, AppendWithInfo) + //M_VCB( IFC_PLAYLIST_APPEND, Append) + CBT( IFC_PLAYLIST_GETNUMITEMS, GetNumItems ) + CBT( IFC_PLAYLIST_GETITEM, GetItem ) + CBT( IFC_PLAYLIST_GETITEMTITLE, GetItemTitle ) + CBT( IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds ) + CBT( IFC_PLAYLIST_GETITEMEXTENDEDINFO, GetItemExtendedInfo ) + CBT( IFC_PLAYLIST_REVERSE, Reverse ) + CBT( IFC_PLAYLIST_SWAP, Swap ) + CBT( IFC_PLAYLIST_RANDOMIZE, Randomize ) + VCBT( IFC_PLAYLIST_REMOVE, Remove ) + CBT( IFC_PLAYLIST_SORTBYTITLE, SortByTitle ) + CBT( IFC_PLAYLIST_SORTBYFILENAME, SortByFilename ) + CBT( IFC_PLAYLIST_SORTBYDIRECTORY, SortByDirectory ) + END_DISPATCH; +#undef CBCLASS +#undef CBCLASST +}; diff --git a/Src/playlist/ifc_playlistdirectorycallback.h b/Src/playlist/ifc_playlistdirectorycallback.h new file mode 100644 index 00000000..1e8a09bd --- /dev/null +++ b/Src/playlist/ifc_playlistdirectorycallback.h @@ -0,0 +1,33 @@ +#ifndef NULLSOFT_IFC_PLAYLISTDIRECTORYCALLBACK_H +#define NULLSOFT_IFC_PLAYLISTDIRECTORYCALLBACK_H + +#include <bfc/dispatch.h> +#include <bfc/platform/types.h> + +class ifc_playlistdirectorycallback : public Dispatchable +{ +protected: + ifc_playlistdirectorycallback() {} + ~ifc_playlistdirectorycallback() {} + +public: + bool ShouldRecurse(const wchar_t *path); + bool ShouldLoad(const wchar_t *filename); + + DISPATCH_CODES + { + IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDRECURSE = 10, + IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDLOAD = 20, + }; +}; + +inline bool ifc_playlistdirectorycallback::ShouldRecurse(const wchar_t *path) +{ + return _call(IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDRECURSE, (bool)false, path); +} + +inline bool ifc_playlistdirectorycallback::ShouldLoad(const wchar_t *filename) +{ + return _call(IFC_PLAYLISTDIRECTORYCALLBACK_SHOULDLOAD, (bool)true, filename); +} +#endif
\ No newline at end of file diff --git a/Src/playlist/ifc_playlistloader.h b/Src/playlist/ifc_playlistloader.h new file mode 100644 index 00000000..d1b6fba4 --- /dev/null +++ b/Src/playlist/ifc_playlistloader.h @@ -0,0 +1,37 @@ +#ifndef NULLSOFT_IFC_PLAYLISTLOADER_H +#define NULLSOFT_IFC_PLAYLISTLOADER_H + +#include <bfc/dispatch.h> +#include <wchar.h> +#include "ifc_playlistloadercallback.h" + +enum +{ + IFC_PLAYLISTLOADER_SUCCESS = 0, + IFC_PLAYLISTLOADER_FAILED = 1, + + IFC_PLAYLISTLOADER_NEXTITEM_EOF = 1, +}; + +class ifc_playlistloader : public Dispatchable +{ +protected: + ifc_playlistloader() {} + ~ifc_playlistloader() {} + +public: + int Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ); + + DISPATCH_CODES + { + IFC_PLAYLISTLOADER_LOAD = 10, + }; + +}; + +inline int ifc_playlistloader::Load( const wchar_t *filename, ifc_playlistloadercallback *playlist ) +{ + return _call( IFC_PLAYLISTLOADER_LOAD, (int)IFC_PLAYLISTLOADER_FAILED, filename, playlist ); +} + +#endif diff --git a/Src/playlist/ifc_playlistloadercallback.h b/Src/playlist/ifc_playlistloadercallback.h new file mode 100644 index 00000000..561fa749 --- /dev/null +++ b/Src/playlist/ifc_playlistloadercallback.h @@ -0,0 +1,96 @@ +#ifndef NULLSOFT_IFC_PLAYLISTLOADERCALLBACK_H +#define NULLSOFT_IFC_PLAYLISTLOADERCALLBACK_H + +#include <bfc/dispatch.h> +#include <bfc/platform/types.h> +#include "ifc_plentryinfo.h" + +#ifndef FILENAME_SIZE +#define FILENAME_SIZE (MAX_PATH * 4) +#endif + +#ifndef FILETITLE_SIZE +#define FILETITLE_SIZE 400 +#endif + +class ifc_playlistinfo; // TODO +class ifc_playlistloadercallback : public Dispatchable +{ +protected: + ifc_playlistloadercallback() {} + ~ifc_playlistloadercallback() {} + +public: + // return 0 to continue enumeration, or 1 to quit + + // title will be NULL if no title found, length will be -1 + int OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info ); + void OnFileOld( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info ); + + // numEntries is just a hint, there is no gaurantee. 0 means "don't know" + int OnPlaylistInfo( const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info ); + void OnPlaylistInfoOld( const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info ); + + const wchar_t *GetBasePath(); // return 0 to use playlist file path as base (or just don't implement) + + DISPATCH_CODES + { + IFC_PLAYLISTLOADERCALLBACK_ONFILE = 10, + IFC_PLAYLISTLOADERCALLBACK_ONFILE_RET = 11, + IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO = 20, + IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO_RET = 21, + IFC_PLAYLISTLOADERCALLBACK_GETBASEPATH = 30, + }; + enum + { + LOAD_CONTINUE = 0, + LOAD_ABORT = 1, + }; +}; + +inline void ifc_playlistloadercallback::OnFileOld( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info ) +{ + _voidcall( IFC_PLAYLISTLOADERCALLBACK_ONFILE, filename, title, lengthInMS, info ); +} + +inline int ifc_playlistloadercallback::OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info ) +{ + void *params[ 4 ] = { &filename, &title, &lengthInMS, &info }; + int retval; + + if ( _dispatch( IFC_PLAYLISTLOADERCALLBACK_ONFILE_RET, &retval, params, 4 ) == 0 ) + { + _dispatch( IFC_PLAYLISTLOADERCALLBACK_ONFILE, 0, params, 4 ); + + return LOAD_CONTINUE; + } + + return retval; +} + +inline void ifc_playlistloadercallback::OnPlaylistInfoOld( const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info ) +{ + _voidcall( IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO, playlistName, numEntries, info ); +} + +inline int ifc_playlistloadercallback::OnPlaylistInfo( const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info ) +{ + void *params[ 3 ] = { &playlistName, &numEntries, &info }; + int retval; + + if ( _dispatch( IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO_RET, &retval, params, 3 ) == 0 ) + { + _dispatch( IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO, 0, params, 3 ); + + return LOAD_CONTINUE; + } + + return retval; +} + +inline const wchar_t *ifc_playlistloadercallback::GetBasePath() +{ + return _call( IFC_PLAYLISTLOADERCALLBACK_GETBASEPATH, (const wchar_t *)0 ); +} + +#endif
\ No newline at end of file diff --git a/Src/playlist/ifc_playlistloadercallbackT.h b/Src/playlist/ifc_playlistloadercallbackT.h new file mode 100644 index 00000000..7ee6366f --- /dev/null +++ b/Src/playlist/ifc_playlistloadercallbackT.h @@ -0,0 +1,29 @@ +#pragma once +#include "ifc_playlistloadercallback.h" + +template <class T> +class ifc_playlistloadercallbackT : public ifc_playlistloadercallback +{ +protected: + ifc_playlistloadercallbackT() {} + ~ifc_playlistloadercallbackT() {} +protected: + // return 0 to continue enumeration, or 1 to quit + + // title will be NULL if no title found, length will be -1 + int OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info) { return LOAD_ABORT; } + // numEntries is just a hint, there is no gaurantee. 0 means "don't know" + int OnPlaylistInfo(const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info) { return LOAD_ABORT; } + // return 0 to use playlist file path as base (or just don't implement) + const wchar_t *GetBasePath() { return 0; } + +#define CBCLASS T +#define CBCLASST ifc_playlistloadercallbackT<T> + START_DISPATCH_INLINE; + CBT(IFC_PLAYLISTLOADERCALLBACK_ONFILE_RET, OnFile); + CBT(IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO_RET, OnPlaylistInfo); + CBT(IFC_PLAYLISTLOADERCALLBACK_GETBASEPATH, GetBasePath); + END_DISPATCH; +#undef CBCLASS +#undef CBCLASST +}; diff --git a/Src/playlist/ifc_plentryinfo.h b/Src/playlist/ifc_plentryinfo.h new file mode 100644 index 00000000..f8c767a9 --- /dev/null +++ b/Src/playlist/ifc_plentryinfo.h @@ -0,0 +1,28 @@ +#ifndef NULLSOFT_IFC_PLENTRYINFO_H +#define NULLSOFT_IFC_PLENTRYINFO_H + +#include <bfc/dispatch.h> +#include <bfc/platform/types.h> + +class ifc_plentryinfo : public Dispatchable +{ +protected: + ifc_plentryinfo() {} + ~ifc_plentryinfo() {} + +public: + // TODO: you can't guarantee that this wchar_t * pointer will last, make a copy before calling again! + const wchar_t *GetExtendedInfo( const wchar_t *parameter ); + + DISPATCH_CODES + { + IFC_PLENTRYINFO_GETEXTENDEDINFO = 10, + }; +}; + +inline const wchar_t *ifc_plentryinfo::GetExtendedInfo( const wchar_t *parameter ) +{ + return _call( IFC_PLENTRYINFO_GETEXTENDEDINFO, (const wchar_t *)0, parameter ); +} + +#endif
\ No newline at end of file diff --git a/Src/playlist/main.cpp b/Src/playlist/main.cpp new file mode 100644 index 00000000..4d7f9fbd --- /dev/null +++ b/Src/playlist/main.cpp @@ -0,0 +1,134 @@ +#include "api__playlist.h" +#include "main.h" + +#include "factory_Handler.h" +#include "factory_playlistmanager.h" +#include "factory_playlists.h" +#include "../Winamp/api_random.h" +#include "Playlists.h" +#include "plstring.h" +#include "ScriptObjectFactory.h" +#include "../nu/ServiceWatcher.h" +#include "JSAPI2_Creator.h" + +extern Playlists playlists; +int (*warand)(void) = 0; + +M3UHandlerFactory m3uHandlerFactory; +PLSHandlerFactory plsHandlerFactory; +B4SHandlerFactory b4sHandlerFactory; +PlaylistManagerFactory playlistManagerFactory; +PlaylistsFactory playlistsFactory; +ScriptObjectFactory scriptObjectFactory; +JSAPI2Factory jsapi2Factory; +ServiceWatcher serviceWatcher; +PlaylistComponent playlistComponent; + +api_service *WASABI_API_SVC = 0; +api_application *WASABI_API_APP = 0; +api_config *AGAVE_API_CONFIG = 0; +api_syscb *WASABI_API_SYSCB = 0; +api_maki *WASABI_API_MAKI = 0; +JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = 0; +api_stats *AGAVE_API_STATS = 0; + +// wasabi based services for localisation support +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +template <class api_t> +api_t *GetService( GUID serviceGUID ) +{ + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid( serviceGUID ); + if ( sf ) + return (api_t *)sf->getInterface(); + else + return 0; + +} + +inline void ReleaseService( GUID serviceGUID, void *service ) +{ + if ( service ) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid( serviceGUID ); + if ( sf ) + sf->releaseInterface( service ); + } +} + +void PlaylistComponent::RegisterServices( api_service *service ) +{ + WASABI_API_SVC = service; + + warand = QuickService<api_random>( randomApiGUID )->GetFunction(); + + WASABI_API_APP = GetService<api_application>( applicationApiServiceGuid ); + WASABI_API_SYSCB = GetService<api_syscb>( syscbApiServiceGuid ); + AGAVE_API_CONFIG = GetService<api_config>( AgaveConfigGUID ); + AGAVE_API_JSAPI2_SECURITY = GetService<JSAPI2::api_security>( JSAPI2::api_securityGUID ); + AGAVE_API_STATS = GetService<api_stats>( AnonymousStatsGUID ); + + serviceWatcher.WatchWith( WASABI_API_SVC ); + serviceWatcher.WatchFor( &WASABI_API_MAKI, makiApiServiceGuid ); + + // need to get WASABI_API_APP first + plstring_init(); + + WASABI_API_SVC->service_register( &m3uHandlerFactory ); + WASABI_API_SVC->service_register( &plsHandlerFactory ); + WASABI_API_SVC->service_register( &b4sHandlerFactory ); + WASABI_API_SVC->service_register( &playlistManagerFactory ); + WASABI_API_SVC->service_register( &playlistsFactory ); + WASABI_API_SVC->service_register( &scriptObjectFactory ); + WASABI_API_SVC->service_register( &jsapi2Factory ); + + WASABI_API_LNG = GetService<api_language>( languageApiGUID ); + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG( hModule, playlistLangGUID ); + + // register for service callbacks in case any of these don't exist yet + WASABI_API_SYSCB->syscb_registerCallback( &serviceWatcher ); +} + +int PlaylistComponent::RegisterServicesSafeModeOk() +{ + return 1; +} + +void PlaylistComponent::DeregisterServices( api_service *service ) +{ + playlists.Flush(); + + service->service_deregister( &playlistsFactory ); + service->service_deregister( &playlistManagerFactory ); + service->service_deregister( &m3uHandlerFactory ); + service->service_deregister( &plsHandlerFactory ); + service->service_deregister( &b4sHandlerFactory ); + service->service_deregister( &scriptObjectFactory ); + service->service_deregister( &jsapi2Factory ); + + serviceWatcher.StopWatching(); + serviceWatcher.Clear(); + + ReleaseService( makiApiServiceGuid, WASABI_API_MAKI ); + ReleaseService( applicationApiServiceGuid, WASABI_API_APP ); + ReleaseService( AgaveConfigGUID, AGAVE_API_CONFIG ); + ReleaseService( syscbApiServiceGuid, WASABI_API_SYSCB ); + ReleaseService( languageApiGUID, WASABI_API_LNG ); + ReleaseService( JSAPI2::api_securityGUID, AGAVE_API_JSAPI2_SECURITY ); + ReleaseService( AnonymousStatsGUID, AGAVE_API_STATS ); +} + +extern "C" __declspec(dllexport) ifc_wa5component *GetWinamp5SystemComponent() +{ + return &playlistComponent; +} + +#define CBCLASS PlaylistComponent +START_DISPATCH; +VCB( API_WA5COMPONENT_REGISTERSERVICES, RegisterServices ) +CB( API_WA5COMPONENT_REGISTERSERVICES_SAFE_MODE, RegisterServicesSafeModeOk ) +VCB( API_WA5COMPONENT_DEREEGISTERSERVICES, DeregisterServices ) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/playlist/main.h b/Src/playlist/main.h new file mode 100644 index 00000000..62b60334 --- /dev/null +++ b/Src/playlist/main.h @@ -0,0 +1,26 @@ +#ifndef NULLSOFT_PLAYLIST_MAIN_H +#define NULLSOFT_PLAYLIST_MAIN_H + +#include <windows.h> +#include <shlwapi.h> +#include "..\Components\wac_network\wac_network_http_receiver_api.h" + +extern int (*warand)(void); +HRESULT ResolveShortCut(HWND hwnd, LPCWSTR pszShortcutFile, LPWSTR pszPath); +bool IsUrl(const wchar_t *url); +void SetUserAgent(api_httpreceiver *http); +const char *GetProxy(); + +#include "../Agave/Component/ifc_wa5component.h" + +class PlaylistComponent : public ifc_wa5component +{ +public: + void RegisterServices(api_service *service); + int RegisterServicesSafeModeOk(); + void DeregisterServices(api_service *service); +protected: + RECVS_DISPATCH; +}; +extern PlaylistComponent playlistComponent; +#endif
\ No newline at end of file diff --git a/Src/playlist/pl_entry.cpp b/Src/playlist/pl_entry.cpp new file mode 100644 index 00000000..5f164654 --- /dev/null +++ b/Src/playlist/pl_entry.cpp @@ -0,0 +1,336 @@ +#include "main.h" +#include "pl_entry.h" +#include "plstring.h" +#include <wchar.h> +#include "../nu/strsafe.h" + +#include <iostream> + +static const wchar_t *_INFO_NAME_MEDIA_HASH = L"mediahash"; +static const wchar_t *_INFO_NAME_META_HASH = L"metahash"; +static const wchar_t *_INFO_NAME_CLOUD_ID = L"cloud_id"; +static const wchar_t *_INFO_NAME_CLOUD_STATUS = L"cloud_status"; +static const wchar_t *_INFO_NAME_CLOUD_DEVICES = L"cloud_devices"; + +pl_entry::pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms ) +{ + SetFilename( p_filename ); + SetTitle( p_title ); + SetLengthMilliseconds( p_length_ms ); +} + +pl_entry::pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, int p_size ) +{ + SetFilename( p_filename ); + SetTitle( p_title ); + SetLengthMilliseconds( p_length_ms ); + SetSizeBytes( p_size ); +} + +pl_entry::pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, ifc_plentryinfo *p_info ) +{ + SetFilename( p_filename ); + SetTitle( p_title ); + SetLengthMilliseconds( p_length_ms ); + + if ( p_info ) + { + SetMediahash( p_info->GetExtendedInfo( _INFO_NAME_MEDIA_HASH ) ); + SetMetahash( p_info->GetExtendedInfo( _INFO_NAME_META_HASH ) ); + SetCloudID( p_info->GetExtendedInfo( _INFO_NAME_CLOUD_ID ) ); + SetCloudStatus( p_info->GetExtendedInfo( _INFO_NAME_CLOUD_STATUS ) ); + SetCloudDevices( p_info->GetExtendedInfo( _INFO_NAME_CLOUD_DEVICES ) ); + + const wchar_t *l_tvg_name = p_info->GetExtendedInfo( L"tvg-name" ); + + if ( l_tvg_name && wcslen( l_tvg_name ) > 0 ) + { + _extended_infos.emplace( L"tvg-name", l_tvg_name ); + + const wchar_t *l_tvg_id = p_info->GetExtendedInfo( L"tvg-id" ); + if ( l_tvg_id && *l_tvg_id ) + _extended_infos.emplace( L"tvg-id", l_tvg_id ); + + const wchar_t *l_tvg_logo = p_info->GetExtendedInfo( L"tvg-logo" ); + if ( l_tvg_logo && *l_tvg_logo ) + _extended_infos.emplace( L"tvg-logo", l_tvg_logo ); + + const wchar_t *l_tvg_title = p_info->GetExtendedInfo( L"tvg-title" ); + if ( l_tvg_title && *l_tvg_title ) + _extended_infos.emplace( L"group-title", l_tvg_title ); + } + + const wchar_t *l_ext = p_info->GetExtendedInfo( L"ext" ); + + if ( l_ext && wcslen( l_ext ) ) + _extended_infos.emplace( L"ext", l_ext ); + } +} + +pl_entry::pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, int p_size, ifc_plentryinfo *p_info ) +{ + SetFilename( p_filename ); + SetTitle( p_title ); + SetLengthMilliseconds( p_length_ms ); + SetSizeBytes( p_size ); + + if ( p_info ) + { + SetMediahash( p_info->GetExtendedInfo( _INFO_NAME_MEDIA_HASH ) ); + SetMetahash( p_info->GetExtendedInfo( _INFO_NAME_META_HASH ) ); + SetCloudID( p_info->GetExtendedInfo( _INFO_NAME_CLOUD_ID ) ); + SetCloudStatus( p_info->GetExtendedInfo( _INFO_NAME_CLOUD_STATUS ) ); + SetCloudDevices( p_info->GetExtendedInfo( _INFO_NAME_CLOUD_DEVICES ) ); + + const wchar_t *l_tvg_name = p_info->GetExtendedInfo( L"tvg-name" ); + + if ( l_tvg_name && wcslen( l_tvg_name ) > 0 ) + { + _extended_infos.emplace( L"tvg-id", p_info->GetExtendedInfo( L"tvg-id" ) ); + _extended_infos.emplace( L"tvg-name", l_tvg_name ); + _extended_infos.emplace( L"tvg-logo", p_info->GetExtendedInfo( L"tvg-logo" ) ); + _extended_infos.emplace( L"group-title", p_info->GetExtendedInfo( L"group-title" ) ); + } + } +} + +pl_entry::pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, + const wchar_t *mediahash, const wchar_t *metahash, + const wchar_t *cloud_id, const wchar_t *cloud_status, + const wchar_t *cloud_devices ) +{ + SetFilename( p_filename ); + SetTitle( p_title ); + SetLengthMilliseconds( p_length_ms ); + + SetMediahash( mediahash ); + SetMetahash( metahash ); + SetCloudID( cloud_id ); + SetCloudStatus( cloud_status ); + SetCloudDevices( cloud_devices ); +} + +pl_entry::~pl_entry() +{ + plstring_release( filename ); + plstring_release( filetitle ); + plstring_release( mediahash ); + plstring_release( metahash ); + plstring_release( cloud_id ); + plstring_release( cloud_status ); + plstring_release( cloud_devices ); +} + + +size_t pl_entry::GetFilename( wchar_t *p_filename, size_t filenameCch ) +{ + if ( !this->filename ) + return 0; + + if ( !p_filename ) + return wcslen( this->filename ); + + if ( !this->filename[ 0 ] ) + return 0; + + StringCchCopyW( p_filename, filenameCch, this->filename ); + + return 1; +} + +size_t pl_entry::GetTitle( wchar_t *title, size_t titleCch ) +{ + if ( !this->filetitle ) + return 0; + + if ( !title ) + return wcslen( this->filetitle ); + + if ( !this->filetitle[ 0 ] ) + return 0; + + StringCchCopyW( title, titleCch, this->filetitle ); + + return 1; +} + +int pl_entry::GetLengthInMilliseconds() +{ + return this->length; +} + +int pl_entry::GetSizeInBytes() +{ + return this->size; +} + +size_t pl_entry::GetExtendedInfo( const wchar_t *metadata, wchar_t *info, size_t infoCch ) +{ + if ( cloud_id ) + { + if ( !_wcsnicmp( _INFO_NAME_MEDIA_HASH, metadata, 9 ) && mediahash ) + { + lstrcpynW( info, mediahash, (int)infoCch ); + return 1; + } + else if ( !_wcsnicmp( _INFO_NAME_META_HASH, metadata, 8 ) && metahash ) + { + lstrcpynW( info, metahash, (int)infoCch ); + return 1; + } + else if ( !_wcsnicmp( _INFO_NAME_CLOUD_ID, metadata, 8 ) && cloud_id ) + { + lstrcpynW( info, cloud_id, (int)infoCch ); + return 1; + } + else if ( !_wcsnicmp( _INFO_NAME_CLOUD_STATUS, metadata, 12 ) && cloud_status ) + { + lstrcpynW( info, cloud_status, (int)infoCch ); + return 1; + } + else if ( !_wcsnicmp( _INFO_NAME_CLOUD_DEVICES, metadata, 13 ) && cloud_devices ) + { + lstrcpynW( info, cloud_devices, (int)infoCch ); + return 1; + } + else if ( !_wcsnicmp( metadata, L"cloud", 5 ) ) + { + if ( _wtoi( cloud_id ) > 0 ) + { + StringCchPrintfW( info, infoCch, L"#EXT-X-NS-CLOUD:mediahash=%s,metahash=%s,cloud_id=%s,cloud_status=%s,cloud_devices=%s", + ( mediahash && *mediahash ? mediahash : L"" ), + ( metahash && *metahash ? metahash : L"" ), cloud_id, + ( cloud_status && *cloud_status ? cloud_status : L"" ), + ( cloud_devices && *cloud_devices ? cloud_devices : L"" ) ); + return 1; + } + } + else + { + auto l_extended_infos_iterator = _extended_infos.find( metadata ); + + if ( l_extended_infos_iterator != _extended_infos.end() ) + { + lstrcpynW( info, ( *l_extended_infos_iterator ).second.c_str(), (int)infoCch); + return 1; + } + } + } + + if ( !this->_extended_infos.empty() ) + { + for ( std::map<std::wstring, std::wstring>::iterator l_extented_infos_iterator = _extended_infos.begin(); l_extented_infos_iterator != _extended_infos.end(); l_extented_infos_iterator++ ) + { + const std::wstring &l_parameter_name = ( *l_extented_infos_iterator ).first; + + if ( l_parameter_name.compare( metadata ) == 0 ) + { + lstrcpynW( info, ( *l_extented_infos_iterator ).second.c_str(), (int)infoCch); + return 1; + } + } + } + + + return 0; +} + + +void pl_entry::SetFilename( const wchar_t *p_filename ) +{ + plstring_release( this->filename ); + + if ( p_filename && p_filename[ 0 ] ) + { + this->filename = plstring_wcsdup( p_filename ); + + if ( wcslen( p_filename ) > 4 ) + _is_local_file = wcsncmp( this->filename, L"http", 4 ) != 0; + } + else + this->filename = 0; +} + +void pl_entry::SetTitle( const wchar_t *title ) +{ + plstring_release( this->filetitle ); + + if ( title && title[ 0 ] ) + { + const wchar_t *t = L" \t\n\r\f\v"; + std::wstring l_title( title ); + + l_title.erase( 0, l_title.find_first_not_of( t ) ); + + this->filetitle = plstring_wcsdup( l_title.c_str() ); + this->cached = true; + } + else + this->filetitle = 0; +} + +void pl_entry::SetLengthMilliseconds( int length ) +{ + if ( length <= 0 ) + this->length = -1000; + else + this->length = length; +} + +void pl_entry::SetMediahash( const wchar_t *mediahash ) +{ + plstring_release( this->mediahash ); + + if ( mediahash && mediahash[ 0 ] ) + this->mediahash = plstring_wcsdup( mediahash ); + else + this->mediahash = 0; +} + +void pl_entry::SetSizeBytes( int size ) +{ + if ( size <= 0 ) + this->size = 0; + else + this->size = size; +} + +void pl_entry::SetMetahash( const wchar_t *metahash ) +{ + plstring_release( this->metahash ); + + if ( metahash && metahash[ 0 ] ) + this->metahash = plstring_wcsdup( metahash ); + else + this->metahash = 0; +} + +void pl_entry::SetCloudID( const wchar_t *cloud_id ) +{ + plstring_release( this->cloud_id ); + + if ( cloud_id && cloud_id[ 0 ] && _wtoi( cloud_id ) > 0 ) + this->cloud_id = plstring_wcsdup( cloud_id ); + else + this->cloud_id = 0; +} + +void pl_entry::SetCloudStatus( const wchar_t *cloud_status ) +{ + plstring_release( this->cloud_status ); + + if ( cloud_status && cloud_status[ 0 ] && _wtoi( cloud_status ) >= 0 ) + this->cloud_status = plstring_wcsdup( cloud_status ); + else + this->cloud_status = 0; +} + +void pl_entry::SetCloudDevices( const wchar_t *cloud_devices ) +{ + plstring_release( this->cloud_devices ); + + if ( cloud_devices && cloud_devices[ 0 ] ) + this->cloud_devices = plstring_wcsdup( cloud_devices ); + else + this->cloud_devices = 0; +} diff --git a/Src/playlist/pl_entry.h b/Src/playlist/pl_entry.h new file mode 100644 index 00000000..852b584d --- /dev/null +++ b/Src/playlist/pl_entry.h @@ -0,0 +1,61 @@ +#ifndef NULLSOFT_ML_PLAYLISTS_PL_ENTRY_H +#define NULLSOFT_ML_PLAYLISTS_PL_ENTRY_H + +#include "ifc_plentryinfo.h" +#include <windows.h> +#include <map> +#include <iostream> + + +class pl_entry +{ +public: + pl_entry() {} + pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms ); + pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, int p_size ); + pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, ifc_plentryinfo *p_info ); + pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, int p_size, ifc_plentryinfo *p_info ); + pl_entry( const wchar_t *p_filename, const wchar_t *p_title, int p_length_ms, + const wchar_t *mediahash, const wchar_t *metahash, + const wchar_t *cloud_id, const wchar_t *cloud_status, + const wchar_t *cloud_devices ); + ~pl_entry(); + + size_t GetFilename( wchar_t *p_filename, size_t filenameCch ); + size_t GetTitle( wchar_t *title, size_t titleCch ); + int GetLengthInMilliseconds(); + int GetSizeInBytes(); + size_t GetExtendedInfo( const wchar_t *metadata, wchar_t *info, size_t infoCch ); + + void SetFilename( const wchar_t *p_filename ); + void SetTitle( const wchar_t *title ); + void SetLengthMilliseconds( int length ); + void SetSizeBytes( int size ); + + void SetMediahash( const wchar_t *mediahash ); + void SetMetahash( const wchar_t *metahash ); + void SetCloudID( const wchar_t *cloud_id ); + void SetCloudStatus( const wchar_t *cloud_status ); + void SetCloudDevices( const wchar_t *cloud_devices ); + + bool isLocal() const { return _is_local_file; } + + wchar_t *filename = 0; + wchar_t *filetitle = 0; + + wchar_t *mediahash = 0; + wchar_t *metahash = 0; + wchar_t *cloud_id = 0; + wchar_t *cloud_status = 0; + wchar_t *cloud_devices = 0; + + std::map<std::wstring, std::wstring> _extended_infos; + + int length = -1; + int size = 0; + + bool cached = false; + bool _is_local_file = false; +}; + +#endif
\ No newline at end of file diff --git a/Src/playlist/playlist.mi b/Src/playlist/playlist.mi new file mode 100644 index 00000000..c82862be --- /dev/null +++ b/Src/playlist/playlist.mi @@ -0,0 +1,49 @@ +#ifndef __PLAYLIST_MI +#define __PLAYLIST_MI + +extern class @{632883FC-159F-4330-B193-CFD62CA47EC1}@ Object &Playlist; +extern class @{5829EE15-3648-4c6e-B2FE-8736CBBF39DB}@ Object _predecl Playlists; +extern class @{C18F8E50-2C81-4001-9F46-FD942B07ECCD}@ Object &PlaylistsEnumerator; +extern class @{C6207729-2600-4bb8-B562-2E0BC04E4416}@ Object _predecl PlaylistManager; + +/* ===== Playlist ===== */ +extern Playlist.Clear(); +extern int Playlist.GetNumItems(); +/* +Retrieve the filename for some item in the playlist +*/ +extern String Playlist.GetItem(int itemNumber); +extern String Playlist.GetItemTitle(int itemNumber); +extern int Playlist.GetItemLength(int itemNumber); +extern String Playlist.GetItemExtendedInfo(int itemNumber, String metadata); +extern Playlist.Reverse(); +extern Playlist.Swap(int item1, int item2); +extern Playlist.Randomize(); +extern Playlist.Remove(int itemNumber); +extern Playlist.SortByTitle(); +extern Playlist.SortByFilename(); + +/* ===== Playlists ===== */ + +extern PlaylistsEnumerator Playlists.GetEnumerator(); +extern Playlist Playlists.OpenPlaylist(String playlistGUID); +extern Playlists.SavePlaylist(String playlistGUID, Playlist playlist_to_save); + +/* ===== PlaylistsEnumerator ===== */ + +/* returns the number of playlists in the enumerator object */ +extern int PlaylistsEnumerator.GetCount(); +extern String PlaylistsEnumerator.GetFilename(int playlistNumber); +extern String PlaylistsEnumerator.GetTitle(int playlistNumber); +extern int PlaylistsEnumerator.GetLength(int playlistNumber); +/* +returns number of items in one of the playlists +*/ +extern int PlaylistsEnumerator.GetNumItems(int playlistNumber); +extern String PlaylistsEnumerator.GetGUID(int playlistNumber); + +/* ===== Playlist Manager ===== */ +extern Playlist PlaylistManager.OpenPlaylist(String filename); +extern PlaylistManager.SavePlaylist(String filename, Playlist playlist_to_save); + +#endif
\ No newline at end of file diff --git a/Src/playlist/playlist.rc b/Src/playlist/playlist.rc new file mode 100644 index 00000000..909ed680 --- /dev/null +++ b/Src/playlist/playlist.rc @@ -0,0 +1,86 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + 65535 "{9E398E5F-EDEC-4dd8-A40D-E29B385A88C0}" +END + +STRINGTABLE +BEGIN + IDS_M3U_PLAYLIST "M3U Playlist" + IDS_PLS_PLAYLIST "PLS Playlist" + IDS_WINAMP3_PLAYLIST "Winamp3 Playlist" + IDS_ALL_PLAYLIST_TYPES "All Playlist Types" + IDS_PLAYLIST "Playlist" + IDS_MPCPL_PLAYLIST "Media Player Classic Playlist (MPCPL)" + IDS_FPL_PLAYLIST "Foobar2000 Playlist (FPL)" + IDS_XSPF_PLAYLIST "XML Shareable Playlist Format" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/playlist/playlist.sln b/Src/playlist/playlist.sln new file mode 100644 index 00000000..54a86262 --- /dev/null +++ b/Src/playlist/playlist.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "playlist", "playlist.vcxproj", "{B2C0F048-C7FA-4864-B5B3-75E69458BA9E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Debug|Win32.ActiveCfg = Debug|Win32 + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Debug|Win32.Build.0 = Debug|Win32 + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Release|Win32.ActiveCfg = Release|Win32 + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Release|Win32.Build.0 = Release|Win32 + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Debug|x64.ActiveCfg = Debug|x64 + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Debug|x64.Build.0 = Debug|x64 + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Release|x64.ActiveCfg = Release|x64 + {B2C0F048-C7FA-4864-B5B3-75E69458BA9E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3815C7C8-CBE1-4F34-B885-16D73AD02A60} + EndGlobalSection +EndGlobal diff --git a/Src/playlist/playlist.vcxproj b/Src/playlist/playlist.vcxproj new file mode 100644 index 00000000..f0bfa185 --- /dev/null +++ b/Src/playlist/playlist.vcxproj @@ -0,0 +1,342 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B2C0F048-C7FA-4864-B5B3-75E69458BA9E}</ProjectGuid> + <RootNamespace>playlist</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnabled>false</VcpkgEnabled> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;PLAYLIST_EXPORTS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_WCSTOK;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x86_Debug\$(ProjectName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;PLAYLIST_EXPORTS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_WCSTOK;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x64_Debug\$(ProjectName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;PLAYLIST_EXPORTS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_WCSTOK;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <DelayLoadDLLs>ole32.dll;rpcrt4.dll;%(DelayLoadDLLs)</DelayLoadDLLs> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x86_Release\$(ProjectName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;PLAYLIST_EXPORTS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_WCSTOK;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <DelayLoadDLLs>ole32.dll;rpcrt4.dll;%(DelayLoadDLLs)</DelayLoadDLLs> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x64_Release\$(ProjectName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\nu\ServiceWatcher.cpp" /> + <ClCompile Include="..\Wasabi\api\script\objcontroller.cpp" /> + <ClCompile Include="..\Wasabi\api\script\objects\rootobj.cpp" /> + <ClCompile Include="..\Wasabi\api\script\objects\rootobjcbx.cpp" /> + <ClCompile Include="..\Wasabi\api\script\scriptobji.cpp" /> + <ClCompile Include="..\Wasabi\api\script\scriptobjx.cpp" /> + <ClCompile Include="..\Wasabi\bfc\assert.cpp" /> + <ClCompile Include="..\Wasabi\bfc\foreach.cpp" /> + <ClCompile Include="..\Wasabi\bfc\memblock.cpp" /> + <ClCompile Include="..\Wasabi\bfc\nsguid.cpp" /> + <ClCompile Include="..\Wasabi\bfc\parse\PathParseW.cpp" /> + <ClCompile Include="..\Wasabi\bfc\std_string.cpp" /> + <ClCompile Include="..\Wasabi\bfc\string\string.cpp" /> + <ClCompile Include="..\Wasabi\bfc\string\StringW.cpp" /> + <ClCompile Include="..\Wasabi\bfc\wasabi_std.cpp" /> + <ClCompile Include="..\Winamp\JSAPI_CallbackParameters.cpp" /> + <ClCompile Include="..\Winamp\JSAPI_ObjectArray.cpp" /> + <ClCompile Include="..\Winamp\strutil.cpp" /> + <ClCompile Include="B4SLoader.cpp" /> + <ClCompile Include="B4SWriter.cpp" /> + <ClCompile Include="factory_Handler.cpp" /> + <ClCompile Include="factory_playlistmanager.cpp" /> + <ClCompile Include="factory_playlists.cpp" /> + <ClCompile Include="Handler.cpp" /> + <ClCompile Include="JSAPI2_Creator.cpp" /> + <ClCompile Include="JSAPI2_Playlist.cpp" /> + <ClCompile Include="JSAPI2_Playlists.cpp" /> + <ClCompile Include="M3U8Writer.cpp" /> + <ClCompile Include="M3ULoader.cpp" /> + <ClCompile Include="M3UWriter.cpp" /> + <ClCompile Include="main.cpp" /> + <ClCompile Include="Playlist.cpp" /> + <ClCompile Include="PlaylistCounter.cpp" /> + <ClCompile Include="PlaylistManager.cpp" /> + <ClCompile Include="Playlists.cpp" /> + <ClCompile Include="PlaylistsXML.cpp" /> + <ClCompile Include="PLSLoader.cpp" /> + <ClCompile Include="plstring.cpp" /> + <ClCompile Include="PLSWriter.cpp" /> + <ClCompile Include="pl_entry.cpp" /> + <ClCompile Include="ScriptObjectFactory.cpp" /> + <ClCompile Include="ScriptObjectService.cpp" /> + <ClCompile Include="SPlaylist.cpp" /> + <ClCompile Include="SPlaylistManager.cpp" /> + <ClCompile Include="SPlaylists.cpp" /> + <ClCompile Include="SPlaylistsEnumerator.cpp" /> + <ClCompile Include="util.cpp" /> + <ClCompile Include="XMLString.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\Winamp\JSAPI_ObjectArray.h" /> + <ClInclude Include="..\Winamp\strutil.h" /> + <ClInclude Include="api__playlist.h" /> + <ClInclude Include="api_playlistmanager.h" /> + <ClInclude Include="api_playlists.h" /> + <ClInclude Include="B4SLoader.h" /> + <ClInclude Include="B4SWriter.h" /> + <ClInclude Include="factory_Handler.h" /> + <ClInclude Include="factory_playlistmanager.h" /> + <ClInclude Include="factory_playlists.h" /> + <ClInclude Include="Handler.h" /> + <ClInclude Include="ifc_playlist.h" /> + <ClInclude Include="ifc_playlistloader.h" /> + <ClInclude Include="JSAPI2_Creator.h" /> + <ClInclude Include="JSAPI2_Playlist.h" /> + <ClInclude Include="JSAPI2_Playlists.h" /> + <ClInclude Include="M3U8Writer.h" /> + <ClInclude Include="M3ULoader.h" /> + <ClInclude Include="M3UWriter.h" /> + <ClInclude Include="main.h" /> + <ClInclude Include="PlaylistCounter.h" /> + <ClInclude Include="PlaylistManager.h" /> + <ClInclude Include="Playlists.h" /> + <ClInclude Include="PlaylistsXML.h" /> + <ClInclude Include="PlaylistWriter.h" /> + <ClInclude Include="PLSLoader.h" /> + <ClInclude Include="plstring.h" /> + <ClInclude Include="PLSWriter.h" /> + <ClInclude Include="pl_entry.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="ScriptObjectFactory.h" /> + <ClInclude Include="ScriptObjectService.h" /> + <ClInclude Include="SPlaylist.h" /> + <ClInclude Include="SPlaylistManager.h" /> + <ClInclude Include="SPlaylists.h" /> + <ClInclude Include="SPlaylistsEnumerator.h" /> + <ClInclude Include="svc_playlisthandler.h" /> + <ClInclude Include="XMLString.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="playlist.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + <ProjectReference Include="..\WAT\WAT.vcxproj"> + <Project>{c5714908-a71f-4644-bd95-aad8ee7914da}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/playlist/playlist.vcxproj.filters b/Src/playlist/playlist.vcxproj.filters new file mode 100644 index 00000000..3445ab88 --- /dev/null +++ b/Src/playlist/playlist.vcxproj.filters @@ -0,0 +1,299 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="B4SLoader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="B4SWriter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="factory_Handler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="factory_playlistmanager.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="factory_playlists.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Handler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JSAPI2_Creator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JSAPI2_Playlist.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JSAPI2_Playlists.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="M3U8Writer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="M3UWriter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="M3ULoader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="pl_entry.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Playlist.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PlaylistCounter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PlaylistManager.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Playlists.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PlaylistsXML.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PLSLoader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="plstring.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PLSWriter.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ScriptObjectFactory.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ScriptObjectService.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SPlaylist.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SPlaylistManager.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SPlaylists.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SPlaylistsEnumerator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="util.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="XMLString.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\assert.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\foreach.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Winamp\JSAPI_CallbackParameters.cpp"> + <Filter>Source Files\Winamp</Filter> + </ClCompile> + <ClCompile Include="..\Winamp\JSAPI_ObjectArray.cpp"> + <Filter>Source Files\Winamp</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\memblock.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\nsguid.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\api\script\objcontroller.cpp"> + <Filter>Source Files\Wasabi\api</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\parse\PathParseW.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\api\script\objects\rootobj.cpp"> + <Filter>Source Files\Wasabi\api</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\api\script\objects\rootobjcbx.cpp"> + <Filter>Source Files\Wasabi\api</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\api\script\scriptobji.cpp"> + <Filter>Source Files\Wasabi\api</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\api\script\scriptobjx.cpp"> + <Filter>Source Files\Wasabi\api</Filter> + </ClCompile> + <ClCompile Include="..\nu\ServiceWatcher.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\std_string.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\string\StringW.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\string\string.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + <ClCompile Include="..\Winamp\strutil.cpp"> + <Filter>Source Files\Winamp</Filter> + </ClCompile> + <ClCompile Include="..\Wasabi\bfc\wasabi_std.cpp"> + <Filter>Source Files\Wasabi\bfc</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__playlist.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="api_playlistmanager.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="api_playlists.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="B4SLoader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="B4SWriter.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="factory_Handler.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="factory_playlistmanager.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="factory_playlists.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Handler.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ifc_playlist.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ifc_playlistloader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\Winamp\JSAPI_ObjectArray.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="JSAPI2_Creator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="JSAPI2_Playlist.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="JSAPI2_Playlists.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="M3U8Writer.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="M3ULoader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="M3UWriter.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="pl_entry.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="PlaylistCounter.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="PlaylistManager.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Playlists.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="PlaylistsXML.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="PlaylistWriter.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="PLSLoader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="plstring.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="PLSWriter.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ScriptObjectFactory.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ScriptObjectService.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="SPlaylist.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="SPlaylistManager.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="SPlaylists.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="SPlaylistsEnumerator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="svc_playlisthandler.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="XMLString.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\Winamp\strutil.h"> + <Filter>Header Files\Winamp</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{e091476c-aae2-467b-9b96-08b12796cbcb}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{b035c0ae-5ec9-481d-a5ce-02d790985bc9}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{3c7b3569-6627-448b-a320-57a5fc880fe1}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Wasabi"> + <UniqueIdentifier>{158dd0e1-252e-49be-8630-9d3b31313f79}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Wasabi\bfc"> + <UniqueIdentifier>{9b816713-5c43-4613-9a32-54c9413d76fd}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Winamp"> + <UniqueIdentifier>{95d29037-d9f4-4431-8b6d-9312dcbdc38e}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Wasabi\api"> + <UniqueIdentifier>{93f96976-a073-46d0-822d-c7da3c1c8286}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\nu"> + <UniqueIdentifier>{01f330ce-f18e-43d9-9b79-b8def37065b1}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\Winamp"> + <UniqueIdentifier>{31801414-58c9-4a09-bd1a-2a71d7255072}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="playlist.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/playlist/playlist.xcodeproj/project.pbxproj b/Src/playlist/playlist.xcodeproj/project.pbxproj new file mode 100644 index 00000000..a0618eff --- /dev/null +++ b/Src/playlist/playlist.xcodeproj/project.pbxproj @@ -0,0 +1,199 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 0C19117B0BC1D80B005443D9 /* PlaylistCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0C19117A0BC1D80B005443D9 /* PlaylistCounter.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0C19117A0BC1D80B005443D9 /* PlaylistCounter.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = PlaylistCounter.cpp; sourceTree = "<group>"; }; + D2AAC0630554660B00DB518D /* playlist.w5s */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = playlist.w5s; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D289988505E68E00004EDB86 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* playlist */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* Source */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = playlist; + sourceTree = "<group>"; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 0C19117A0BC1D80B005443D9 /* PlaylistCounter.cpp */, + 0C1911740BC1D456005443D9 /* PLS */, + ); + name = Source; + sourceTree = "<group>"; + }; + 0C1911740BC1D456005443D9 /* PLS */ = { + isa = PBXGroup; + children = ( + ); + name = PLS; + sourceTree = "<group>"; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + D2AAC0630554660B00DB518D /* playlist.w5s */, + ); + name = Products; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D2AAC0600554660B00DB518D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + D2AAC0620554660B00DB518D /* playlist */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "playlist" */; + buildPhases = ( + D2AAC0600554660B00DB518D /* Headers */, + D2AAC0610554660B00DB518D /* Sources */, + D289988505E68E00004EDB86 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = playlist; + productName = playlist; + productReference = D2AAC0630554660B00DB518D /* playlist.w5s */; + productType = "com.apple.product-type.library.dynamic"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "playlist" */; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* playlist */; + projectDirPath = ""; + targets = ( + D2AAC0620554660B00DB518D /* playlist */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + D2AAC0610554660B00DB518D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C19117B0BC1D80B005443D9 /* PlaylistCounter.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB914B08733D8E0010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + EXECUTABLE_EXTENSION = w5s; + EXECUTABLE_PREFIX = ""; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ../Wasabi; + INSTALL_PATH = /usr/local/lib; + PRODUCT_NAME = playlist; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB914C08733D8E0010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + ppc, + i386, + ); + EXECUTABLE_EXTENSION = w5s; + EXECUTABLE_PREFIX = ""; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ../Wasabi; + INSTALL_PATH = /usr/local/lib; + PRODUCT_NAME = playlist; + }; + name = Release; + }; + 1DEB914F08733D8E0010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Debug; + }; + 1DEB915008733D8E0010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "playlist" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB914B08733D8E0010E9CD /* Debug */, + 1DEB914C08733D8E0010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "playlist" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB914F08733D8E0010E9CD /* Debug */, + 1DEB915008733D8E0010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/Src/playlist/plstring.cpp b/Src/playlist/plstring.cpp new file mode 100644 index 00000000..49c09110 --- /dev/null +++ b/Src/playlist/plstring.cpp @@ -0,0 +1,86 @@ +#include "plstring.h" +#include "api__playlist.h" +#include <shlwapi.h> +#include <stdint.h> + +static wchar_t *_plstring_wcsdup( const wchar_t *str ) +{ + if ( !str ) + return 0; + + size_t len = wcslen( str ); + size_t *self = (size_t *)calloc( ( len + 1 ) * sizeof( wchar_t ), sizeof( size_t ) ); + *self = 1; + + wchar_t *new_str = (wchar_t *)( ( (int8_t *)self ) + sizeof( size_t ) ); + memcpy( new_str, str, ( len + 1 ) * sizeof( wchar_t ) ); + + return new_str; +} + +static wchar_t *_plstring_malloc( size_t str_size ) +{ + size_t *self = (size_t *)calloc( ( str_size ), sizeof( size_t ) ); + *self = 1; + + wchar_t *new_str = (wchar_t *)( ( (int8_t *)self ) + sizeof( size_t ) ); + + return new_str; +} + +static void _plstring_release( wchar_t *str ) +{ + if ( str ) + { + size_t *self = (size_t *)( ( (int8_t *)str ) - sizeof( size_t ) ); + ( *self )--; + + if ( *self == 0 ) + free( self ); + } +} + +static void _plstring_retain( wchar_t *str ) +{ + if ( str ) + { + size_t *self = (size_t *)( ( (int8_t *)str ) - sizeof( size_t ) ); + ( *self )++; + } +} + +wchar_t *( *plstring_wcsdup )( const wchar_t *str ) = _plstring_wcsdup; +wchar_t *( *plstring_malloc )( size_t str_size ) = _plstring_malloc; +void ( *plstring_release )( wchar_t *str ) = _plstring_release; +void ( *plstring_retain )( wchar_t *str ) = _plstring_retain; + +static bool ndestring_tried_load = false; + +void plstring_init() +{ + if ( !ndestring_tried_load ) + { + wchar_t path[ MAX_PATH ] = { 0 }; + const wchar_t *PROGDIR = WASABI_API_APP->path_getAppPath(); + + PathCombineW( path, PROGDIR, L"winamp.exe" ); + HMODULE ndelib = LoadLibraryW( path ); + if ( ndelib ) + { + FARPROC ndestring_wcsdup = GetProcAddress( ndelib, "plstring_wcsdup" ); + FARPROC ndestring_malloc = GetProcAddress( ndelib, "plstring_malloc" ); + FARPROC ndestring_release = GetProcAddress( ndelib, "plstring_release" ); + FARPROC ndestring_retain = GetProcAddress( ndelib, "plstring_retain" ); + + if ( ndestring_wcsdup && ndestring_malloc && ndestring_release && ndestring_retain ) + { + *(FARPROC *)&plstring_wcsdup = *(FARPROC *)ndestring_wcsdup; + *(FARPROC *)&plstring_malloc = *(FARPROC *)ndestring_malloc; + *(FARPROC *)&plstring_release = *(FARPROC *)ndestring_release; + *(FARPROC *)&plstring_retain = *(FARPROC *)ndestring_retain; + } + } + + ndestring_tried_load = true; + } +}
\ No newline at end of file diff --git a/Src/playlist/plstring.h b/Src/playlist/plstring.h new file mode 100644 index 00000000..50dbc433 --- /dev/null +++ b/Src/playlist/plstring.h @@ -0,0 +1,7 @@ +#pragma once +#include <bfc/platform/types.h> +extern wchar_t *(*plstring_wcsdup)(const wchar_t *str); +extern wchar_t *(*plstring_malloc)(size_t str_size); +extern void (*plstring_release)(wchar_t *str); +extern void (*plstring_retain)(wchar_t *str); +void plstring_init();
\ No newline at end of file diff --git a/Src/playlist/resource.h b/Src/playlist/resource.h new file mode 100644 index 00000000..e27843b3 --- /dev/null +++ b/Src/playlist/resource.h @@ -0,0 +1,29 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by playlist.rc +// +#define IDS_STRING0 1 +#define IDS_M3U_PLAYLIST 1 +#define IDS_PLS_PLAYLIST 2 +#define IDS_WINAMP3_PLAYLIST 3 +#define IDS_ALL_PLAYLIST_TYPES 4 +#define IDS_PLAYLIST 5 +#define IDS_MPCPL_PLAYLIST 6 +#define IDS_FPL_PLAYLIST 7 +#define IDS_XSPF_PLAYLIST 8 +#define IDC_ALLOW_ALWAYS 1001 +#define IDC_ALLOW 1002 +#define IDC_BUTTON3 1003 +#define IDC_DENY 1003 +#define IDC_CHECK1 1004 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1005 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/playlist/svc_playlisthandler.h b/Src/playlist/svc_playlisthandler.h new file mode 100644 index 00000000..8dc44bc1 --- /dev/null +++ b/Src/playlist/svc_playlisthandler.h @@ -0,0 +1,110 @@ +#ifndef NULLSOFT_SVC_PLAYLISTHANDLER_H +#define NULLSOFT_SVC_PLAYLISTHANDLER_H + +#include <bfc/dispatch.h> +#include <bfc/platform/types.h> +#include "ifc_playlistloader.h" +#include <stdint.h> + +enum +{ + SVC_PLAYLISTHANDLER_SUCCESS = 0, + SVC_PLAYLISTHANDLER_FAILED = 1, +}; +class svc_playlisthandler : public Dispatchable +{ +protected: + svc_playlisthandler() {} + ~svc_playlisthandler() {} + +public: + static FOURCC getServiceType() { return svc_playlisthandler::SERVICETYPE; } + const wchar_t *EnumerateExtensions(size_t n); // returns 0 when it's done + const char *EnumerateMIMETypes(size_t n); // returns 0 when it's done, returns char * to match HTTP specs + const wchar_t *GetName(); // returns a name suitable for display to user of this playlist form (e.g. PLS Playlist) + int SupportedFilename(const wchar_t *filename); // returns SUCCESS and FAILED, so be careful ... + int SupportedMIMEType(const char *filename); // returns SUCCESS and FAILED, so be careful ... + ifc_playlistloader *CreateLoader(const wchar_t *filename); + void ReleaseLoader(ifc_playlistloader *loader); + int HasWriter(); // returns 1 if writing is supported + //ifc_playlistwriter CreateWriter(const wchar_t *writer); + //void ReleaseWriter(ifc_playlistwriter *writer); + + size_t SniffSizeRequired(); // return number of bytes required for detection on an unknown file + bool IsOurs(const int8_t *data, size_t sizeBytes); + +public: + DISPATCH_CODES + { + SVC_PLAYLISTHANDLER_ENUMEXTENSIONS = 10, + SVC_PLAYLISTHANDLER_ENUMMIMETYPES = 20, + SVC_PLAYLISTHANDLER_SUPPORTFILENAME= 30, + SVC_PLAYLISTHANDLER_SUPPORTMIME= 40, + SVC_PLAYLISTHANDLER_CREATELOADER = 50, + SVC_PLAYLISTHANDLER_RELEASELOADER = 60, + SVC_PLAYLISTHANDLER_CREATEWRITER= 70, + SVC_PLAYLISTHANDLER_RELEASEWRITER= 80, + SVC_PLAYLISTHANDLER_SNIFFSIZE=90, + SVC_PLAYLISTHANDLER_SNIFF=100, + SVC_PLAYLISTHANDLER_GETNAME=110, + SVC_PLAYLISTHANDLER_HASWRITER=120, + }; + + enum + { + SERVICETYPE = MK3CC('p','l','h') + }; + +}; + +inline const wchar_t *svc_playlisthandler::GetName() +{ + return _call(SVC_PLAYLISTHANDLER_GETNAME, (const wchar_t *)0); +} + +inline const wchar_t *svc_playlisthandler::EnumerateExtensions(size_t n) +{ + return _call(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, (const wchar_t *)0, n); +}; + +inline const char *svc_playlisthandler::EnumerateMIMETypes(size_t n) +{ + return _call(SVC_PLAYLISTHANDLER_ENUMMIMETYPES, (const char *)0, n); +} + +inline int svc_playlisthandler::SupportedFilename(const wchar_t *filename) +{ + return _call(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, (int)SVC_PLAYLISTHANDLER_FAILED, filename); +} + +inline int svc_playlisthandler::SupportedMIMEType(const char *filename) +{ + return _call(SVC_PLAYLISTHANDLER_SUPPORTMIME, (int)SVC_PLAYLISTHANDLER_FAILED, filename); +} + +inline ifc_playlistloader *svc_playlisthandler::CreateLoader(const wchar_t *filename) +{ + return _call(SVC_PLAYLISTHANDLER_CREATELOADER, (ifc_playlistloader *)0, filename); +} + +inline void svc_playlisthandler::ReleaseLoader(ifc_playlistloader *loader) +{ + _voidcall(SVC_PLAYLISTHANDLER_RELEASELOADER, loader); +} + +inline size_t svc_playlisthandler::SniffSizeRequired() +{ + return _call(SVC_PLAYLISTHANDLER_SNIFFSIZE, (size_t)0); +} + +inline bool svc_playlisthandler::IsOurs(const int8_t *data, size_t sizeBytes) +{ + return _call(SVC_PLAYLISTHANDLER_SNIFF, (bool)false, data, sizeBytes); +} + +inline int svc_playlisthandler::HasWriter() +{ + return _call(SVC_PLAYLISTHANDLER_HASWRITER, (int)0); +} + +#endif
\ No newline at end of file diff --git a/Src/playlist/util.cpp b/Src/playlist/util.cpp new file mode 100644 index 00000000..debd3be0 --- /dev/null +++ b/Src/playlist/util.cpp @@ -0,0 +1,77 @@ +#include <shlobj.h> +#include <strsafe.h> + +#include "main.h" +#include "api__playlist.h" +#include "../nu/ns_wc.h" + +HRESULT ResolveShortCut(HWND hwnd, LPCWSTR pszShortcutFile, LPWSTR pszPath) +{ + IShellLinkW *psl = 0; + WIN32_FIND_DATAW wfd = {0}; + + *pszPath = 0; // assume failure + + HRESULT hres = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void **)&psl ); + if ( SUCCEEDED( hres ) ) + { + IPersistFile *ppf = 0; + hres = psl->QueryInterface( &ppf ); // OLE 2! Yay! --YO + if ( SUCCEEDED( hres ) ) + { + hres = ppf->Load( pszShortcutFile, STGM_READ ); + if ( SUCCEEDED( hres ) ) + { + hres = psl->Resolve( hwnd, SLR_ANY_MATCH ); + if ( SUCCEEDED( hres ) ) + { + wchar_t szGotPath[ MAX_PATH ] = { 0 }; + StringCchCopyW( szGotPath, MAX_PATH, pszShortcutFile ); + hres = psl->GetPath( szGotPath, MAX_PATH, &wfd, SLGP_SHORTPATH ); + StringCchCopyW( pszPath, MAX_PATH, szGotPath ); + } + } + + ppf->Release(); + } + + psl->Release(); + } + + return SUCCEEDED(hres); +} + +bool IsUrl(const wchar_t *url) +{ + return !!wcsstr(url, L"://"); +} + +void SetUserAgent(api_httpreceiver *http) +{ + char agent[256] = {0}; + StringCchPrintfA(agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString()); + http->addheader(agent); +} + +const char *GetProxy() +{ + static char proxy[ 256 ] = ""; + // {C0A565DC-0CFE-405a-A27C-468B0C8A3A5C} + const GUID internetConfigGroupGUID = + { 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c } }; + ifc_configgroup *group = AGAVE_API_CONFIG->GetGroup( internetConfigGroupGUID ); + if ( group ) + { + ifc_configitem *item = group->GetItem( L"Proxy" ); + if ( item ) + { + const wchar_t *wideProxy = item->GetString(); + if ( wideProxy ) + { + WideCharToMultiByteSZ( CP_ACP, 0, wideProxy, -1, proxy, 256, 0, 0 ); + } + } + } + + return proxy; +}
\ No newline at end of file diff --git a/Src/playlist/version.rc2 b/Src/playlist/version.rc2 new file mode 100644 index 00000000..e4d8ea6f --- /dev/null +++ b/Src/playlist/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION WINAMP_PRODUCTVER + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp 5.x System Component" + VALUE "FileVersion", STR_WINAMP_PRODUCTVER + VALUE "InternalName", "playlist.w5s" + VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "playlist.w5s" + VALUE "ProductName", "Winamp Playlists Core Service" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |