aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_local/add.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_local/add.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-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.cpp478
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,&lt))
+ {
+ *file_time=FileTimeToUnixTime(&lt);
+ }
+ *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