Create replacement repo for previous corrupted one

This commit is contained in:
2025-11-19 20:50:15 -07:00
commit ba3ff29405
1022 changed files with 957 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
#include "albumButton.h"
#include <QPixmap>
#include <QIcon>
AlbumButton::AlbumButton(const AlbumData& item, QWidget* parent)
: QPushButton(parent), item_(item)
{
QPixmap pix(item.imagePath);
QPixmap scaled = pix.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation);
setIcon(QIcon(scaled));
setIconSize(QSize(200, 200));
setFixedSize(210, 210);
setStyleSheet("border: none;");
connect(this, &QPushButton::clicked, this, [this]() {
emit activated(item_);
});
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include <QPushButton>
#include "src/data/albumInformation.h"
class AlbumButton : public QPushButton
{
Q_OBJECT
public:
explicit AlbumButton(const AlbumData& item, QWidget* parent = nullptr);
signals:
void activated(const AlbumData& item);
private:
AlbumData item_;
};

View File

@@ -0,0 +1,42 @@
#include "albumtile.h"
#include <QPixmap>
#include <QMouseEvent>
AlbumTile::AlbumTile(const AlbumData& item, QWidget* parent)
: QWidget(parent), item_(item)
{
auto* layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignCenter);
layout->setSpacing(6);
// Cover Image
QPixmap pix(item.imagePath);
QLabel* cover = new QLabel;
cover->setPixmap(
pix.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation)
);
cover->setAlignment(Qt::AlignCenter);
// Album Label
QLabel* albumLabel = new QLabel(item.album);
albumLabel->setAlignment(Qt::AlignCenter);
albumLabel->setStyleSheet("font-weight: 600; font-size: 14px;");
// Artist Label
QLabel* artistLabel = new QLabel(item.artist);
artistLabel->setAlignment(Qt::AlignCenter);
artistLabel->setStyleSheet("color: #888; font-size: 12px;");
layout->addWidget(cover);
layout->addWidget(albumLabel);
layout->addWidget(artistLabel);
setCursor(Qt::PointingHandCursor);
}
// Simple click handler
void AlbumTile::mousePressEvent(QMouseEvent* event)
{
emit activated(item_);
QWidget::mousePressEvent(event);
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QMouseEvent>
#include "src/data/albumInformation.h"
class AlbumTile : public QWidget
{
Q_OBJECT
public:
explicit AlbumTile(const AlbumData& item, QWidget* parent = nullptr);
signals:
void activated(const AlbumData& item);
protected:
void mousePressEvent(QMouseEvent* event) override; // <-- REQUIRED
private:
AlbumData item_;
};

View File

@@ -0,0 +1,20 @@
#include "mediaButton.h"
#include <QPixmap>
#include <QIcon>
MediaButton::MediaButton(const AlbumData& item, QWidget* parent)
: QPushButton(parent), item_(item)
{
QPixmap pix(item.imagePath);
if (!pix.isNull()) {
QPixmap scaled = pix.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation);
this->setIcon(QIcon(scaled));
this->setIconSize(QSize(200, 200));
this->setFixedSize(210, 210);
this->setStyleSheet("border: none;");
}
connect(this, &QPushButton::clicked, this, [this]() {
emit activated(item_);
});
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <QPushButton>
#include "src/data/albumInformation.h"
class MediaButton : public QPushButton {
Q_OBJECT
public:
explicit MediaButton(const AlbumData& item, QWidget* parent = nullptr);
signals:
void activated(const AlbumData& item);
private:
AlbumData item_;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include <QString>
#include <QStringList>
struct AlbumData {
QString folderPath;
QString imagePath;
QStringList audioFiles;
QString artist;
QString album;
};

View File

@@ -0,0 +1,151 @@
#include "flowLayout.h"
#include <QtWidgets>
#include <utility>
#include <algorithm>
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
FlowLayout::~FlowLayout()
{
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
void FlowLayout::addItem(QLayoutItem *item)
{
itemList.append(item);
}
int FlowLayout::horizontalSpacing() const
{
if (m_hSpace >= 0)
return m_hSpace;
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
}
int FlowLayout::verticalSpacing() const
{
if (m_vSpace >= 0)
return m_vSpace;
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}
int FlowLayout::count() const
{
return itemList.size();
}
QLayoutItem *FlowLayout::itemAt(int index) const
{
return itemList.value(index);
}
QLayoutItem *FlowLayout::takeAt(int index)
{
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
return nullptr;
}
Qt::Orientations FlowLayout::expandingDirections() const
{
return {};
}
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth(int width) const
{
return doLayout(QRect(0, 0, width, 0), true);
}
QSize FlowLayout::minimumSize() const
{
QSize size;
for (QLayoutItem *item : std::as_const(itemList))
size = size.expandedTo(item->minimumSize());
const auto m = contentsMargins();
size += QSize(m.left() + m.right(), m.top() + m.bottom());
return size;
}
void FlowLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
doLayout(rect, false);
}
QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effective = rect.adjusted(+left, +top, -right, -bottom);
int x = effective.x();
int y = effective.y();
int lineHeight = 0;
for (QLayoutItem *item : std::as_const(itemList)) {
QWidget *widget = item->widget();
int spaceX = horizontalSpacing();
if (spaceX < 0)
spaceX = widget->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY < 0)
spaceY = widget->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
const QSize hint = item->sizeHint();
int nextX = x + hint.width() + spaceX;
if (nextX - spaceX > effective.right() && lineHeight > 0) {
// wrap
x = effective.x();
y += lineHeight + spaceY;
nextX = x + hint.width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), hint));
x = nextX;
lineHeight = qMax(lineHeight, hint.height());
}
return y + lineHeight + bottom - rect.y();
}
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
QObject *parentObj = parent();
if (!parentObj)
return -1;
if (parentObj->isWidgetType()) {
QWidget *w = static_cast<QWidget *>(parentObj);
return w->style()->pixelMetric(pm, nullptr, w);
}
return static_cast<QLayout *>(parentObj)->spacing();
}

View File

@@ -0,0 +1,35 @@
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H
#include <QLayout>
#include <QRect>
#include <QStyle>
class FlowLayout : public QLayout {
public:
explicit FlowLayout(QWidget *parent = nullptr, int margin = -1, int hSpacing = -1, int vSpacing = -1);
~FlowLayout();
void addItem(QLayoutItem *item) override;
int horizontalSpacing() const;
int verticalSpacing() const;
Qt::Orientations expandingDirections() const override;
bool hasHeightForWidth() const override;
int heightForWidth(int) const override;
int count() const override;
QLayoutItem *itemAt(int index) const override;
QSize minimumSize() const override;
void setGeometry(const QRect &rect) override;
QSize sizeHint() const override;
QLayoutItem *takeAt(int index) override;
private:
int doLayout(const QRect &rect, bool testOnly) const;
int smartSpacing(QStyle::PixelMetric pm) const;
QList<QLayoutItem *> itemList;
int m_hSpace;
int m_vSpace;
};
#endif

23
src/main.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "src/windows/musicSelector/musicSelector.h"
#include <QApplication>
#include <QLocale>
#include <QTranslator>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
const QString baseName = "Music-Player_" + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName)) {
a.installTranslator(&translator);
break;
}
}
MusicSelector w;
w.show();
return a.exec();
}

View File

@@ -0,0 +1,97 @@
#include "src/windows/musicSelector/musicSelector.h"
#include "src/components/albumTile/albumTile.h"
#include "src/windows/playerWindow/playerWindow.h"
#include "src/layout/flowlayout/flowLayout.h"
#include <QDir>
#include <QCoreApplication>
#include <QFileInfo>
MusicSelector::MusicSelector(QWidget* parent)
: QWidget(parent)
{
setWindowTitle("Album Selector");
resize(900, 600);
// Use flow layout from Qt Library
auto* flow = new FlowLayout(this);
setLayout(flow);
// Set Media Folder
QString projectRoot = QDir(QFileInfo(__FILE__).absolutePath()).absoluteFilePath("../../../");
QString mediaFolder = projectRoot + "/resources/media";
// Debug Media Path
qDebug() << "projectRoot:" << projectRoot;
qDebug() << "mediaFolder:" << mediaFolder;
// Scan the /media Folder
QList<AlbumData> items = scanFolder(mediaFolder);
for (const AlbumData& item : items)
{
AlbumTile* tile = new AlbumTile(item);
flow->addWidget(tile);
connect(tile, &AlbumTile::activated, this, [this](const AlbumData& it) {
auto* player = new PlayerWindow(it);
player->show();
});
}
}
QList<AlbumData> MusicSelector::scanFolder(const QString& root)
{
QList<AlbumData> results;
QDir mediaRoot(root);
mediaRoot.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
// Loop over artists
for (const QFileInfo& artistInfo : mediaRoot.entryInfoList())
{
QDir artistDir(artistInfo.absoluteFilePath());
artistDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
// Loop over albums inside artist
for (const QFileInfo& albumInfo : artistDir.entryInfoList())
{
QDir albumDir(albumInfo.absoluteFilePath());
QString image;
QStringList audioFiles;
// Loop files in album directory
for (const QFileInfo& f : albumDir.entryInfoList(QDir::Files))
{
QString ext = f.suffix().toLower();
if (ext == "jpg" || ext == "jpeg" || ext == "png")
if (image.isEmpty()) image = f.absoluteFilePath();
if (ext == "mp3" || ext == "wav" || ext == "flac" || ext == "m4a")
audioFiles.append(f.absoluteFilePath());
}
// 🔍 DEBUG GOES HERE, where artistInfo, albumInfo, image, audioFiles exist
qDebug() << "Artist:" << artistInfo.fileName();
qDebug() << "Album:" << albumInfo.fileName();
qDebug() << "Image found:" << image;
qDebug() << "Tracks:" << audioFiles;
// Only add if album valid
if (!image.isEmpty() && !audioFiles.isEmpty())
{
AlbumData m;
m.folderPath = albumDir.absolutePath();
m.imagePath = image;
m.audioFiles = audioFiles;
m.artist = artistInfo.fileName(); // <--- add this
m.album = albumInfo.fileName();
results.append(m);
}
}
}
return results;
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include <QWidget>
#include <QGridLayout>
#include "src/data/albumInformation.h"
class MusicSelector : public QWidget
{
Q_OBJECT
public:
explicit MusicSelector(QWidget *parent = nullptr);
private:
QList<AlbumData> scanFolder(const QString& root); // <-- RIGHT HERE
QGridLayout* grid;
};

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MusicSelector</class>
<widget class="QMainWindow" name="MusicSelector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MusicSelector</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>33</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,54 @@
#include "playerWindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QPixmap>
PlayerWindow::PlayerWindow(const AlbumData& item, QWidget* parent)
: QWidget(parent), item_(item)
{
this->setWindowTitle("Now Playing");
this->resize(300, 300);
QVBoxLayout* layout = new QVBoxLayout(this);
QLabel* art = new QLabel;
QPixmap pix(item.imagePath);
art->setPixmap(pix.scaled(250, 250, Qt::KeepAspectRatio, Qt::SmoothTransformation));
art->setAlignment(Qt::AlignCenter);
layout->addWidget(art);
player_ = new QMediaPlayer(this);
audio_ = new QAudioOutput(this);
player_->setAudioOutput(audio_);
connect(player_, &QMediaPlayer::mediaStatusChanged, this, [this](QMediaPlayer::MediaStatus s){
if (s == QMediaPlayer::EndOfMedia) {
playNext();
}
});
playNext();
}
void PlayerWindow::playNext()
{
if (index_ >= item_.audioFiles.size()) {
this->close();
return;
}
player_->setSource(QUrl::fromLocalFile(item_.audioFiles[index_]));
player_->play();
index_++;
}
// Stop the music when the window is closed
void PlayerWindow::closeEvent(QCloseEvent* event)
{
if (player_) {
player_->stop();
}
QWidget::closeEvent(event); // still let Qt do its normal cleanup
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <QWidget>
#include <QMediaPlayer>
#include <QAudioOutput>
#include "src/data/albumInformation.h"
class PlayerWindow : public QWidget {
Q_OBJECT
public:
explicit PlayerWindow(const AlbumData& item, QWidget* parent = nullptr);
private slots:
void playNext();
private:
AlbumData item_;
int index_ = 0;
QMediaPlayer* player_;
QAudioOutput* audio_;
protected:
void closeEvent(QCloseEvent* event) override;
};