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"
47 #define CONF_STATE_PODCAST_PREFIX CONF_PREFIX "/state/podcast"
48 #define CONF_STATE_PODCAST_DOWNLOAD_DIR CONF_STATE_PODCAST_PREFIX "/download_prefix"
49 #define CONF_STATE_PODCAST_DOWNLOAD_INTERVAL CONF_STATE_PODCAST_PREFIX "/download_interval"
50 #define CONF_STATE_PODCAST_DOWNLOAD_NEXT_TIME CONF_STATE_PODCAST_PREFIX "/download_next_time"
72 FEED_UPDATES_AVAILABLE
,
80 } RBPodcastParseResultType
;
82 /* passed from feed parsing threads back to main thread */
85 RBPodcastParseResultType result
;
86 RBPodcastChannel
*channel
;
88 } RBPodcastManagerParseResult
;
94 GnomeVFSAsyncHandle
*read_handle
;
95 GnomeVFSURI
*write_uri
;
96 GnomeVFSURI
*read_uri
;
102 } RBPodcastManagerInfo
;
106 RBPodcastManager
*pd
;
108 } RBPodcastThreadInfo
;
110 struct RBPodcastManagerPrivate
113 GList
*download_list
;
114 RBPodcastManagerInfo
*active_download
;
117 guint update_interval_notify_id
;
120 gboolean remove_files
;
123 #define RB_PODCAST_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PODCAST_MANAGER, RBPodcastManagerPrivate))
126 static guint rb_podcast_manager_signals
[LAST_SIGNAL
] = { 0 };
129 static void rb_podcast_manager_class_init (RBPodcastManagerClass
*klass
);
130 static void rb_podcast_manager_init (RBPodcastManager
*dp
);
131 static GObject
*rb_podcast_manager_constructor (GType type
, guint n_construct_properties
,
132 GObjectConstructParam
*construct_properties
);
133 static void rb_podcast_manager_dispose (GObject
*object
);
134 static void rb_podcast_manager_finalize (GObject
*object
);
135 static void rb_podcast_manager_set_property (GObject
*object
,
139 static void rb_podcast_manager_get_property (GObject
*object
,
143 static void rb_podcast_manager_download_file_info_cb (GnomeVFSAsyncHandle
*handle
,
145 RBPodcastManagerInfo
*data
);
146 static void rb_podcast_manager_abort_download (RBPodcastManagerInfo
*data
);
147 static gboolean
rb_podcast_manager_sync_head_cb (gpointer data
);
148 static gboolean
rb_podcast_manager_head_query_cb (GtkTreeModel
*query_model
,
151 RBPodcastManager
*data
);
152 static gboolean
rb_podcast_manager_save_metadata (RhythmDB
*db
,
153 RhythmDBEntry
*entry
,
155 static void rb_podcast_manager_db_entry_added_cb (RBPodcastManager
*pd
,
156 RhythmDBEntry
*entry
);
157 static void rb_podcast_manager_db_entry_deleted_cb (RBPodcastManager
*pd
,
158 RhythmDBEntry
*entry
);
159 static gboolean
rb_podcast_manager_next_file (RBPodcastManager
* pd
);
160 static void rb_podcast_manager_insert_feed (RBPodcastManager
*pd
, RBPodcastChannel
*data
);
162 static gpointer
rb_podcast_manager_thread_parse_feed (RBPodcastThreadInfo
*info
);
164 /* async read file functions */
165 static guint
download_progress_cb (GnomeVFSXferProgressInfo
*info
,
167 static guint
download_progress_update_cb (GnomeVFSAsyncHandle
*handle
,
168 GnomeVFSXferProgressInfo
*info
,
171 /* internal functions */
172 static void download_info_free (RBPodcastManagerInfo
*data
);
173 static void start_job (RBPodcastManagerInfo
*data
);
174 static gboolean
end_job (RBPodcastManagerInfo
*data
);
175 static void cancel_job (RBPodcastManagerInfo
*pd
);
176 static void rb_podcast_manager_update_synctime (RBPodcastManager
*pd
);
177 static void rb_podcast_manager_config_changed (GConfClient
* client
,
182 G_DEFINE_TYPE (RBPodcastManager
, rb_podcast_manager
, G_TYPE_OBJECT
)
185 rb_podcast_manager_class_init (RBPodcastManagerClass
*klass
)
187 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
189 object_class
->constructor
= rb_podcast_manager_constructor
;
190 object_class
->dispose
= rb_podcast_manager_dispose
;
191 object_class
->finalize
= rb_podcast_manager_finalize
;
193 object_class
->set_property
= rb_podcast_manager_set_property
;
194 object_class
->get_property
= rb_podcast_manager_get_property
;
196 g_object_class_install_property (object_class
,
198 g_param_spec_object ("db",
204 rb_podcast_manager_signals
[STATUS_CHANGED
] =
205 g_signal_new ("status_changed",
206 G_OBJECT_CLASS_TYPE (object_class
),
208 G_STRUCT_OFFSET (RBPodcastManagerClass
, status_changed
),
210 rb_marshal_VOID__BOXED_ULONG
,
216 rb_podcast_manager_signals
[START_DOWNLOAD
] =
217 g_signal_new ("start_download",
218 G_OBJECT_CLASS_TYPE (object_class
),
220 G_STRUCT_OFFSET (RBPodcastManagerClass
, start_download
),
222 g_cclosure_marshal_VOID__BOXED
,
225 RHYTHMDB_TYPE_ENTRY
);
227 rb_podcast_manager_signals
[FINISH_DOWNLOAD
] =
228 g_signal_new ("finish_download",
229 G_OBJECT_CLASS_TYPE (object_class
),
231 G_STRUCT_OFFSET (RBPodcastManagerClass
, finish_download
),
233 g_cclosure_marshal_VOID__BOXED
,
236 RHYTHMDB_TYPE_ENTRY
);
238 rb_podcast_manager_signals
[FEED_UPDATES_AVAILABLE
] =
239 g_signal_new ("feed_updates_available",
240 G_OBJECT_CLASS_TYPE (object_class
),
242 G_STRUCT_OFFSET (RBPodcastManagerClass
, feed_updates_available
),
244 g_cclosure_marshal_VOID__BOXED
,
247 RHYTHMDB_TYPE_ENTRY
);
249 rb_podcast_manager_signals
[PROCESS_ERROR
] =
250 g_signal_new ("process_error",
251 G_OBJECT_CLASS_TYPE (object_class
),
253 G_STRUCT_OFFSET (RBPodcastManagerClass
, process_error
),
255 g_cclosure_marshal_VOID__STRING
,
260 g_type_class_add_private (klass
, sizeof (RBPodcastManagerPrivate
));
264 rb_podcast_manager_init (RBPodcastManager
*pd
)
266 pd
->priv
= RB_PODCAST_MANAGER_GET_PRIVATE (pd
);
268 pd
->priv
->source_sync
= 0;
270 eel_gconf_monitor_add (CONF_STATE_PODCAST_PREFIX
);
274 rb_podcast_manager_constructor (GType type
, guint n_construct_properties
,
275 GObjectConstructParam
*construct_properties
)
277 RBPodcastManager
*pd
;
279 pd
= RB_PODCAST_MANAGER (G_OBJECT_CLASS (rb_podcast_manager_parent_class
)
280 ->constructor (type
, n_construct_properties
, construct_properties
));
282 pd
->priv
->update_interval_notify_id
= eel_gconf_notification_add (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL
,
283 rb_podcast_manager_config_changed
,
286 return G_OBJECT (pd
);
291 rb_podcast_manager_dispose (GObject
*object
)
293 RBPodcastManager
*pd
;
294 g_return_if_fail (object
!= NULL
);
295 g_return_if_fail (RB_IS_PODCAST_MANAGER (object
));
297 pd
= RB_PODCAST_MANAGER (object
);
298 g_return_if_fail (pd
->priv
!= NULL
);
300 eel_gconf_monitor_remove (CONF_STATE_PODCAST_PREFIX
);
302 if (pd
->priv
->source_sync
!= 0) {
303 g_source_remove (pd
->priv
->source_sync
);
304 pd
->priv
->source_sync
= 0;
307 if (pd
->priv
->update_interval_notify_id
!= 0) {
308 eel_gconf_notification_remove (pd
->priv
->update_interval_notify_id
);
309 pd
->priv
->update_interval_notify_id
= 0;
312 if (pd
->priv
->db
!= NULL
) {
313 g_object_unref (pd
->priv
->db
);
317 G_OBJECT_CLASS (rb_podcast_manager_parent_class
)->dispose (object
);
321 rb_podcast_manager_finalize (GObject
*object
)
323 RBPodcastManager
*pd
;
324 g_return_if_fail (object
!= NULL
);
325 g_return_if_fail (RB_IS_PODCAST_MANAGER (object
));
327 pd
= RB_PODCAST_MANAGER(object
);
329 g_return_if_fail (pd
->priv
!= NULL
);
331 if (pd
->priv
->download_list
) {
332 g_list_foreach (pd
->priv
->download_list
, (GFunc
)g_free
, NULL
);
333 g_list_free (pd
->priv
->download_list
);
336 G_OBJECT_CLASS (rb_podcast_manager_parent_class
)->finalize (object
);
340 rb_podcast_manager_set_property (GObject
*object
,
345 RBPodcastManager
*pd
= RB_PODCAST_MANAGER (object
);
350 g_signal_handlers_disconnect_by_func (pd
->priv
->db
,
351 G_CALLBACK (rb_podcast_manager_db_entry_added_cb
),
354 g_signal_handlers_disconnect_by_func (pd
->priv
->db
,
355 G_CALLBACK (rb_podcast_manager_db_entry_deleted_cb
),
357 g_object_unref (pd
->priv
->db
);
360 pd
->priv
->db
= g_value_get_object (value
);
361 g_object_ref (pd
->priv
->db
);
363 g_signal_connect_object (pd
->priv
->db
,
365 G_CALLBACK (rb_podcast_manager_db_entry_added_cb
),
366 pd
, G_CONNECT_SWAPPED
);
368 g_signal_connect_object (pd
->priv
->db
,
370 G_CALLBACK (rb_podcast_manager_db_entry_deleted_cb
),
371 pd
, G_CONNECT_SWAPPED
);
375 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
380 rb_podcast_manager_get_property (GObject
*object
,
385 RBPodcastManager
*pd
= RB_PODCAST_MANAGER (object
);
389 g_value_set_object (value
, pd
->priv
->db
);
392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
398 rb_podcast_manager_new (RhythmDB
*db
)
400 RBPodcastManager
*pd
;
402 pd
= g_object_new (RB_TYPE_PODCAST_MANAGER
, "db", db
, NULL
);
407 rb_podcast_manager_download_entry (RBPodcastManager
*pd
,
408 RhythmDBEntry
*entry
)
411 g_assert (rb_is_main_thread ());
413 g_return_if_fail (RB_IS_PODCAST_MANAGER (pd
));
418 status
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_STATUS
);
419 if ((status
< RHYTHMDB_PODCAST_STATUS_COMPLETE
) ||
420 (status
== RHYTHMDB_PODCAST_STATUS_WAITING
)) {
421 RBPodcastManagerInfo
*data
;
422 if (status
< RHYTHMDB_PODCAST_STATUS_COMPLETE
) {
423 GValue status_val
= { 0, };
424 g_value_init (&status_val
, G_TYPE_ULONG
);
425 g_value_set_ulong (&status_val
, RHYTHMDB_PODCAST_STATUS_WAITING
);
426 rhythmdb_entry_set (pd
->priv
->db
, entry
, RHYTHMDB_PROP_STATUS
, &status_val
);
427 g_value_unset (&status_val
);
429 rhythmdb_commit (pd
->priv
->db
);
431 rb_debug ("Adding podcast episode %s to download list",
432 rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
));
434 data
= g_new0 (RBPodcastManagerInfo
, 1);
435 data
->pd
= g_object_ref (pd
);
436 data
->entry
= rhythmdb_entry_ref (entry
);
438 pd
->priv
->download_list
= g_list_append (pd
->priv
->download_list
, data
);
439 g_idle_add ((GSourceFunc
) rb_podcast_manager_next_file
, pd
);
444 rb_podcast_manager_entry_downloaded (RhythmDBEntry
*entry
)
447 const gchar
*file_name
;
448 RhythmDBEntryType type
= rhythmdb_entry_get_entry_type (entry
);
450 g_assert (type
== RHYTHMDB_ENTRY_TYPE_PODCAST_POST
);
452 status
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_STATUS
);
453 file_name
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_MOUNTPOINT
);
455 return (status
!= RHYTHMDB_PODCAST_STATUS_ERROR
&& file_name
!= NULL
);
459 rb_podcast_manager_start_sync (RBPodcastManager
*pd
)
463 g_return_if_fail (RB_IS_PODCAST_MANAGER (pd
));
465 if (pd
->priv
->next_time
> 0) {
466 next_time
= pd
->priv
->next_time
;
468 next_time
= eel_gconf_get_integer (CONF_STATE_PODCAST_DOWNLOAD_NEXT_TIME
);
472 if (pd
->priv
->source_sync
!= 0) {
473 g_source_remove (pd
->priv
->source_sync
);
474 pd
->priv
->source_sync
= 0;
476 next_time
= next_time
- ((int)time (NULL
));
477 if (next_time
<= 0) {
478 rb_podcast_manager_update_feeds (pd
);
479 pd
->priv
->next_time
= 0;
480 rb_podcast_manager_update_synctime (pd
);
483 pd
->priv
->source_sync
= g_timeout_add (next_time
* 1000, (GSourceFunc
) rb_podcast_manager_sync_head_cb
, pd
);
489 rb_podcast_manager_sync_head_cb (gpointer data
)
491 RBPodcastManager
*pd
= RB_PODCAST_MANAGER (data
);
493 g_assert (rb_is_main_thread ());
495 GDK_THREADS_ENTER ();
497 rb_podcast_manager_update_feeds (pd
);
498 pd
->priv
->source_sync
= 0;
499 pd
->priv
->next_time
= 0;
500 rb_podcast_manager_update_synctime (RB_PODCAST_MANAGER (data
));
501 GDK_THREADS_LEAVE ();
506 rb_podcast_manager_update_feeds (RBPodcastManager
*pd
)
508 GtkTreeModel
*query_model
;
510 g_return_if_fail (RB_IS_PODCAST_MANAGER (pd
));
512 query_model
= GTK_TREE_MODEL (rhythmdb_query_model_new_empty (pd
->priv
->db
));
514 rhythmdb_do_full_query (pd
->priv
->db
,
515 RHYTHMDB_QUERY_RESULTS (query_model
),
516 RHYTHMDB_QUERY_PROP_EQUALS
,
517 RHYTHMDB_PROP_TYPE
, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
,
520 gtk_tree_model_foreach (query_model
,
521 (GtkTreeModelForeachFunc
) rb_podcast_manager_head_query_cb
,
524 g_object_unref (query_model
);
528 rb_podcast_manager_head_query_cb (GtkTreeModel
*query_model
,
531 RBPodcastManager
*manager
)
534 RhythmDBEntry
*entry
;
537 gtk_tree_model_get (query_model
, iter
, 0, &entry
, -1);
538 uri
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
539 status
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_STATUS
);
542 rb_podcast_manager_subscribe_feed (manager
, uri
);
544 rhythmdb_entry_unref (entry
);
550 rb_podcast_manager_next_file (RBPodcastManager
* pd
)
552 const char *location
;
553 RBPodcastManagerInfo
*data
;
557 g_assert (rb_is_main_thread ());
559 rb_debug ("looking for something to download");
561 GDK_THREADS_ENTER ();
563 if (pd
->priv
->active_download
!= NULL
) {
564 rb_debug ("already downloading something");
565 GDK_THREADS_LEAVE ();
569 d
= g_list_first (pd
->priv
->download_list
);
571 rb_debug ("download queue is empty");
572 GDK_THREADS_LEAVE ();
576 data
= (RBPodcastManagerInfo
*) d
->data
;
577 g_assert (data
!= NULL
);
578 g_assert (data
->entry
!= NULL
);
580 pd
->priv
->active_download
= data
;
582 location
= rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
);
583 rb_debug ("processing %s", location
);
585 /* gnome-vfs currently doesn't handle HTTP query strings correctly.
586 * so we do it ourselves.
588 query_string
= strrchr (location
, '?');
589 if (query_string
!= NULL
) {
592 base_uri
= g_strdup (location
);
593 query_string
= strrchr (base_uri
, '?');
594 *query_string
++ = '\0';
595 rb_debug ("hiding query string %s from gnome-vfs", query_string
);
597 data
->read_uri
= gnome_vfs_uri_new (base_uri
);
598 if (data
->read_uri
!= NULL
) {
601 full_uri
= g_strdup_printf ("%s?%s",
602 data
->read_uri
->text
,
604 g_free (data
->read_uri
->text
);
605 data
->read_uri
->text
= full_uri
;
607 /* include the question mark in data->query_string to make
608 * the later check easier.
612 data
->query_string
= g_strdup (query_string
);
616 data
->read_uri
= gnome_vfs_uri_new (location
);
619 if (data
->read_uri
== NULL
) {
620 rb_debug ("Error downloading podcast: could not create remote uri");
621 rb_podcast_manager_abort_download (data
);
625 l
= g_list_prepend (NULL
, data
->read_uri
);
626 gnome_vfs_async_get_file_info (&data
->read_handle
,
628 GNOME_VFS_FILE_INFO_FOLLOW_LINKS
,
629 GNOME_VFS_PRIORITY_DEFAULT
,
630 (GnomeVFSAsyncGetFileInfoCallback
) rb_podcast_manager_download_file_info_cb
,
635 GDK_THREADS_LEAVE ();
640 rb_podcast_manager_download_file_info_cb (GnomeVFSAsyncHandle
*handle
,
642 RBPodcastManagerInfo
*data
)
644 GnomeVFSGetFileInfoResult
*result
= results
->data
;
645 char *local_file_name
;
646 char *local_file_path
;
650 g_assert (rb_is_main_thread ());
652 rb_debug ("got file info results for %s",
653 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
));
655 if (result
->result
!= GNOME_VFS_OK
) {
659 g_value_init (&val
, G_TYPE_ULONG
);
660 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_ERROR
);
661 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
662 g_value_unset (&val
);
664 g_value_init (&val
, G_TYPE_STRING
);
665 g_value_set_string (&val
, gnome_vfs_result_to_string (result
->result
));
666 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
, &val
);
667 g_value_unset (&val
);
669 rhythmdb_commit (data
->pd
->priv
->db
);
671 rb_debug ("get_file_info request failed");
672 rb_podcast_manager_abort_download (data
);
676 /* construct download directory */
677 conf_dir_name
= rb_podcast_manager_get_podcast_dir (data
->pd
);
678 dir_name
= g_build_filename (conf_dir_name
,
679 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_ALBUM
),
681 g_free (conf_dir_name
);
683 if (g_mkdir_with_parents (dir_name
, 0750) == -1) {
684 rb_debug ("Could not create podcast download directory %s", dir_name
);
685 /* FIXME: display error to user */
687 rb_podcast_manager_abort_download (data
);
691 /* if the filename ends with the query string from the original URI,
694 if (data
->query_string
&&
695 g_str_has_suffix (result
->file_info
->name
, data
->query_string
)) {
696 local_file_name
= g_strdup (result
->file_info
->name
);
697 local_file_name
[strlen (local_file_name
) - strlen (data
->query_string
)] = '\0';
698 rb_debug ("removing query string \"%s\" -> local file name \"%s\"", data
->query_string
, local_file_name
);
700 local_file_name
= result
->file_info
->name
;
703 /* construct local filename */
704 local_file_path
= g_build_filename (dir_name
,
708 if (local_file_name
!= result
->file_info
->name
)
709 g_free (local_file_name
);
712 rb_debug ("creating file %s", local_file_path
);
714 data
->write_uri
= gnome_vfs_uri_new (local_file_path
);
715 if (data
->write_uri
== NULL
) {
716 g_warning ("Could not create local podcast URI for %s", local_file_path
);
717 rb_podcast_manager_abort_download (data
);
721 if (g_file_test (local_file_path
, G_FILE_TEST_EXISTS
)) {
723 GnomeVFSFileInfo
*local_info
;
724 GnomeVFSResult local_result
;
726 local_info
= gnome_vfs_file_info_new ();
727 local_result
= gnome_vfs_get_file_info (local_file_path
,
729 GNOME_VFS_FILE_INFO_FOLLOW_LINKS
);
730 local_size
= local_info
->size
;
731 gnome_vfs_file_info_unref (local_info
);
733 if (local_result
!= GNOME_VFS_OK
) {
734 g_warning ("Could not get info on downloaded podcast file %s",
736 rb_podcast_manager_abort_download (data
);
738 } else if (result
->file_info
->size
== local_size
) {
743 uri
= gnome_vfs_uri_to_string (data
->write_uri
, GNOME_VFS_URI_HIDE_NONE
);
744 canon_uri
= rb_canonicalise_uri (uri
);
747 rb_debug ("podcast %s already downloaded",
748 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
));
750 g_value_init (&val
, G_TYPE_ULONG
);
751 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_COMPLETE
);
752 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
753 g_value_unset (&val
);
755 g_value_init (&val
, G_TYPE_STRING
);
756 g_value_set_string (&val
, canon_uri
);
757 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_MOUNTPOINT
, &val
);
758 g_value_unset (&val
);
760 rb_podcast_manager_save_metadata (data
->pd
->priv
->db
, data
->entry
, canon_uri
);
764 rb_podcast_manager_abort_download (data
);
766 } else if (result
->file_info
->size
> local_size
) {
767 /* TODO: support resume file */
768 rb_debug ("podcast episode already partially downloaded, but we can't resume downloads");
770 /* the local file is larger. replace it */
774 g_free (local_file_path
);
780 rb_podcast_manager_abort_download (RBPodcastManagerInfo
*data
)
782 RBPodcastManager
*mgr
= data
->pd
;
784 g_assert (rb_is_main_thread ());
786 mgr
->priv
->download_list
= g_list_remove (mgr
->priv
->download_list
, data
);
787 download_info_free (data
);
789 if (mgr
->priv
->active_download
== data
)
790 mgr
->priv
->active_download
= NULL
;
792 g_idle_add ((GSourceFunc
) rb_podcast_manager_next_file
, mgr
);
796 rb_podcast_manager_subscribe_feed (RBPodcastManager
*pd
, const char *url
)
798 RBPodcastThreadInfo
*info
;
799 gchar
*valid_url
= gnome_vfs_make_uri_from_input (url
);
801 if (valid_url
== NULL
) {
802 rb_error_dialog (NULL
, _("Invalid URL"),
803 _("The URL \"%s\" is not valid, please check it."), url
);
807 RhythmDBEntry
*entry
= rhythmdb_entry_lookup_by_location (pd
->priv
->db
, valid_url
);
809 if (rhythmdb_entry_get_entry_type (entry
) != RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
) {
810 /* added as something else, probably iradio */
811 rb_error_dialog (NULL
, _("URL already added"),
812 _("The URL \"%s\" has already been added as a radio station. "
813 "If this is a podcast feed, please remove the radio station."), url
);
818 info
= g_new0 (RBPodcastThreadInfo
, 1);
819 info
->pd
= g_object_ref (pd
);
820 info
->url
= valid_url
;
822 g_thread_create ((GThreadFunc
) rb_podcast_manager_thread_parse_feed
,
829 rb_podcast_manager_free_parse_result (RBPodcastManagerParseResult
*result
)
831 rb_podcast_parse_channel_free (result
->channel
);
832 g_object_unref (result
->pd
);
837 rb_podcast_manager_parse_complete_cb (RBPodcastManagerParseResult
*result
)
839 GDK_THREADS_ENTER ();
840 if (result
->pd
->priv
->shutdown
) {
841 GDK_THREADS_LEAVE ();
845 switch (result
->result
)
847 case RESULT_PARSE_OK
:
848 rb_podcast_manager_insert_feed (result
->pd
, result
->channel
);
850 case RESULT_PARSE_ERROR
:
853 error_msg
= g_strdup_printf (_("There was a problem adding this podcast. Please verify the URL: %s"),
854 (gchar
*) result
->channel
->url
);
855 g_signal_emit (result
->pd
,
856 rb_podcast_manager_signals
[PROCESS_ERROR
],
863 GDK_THREADS_LEAVE ();
869 rb_podcast_manager_thread_parse_feed (RBPodcastThreadInfo
*info
)
871 RBPodcastChannel
*feed
= g_new0 (RBPodcastChannel
, 1);
873 if (rb_podcast_parse_load_feed (feed
, info
->url
)) {
874 RBPodcastManagerParseResult
*result
;
876 result
= g_new0 (RBPodcastManagerParseResult
, 1);
877 result
->channel
= feed
;
878 result
->result
= (feed
->title
== NULL
) ? RESULT_PARSE_ERROR
: RESULT_PARSE_OK
;
879 result
->pd
= g_object_ref (info
->pd
);
881 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE
,
882 (GSourceFunc
) rb_podcast_manager_parse_complete_cb
,
884 (GDestroyNotify
) rb_podcast_manager_free_parse_result
);
887 g_object_unref (info
->pd
);
894 rb_podcast_manager_add_post (RhythmDB
*db
,
897 const char *subtitle
,
898 const char *generator
,
900 const char *description
,
905 RhythmDBEntry
*entry
;
909 if (!uri
|| !name
|| !title
|| !g_utf8_validate(uri
, -1, NULL
)) {
912 entry
= rhythmdb_entry_lookup_by_location (db
, uri
);
916 entry
= rhythmdb_entry_new (db
,
917 RHYTHMDB_ENTRY_TYPE_PODCAST_POST
,
922 g_get_current_time (&time
);
926 g_value_init (&val
, G_TYPE_STRING
);
927 g_value_set_string (&val
, name
);
928 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_ALBUM
, &val
);
930 g_value_reset (&val
);
931 g_value_set_static_string (&val
, _("Podcast"));
932 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_GENRE
, &val
);
934 g_value_reset (&val
);
935 g_value_set_string (&val
, title
);
936 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_TITLE
, &val
);
938 g_value_reset (&val
);
940 g_value_set_string (&val
, subtitle
);
942 g_value_set_static_string (&val
, "");
943 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_SUBTITLE
, &val
);
945 g_value_reset (&val
);
947 g_value_set_string (&val
, description
);
949 g_value_set_static_string (&val
, "");
950 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DESCRIPTION
, &val
);
952 g_value_reset (&val
);
954 g_value_set_string (&val
, generator
);
956 g_value_set_static_string (&val
, "");
957 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_ARTIST
, &val
);
958 g_value_unset (&val
);
960 g_value_init (&val
, G_TYPE_ULONG
);
961 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_PAUSED
);
962 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &val
);
964 g_value_reset (&val
);
965 g_value_set_ulong (&val
, date
);
966 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_POST_TIME
, &val
);
968 g_value_reset (&val
);
969 g_value_set_ulong (&val
, duration
);
970 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DURATION
, &val
);
972 g_value_reset (&val
);
973 g_value_set_ulong (&val
, 0);
974 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LAST_PLAYED
, &val
);
977 g_value_reset (&val
);
978 g_value_set_ulong (&val
, time
.tv_sec
);
979 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_FIRST_SEEN
, &val
);
980 g_value_unset (&val
);
982 /* initialize the rating */
983 g_value_init (&val
, G_TYPE_DOUBLE
);
984 g_value_set_double (&val
, 2.5);
985 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_RATING
, &val
);
986 g_value_unset (&val
);
988 g_value_init (&val
, G_TYPE_UINT64
);
989 g_value_set_uint64 (&val
, filesize
);
990 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_FILE_SIZE
, &val
);
991 g_value_unset (&val
);
997 rb_podcast_manager_save_metadata (RhythmDB
*db
, RhythmDBEntry
*entry
, const char *uri
)
999 RBMetaData
*md
= rb_metadata_new ();
1000 GError
*error
= NULL
;
1001 GValue val
= { 0, };
1004 rb_debug ("Loading podcast metadata from %s", uri
);
1005 rb_metadata_load (md
, uri
, &error
);
1007 if (error
!= NULL
) {
1008 /* this probably isn't an audio enclosure. or some other error */
1009 g_value_init (&val
, G_TYPE_ULONG
);
1010 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_ERROR
);
1011 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &val
);
1012 g_value_unset (&val
);
1014 g_value_init (&val
, G_TYPE_STRING
);
1015 g_value_set_string (&val
, error
->message
);
1016 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
, &val
);
1017 g_value_unset (&val
);
1019 rhythmdb_commit (db
);
1021 g_object_unref (md
);
1022 g_error_free (error
);
1027 mime
= rb_metadata_get_mime (md
);
1029 g_value_init (&val
, G_TYPE_STRING
);
1030 g_value_set_string (&val
, mime
);
1031 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_MIMETYPE
, &val
);
1032 g_value_unset (&val
);
1035 if (rb_metadata_get (md
,
1036 RB_METADATA_FIELD_DURATION
,
1038 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DURATION
, &val
);
1039 g_value_unset (&val
);
1042 if (rb_metadata_get (md
,
1043 RB_METADATA_FIELD_BITRATE
,
1045 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_BITRATE
, &val
);
1046 g_value_unset (&val
);
1049 rhythmdb_commit (db
);
1051 g_object_unref (md
);
1056 rb_podcast_manager_db_entry_added_cb (RBPodcastManager
*pd
, RhythmDBEntry
*entry
)
1058 RhythmDBEntryType type
= rhythmdb_entry_get_entry_type (entry
);
1060 if (type
!= RHYTHMDB_ENTRY_TYPE_PODCAST_POST
)
1063 rb_podcast_manager_download_entry (pd
, entry
);
1067 download_info_free (RBPodcastManagerInfo
*data
)
1069 if (data
->write_uri
) {
1070 gnome_vfs_uri_unref (data
->write_uri
);
1071 data
->write_uri
= NULL
;
1074 if (data
->read_uri
) {
1075 gnome_vfs_uri_unref (data
->read_uri
);
1076 data
->read_uri
= NULL
;
1078 if (data
->query_string
) {
1079 g_free (data
->query_string
);
1080 data
->query_string
= NULL
;
1084 rhythmdb_entry_unref (data
->entry
);
1091 start_job (RBPodcastManagerInfo
*data
)
1093 GList
*source_uri_list
;
1094 GList
*target_uri_list
;
1096 GDK_THREADS_ENTER ();
1097 g_signal_emit (data
->pd
, rb_podcast_manager_signals
[START_DOWNLOAD
],
1099 GDK_THREADS_LEAVE ();
1101 source_uri_list
= g_list_prepend (NULL
, data
->read_uri
);
1102 target_uri_list
= g_list_prepend (NULL
, data
->write_uri
);
1104 gnome_vfs_async_xfer (&data
->read_handle
,
1107 GNOME_VFS_XFER_DEFAULT
,
1108 GNOME_VFS_XFER_ERROR_MODE_ABORT
,
1109 GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE
,
1110 GNOME_VFS_PRIORITY_DEFAULT
,
1111 (GnomeVFSAsyncXferProgressCallback
) download_progress_update_cb
,
1113 (GnomeVFSXferProgressCallback
) download_progress_cb
,
1116 g_list_free (source_uri_list
);
1117 g_list_free (target_uri_list
);
1121 end_job (RBPodcastManagerInfo
*data
)
1123 RBPodcastManager
*pd
= data
->pd
;
1125 g_assert (rb_is_main_thread ());
1127 rb_debug ("cleaning up download of %s",
1128 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
));
1130 data
->pd
->priv
->download_list
= g_list_remove (data
->pd
->priv
->download_list
, data
);
1132 GDK_THREADS_ENTER ();
1133 g_signal_emit (data
->pd
, rb_podcast_manager_signals
[FINISH_DOWNLOAD
],
1135 GDK_THREADS_LEAVE ();
1137 g_assert (pd
->priv
->active_download
== data
);
1138 pd
->priv
->active_download
= NULL
;
1140 download_info_free (data
);
1142 g_idle_add ((GSourceFunc
) rb_podcast_manager_next_file
, pd
);
1147 cancel_job (RBPodcastManagerInfo
*data
)
1149 g_assert (rb_is_main_thread ());
1150 rb_debug ("cancelling download of %s",
1151 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
));
1153 /* is this the active download? */
1154 if (data
== data
->pd
->priv
->active_download
) {
1155 data
->cancelled
= TRUE
;
1156 if (data
->read_handle
!= NULL
) {
1157 gnome_vfs_async_cancel (data
->read_handle
);
1158 data
->read_handle
= NULL
;
1161 /* download data will be cleaned up after next progress callback */
1163 /* destroy download data */
1164 data
->pd
->priv
->download_list
= g_list_remove (data
->pd
->priv
->download_list
, data
);
1165 download_info_free (data
);
1170 download_progress_cb (GnomeVFSXferProgressInfo
*info
, gpointer cb_data
)
1173 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) cb_data
;
1176 return GNOME_VFS_XFER_ERROR_ACTION_ABORT
;
1179 if (info
->status
!= GNOME_VFS_XFER_PROGRESS_STATUS_OK
||
1180 ((info
->phase
== GNOME_VFS_XFER_PHASE_COMPLETED
) && (info
->file_size
== 0))) {
1182 rb_debug ("error downloading %s",
1183 rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_LOCATION
));
1185 g_value_init (&val
, G_TYPE_ULONG
);
1186 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_ERROR
);
1187 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
1188 g_value_unset (&val
);
1190 if (info
->vfs_status
!= GNOME_VFS_OK
) {
1191 g_value_init (&val
, G_TYPE_STRING
);
1192 g_value_set_string (&val
, gnome_vfs_result_to_string (info
->vfs_status
));
1193 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_PLAYBACK_ERROR
, &val
);
1194 g_value_unset (&val
);
1197 rhythmdb_commit (data
->pd
->priv
->db
);
1198 g_idle_add ((GSourceFunc
)end_job
, data
);
1199 return GNOME_VFS_XFER_ERROR_ACTION_ABORT
;
1202 if (rhythmdb_entry_get_string (data
->entry
, RHYTHMDB_PROP_MOUNTPOINT
) == NULL
) {
1203 char *uri
= gnome_vfs_uri_to_string (data
->write_uri
, GNOME_VFS_URI_HIDE_NONE
);
1204 char *canon_uri
= rb_canonicalise_uri (uri
);
1207 g_value_init (&val
, G_TYPE_STRING
);
1208 g_value_set_string (&val
, canon_uri
);
1209 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_MOUNTPOINT
, &val
);
1210 g_value_unset (&val
);
1212 rhythmdb_commit (data
->pd
->priv
->db
);
1216 if (info
->phase
== GNOME_VFS_XFER_PHASE_COMPLETED
) {
1217 if (data
->cancelled
== FALSE
) {
1221 uri
= gnome_vfs_uri_to_string (data
->write_uri
,
1222 GNOME_VFS_URI_HIDE_NONE
);
1223 canon_uri
= rb_canonicalise_uri (uri
);
1225 rb_debug ("download of %s completed", canon_uri
);
1227 g_value_init (&val
, G_TYPE_UINT64
);
1228 g_value_set_uint64 (&val
, info
->file_size
);
1229 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_FILE_SIZE
, &val
);
1230 g_value_unset (&val
);
1232 g_value_init (&val
, G_TYPE_ULONG
);
1233 g_value_set_ulong (&val
, RHYTHMDB_PODCAST_STATUS_COMPLETE
);
1234 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
1235 g_value_unset (&val
);
1237 rb_podcast_manager_save_metadata (data
->pd
->priv
->db
,
1242 g_idle_add ((GSourceFunc
)end_job
, data
);
1243 return GNOME_VFS_XFER_ERROR_ACTION_SKIP
;
1250 download_progress_update_cb (GnomeVFSAsyncHandle
*handle
, GnomeVFSXferProgressInfo
*info
, gpointer cb_data
)
1252 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) cb_data
;
1255 return GNOME_VFS_XFER_ERROR_ACTION_ABORT
;
1258 if ((info
->phase
== GNOME_VFS_XFER_PHASE_COPYING
) &&
1259 (data
->entry
!= NULL
)) {
1260 guint local_progress
= 0;
1262 if (info
->file_size
> 0)
1263 local_progress
= (gint
) 100 * info
->total_bytes_copied
/ info
->file_size
;
1265 if (local_progress
!= data
->progress
) {
1268 GDK_THREADS_ENTER ();
1270 g_value_init (&val
, G_TYPE_ULONG
);
1271 g_value_set_ulong (&val
, local_progress
);
1272 rhythmdb_entry_set (data
->pd
->priv
->db
, data
->entry
, RHYTHMDB_PROP_STATUS
, &val
);
1273 g_value_unset (&val
);
1275 g_signal_emit (data
->pd
, rb_podcast_manager_signals
[STATUS_CHANGED
],
1276 0, data
->entry
, local_progress
);
1278 GDK_THREADS_LEAVE ();
1280 data
->progress
= local_progress
;
1284 return GNOME_VFS_XFER_ERROR_ACTION_SKIP
;
1288 rb_podcast_manager_unsubscribe_feed (RhythmDB
*db
, const char *url
)
1290 RhythmDBEntry
*entry
= rhythmdb_entry_lookup_by_location (db
, url
);
1293 g_value_init (&val
, G_TYPE_ULONG
);
1294 g_value_set_ulong (&val
, 0);
1295 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &val
);
1296 g_value_unset (&val
);
1302 rb_podcast_manager_remove_feed (RBPodcastManager
*pd
, const char *url
, gboolean remove_files
)
1304 RhythmDBEntry
*entry
= rhythmdb_entry_lookup_by_location (pd
->priv
->db
, url
);
1307 rb_debug ("Removing podcast feed: %s remove_files: %d", url
, remove_files
);
1309 rb_podcast_manager_set_remove_files (pd
, remove_files
);
1310 rhythmdb_entry_delete (pd
->priv
->db
, entry
);
1311 rhythmdb_commit (pd
->priv
->db
);
1319 rb_podcast_manager_db_entry_deleted_cb (RBPodcastManager
*pd
,
1320 RhythmDBEntry
*entry
)
1322 RhythmDBEntryType type
= rhythmdb_entry_get_entry_type (entry
);
1324 if ((type
== RHYTHMDB_ENTRY_TYPE_PODCAST_POST
) && (pd
->priv
->remove_files
== TRUE
)) {
1325 const char *file_name
;
1326 const char *dir_name
;
1327 const char *conf_dir_name
;
1328 GnomeVFSResult result
;
1330 rb_debug ("Handling entry deleted");
1332 /* make sure we're not downloading it */
1333 rb_podcast_manager_cancel_download (pd
, entry
);
1335 file_name
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_MOUNTPOINT
);
1336 if (file_name
== NULL
) {
1337 /* episode has not been downloaded */
1338 rb_debug ("Episode not downloaded, skipping.");
1342 result
= gnome_vfs_unlink (file_name
);
1343 if (result
!= GNOME_VFS_OK
) {
1344 rb_debug ("Removing episode failed: %s", gnome_vfs_result_to_string (result
));
1349 rb_debug ("removing dir");
1350 conf_dir_name
= eel_gconf_get_string (CONF_STATE_PODCAST_DOWNLOAD_DIR
);
1352 dir_name
= g_build_filename (conf_dir_name
,
1353 rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ALBUM
),
1355 gnome_vfs_remove_directory (dir_name
);
1357 } else if (type
== RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
) {
1358 GtkTreeModel
*query_model
;
1361 query_model
= GTK_TREE_MODEL (rhythmdb_query_model_new_empty (pd
->priv
->db
));
1362 rhythmdb_do_full_query (pd
->priv
->db
,
1363 RHYTHMDB_QUERY_RESULTS (query_model
),
1364 RHYTHMDB_QUERY_PROP_EQUALS
,
1365 RHYTHMDB_PROP_TYPE
, RHYTHMDB_ENTRY_TYPE_PODCAST_POST
,
1366 RHYTHMDB_QUERY_PROP_LIKE
,
1367 RHYTHMDB_PROP_SUBTITLE
, rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
),
1368 RHYTHMDB_QUERY_END
);
1370 if (gtk_tree_model_get_iter_first (query_model
, &iter
)) {
1373 RhythmDBEntry
*entry
;
1375 gtk_tree_model_get (query_model
, &iter
, 0, &entry
, -1);
1376 has_next
= gtk_tree_model_iter_next (query_model
, &iter
);
1378 /* make sure we're not downloading it */
1379 rb_podcast_manager_cancel_download (pd
, entry
);
1381 rhythmdb_entry_delete (pd
->priv
->db
, entry
);
1382 rhythmdb_entry_unref (entry
);
1386 rhythmdb_commit (pd
->priv
->db
);
1389 g_object_unref (query_model
);
1394 rb_podcast_manager_cancel_download (RBPodcastManager
*pd
, RhythmDBEntry
*entry
)
1397 g_assert (rb_is_main_thread ());
1399 for (lst
= pd
->priv
->download_list
; lst
!= NULL
; lst
= lst
->next
) {
1400 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) lst
->data
;
1401 if (data
->entry
== entry
) {
1409 rb_podcast_manager_update_synctime (RBPodcastManager
*pd
)
1412 gint index
= eel_gconf_get_integer (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL
);
1416 case UPDATE_EVERY_HOUR
:
1417 value
= time (NULL
) + 3600;
1419 case UPDATE_EVERY_DAY
:
1420 value
= time (NULL
) + (3600 * 24);
1422 case UPDATE_EVERY_WEEK
:
1423 value
= time (NULL
) + (3600 * 24 * 7);
1425 case UPDATE_MANUALLY
:
1429 g_warning ("unknown download-inteval");
1433 eel_gconf_set_integer (CONF_STATE_PODCAST_DOWNLOAD_NEXT_TIME
, value
);
1434 eel_gconf_suggest_sync ();
1435 pd
->priv
->next_time
= value
;
1436 rb_podcast_manager_start_sync (pd
);
1440 rb_podcast_manager_config_changed (GConfClient
* client
,
1445 rb_podcast_manager_update_synctime (RB_PODCAST_MANAGER (user_data
));
1448 /* this bit really wants to die */
1451 rb_podcast_manager_set_remove_files (RBPodcastManager
*pd
, gboolean flag
)
1453 pd
->priv
->remove_files
= flag
;
1458 rb_podcast_manager_get_remove_files (RBPodcastManager
*pd
)
1460 return pd
->priv
->remove_files
;
1464 rb_podcast_manager_insert_feed (RBPodcastManager
*pd
, RBPodcastChannel
*data
)
1466 GValue description_val
= { 0, };
1467 GValue title_val
= { 0, };
1468 GValue subtitle_val
= { 0, };
1469 GValue summary_val
= { 0, };
1470 GValue lang_val
= { 0, };
1471 GValue copyright_val
= { 0, };
1472 GValue image_val
= { 0, };
1473 GValue author_val
= { 0, };
1474 GValue status_val
= { 0, };
1475 GValue last_post_val
= { 0, };
1476 GValue last_update_val
= { 0, };
1477 gulong last_post
= 0;
1478 gulong new_last_post
;
1479 GList
*download_entries
= NULL
;
1480 gboolean new_feed
, updated
, download_last
;
1481 RhythmDB
*db
= pd
->priv
->db
;
1483 RhythmDBEntry
*entry
;
1487 if (data
->title
== NULL
) {
1488 g_list_free (data
->posts
);
1495 /* processing podcast head */
1496 entry
= rhythmdb_entry_lookup_by_location (db
, (gchar
*)data
->url
);
1498 if (rhythmdb_entry_get_entry_type (entry
) != RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
)
1501 rb_debug ("Podcast feed entry for %s found", data
->url
);
1502 g_value_init (&status_val
, G_TYPE_ULONG
);
1503 g_value_set_ulong (&status_val
, 1);
1504 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &status_val
);
1505 g_value_unset (&status_val
);
1506 last_post
= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_POST_TIME
);
1509 rb_debug ("Adding podcast feed: %s", data
->url
);
1510 entry
= rhythmdb_entry_new (db
,
1511 RHYTHMDB_ENTRY_TYPE_PODCAST_FEED
,
1512 (gchar
*) data
->url
);
1516 g_value_init (&title_val
, G_TYPE_STRING
);
1517 g_value_set_string (&title_val
, (gchar
* ) data
->title
);
1518 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_TITLE
, &title_val
);
1519 g_value_unset (&title_val
);
1521 g_value_init (&author_val
, G_TYPE_STRING
);
1523 g_value_set_string (&author_val
, (gchar
*) data
->author
);
1525 g_value_set_static_string (&author_val
, _("Unknown"));
1526 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_ARTIST
, &author_val
);
1527 g_value_unset (&author_val
);
1529 if (data
->subtitle
) {
1530 g_value_init (&subtitle_val
, G_TYPE_STRING
);
1531 g_value_set_string (&subtitle_val
, (gchar
*) data
->subtitle
);
1532 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_SUBTITLE
, &subtitle_val
);
1533 g_value_unset (&subtitle_val
);
1536 if (data
->description
) {
1537 g_value_init (&description_val
, G_TYPE_STRING
);
1538 g_value_set_string (&description_val
, (gchar
*) data
->description
);
1539 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_DESCRIPTION
, &description_val
);
1540 g_value_unset (&description_val
);
1543 if (data
->summary
) {
1544 g_value_init (&summary_val
, G_TYPE_STRING
);
1545 g_value_set_string (&summary_val
, (gchar
*) data
->summary
);
1546 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_SUMMARY
, &summary_val
);
1547 g_value_unset (&summary_val
);
1551 g_value_init (&lang_val
, G_TYPE_STRING
);
1552 g_value_set_string (&lang_val
, (gchar
*) data
->lang
);
1553 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LANG
, &lang_val
);
1554 g_value_unset (&lang_val
);
1557 if (data
->copyright
) {
1558 g_value_init (©right_val
, G_TYPE_STRING
);
1559 g_value_set_string (©right_val
, (gchar
*) data
->copyright
);
1560 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_COPYRIGHT
, ©right_val
);
1561 g_value_unset (©right_val
);
1565 g_value_init (&image_val
, G_TYPE_STRING
);
1566 g_value_set_string (&image_val
, (gchar
*) data
->img
);
1567 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_IMAGE
, &image_val
);
1568 g_value_unset (&image_val
);
1571 g_value_init (&status_val
, G_TYPE_ULONG
);
1572 g_value_set_ulong (&status_val
, 1);
1573 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_STATUS
, &status_val
);
1574 g_value_unset (&status_val
);
1577 /* insert episodes */
1578 new_last_post
= last_post
;
1581 download_last
= (eel_gconf_get_integer (CONF_STATE_PODCAST_DOWNLOAD_INTERVAL
) != UPDATE_MANUALLY
);
1582 for (lst_songs
= data
->posts
; lst_songs
!= NULL
; lst_songs
= g_list_next (lst_songs
)) {
1583 RBPodcastItem
*item
= (RBPodcastItem
*) lst_songs
->data
;
1584 RhythmDBEntry
*post_entry
;
1586 if (item
->pub_date
> last_post
|| item
->pub_date
== 0) {
1590 rb_podcast_manager_add_post (db
,
1591 (gchar
*) data
->title
,
1592 (gchar
*) item
->title
,
1593 (gchar
*) data
->url
,
1594 (gchar
*) (item
->author
? item
->author
: data
->author
),
1595 (gchar
*) item
->url
,
1596 (gchar
*) item
->description
,
1597 (gulong
) (item
->pub_date
> 0 ? item
->pub_date
: data
->pub_date
),
1598 (gulong
) item
->duration
,
1600 if (post_entry
&& item
->pub_date
>= new_last_post
) {
1601 if (item
->pub_date
> new_last_post
) {
1602 g_list_free (download_entries
);
1603 download_entries
= NULL
;
1605 download_entries
= g_list_prepend (download_entries
, post_entry
);
1606 new_last_post
= item
->pub_date
;
1611 if (download_last
) {
1612 GValue status
= {0,};
1615 g_value_init (&status
, G_TYPE_ULONG
);
1616 g_value_set_ulong (&status
, RHYTHMDB_PODCAST_STATUS_WAITING
);
1617 for (t
= download_entries
; t
!= NULL
; t
= g_list_next (t
)) {
1618 rhythmdb_entry_set (db
,
1619 (RhythmDBEntry
*) t
->data
,
1620 RHYTHMDB_PROP_STATUS
,
1623 g_value_unset (&status
);
1625 g_list_free (download_entries
);
1628 g_signal_emit (pd
, rb_podcast_manager_signals
[FEED_UPDATES_AVAILABLE
],
1631 if (data
->pub_date
> new_last_post
)
1632 new_last_post
= data
->pub_date
;
1634 g_value_init (&last_post_val
, G_TYPE_ULONG
);
1635 g_value_set_ulong (&last_post_val
, new_last_post
);
1638 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_POST_TIME
, &last_post_val
);
1640 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_POST_TIME
, &last_post_val
);
1641 g_value_unset (&last_post_val
);
1643 g_value_init (&last_update_val
, G_TYPE_ULONG
);
1644 g_value_set_ulong (&last_update_val
, time(NULL
));
1647 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LAST_SEEN
, &last_update_val
);
1649 rhythmdb_entry_set (db
, entry
, RHYTHMDB_PROP_LAST_SEEN
, &last_update_val
);
1650 g_value_unset (&last_update_val
);
1652 rhythmdb_commit (db
);
1656 rb_podcast_manager_shutdown (RBPodcastManager
*pd
)
1660 g_assert (rb_is_main_thread ());
1662 lst
= g_list_reverse (pd
->priv
->download_list
);
1663 for (l
= lst
; l
!= NULL
; l
= l
->next
) {
1664 RBPodcastManagerInfo
*data
= (RBPodcastManagerInfo
*) l
->data
;
1669 pd
->priv
->shutdown
= TRUE
;
1673 rb_podcast_manager_get_podcast_dir (RBPodcastManager
*pd
)
1675 gchar
*conf_dir_name
= eel_gconf_get_string (CONF_STATE_PODCAST_DOWNLOAD_DIR
);
1677 if (conf_dir_name
== NULL
|| (strcmp (conf_dir_name
, "") == 0)) {
1678 conf_dir_name
= g_build_filename (g_get_home_dir (),
1681 eel_gconf_set_string (CONF_STATE_PODCAST_DOWNLOAD_DIR
, conf_dir_name
);
1684 return conf_dir_name
;