add song library abstraction
This commit is contained in:
parent
af5fd74a2d
commit
95a67c626d
6 changed files with 171 additions and 16 deletions
|
@ -11,6 +11,7 @@ alsa_dep = dependency('alsa')
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'hyperpop',
|
'hyperpop',
|
||||||
|
'./src/player/SongLibrary.cpp',
|
||||||
'./src/player/OpusFile.cpp',
|
'./src/player/OpusFile.cpp',
|
||||||
'./src/player/ALSAOutput.cpp',
|
'./src/player/ALSAOutput.cpp',
|
||||||
'./src/player/Player.cpp',
|
'./src/player/Player.cpp',
|
||||||
|
|
12
src/main.cpp
12
src/main.cpp
|
@ -5,12 +5,16 @@
|
||||||
#include "raven/Widget.hpp"
|
#include "raven/Widget.hpp"
|
||||||
#include "raven/Button.hpp"
|
#include "raven/Button.hpp"
|
||||||
#include "src/player/Player.hpp"
|
#include "src/player/Player.hpp"
|
||||||
|
#include "src/player/SongLibrary.hpp"
|
||||||
#include "ui/AppWidget.hpp"
|
#include "ui/AppWidget.hpp"
|
||||||
#include "player/ALSAOutput.hpp"
|
#include "player/ALSAOutput.hpp"
|
||||||
#include "player/OpusFile.hpp"
|
#include "player/OpusFile.hpp"
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
SongLibrary *library = new SongLibrary();
|
||||||
|
library->populate_from("/home/hippoz/music");
|
||||||
|
|
||||||
Player player;
|
Player player;
|
||||||
|
|
||||||
Raven::Application application(true);
|
Raven::Application application(true);
|
||||||
|
@ -18,7 +22,11 @@ int main()
|
||||||
|
|
||||||
window->spawn_window();
|
window->spawn_window();
|
||||||
|
|
||||||
window->set_main_widget<AppWidget>(&player);
|
window->set_main_widget<AppWidget>(&player, library);
|
||||||
|
|
||||||
return application.run();
|
application.run();
|
||||||
|
|
||||||
|
delete library;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
41
src/player/SongLibrary.cpp
Normal file
41
src/player/SongLibrary.cpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include "SongLibrary.hpp"
|
||||||
|
#include <opusfile.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
void SongLibrary::populate_from(std::filesystem::path directory) {
|
||||||
|
m_all_songs.clear();
|
||||||
|
m_songs_by_artist.clear();
|
||||||
|
|
||||||
|
for (const auto &entry : std::filesystem::directory_iterator(directory)) {
|
||||||
|
OggOpusFile *file = op_open_file(entry.path().c_str(), NULL);
|
||||||
|
if (file == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const OpusTags *tags = op_tags(file, -1);
|
||||||
|
if (tags == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *artist_value = opus_tags_query(tags, "artist", 0);
|
||||||
|
const char *title_value = opus_tags_query(tags, "title", 0);
|
||||||
|
const char *album_value = opus_tags_query(tags, "album", 0);
|
||||||
|
if (artist_value == NULL || title_value == NULL || album_value == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SongEntry> song_entry = std::make_shared<SongEntry>();
|
||||||
|
song_entry->artist = artist_value;
|
||||||
|
song_entry->title = title_value;
|
||||||
|
song_entry->album = album_value;
|
||||||
|
song_entry->absolute_path = entry.path().c_str();
|
||||||
|
song_entry->length_ms = op_pcm_total(file, -1);
|
||||||
|
|
||||||
|
m_all_songs.push_back(song_entry);
|
||||||
|
m_songs_by_artist[song_entry->artist].push_back(song_entry);
|
||||||
|
|
||||||
|
op_free(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
28
src/player/SongLibrary.hpp
Normal file
28
src/player/SongLibrary.hpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
|
||||||
|
class SongLibrary {
|
||||||
|
public:
|
||||||
|
struct SongEntry {
|
||||||
|
std::string artist;
|
||||||
|
std::string album;
|
||||||
|
std::string title;
|
||||||
|
std::string absolute_path;
|
||||||
|
size_t length_ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
SongLibrary() {}
|
||||||
|
|
||||||
|
void populate_from(std::filesystem::path directory);
|
||||||
|
|
||||||
|
const std::vector<std::shared_ptr<SongEntry>> &all_songs() { return m_all_songs; }
|
||||||
|
const std::map<std::string, std::vector<std::shared_ptr<SongEntry>>> &songs_by_artist() { return m_songs_by_artist; }
|
||||||
|
private:
|
||||||
|
std::vector<std::shared_ptr<SongEntry>> m_all_songs;
|
||||||
|
std::map<std::string, std::vector<std::shared_ptr<SongEntry>>> m_songs_by_artist;
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "AppWidget.hpp"
|
#include "AppWidget.hpp"
|
||||||
|
#include "GenericStyle.hpp"
|
||||||
#include "raven/Styles.hpp"
|
#include "raven/Styles.hpp"
|
||||||
#include "raven/Box.hpp"
|
#include "raven/Box.hpp"
|
||||||
#include "raven/TextInput.hpp"
|
#include "raven/TextInput.hpp"
|
||||||
|
@ -11,17 +12,65 @@
|
||||||
#include "raven/DocumentLayout.hpp"
|
#include "raven/DocumentLayout.hpp"
|
||||||
#include "src/player/OpusFile.hpp"
|
#include "src/player/OpusFile.hpp"
|
||||||
#include "src/player/Player.hpp"
|
#include "src/player/Player.hpp"
|
||||||
|
#include "src/player/SongLibrary.hpp"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr<Raven::Widget> song_list_widget, Player *player) {
|
Raven::RGB accent_color = { 0xF7A99E };
|
||||||
|
Raven::RGB background_color = { 0xfff9f4 };
|
||||||
|
|
||||||
|
Raven::GenericStyle background_color_style {
|
||||||
|
NULL,
|
||||||
|
Raven::unused,
|
||||||
|
accent_color,
|
||||||
|
Raven::unused,
|
||||||
|
Raven::unused,
|
||||||
|
0.0,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
Raven::GenericStyle main_view_style {
|
||||||
|
NULL,
|
||||||
|
Raven::unused,
|
||||||
|
background_color,
|
||||||
|
Raven::unused,
|
||||||
|
Raven::unused,
|
||||||
|
12.0,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
Raven::GenericStyle song_button_style {
|
||||||
|
pango_font_description_from_string("sans-serif"),
|
||||||
|
Raven::black1,
|
||||||
|
background_color,
|
||||||
|
accent_color.factor(1.1),
|
||||||
|
accent_color.factor(1.2),
|
||||||
|
6.0,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
Raven::GenericStyle artist_button_style {
|
||||||
|
pango_font_description_from_string("sans-serif"),
|
||||||
|
Raven::black1,
|
||||||
|
accent_color,
|
||||||
|
accent_color.factor(1.1),
|
||||||
|
accent_color.factor(1.2),
|
||||||
|
6.0,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
static void populate_song_list(std::shared_ptr<Raven::Widget> song_list_widget, Player *player, SongLibrary *song_library, std::string target_artist) {
|
||||||
song_list_widget->clear_children();
|
song_list_widget->clear_children();
|
||||||
for (const auto &entry : std::filesystem::directory_iterator(songs_directory)) {
|
|
||||||
std::string filename = entry.path().filename();
|
for (const auto &entry : song_library->songs_by_artist().at(target_artist)) {
|
||||||
std::string path_string = entry.path().string();
|
auto button = song_list_widget->add<Raven::Button>(entry->title);
|
||||||
auto button = song_list_widget->add<Raven::Button>(filename);
|
button->set_style(&song_button_style);
|
||||||
button->on_click = [player, path_string]() {
|
button->on_click = [player, entry]() {
|
||||||
auto file = std::make_shared<OpusFile>();
|
auto file = std::make_shared<OpusFile>();
|
||||||
file->open(path_string);
|
file->open(entry->absolute_path);
|
||||||
player->set_file(file);
|
player->set_file(file);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -30,23 +79,47 @@ static void populate_song_list(std::filesystem::path songs_directory, std::share
|
||||||
void AppWidget::on_init() {
|
void AppWidget::on_init() {
|
||||||
/* init */
|
/* init */
|
||||||
{
|
{
|
||||||
auto layout = set_layout<Raven::BoxLayout>(Raven::Direction::Vertical);
|
auto layout = set_layout<Raven::BoxLayout>(Raven::Direction::Horizontal);
|
||||||
layout->set_spacing(8);
|
layout->set_spacing(8);
|
||||||
layout->set_margin(12);
|
layout->set_margin(16);
|
||||||
|
|
||||||
|
set_style(&background_color_style);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sidebar */
|
||||||
|
{
|
||||||
|
m_sidebar = add<Raven::ScrollContainer>();
|
||||||
|
m_sidebar->set_style(&background_color_style);
|
||||||
|
m_sidebar->rect().set_min_width(250.0);
|
||||||
|
auto target = m_sidebar->make_target();
|
||||||
|
target->set_style(&background_color_style);
|
||||||
|
auto layout = target->set_layout<Raven::ListLayout>(Raven::Direction::Vertical);
|
||||||
|
layout->set_spacing(6.0);
|
||||||
|
layout->set_inherit_secondary_dimension(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* song list */
|
/* song list */
|
||||||
{
|
{
|
||||||
m_song_list = add<Raven::ScrollContainer>();
|
m_song_list = add<Raven::ScrollContainer>();
|
||||||
m_song_list->set_grows(true);
|
m_song_list->set_grows(true);
|
||||||
|
m_song_list->set_style(&main_view_style);
|
||||||
auto target = m_song_list->make_target();
|
auto target = m_song_list->make_target();
|
||||||
auto layout = target->set_layout<Raven::ListLayout>(Raven::Direction::Vertical);
|
auto layout = target->set_layout<Raven::ListLayout>(Raven::Direction::Vertical);
|
||||||
layout->set_spacing(4.0);
|
layout->set_spacing(6.0);
|
||||||
|
layout->set_margin(10.0);
|
||||||
layout->set_inherit_secondary_dimension(true);
|
layout->set_inherit_secondary_dimension(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
populate_song_list("/home/hippoz/music", m_song_list->target(), m_player);
|
populate_song_list(m_song_list->target(), m_player, m_song_library, "EDEN");
|
||||||
|
for (const auto &it : m_song_library->songs_by_artist()) {
|
||||||
|
auto button = m_sidebar->target()->add<Raven::Button>(it.first);
|
||||||
|
button->set_style(&artist_button_style);
|
||||||
|
button->on_click = [this, it]() {
|
||||||
|
populate_song_list(m_song_list->target(), m_player, m_song_library, it.first);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
reflow();
|
reflow();
|
||||||
set_did_init(true);
|
set_did_init(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,20 @@
|
||||||
#include "raven/Widget.hpp"
|
#include "raven/Widget.hpp"
|
||||||
#include "raven/TextInput.hpp"
|
#include "raven/TextInput.hpp"
|
||||||
#include "src/player/Player.hpp"
|
#include "src/player/Player.hpp"
|
||||||
|
#include "src/player/SongLibrary.hpp"
|
||||||
|
|
||||||
class AppWidget : public Raven::Widget {
|
class AppWidget : public Raven::Widget {
|
||||||
public:
|
public:
|
||||||
AppWidget(Player *player)
|
AppWidget(Player *player, SongLibrary *library)
|
||||||
: Raven::Widget()
|
: Raven::Widget()
|
||||||
, m_player(player) {}
|
, m_player(player)
|
||||||
|
, m_song_library(library) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void on_init() override;
|
void on_init() override;
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Raven::ScrollContainer> m_song_list;
|
std::shared_ptr<Raven::ScrollContainer> m_song_list;
|
||||||
|
std::shared_ptr<Raven::ScrollContainer> m_sidebar;
|
||||||
Player *m_player;
|
Player *m_player;
|
||||||
|
SongLibrary *m_song_library;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue