Mercurial > audlegacy-plugins
annotate src/streambrowser/streambrowser.c @ 2811:7977bdc02664
added GPL headers
| author | Calin Crisan ccrisan@gmail.com |
|---|---|
| date | Sat, 12 Jul 2008 13:55:57 +0300 |
| parents | b1e4929e0990 |
| children | cc6f02424609 |
| rev | line source |
|---|---|
| 2811 | 1 /* |
| 2 * Audacious Streambrowser Plugin | |
| 3 * | |
| 4 * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com> | |
| 5 * | |
| 6 * This program is free software; you can redistribute it and/or modify | |
| 7 * it under the terms of the GNU General Public License as published by | |
| 8 * the Free Software Foundation; under version 3 of the License. | |
| 9 * | |
| 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. | |
| 14 * | |
| 15 * You should have received a copy of the GNU General Public License | |
| 16 * along with this program. If not, see <http://www.gnu.org/licenses>. | |
| 17 */ | |
| 18 | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
19 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
20 #include <stdlib.h> |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
21 #include <gtk/gtk.h> |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
22 #include <audacious/plugin.h> |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
23 #include <audacious/ui_plugin_menu.h> |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
24 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
25 #include "streambrowser.h" |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
26 #include "streamdir.h" |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
27 #include "shoutcast.h" |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
28 #include "gui/streambrowser_win.h" |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
29 #include "gui/about_win.h" |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
30 |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
31 |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
32 typedef struct { |
| 2788 | 33 streamdir_t *streamdir; |
| 34 category_t *category; | |
| 35 streaminfo_t *streaminfo; | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
36 } update_thread_data_t; |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
37 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
38 |
| 2788 | 39 static void sb_init(); |
| 40 static void sb_about(); | |
| 41 static void sb_configure(); | |
| 42 static void sb_cleanup(); | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
43 |
| 2788 | 44 static void gui_init(); |
| 45 static void gui_done(); | |
| 46 static void config_load(); | |
| 47 static void config_save(); | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
48 |
| 2788 | 49 static void streamdir_update(streamdir_t *streamdir, category_t *category, streaminfo_t *streaminfo); |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
50 static gpointer update_thread_core(gpointer user_data); |
| 2788 | 51 static void streaminfo_add_to_playlist(streaminfo_t *streaminfo); |
| 52 static void on_plugin_services_menu_item_click(); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
53 |
| 2788 | 54 static GtkWidget *playlist_menu_item; |
| 55 static GtkWidget *main_menu_item; | |
| 56 static GQueue *update_thread_data_queue = NULL; | |
| 57 static GMutex *update_thread_mutex = NULL; | |
| 58 static gint update_thread_count = 0; | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
59 |
| 2788 | 60 streambrowser_cfg_t streambrowser_cfg; |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
61 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
62 static GeneralPlugin sb_plugin = |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
63 { |
| 2788 | 64 .description = "Stream Browser", |
| 65 .init = sb_init, | |
| 66 .about = sb_about, | |
| 67 .configure = sb_configure, | |
| 68 .cleanup = sb_cleanup | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
69 }; |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
70 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
71 GeneralPlugin *sb_gplist[] = |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
72 { |
| 2788 | 73 &sb_plugin, |
| 74 NULL | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
75 }; |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
76 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
77 SIMPLE_GENERAL_PLUGIN(streambrowser, sb_gplist); |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
78 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
79 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
80 void debug(const char *fmt, ...) |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
81 { |
| 2788 | 82 if (streambrowser_cfg.debug) { |
| 83 va_list ap; | |
| 84 fprintf(stderr, "* streambrowser: "); | |
| 85 va_start(ap, fmt); | |
| 86 vfprintf(stderr, fmt, ap); | |
| 87 va_end(ap); | |
| 88 } | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
89 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
90 |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
91 void failure(const char *fmt, ...) |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
92 { |
| 2788 | 93 va_list ap; |
| 94 fprintf(stderr, "! streambrowser: "); | |
| 95 va_start(ap, fmt); | |
| 96 vfprintf(stderr, fmt, ap); | |
| 97 va_end(ap); | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
98 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
99 |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
100 gboolean fetch_remote_to_local_file(gchar *remote_url, gchar *local_url) |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
101 { |
| 2788 | 102 VFSFile *remote_file = aud_vfs_fopen(remote_url, "r"); |
| 103 if (remote_file == NULL) { | |
| 104 failure("failed to fetch file '%s'\n", remote_url); | |
| 105 return FALSE; | |
| 106 } | |
| 107 | |
| 108 VFSFile *local_file = aud_vfs_fopen(local_url, "w"); | |
| 109 if (local_file == NULL) { | |
| 110 aud_vfs_fclose(remote_file); | |
| 111 | |
| 112 failure("failed to create local file '%s'\n", local_file); | |
| 113 return FALSE; | |
| 114 } | |
| 115 | |
| 116 unsigned char buff[DEF_BUFFER_SIZE]; | |
| 117 int size; | |
| 118 while (!aud_vfs_feof(remote_file)) { | |
| 119 size = aud_vfs_fread(buff, 1, DEF_BUFFER_SIZE, remote_file); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
120 |
| 2788 | 121 // i don't know why aud_vfs_feof() doesn't ever return TRUE |
| 122 // so this is a workaround to properly end the loop | |
| 123 if (size == 0) | |
| 124 break; | |
| 125 | |
| 126 size = aud_vfs_fwrite(buff, 1, size, local_file); | |
| 127 if (size == 0) { | |
| 128 aud_vfs_fclose(local_file); | |
| 129 aud_vfs_fclose(remote_file); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
130 |
| 2788 | 131 failure("failed to write to local file '%s'\n", local_file); |
| 132 return FALSE; | |
| 133 } | |
| 134 } | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
135 |
| 2788 | 136 aud_vfs_fclose(local_file); |
| 137 aud_vfs_fclose(remote_file); | |
| 138 | |
| 139 return TRUE; | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
140 } |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
141 |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
142 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
143 static void sb_init() |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
144 { |
| 2788 | 145 /* workaround to print sb_init() */ |
| 146 streambrowser_cfg.debug = TRUE; | |
| 147 debug("sb_init()\n"); | |
| 148 streambrowser_cfg.debug = FALSE; | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
149 |
| 2788 | 150 config_load(); |
| 151 gui_init(); | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
152 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
153 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
154 static void sb_about() |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
155 { |
| 2788 | 156 debug("sb_about()\n"); |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
157 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
158 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
159 static void sb_configure() |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
160 { |
| 2788 | 161 debug("sb_configure()\n"); |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
162 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
163 |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
164 static void sb_cleanup() |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
165 { |
| 2788 | 166 debug("sb_cleanup()\n"); |
| 167 | |
| 168 gui_done(); | |
| 169 config_save(); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
170 } |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
171 |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
172 static void gui_init() |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
173 { |
| 2788 | 174 /* the plugin services menu */ |
|
2790
c156102069ae
background fetching of streaming info is now indicated in gui
Calin Crisan ccrisan@gmail.com
parents:
2788
diff
changeset
|
175 playlist_menu_item = gtk_image_menu_item_new_with_label(_("Streambrowser")); |
|
2791
f9c6a9cb442e
streambrowser has now an icon
Calin Crisan ccrisan@gmail.com
parents:
2790
diff
changeset
|
176 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(playlist_menu_item), gtk_image_new_from_file(STREAMBROWSER_ICON_SMALL)); |
| 2788 | 177 gtk_widget_show(playlist_menu_item); |
| 178 g_signal_connect(G_OBJECT(playlist_menu_item), "activate", G_CALLBACK(on_plugin_services_menu_item_click), NULL); | |
| 179 audacious_menu_plugin_item_add(AUDACIOUS_MENU_PLAYLIST_RCLICK, playlist_menu_item); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
180 |
|
2790
c156102069ae
background fetching of streaming info is now indicated in gui
Calin Crisan ccrisan@gmail.com
parents:
2788
diff
changeset
|
181 main_menu_item = gtk_image_menu_item_new_with_label(_("Streambrowser")); |
|
2791
f9c6a9cb442e
streambrowser has now an icon
Calin Crisan ccrisan@gmail.com
parents:
2790
diff
changeset
|
182 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(main_menu_item), gtk_image_new_from_file(STREAMBROWSER_ICON_SMALL)); |
| 2788 | 183 gtk_widget_show(main_menu_item); |
| 184 g_signal_connect(G_OBJECT(main_menu_item), "activate", G_CALLBACK(on_plugin_services_menu_item_click), NULL); | |
| 185 audacious_menu_plugin_item_add(AUDACIOUS_MENU_MAIN, main_menu_item); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
186 |
| 2788 | 187 /* main streambrowser window */ |
| 188 streambrowser_win_init(); | |
| 189 streambrowser_win_set_update_function(streamdir_update); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
190 |
| 2788 | 191 /* others */ |
| 192 update_thread_mutex = g_mutex_new(); | |
| 193 update_thread_data_queue = g_queue_new(); | |
| 194 | |
| 195 debug("gui initialized\n"); | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
196 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
197 |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
198 static void gui_done() |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
199 { |
| 2788 | 200 /* the plugin services menu */ |
| 201 audacious_menu_plugin_item_remove(AUDACIOUS_MENU_PLAYLIST_RCLICK, playlist_menu_item); | |
| 202 audacious_menu_plugin_item_remove(AUDACIOUS_MENU_MAIN, main_menu_item); | |
| 203 | |
| 204 /* main streambrowser window */ | |
| 205 streambrowser_win_hide(); | |
| 206 streambrowser_win_done(); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
207 |
| 2788 | 208 /* others */ |
| 209 if (update_thread_mutex) | |
| 210 g_mutex_free(update_thread_mutex); | |
| 211 update_thread_mutex = NULL; | |
| 212 if (update_thread_data_queue) | |
| 213 g_queue_free(update_thread_data_queue); | |
| 214 update_thread_data_queue = NULL; | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
215 |
| 2788 | 216 debug("gui destroyed\n"); |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
217 } |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
218 |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
219 static void config_load() |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
220 { |
| 2788 | 221 streambrowser_cfg.debug = FALSE; |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
222 |
| 2788 | 223 mcs_handle_t *db; |
| 224 if ((db = aud_cfg_db_open()) == NULL) { | |
| 225 failure("failed to load configuration\n"); | |
| 226 return; | |
| 227 } | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
228 |
| 2788 | 229 aud_cfg_db_get_bool(db, "streambrowser", "debug", &streambrowser_cfg.debug); |
| 230 | |
| 231 aud_cfg_db_close(db); | |
| 232 | |
| 233 debug("configuration loaded\n"); | |
| 234 debug("debug = %d\n", streambrowser_cfg.debug); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
235 } |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
236 |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
237 static void config_save() |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
238 { |
| 2788 | 239 mcs_handle_t *db; |
| 240 if ((db = aud_cfg_db_open()) == NULL) { | |
| 241 failure("failed to save configuration\n"); | |
| 242 return; | |
| 243 } | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
244 |
| 2788 | 245 aud_cfg_db_set_bool(db, "streambrowser", "debug", streambrowser_cfg.debug); |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
246 |
| 2788 | 247 aud_cfg_db_close(db); |
| 248 | |
| 249 debug("configuration saved\n"); | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
250 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
251 |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
252 static void streamdir_update(streamdir_t *streamdir, category_t *category, streaminfo_t *streaminfo) |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
253 { |
| 2788 | 254 debug("requested streamdir update (streamdir = '%s', category = '%s', streaminfo = '%s')\n", |
| 255 streamdir == NULL ? "" : streamdir->name, | |
| 256 category == NULL ? "" : category->name, | |
| 257 streaminfo == NULL ? "" : streaminfo->name); | |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
258 |
| 2788 | 259 if (update_thread_count >= MAX_UPDATE_THREADS) { |
| 260 debug("another %d streamdir updates are pending, this request will be dropped\n", update_thread_count); | |
| 261 } | |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
262 else { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
263 g_mutex_lock(update_thread_mutex); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
264 |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
265 /* do we have a running thread? */ |
| 2788 | 266 if (update_thread_count > 0) { |
| 267 int i; | |
| 268 gboolean exists = FALSE; | |
| 269 update_thread_data_t *update_thread_data; | |
| 270 | |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
271 /* search for another identic update request */ |
| 2788 | 272 for (i = 0; i < g_queue_get_length(update_thread_data_queue); i++) { |
| 273 update_thread_data = g_queue_peek_nth(update_thread_data_queue, i); | |
| 274 if (update_thread_data->streamdir == streamdir && | |
| 275 update_thread_data->category == category && | |
| 276 update_thread_data->streaminfo == streaminfo) { | |
| 277 exists = TRUE; | |
| 278 break; | |
| 279 } | |
| 280 } | |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
281 |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
282 /* if no other similar request exists, we enqueue it */ |
| 2788 | 283 if (!exists) { |
| 284 debug("another %d streamdir updates are pending, this request will be queued\n", update_thread_count); | |
| 285 | |
| 286 update_thread_data = g_malloc(sizeof(update_thread_data_t)); | |
| 287 | |
| 288 update_thread_data->streamdir = streamdir; | |
| 289 update_thread_data->category = category; | |
| 290 update_thread_data->streaminfo = streaminfo; | |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
291 |
| 2788 | 292 g_queue_push_tail(update_thread_data_queue, update_thread_data); |
| 293 update_thread_count++; | |
| 294 } | |
| 295 else { | |
| 296 debug("this request is already present in the queue, dropping\n"); | |
| 297 } | |
| 298 } | |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
299 /* no thread is currently running, we start one */ |
| 2788 | 300 else { |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
301 debug("no other streamdir updates are pending, starting to process this request immediately\n"); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
302 |
| 2788 | 303 update_thread_data_t *data = g_malloc(sizeof(update_thread_data_t)); |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
304 |
| 2788 | 305 data->streamdir = streamdir; |
| 306 data->category = category; | |
| 307 data->streaminfo = streaminfo; | |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
308 |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
309 g_queue_push_tail(update_thread_data_queue, data); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
310 update_thread_count++; |
| 2788 | 311 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
312 g_thread_create((GThreadFunc) update_thread_core, NULL, FALSE, NULL); |
| 2788 | 313 } |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
314 |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
315 g_mutex_unlock(update_thread_mutex); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
316 } |
|
2570
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
317 } |
|
28498c0bde64
Initial commit for the streambrowser plugin
Calin Crisan ccrisan@gmail.com
parents:
diff
changeset
|
318 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
319 static gpointer update_thread_core(gpointer user_data) |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
320 { |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
321 debug("entering update thread core\n"); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
322 |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
323 /* try to get the last item in the queue */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
324 g_mutex_lock(update_thread_mutex); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
325 update_thread_data_t *data = NULL; |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
326 if (update_thread_count > 0) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
327 data = g_queue_pop_head(update_thread_data_queue); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
328 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
329 g_mutex_unlock(update_thread_mutex); |
| 2788 | 330 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
331 /* repetitively process the queue elements, until queue is empty */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
332 while (data != NULL && update_thread_count > 0) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
333 /* update a streaminfo - that is - add this streaminfo to playlist */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
334 if (data->streaminfo != NULL) { |
|
2797
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
335 gdk_threads_enter(); |
|
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
336 streambrowser_win_set_streaminfo_state(data->streamdir, data->category, data->streaminfo, TRUE); |
|
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
337 gdk_threads_leave(); |
|
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
338 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
339 streaminfo_add_to_playlist(data->streaminfo); |
|
2797
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
340 |
|
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
341 gdk_threads_enter(); |
|
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
342 streambrowser_win_set_streaminfo_state(data->streamdir, data->category, data->streaminfo, FALSE); |
|
11fb1503ee4b
double click adds stream to playlist; visual feedback when adding streams
Calin Crisan ccrisan@gmail.com
parents:
2795
diff
changeset
|
343 gdk_threads_leave(); |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
344 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
345 /* update a category */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
346 else if (data->category != NULL) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
347 /* shoutcast */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
348 if (strncmp(data->streamdir->name, SHOUTCAST_NAME, strlen(SHOUTCAST_NAME)) == 0) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
349 gdk_threads_enter(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
350 streambrowser_win_set_category_state(data->streamdir, data->category, TRUE); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
351 gdk_threads_leave(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
352 |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
353 shoutcast_category_fetch(data->category); |
|
2757
4ec0e13208de
added shoutcast icon; fixed some small bugs
Calin Crisan ccrisan@gmail.com
parents:
2735
diff
changeset
|
354 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
355 gdk_threads_enter(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
356 streambrowser_win_set_category(data->streamdir, data->category); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
357 streambrowser_win_set_category_state(data->streamdir, data->category, FALSE); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
358 gdk_threads_leave(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
359 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
360 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
361 /* update a streamdir */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
362 else if (data->streamdir != NULL) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
363 /* shoutcast */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
364 if (strncmp(data->streamdir->name, SHOUTCAST_NAME, strlen(SHOUTCAST_NAME)) == 0) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
365 streamdir_t *streamdir = shoutcast_streamdir_fetch(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
366 if (streamdir != NULL) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
367 gdk_threads_enter(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
368 streambrowser_win_set_streamdir(streamdir, SHOUTCAST_ICON); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
369 gdk_threads_leave(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
370 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
371 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
372 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
373 /* update all streamdirs */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
374 else { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
375 /* shoutcast */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
376 streamdir_t *shoutcast_streamdir = shoutcast_streamdir_fetch(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
377 if (shoutcast_streamdir != NULL) { |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
378 gdk_threads_enter(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
379 streambrowser_win_set_streamdir(shoutcast_streamdir, SHOUTCAST_ICON); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
380 gdk_threads_leave(); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
381 } |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
382 } |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
383 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
384 g_free(data); |
| 2788 | 385 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
386 g_mutex_lock(update_thread_mutex); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
387 update_thread_count--; |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
388 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
389 /* try to get the last item in the queue */ |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
390 if (update_thread_count > 0) |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
391 data = g_queue_pop_head(update_thread_data_queue); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
392 else |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
393 data = NULL; |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
394 g_mutex_unlock(update_thread_mutex); |
|
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
395 } |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
396 |
|
2795
4c9db0b2194f
added strcasestr implementation; improved searching and updating
Calin Crisan ccrisan@gmail.com
parents:
2791
diff
changeset
|
397 debug("leaving update thread core\n"); |
| 2788 | 398 |
| 399 return NULL; | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
400 } |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
401 |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
402 static void streaminfo_add_to_playlist(streaminfo_t *streaminfo) |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
403 { |
| 2788 | 404 debug("fetching stream playlist for station '%s' from '%s'\n", streaminfo->name, streaminfo->playlist_url); |
| 405 if (!fetch_remote_to_local_file(streaminfo->playlist_url, PLAYLIST_TEMP_FILE)) { | |
| 406 failure("shoutcast: stream playlist '%s' could not be downloaded to '%s'\n", streaminfo->playlist_url, PLAYLIST_TEMP_FILE); | |
| 407 return; | |
| 408 } | |
| 409 debug("stream playlist '%s' successfuly downloaded to '%s'\n", streaminfo->playlist_url, PLAYLIST_TEMP_FILE); | |
| 410 | |
| 2811 | 411 aud_playlist_add_url(aud_playlist_get_active(), PLAYLIST_TEMP_FILE); |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
412 } |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
413 |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
414 static void on_plugin_services_menu_item_click() |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
415 { |
| 2788 | 416 debug("on_plugin_services_menu_item_click()\n"); |
| 417 | |
| 418 streambrowser_win_show(); | |
| 419 streamdir_update(NULL, NULL, NULL); | |
|
2735
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
420 } |
|
6d6a3eb67510
some work on the streambrowser
Calin Crisan ccrisan@gmail.com
parents:
2570
diff
changeset
|
421 |
