design: Rework playlists, add filtering
Signed-off-by: Bryan Schumaker <bjschuma@gmail.com>
This commit is contained in:
parent
b786b44a49
commit
0b2fc03f19
137
design.txt
137
design.txt
|
@ -21,6 +21,7 @@ Files:
|
|||
database.h
|
||||
database.hpp
|
||||
file.h
|
||||
filter.h
|
||||
group.h
|
||||
index.h
|
||||
library.h
|
||||
|
@ -43,6 +44,7 @@ Files:
|
|||
genre.db
|
||||
groups.idx
|
||||
library.db
|
||||
playlists.lst
|
||||
track.db
|
||||
|
||||
|
||||
|
@ -208,7 +210,8 @@ Database: (lib/database.cpp)
|
|||
|
||||
- API:
|
||||
Database.Database(filename);
|
||||
Initializes database to use ~/.ocarina{-debug}/filename
|
||||
Initializes database to use ~/.ocarina{-debug}/filename. Pass
|
||||
an empty string if you do not want this database to be saved.
|
||||
Database.load();
|
||||
Reads data from file. Call after static initialization of
|
||||
Ocarina to ensure idle tasks are configured.
|
||||
|
@ -249,7 +252,8 @@ Index: (lib/index.cpp)
|
|||
|
||||
- API:
|
||||
Index.Index(filename);
|
||||
Initializes an index using ~/.ocarina{-debug}/filename
|
||||
Initializes an index using ~/.ocarina{-debug}K/filename. Pass
|
||||
an empty string if you do not want this index to be saved.
|
||||
Index.load();
|
||||
Reads data from a file. Call after static initialization of
|
||||
Ocarina to ensure idle tasks are configured
|
||||
|
@ -361,8 +365,8 @@ Library: (lib/library.cpp)
|
|||
2) For each file in the scan directory, check if (lib_id, track_path)
|
||||
exists in the known_tracks map.
|
||||
2a) If the file is in the map, do nothing.
|
||||
2b) Else, add track to the library and to the "All Music" and
|
||||
"Library" and "Unplayed Tracks" groups
|
||||
2b) Else, add track to the library, to the groups "All Music" and
|
||||
"Library", and then to the filter index.
|
||||
3) Save all databases
|
||||
|
||||
The taglib library should be used for finding artist, album, etc. tags
|
||||
|
@ -404,8 +408,6 @@ Groups: (lib/group.cpp)
|
|||
Banned Songs
|
||||
These groups are mutually exclusive. A track is either
|
||||
in the Library or the Banned Songs group
|
||||
Unplayed tracks
|
||||
Tracks with a play count of 0
|
||||
|
||||
- API
|
||||
group :: init();
|
||||
|
@ -437,26 +439,97 @@ Preferences: (lib/prefs.cpp)
|
|||
|
||||
|
||||
Playlist: (lib/playlist.cpp)
|
||||
A playlist is a simple list of songs that can be played either randomly
|
||||
or in a user-defined order. It would probably be best to use a linked
|
||||
list or vector to represent playlists, rather than creating a SQLite
|
||||
table. I will be able to easily rearrange tracks in the playlist this
|
||||
way. This will also make it easier to deal with playlist renames and
|
||||
reordering by the user.
|
||||
Playlists are a list of songs that the user has configured to play. I
|
||||
will create a pool of playlists that will be filled by user actions.
|
||||
|
||||
Playlists will be put on a "deck" that is used to give an order to the
|
||||
next songs played. When deck :: next() is called, find the first
|
||||
playlist with PL_ENABLED set and call that playlists next() function.
|
||||
|
||||
When a playlist is empty, remove it from the deck.
|
||||
|
||||
- Flags:
|
||||
enum playlist_flags {
|
||||
PL_ENABLED (1 << 0),
|
||||
PL_RANDOM (1 << 1),
|
||||
PL_DRAIN (1 << 2),
|
||||
};
|
||||
|
||||
- Playlist:
|
||||
class Playlist : public Database {
|
||||
private:
|
||||
database<track_id> tracks; /* Keyed on track id */
|
||||
unsigned int cur;
|
||||
unsigned int flags;
|
||||
public:
|
||||
Playlist();
|
||||
void add(vector<track_id> &);
|
||||
void del(vector<track_id> &);
|
||||
void set_flag(playlist_flags);
|
||||
const unsigned int get_flags();
|
||||
unsigned int size()
|
||||
|
||||
File &operator<<(File &);
|
||||
File &operator>>(File &);
|
||||
|
||||
void sort();
|
||||
void next();
|
||||
}
|
||||
|
||||
File << flags << cur << tracks[0] << tracks[1] << ... << tracks[N];
|
||||
|
||||
- Deck:
|
||||
list<Playlist> deck;
|
||||
File << deck[0] << endl;
|
||||
File << deck[1] << endl;
|
||||
File << deck[N] << endl;
|
||||
|
||||
- API
|
||||
/* Playlist management */
|
||||
new_playlist();
|
||||
del_playlist(playlist);
|
||||
add_to_playlist(playlist, songid);
|
||||
rm_from_playlist(playlist, songid);
|
||||
playlist_size(playlist)
|
||||
set_flag(playlist, flag)
|
||||
deck :: init();
|
||||
Read in the playlist file
|
||||
deck :: new();
|
||||
Adds a new playlist to the deck
|
||||
deck :: rm(N)
|
||||
Removes playlist N from the deck
|
||||
Playlist *deck :: get(N)
|
||||
Return playlist N from the deck
|
||||
|
||||
- Flags
|
||||
PL_ENABLED (1 << 0)
|
||||
PL_RANDOM (1 << 1)
|
||||
PL_DRAIN (1 << 2)
|
||||
- TODO <<<<<
|
||||
What if each playlist has its own playlist_id for tracks? This would
|
||||
allow for simpler removals, since I won't need to search for a track id.
|
||||
I can easily create a function for mapping a list of playlist_ids to
|
||||
track_ids...
|
||||
|
||||
|
||||
|
||||
Filter: (lib/filter.cpp)
|
||||
Filtering is used to generate a subset of songs for easier searching.
|
||||
|
||||
- Index:
|
||||
map<string, string> lowercase_cache;
|
||||
map<string, set<string>> substring_cache;
|
||||
Index filter_index("");
|
||||
|
||||
- Parsing:
|
||||
1) Convert the provided string into a list of words, using whitespace
|
||||
and the following characters as delimiters: \/,;()_~+"
|
||||
|
||||
For each word:
|
||||
2) Check the lowercase_cache to see if we have seen the word before,
|
||||
a) If we have, return the stored string
|
||||
b) Convert the string to lowercase and strip out remaining
|
||||
special characters. Add the result to the lowercase_cache;
|
||||
3) Check the substring_cache to see if we have seen the word before,
|
||||
a) If we have, use the substring set returned
|
||||
b) Break the word into substrings from the front only. For
|
||||
example: "dalek" would contain the substrings
|
||||
{d, da, dal, dale, dalek}. Add to the substring cache.
|
||||
|
||||
- API:
|
||||
filter :: add(string, track_id);
|
||||
Parses the string and adds the track_id to the index.
|
||||
void filter :: search(string, set<track_id> &);
|
||||
Parse the string and fill in the set with matching tracks.
|
||||
|
||||
|
||||
|
||||
|
@ -468,6 +541,12 @@ Future work:
|
|||
|
||||
Hint: If feature B depends on A, implement A in 6.x and B in 6.x+1
|
||||
|
||||
- Break design doc into pieces: (6.1)
|
||||
Createa script to join everything together cleanly.
|
||||
|
||||
- New default groups: (6.1)
|
||||
Unplayed tracks
|
||||
|
||||
- Categories: (6.1)
|
||||
Use these to make "groups of groups" for better organization.
|
||||
Different categories can include Album, Artist and Genere
|
||||
|
@ -500,3 +579,15 @@ Future work:
|
|||
- Track tag editor: (6.2)
|
||||
Make a pop-up window for editing the tags of a track. Be sure
|
||||
to update the library information and the on-disk file.
|
||||
|
||||
- Clean up Scons files / build system: (6.0) <<<<<
|
||||
I just had to try looking up something for Josh. I have no idea
|
||||
how my build system works anymore. This is bad.
|
||||
|
||||
- Album art: (6.1)
|
||||
|
||||
- Playlist custom sorting: (6.1)
|
||||
Click column headers to choos sort order
|
||||
Keep a list of fields that the user has selected and place new
|
||||
fields in the front of this list. Use a recursive stable sort
|
||||
to do the sorting.
|
||||
|
|
Loading…
Reference in New Issue