library: Implement basic path management

I can add and remove root-level directories from the library database.
I do not save the database when modified, and I don't yet run an update
on all the files.

Signed-off-by: Anna Schumaker <schumaker.anna@gmail.com>
This commit is contained in:
Anna Schumaker 2013-10-27 17:25:26 -04:00 committed by Anna Schumaker
parent fc3b5db59e
commit 9ddbaf0b97
9 changed files with 253 additions and 36 deletions

1
config
View File

@ -40,6 +40,7 @@ class Config:
self.GROUP = False self.GROUP = False
self.IDLE = False self.IDLE = False
self.INDEX = False self.INDEX = False
self.LIBRARY = False
self.TEST = TEST self.TEST = TEST
self.reconfigure() self.reconfigure()

View File

@ -22,6 +22,15 @@ Library: (lib/library.cpp)
When a library : Track is created, it should be added to the "Library" When a library : Track is created, it should be added to the "Library"
group if it is NOT a member of the banned songs group. group if it is NOT a member of the banned songs group.
- Databases:
enum DB_Type {
DB_ALBUM,
DB_ARTIST,
DB_GENRE,
DB_LIBRARY,
DB_TRACK,
};
- Album: - Album:
class library :: Album : public DatabaseEntry { class library :: Album : public DatabaseEntry {
public: public:
@ -47,8 +56,8 @@ Library: (lib/library.cpp)
File << name File << name
- Path: - Library:
class library :: Path : public DatabaseEntry { class library :: Library : public DatabaseEntry {
public: public:
string root_path; string root_path;
bool enabled; bool enabled;
@ -70,6 +79,7 @@ Library: (lib/library.cpp)
short last_day; short last_day;
unsigned int play_count; unsigned int play_count;
unsigned int length; unsigned int length;
bool banned; bool banned;
string title; string title;
string length_str; string length_str;
@ -119,39 +129,33 @@ Library: (lib/library.cpp)
happen while idle. happen while idle.
- Testing: - Testing:
A test library should be created to test adding tags and anything else. The script tests/library/gen_library.sh will create a sample library
The command `arecord -d 10 </dev/zero | lame - -b 32 -h silence.mp3` in the /tmp/ directory for testing purposes. All the track files are
will create a 10 second long, silent mp3 file. Do something with this complete silence, but the script will fake up tags for each file.
command and figure out how to apply tags!
- API - API
library :: init(); void library :: init();
Initialize databases and read files from disk. Fill out Initialize databases and read files from disk. Fill out
groups and prepare filter as tracks are read. groups and prepare filter as tracks are read.
library :: add_path(string dir); bool library :: add_path(string dir);
Add new row to paths table, update If dir is not a directory:
return false
library :: del_path(unsigned int lib_id); Add new row to the library_db table, begin an update only
Invalidate a path row and all tracks owned by that path on the new path.
return true
library :: update_path(lib_id); void library :: del_path(unsigned int lib_id);
Update the given library path, if valid. Invalidate a library_db row and all tracks owned by that path
struct Track library :: resolve(track_id) void library :: update_path(lib_id);
Update the given library_db row, if valid.
struct Track library :: lookup(track_id)
Fill out a Track structure for the provided track_id Fill out a Track structure for the provided track_id
const Database<library :: Album> &library :: get_albums(); #ifdef CONFIG_DEBUG
Return the album database. void library :: print_db(DB_Type);
Print the database corresponding to DB_Type
const Database<library :: Artist> &library :: get_artists(); endif /* CONFIG_DEBUG */
Return the artist database.
const Database<library :: Genre> &library :: get_genres();
Return the genre database.
const Database<library :: Library> &library :: get_libraries();
Return the library database.
const Database<library :: Track> &library :: get_tracks();
Return the track database.

44
include/library.h Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#ifndef OCARINA_LIBRARY_H
#define OCARINA_LIBRARY_H
#include <database.h>
#include <string>
namespace library
{
enum DB_Type {
DB_ALBUM,
DB_ARTIST,
DB_GENRE,
DB_LIBRARY,
DB_TRACK,
};
class Library : public DatabaseEntry {
public:
std::string root_path;
bool enabled;
Library(const std::string &, bool);
void read(File &);
void write(File &);
#ifdef CONFIG_DEBUG
void print();
#endif /* CONFIG_DEBUG */
bool operator==(Library &);
};
bool add_path(const std::string &);
void del_path(unsigned int);
#ifdef CONFIG_DEBUG
void print_db(DB_Type);
void reset();
#endif /* CONFIG_DEBUG */
};
#endif /* OCARINA_LIBRARY_H */

View File

@ -2,7 +2,7 @@
Import("env", "CONFIG") Import("env", "CONFIG")
class Module: class Module:
def __init__(self, source = None, package = "", depends = ""): def __init__(self, source = None, package = "", depends = []):
self.depends = depends self.depends = depends
self.package = package self.package = package
self.source = source self.source = source
@ -15,12 +15,13 @@ modules = {
# # # #
########################### ###########################
"DATABASE" : Module("database.cpp", depends = "FILE"), "DATABASE" : Module("database.cpp", depends = [ "FILE" ]),
"FILE" : Module("file.cpp", package = "glib-2.0"), "FILE" : Module("file.cpp", package = "glib-2.0"),
"FILTER" : Module("filter.cpp", depends = "INDEX"), "FILTER" : Module("filter.cpp", depends = [ "INDEX" ]),
"GROUP" : Module("group.cpp", depends = "INDEX"), "GROUP" : Module("group.cpp", depends = [ "INDEX" ]),
"IDLE" : Module("idle.cpp"), "IDLE" : Module("idle.cpp"),
"INDEX" : Module("index.cpp", depends = "FILE"), "INDEX" : Module("index.cpp", depends = [ "FILE" ]),
"LIBRARY" : Module("library.cpp", depends = [ "DATABASE", "IDLE" ]),
########################### ###########################
########################### ###########################
@ -38,8 +39,10 @@ def resolve(name):
CONFIG.package(mod.package) CONFIG.package(mod.package)
res = [ env.Object(mod.source) ] res = [ env.Object(mod.source) ]
if CONFIG.__dict__.get(mod.depends) == False:
res += resolve(mod.depends) for dep in mod.depends:
if CONFIG.__dict__.get(dep) == False:
res += resolve(dep)
return res return res

82
lib/library.cpp Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <library.h>
#include <glib.h>
static Database<library :: Library> library_db("library.db", DB_NORMAL);
/*
* library :: Library: Basic information about each directory in the library
*/
library :: Library :: Library(const std::string &path, bool is_enabled)
: root_path(path), enabled(is_enabled)
{
}
void library :: Library :: read(File &f)
{
}
void library :: Library :: write(File &f)
{
}
#ifdef CONFIG_DEBUG
void library :: Library :: print()
{
:: print("%s", root_path.c_str());
if (enabled == true)
:: print(" (enabled)");
else
:: print(" (disabled)");
}
#endif /* CONFIG_DEBUG */
bool library :: Library :: operator==(library :: Library &rhs)
{
return root_path == rhs.root_path;
}
/*
* API used by the GUI begins here
*/
bool library :: add_path(const std::string &dir)
{
if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false)
return false;
library_db.insert(library :: Library(dir, true));
return true;
}
void library :: del_path(unsigned int id)
{
library_db.remove(id);
}
#ifdef CONFIG_DEBUG
void library :: print_db(DB_Type type)
{
switch (type) {
case DB_LIBRARY:
library_db.print();
break;
default:
break;
}
}
void library :: reset()
{
library_db.clear();
}
#endif /* CONFIG_DEBUG */

View File

@ -57,7 +57,7 @@ rm_test_dir(xdg.BaseDirectory.xdg_data_home);
# #
# Read SConscript files # Read SConscript files
# #
scripts = [ "database", "file", "filter", "group", "idle", "index", "print" ] scripts = [ "database", "file", "filter", "group", "idle", "index", "library", "print" ]
for s in scripts: for s in scripts:
CONFIG.reset(TEST = True) CONFIG.reset(TEST = True)
SConscript("%s/Sconscript" % s) SConscript("%s/Sconscript" % s)

6
tests/library/Sconscript Normal file
View File

@ -0,0 +1,6 @@
#!/usr/bin/python
Import("Test", "CONFIG")
CONFIG.LIBRARY = True
Test("library", "library.cpp")

View File

@ -29,6 +29,11 @@ function gen_tracks()
for i in $(seq 10); do for i in $(seq 10); do
track="Track $i" track="Track $i"
let remainder=$i%4 let remainder=$i%4
out="/tmp/$library/$artist/$album/$i - $track.ogg"
if [ -f "$out" ]; then
continue
fi
case $remainder in case $remainder in
0) OGG="1.ogg" ;; 0) OGG="1.ogg" ;;
@ -74,3 +79,5 @@ for i in $(seq 0 4); do
echo "Generating library: $i" echo "Generating library: $i"
gen_artists $i gen_artists $i
done done
touch /tmp/library/file

70
tests/library/library.cpp Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <library.h>
#include <print.h>
#include <stdlib.h>
void gen_library()
{
system("tests/library/gen_library.sh");
print("\n");
}
void test_add_dir(const std::string &test, const std::string &dir, bool expected)
{
print("Test %s: ", test.c_str());
if (library :: add_path(dir.c_str()) == expected)
print("PASSED\n");
else
print("FAILED\n");
library :: print_db(library :: DB_LIBRARY);
}
void test_del_dir(const std::string &test, const unsigned int path_id)
{
print("Test %s\n", test.c_str());
library :: del_path(path_id);
library :: print_db(library :: DB_LIBRARY);
}
/* Add paths library that SHOULD fail */
void test_0()
{
test_add_dir("0a", "/tmp/library/error", false);
test_add_dir("0b", "/tmp/library/file", false);
print("\n");
}
/* Simple library path operations */
void test_1()
{
test_add_dir("1a", "/tmp/library/0", true);
library :: del_path(0);
print("\n");
}
/* Test multiple paths */
void test_2()
{
library :: reset();
test_add_dir("2a", "/tmp/library/0", true);
test_add_dir("2b", "/tmp/library/1", true);
test_add_dir("2c", "/tmp/library/2", true);
test_del_dir("2d", 1);
test_del_dir("2e", 0);
test_del_dir("2f", 2);
print("\n");
}
int main(int argc, char **argv)
{
gen_library();
test_0();
test_1();
test_2();
return 0;
}