Deezer native API under linux: Unanble to create simple app - c++

I tried to create simple app which will connect to deezer and play single song. I got access token and wrote this program.
Target machine: linux x86_64
API version: v1.0.1
#include <iostream>
#include <deezer-api.h>
#include <deezer-player.h>
#define ACCESS_TOKEN "ACCESS TOKEN HERE"
int main(int argc, char **argv)
{
dz_connect_configuration cfg = {0};
cfg.app_id = "APP ID HERE";
cfg.product_id = "product_id";
cfg.product_build_id = "0.0.0";
dz_connect_handle dz_handle = dz_connect_new(&cfg);
if (dz_handle == nullptr)
{
return 100;
}
auto err_code = dz_connect_activate(dz_handle, nullptr);
if (err_code > DZ_ERROR_NO_ERROR_ASYNC)
{
return err_code;
}
err_code = dz_connect_set_access_token(dz_handle, nullptr, nullptr, ACCESS_TOKEN);
if (err_code > DZ_ERROR_NO_ERROR_ASYNC)
{
return err_code;
}
auto dz_player_handle = dz_player_new(dz_handle);
if (dz_player_handle == nullptr)
{
return 200;
}
err_code = dz_player_activate(dz_player_handle, nullptr);
if (err_code > DZ_ERROR_NO_ERROR_ASYNC)
{
return err_code;
}
err_code = dz_player_load(dz_player_handle, nullptr, nullptr, "dzmedia:///track/3135556");
if (err_code > DZ_ERROR_NO_ERROR_ASYNC)
{
return err_code;
}
err_code = dz_player_play(dz_player_handle, nullptr, nullptr, DZ_PLAYER_PLAY_CMD_START_TRACKLIST, DZ_TRACKLIST_AUTOPLAY_MANUAL, DZ_INDEX_IN_PLAYLIST_CURRENT);
if (err_code > DZ_ERROR_NO_ERROR_ASYNC)
{
return err_code;
}
std::cin.get();
return 0;
}
Suddenly i ran into problems.
First run shows me this output:
398748:201417 dz_bufferevent: [dz_bufferevent_on_activation:561] could not open /var/tmp/settings.dat
I created this file, but I suspect that it shouldn't be empty.
After second run with /var/tmp/settings.dat created i got this output:
399206:328658 dz_crash_handler: [dz_crash_handler_init:284] Crash Handler available
399206:329404 dzcrashreport-server-disk: [dz_crashreport_server_url_on_read_data:436] ERROR - Open failure err: 7
399206:329533 dzcrashreport-server-disk: [dz_crashreport_server_url_on_read_data:436] ERROR - Open failure err: 7
399206:329590 dzcrashreport-server-disk: [dz_crashreport_server_url_on_read_data:436] ERROR - Open failure err: 7
399206:329664 dzcrashreport-server-disk: [dz_crashreport_server_url_on_read_data:436] ERROR - Open failure err: 7
399206:329726 dzcrashreport-server-disk: [dz_crashreport_server_url_on_read_data:436] ERROR - Open failure err: 7
399206:329736 dzcrashreport-sender: [dz_crash_report_sender_retrieve_server_url_on_result:213] Error - couldn't read URL server err:0
399206:336505 pulseaudio-engine: [dz_audioengine_set_output_gain:1184] not init
399206:336520 pulseaudio-engine: [dz_audioengine_set_output_gain:1184] not init
399206:336577 player: [dz_player_play_licence:1756] unknown error 131079
I can't understand, what i did wrong?
UPDATE:
Deezer Native API: v1.0.1-v00349200
Still no luck. Tried different variations of "user_profile_path":
.
./
./user - was created in working dir with 777 rights
/var/tmp
There were no files in the folder created by API.
Still got errors:
[dz_crashreport_server_url_on_read_data:436] ERROR - Open failure err: 7
[dz_crash_report_sender_retrieve_server_url_on_result:213] Error - couldn't read URL server err:0
After adding implementation of callbacks i see this:
dz_connect_set_access_token calls callback dz_activity_operation_callback with status DZ_ERROR_CONNECT_SESSION_OFFLINE_MODE
dz_player_play calls callback dz_player_onevent_cb with event DZ_PLAYER_EVENT_PLAYLIST_TRACK_NO_RIGHT
Well, i checked that i have rights to play this track.

the "could not open" message is just informative.
The file will be automatically created (or updated if detected corrupted).
The issue seems more linked to the dz_connect_configuration, you have to set the "user_profile_path" to a valid path. This is where user temporary files will be stored.
Regards,
Cyril
UPDATE:
Few tips that could also help:
My mistake, one call seems missing:
dz_connect_cache_path_set(dz_handle, NULL, NULL, <user_profile_path>);
The DZ_INDEX_IN_PLAYLIST_CURRENT must be replaced by 0. I do agree this one is not obvious...
Check that the access_token you have created has the offline_access enable when calling https://connect.deezer.com/oauth/auth.php?app_id=YOUR_APP_ID&redirect_uri=YOUR_REDIRECT_URI&perms=basic_access,email,offline_access (cf: http://developers.deezer.com/api/oauth and http://developers.deezer.com/api/permissions)
UPDATE 2:
Since my last answer I have released a sample code on Github:
https://github.com/deezer/native-sdk-samples
I suggest that you have a quick look :)
What I have noticed in your full code you sent me in private is that:
You are not calling dz_connect_offline_mode(...,false); It will actually trigger the login process of the Native SDK.
You are not waiting the DZ_CONNECT_EVENT_USER_LOGIN_OK to load and play the track.
Best regards,
Cyril

Related

Setting Status icon for CFAPI does not work as expected

I try to set the status icon of the placeholder file created with CFAPI to error. (see below)
content of folder T:
I set the error state on the file, but it does not display the error. However the error is displayed on the containing folder.
I use following code to set the error on the file (the complete code is published on github):
void SetTransferStatus(_In_ PCWSTR fullPath, _In_ SYNC_TRANSFER_STATUS status)
{
// Tell the Shell so File Explorer can display the progress bar in its view
try
{
// First, get the Volatile property store for the file. That's where the properties are maintained.
winrt::com_ptr<IShellItem2> shellItem;
winrt::check_hresult(SHCreateItemFromParsingName(fullPath, nullptr, __uuidof(shellItem), shellItem.put_void()));
winrt::com_ptr<IPropertyStore> propStoreVolatile;
winrt::check_hresult(
shellItem->GetPropertyStore(
GETPROPERTYSTOREFLAGS::GPS_READWRITE | GETPROPERTYSTOREFLAGS::GPS_VOLATILEPROPERTIESONLY,
__uuidof(propStoreVolatile),
propStoreVolatile.put_void()));
// Set the sync transfer status accordingly
PROPVARIANT transferStatus;
winrt::check_hresult(
InitPropVariantFromUInt32(
status,
&transferStatus));
winrt::check_hresult(propStoreVolatile->SetValue(PKEY_SyncTransferStatus, transferStatus));
// Without this, all your hard work is wasted.
winrt::check_hresult(propStoreVolatile->Commit());
// Broadcast a notification that something about the file has changed, so that apps
// who subscribe (such as File Explorer) can update their UI to reflect the new progress
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, static_cast<LPCVOID>(fullPath), nullptr);
//wprintf(L"Succesfully Set Transfer Progress on \"%s\" to %llu/%llu\n", fullPath, completed, total);
}
catch (...)
{
// winrt::to_hresult() will eat the exception if it is a result of winrt::check_hresult,
// otherwise the exception will get rethrown and this method will crash out as it should
wprintf(L"Failed to Set Transfer Progress on \"%s\" with %08x\n", fullPath, static_cast<HRESULT>(winrt::to_hresult()));
}
}
In addition, if I delete the file and create a new file the state will still be on error.
Someone pointed me to an pull request in the windows cloud mirror sample that shows how to accomplish this.
This is the code:
void Utilities::UpdateErrorOnItem(PCWSTR path, bool setError)
{
try
{
winrt::com_ptr<IShellItem2> item;
winrt::check_hresult(SHCreateItemFromParsingName(path, nullptr, IID_PPV_ARGS(item.put())));
winrt::com_ptr<IPropertyStore> propertyStore;
winrt::check_hresult(item->GetPropertyStore(GPS_READWRITE | GPS_EXTRINSICPROPERTIESONLY, IID_PPV_ARGS(propertyStore.put())));
PROPVARIANT propVar{};
if (setError)
{
propVar.vt = VT_UI4;
propVar.ulVal = static_cast<unsigned long>(E_FAIL);
winrt::check_hresult(propertyStore->SetValue(PKEY_LastSyncError, propVar));
}
else
{
// Clear by setting to empty
propVar.vt = VT_EMPTY;
winrt::check_hresult(propertyStore->SetValue(PKEY_LastSyncError, propVar));
}
winrt::check_hresult(propertyStore->Commit());
}
catch (...)
{
// winrt::to_hresult() will eat the exception if it is a result of winrt::check_hresult,
// otherwise the exception will get rethrown and this method will crash out as it should
wprintf(L"Failed to set error state with %08x\n", static_cast<HRESULT>(winrt::to_hresult()));
}

Cannot set Scanner Capability because L_TwainStartCapsNeg returns error -84

I'm trying to use the Leadtools API version 21 for automatically scanning some documents and here is a sudo code of what I have done (it runs in a secondary thread and the unlock has been done in the main thread):
void CheckRetCode(int rc)
{
if (SUCCESS != rc)
{
L_TCHAR errMsg[1024];
memset(errMsg, 0, sizeof(errMsg));
L_GetFriendlyErrorMessage(rc, errMsg, 1024, L_FALSE);
throw TLeadException(errMsg, rc);
}
}
void OnThreadExecute(void)
{
HTWAINSESSION hSession = nullptr;
APPLICATIONDATA appData;
L_INT nRet;
L_TCHAR pszTwnSourceName[1024];
LTWAINSOURCE sInfo;
memset(&appData, 0, sizeof(APPLICATIONDATA));
appData.uStructSize = sizeof(APPLICATIONDATA);
appData.hWnd = hWnd;// hWnd is valid handle of my main window
appData.uLanguage = TWLG_ENGLISH_USA;
appData.uCountry = TWCY_USA;
wcscpy(appData.szManufacturerName, L"MyCompanyName");
wcscpy(appData.szAppProductFamily, L"MyProductName");
wcscpy(appData.szAppName, appData.szAppProductFamily);
wcscpy(appData.szVersionInfo, L"Version 0.1.0.1");
nRet = L_TwainInitSession2(&hSession, &appData, LTWAIN_INIT_MULTI_THREADED);
CheckRetCode(nRet);// the exception gets catched elsewhere but no error reported here
memset(pszTwnSourceName, 0, sizeof(pszTwnSourceName));
wcscpy(pszTwnSourceName, L"EPSON Artisan837/PX830"); // the name of the scanner is verifyed
sInfo.uStructSize = sizeof(LTWAINSOURCE);
sInfo.pszTwainSourceName = pszTwnSourceName;
CheckRetCode(L_TwainSelectSource(hSession, &sInfo)); // No error reported here
CheckRetCode(L_TwainStartCapsNeg(hSession)); // in here I get the return value -84 which is reported as "TWAIN DS or DSM reported error, app shouldn't (no need for your app to report the error)."
// the rest of the code but we cannot get there since above code reports error
}
Can anyone tell me what I'm doing wrong? Is there a step that I'm missing here?
EditThe function L_TwainSelectSource() make no effort to make sure the supplied source is valid and does not even return an error. As result, if you set the selected source to a garbage name, it will act as if it accepted it. From that point on if you try to Get/Set anything or try to acquire an image, every function returns -84.
Thank you
Sam
To test your code, I put the main window’s handle in a global variable:
globalhWnd = hWnd;
And modified your function to use that handle like this:
void OnThreadExecute(void *)
{
...
appData.hWnd = globalhWnd; // hWnd is valid handle of my main window
...
}
Then created a thread for it from the main program like this:
globalhWnd = hWnd;
_beginthread(OnThreadExecute, 0, 0);
I tried this with 5 different Twain sources: 2 virtual and 3 physical scanners (one of them an old Epson). All 5 drivers returned SUCCESS when calling L_TwainStartCapsNeg() from within the thread.
Two possibilities come to mind:
The problem might be caused by something else in your code other than the thread function.
Or the problem could be specific to your Twain driver.
To rule out the first possibility, I suggest creating a small test project that only creates a similar thread and does nothing else and trying it with different scanners. If it causes the same problem with all scanners, send that test project (not your full application) to support#leadtools.com and our support engineers with test it for you.
If the problem only happens with a specific Twain driver, try contacting the scanner’s vendor to see if they have an updated driver.

Why WNetAddConnection2 still returns 1219 after successfully calling WNetCancelConnection2?

I wrote some code to connect with some share on a remote server. If WNetAddConnection2 returns ERROR_SESSION_CREDENTIAL_CONFLICT (1219), I will first cancel the connection by WNetCancelConnection2 (return NO_ERROR). And then reconnect. But WNetAddConnection2 still returns 1219.
Why this and how to fix it?
Here's my code
BOOL ADDirectorySearch::IPCConnect(CString strServerName, CString strDomainName, CString strUserName, CString strPassWord)
{
CString strServerNameWithSlash = _T("\\\\") + strServerName; //actually is \\klbnt
CString strFullUserName = strDomainName + _T("\\") + strUserName; //is domaintest\administrator
_bstr_t bstrServerNameWithSlash = strServerNameWithSlash;
_bstr_t bstrFullUserName = strFullUserName;
_bstr_t bstrPassWord = strPassWord;
DWORD dwResult;
NETRESOURCEW netResource;
memset(&netResource, 0, sizeof(netResource));
netResource.dwScope = RESOURCE_GLOBALNET;
netResource.dwType = RESOURCETYPE_DISK;
netResource.dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
netResource.dwUsage = RESOURCEUSAGE_CONNECTABLE;
netResource.lpProvider = L"";
netResource.lpRemoteName = bstrServerNameWithSlash;//Remote IP like:\\192.168.1.11
dwResult = WNetAddConnection2W(&netResource, bstrPassWord, bstrFullUserName, CONNECT_INTERACTIVE);
if (dwResult == ERROR_SESSION_CREDENTIAL_CONFLICT)
{
dwResult = WNetCancelConnection2W(bstrServerNameWithSlash, CONNECT_UPDATE_PROFILE, TRUE);
if (dwResult == NO_ERROR)
{
dwResult = WNetAddConnection2W(&netResource, bstrPassWord, bstrFullUserName, CONNECT_INTERACTIVE);
}
else
{
//MyMessageBox_Error(_T("IPCConnect Error."), _T("Error"));
return FALSE;
}
}
if (dwResult == NO_ERROR)
{
return TRUE;
}
else
{
//MyMessageBox_Error(_T("IPCConnect Error."), _T("Error"));
return FALSE;
}
}
FYI: After typing "net use" in cmd, I got this, I feel there's something with error:
Status Local Remote Network
-------------------------------------------------------------------------------
OK \\klbnt\NRDC1001 Microsoft Windows Network
The command completed successfully.
I was just having this problem now, and basically it seemed that it was due to another process still having file open, even though I specified "true" as the last parameter of WNetCancelConnection2() to force close the connection. Once I shut-down that other process, I was able to use successfully switch between credentials connecting and re-connecting to the same share. This is on Windows 2012 (64-bit), and the share was local (referenced by the machinename).
BUT...it's still a problem if you want to connect to different shares on the same machine. If I try to connect to \\mymachine\share1 as user1 then to \\mymachine\share2 as user2, I get the 1219 error (even if it's in a completely different process). I have to explicitly call WNetCancelConnnection on \\mymachine\share1 before I can connect to share2, which means at the point you connect to a share on a particular machine, you may have to first enumerate existing connections and close each one.
Rather frustrating, and I can't understand the design principle here. It seems the flags to create temporary connections etc. have no effect on this behaviour either. Really what I want to be able to do is say "for this thread, connect to this share on this machine and as this user, such that all attempts to access files on the share are done with that user's credentials". That way what other processes/threads are doing can't cause issues with the current one.

Scriptable plugin: Error calling method on NPObject

I am getting a JavaScript error: Error calling method on NPObject when calling a method in my NPAPI plugin in Chrome & Firefox on XP. Running the same code on Windows 7 with the same browsers was successful.
I have built a Scriptable plugin using the NPAPI, so far I can debug into the Invoke method of my scriptable object. But I don't believe I have any control after it's finished.
Does anyone have any ideas? Is this an issue only in Windows XP?
bool MY_ScriptableObject::Invoke(NPObject* npobj,
NPIdentifier name,
const NPVariant* args,
uint32_t argCount,
NPVariant* result)
{
bool rc = true;
char* wptr = NULL;
rc = false;
wptr = NULL;
if (name == NPN_GetStringIdentifier("getVersion"))
{
wptr = (NPUTF8*)NPN_MemAlloc(strlen("version:1.0.1") + 1); //Should be freed by browser
if (wptr != NULL)
{
rc = true;
memset(wptr,
0x00,
strlen("version:1.0.1")+1);
memcpy(wptr,
"version:1.0.1",
strlen("version:1.0.1"));
STRINGZ_TO_NPVARIANT(wptr,
*result);
}
}
return (rc);
}
Here is the HTML function that I am executing:
function Version()
{
var plugin = document.getElementById("plugin");
if (plugin == undefined)
{
alert("plugin failed");
return;
}
var text = plugin.getVersion(); //Error happens at this line
alert(text);
}
The (sarcasm)awesome(/sarcasm) thing about NPAPI in current versions of the browsers is that if anything goes wrong with the call you automatically get that error message, even if the plugin has otherwise tried to set an exception with NPN_SetException.
My first guess would be that you compiled your code targeting a later version of windows than windows XP; I'm not sure if that would produce this issue or not. I have never seen the issue you're describing, and I've got plugins running on xp, vista, and 7 with no trouble. You might also try playing with a FireBreath plugin and see if the issue occurs there or not.
I would recommend that you attach with a debugger and set some breakpoints. Start in NPN_GetValue and make sure that it's instantiating your NPObject, then set breakpoints in your NPObject's HasMethod and Invoke methods and see what is hit. Likely something in there will show you what is actually happening, or at least tell you what code is or isn't getting hit.

Why does GetLastError() return different codes during debug vs "normal" execution?

try
{
pConnect = sess->GetFtpConnection(ftpArgs.host, ftpArgs.userName, ftpArgs.password, port, FALSE );
}
catch (CInternetException* pEx)
{
loginErrCode = GetLastError();
printf("loginErrCode: %d\n", loginErrCode);
if(loginErrCode == 12013)
{
printf("Incorrect user name!\n");
exit(0);
}
else if(loginErrCode == 12014)
{
printf("Incorrect password!\n");
exit(0);
}
else if(loginErrCode == 12007)
{
printf("Incorrect server name!\n");
exit(0);
}
else //display all other errors
{
TCHAR sz[1024];
pEx->GetErrorMessage(sz, 1024);
printf("ERROR! %s\n, sz);
pEx->Delete();
exit(0);
}
When this code runs from visual studio with an intentional incorrect user name, GetLastError() returns 12014 (expected).
However, when running the same code from the command line (with the same exact incorrect username) GetLastError() returns 2? (the GetErrorMessage() does return incorrect password)
I do not understand what the difference is.
In addition, I ran the program from the command line while attaching the process to it in visual studio, to debug. I received 12014.
Whenever the debugger is involved, I get 12014. When I run the executable "normally" with the same parameters, I get 2.
Are the WinInet error codes not being found when I run the program outside of the debugger? Do I need to compile the program differently?
Any help is appreciated. Thank You.
My memory is a little hazy in this regard, but what happens if you use the m_dwError field of the CInternetException object instead of calling GetLastError()?
My guess is that something is causing the error code to be reset between when the actual error and your call to GetLastError(). I don't know why this happens when running outside the debugger but not inside the debugger. However, MFC caches the error code that caused the exception in the thrown object, so you should be able to use the cached value regardless of what API calls have happened since the exception was thrown.
GetErrorMessage() returns the correct error string because it uses this m_dwError field, rather than calling GetLastError().