1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-09-28 06:18:13 +00:00

Feat: Smart playlists (most played and best rated) #38

This commit is contained in:
jcorporation 2018-09-24 21:55:53 +01:00
parent a8a8503a26
commit 76161dc0ac
11 changed files with 143 additions and 20 deletions

View File

@ -55,3 +55,4 @@ install(FILES dist/htdocs/css/bootstrap.min.css DESTINATION share/${PROJECT_NAME
install(FILES dist/htdocs/css/mympd.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/) install(FILES dist/htdocs/css/mympd.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/)
install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs) install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs)
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/pics) install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/pics)
install(DIRECTORY DESTINATION ../var/lib/${PROJECT_NAME}/tmp)

View File

@ -28,6 +28,7 @@ This fork provides a reworked ui based on Bootstrap 4, a modernized backend and
- Play statistics and song voting (uses mpd stickers) - Play statistics and song voting (uses mpd stickers)
- Embedded Webserver (mongoose) - Embedded Webserver (mongoose)
- Jukebox mode (add's random songs from database or playlists to queue) - Jukebox mode (add's random songs from database or playlists to queue)
- Smart playlists (most played and best rated songs)
myMPD is work in progress. Bugreportes and feature requests are very welcome. myMPD is work in progress. Bugreportes and feature requests are very welcome.
- https://github.com/jcorporation/myMPD/issues - https://github.com/jcorporation/myMPD/issues

View File

@ -11,11 +11,8 @@ post_upgrade() {
[ "$?" = "2" ] && useradd --system -d /var/lib/mympd -s /usr/sbin/nologin -g mympd mympd [ "$?" = "2" ] && useradd --system -d /var/lib/mympd -s /usr/sbin/nologin -g mympd mympd
# fix ownership of /var/lib/mympd # fix ownership of /var/lib/mympd
if ! [ $(stat -c '%U:%G' /var/lib/mympd/) = 'mympd:mympd' ] echo "INFO: Fixing ownership of /var/lib/mympd"
then chown -R mympd.mympd /var/lib/mympd
echo "INFO: Fixing ownership of /var/lib/mympd"
chown -R mympd.mympd /var/lib/mympd
fi
# link music directory to mympd if not already exising # link music directory to mympd if not already exising
echo "INFO: Trying to link musicdir to library" echo "INFO: Trying to link musicdir to library"

View File

@ -44,11 +44,8 @@ getent group mympd > /dev/null
getent passwd mympd > /dev/null getent passwd mympd > /dev/null
[ "$?" = "2" ] && useradd -r mympd -g mympd -d /var/lib/mympd -s /usr/sbin/nologin [ "$?" = "2" ] && useradd -r mympd -g mympd -d /var/lib/mympd -s /usr/sbin/nologin
if ! [ $(stat -c '%U:%G' /var/lib/mympd/) = 'mympd:mympd' ] echo "Fixing ownership of /var/lib/mympd"
then chown -R mympd.mympd /var/lib/mympd
echo "Fixing ownership of /var/lib/mympd"
chown -R mympd.mympd /var/lib/mympd
fi
if [ -d /etc/systemd ] if [ -d /etc/systemd ]
then then

View File

@ -35,3 +35,6 @@ stickers = true
#List of tags in myMPD gui #List of tags in myMPD gui
#Supported tags: Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer #Supported tags: Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer
taglist = Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer taglist = Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer
#Enable smart playlists
smartplaylists = true

7
debian/postinst vendored
View File

@ -6,11 +6,8 @@ getent group mympd > /dev/null
getent passwd mympd > /dev/null getent passwd mympd > /dev/null
[ "$?" = "2" ] && useradd -r mympd -g mympd -d /var/lib/mympd -s /usr/sbin/nologin [ "$?" = "2" ] && useradd -r mympd -g mympd -d /var/lib/mympd -s /usr/sbin/nologin
if ! [ $(stat -c '%U:%G' /var/lib/mympd/) = 'mympd:mympd' ] echo "Fixing ownership of /var/lib/mympd"
then chown -R mympd.mympd /var/lib/mympd
echo "Fixing ownership of /var/lib/mympd"
chown -R mympd.mympd /var/lib/mympd
fi
echo "Trying to link musicdir to library" echo "Trying to link musicdir to library"
if [ -f /etc/mpd.conf ] if [ -f /etc/mpd.conf ]

View File

@ -24,10 +24,11 @@
</a> </a>
<div class="dropdown-menu bg-dark"> <div class="dropdown-menu bg-dark">
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "showAddToPlaylist", "options": ["stream"]}'>Add Stream</a> <a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "showAddToPlaylist", "options": ["stream"]}'>Add Stream</a>
<a id="navDBupdate" class="dropdown-item text-light bg-dark" data-toggle="collapse" href="#menu-dbupdate"><span class="material-icons material-icons-left">keyboard_arrow_right</span>Database</a> <a id="navDBupdate" class="dropdown-item text-light bg-dark" data-toggle="collapse" href="#menu-dbupdate"><span class="material-icons material-icons-left">keyboard_arrow_right</span>Update</a>
<div class="collapse" id="menu-dbupdate"> <div class="collapse" id="menu-dbupdate">
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "updateDB", "options": []}'>Update</a> <a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "updateDB", "options": []}'>Update Database</a>
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "rescanDB", "options": []}'>Rescan</a> <a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "rescanDB", "options": []}'>Rescan Database</a>
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "updateSmartPlaylists", "options": []}'>Update Smart Playlists</a>
</div> </div>
<a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "openLocalPlayer", "options": []}'>Local Player</a> <a class="dropdown-item text-light bg-dark" href="#" data-href='{"cmd": "openLocalPlayer", "options": []}'>Local Player</a>
<a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a> <a class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a>

View File

@ -1542,6 +1542,10 @@ function getAllPlaylists(obj) {
} }
} }
function updateSmartPlaylists() {
sendAPI({"cmd": "MPD_API_SMARTPLS_UPDATE"});
}
function voteSong(vote) { function voteSong(vote) {
var uri = domCache.currentTrack.getAttribute('data-uri'); var uri = domCache.currentTrack.getAttribute('data-uri');
if (uri == '') if (uri == '')

View File

@ -183,7 +183,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
uint_rc = mpd_run_rescan(mpd.conn, NULL); uint_rc = mpd_run_rescan(mpd.conn, NULL);
if (uint_rc > 0) if (uint_rc > 0)
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
break; break;
case MPD_API_SMARTPLS_UPDATE:
uint_rc = mympd_smartpls_update_all();
if (uint_rc == 0)
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Smart Playlists updated\"}");
else
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Smart Playlists update failed\"}");
break;
case MPD_API_PLAYER_PAUSE: case MPD_API_PLAYER_PAUSE:
mpd_run_toggle_pause(mpd.conn); mpd_run_toggle_pause(mpd.conn);
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
@ -549,6 +556,7 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) {
switch(idle_event) { switch(idle_event) {
case MPD_IDLE_DATABASE: case MPD_IDLE_DATABASE:
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}"); len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}");
mympd_smartpls_update_all();
break; break;
case MPD_IDLE_STORED_PLAYLIST: case MPD_IDLE_STORED_PLAYLIST:
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}");
@ -628,6 +636,10 @@ void mympd_mpd_features() {
printf("MPD don't support stickers, disabling myMPD feature\n"); printf("MPD don't support stickers, disabling myMPD feature\n");
config.stickers = false; config.stickers = false;
} }
if (config.stickers == false && config.smartplaylists == true) {
printf("Stickers are disabled, disabling smartplaylists\n");
config.smartplaylists = false;
}
printf("MPD supported tags: "); printf("MPD supported tags: ");
mpd_send_list_tag_types(mpd.conn); mpd_send_list_tag_types(mpd.conn);
@ -736,6 +748,7 @@ void mympd_idle(struct mg_mgr *s, int timeout) {
mpd_connection_set_timeout(mpd.conn, mpd.timeout); mpd_connection_set_timeout(mpd.conn, mpd.timeout);
mpd.conn_state = MPD_CONNECTED; mpd.conn_state = MPD_CONNECTED;
mympd_mpd_features(); mympd_mpd_features();
mympd_smartpls_update_all();
if (mympd_state.jukeboxMode == true) if (mympd_state.jukeboxMode == true)
mympd_jukebox(); mympd_jukebox();
mpd_send_idle(mpd.conn); mpd_send_idle(mpd.conn);
@ -1805,7 +1818,7 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char
else { else {
len = json_printf(&out, "{type: queuesearch, data: ["); len = json_printf(&out, "{type: queuesearch, data: [");
while((song = mpd_recv_song(mpd.conn)) != NULL) { while ((song = mpd_recv_song(mpd.conn)) != NULL) {
entity_count ++; entity_count ++;
if (entity_count > offset && entity_count <= offset + MAX_ELEMENTS_PER_PAGE) { if (entity_count > offset && entity_count <= offset + MAX_ELEMENTS_PER_PAGE) {
if (entities_returned ++) if (entities_returned ++)
@ -1868,3 +1881,102 @@ void mympd_disconnect() {
mpd.conn_state = MPD_DISCONNECT; mpd.conn_state = MPD_DISCONNECT;
mympd_idle(NULL, 0); mympd_idle(NULL, 0);
} }
int mympd_smartpls_update_all() {
if (!config.smartplaylists)
return 0;
if (mympd_smartpls_update("like", "myMPDsmart-bestRated") != 0)
return 1;
if (mympd_smartpls_update("playCount", "myMPDsmart-mostPlayed") != 0)
return 1;
return 0;
}
int mympd_smartpls_update(char *sticker, char *playlist) {
struct mpd_pair *pair;
char *uri = NULL;
char *name;
char *p_value;
char *crap;
long value;
long value_max = 0;
size_t len = 0;
ssize_t read;
unsigned i = 0;
struct mpd_playlist *pl;
const char *plpath;
bool exists = false;
FILE *fp = fopen("/var/lib/mympd/tmp/playlist.tmp", "w");
if (fp == NULL) {
printf("Error opening /var/lib/mympd/tmp/playlist.tmp");
return 1;
}
if (!mpd_send_sticker_find(mpd.conn, "song", "", sticker)) {
LOG_ERROR_AND_RECOVER("mpd_run_rm");
return 1;
}
while ((pair = mpd_recv_pair(mpd.conn)) != NULL) {
if (strcmp(pair->name, "file") == 0) {
uri = strdup(pair->value);
}
else if (strcmp(pair->name, "sticker") == 0) {
name = strtok(strdup(pair->value), "=");
p_value = strtok(NULL, "=");
value = strtol(p_value, &crap, 10);
if (value > 1)
fprintf(fp, "%s::%ld\n", uri, value);
if (value > value_max)
value_max = value;
}
mpd_return_pair(mpd.conn, pair);
}
mpd_response_finish(mpd.conn);
fclose(fp);
if (!mpd_send_list_playlists(mpd.conn)) {
LOG_ERROR_AND_RECOVER("mpd_run_rm");
return 1;
}
while((pl = mpd_recv_playlist(mpd.conn)) != NULL) {
plpath = mpd_playlist_get_path(pl);
if (strcmp(playlist, plpath) == 0)
exists = true;
mpd_playlist_free(pl);
}
if (exists)
if (!mpd_run_rm(mpd.conn, playlist)) {
LOG_ERROR_AND_RECOVER("mpd_run_rm");
return 1;
}
if (value_max > 2)
value_max = value_max / 2;
fp = fopen("/var/lib/mympd/tmp/playlist.tmp", "r");
if (fp == NULL) {
printf("Error opening /var/lib/mympd/tmp/playlist.tmp");
return 1;
}
while ((read = getline(&uri, &len, fp)) != -1) {
name = strtok(uri, "::");
p_value = strtok(NULL, "::");
value = strtol(p_value, &crap, 10);
if (strcmp(sticker, "playCount") == 0) {
if (value <= value_max)
continue;
}
if (!mpd_run_playlist_add(mpd.conn, playlist, name)) {
LOG_ERROR_AND_RECOVER("mpd_run_rm");
fclose(fp);
free(uri);
return 1;
}
i++;
}
fclose(fp);
free(uri);
unlink("/var/lib/mympd/tmp/playlist.tmp");
printf("Updated %s with %u songs, minValue: %ld\n", playlist, i, value_max);
return 0;
}

View File

@ -72,6 +72,7 @@
X(MPD_API_PLAYLIST_RM_TRACK) \ X(MPD_API_PLAYLIST_RM_TRACK) \
X(MPD_API_PLAYLIST_LIST) \ X(MPD_API_PLAYLIST_LIST) \
X(MPD_API_PLAYLIST_CONTENT_LIST) \ X(MPD_API_PLAYLIST_CONTENT_LIST) \
X(MPD_API_SMARTPLS_UPDATE) \
X(MPD_API_DATABASE_SEARCH) \ X(MPD_API_DATABASE_SEARCH) \
X(MPD_API_DATABASE_UPDATE) \ X(MPD_API_DATABASE_UPDATE) \
X(MPD_API_DATABASE_RESCAN) \ X(MPD_API_DATABASE_RESCAN) \
@ -161,6 +162,7 @@ typedef struct {
bool stickers; bool stickers;
bool mixramp; bool mixramp;
const char* taglist; const char* taglist;
bool smartplaylists;
} t_config; } t_config;
t_config config; t_config config;
@ -196,6 +198,8 @@ void mympd_last_played_song_uri(const char *uri);
void mympd_last_played_song_id(int song_id); void mympd_last_played_song_id(int song_id);
void mympd_get_sticker(const char *uri, t_sticker *sticker); void mympd_get_sticker(const char *uri, t_sticker *sticker);
void mympd_jukebox(); void mympd_jukebox();
int mympd_smartpls_update_all();
int mympd_smartpls_update(char *sticker, char *playlist);
int mympd_get_updatedb_state(char *buffer); int mympd_get_updatedb_state(char *buffer);
int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length); int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length);
int mympd_put_outputs(char *buffer); int mympd_put_outputs(char *buffer);

View File

@ -162,6 +162,11 @@ static int inihandler(void* user, const char* section, const char* name, const c
p_config->stickers = true; p_config->stickers = true;
else else
p_config->stickers = false; p_config->stickers = false;
else if (MATCH("smartplaylists"))
if (strcmp(value, "true") == 0)
p_config->smartplaylists = true;
else
p_config->smartplaylists = false;
else if (MATCH("mixramp")) else if (MATCH("mixramp"))
if (strcmp(value, "true") == 0) if (strcmp(value, "true") == 0)
p_config->mixramp = true; p_config->mixramp = true;
@ -198,6 +203,7 @@ int main(int argc, char **argv) {
config.stickers = true; config.stickers = true;
config.mixramp = true; config.mixramp = true;
config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer"; config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer";
config.smartplaylists = true;
mpd.timeout = 3000; mpd.timeout = 3000;
mpd.last_update_sticker_song_id = -1; mpd.last_update_sticker_song_id = -1;