Add tagging support & warn on unsupported bitrate

This commit is contained in:
_ 2020-12-02 15:21:28 -07:00
parent 4cd1139b8b
commit aa323ee11b
6 changed files with 272 additions and 70 deletions

View File

@ -34,7 +34,13 @@ Spotify version:
* ~~1.1.25~~
## Building
This project uses C++14 on Visual Studio 2019.
This project uses C++14 on Visual Studio 2019 with vcpkg:
1. [Install vcpkg](https://github.com/microsoft/vcpkg#quick-start-windows) if you haven't done so already
2. Install TagLib dependency with vcpkg (`vcpkg install taglib:x86-windows-static-md`)
3. Clone the repository (`git clone https://gitlab.com/fuck-capitalism/spotifykeydumper.git`)
4. Open the solution 'SpotifyKeyDumper.sln' in Visual Studio 2019 and set the configuration to Release and the
platform to x86 and then select Build -> Build Solution **or** run MSBuild (requires Visual Studio 2019 installation):
`msbuild /p:Configuration="Release" /p:Platform="x86"`.
*If you want a specific version, create an issue.*

View File

@ -37,10 +37,14 @@ uriHandler_v47 uriHandler_v47_hook = nullptr;
typedef bool (__cdecl* isValidUrl_v25)(int* urlThingArg, int a2);
isValidUrl_v25 isValidUrl_v25_hook = nullptr;
typedef int (__cdecl* getBitrate_v25)(int quality);
getBitrate_v25 getBitrate_v25_hook = nullptr;
std::string authToken = std::string();
std::string keyStr = std::string();
std::string uriStr = std::string();
std::string fileId = std::string();
int quality = 4;
__int64 newPosition = 0;
bool signalled = false;
@ -54,7 +58,7 @@ void TryDownload()
{
signalled = false;
lastUri = uriStr;
std::thread t2(Utils::DownloadSong, std::string(fileId), uriStr, keyStr, authToken);
std::thread t2(Utils::DownloadSong, std::string(fileId), uriStr, keyStr, authToken, quality);
t2.detach();
}
}
@ -63,7 +67,7 @@ int __cdecl keyToLE_hook_v25(unsigned int* dest, int* key, int bits)
{
if (bits == 128)
{
BYTE keyBuffer[16];
BYTE keyBuffer[16] = {};
BYTE* keyBufPtr = keyBuffer;
memcpy(keyBufPtr, key, 16);
@ -88,7 +92,7 @@ int __cdecl keyToLE_hook_v28(unsigned int* dest, int* key, int bits, bool isEnco
{
// key is encoded with some sort of algorithm; decode it here
unsigned int keyDecoded[4];
unsigned int keyDecoded[4] = {};
unsigned int uVar1;
unsigned int keyPtr;
unsigned int uVar3;
@ -118,7 +122,7 @@ int __cdecl keyToLE_hook_v28(unsigned int* dest, int* key, int bits, bool isEnco
}
// Copy key bytes to new buffer
char keyBuffer[16];
char keyBuffer[16] = {};
char* keyBufPtr = keyBuffer;
memcpy(keyBufPtr, decodedKeyPtr, 16);
@ -331,6 +335,57 @@ bool __cdecl isValidUrl_hook_v25(int* urlThingArg, int arg_4)
return isValidUrl_v25_hook(urlThingArg, arg_4);
}
// TODO: Fix downloads on low quality and very high quality to get rid of this
bool hasAlertedQuality = false;
int __cdecl getBitrate_hook_v25(int quality)
{
// Force quality to be normal or high quality (since other modes do not reliably work)
// (no, forcing it to very high on a free account does not work)
/*switch (quality)
{
case 0: // Automatic
quality = 3;
break;
case 1: // Low
quality = 2;
break;
case 4: // Very high
quality = 3;
break;
}*/
std::string qualityStr = "?";
switch (quality)
{
case 0:
qualityStr = "Automatic";
break;
case 1:
qualityStr = "Low";
break;
case 2:
qualityStr = "Normal";
break;
case 3:
qualityStr = "High";
break;
case 4:
qualityStr = "Very High";
break;
}
if (!hasAlertedQuality && quality != 2 && quality != 3)
{
std::cout << "Warning! You have selected an unsupported quality (" + qualityStr + "); select Normal or High"
" quality in the Spotify settings for proper compatibility." << std::endl;
hasAlertedQuality = true;
}
::quality = quality;
return getBitrate_v25_hook(quality);
}
char* GetKeyFuncAddrV26()
{
@ -407,7 +462,8 @@ void Hooks::Init()
fileIdWriter_v45_hook = (fileIdWriter_v45)Utils::TrampHook32((char*)0x00CBB560, (char*)fileIdWriter_hook_v45,
5);
signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00B095A0, (char*)signalEmitter_hook_v45,
5);
5); // 00E48410
getBitrate_v25_hook = (getBitrate_v25)Utils::TrampHook32((char*)0x00E48410, (char*)getBitrate_hook_v25, 6);
break;
case 46:
keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010C2FB0, (char*)keyToLE_hook_v28, 6);
@ -417,6 +473,7 @@ void Hooks::Init()
5);
signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00B02270, (char*)signalEmitter_hook_v45,
5);
getBitrate_v25_hook = (getBitrate_v25)Utils::TrampHook32((char*)0x00E3C780, (char*)getBitrate_hook_v25, 6);
break;
case 47:
keyBuffer_v47 = new char[16]; // 128 bits = 16 bytes
@ -428,6 +485,7 @@ void Hooks::Init()
5);
signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00AFBB50, (char*)signalEmitter_hook_v45,
5);
getBitrate_v25_hook = (getBitrate_v25)Utils::TrampHook32((char*)0x00E3E460, (char*)getBitrate_hook_v25, 6);
//startUpdate_v47_hook = (startUpdate_v47)Utils::TrampHook32((char*)0x009FB530, (char*)startUpdate_hook_v47, 5);
//uriHandler_v47_hook = (uriHandler_v47)Utils::TrampHook32((char*)0x010A39E0, (char*)uriHandler_hook_v47, 5);
//isValidUrl_v25_hook = (isValidUrl_v25)Utils::TrampHook32((char*)0x0105CD80, (char*)isValidUrl_hook_v25, 6);

View File

@ -16,6 +16,7 @@
<ProjectGuid>{e6674e55-3cf4-45e4-a22f-7e257bcea583}</ProjectGuid>
<RootNamespace>SpotifyKeyDumper</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static-md</VcpkgTriplet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@ -78,6 +79,7 @@
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<LanguageStandard>stdcpp14</LanguageStandard>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -85,6 +87,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -3,6 +3,8 @@
#include "tiny-AES-c/aes.h"
const uint8_t IV[] = { 0x72, 0xE0, 0x67, 0xFB, 0xDD, 0xCB, 0xCF, 0x77, 0xEB, 0xE8, 0xBC, 0x64, 0x3F, 0x63, 0x0D, 0x93 };
const std::string urlRegex = "https?:\\/\\/(?:www\.)?([-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})"
"\\b([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*)";
bool Utils::Detour32(char* src, char* dst, const intptr_t len)
{
@ -61,7 +63,7 @@ int Utils::GetSpotifyVersion()
LPBYTE lpVersionInfo = new BYTE[dwFVISize];
GetFileVersionInfo(lpszFilePath, 0, dwFVISize, lpVersionInfo);
UINT uLen;
VS_FIXEDFILEINFO* lpFfi;
VS_FIXEDFILEINFO* lpFfi = {};
VerQueryValue(lpVersionInfo, _T("\\"), (LPVOID*)&lpFfi, &uLen);
DWORD dwFileVersionMS = lpFfi->dwFileVersionMS;
DWORD dwFileVersionLS = lpFfi->dwFileVersionLS;
@ -115,30 +117,158 @@ std::wstring Utils::Utf8ToUtf16(const std::string& str)
return convertedString;
}
enum class FileType
{
OGG,
MP3
};
struct SongInfo
{
std::string title, artist, album, cover;
} songInfo;
FileType fileType{ FileType::OGG };
std::string title{ std::string() };
std::string artist{ std::string() };
std::string album{ std::string() };
std::string coverUrl{ std::string() };
std::string releaseType{ std::string() };
std::string releaseDate{ std::string() };
std::string isrc{ std::string() };
unsigned int year{ 0 };
unsigned int trackNum{ 1 };
unsigned int totalTracks{ 1 };
unsigned int discNum{ 0 };
bool isExplicit{ false };
};
void ClearSongInfo()
void TagSong(std::wstring songPath, SongInfo* songInfo)
{
songInfo.title = "";
songInfo.artist = "";
songInfo.album = "";
songInfo.cover = "";
TagLib::String fileName = songPath.c_str();
TagLib::String tTitle(songInfo->title, TagLib::String::Type::UTF8);
TagLib::String tArtist(songInfo->artist, TagLib::String::Type::UTF8);
TagLib::String tAlbum(songInfo->album, TagLib::String::Type::UTF8);
TagLib::String tReleaseType(songInfo->releaseType, TagLib::String::Type::UTF8);
TagLib::String tReleaseDate(songInfo->releaseDate, TagLib::String::Type::UTF8);
TagLib::String tIsrc(songInfo->isrc, TagLib::String::Type::UTF8);
TagLib::String tTotalTracks(std::to_string(songInfo->totalTracks), TagLib::String::Type::UTF8);
TagLib::String tDiscNum(std::to_string(songInfo->discNum), TagLib::String::Type::UTF8);
//TagLib::String tIsExplicit(std::to_string(songInfo->isExplicit), TagLib::String::Type::UTF8);
// Parse episode URL to separate host and path
std::string coverUrlHost, coverUrlPath;
try
{
std::regex re(urlRegex);
std::smatch match;
if (std::regex_search(songInfo->coverUrl, match, re) && match.size() > 1)
{
coverUrlHost = match.str(1);
coverUrlPath = match.str(2);
}
else
{
std::cout << "Error: Cover art URL is not valid!" << std::endl;
return;
}
}
catch (std::regex_error& e)
{
// Syntax error in the regular expression
std::cout << "Error: Invalid regex!" << std::endl;
return;
}
std::string coverArtData = Utils::DownloadSpotifyUrl(coverUrlHost, coverUrlPath, "");
TagLib::ByteVector tCoverArtData(reinterpret_cast<const char*>(&coverArtData[0]), coverArtData.length());
switch (songInfo->fileType)
{
case FileType::OGG:
{
TagLib::Ogg::Vorbis::File audioFile(songPath.c_str());
TagLib::Ogg::XiphComment* tag = audioFile.tag();
TagLib::FLAC::Picture* coverArt = new TagLib::FLAC::Picture();
coverArt->setType((TagLib::FLAC::Picture::Type)0x03); // Front Cover
coverArt->setMimeType("image/jpeg");
coverArt->setDescription("Front Cover");
coverArt->setData(tCoverArtData);
tag->addPicture(coverArt);
tag->setTitle(tTitle);
tag->setArtist(tArtist);
tag->setAlbum(tAlbum);
tag->setTrack(songInfo->trackNum);
tag->setYear(songInfo->year);
tag->addField("DATE", tReleaseDate);
tag->addField("DISCNUMBER", tDiscNum);
tag->addField("ISRC", tIsrc);
tag->addField("SOURCEMEDIA", "Digital Media");
tag->addField("RELEASETYPE", tReleaseType);
tag->addField("TOTALTRACKS", tTotalTracks);
audioFile.save();
break;
}
case FileType::MP3:
{
TagLib::MPEG::File audioFile(songPath.c_str());
TagLib::ID3v2::Tag* tag = audioFile.ID3v2Tag(true);
tag->setTitle(tTitle);
tag->setArtist(tArtist);
tag->setAlbum(tAlbum);
tag->setTrack(songInfo->trackNum);
tag->setYear(songInfo->year);
TagLib::ID3v2::AttachedPictureFrame* frame = new TagLib::ID3v2::AttachedPictureFrame;
if (frame->picture().size() < tCoverArtData.size())
{
frame->setMimeType("image/jpeg");
frame->setPicture(tCoverArtData);
tag->addFrame(frame);
}
audioFile.save();
break;
}
}
}
int GetUrlNum(int quality)
{
switch (quality)
{
case 0: // Automatic
return 1; // Set to high quality
case 1: // Low
return 8;
case 2: // Normal
return 0;
case 3: // High
return 1;
case 4: // Very high
return 2;
default:
return 1; // Shouldn't happen; set to high quality
}
}
static const std::string songRegex =
"https?:\\/\\/(?:www\.)?([-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})"
"\\b([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*)";
static const std::string albumSearchPattern = "\x68\x65\x69\x67\x68\x74\x22\x20\x3A\x20\x36\x34\x30";
static const std::wstring songDirRoot = L"Downloads";
static std::wstring songDir = songDirRoot;
void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, std::string authToken)
void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, std::string authToken, int quality)
{
std::string downloadStr;
std::wstring songExtension = L".ogg";
SongInfo* songInfo = new SongInfo();
/*songInfo->title = "";
songInfo->artist = "";
songInfo->album = "";
songInfo->coverUrl = "";
songInfo->fileType = FileType::OGG;*/
if (fileId.empty() || uri.empty() || authToken.empty())
{
std::cout << "Could not download song or episode: missing fileId, trackUri, or authToken!" << std::endl;
@ -157,8 +287,9 @@ void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, s
}
// Get storage resolve from Spotify
std::string urlNum = std::to_string(GetUrlNum(quality));
std::string srStr = DownloadSpotifyUrl("spclient.wg.spotify.com",
"/storage-resolve/files/audio/interactive_prefetch/" + fileId + "?product=0", authToken);
"/storage-resolve/v2/files/audio/interactive/" + urlNum + "/" + fileId + "?product=0", authToken);
if (srStr.length() <= 5)
{
@ -177,7 +308,7 @@ void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, s
std::string songPath;
try
{
std::regex re(songRegex);
std::regex re(urlRegex);
std::smatch match;
if (std::regex_search(srStr, match, re) && match.size() > 1)
{
@ -206,11 +337,16 @@ void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, s
return;
}
if (downloadStr.substr(0, 6).compare("<HTML>") == 0)
if (downloadStr.compare(0, 6, "<HTML>") == 0)
{
std::cout << "Error: " + downloadStr << std::endl;
return;
}
else if (downloadStr.compare(0, 5, "Error") == 0)
{
std::cout << downloadStr << std::endl;
return;
}
// Decrypt encrypted song data with Tiny AES in C
struct AES_ctx ctx;
@ -231,10 +367,21 @@ void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, s
std::string metadata = DownloadSpotifyUrl("api.spotify.com", "/v1/tracks/"
+ uri.substr(uri.find("spotify:track:") + 14), authToken);
songInfo.title = strtok((char*)(metadata.substr(metadata.find("is_local") + 55)).c_str(), "\"");
songInfo.artist = strtok((char*)(metadata.substr(metadata.find("name") + 9)).c_str(), "\"");
songInfo.album = strtok((char*)(metadata.substr(metadata.find(albumSearchPattern) + 404)).c_str(), "\"");
songInfo.cover = strtok((char*)(metadata.substr(metadata.find("height") + 30)).c_str(), "\"");
songInfo->title = strtok((char*)(metadata.substr(metadata.find("is_local\" :") + 55)).c_str(), "\"");
songInfo->artist = strtok((char*)(metadata.substr(metadata.find("name\" :") + 9)).c_str(), "\"");
songInfo->album = strtok((char*)(metadata.substr(metadata.find(albumSearchPattern) + 404)).c_str(), "\"");
songInfo->coverUrl = strtok((char*)(metadata.substr(metadata.find("height\" :") + 30)).c_str(), "\"");
songInfo->releaseType = strtok((char*)(metadata.substr(metadata.find("album_type\" :") + 15)).c_str(), "\"");
songInfo->releaseDate = strtok((char*)(metadata.substr(metadata.find("release_date\" :") + 17)).c_str(), "\"");
songInfo->isrc = strtok((char*)(metadata.substr(metadata.find("isrc\" :") + 9)).c_str(), "\"");
songInfo->trackNum = std::stoi(strtok((char*)(metadata.substr(metadata.find("track_number\" :") + 16)).c_str(),
","));
songInfo->totalTracks = std::stoi(strtok((char*)(metadata.substr(metadata.find("total_tracks\" :")
+ 16)).c_str(), ","));
songInfo->discNum = std::stoi(strtok((char*)(metadata.substr(metadata.find("disc_number\" :") + 15)).c_str(),
","));
songInfo->isExplicit = strtok((char*)(metadata.substr(metadata.find("explicit\" :") + 12)).c_str(), ",");
songInfo->fileType = FileType::OGG;
songExtension = L".ogg";
}
@ -249,7 +396,7 @@ void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, s
// Parse episode URL to separate host and path
try
{
std::regex re(songRegex);
std::regex re(urlRegex);
std::smatch match;
if (std::regex_search(episodeUrl, match, re) && match.size() > 1)
{
@ -284,46 +431,50 @@ void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, s
std::string metadata = DownloadSpotifyUrl("api.spotify.com", "/v1/episodes/"
+ uri.substr(uri.find("spotify:episode:") + 16), authToken);
songInfo.title = strtok((char*)(metadata.substr(metadata.find("name") + 9)).c_str(), "\"");
songInfo.artist = strtok((char*)(metadata.substr(metadata.find("publisher") + 14)).c_str(), "\"");
songInfo.album = strtok((char*)(metadata.substr(metadata.find("media_type") + 37)).c_str(), "\"");
songInfo.cover = strtok((char*)(metadata.substr(metadata.find("height") + 28)).c_str(), "\"");
songInfo->title = strtok((char*)(metadata.substr(metadata.find("name") + 9)).c_str(), "\"");
songInfo->artist = strtok((char*)(metadata.substr(metadata.find("publisher") + 14)).c_str(), "\"");
songInfo->album = strtok((char*)(metadata.substr(metadata.find("media_type") + 37)).c_str(), "\"");
songInfo->coverUrl = strtok((char*)(metadata.substr(metadata.find("height") + 28)).c_str(), "\"");
songInfo->fileType = FileType::MP3;
songExtension = L".mp3";
}
if (songInfo.title.empty() || songInfo.artist.empty() || songInfo.album.empty())
if (songInfo->title.empty() || songInfo->artist.empty() || songInfo->album.empty())
{
std::cout << "Error: Invalid title/artist/album name!" << std::endl;
delete songInfo;
return;
}
std::wstring tempDirArtist = FixPathStr(Utf8ToUtf16(songInfo.artist));
std::wstring tempDirArtist = FixPathStr(Utf8ToUtf16(songInfo->artist));
songDir = songDirRoot;
if (!CreateDirectoryW(songDir.c_str(), NULL) && ERROR_ALREADY_EXISTS != GetLastError())
std::cout << "Couldn't create main downloads directory!" << std::endl;
if (CreateDirectoryW(std::wstring(songDir + L"\\" + tempDirArtist).c_str(), NULL)
else if (CreateDirectoryW(std::wstring(songDir + L"\\" + tempDirArtist).c_str(), NULL)
|| ERROR_ALREADY_EXISTS == GetLastError())
{
std::wstring tempDirAlbum = FixPathStr(Utf8ToUtf16(songInfo.album));
std::wstring tempDirAlbum = FixPathStr(Utf8ToUtf16(songInfo->album));
if (CreateDirectoryW(std::wstring(songDir + L"\\" + tempDirArtist + std::wstring(L"\\")
+ tempDirAlbum).c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError())
{
songDir += L"\\" + tempDirArtist + std::wstring(L"\\") + tempDirAlbum;
std::wstring tempDirSong = FixPathStr(Utf8ToUtf16(songInfo->title));
std::wstring tempDirSong = FixPathStr(Utf8ToUtf16(songInfo.title));
songDir += L"\\" + tempDirArtist + std::wstring(L"\\") + tempDirAlbum + L".\\" + tempDirArtist + L" - "
+ tempDirSong + songExtension;
std::ofstream songFileOut(songDir + L".\\" + tempDirArtist + L" - " + tempDirSong + songExtension,
std::ios_base::binary);
std::ofstream songFileOut(songDir, std::ios_base::binary);
songFileOut.write(downloadStr.c_str(), downloadStr.size());
songFileOut.close();
std::cout << "Finished downloading: " << songInfo.artist << " - \"" << songInfo.title << "\"!" << std::endl;
TagSong(songDir, songInfo);
ClearSongInfo();
std::cout << "Finished downloading: " << songInfo->artist << " - \"" << songInfo->title << "\"!"
<< std::endl;
delete songInfo;
return;
}
else
@ -335,29 +486,6 @@ void Utils::DownloadSong(std::string fileId, std::string uri, std::string key, s
{
std::cout << "Couldn't create artist directory!" << std::endl;
}
std::cout << "Could not finish downloading song!" << std::endl;
ClearSongInfo();
}
std::string GetLastErrorAsString()
{
//Get the error message, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string(); //No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
//Free the buffer.
LocalFree(messageBuffer);
return message;
}
std::string Utils::DownloadSpotifyUrl(std::string host, std::string path, std::string authToken)
@ -373,26 +501,26 @@ std::string Utils::DownloadSpotifyUrl(std::string host, std::string path, std::s
hSession = InternetOpenA(userAgent.c_str(), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hSession == NULL)
return "Error: Could not initialize request!";
return "Error: Could not initialize request: " + GetLastError();
hConnect = InternetConnectA(hSession, host.c_str(), 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0,
NULL);
if (hConnect == NULL)
return "Error: Could not create connect!";
return "Error: Could not create connect: " + GetLastError();
hRequest = HttpOpenRequestA(hConnect, "GET", path.c_str(), NULL, NULL, NULL, INTERNET_FLAG_NO_AUTH, 0);
if (hRequest == NULL)
return "Error: Could not create open request!";
return "Error: Could not create open request: " + GetLastError();
HttpAddRequestHeadersA(hRequest, authHeader.c_str(), -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
bRequestSent = HttpSendRequestA(hRequest, NULL, 0, NULL, 0);
if (!bRequestSent)
return "Error: Could not send request!";
return "Error: Could not send request: " + GetLastError();
char tmpBuffer[bufferSize];
char tmpBuffer[bufferSize] = {};
BOOL canRead = true;
DWORD bytesRead = -1;

View File

@ -9,7 +9,7 @@ public:
static std::string HexString(BYTE* data, int len);
static std::wstring FixPathStr(std::wstring str);
static std::wstring Utf8ToUtf16(const std::string& str);
static void DownloadSong(std::string fileId, std::string fileUri, std::string key, std::string authToken);
static void DownloadSong(std::string fileId, std::string uri, std::string key, std::string authToken, int quality);
static std::string DownloadSpotifyUrl(std::string host, std::string path, std::string authToken);
static bool BadPtr(void* ptr);
};

View File

@ -25,4 +25,11 @@
#include <vector>
#include <WinInet.h>
// TagLib
#define TAGLIB_STATIC
#include <taglib/mpegfile.h>
#include <taglib/attachedpictureframe.h>
#include <taglib/id3v2tag.h>
#include <taglib/vorbisfile.h>
#endif //PCH_H