Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

253
Src/playlist/B4SLoader.cpp Normal file
View File

@ -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;

46
Src/playlist/B4SLoader.h Normal file
View File

@ -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

View File

@ -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);
}

24
Src/playlist/B4SWriter.h Normal file
View File

@ -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

210
Src/playlist/Handler.cpp Normal file
View File

@ -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;

32
Src/playlist/Handler.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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 );
};
}

View File

@ -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;
}

View File

@ -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);
};
}

View File

@ -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);
}

21
Src/playlist/M3U8Writer.h Normal file
View File

@ -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

466
Src/playlist/M3ULoader.cpp Normal file
View File

@ -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;

31
Src/playlist/M3ULoader.h Normal file
View File

@ -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

View File

@ -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;
}
}

23
Src/playlist/M3UWriter.h Normal file
View File

@ -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

159
Src/playlist/PLSLoader.cpp Normal file
View File

@ -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

22
Src/playlist/PLSLoader.h Normal file
View File

@ -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

View File

@ -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);
}

21
Src/playlist/PLSWriter.h Normal file
View File

@ -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

230
Src/playlist/Playlist.cpp Normal file
View File

@ -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;
}*/

48
Src/playlist/Playlist.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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, &copy );
Save( destFn, &copy );
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

View File

@ -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

View File

@ -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

746
Src/playlist/Playlists.cpp Normal file
View File

@ -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 &copy )
{
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"&amp;", fp );
break;
case L'>':
fputws( L"&gt;", fp );
break;
case L'<':
fputws( L"&lt;", fp );
break;
case L'\'':
fputws( L"&apos;", fp );
break;
case L'\"':
fputws( L"&quot;", 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

74
Src/playlist/Playlists.h Normal file
View File

@ -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 &copy);
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

View File

@ -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

View File

@ -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

270
Src/playlist/SPlaylist.cpp Normal file
View File

@ -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
}

52
Src/playlist/SPlaylist.h Normal file
View File

@ -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 } };

View File

@ -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;
}

View File

@ -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:
};

157
Src/playlist/SPlaylists.cpp Normal file
View File

@ -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 );
}

40
Src/playlist/SPlaylists.h Normal file
View File

@ -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:
};

View File

@ -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 ) );
}

View File

@ -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;
};

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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;
};

View File

@ -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

29
Src/playlist/XMLString.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

139
Src/playlist/ifc_playlist.h Normal file
View File

@ -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

View File

@ -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
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
};

View File

@ -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

134
Src/playlist/main.cpp Normal file
View File

@ -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

26
Src/playlist/main.h Normal file
View File

@ -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

336
Src/playlist/pl_entry.cpp Normal file
View File

@ -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;
}

61
Src/playlist/pl_entry.h Normal file
View File

@ -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

49
Src/playlist/playlist.mi Normal file
View File

@ -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

86
Src/playlist/playlist.rc Normal file
View File

@ -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

30
Src/playlist/playlist.sln Normal file
View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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 */;
}

86
Src/playlist/plstring.cpp Normal file
View File

@ -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;
}
}

7
Src/playlist/plstring.h Normal file
View File

@ -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();

29
Src/playlist/resource.h Normal file
View File

@ -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

View File

@ -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

77
Src/playlist/util.cpp Normal file
View File

@ -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;
}

39
Src/playlist/version.rc2 Normal file
View File

@ -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 <20> 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