feat: album cover support copy-paste from the old SP codebase

This commit is contained in:
Gary Wang 2020-04-09 18:12:35 +08:00
parent 2e543bfe0a
commit ce885bee3f
7 changed files with 773 additions and 2 deletions

View File

@ -27,6 +27,10 @@ add_executable(${EXE_NAME}
playlistmodel.cpp playlistmodel.cpp
mainwindow.ui mainwindow.ui
resources.qrc resources.qrc
# 3rd party code
FlacPic.h
ID3v2Pic.h
) )
target_link_libraries(${EXE_NAME} PRIVATE Qt5::Widgets Qt5::Multimedia) target_link_libraries(${EXE_NAME} PRIVATE Qt5::Widgets Qt5::Multimedia)

264
FlacPic.h Executable file
View File

@ -0,0 +1,264 @@
/*
FLAC标签图片提取库 Ver 1.0
FLAC文件中稳定便
BMPJPEGPNGGIF图片格式
使ID3v2版本相同
ShadowPower 2014/8/1
*/
#ifndef _ShadowPower_FLACPIC___
#define _ShadowPower_FLACPIC___
#define _CRT_SECURE_NO_WARNINGS
#ifndef NULL
#define NULL 0
#endif
#include <cstdio>
#include <cstdlib>
#include <memory.h>
#include <cstring>
typedef unsigned char byte;
using namespace std;
namespace spFLAC {
//Flac元数据块头部结构体定义
struct FlacMetadataBlockHeader
{
byte flag; //标志位高1位是否为最后一个数据块低7位数据块类型
byte length[3]; //数据块长度,不含数据块头部
};
byte *pPicData = 0; //指向图片数据的指针
int picLength = 0; //存放图片数据长度
char picFormat[4] = {}; //存放图片数据的格式(扩展名)
//检测图片格式参数1数据返回值是否成功不是图片则失败
bool verificationPictureFormat(char *data)
{
//支持格式JPEG/PNG/BMP/GIF
byte jpeg[2] = { 0xff, 0xd8 };
byte png[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
byte gif[6] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
byte gif2[6] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
byte bmp[2] = { 0x42, 0x4d };
memset(&picFormat, 0, 4);
if (memcmp(data, &jpeg, 2) == 0)
{
strcpy(picFormat, "jpg");
}
else if (memcmp(data, &png, 8) == 0)
{
strcpy(picFormat, "png");
}
else if (memcmp(data, &gif, 6) == 0 || memcmp(data, &gif2, 6) == 0)
{
strcpy(picFormat, "gif");
}
else if (memcmp(data, &bmp, 2) == 0)
{
strcpy(picFormat, "bmp");
}
else
{
return false;
}
return true;
}
//安全释放内存
void freePictureData()
{
if (pPicData)
{
delete pPicData;
}
pPicData = 0;
picLength = 0;
memset(&picFormat, 0, 4);
}
//将图片提取到内存参数1文件路径成功返回true
bool loadPictureData(const char *inFilePath)
{
freePictureData();
FILE *fp = NULL;
fp = fopen(inFilePath, "rb");
if (!fp) //如果打开失败
{
fp = NULL;
return false;
}
fseek(fp, 0, SEEK_SET); //设文件流指针到文件头部
byte magic[4] = {}; //存放校验数据
memset(&magic, 0, 4);
fread(&magic, 4, 1, fp); //读入校验数据
byte fLaC[4] = { 0x66, 0x4c, 0x61, 0x43 };
if (memcmp(&magic, &fLaC, 4) == 0)
{
//数据校验正确文件类型为Flac
FlacMetadataBlockHeader fmbh; //创建Flac元数据块头部结构体
memset(&fmbh, 0, 4); //清空内存
fread(&fmbh, 4, 1, fp); //读入头部数据
//计算数据块长度,不含头部
int blockLength = fmbh.length[0] * 0x10000 + fmbh.length[1] * 0x100 + fmbh.length[2];
int loopCount = 0; //循环计数,防死
while ((fmbh.flag & 0x7f) != 6)
{
//如果数据类型不是图片,此处循环执行
loopCount++;
if (loopCount > 40)
{
//循环40次没有遇到末尾就直接停止
fclose(fp);
fp = NULL;
return false; //可能文件不正常
}
fseek(fp, blockLength, SEEK_CUR); //跳过数据块
if ((fmbh.flag & 0x80) == 0x80)
{
//已经是最后一个数据块了,仍然不是图片
fclose(fp);
fp = NULL;
return false; //没有找到图片数据
}
//取得下一数据块头部
memset(&fmbh, 0, 4); //清空内存
fread(&fmbh, 4, 1, fp); //读入头部数据
blockLength = fmbh.length[0] * 0x10000 + fmbh.length[1] * 0x100 + fmbh.length[2];//计算数据块长度
}
//此时已到图片数据块
int nonPicDataLength = 0; //非图片数据长度
fseek(fp, 4, SEEK_CUR); //信仰之跃
nonPicDataLength += 4;
char nextJumpLength[4]; //下次要跳的长度
fread(&nextJumpLength, 4, 1, fp); //读取安全跳跃距离
nonPicDataLength += 4;
int jumpLength = nextJumpLength[0] * 0x1000000 + nextJumpLength[1] * 0x10000 + nextJumpLength[2] * 0x100 + nextJumpLength[3];//计算数据块长度
fseek(fp, jumpLength, SEEK_CUR); //Let's Jump!!
nonPicDataLength += jumpLength;
fread(&nextJumpLength, 4, 1, fp);
nonPicDataLength += 4;
jumpLength = nextJumpLength[0] * 0x1000000 + nextJumpLength[1] * 0x10000 + nextJumpLength[2] * 0x100 + nextJumpLength[3];
fseek(fp, jumpLength, SEEK_CUR); //Let's Jump too!!
nonPicDataLength += jumpLength;
fseek(fp, 20, SEEK_CUR); //信仰之跃
nonPicDataLength += 20;
//非主流情况检测+获得文件格式
char tempData[20] = {};
memset(tempData, 0, 20);
fread(&tempData, 8, 1, fp);
fseek(fp, -8, SEEK_CUR); //回到原位
//判断40次一位一位跳到文件头
bool ok = false; //是否正确识别出文件头
for (int i = 0; i < 40; i++)
{
//校验文件头
if (verificationPictureFormat(tempData))
{
ok = true;
break;
}
else
{
//如果校验失败尝试继续向后校验
fseek(fp, 1, SEEK_CUR);
nonPicDataLength++;
fread(&tempData, 8, 1, fp);
fseek(fp, -8, SEEK_CUR);
}
}
if (!ok)
{
fclose(fp);
fp = NULL;
freePictureData();
return false; //无法识别的数据
}
//-----抵达图片数据区-----
picLength = blockLength - nonPicDataLength; //计算图片数据长度
pPicData = new byte[picLength]; //动态分配图片数据内存空间
memset(pPicData, 0, picLength); //清空图片数据内存
fread(pPicData, picLength, 1, fp); //得到图片数据
//------------------------
fclose(fp); //操作已完成,关闭文件。
}
else
{
//校验失败不是Flac
fclose(fp);
fp = NULL;
freePictureData();
return false;
}
return true;
}
//取得图片数据的长度
int getPictureLength()
{
return picLength;
}
//取得指向图片数据的指针
byte *getPictureDataPtr()
{
return pPicData;
}
//取得图片数据的扩展名(指针)
char *getPictureFormat()
{
return picFormat;
}
bool writePictureDataToFile(const char *outFilePath)
{
FILE *fp = NULL;
if (picLength > 0)
{
fp = fopen(outFilePath, "wb"); //打开目标文件
if (fp) //打开成功
{
fwrite(pPicData, picLength, 1, fp); //写入文件
fclose(fp); //关闭
return true;
}
else
{
return false; //文件打开失败
}
}
else
{
return false; //没有图像数据
}
}
//提取图片文件参数1输入文件参数2输出文件返回值是否成功
bool extractPicture(const char *inFilePath, const char *outFilePath)
{
if (loadPictureData(inFilePath)) //如果取得图片数据成功
{
if (writePictureDataToFile(outFilePath))
{
return true; //文件写出成功
}
else
{
return false; //文件写出失败
}
}
else
{
return false; //无图片数据
}
freePictureData();
}
}
#endif

430
ID3v2Pic.h Executable file
View File

@ -0,0 +1,430 @@
/*
ID3v2标签图片提取库 Ver 1.0
ID3v2所有版本
ID3v2标签中稳定便
BMPJPEGPNGGIF图片格式
ShadowPower 2014/8/1
*/
#ifndef _ShadowPower_ID3V2PIC___
#define _ShadowPower_ID3V2PIC___
#define _CRT_SECURE_NO_WARNINGS
#ifndef NULL
#define NULL 0
#endif
#include <cstdio>
#include <cstdlib>
#include <memory.h>
#include <cstring>
typedef unsigned char byte;
using namespace std;
namespace spID3 {
//ID3v2标签头部结构体定义
struct ID3V2Header
{
char identi[3];//ID3头部校验必须为“ID3”否则认为不存在ID3标签
byte major; //ID3版本号3是ID3v2.34是ID3v2.4,以此类推
byte revsion; //ID3副版本号此版本为00
byte flags; //标志位
byte size[4]; //标签大小不含标签头的10个字节
};
//ID3v2标签帧头部结构体定义
struct ID3V2FrameHeader
{
char FrameId[4];//标识符,用于描述此标签帧的内容类型
byte size[4]; //标签帧的大小不含标签头的10个字节
byte flags[2]; //标志位
};
struct ID3V22FrameHeader
{
char FrameId[3];//标识符,用于描述此标签帧的内容类型
byte size[3]; //标签帧的大小不含标签头的6个字节
};
byte *pPicData = 0; //指向图片数据的指针
int picLength = 0; //存放图片数据长度
char picFormat[4] = {}; //存放图片数据的格式(扩展名)
//检测图片格式参数1数据返回值是否成功不是图片则失败
bool verificationPictureFormat(char *data)
{
//支持格式JPEG/PNG/BMP/GIF
byte jpeg[2] = { 0xff, 0xd8 };
byte png[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
byte gif[6] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
byte gif2[6] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
byte bmp[2] = { 0x42, 0x4d };
memset(&picFormat, 0, 4);
if (memcmp(data, &jpeg, 2) == 0)
{
strcpy(picFormat, "jpg");
}
else if (memcmp(data, &png, 8) == 0)
{
strcpy(picFormat, "png");
}
else if (memcmp(data, &gif, 6) == 0 || memcmp(data, &gif2, 6) == 0)
{
strcpy(picFormat, "gif");
}
else if (memcmp(data, &bmp, 2) == 0)
{
strcpy(picFormat, "bmp");
}
else
{
return false;
}
return true;
}
//安全释放内存
void freePictureData()
{
if (pPicData)
{
delete pPicData;
}
pPicData = 0;
picLength = 0;
memset(&picFormat, 0, 4);
}
//将图片提取到内存参数1文件路径成功返回true
bool loadPictureData(const char *inFilePath)
{
freePictureData();
FILE *fp = NULL; //初始化文件指针,置空
fp = fopen(inFilePath, "rb"); //以只读&二进制方式打开文件
if (!fp) //如果打开失败
{
fp = NULL;
return false;
}
fseek(fp, 0, SEEK_SET); //设文件流指针到文件头部(印象中打开之后默认在尾部)
//读取
ID3V2Header id3v2h; //创建一个ID3v2标签头结构体
memset(&id3v2h, 0, 10); //内存填010个字节
fread(&id3v2h, 10, 1, fp); //把文件头部10个字节写入结构体内存
//文件头识别
if (strncmp(id3v2h.identi, "ID3", 3) != 0)
{
fclose(fp);
fp = NULL;
return false;//没有ID3标签
}
//能运行到这里应该已经成功打开文件了
//计算整个标签长度每个字节仅7位有效
int tagTotalLength = (id3v2h.size[0] & 0x7f) * 0x200000 + (id3v2h.size[1] & 0x7f) * 0x4000 + (id3v2h.size[2] & 0x7f) * 0x80 + (id3v2h.size[3] & 0x7f);
if (id3v2h.major == 3 || id3v2h.major == 4) //ID3v2.3 或 ID3v2.4
{
ID3V2FrameHeader id3v2fh; //创建一个ID3v2标签帧头结构体
memset(&id3v2fh, 0, 10);
bool hasExtendedHeader = ((id3v2h.flags >> 6 & 0x1) == 1);//是否有扩展头
if (hasExtendedHeader)
{
//如果有扩展头
byte extendedHeaderSize[4] = {};
memset(&extendedHeaderSize, 0, 4);
fread(&extendedHeaderSize, 4, 1, fp);
//取得扩展头大小(不含以上数据)
int extendedHeaderLength = extendedHeaderSize[0] * 0x1000000 + extendedHeaderSize[1] * 0x10000 + extendedHeaderSize[2] * 0x100 + extendedHeaderSize[3];
//跳过扩展头
fseek(fp, extendedHeaderLength, SEEK_CUR);
}
fread(&id3v2fh, 10, 1, fp); //将数据写到ID3V2FrameHeader结构体中
int curDataLength = 10; //存放当前已经读取的数据大小刚才已经读入10字节
while ((strncmp(id3v2fh.FrameId, "APIC", 4) != 0))//如果帧头没有APIC标识符则循环执行
{
if (curDataLength > tagTotalLength)
{
fclose(fp);
fp = NULL;
return false; //未发现图片数据
}
//计算帧数据长度
int frameLength = id3v2fh.size[0] * 0x1000000 + id3v2fh.size[1] * 0x10000 + id3v2fh.size[2] * 0x100 + id3v2fh.size[3];
fseek(fp, frameLength, SEEK_CUR); //向前跳跃到下一个帧头
memset(&id3v2fh, 0, 10); //清除帧头结构体数据
fread(&id3v2fh, 10, 1, fp); //重新读取数据
curDataLength += frameLength + 10; //记录当前所在的ID3标签位置以便退出循环
}
//计算一下当前图片帧的数据长度
int frameLength = id3v2fh.size[0] * 0x1000000 + id3v2fh.size[1] * 0x10000 + id3v2fh.size[2] * 0x100 + id3v2fh.size[3];
/*
ID3v2.3
<Header for 'Attached picture', ID: "APIC">
10
Text encoding $xx
MIME type <text string> $00
+ /0
Picture type $xx
Description <text string according to encoding> $00 (00)
+ /0
Picture data <binary data>
*/
int nonPicDataLength = 0; //非图片数据的长度
fseek(fp, 1, SEEK_CUR); //信仰之跃
nonPicDataLength++;
char tempData[20] = {}; //临时存放数据的空间
char mimeType[20] = {}; //图片类型
int mimeTypeLength = 0; //图片类型文本长度
fread(&tempData, 20, 1, fp);//取得一小段数据
fseek(fp, -20, SEEK_CUR); //回到原位
strcpy(mimeType, tempData); //复制出一个字符串
mimeTypeLength = strlen(mimeType) + 1; //测试字符串长度补上末尾00
fseek(fp, mimeTypeLength, SEEK_CUR); //跳到此数据之后
nonPicDataLength += mimeTypeLength; //记录长度
fseek(fp, 1, SEEK_CUR); //再一次信仰之跃
nonPicDataLength++;
int temp = 0; //记录当前字节数据的变量
fread(&temp, 1, 1, fp); //读取一个字节
nonPicDataLength++; //+1
while (temp) //循环到temp为0
{
fread(&temp, 1, 1, fp); //如果不是0继续读一字节的数据
nonPicDataLength++; //计数
}
//跳过了Description文本以及末尾的\0
//非主流情况检测
memset(tempData, 0, 20);
fread(&tempData, 8, 1, fp);
fseek(fp, -8, SEEK_CUR); //回到原位
//判断40次一位一位跳到文件头
bool ok = false; //是否正确识别出文件头
for (int i = 0; i < 40; i++)
{
//校验文件头
if (verificationPictureFormat(tempData))
{
ok = true;
break;
}
else
{
//如果校验失败尝试继续向后校验
fseek(fp, 1, SEEK_CUR);
nonPicDataLength++;
fread(&tempData, 8, 1, fp);
fseek(fp, -8, SEEK_CUR);
}
}
if (!ok)
{
fclose(fp);
fp = NULL;
freePictureData();
return false; //无法识别的数据
}
//-----真正的图片数据-----
picLength = frameLength - nonPicDataLength; //计算图片数据长度
pPicData = new byte[picLength]; //动态分配图片数据内存空间
memset(pPicData, 0, picLength); //清空图片数据内存
fread(pPicData, picLength, 1, fp); //得到图片数据
//------------------------
fclose(fp); //操作已完成,关闭文件。
}
else if (id3v2h.major == 2)
{
//ID3v2.2
ID3V22FrameHeader id3v2fh; //创建一个ID3v2.2标签帧头结构体
memset(&id3v2fh, 0, 6);
fread(&id3v2fh, 6, 1, fp); //将数据写到ID3V2.2FrameHeader结构体中
int curDataLength = 6; //存放当前已经读取的数据大小刚才已经读入6字节
while ((strncmp(id3v2fh.FrameId, "PIC", 3) != 0))//如果帧头没有PIC标识符则循环执行
{
if (curDataLength > tagTotalLength)
{
fclose(fp);
fp = NULL;
return false; //未发现图片数据
}
//计算帧数据长度
int frameLength = id3v2fh.size[0] * 0x10000 + id3v2fh.size[1] * 0x100 + id3v2fh.size[2];
fseek(fp, frameLength, SEEK_CUR); //向前跳跃到下一个帧头
memset(&id3v2fh, 0, 6); //清除帧头结构体数据
fread(&id3v2fh, 6, 1, fp); //重新读取数据
curDataLength += frameLength + 6; //记录当前所在的ID3标签位置以便退出循环
}
int frameLength = id3v2fh.size[0] * 0x10000 + id3v2fh.size[1] * 0x100 + id3v2fh.size[2]; //如果读到了图片帧,计算帧长
/*
Attached picture "PIC"
Frame size $xx xx xx
Text encoding $xx
Image format $xx xx xx
Picture type $xx
Description <textstring> $00 (00)
Picture data <binary data>
*/
int nonPicDataLength = 0; //非图片数据的长度
fseek(fp, 1, SEEK_CUR); //信仰之跃 Text encoding
nonPicDataLength++;
char imageType[4] = {};
memset(&imageType, 0, 4);
fread(&imageType, 3, 1, fp);//图像格式
nonPicDataLength += 3;
fseek(fp, 1, SEEK_CUR); //信仰之跃 Picture type
nonPicDataLength++;
int temp = 0; //记录当前字节数据的变量
fread(&temp, 1, 1, fp); //读取一个字节
nonPicDataLength++; //+1
while (temp) //循环到temp为0
{
fread(&temp, 1, 1, fp); //如果不是0继续读一字节的数据
nonPicDataLength++; //计数
}
//跳过了Description文本以及末尾的\0
//非主流情况检测
char tempData[20] = {};
memset(tempData, 0, 20);
fread(&tempData, 8, 1, fp);
fseek(fp, -8, SEEK_CUR); //回到原位
//判断40次一位一位跳到文件头
bool ok = false; //是否正确识别出文件头
for (int i = 0; i < 40; i++)
{
//校验文件头
if (verificationPictureFormat(tempData))
{
ok = true;
break;
}
else
{
//如果校验失败尝试继续向后校验
fseek(fp, 1, SEEK_CUR);
nonPicDataLength++;
fread(&tempData, 8, 1, fp);
fseek(fp, -8, SEEK_CUR);
}
}
if (!ok)
{
fclose(fp);
fp = NULL;
freePictureData();
return false; //无法识别的数据
}
//-----真正的图片数据-----
picLength = frameLength - nonPicDataLength; //计算图片数据长度
pPicData = new byte[picLength]; //动态分配图片数据内存空间
memset(pPicData, 0, picLength); //清空图片数据内存
fread(pPicData, picLength, 1, fp); //得到图片数据
//------------------------
fclose(fp); //操作已完成,关闭文件。
}
else
{
//其余不支持的版本
fclose(fp);//关闭
fp = NULL;
return false;
}
return true;
}
//取得图片数据的长度
int getPictureLength()
{
return picLength;
}
//取得指向图片数据的指针
byte *getPictureDataPtr()
{
return pPicData;
}
//取得图片数据的扩展名(指针)
char *getPictureFormat()
{
return picFormat;
}
bool writePictureDataToFile(const char *outFilePath)
{
FILE *fp = NULL;
if (picLength > 0)
{
fp = fopen(outFilePath, "wb"); //打开目标文件
if (fp) //打开成功
{
fwrite(pPicData, picLength, 1, fp); //写入文件
fclose(fp); //关闭
return true;
}
else
{
return false; //文件打开失败
}
}
else
{
return false; //没有图像数据
}
}
//提取图片文件参数1输入文件参数2输出文件返回值是否成功
bool extractPicture(const char *inFilePath, const char *outFilePath)
{
if (loadPictureData(inFilePath)) //如果取得图片数据成功
{
if (writePictureDataToFile(outFilePath))
{
return true; //文件写出成功
}
else
{
return false; //文件写出失败
}
}
else
{
return false; //无图片数据
}
freePictureData();
}
}
#endif

View File

@ -13,3 +13,17 @@ Windows|[Supported Formats In DirectsShow](https://msdn.microsoft.com/en-us/libr
macOS|[Media formats supported by QuickTime Player](https://support.apple.com/en-us/HT201290)|Sorry, I don't know... macOS|[Media formats supported by QuickTime Player](https://support.apple.com/en-us/HT201290)|Sorry, I don't know...
Unix/Linux|depends on [GStreamer](https://gstreamer.freedesktop.org/) plugins which user installed|[GStreamer Plug-ins: gst-plugins-base, gst-plugins-good, gst-plugins-ugly, gst-plugins-bad](https://gstreamer.freedesktop.org/documentation/additional/splitup.html?gi-language=c) Unix/Linux|depends on [GStreamer](https://gstreamer.freedesktop.org/) plugins which user installed|[GStreamer Plug-ins: gst-plugins-base, gst-plugins-good, gst-plugins-ugly, gst-plugins-bad](https://gstreamer.freedesktop.org/documentation/additional/splitup.html?gi-language=c)
## About License
Since this is a toy repo, I don't spend much time about the license stuff. Currently this project use some assets and code from [ShadowPlayer](https://github.com/ShadowPower/ShadowPlayer), which have a very interesting license -- do whatever you want but cannot be used as homework -- obviously it's not a so called *free* license. I *may* do some license housecleaning works by replaceing the assets and code implementation when the code become reasonable, and the final codebase may probably released under MIT license.
Anyway here is a list of file which is in non-free state (with license: do whatever you want but cannot be used as homework):
- All png images inside `icons` folder.
- FlacPic.h
- ID3v2Pic.h
- seekableslider.{h,cpp}
Also there are some source code which I copy-paste from Qt codebase, which released under BSD-3-Clause license by the Qt Company:
- playlistmodel.{h,cpp}

View File

@ -3,6 +3,9 @@
#include "playlistmodel.h" #include "playlistmodel.h"
#include "ID3v2Pic.h"
#include "FlacPic.h"
#include <QPainter> #include <QPainter>
#include <QMediaPlayer> #include <QMediaPlayer>
#include <QMediaPlaylist> #include <QMediaPlaylist>
@ -13,6 +16,7 @@
#include <QScreen> #include <QScreen>
#include <QListView> #include <QListView>
#include <QCollator> #include <QCollator>
#include <QMimeData>
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
@ -124,6 +128,31 @@ void MainWindow::mouseReleaseEvent(QMouseEvent *event)
return QMainWindow::mouseReleaseEvent(event); return QMainWindow::mouseReleaseEvent(event);
} }
void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
// TODO: file/format filter?
e->acceptProposedAction();
}
void MainWindow::dropEvent(QDropEvent *e)
{
QList<QUrl> urls = e->mimeData()->urls();
if (urls.isEmpty()) {
return;
}
QString fileName = urls.first().toLocalFile();
if (fileName.isEmpty()) {
return;
}
// TODO: file/format filter?
createPlaylist(urls);
m_mediaPlayer->play();
}
void MainWindow::loadFile() void MainWindow::loadFile()
{ {
QStringList files = QFileDialog::getOpenFileNames(this, QStringList files = QFileDialog::getOpenFileNames(this,
@ -281,10 +310,32 @@ void MainWindow::initConnections()
{ {
connect(m_mediaPlayer, &QMediaPlayer::currentMediaChanged, this, [=](const QMediaContent &media) { connect(m_mediaPlayer, &QMediaPlayer::currentMediaChanged, this, [=](const QMediaContent &media) {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
ui->titleLabel->setText(media.canonicalUrl().fileName()); QUrl fileUrl = media.canonicalUrl();
#else #else
ui->titleLabel->setText(media.request().url().fileName()); QUrl fileUrl = media.request().url();
#endif // QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #endif // QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
ui->titleLabel->setText(fileUrl.fileName());
if (fileUrl.isLocalFile()) {
using namespace spID3;
using namespace spFLAC;
QString filePath(fileUrl.toLocalFile());
if (filePath.endsWith(".mp3")) {
if (spID3::loadPictureData(filePath.toLocal8Bit().data())) {
QByteArray picData((const char*)spID3::getPictureDataPtr(), spID3::getPictureLength());
ui->coverLabel->setPixmap(QPixmap::fromImage(QImage::fromData(picData)));
spID3::freePictureData();
}
} else if (filePath.endsWith(".flac")) {
if (spFLAC::loadPictureData(filePath.toLocal8Bit().data())) {
QByteArray picData((const char*)spFLAC::getPictureDataPtr(), spFLAC::getPictureLength());
ui->coverLabel->setPixmap(QPixmap::fromImage(QImage::fromData(picData)));
spFLAC::freePictureData();
}
}
}
}); });
connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, [=](qint64 pos) { connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, [=](qint64 pos) {

View File

@ -29,6 +29,8 @@ protected:
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *e) override;
void dropEvent(QDropEvent *e) override;
void loadFile(); void loadFile();
void centerWindow(); void centerWindow();

View File

@ -16,6 +16,9 @@
<height>160</height> <height>160</height>
</size> </size>
</property> </property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Pineapple Player</string> <string>Pineapple Player</string>
</property> </property>
@ -185,6 +188,9 @@ QLabel#coverLabel {
<property name="text"> <property name="text">
<string>AlbumCover</string> <string>AlbumCover</string>
</property> </property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>