1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implemetation files for podcast download manager
5 * Copyright (C) 2005 Renato Araujo Oliveira Filho - INdT <renato.filho@indt.org.br>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <glib/gi18n.h>
30 #include <glib/gstdio.h>
32 #include <libgnomevfs/gnome-vfs-uri.h>
34 #include "rb-preferences.h"
35 #include "eel-gconf-extensions.h"
36 #include "rb-podcast-manager.h"
37 #include "rb-file-helpers.h"
39 #include "rb-marshal.h"
41 #include "rhythmdb-query-model.h"
42 #include "rb-podcast-parse.h"
43 #include "rb-dialog.h"
44 #include "rb-metadata.h"
46 #define CONF_STATE_PODCAST_PREFIX CONF_PREFIX "/state/podcast"
47 #define CONF_STATE_PODCAST_DOWNLOAD_DIR CONF_STATE_PODCAST_PREFIX "/download_prefix"
48 #define CONF_STATE_PODCAST_DOWNLOAD_INTERVAL CONF_STATE_PODCAST_PREFIX "/download_interval"
49 #define CONF_STATE_PODCAST_DOWNLOAD_NEXT_TIME CONF_STATE_PODCAST_PREFIX "/download_next_time"
71 FEED_UPDATES_AVALIABLE
,
81 struct RBPodcastManagerPrivate
87 guint update_interval_notify_id
;
89 GMutex
*download_list_mutex
;
91 gboolean remove_files
;
93 GAsyncQueue
*event_queue
;
96 #define RB_PODCAST_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PODCAST_MANAGER, RBPodcastManagerPrivate))
98 /* used on event loop */
101 RBPodcastEventType type
;
102 RBPodcastChannel
*channel
;
103 } RBPodcastManagerEvent
;
105 /* used on donwload thread */
108 RBPodcastManager
*pd
;
109 RhythmDBEntry
*entry
;
110 GnomeVFSAsyncHandle
*read_handle
;
111 GnomeVFSURI
*write_uri
;
112 GnomeVFSURI
*read_uri
;
114 GMutex
*mutex_working
;
119 } RBPodcastManagerInfo
;
121 /* used on subscribe thread */
124 RBPodcastManager
*pd
;
126 } RBPodcastThreadInfo
;
128 static guint rb_podcast_manager_signals
[LAST_SIGNAL
] = { 0 };
131 static void rb_podcast_manager_class_init (RBPodcastManagerClass
*klass
);
132 static void rb_podcast_manager_init (RBPodcastManager
*dp
);
133 static GObject
*rb_podcast_manager_constructor (GType type
, guint n_construct_properties
,
134 GObjectConstructParam
*construct_properties
);
135 static void rb_podcast_manager_finalize (GObject
*object
);
136 static void rb_podcast_manager_set_property (GObject
*object
,
140 static void rb_podcast_manager_get_property (GObject
*object
,
144 static void rb_podcast_manager_copy_post (RBPodcastManager
*pd
);
145 static void rb_podcast_manager_download_file_info_cb (GnomeVFSAsyncHandle
*handle
,
147 RBPodcastManagerInfo
*data
);
148 static void rb_podcast_manager_abort_download (RBPodcastManagerInfo
*data
);
149 static gboolean
rb_podcast_manager_sync_head_cb (gpointer data
);
150 static gboolean
rb_podcast_manager_head_query_cb (GtkTreeModel
*query_model
,
153 RBPodcastManager
*data
);
154 static gboolean
rb_podcast_manager_save_metadata (RhythmDB
*db
,
155 RhythmDBEntry
*entry
,
157 static void rb_podcast_manager_db_entry_added_cb (RBPodcastManager
*pd
,
158 RhythmDBEntry
*entry
);
159 static void rb_podcast_manager_db_entry_deleted_cb (RBPodcastManager
*pd
,
160 RhythmDBEntry
*entry
);
161 static gboolean
rb_podcast_manager_next_file (RBPodcastManager
* pd
);
162 static void rb_podcast_manager_insert_feed (RBPodcastManager
*pd
, RBPodcastChannel
*data
);
163 static void rb_podcast_manager_abort_subscribe (RBPodcastManager
*pd
);
166 static gboolean
rb_podcast_manager_event_loop (RBPodcastManager
*pd
) ;
167 static gpointer
rb_podcast_manager_thread_parse_feed (RBPodcastThreadInfo
*info
);
169 /* async read file functions */
170 static guint
download_progress_cb (GnomeVFSXferProgressInfo
*info
,
172 static guint
download_progress_update_cb (GnomeVFSAsyncHandle
*handle
,
173 GnomeVFSXferProgressInfo
*info
,
176 /* internal functions */
177 static void download_info_free (RBPodcastManagerInfo
*data
);
178 static RBPodcastManagerInfo
*download_info_new (void);
179 static void start_job (RBPodcastManagerInfo
*data
);
180 static void end_job (RBPodcastManagerInfo
*data
);
181 static void cancel_job (RBPodcastManagerInfo
*pd
);
182 static void write_job_data (RBPodcastManagerInfo
*data
);
183 static void rb_podcast_manager_update_synctime (RBPodcastManager
*pd
);
184 static void rb_podcast_manager_config_changed (GConfClient
* client
,
189 G_DEFINE_TYPE (RBPodcastManager
, rb_podcast_manager
, G_TYPE_OBJECT
)
192 rb_podcast_manager_class_init (RBPodcastManagerClass
*klass
)
194 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
196 object_class
->constructor
= rb_podcast_manager_constructor
;
197 object_class
->finalize
= rb_podcast_manager_finalize
;
199 object_class
->set_property
= rb_podcast_manager_set_property
;
200 object_class
->get_property
= rb_podcast_manager_get_property
;
202 g_object_class_install_property (object_class
,
204 g_param_spec_object ("db",
210 rb_podcast_manager_signals
[STATUS_CHANGED
] =
211 g_signal_new ("status_changed",
212 G_OBJECT_CLASS_TYPE (object_class
),
214 G_STRUCT_OFFSET (RBPodcastManagerClass
, status_changed
),
216 rb_marshal_VOID__BOXED_ULONG
,
222 rb_podcast_manager_signals
[START_DOWNLOAD
] =
223 g_signal_new ("start_download",
224 G_OBJECT_CLASS_TYPE (object_class
),
226 G_STRUCT_OFFSET (RBPodcastManagerClass
, start_download
),
228 g_cclosure_marshal_VOID__BOXED
,
231 RHYTHMDB_TYPE_ENTRY
);
233 rb_podcast_manager_signals
[FINISH_DOWNLOAD
] =
234 g_signal_new ("finish_download",
235 G_OBJECT_CLASS_TYPE (object_class
),
237 G_STRUCT_OFFSET (RBPodcastManagerClass
, finish_download
),
239 g_cclosure_marshal_VOID__BOXED
,
242 RHYTHMDB_TYPE_ENTRY
);
244 rb_podcast_manager_signals
[FEED_UPDATES_AVALIABLE
] =
245 g_signal_new ("feed_updates_avaliable",
246 G_OBJECT_CLASS_TYPE (object_class
),
248 G_STRUCT_OFFSET (RBPodcastManagerClass
, feed_updates_avaliable
),
250 g_cclosure_marshal_VOID__BOXED
,
253 RHYTHMDB_TYPE_ENTRY
);
255 rb_podcast_manager_signals
[PROCESS_ERROR
] =
256 g_signal_new ("process_error",
257 G_OBJECT_CLASS_TYPE (object_class
),
259 G_STRUCT_OFFSET (RBPodcastManagerClass
, process_error
),
261 g_cclosure_marshal_VOID__STRING
,
266 g_type_class_add_private (klass
, sizeof (RBPodcastManagerPrivate
));
270 rb_podcast_manager_init (RBPodcastManager
*pd
)
272 pd
->priv
= RB_PODCAST_MANAGER_GET_PRIVATE (pd
);
274 pd
->priv
->source_sync
= 0;
275 pd
->priv
->mutex_job
= g_mutex_new();
276 pd
->priv
->download_list_mutex
= g_mutex_new();
277 pd
->priv
->event_queue
= g_async_queue_new ();
279 eel_gconf_monitor_add (CONF_STATE_PODCAST_PREFIX
);
283 rb_podcast_manager_constructor (GType type
, guint n_construct_properties
,
284 GObjectConstructParam
*construct_properties
)
286 RBPodcastManager
*pd
;
288 pd
= RB_PODCAST_MANAGER (G_OBJECT_CLASS (rb_podcast_manager_parent_class
)
289 ->constructor (type
, n_construct_properties
, construct_properties
));
291 pd
->priv
->update_interval_notify_id
= eel_gconf_notification_add (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL
,
292 rb_podcast_manager_config_changed
,
295 return G_OBJECT (pd
);
300 rb_podcast_manager_finalize (GObject
*object
)
302 RBPodcastManager
*pd
;
303 g_return_if_fail (object
!= NULL
);
304 g_return_if_fail (RB_IS_PODCAST_MANAGER (object
));
306 pd
= RB_PODCAST_MANAGER(object
);
308 g_return_if_fail (pd
->priv
!= NULL
);
310 eel_gconf_monitor_remove (CONF_STATE_PODCAST_PREFIX
);
312 if (pd
->priv
->source_sync
) {
313 g_source_remove (pd
->priv
->source_sync
);
314 pd
->priv
->source_sync
= 0;
317 eel_gconf_notification_remove (pd
->priv
->update_interval_notify_id
);
319 if (pd
->priv
->download_list
) {
320 g_list_foreach (pd
->priv
->download_list
, (GFunc
)g_free
, NULL
);
321 g_list_free (pd
->priv
->download_list
);
324 g_mutex_free (pd
->priv
->mutex_job
);
325 g_mutex_free (pd
->priv
->download_list_mutex
);
326 g_async_queue_unref (pd
->priv
->event_queue
);
328 G_OBJECT_CLASS (rb_podcast_manager_parent_class
)->finalize (object
);
329 rb_debug ("Podcast Manager END");
333 rb_podcast_manager_set_property (GObject
*object
,
338 RBPodcastManager
*pd
= RB_PODCAST_MANAGER (object
);
343 g_signal_handlers_disconnect_by_func (G_OBJECT (pd
->priv
->db
),
344 G_CALLBACK (rb_podcast_manager_db_entry_added_cb
),
347 g_signal_handlers_disconnect_by_func (G_OBJECT (pd
->priv
->db
),
348 G_CALLBACK (rb_podcast_manager_db_entry_deleted_cb
),
353 pd
->priv
->db
= g_value_get_object (value
);
355 g_signal_connect_object (G_OBJECT (pd
->priv
->db
),
357 G_CALLBACK (rb_podcast_manager_db_entry_added_cb
),
358 pd
, G_CONNECT_SWAPPED
);
360 g_signal_connect_object (G_OBJECT (pd
->priv
->db
),
362 G_CALLBACK (rb_podcast_manager_db_entry_deleted_cb
),
363 pd
, G_CONNECT_SWAPPED
);
367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
372 rb_podcast_manager_get_property (GObject
*object
,
377 RBPodcastManager
*pd
= RB_PODCAST_MANAGER (object
);
381 g_value_set_object (value
, pd
->priv
->db
);
384 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
390 rb_podcast_manager_new (RhythmDB
*db
)
392 RBPodcastManager
*pd
;
394 pd
= g_object_new (RB_TYPE_PODCAST_MANAGER
, "db", db
, NULL
);
399 rb_podcast_manager_download_entry (RBPodcastManager
*pd
,
400 RhythmDBEntry
*entry
)
404 g_return_if_fail (RB_IS_PODCAST_MANAGER (pd
));
409 status
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_STATUS
);
410 if ((status
< RHYTHMDB_PODCAST_STATUS_COMPLETE
) ||
411 (status
== RHYTHMDB_PODCAST_STATUS_WAITING
)) {
412 RBPodcastManagerInfo
*data
;
413 if (status
< RHYTHMDB_PODCAST_STATUS_COMPLETE
) {
414 GValue status_val
= { 0, };
415 g_value_init (&status_val
, G_TYPE_ULONG
);
416 g_value_set_ulong (&status_val
, RHYTHMDB_PODCAST_STATUS_WAITING
);
417 rhythmdb_entry_set (pd
->priv
->db
, entry
, RHYTHMDB_PROP_STATUS
, &status_val
);
418 g_value_unset (&status_val
);
420 rb_debug ("Try insert entry for download.");
421 data
= download_info_new();
424 g_mutex_lock (pd
->priv
->download_list_mutex
);
425 pd
->priv
->download_list
= g_list_append (pd
->priv
->download_list
, data
);
426 g_mutex_unlock (pd
->priv
->download_list_mutex
);
427 g_idle_add ((GtkFunction
) rb_podcast_manager_next_file
, pd
);
432 rb_podcast_manager_entry_downloaded (RhythmDBEntry
*entry
)
435 const gchar
*file_name
;
436 RhythmDBEntryType type
= rhythmdb_entry_get_entry_type (entry
);
438 g_assert (type
== RHYTHMDB_ENTRY_TYPE_PODCAST_POST
);
440 status
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_STATUS
);
441 file_name
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_MOUNTPOINT
);
443 return (status
!= RHYTHMDB_PODCAST_STATUS_ERROR
&& file_name
!= NULL
);
447 rb_podcast_manager_start_sync (RBPodcastManager
*pd
)
451 g_return_if_fail (RB_IS_PODCAST_MANAGER (pd
));
453 if (pd
->priv
->next_time
> 0) {
454 next_time
= pd
->priv
->next_time
;
456 next_time
= eel_gconf_get_integer (CONF_STATE_PODCAST_DOWNLOAD_NEXT_TIME
);
460 if (pd
->priv
->source_sync
!= 0) {
461 g_source_remove (pd
->priv
->source_sync
);
462 pd
->priv
->source_sync
= 0;
464 next_time
= next_time
- ((int)time (NULL
));
465 if (next_time
<= 0) {
466 rb_podcast_manager_update_feeds (pd
);
467 pd
->priv
->next_time
= 0;
468 rb_podcast_manager_update_synctime (pd
);
471 pd
->priv
->source_sync
= g_timeout_add (next_time
* 1000, (GSourceFunc
) rb_podcast_manager_sync_head_cb
, pd
);
477 rb_podcast_manager_sync_head_cb (gpointer data
)
479 RBPodcastManager
*pd
= RB_PODCAST_MANAGER (data
);
481 GDK_THREADS_ENTER ();
483 rb_podcast_manager_update_feeds (pd
);
484 pd
->priv
->source_sync
= 0;
485 pd
->priv
->next_time
= 0;
486 rb_podcast_manager_update_synctime (RB_PODCAST_MANAGER (data
));
487 GDK_THREADS_LEAVE ();
492 rb_podcast_manager_update_feeds (RBPodcastManager
*pd
)
494 GtkTreeModel
*query_model
;
496 g_return_if_fail (RB_IS_PODCAST_MANAGER (pd
));
498 query_model
= GTK_TREE_MODEL (rhythmdb_query_model_new_empty (pd
->priv
->db
));
500 rhythmdb_do_full_query (pd
->priv
->db
,
501 RHYTHMDB_QUERY_RESULTS (query_model
),
502 RHYTHMDB_QUERY_PROP_EQUALS
,
503 RHYTHMDB_PROP_TYPE
, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
,
506 gtk_tree_model_foreach (query_model
,
507 (GtkTreeModelForeachFunc
) rb_podcast_manager_head_query_cb
,
510 g_object_unref (query_model
);
514 rb_podcast_manager_head_query_cb (GtkTreeModel
*query_model
,
517 RBPodcastManager
*manager
)
520 RhythmDBEntry
*entry
;
523 gtk_tree_model_get (query_model
, iter
, 0, &entry
, -1);
524 uri
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
525 status
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_STATUS
);
528 rb_podcast_manager_subscribe_feed (manager
, uri
);
530 rhythmdb_entry_unref (entry
);
536 rb_podcast_manager_next_file (RBPodcastManager
* pd
)
538 GDK_THREADS_ENTER ();
540 rb_debug ("try lock file_process mutex");
541 if (g_mutex_trylock (pd
->priv
->mutex_job
) == TRUE
) {
544 g_mutex_lock (pd
->priv
->download_list_mutex
);
545 size
= g_list_length (pd
->priv
->download_list
);
546 g_mutex_unlock (pd
->priv
->download_list_mutex
);
549 rb_podcast_manager_copy_post (pd
);
551 g_mutex_unlock (pd
->priv
->mutex_job
);
553 rb_debug ("not start");
556 GDK_THREADS_LEAVE ();
561 rb_podcast_manager_copy_post (RBPodcastManager
*pd
)
563 const char *location
;
564 RBPodcastManagerInfo
*data
;
567 /* get first element of list */
568 g_mutex_lock (pd
->priv
->download_list_mutex
);
569 data
= (RBPodcastManagerInfo
*) g_list_first (pd
->priv
->download_list
)->data
;
570 g_mutex_unlock (pd
->priv
->download_list_mutex
);
572 g_assert (data
!= NULL
);
573 g_assert (data
->entry
!= NULL
);
575 location
= rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
);
576 rb_debug ("processing %s", location
);
578 /* gnome-vfs currently doesn't handle HTTP query strings correctly.
579 * so we do it ourselves.
581 query_string
= strrchr (location
, '?');
582 if (query_string
!= NULL
) {
585 base_uri
= g_strdup (location
);
586 query_string
= strrchr (base_uri
, '?');
587 *query_string
++ = '\0';
588 rb_debug ("hiding query string %s from gnome-vfs", query_string
);
590 data
->read_uri
= gnome_vfs_uri_new (base_uri
);
591 if (data
->read_uri
!= NULL
) {
594 full_uri
= g_strdup_printf ("%s?%s",
595 data
->read_uri
->text
,
597 g_free (data
->read_uri
->text
);
598 data
->read_uri
->text
= full_uri
;
600 /* include the question mark in data->query_string to make
601 * the later check easier.
605 data
->query_string
= g_strdup (query_string
);
609 data
->read_uri
= gnome_vfs_uri_new (location
);
611 if (data
->read_uri
== NULL
) {
612 rb_debug ("Error downloading podcast: could not create remote uri");
613 rb_podcast_manager_abort_download (data
);
617 gnome_vfs_async_get_file_info (&data
->read_handle
,
618 g_list_prepend (NULL
, data
->read_uri
),
619 GNOME_VFS_FILE_INFO_FOLLOW_LINKS
,
620 GNOME_VFS_PRIORITY_DEFAULT
,
621 (GnomeVFSAsyncGetFileInfoCallback
) rb_podcast_manager_download_file_info_cb
,
626 rb_podcast_manager_download_file_info_cb (GnomeVFSAsyncHandle
*handle
,
628 RBPodcastManagerInfo
*data
)
630 GnomeVFSGetFileInfoResult
*result
= results
->data
;
631 char *local_file_name
;
632 char *local_file_path
;
636 rb_debug ("got file info results for %s",
637 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
));
639 if (result
->result
!= GNOME_VFS_OK
) {
643 g_value_init (&val
, G_TYPE_ULONG
);
644 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_ERROR
);
645 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
646 g_value_unset (&val
);
648 g_value_init (&val
, G_TYPE_STRING
);
649 g_value_set_string (&val
, gnome_vfs_result_to_string (result
->result
));
650 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
, &val
);
651 g_value_unset (&val
);
653 rhythmdb_commit (data
->pd
->priv
->db
);
655 rb_debug ("get_file_info request failed");
656 rb_podcast_manager_abort_download (data
);
660 /* construct download directory */
661 conf_dir_name
= rb_podcast_manager_get_podcast_dir (data
->pd
);
662 dir_name
= g_build_filename (conf_dir_name
,
663 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_ALBUM
),
665 g_free (conf_dir_name
);
667 if (g_mkdir_with_parents (dir_name
, 0750) == -1) {
668 rb_debug ("Could not create podcast download directory %s", dir_name
);
669 /* FIXME: display error to user */
671 rb_podcast_manager_abort_download (data
);
675 /* if the filename ends with the query string from the original URI,
678 if (data
->query_string
&&
679 g_str_has_suffix (result
->file_info
->name
, data
->query_string
)) {
680 local_file_name
= g_strdup (result
->file_info
->name
);
681 local_file_name
[strlen (local_file_name
) - strlen (data
->query_string
)] = '\0';
682 rb_debug ("removing query string \"%s\" -> local file name \"%s\"", data
->query_string
, local_file_name
);
684 local_file_name
= result
->file_info
->name
;
687 /* construct local filename */
688 local_file_path
= g_build_filename (dir_name
,
692 if (local_file_name
!= result
->file_info
->name
)
693 g_free (local_file_name
);
696 rb_debug ("creating file %s", local_file_path
);
698 data
->write_uri
= gnome_vfs_uri_new (local_file_path
);
699 if (data
->write_uri
== NULL
) {
700 g_warning ("Could not create local podcast URI for %s", local_file_path
);
701 rb_podcast_manager_abort_download (data
);
705 if (g_file_test (local_file_path
, G_FILE_TEST_EXISTS
)) {
707 GnomeVFSFileInfo
*local_info
;
708 GnomeVFSResult local_result
;
710 local_info
= gnome_vfs_file_info_new ();
711 local_result
= gnome_vfs_get_file_info (local_file_path
,
713 GNOME_VFS_FILE_INFO_FOLLOW_LINKS
);
714 local_size
= local_info
->size
;
715 gnome_vfs_file_info_unref (local_info
);
717 if (local_result
!= GNOME_VFS_OK
) {
718 g_warning ("Could not get info on downloaded podcast file %s",
720 rb_podcast_manager_abort_download (data
);
722 } else if (result
->file_info
->size
== local_size
) {
727 uri
= gnome_vfs_uri_to_string (data
->write_uri
, GNOME_VFS_URI_HIDE_NONE
);
728 canon_uri
= rb_canonicalise_uri (uri
);
731 rb_debug ("podcast %s already downloaded",
732 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
));
734 g_value_init (&val
, G_TYPE_ULONG
);
735 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_COMPLETE
);
736 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
737 g_value_unset (&val
);
739 g_value_init (&val
, G_TYPE_STRING
);
740 g_value_set_string (&val
, canon_uri
);
741 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_MOUNTPOINT
, &val
);
742 g_value_unset (&val
);
744 rb_podcast_manager_save_metadata (data
->pd
->priv
->db
, data
->entry
, canon_uri
);
745 rhythmdb_commit (data
->pd
->priv
->db
);
748 rb_podcast_manager_abort_download (data
);
750 } else if (result
->file_info
->size
> local_size
) {
751 /* TODO: support resume file */
752 rb_debug ("podcast episode already partially downloaded, but we can't resume downloads");
754 /* the local file is larger. replace it */
758 rb_debug ("starting download");
759 g_free (local_file_path
);
764 rb_podcast_manager_abort_download (RBPodcastManagerInfo
*data
)
766 RBPodcastManager
*mgr
= data
->pd
;
768 g_mutex_lock (mgr
->priv
->download_list_mutex
);
769 mgr
->priv
->download_list
= g_list_remove (mgr
->priv
->download_list
, (gconstpointer
) data
);
770 g_mutex_unlock (mgr
->priv
->download_list_mutex
);
772 download_info_free (data
);
774 g_mutex_unlock (mgr
->priv
->mutex_job
);
775 g_idle_add ((GtkFunction
) rb_podcast_manager_next_file
, mgr
);
779 rb_podcast_manager_subscribe_feed (RBPodcastManager
*pd
, const char *url
)
781 RBPodcastThreadInfo
*info
;
782 gchar
*valid_url
= gnome_vfs_make_uri_from_input (url
);
784 if (valid_url
== NULL
) {
785 rb_error_dialog (NULL
, _("Invalid URL"),
786 _("The URL \"%s\" is not valid, please check it."), url
);
790 RhythmDBEntry
*entry
= rhythmdb_entry_lookup_by_location (pd
->priv
->db
, valid_url
);
792 if (rhythmdb_entry_get_entry_type (entry
) != RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
) {
793 /* added as something else, probably iradio */
794 rb_error_dialog (NULL
, _("URL already added"),
795 _("The URL \"%s\" has already been added as a radio station. "
796 "If this is a podcast feed, please remove the radio station."), url
);
801 info
= g_new0 (RBPodcastThreadInfo
, 1);
803 info
->url
= valid_url
;
805 g_async_queue_ref (info
->pd
->priv
->event_queue
);
806 g_thread_create ((GThreadFunc
) rb_podcast_manager_thread_parse_feed
,
813 rb_podcast_manager_thread_parse_feed (RBPodcastThreadInfo
*info
)
815 RBPodcastManagerEvent
*event
= g_new0 (RBPodcastManagerEvent
, 1);
816 RBPodcastChannel
*feed
= g_new0 (RBPodcastChannel
, 1);
818 if (rb_podcast_parse_load_feed (feed
, info
->url
)) {
819 event
->channel
= feed
;
820 event
->type
= (feed
->title
== NULL
) ? EVENT_ERROR_FEED
: EVENT_INSERT_FEED
;
822 g_async_queue_push (info
->pd
->priv
->event_queue
, event
);
823 g_idle_add ((GSourceFunc
) rb_podcast_manager_event_loop
, info
->pd
);
832 rb_podcast_manager_add_post (RhythmDB
*db
,
835 const char *subtitle
,
836 const char *generator
,
838 const char *description
,
843 RhythmDBEntry
*entry
;
847 if (!uri
|| !name
|| !title
|| !date
|| !g_utf8_validate(uri
, -1, NULL
)) {
850 entry
= rhythmdb_entry_lookup_by_location (db
, uri
);
854 entry
= rhythmdb_entry_new (db
,
855 RHYTHMDB_ENTRY_TYPE_PODCAST_POST
,
860 g_value_init (&val
, G_TYPE_STRING
);
861 g_value_set_string (&val
, name
);
862 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_ALBUM
, &val
);
864 g_value_reset (&val
);
865 g_value_set_static_string (&val
, _("Podcast"));
866 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_GENRE
, &val
);
868 g_value_reset (&val
);
869 g_value_set_string (&val
, title
);
870 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_TITLE
, &val
);
872 g_value_reset (&val
);
874 g_value_set_string (&val
, subtitle
);
876 g_value_set_static_string (&val
, "");
877 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_SUBTITLE
, &val
);
879 g_value_reset (&val
);
881 g_value_set_string (&val
, description
);
883 g_value_set_static_string (&val
, "");
884 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DESCRIPTION
, &val
);
886 g_value_reset (&val
);
888 g_value_set_string (&val
, generator
);
890 g_value_set_static_string (&val
, "");
891 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_ARTIST
, &val
);
892 g_value_unset (&val
);
894 g_value_init (&val
, G_TYPE_ULONG
);
895 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_PAUSED
);
896 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &val
);
898 g_value_reset (&val
);
899 g_value_set_ulong (&val
, date
);
900 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_POST_TIME
, &val
);
902 g_value_reset (&val
);
903 g_value_set_ulong (&val
, duration
);
904 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DURATION
, &val
);
906 g_value_reset (&val
);
907 g_value_set_ulong (&val
, 0);
908 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LAST_PLAYED
, &val
);
911 g_get_current_time (&time
);
912 g_value_reset (&val
);
913 g_value_set_ulong (&val
, time
.tv_sec
);
914 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_FIRST_SEEN
, &val
);
915 g_value_unset (&val
);
917 /* initialize the rating */
918 g_value_init (&val
, G_TYPE_DOUBLE
);
919 g_value_set_double (&val
, 2.5);
920 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_RATING
, &val
);
921 g_value_unset (&val
);
923 g_value_init (&val
, G_TYPE_UINT64
);
924 g_value_set_uint64 (&val
, filesize
);
925 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_FILE_SIZE
, &val
);
926 g_value_unset (&val
);
932 rb_podcast_manager_save_metadata (RhythmDB
*db
, RhythmDBEntry
*entry
, const char *uri
)
934 RBMetaData
*md
= rb_metadata_new();
935 GError
*error
= NULL
;
939 rb_debug("Loading podcast metadata");
940 rb_metadata_load (md
, uri
, &error
);
943 /* this probably isn't an audio enclosure. or some other error */
944 g_value_init (&val
, G_TYPE_ULONG
);
945 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_ERROR
);
946 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &val
);
947 g_value_unset (&val
);
949 g_value_init (&val
, G_TYPE_STRING
);
950 g_value_set_string (&val
, error
->message
);
951 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
, &val
);
952 g_value_unset (&val
);
954 rhythmdb_commit (db
);
957 g_error_free (error
);
962 mime
= rb_metadata_get_mime (md
);
964 g_value_init (&val
, G_TYPE_STRING
);
965 g_value_set_string (&val
, mime
);
966 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_MIMETYPE
, &val
);
967 g_value_unset (&val
);
970 if (rb_metadata_get (md
,
971 RB_METADATA_FIELD_DURATION
,
973 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DURATION
, &val
);
974 g_value_unset (&val
);
977 if (rb_metadata_get (md
,
978 RB_METADATA_FIELD_BITRATE
,
980 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_BITRATE
, &val
);
981 g_value_unset (&val
);
984 rhythmdb_commit (db
);
991 rb_podcast_manager_db_entry_added_cb (RBPodcastManager
*pd
, RhythmDBEntry
*entry
)
993 RhythmDBEntryType type
= rhythmdb_entry_get_entry_type (entry
);
995 if (type
!= RHYTHMDB_ENTRY_TYPE_PODCAST_POST
)
998 rb_podcast_manager_download_entry (pd
, entry
);
1002 write_job_data (RBPodcastManagerInfo
*data
)
1006 RhythmDB
*db
= data
->pd
->priv
->db
;
1008 rb_debug ("in the write_job");
1010 g_value_init (&val
, G_TYPE_UINT64
);
1011 g_value_set_uint64 (&val
, data
->total_size
);
1012 rhythmdb_entry_set (db
, data
->entry
, RHYTHMDB_PROP_FILE_SIZE
, &val
);
1013 g_value_unset (&val
);
1015 g_value_init (&val
, G_TYPE_ULONG
);
1016 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_COMPLETE
);
1017 rhythmdb_entry_set (db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
1018 g_value_unset (&val
);
1020 rb_podcast_manager_save_metadata (db
, data
->entry
,
1021 gnome_vfs_uri_to_string (data
->write_uri
, GNOME_VFS_URI_HIDE_NONE
));
1023 rhythmdb_commit (db
);
1027 download_info_free (RBPodcastManagerInfo
*data
)
1029 if (data
->write_uri
) {
1030 gnome_vfs_uri_unref (data
->write_uri
);
1031 data
->write_uri
= NULL
;
1034 if (data
->read_uri
) {
1035 gnome_vfs_uri_unref (data
->read_uri
);
1036 data
->read_uri
= NULL
;
1038 if (data
->query_string
) {
1039 g_free (data
->query_string
);
1040 data
->query_string
= NULL
;
1043 g_mutex_free (data
->mutex_working
);
1048 static RBPodcastManagerInfo
*
1049 download_info_new (void)
1051 RBPodcastManagerInfo
*data
= g_new0 (RBPodcastManagerInfo
, 1);
1052 data
->mutex_working
= g_mutex_new ();
1057 start_job (RBPodcastManagerInfo
*data
)
1060 GList
*source_uri_list
= NULL
;
1061 GList
*target_uri_list
= NULL
;
1063 rb_debug ("start job");
1065 GDK_THREADS_ENTER ();
1066 g_signal_emit (data
->pd
, rb_podcast_manager_signals
[START_DOWNLOAD
],
1068 GDK_THREADS_LEAVE ();
1070 source_uri_list
= g_list_prepend (source_uri_list
, data
->read_uri
);
1071 target_uri_list
= g_list_prepend (target_uri_list
, data
->write_uri
);
1073 g_mutex_lock (data
->mutex_working
);
1075 rb_debug ("start async copy");
1076 gnome_vfs_async_xfer ( &data
->read_handle
,
1079 GNOME_VFS_XFER_DEFAULT
,
1080 GNOME_VFS_XFER_ERROR_MODE_ABORT
,
1081 GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE
,
1082 GNOME_VFS_PRIORITY_DEFAULT
,
1083 (GnomeVFSAsyncXferProgressCallback
) download_progress_update_cb
,
1085 (GnomeVFSXferProgressCallback
) download_progress_cb
,
1091 rb_podcast_manager_cancel_all (RBPodcastManager
*pd
)
1097 g_mutex_lock (pd
->priv
->download_list_mutex
);
1098 lst
= g_list_reverse (pd
->priv
->download_list
);
1099 g_mutex_unlock (pd
->priv
->download_list_mutex
);
1101 rb_debug ("cancel all job %d", g_list_length (lst
));
1102 lst_len
= g_list_length (lst
);
1104 for (i
=0; i
< lst_len
; i
++) {
1105 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) lst
->data
;
1108 rb_debug ("cancel next job");
1112 g_mutex_lock (pd
->priv
->mutex_job
);
1113 g_mutex_unlock (pd
->priv
->mutex_job
);
1118 end_job (RBPodcastManagerInfo
*data
)
1120 RBPodcastManager
*pd
= data
->pd
;
1122 rb_debug ("end_job");
1124 g_mutex_lock (data
->pd
->priv
->download_list_mutex
);
1125 data
->pd
->priv
->download_list
= g_list_remove (data
->pd
->priv
->download_list
, (gconstpointer
) data
);
1126 g_mutex_unlock (data
->pd
->priv
->download_list_mutex
);
1128 g_mutex_unlock (data
->mutex_working
);
1130 if (data
->canceled
!= TRUE
) {
1131 GDK_THREADS_ENTER ();
1133 g_signal_emit (data
->pd
, rb_podcast_manager_signals
[FINISH_DOWNLOAD
],
1136 GDK_THREADS_LEAVE ();
1139 download_info_free (data
);
1140 g_mutex_unlock (pd
->priv
->mutex_job
);
1142 g_idle_add ((GtkFunction
) rb_podcast_manager_next_file
, pd
);
1146 cancel_job (RBPodcastManagerInfo
*data
)
1148 if (g_mutex_trylock (data
->mutex_working
) == FALSE
) {
1149 rb_debug ("async cancel");
1150 data
->canceled
= TRUE
;
1153 rb_debug ("job cancel");
1155 g_mutex_lock (data
->pd
->priv
->download_list_mutex
);
1156 data
->pd
->priv
->download_list
= g_list_remove (data
->pd
->priv
->download_list
, (gconstpointer
) data
);
1157 g_mutex_unlock (data
->pd
->priv
->download_list_mutex
);
1159 g_mutex_unlock (data
->mutex_working
);
1161 download_info_free (data
);
1167 download_progress_cb (GnomeVFSXferProgressInfo
*info
, gpointer cb_data
)
1169 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) cb_data
;
1172 return GNOME_VFS_XFER_ERROR_ACTION_ABORT
;
1175 if (info
->status
!= GNOME_VFS_XFER_PROGRESS_STATUS_OK
||
1176 ((info
->phase
== GNOME_VFS_XFER_PHASE_COMPLETED
) && (info
->file_size
== 0))) {
1178 rb_debug ("error on download");
1179 g_value_init (&val
, G_TYPE_ULONG
);
1180 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_ERROR
);
1181 GDK_THREADS_ENTER ();
1182 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
1183 rhythmdb_commit (data
->pd
->priv
->db
);
1184 GDK_THREADS_LEAVE ();
1185 g_value_unset (&val
);
1188 return GNOME_VFS_XFER_ERROR_ACTION_ABORT
;
1191 if (rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_MOUNTPOINT
) == NULL
) {
1193 RhythmDB
*db
= data
->pd
->priv
->db
;
1194 char *uri
= gnome_vfs_uri_to_string (data
->write_uri
, GNOME_VFS_URI_HIDE_NONE
);
1195 char *canon_uri
= rb_canonicalise_uri (uri
);
1198 g_value_init (&val
, G_TYPE_STRING
);
1199 g_value_set_string (&val
, canon_uri
);
1200 rhythmdb_entry_set (db
, data
->entry
, RHYTHMDB_PROP_MOUNTPOINT
, &val
);
1201 g_value_unset (&val
);
1202 rhythmdb_commit (db
);
1206 if (info
->phase
== GNOME_VFS_XFER_PHASE_COMPLETED
) {
1207 if (data
->canceled
!= TRUE
) {
1208 rb_debug ("download completed");
1209 data
->total_size
= info
->file_size
;
1210 write_job_data (data
);
1214 return GNOME_VFS_XFER_ERROR_ACTION_SKIP
;
1217 if (data
->canceled
== TRUE
) {
1218 rb_debug ("job canceled");
1219 gnome_vfs_async_cancel (data
->read_handle
);
1220 return GNOME_VFS_XFER_ERROR_ACTION_ABORT
;
1227 download_progress_update_cb (GnomeVFSAsyncHandle
*handle
, GnomeVFSXferProgressInfo
*info
, gpointer cb_data
)
1230 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) cb_data
;
1233 return GNOME_VFS_XFER_ERROR_ACTION_ABORT
;
1236 if ((info
->phase
== GNOME_VFS_XFER_PHASE_COPYING
) &&
1237 (data
->entry
!= NULL
)) {
1238 guint local_progress
= 0;
1240 if (info
->file_size
> 0)
1241 local_progress
= (gint
) 100 * info
->total_bytes_copied
/ info
->file_size
;
1243 if (local_progress
!= data
->progress
) {
1246 g_value_init (&val
, G_TYPE_ULONG
);
1247 g_value_set_ulong (&val
, local_progress
);
1248 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
1249 g_value_unset (&val
);
1251 GDK_THREADS_ENTER ();
1253 g_signal_emit (data
->pd
, rb_podcast_manager_signals
[STATUS_CHANGED
],
1254 0, data
->entry
, local_progress
);
1256 GDK_THREADS_LEAVE ();
1257 data
->progress
= local_progress
;
1261 return GNOME_VFS_XFER_ERROR_ACTION_SKIP
;
1265 rb_podcast_manager_unsubscribe_feed (RhythmDB
*db
, const char *url
)
1267 RhythmDBEntry
*entry
= rhythmdb_entry_lookup_by_location (db
, url
);
1270 g_value_init (&val
, G_TYPE_ULONG
);
1271 g_value_set_ulong (&val
, 0);
1272 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &val
);
1273 g_value_unset (&val
);
1279 rb_podcast_manager_remove_feed (RBPodcastManager
*pd
, const char *url
, gboolean remove_files
)
1281 RhythmDBEntry
*entry
= rhythmdb_entry_lookup_by_location (pd
->priv
->db
, url
);
1284 rb_debug ("Removing podcast feed: %s remove_files: %d", url
, remove_files
);
1286 rb_podcast_manager_set_remove_files (pd
, remove_files
);
1287 rhythmdb_entry_delete (pd
->priv
->db
, entry
);
1288 rhythmdb_commit (pd
->priv
->db
);
1296 rb_podcast_manager_db_entry_deleted_cb (RBPodcastManager
*pd
,
1297 RhythmDBEntry
*entry
)
1299 RhythmDBEntryType type
= rhythmdb_entry_get_entry_type (entry
);
1301 if ((type
== RHYTHMDB_ENTRY_TYPE_PODCAST_POST
) && (pd
->priv
->remove_files
== TRUE
)) {
1302 const char *file_name
;
1303 const char *dir_name
;
1304 const char *conf_dir_name
;
1305 GnomeVFSResult result
;
1307 rb_debug ("Handling entry deleted");
1309 /* make sure we're not downloading it */
1310 rb_podcast_manager_cancel_download (pd
, entry
);
1312 file_name
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_MOUNTPOINT
);
1313 if (file_name
== NULL
) {
1314 /* episode has not been downloaded */
1315 rb_debug ("Episode not downloaded, skipping.");
1319 result
= gnome_vfs_unlink (file_name
);
1320 if (result
!= GNOME_VFS_OK
) {
1321 rb_debug ("Removing episode failed: %s", gnome_vfs_result_to_string (result
));
1326 rb_debug ("removing dir");
1327 conf_dir_name
= eel_gconf_get_string (CONF_STATE_PODCAST_DOWNLOAD_DIR
);
1329 dir_name
= g_build_filename (conf_dir_name
,
1330 rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ALBUM
),
1332 gnome_vfs_remove_directory (dir_name
);
1334 } else if (type
== RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
) {
1335 GtkTreeModel
*query_model
;
1338 query_model
= GTK_TREE_MODEL (rhythmdb_query_model_new_empty (pd
->priv
->db
));
1339 rhythmdb_do_full_query (pd
->priv
->db
,
1340 RHYTHMDB_QUERY_RESULTS (query_model
),
1341 RHYTHMDB_QUERY_PROP_EQUALS
,
1342 RHYTHMDB_PROP_TYPE
, RHYTHMDB_ENTRY_TYPE_PODCAST_POST
,
1343 RHYTHMDB_QUERY_PROP_LIKE
,
1344 RHYTHMDB_PROP_SUBTITLE
, rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
),
1345 RHYTHMDB_QUERY_END
);
1347 if (gtk_tree_model_get_iter_first (query_model
, &iter
)) {
1350 RhythmDBEntry
*entry
;
1352 gtk_tree_model_get (query_model
, &iter
, 0, &entry
, -1);
1353 has_next
= gtk_tree_model_iter_next (query_model
, &iter
);
1354 rhythmdb_entry_delete (pd
->priv
->db
, entry
);
1355 rhythmdb_entry_unref (entry
);
1359 rhythmdb_commit (pd
->priv
->db
);
1362 g_object_unref (query_model
);
1367 rb_podcast_manager_cancel_download (RBPodcastManager
*pd
, RhythmDBEntry
*entry
)
1371 g_mutex_lock (pd
->priv
->download_list_mutex
);
1373 lst
= pd
->priv
->download_list
;
1375 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) lst
->data
;
1376 if (data
->entry
== entry
) {
1377 rb_debug ("Found job");
1382 g_mutex_unlock (pd
->priv
->download_list_mutex
);
1385 cancel_job (lst
->data
);
1389 rb_podcast_manager_update_synctime (RBPodcastManager
*pd
)
1392 gint index
= eel_gconf_get_integer (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL
);
1396 case UPDATE_EVERY_HOUR
:
1397 value
= time (NULL
) + 3600;
1399 case UPDATE_EVERY_DAY
:
1400 value
= time (NULL
) + (3600 * 24);
1402 case UPDATE_EVERY_WEEK
:
1403 value
= time (NULL
) + (3600 * 24 * 7);
1405 case UPDATE_MANUALLY
:
1409 g_warning ("unknown download-inteval");
1413 eel_gconf_set_integer (CONF_STATE_PODCAST_DOWNLOAD_NEXT_TIME
, value
);
1414 eel_gconf_suggest_sync ();
1415 pd
->priv
->next_time
= value
;
1416 rb_podcast_manager_start_sync (pd
);
1420 rb_podcast_manager_config_changed (GConfClient
* client
,
1425 rb_podcast_manager_update_synctime (RB_PODCAST_MANAGER (user_data
));
1429 rb_podcast_manager_set_remove_files (RBPodcastManager
*pd
, gboolean flag
)
1431 pd
->priv
->remove_files
= flag
;
1436 rb_podcast_manager_get_remove_files (RBPodcastManager
*pd
)
1438 return pd
->priv
->remove_files
;
1442 rb_podcast_manager_insert_feed (RBPodcastManager
*pd
, RBPodcastChannel
*data
)
1444 GValue description_val
= { 0, };
1445 GValue title_val
= { 0, };
1446 GValue subtitle_val
= { 0, };
1447 GValue summary_val
= { 0, };
1448 GValue lang_val
= { 0, };
1449 GValue copyright_val
= { 0, };
1450 GValue image_val
= { 0, };
1451 GValue author_val
= { 0, };
1452 GValue status_val
= { 0, };
1453 GValue last_post_val
= { 0, };
1454 GValue last_update_val
= { 0, };
1455 gulong last_post
= 0;
1456 gulong new_last_post
;
1457 GList
*download_entries
= NULL
;
1458 gboolean new_feed
, updated
, download_last
;
1459 RhythmDB
*db
= pd
->priv
->db
;
1461 RhythmDBEntry
*entry
;
1465 if (data
->title
== NULL
) {
1466 g_list_free (data
->posts
);
1473 /* processing podcast head */
1474 entry
= rhythmdb_entry_lookup_by_location (db
, (gchar
*)data
->url
);
1476 if (rhythmdb_entry_get_entry_type (entry
) != RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
)
1479 rb_debug ("Head found");
1480 g_value_init (&status_val
, G_TYPE_ULONG
);
1481 g_value_set_ulong (&status_val
, 1);
1482 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &status_val
);
1483 g_value_unset (&status_val
);
1484 last_post
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_POST_TIME
);
1487 rb_debug ("Insert new entry");
1488 entry
= rhythmdb_entry_new (db
,
1489 RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
,
1490 (gchar
*) data
->url
);
1493 rb_debug("New entry create\n");
1495 g_value_init (&title_val
, G_TYPE_STRING
);
1496 g_value_set_string (&title_val
, (gchar
* ) data
->title
);
1497 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_TITLE
, &title_val
);
1498 g_value_unset (&title_val
);
1500 g_value_init (&author_val
, G_TYPE_STRING
);
1502 g_value_set_string (&author_val
, (gchar
*) data
->author
);
1504 g_value_set_static_string (&author_val
, _("Unknown"));
1505 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_ARTIST
, &author_val
);
1506 g_value_unset (&author_val
);
1508 if (data
->subtitle
) {
1509 g_value_init (&subtitle_val
, G_TYPE_STRING
);
1510 g_value_set_string (&subtitle_val
, (gchar
*) data
->subtitle
);
1511 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_SUBTITLE
, &subtitle_val
);
1512 g_value_unset (&subtitle_val
);
1515 if (data
->description
) {
1516 g_value_init (&description_val
, G_TYPE_STRING
);
1517 g_value_set_string (&description_val
, (gchar
*) data
->description
);
1518 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DESCRIPTION
, &description_val
);
1519 g_value_unset (&description_val
);
1522 if (data
->summary
) {
1523 g_value_init (&summary_val
, G_TYPE_STRING
);
1524 g_value_set_string (&summary_val
, (gchar
*) data
->summary
);
1525 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_SUMMARY
, &summary_val
);
1526 g_value_unset (&summary_val
);
1530 g_value_init (&lang_val
, G_TYPE_STRING
);
1531 g_value_set_string (&lang_val
, (gchar
*) data
->lang
);
1532 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LANG
, &lang_val
);
1533 g_value_unset (&lang_val
);
1536 if (data
->copyright
) {
1537 g_value_init (©right_val
, G_TYPE_STRING
);
1538 g_value_set_string (©right_val
, (gchar
*) data
->copyright
);
1539 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_COPYRIGHT
, ©right_val
);
1540 g_value_unset (©right_val
);
1544 g_value_init (&image_val
, G_TYPE_STRING
);
1545 g_value_set_string (&image_val
, (gchar
*) data
->img
);
1546 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_IMAGE
, &image_val
);
1547 g_value_unset (&image_val
);
1550 g_value_init (&status_val
, G_TYPE_ULONG
);
1551 g_value_set_ulong (&status_val
, 1);
1552 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &status_val
);
1553 g_value_unset (&status_val
);
1555 rb_debug("Podcast head Inserted");
1558 /* insert episodes */
1559 new_last_post
= last_post
;
1562 download_last
= (eel_gconf_get_integer (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL
) != UPDATE_MANUALLY
);
1563 for (lst_songs
= data
->posts
; lst_songs
!= NULL
; lst_songs
= g_list_next (lst_songs
)) {
1564 RBPodcastItem
*item
= (RBPodcastItem
*) lst_songs
->data
;
1565 RhythmDBEntry
*post_entry
;
1567 if (item
->pub_date
> last_post
|| item
->pub_date
== 0) {
1571 rb_podcast_manager_add_post (db
,
1572 (gchar
*) data
->title
,
1573 (gchar
*) item
->title
,
1574 (gchar
*) data
->url
,
1575 (gchar
*) (item
->author
? item
->author
: data
->author
),
1576 (gchar
*) item
->url
,
1577 (gchar
*) item
->description
,
1578 (gulong
) (item
->pub_date
> 0 ? item
->pub_date
: data
->pub_date
),
1579 (gulong
) item
->duration
,
1581 if (post_entry
&& item
->pub_date
>= new_last_post
) {
1582 if (item
->pub_date
> new_last_post
) {
1583 g_list_free (download_entries
);
1584 download_entries
= NULL
;
1586 download_entries
= g_list_prepend (download_entries
, post_entry
);
1587 new_last_post
= item
->pub_date
;
1592 if (download_last
) {
1593 GValue status
= {0,};
1596 g_value_init (&status
, G_TYPE_ULONG
);
1597 g_value_set_ulong (&status
, RHYTHMDB_PODCAST_STATUS_WAITING
);
1598 for (t
= download_entries
; t
!= NULL
; t
= g_list_next (t
)) {
1599 rhythmdb_entry_set (db
,
1600 (RhythmDBEntry
*) t
->data
,
1601 RHYTHMDB_PROP_STATUS
,
1604 g_value_unset (&status
);
1606 g_list_free (download_entries
);
1609 g_signal_emit (pd
, rb_podcast_manager_signals
[FEED_UPDATES_AVALIABLE
],
1612 if (data
->pub_date
> new_last_post
)
1613 new_last_post
= data
->pub_date
;
1615 g_value_init (&last_post_val
, G_TYPE_ULONG
);
1616 g_value_set_ulong (&last_post_val
, new_last_post
);
1619 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_POST_TIME
, &last_post_val
);
1621 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_POST_TIME
, &last_post_val
);
1622 g_value_unset (&last_post_val
);
1624 g_value_init (&last_update_val
, G_TYPE_ULONG
);
1625 g_value_set_ulong (&last_update_val
, time(NULL
));
1628 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LAST_SEEN
, &last_update_val
);
1630 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LAST_SEEN
, &last_update_val
);
1631 g_value_unset (&last_update_val
);
1633 rhythmdb_commit (db
);
1637 rb_podcast_manager_event_loop (RBPodcastManager
*pd
)
1639 RBPodcastManagerEvent
*event
;
1641 GDK_THREADS_ENTER ();
1643 while ((event
= g_async_queue_try_pop (pd
->priv
->event_queue
))) {
1644 switch (event
->type
)
1646 case EVENT_INSERT_FEED
:
1647 rb_podcast_manager_insert_feed (pd
, event
->channel
);
1649 case EVENT_ERROR_FEED
:
1652 error_msg
= g_strdup_printf (_("There was a problem adding this podcast. Please verify the URL: %s"),
1653 (gchar
*) event
->channel
->url
);
1654 g_signal_emit (G_OBJECT (pd
),
1655 rb_podcast_manager_signals
[PROCESS_ERROR
],
1662 rb_podcast_parse_channel_free (event
->channel
);
1666 g_async_queue_unref (pd
->priv
->event_queue
);
1668 GDK_THREADS_LEAVE ();
1673 rb_podcast_manager_abort_subscribe (RBPodcastManager
*pd
)
1675 RBPodcastManagerEvent
*event
;
1677 /* remove all event processing functions */
1678 while (g_idle_remove_by_data (pd
))
1681 /* purge the event queue */
1682 while ((event
= g_async_queue_try_pop (pd
->priv
->event_queue
))) {
1683 rb_podcast_parse_channel_free (event
->channel
);
1689 rb_podcast_manager_shutdown (RBPodcastManager
*pd
)
1691 rb_podcast_manager_cancel_all (pd
);
1692 rb_podcast_manager_abort_subscribe (pd
);
1696 rb_podcast_manager_get_podcast_dir (RBPodcastManager
*pd
)
1698 gchar
*conf_dir_name
= eel_gconf_get_string (CONF_STATE_PODCAST_DOWNLOAD_DIR
);
1700 if (conf_dir_name
== NULL
|| (strcmp (conf_dir_name
, "") == 0)) {
1701 conf_dir_name
= g_build_filename (g_get_home_dir (),
1704 eel_gconf_set_string (CONF_STATE_PODCAST_DOWNLOAD_DIR
, conf_dir_name
);
1707 return conf_dir_name
;