diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_local/add.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/Plugins/Library/ml_local/add.cpp')
-rw-r--r-- | Src/Plugins/Library/ml_local/add.cpp | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_local/add.cpp b/Src/Plugins/Library/ml_local/add.cpp new file mode 100644 index 00000000..d99c93b8 --- /dev/null +++ b/Src/Plugins/Library/ml_local/add.cpp @@ -0,0 +1,478 @@ +#include "main.h" +#include "ml_local.h" +#include "api_mldb.h" +#include <commctrl.h> +#include "resource.h" +#include "../replicant/nu/ns_wc.h" +#include "../nde/nde.h" +#include "../Agave/Language/api_language.h" +#include "..\..\General\gen_ml/config.h" +#include "..\..\General\gen_ml/gaystring.h" +#include "time.h" +#include "../winamp/in2.h" +#include "../Winamp/strutil.h" +#include <shlwapi.h> +#include <strsafe.h> + + +bool skipTitleInfo=false; + +static int getFileInfoW(const wchar_t *filename, const wchar_t *metadata, wchar_t *dest, size_t len) +{ + dest[0]=0; + return AGAVE_API_METADATA->GetExtendedFileInfo(filename, metadata, dest, len); +} + +static time_t FileTimeToUnixTime(FILETIME *ft) +{ + ULARGE_INTEGER end; + memcpy(&end,ft,sizeof(end)); + end.QuadPart -= 116444736000000000; + end.QuadPart /= 10000000; // 100ns -> seconds + return (time_t)end.QuadPart; +} + +void makeFilename2W(const wchar_t *filename, wchar_t *filename2, int filename2_len) +{ + filename2[0]=0; + GetLongPathNameW(filename, filename2, filename2_len); + if (!_wcsicmp(filename,filename2)) filename2[0]=0; +} + +void makeFilename2(const char *filename, char *filename2, int filename2_len) +{ + filename2[0]=0; + GetLongPathNameA(filename, filename2, filename2_len); + if (!stricmp(filename,filename2)) filename2[0]=0; +} + +static __int64 FileSize64(HANDLE file) +{ + LARGE_INTEGER position; + position.QuadPart=0; + position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart); + + if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) + return INVALID_FILE_SIZE; + else + return position.QuadPart; +} + +static void GetFileSizeAndTime(const wchar_t *filename, __int64 *file_size, time_t *file_time) +{ + WIN32_FILE_ATTRIBUTE_DATA file_data; + if (GetFileAttributesExW(filename, GetFileExInfoStandard, &file_data) == FALSE) + { + // GetFileAttributesEx failed. that sucks, let's try something else + HANDLE hFile=CreateFileW(filename,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + FILETIME lt = {0}; + if (GetFileTime(hFile,NULL,NULL,<)) + { + *file_time=FileTimeToUnixTime(<); + } + *file_size=FileSize64(hFile); + CloseHandle(hFile); + } + } + else + { + // success + *file_time = FileTimeToUnixTime(&file_data.ftLastWriteTime); + LARGE_INTEGER size64; + size64.LowPart = file_data.nFileSizeLow; + size64.HighPart = file_data.nFileSizeHigh; + *file_size = size64.QuadPart; + } +} + +int FindFileInDatabase(nde_scanner_t s, int fieldId, const wchar_t *filename, wchar_t alternate[MAX_PATH]) +{ + alternate[0]=0; + + makeFilename2W(filename,alternate,MAX_PATH); + if (alternate[0]) + { + if (NDE_Scanner_LocateFilename(s, fieldId, FIRST_RECORD, alternate)) + { + return 2; + } + } + if (NDE_Scanner_LocateFilename(s, fieldId, FIRST_RECORD, filename)) + return 1; + + return 0; +} + +static inline void GetOptionalField(nde_scanner_t s, const wchar_t *filename, const wchar_t *field, unsigned char field_id) +{ + wchar_t tmp[1024]={0}; + if (getFileInfoW(filename,field,tmp,sizeof(tmp)/sizeof(wchar_t))) + { + if(tmp[0]) + { + tmp[1023]=0; // just in case + db_setFieldStringW(s, field_id,tmp); + } + else + { + db_removeField(s, field_id); + } + } +} + +static inline void GetOptionalFieldInt(nde_scanner_t s, const wchar_t *filename, const wchar_t *field, unsigned char field_id) +{ + wchar_t tmp[128]={0}; + if (getFileInfoW(filename,field,tmp,sizeof(tmp)/sizeof(wchar_t))) + { + if(tmp[0]) + { + tmp[127]=0; // just in case + db_setFieldInt(s,field_id,_wtoi(tmp)); + } + else + { + db_removeField(s, field_id); + } + } +} + +static inline void GetNonBlankFieldInt(nde_scanner_t s, const wchar_t *filename, const wchar_t *field, unsigned char field_id) +{ + wchar_t tmp[128]={0}; + if (getFileInfoW(filename,field,tmp,sizeof(tmp)/sizeof(wchar_t))) + { + if(tmp[0]) + { + tmp[127]=0; // just in case + db_setFieldInt(s,field_id,_wtoi(tmp)); + } + } +} + + +// return values +// 0 - error +// 1 - success +// -2 - record not found (in update only mode) +int addFileToDb(const wchar_t *filename, int onlyupdate, int use_metadata, int guess_mode, int playcnt, int lastplay, bool force) +{ + if (!_wcsicmp(PathFindExtensionW(filename), L".cda")) + return 0; + + __int64 file_size=INVALID_FILE_SIZE; + time_t file_time=0; + GetFileSizeAndTime(filename, &file_size, &file_time); + if (file_size == INVALID_FILE_SIZE || file_size == 0) + return 0; + + openDb(); // just in case it's not opened yet (this function will return immediately if it's already open) + EnterCriticalSection(&g_db_cs); + + nde_scanner_t s = NDE_Table_CreateScanner(g_table); + + wchar_t filename2[MAX_PATH] = {0}; // full lfn path if set + int found = FindFileInDatabase(s, MAINTABLE_ID_FILENAME, filename, filename2); + + if (found) // For updating + { + // if an update wasn't forced, see if the file's timestamp or filesize have changed + if (!force && NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_FILESIZE)) + { + nde_field_t f=NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_FILETIME); + if (f && file_time <= NDE_IntegerField_GetValue(f)) + { + f=NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_LASTUPDTIME); + if (f && file_time <= NDE_IntegerField_GetValue(f)) + { + NDE_Table_DestroyScanner(g_table, s); + LeaveCriticalSection(&g_db_cs); + return 1; + } + } + } + NDE_Scanner_Edit(s); + if (found == 1 && filename2[0]) db_setFieldStringW(s,MAINTABLE_ID_FILENAME,filename2); // if we have a better filename, update it + nde_field_t f=NDE_Scanner_GetFieldByID(s, MAINTABLE_ID_PLAYCOUNT); + int cnt = f?NDE_IntegerField_GetValue(f):0; + if (!cnt) + db_setFieldInt(s,MAINTABLE_ID_PLAYCOUNT,0); + } + else // Adding an entry from scratch + { + if (onlyupdate) + { + NDE_Table_DestroyScanner(g_table, s); + LeaveCriticalSection(&g_db_cs); + + // Issue a wasabi system callback after we have successfully updated a file in the ml database + WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_UPDATED_EXTERNAL, (size_t)filename, 0); + return -2; + } + // new file + NDE_Scanner_New(s); + db_setFieldStringW(s,MAINTABLE_ID_FILENAME,filename2[0]?filename2:filename); + db_setFieldInt(s,MAINTABLE_ID_PLAYCOUNT,playcnt); + if (lastplay) + db_setFieldInt(s,MAINTABLE_ID_LASTPLAY,lastplay); + db_setFieldInt(s,MAINTABLE_ID_DATEADDED, (int)time(NULL)); + } + + int hasttitle=0, hastartist=0, hastalbum=0, hasttrack=0; + int hastyear=0, hastlength=0; + int hasdisc=0, hasalbumartist=0; + int hasttype=0, hasdiscs=0, hastracks=0, hasbitrate=0; + int the_length_sec=0; + wchar_t m_artist[1024] = {0}, tmp[1024] = {0}; + + if(getFileInfoW(filename, DB_FIELDNAME_type, tmp, sizeof(tmp) / sizeof(wchar_t)) ) + { + if(tmp[0]) { int type=_wtoi(tmp); db_setFieldInt(s,MAINTABLE_ID_TYPE,type); hasttype++; } + } + + if (getFileInfoW(filename, DB_FIELDNAME_length,tmp,sizeof(tmp)/sizeof(wchar_t))) + { + if(tmp[0]) { db_setFieldInt(s,MAINTABLE_ID_LENGTH,the_length_sec=(_wtoi(tmp)/1000)); hastlength++; } + } + + if (getFileInfoW(filename, DB_FIELDNAME_bitrate,tmp,sizeof(tmp)/sizeof(wchar_t))) + { + if(tmp[0]) { db_setFieldInt(s,MAINTABLE_ID_BITRATE,_wtoi(tmp)); hasbitrate++; } + } + + if(use_metadata && getFileInfoW(filename, DB_FIELDNAME_title,tmp,sizeof(tmp)/sizeof(wchar_t))) + { + if(tmp[0]) + { + db_setFieldStringW(s,MAINTABLE_ID_TITLE,tmp); + hasttitle++; + } + + getFileInfoW( filename, DB_FIELDNAME_artist, tmp, sizeof( tmp ) / sizeof( wchar_t ) ); + + if(tmp[0]) + { + StringCchCopyW(m_artist, 1024, tmp); + db_setFieldStringW(s,MAINTABLE_ID_ARTIST,tmp); + hastartist++; + } + + getFileInfoW(filename, DB_FIELDNAME_album,tmp,sizeof(tmp)/sizeof(wchar_t)); + + if(tmp[0]) + { + db_setFieldStringW(s,MAINTABLE_ID_ALBUM,tmp); + hastalbum++; + } + + GetOptionalField(s, filename, DB_FIELDNAME_comment, MAINTABLE_ID_COMMENT); + + getFileInfoW(filename, DB_FIELDNAME_year,tmp,sizeof(tmp)/sizeof(wchar_t)); + if(tmp[0] && !wcsstr(tmp,L"__") && !wcsstr(tmp,L"/") && !wcsstr(tmp,L"\\") && !wcsstr(tmp,L".")) { + wchar_t *p=tmp; + while (p && *p) + { + if (*p == L'_') *p=L'0'; + p++; + } + int y=_wtoi(tmp); + if(y!=0) { db_setFieldInt(s,MAINTABLE_ID_YEAR,_wtoi(tmp)); hastyear++; } + } + GetOptionalField(s, filename, DB_FIELDNAME_genre, MAINTABLE_ID_GENRE); + + getFileInfoW(filename, DB_FIELDNAME_track,tmp,sizeof(tmp)/sizeof(wchar_t)); + if(tmp[0]) + { + int track, tracks; + ParseIntSlashInt(tmp, &track, &tracks); + if (track > 0) + { + db_setFieldInt(s,MAINTABLE_ID_TRACKNB,track); + hasttrack++; + } + if (tracks > 0) + { + db_setFieldInt(s,MAINTABLE_ID_TRACKS,tracks); + hastracks++; + } + } + getFileInfoW(filename, DB_FIELDNAME_disc,tmp,sizeof(tmp)/sizeof(wchar_t)); + if(tmp[0]) + { + int disc, discs; + ParseIntSlashInt(tmp, &disc, &discs); + if (disc > 0) + { + db_setFieldInt(s,MAINTABLE_ID_DISC,disc); + hasdisc++; + } + if (discs > 0) + { + db_setFieldInt(s,MAINTABLE_ID_DISCS,discs); + hasdiscs++; + } + } + getFileInfoW(filename, DB_FIELDNAME_albumartist,tmp,sizeof(tmp)/sizeof(wchar_t)); + if(tmp[0]) { db_setFieldStringW(s,MAINTABLE_ID_ALBUMARTIST,tmp); hasalbumartist++; } + GetOptionalField(s, filename, DB_FIELDNAME_publisher, MAINTABLE_ID_PUBLISHER); + GetOptionalField(s, filename, DB_FIELDNAME_composer, MAINTABLE_ID_COMPOSER); + GetOptionalField(s, filename, DB_FIELDNAME_replaygain_album_gain, MAINTABLE_ID_ALBUMGAIN); + GetOptionalField(s, filename, DB_FIELDNAME_replaygain_track_gain, MAINTABLE_ID_TRACKGAIN); + GetOptionalFieldInt(s, filename, DB_FIELDNAME_bpm, MAINTABLE_ID_BPM); + GetOptionalField(s, filename, DB_FIELDNAME_GracenoteFileID, MAINTABLE_ID_GRACENOTEFILEID); + GetOptionalField(s, filename, DB_FIELDNAME_GracenoteExtData, MAINTABLE_ID_GRACENOTEEXTDATA); + GetOptionalFieldInt(s, filename, DB_FIELDNAME_lossless, MAINTABLE_ID_LOSSLESS); + GetOptionalField(s, filename, DB_FIELDNAME_category, MAINTABLE_ID_CATEGORY); + GetOptionalField(s, filename, DB_FIELDNAME_codec, MAINTABLE_ID_CODEC); + GetOptionalField(s, filename, DB_FIELDNAME_director, MAINTABLE_ID_DIRECTOR); + GetOptionalField(s, filename, DB_FIELDNAME_producer, MAINTABLE_ID_PRODUCER); + GetOptionalFieldInt(s, filename, DB_FIELDNAME_width, MAINTABLE_ID_WIDTH); + GetOptionalFieldInt(s, filename, DB_FIELDNAME_height, MAINTABLE_ID_HEIGHT); + if (g_config->ReadInt(L"writeratings", 0)) + GetOptionalFieldInt(s, filename, DB_FIELDNAME_rating, MAINTABLE_ID_RATING); + else + GetNonBlankFieldInt(s, filename, DB_FIELDNAME_rating, MAINTABLE_ID_RATING); + GetOptionalField(s, filename, L"mime", MAINTABLE_ID_MIMETYPE); + } + + int guessmode = guess_mode; + if (guessmode != 2 && ((!hasttitle) + (!hastartist) + (!hastalbum) + (!hasttrack) >= (g_guessifany ? 1 : 4))) + { + int tn = 0; + wchar_t *artist = 0, *album = 0, *title = 0, *guessbuf = 0; + + if (guessmode==1) + { + guessbuf = _wcsdup(filename2[0] ? filename2 : filename); + + wchar_t *p=scanstr_backW(guessbuf, L"\\/.", guessbuf); + if (*p == '.') + { + *p = 0; + p = scanstr_backW(guessbuf, L"\\/", guessbuf); + } + + if (p > guessbuf) + { + *p = 0; + title = p+1; + p=scanstr_backW(guessbuf, L"\\/", guessbuf); + if (p > guessbuf) + { + *p = 0; + album = p+1; + p=scanstr_backW(guessbuf,L"\\/", guessbuf); + if (p > guessbuf) + { + *p = 0; + artist = p+1; + } + } + } + } + else + guessbuf = guessTitles(filename2[0] ? filename2 : filename, &tn, &artist, &album, &title); + + if (guessbuf) + { + if (!hasttitle && title) { hasttitle++; db_setFieldStringW(s,MAINTABLE_ID_TITLE,title); } + if (!hastartist && artist) { hastartist++; db_setFieldStringW(s,MAINTABLE_ID_ARTIST,artist); StringCbCopyW(m_artist, sizeof(m_artist), artist); } + if (!hastalbum && album) { hastalbum++; db_setFieldStringW(s,MAINTABLE_ID_ALBUM,album); } + if (!hasttrack && tn) { hasttrack++; db_setFieldInt(s,MAINTABLE_ID_TRACKNB,tn); } + free(guessbuf); + } + } + + if (!hastlength || !hasttitle) + { + // try to query length and title using older GetFileInfo input plugin API + wchar_t ft[1024] = {0}; + basicFileInfoStructW bi={0}; + bi.filename=filename2[0]?filename2:filename; + bi.length=-1; + bi.title=ft; + bi.titlelen=1024; + skipTitleInfo=true; + LeaveCriticalSection(&g_db_cs); // benski> not actually sure if this is safe, but it prevents a deadlock + SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&bi,IPC_GET_BASIC_FILE_INFOW); + EnterCriticalSection(&g_db_cs); + skipTitleInfo=false; + if (!hastlength && bi.length >= 0) + { + hastlength=1; + db_setFieldInt(s,MAINTABLE_ID_LENGTH,the_length_sec=bi.length); + } + if (!hasttitle && ft[0]) + { + hasttitle=1; + db_setFieldStringW(s,MAINTABLE_ID_TITLE,ft); + } + } + + // set up default (empty) strings/values + if (!hasttitle) // title=filename + { + wchar_t *p = PathFindFileNameW(filename), *dup = _wcsdup(p); + if (dup) + { + PathRemoveExtensionW(dup); + db_setFieldStringW(s,MAINTABLE_ID_TITLE,dup); + free(dup); + } + } + + if (!hastartist) db_removeField(s,MAINTABLE_ID_ARTIST); + if (!hastalbum) db_removeField(s,MAINTABLE_ID_ALBUM); + if (!hasttrack) db_removeField(s,MAINTABLE_ID_TRACKNB); + if (!hastracks) db_removeField(s,MAINTABLE_ID_TRACKS); + if (!hastyear) db_removeField(s,MAINTABLE_ID_YEAR); + if (!hastlength) db_removeField(s,MAINTABLE_ID_LENGTH); + if (!hasttype) db_setFieldInt(s,MAINTABLE_ID_TYPE,0); //audio + if (!hasdisc) db_removeField(s, MAINTABLE_ID_DISC); + if (!hasdiscs) db_removeField(s, MAINTABLE_ID_DISCS); + if (!hasalbumartist) + { + if (hastartist && g_config->ReadInt(L"artist_as_albumartist", 1)) + db_setFieldStringW(s, MAINTABLE_ID_ALBUMARTIST, m_artist); + else + db_removeField(s, MAINTABLE_ID_ALBUMARTIST); + } + + if (file_size != INVALID_FILE_SIZE) + { + db_setFieldInt64(s,MAINTABLE_ID_FILESIZE, file_size); + } + else db_removeField(s,MAINTABLE_ID_FILESIZE); + + db_setFieldInt(s,MAINTABLE_ID_LASTUPDTIME, (int)time(NULL)); + db_setFieldInt(s,MAINTABLE_ID_FILETIME, (int)file_time); + + if (!hasbitrate && the_length_sec) + { + __int64 br =(file_size*8LL) / (__int64)the_length_sec; + br /= 1000; + db_setFieldInt(s,MAINTABLE_ID_BITRATE,(int)br); + } + + NDE_Scanner_Post(s); + g_table_dirty++; + + NDE_Table_DestroyScanner(g_table, s); + + LeaveCriticalSection(&g_db_cs); + + if (found) + { + // Issue a wasabi system callback after we have successfully updated a file in the ml database + WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_UPDATED, (size_t)filename, 0); + } + else + { + // Issue a wasabi system callback after we have successfully added a file in the ml database + WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_ADDED, (size_t)filename, 0); + } + + return 1; +}
\ No newline at end of file |