1 /* gmpc-dynamic-playlist (GMPC plugin)
2 * Copyright (C) 2009 Andre Klitzing <andre@incubo.de>
3 * Homepage: http://www.incubo.de
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "blacklist.h"
27 #include <glib/gi18n-lib.h>
28 #include <gmpc/playlist3-messages.h>
29 #include <gmpc/metadata.h>
32 #define BUFFER_SECONDS 5
35 static void tryToAdd_select(const status l_status
, mpd_Song
* l_song
);
37 static guint8 m_queue_songs
= 1;
38 static guint m_delay_source
= 0;
39 static guint8 m_delay_timeout
= 0;
40 static gint m_similar_songs_max
= 0;
41 static gint m_similar_artists_max
= 0;
42 static gint m_similar_genre_max
= 0;
43 static gint m_similar_artist_same
= TRUE
;
44 static gint m_similar_genre_same
= TRUE
;
45 static gboolean m_similar_songs
= FALSE
;
46 static gboolean m_similar_artists
= FALSE
;
47 static gboolean m_similar_genre
= FALSE
;
48 static gboolean m_search_genre
= FALSE
;
49 static searchStyle m_search_genre_style
= ArtistOf
;
50 static gboolean m_search_comment
= FALSE
;
51 static gboolean m_enabled_search
= FALSE
;
52 static gboolean m_is_searching
= FALSE
;
56 m_queue_songs
= (guint8
) cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "queue_songs", 1);
57 m_delay_timeout
= (guint8
) cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "delayTimeout", 0);
58 m_similar_songs_max
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "maxSongs", 20);
59 m_similar_artists_max
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "maxArtists", 30);
60 m_similar_genre_max
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "maxGenres", 20);
61 m_similar_songs
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "similar_songs", FALSE
);
62 m_similar_artists
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "similar_artists", FALSE
);
63 m_similar_genre
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "similar_genre", FALSE
);
64 m_similar_artist_same
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "similar_artist_same", TRUE
);
65 m_similar_genre_same
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "similar_genre_same", TRUE
);
66 m_search_genre
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "search_genre", FALSE
);
67 m_search_genre_style
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "search_genre_style", ArtistOf
);
68 m_search_comment
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "search_comment", FALSE
);
69 m_enabled_search
= cfg_get_single_value_as_int_with_default(config
, "dynamic-playlist", "similar_search", FALSE
);
72 static status
getNextStatus(status l_status
)
74 status ret
= NotFound
;
76 guint available
[STATUS_COUNT
-1];
79 for(i
= 1; i
< STATUS_COUNT
; ++i
) /* index 0 is Found/NotFound */
81 if( !((guint
)1 << i
& l_status
) )
82 available
[count
++] = i
;
88 ret
|= (guint
)1 << available
[ g_rand_int_range(m_rand
, 0, count
) ];
94 static void tryToAdd_artists(mpd_Song
* l_song
, MetaDataResult l_result
, MetaData
* l_data
, gpointer l_last_status
)
96 if(l_result
== META_DATA_FETCHING
)
99 status l_status
= GPOINTER_TO_INT(l_last_status
);
100 g_assert(!(l_status
& Artist
));
102 if(l_result
== META_DATA_AVAILABLE
)
104 g_assert(l_data
!= NULL
&& l_data
->type
== META_ARTIST_SIMILAR
);
107 strList
* artistList
= NULL
;
110 for(iter
= meta_data_get_text_list(l_data
); iter
!= NULL
&& maxIter
< m_similar_artists_max
; iter
= g_list_next(iter
), ++maxIter
)
112 const gchar
* artist
= (const gchar
*) iter
->data
;
113 artistList
= database_get_artists(artistList
, artist
, NULL
, &count
);
118 // add one artist to artistList (mostly because 'same artist' is also 'similar')
119 if(l_song
->artist
!= NULL
&& m_similar_artist_same
&& get_played_limit_artist() == 0)
120 artistList
= database_get_artists(artistList
, l_song
->artist
, NULL
, &count
);
121 if(database_tryToAdd_artists(&artistList
, count
))
125 if(artistList
!= NULL
)
126 free_strList(artistList
);
129 tryToAdd_select(l_status
, l_song
);
132 static dbList
* add_random_song(gint l_count
, dbList
* l_list
)
134 g_assert(l_count
> 0);
135 g_assert(l_list
!= NULL
);
137 gint selected
= g_rand_int_range(m_rand
, 0, l_count
);
140 for(listIter
= l_list
; i
< selected
; ++i
)
141 listIter
= g_list_next(listIter
);
143 dbSong
* song
= (dbSong
*) listIter
->data
;
144 mpd_playlist_add(connection
, song
->path
);
145 add_played_song(song
);
146 g_debug("Added via song | artist: %s | title: %s", song
->artist
, song
->title
);
148 // Remove added dbSong* from dbList so it won't be freed
149 return g_list_delete_link(l_list
, listIter
);
152 static void tryToAdd_songs(mpd_Song
* l_song
, MetaDataResult l_result
, MetaData
* l_data
, gpointer l_last_status
)
154 if(l_result
== META_DATA_FETCHING
)
157 status l_status
= GPOINTER_TO_INT(l_last_status
);
158 g_assert(!(l_status
& Song
));
160 if(l_result
== META_DATA_AVAILABLE
)
162 g_assert(l_data
!= NULL
&& l_data
->type
== META_SONG_SIMILAR
);
165 dbList
* songList
= NULL
;
168 for(iter
= meta_data_get_text_list(l_data
); iter
!= NULL
&& maxIter
< m_similar_songs_max
; iter
= g_list_next(iter
), ++maxIter
)
170 gchar
** song
= g_strsplit(iter
->data
, "::", 2);
171 if(song
[0] != NULL
&& song
[1] != NULL
)
172 songList
= database_get_songs(songList
, song
[0], song
[1], &count
);
178 songList
= add_random_song(count
, songList
);
180 free_dbList(songList
);
185 tryToAdd_select(l_status
, l_song
);
188 static void tryToAdd_multiple_genre(mpd_Song
* l_song
, MetaDataResult l_result
, MetaData
* l_data
, gpointer l_last_status
)
190 if(l_result
== META_DATA_FETCHING
)
193 status l_status
= (status
) l_last_status
;
194 g_assert(!(l_status
& Genre
));
196 if(l_result
== META_DATA_AVAILABLE
)
198 g_assert(l_data
!= NULL
&& l_data
->type
== META_GENRE_SIMILAR
);
201 strList
* artistList
= NULL
;
204 for(iter
= meta_data_get_text_list(l_data
); iter
!= NULL
&& maxIter
< m_similar_genre_max
; iter
= g_list_next(iter
), ++maxIter
)
206 const gchar
* genre
= (const gchar
*) iter
->data
;
207 artistList
= database_get_artists(artistList
, NULL
, genre
, &count
);
210 // add one genre to artistList (mostly because 'same genre' is also 'similar')
211 if(m_similar_genre_same
)
212 artistList
= database_get_artists(artistList
, NULL
, l_song
->genre
, &count
);
214 if(count
> 0 && database_tryToAdd_artists(&artistList
, count
))
217 if(artistList
!= NULL
)
218 free_strList(artistList
);
221 tryToAdd_select(l_status
, l_song
);
224 static void check_for_search(gboolean l_force_no_delay
)
226 g_idle_add((GSourceFunc
) dyn_check_search
, GINT_TO_POINTER(l_force_no_delay
));
229 void tryToAdd_select(const status l_status
, mpd_Song
* l_song
)
231 g_assert(l_song
!= NULL
);
232 g_assert(m_is_searching
);
236 m_is_searching
= FALSE
;
237 check_for_search(TRUE
);
241 status next
= getNextStatus(l_status
);
246 g_debug("Try similar song... %s - %s", l_song
->artist
, l_song
->title
);
247 meta_data_get_path_callback(l_song
, META_SONG_SIMILAR
, tryToAdd_songs
, GINT_TO_POINTER(l_status
));
249 else if(next
& Artist
)
251 g_debug("Try similar artist... %s", l_song
->artist
);
252 meta_data_get_path_callback(l_song
, META_ARTIST_SIMILAR
, tryToAdd_artists
, GINT_TO_POINTER(l_status
));
254 else if(next
& Genre
)
256 g_debug("Try similar genre... %s", l_song
->genre
);
257 meta_data_get_path_callback(l_song
, META_GENRE_SIMILAR
, tryToAdd_multiple_genre
, GINT_TO_POINTER(l_status
));
260 g_assert_not_reached();
264 if(m_search_genre
&& !m_similar_genre
&& l_song
->genre
!= NULL
&& !is_blacklisted_genre(l_song
->genre
) && tryToAdd_genre(l_song
->genre
))
265 g_debug("Added same genre song");
266 else if(m_search_comment
&& l_song
->comment
!= NULL
&& tryToAdd_comment(l_song
->comment
))
267 g_debug("Added same comment song");
268 else if(tryToAdd_random())
269 g_debug("Added random song");
272 playlist3_show_error_message(_("Dynamic search cannot find a new song"), ERROR_INFO
);
273 g_debug("Cannot find a new song");
275 m_is_searching
= FALSE
;
276 check_for_search(TRUE
);
280 static gboolean
tryToAdd_random_song(dbList
* l_songList
, gint l_count
)
284 g_assert(l_songList
!= NULL
);
285 l_songList
= add_random_song(l_count
, l_songList
);
287 if(l_songList
!= NULL
)
288 free_dbList(l_songList
);
296 static gboolean
tryToAdd_genre_songs(const gchar
* l_genre
)
298 g_assert(l_genre
!= NULL
);
301 dbList
* songList
= database_get_songs_genre(NULL
, l_genre
, &count
);
302 return tryToAdd_random_song(songList
, count
);
305 static gboolean
tryToAdd_genre_artists(const gchar
* l_genre
)
307 gboolean ret
= FALSE
;
309 strList
* artistList
= database_get_artists(NULL
, NULL
, l_genre
, &count
);
311 ret
= database_tryToAdd_artists(&artistList
, count
);
313 if(artistList
!= NULL
)
314 free_strList(artistList
);
319 gboolean
tryToAdd_genre(const gchar
* l_genre
)
321 if(m_search_genre_style
== ArtistOf
)
322 return tryToAdd_genre_artists(l_genre
);
323 else if(m_search_genre_style
== Same
)
324 return tryToAdd_genre_songs(l_genre
);
326 g_assert_not_reached();
330 gboolean
tryToAdd_comment(const gchar
* l_comment
)
332 g_assert(l_comment
!= NULL
);
335 dbList
* songList
= database_get_songs_comment(NULL
, l_comment
, &count
);
336 return tryToAdd_random_song(songList
, count
);
339 gboolean
tryToAdd_random()
341 return tryToAdd_genre_artists(NULL
);
344 static gboolean
search_delayed(mpd_Song
* l_song
)
346 search_start(l_song
);
351 static void set_search_delay(mpd_Song
* l_song
)
353 g_assert(l_song
!= NULL
);
354 reset_search_delay();
357 if(l_song
->time
== MPD_SONG_NO_TIME
|| l_song
->time
> m_delay_timeout
+ BUFFER_SECONDS
)
358 timeout
= m_delay_timeout
;
360 timeout
= (guint
) l_song
->time
- BUFFER_SECONDS
;
362 m_delay_source
= g_timeout_add_seconds(timeout
,
363 (GSourceFunc
) search_delayed
, l_song
);
366 m_delay_source = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
367 m_delay_timeout, (GSourceFunc) findSimilar_delayed,
368 mpd_songDup(l_song), (GDestroyNotify) mpd_freeSong);
372 void reset_search_delay()
374 if(m_delay_source
!= 0)
375 g_source_remove(m_delay_source
);
378 void search(mpd_Song
* l_song
, gint l_remains
, gboolean l_force_no_delay
)
380 g_assert(l_song
!= NULL
);
381 g_assert(l_remains
>= 0);
383 if(l_remains
< m_queue_songs
&& !m_is_searching
)
385 if(m_delay_timeout
> 0 && !l_force_no_delay
)
386 set_search_delay(l_song
);
388 search_start(l_song
);
390 else if(m_delay_timeout
> 0)
391 reset_search_delay();
396 if(!dyn_get_enabled())
398 playlist3_show_error_message(_("Dynamic playlist is disabled"), ERROR_INFO
);
404 playlist3_show_error_message(_("Dynamic search is already busy"), ERROR_INFO
);
408 mpd_Song
* curSong
= mpd_playlist_get_current_song(connection
);
411 playlist3_show_error_message(_("You need to play a song that will be used"), ERROR_INFO
);
415 search_start(curSong
);
418 void search_start(mpd_Song
* l_song
)
420 g_assert(l_song
!= NULL
);
421 g_assert(!m_is_searching
);
423 m_is_searching
= TRUE
;
424 status start
= NotFound
;
425 if(!m_similar_songs
|| l_song
->artist
== NULL
|| l_song
->title
== NULL
)
428 if(!m_similar_artists
|| l_song
->artist
== NULL
)
431 if(!m_similar_genre
|| l_song
->genre
== NULL
)
434 g_debug("Search | song: %d | artist: %d | genre: %d | artist: %s | title: %s | genre: %s",
435 !(start
& Song
), !(start
& Artist
), !(start
& Genre
),
436 l_song
->artist
, l_song
->title
, l_song
->genre
);
438 tryToAdd_select(start
, l_song
);
441 gboolean
is_searching()
443 return m_is_searching
;
446 gboolean
is_search_delayed()
448 return m_delay_source
!= 0;
451 gboolean
will_search_delay()
453 return m_delay_timeout
> 0;
456 gboolean
get_search_active()
458 return m_enabled_search
;
461 void set_search_active(gboolean l_value
)
463 if(l_value
== m_enabled_search
)
466 m_enabled_search
= l_value
;
467 cfg_set_single_value_as_int(config
, "dynamic-playlist", "similar_search", m_enabled_search
);
471 check_for_search(FALSE
);
473 reset_search_delay();
476 void set_search_active_easy(G_GNUC_UNUSED gpointer l_data
, const gchar
* l_param
)
478 g_assert(l_param
!= NULL
);
480 if(g_str_has_prefix(l_param
, _("on")))
481 set_search_active(TRUE
);
482 else if(g_str_has_prefix(l_param
, _("off")))
483 set_search_active(FALSE
);
485 set_search_active(!m_enabled_search
);
488 void set_local_search_genre(gboolean l_value
)
490 m_search_genre
= l_value
;
491 cfg_set_single_value_as_int(config
, "dynamic-playlist", "search_genre", m_search_genre
);
494 gboolean
get_local_search_genre()
496 return m_search_genre
;
499 void set_local_search_genre_style(searchStyle l_value
)
501 m_search_genre_style
= l_value
;
502 cfg_set_single_value_as_int(config
, "dynamic-playlist", "search_genre_style", m_search_genre_style
);
505 searchStyle
get_local_search_genre_style()
507 return m_search_genre_style
;
510 void set_local_search_comment(gboolean l_value
)
512 m_search_comment
= l_value
;
513 cfg_set_single_value_as_int(config
, "dynamic-playlist", "search_comment", m_search_comment
);
516 gboolean
get_local_search_comment()
518 return m_search_comment
;
521 guint8
get_queue_songs()
523 return m_queue_songs
;
526 void set_queue_songs(guint8 l_value
)
528 m_queue_songs
= l_value
;
529 cfg_set_single_value_as_int(config
, "dynamic-playlist", "queue_songs", m_queue_songs
);
532 guint8
get_delay_time()
534 return m_delay_timeout
;
537 void set_delay_time(guint8 l_value
)
539 m_delay_timeout
= l_value
;
540 cfg_set_single_value_as_int(config
, "dynamic-playlist", "delayTimeout", m_delay_timeout
);
543 gboolean
get_search_artist()
545 return m_similar_artists
;
548 void set_search_artist(gboolean l_value
)
550 m_similar_artists
= l_value
;
551 cfg_set_single_value_as_int(config
, "dynamic-playlist", "similar_artists", m_similar_artists
);
554 gboolean
get_search_artist_same()
556 return m_similar_artist_same
;
559 void set_search_artist_same(gboolean l_value
)
561 m_similar_artist_same
= l_value
;
562 cfg_set_single_value_as_int(config
, "dynamic-playlist", "similar_artist_same", m_similar_artist_same
);
565 gint
get_search_artist_max()
567 return m_similar_artists_max
;
570 void set_search_artist_max(gint l_value
)
572 m_similar_artists_max
= l_value
;
573 cfg_set_single_value_as_int(config
, "dynamic-playlist", "maxArtists", m_similar_artists_max
);
576 gboolean
get_search_song()
578 return m_similar_songs
;
581 void set_search_song(gboolean l_value
)
583 m_similar_songs
= l_value
;
584 cfg_set_single_value_as_int(config
, "dynamic-playlist", "similar_songs", m_similar_songs
);
587 gint
get_search_song_max()
589 return m_similar_songs_max
;
592 void set_search_song_max(gint l_value
)
594 m_similar_songs_max
= l_value
;
595 cfg_set_single_value_as_int(config
, "dynamic-playlist", "maxSongs", m_similar_songs_max
);
598 gboolean
get_search_genre()
600 return m_similar_genre
;
603 void set_search_genre(gboolean l_value
)
605 m_similar_genre
= l_value
;
606 cfg_set_single_value_as_int(config
, "dynamic-playlist", "similar_genre", m_similar_genre
);
609 gboolean
get_search_genre_same()
611 return m_similar_genre_same
;
614 void set_search_genre_same(gboolean l_value
)
616 m_similar_genre_same
= l_value
;
617 cfg_set_single_value_as_int(config
, "dynamic-playlist", "similar_genre_same", m_similar_genre_same
);
620 gint
get_search_genre_max()
622 return m_similar_genre_max
;
625 void set_search_genre_max(gint l_value
)
627 m_similar_genre_max
= l_value
;
628 cfg_set_single_value_as_int(config
, "dynamic-playlist", "maxGenres", m_similar_genre_max
);
631 /* vim:set ts=4 sw=4: */