Add/Update translations
[gmpc-dynamic-playlist.git] / src / search.c
blob6fbd9ec9e258b6b3b2223cf0c7d3c9c3c084144a
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.
20 #include "search.h"
21 #include "blacklist.h"
22 #include "played.h"
23 #include "database.h"
24 #include "plugin.h"
25 #include "prefs.h"
26 #include "icon.h"
27 #include <glib/gi18n-lib.h>
28 #include <gmpc/playlist3-messages.h>
29 #include <gmpc/metadata.h>
30 #include "defaults.h"
32 #define BUFFER_SECONDS 5
33 extern GRand* m_rand;
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;
54 void init_search()
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];
77 gint count = 0;
78 guint i;
79 for(i = 1; i < STATUS_COUNT; ++i) /* index 0 is Found/NotFound */
81 if( !((guint)1 << i & l_status) )
82 available[count++] = i;
85 if(count > 0)
87 ret = Found;
88 ret |= (guint)1 << available[ g_rand_int_range(m_rand, 0, count) ];
91 return ret;
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)
97 return;
99 status l_status = GPOINTER_TO_INT(l_last_status);
100 g_assert(!(l_status & Artist));
101 l_status |= Artist;
102 if(l_result == META_DATA_AVAILABLE)
104 g_assert(l_data != NULL && l_data->type == META_ARTIST_SIMILAR);
106 gint count = 0;
107 strList* artistList = NULL;
108 gint maxIter = 0;
109 const GList* iter;
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);
116 if(count > 0)
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))
122 l_status |= Found;
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);
138 gint i = 0;
139 dbList* listIter;
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)
155 return;
157 status l_status = GPOINTER_TO_INT(l_last_status);
158 g_assert(!(l_status & Song));
159 l_status |= Song;
160 if(l_result == META_DATA_AVAILABLE)
162 g_assert(l_data != NULL && l_data->type == META_SONG_SIMILAR);
164 gint count = 0;
165 dbList* songList = NULL;
166 gint maxIter = 0;
167 const GList* iter;
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);
173 g_strfreev(song);
176 if(count > 0)
178 songList = add_random_song(count, songList);
179 if(songList != NULL)
180 free_dbList(songList);
181 l_status |= Found;
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)
191 return;
193 status l_status = (status) l_last_status;
194 g_assert(!(l_status & Genre));
195 l_status |= Genre;
196 if(l_result == META_DATA_AVAILABLE)
198 g_assert(l_data != NULL && l_data->type == META_GENRE_SIMILAR);
200 gint count = 0;
201 strList* artistList = NULL;
202 gint maxIter = 0;
203 const GList* iter;
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))
215 l_status |= Found;
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);
234 if(l_status & Found)
236 m_is_searching = FALSE;
237 check_for_search(TRUE);
238 return;
241 status next = getNextStatus(l_status);
242 if(next & Found)
244 if(next & Song)
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));
259 else
260 g_assert_not_reached();
262 else
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");
270 else
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)
282 if(l_count > 0)
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);
290 return TRUE;
293 return FALSE;
296 static gboolean tryToAdd_genre_songs(const gchar* l_genre)
298 g_assert(l_genre != NULL);
300 gint count = 0;
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;
308 gint count = 0;
309 strList* artistList = database_get_artists(NULL, NULL, l_genre, &count);
310 if(count > 0)
311 ret = database_tryToAdd_artists(&artistList, count);
313 if(artistList != NULL)
314 free_strList(artistList);
316 return ret;
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();
327 return FALSE;
330 gboolean tryToAdd_comment(const gchar* l_comment)
332 g_assert(l_comment != NULL);
334 gint count = 0;
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);
347 m_delay_source = 0;
348 return FALSE;
351 static void set_search_delay(mpd_Song* l_song)
353 g_assert(l_song != NULL);
354 reset_search_delay();
356 guint timeout;
357 if(l_song->time == MPD_SONG_NO_TIME || l_song->time > m_delay_timeout + BUFFER_SECONDS)
358 timeout = m_delay_timeout;
359 else
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);
387 else
388 search_start(l_song);
390 else if(m_delay_timeout > 0)
391 reset_search_delay();
394 void search_easy()
396 if(!dyn_get_enabled())
398 playlist3_show_error_message(_("Dynamic playlist is disabled"), ERROR_INFO);
399 return;
402 if(m_is_searching)
404 playlist3_show_error_message(_("Dynamic search is already busy"), ERROR_INFO);
405 return;
408 mpd_Song* curSong = mpd_playlist_get_current_song(connection);
409 if(curSong == NULL)
411 playlist3_show_error_message(_("You need to play a song that will be used"), ERROR_INFO);
412 return;
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)
426 start |= Song;
428 if(!m_similar_artists || l_song->artist == NULL)
429 start |= Artist;
431 if(!m_similar_genre || l_song->genre == NULL)
432 start |= Genre;
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)
464 return;
466 m_enabled_search = l_value;
467 cfg_set_single_value_as_int(config, "dynamic-playlist", "similar_search", m_enabled_search);
468 reload_menu_list();
469 reload_icon();
470 if(m_enabled_search)
471 check_for_search(FALSE);
472 else
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);
484 else
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: */