1 /* totem-uri.c
2
3 Copyright (C) 2004 Bastien Nocera
4
5 The Gnome Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The Gnome Library 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 GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the Gnome Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301 USA.
19
20 Author: Bastien Nocera <hadess@hadess.net>
21 */
22
23 #include "config.h"
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <gio/gio.h>
32
33 #define WANT_MIME_TYPES 1
34 #define WANT_AUDIO_MIME_TYPES 1
35 #define WANT_VIDEO_MIME_TYPES 1
36 #include "totem-mime-types.h"
37 #include "totem-uri.h"
38 #include "totem-private.h"
39
40 static GtkFileFilter *filter_all = NULL;
41 static GtkFileFilter *filter_subs = NULL;
42 static GtkFileFilter *filter_video = NULL;
43
44 gboolean
totem_playing_dvd(const char * uri)45 totem_playing_dvd (const char *uri)
46 {
47 if (uri == NULL)
48 return FALSE;
49
50 return g_str_has_prefix (uri, "dvd:/");
51 }
52
53 static void
totem_ensure_dir(const char * path)54 totem_ensure_dir (const char *path)
55 {
56 if (g_file_test (path, G_FILE_TEST_IS_DIR) != FALSE)
57 return;
58
59 g_mkdir_with_parents (path, 0700);
60 }
61
62 const char *
totem_dot_dir(void)63 totem_dot_dir (void)
64 {
65 static char *totem_dir = NULL;
66
67 if (totem_dir != NULL) {
68 totem_ensure_dir (totem_dir);
69 return totem_dir;
70 }
71
72 totem_dir = g_build_filename (g_get_user_config_dir (),
73 "totem",
74 NULL);
75
76 totem_ensure_dir (totem_dir);
77
78 return (const char *)totem_dir;
79 }
80
81 const char *
totem_data_dot_dir(void)82 totem_data_dot_dir (void)
83 {
84 static char *totem_dir = NULL;
85
86 if (totem_dir != NULL) {
87 totem_ensure_dir (totem_dir);
88 return totem_dir;
89 }
90
91 totem_dir = g_build_filename (g_get_user_data_dir (),
92 "totem",
93 NULL);
94
95 totem_ensure_dir (totem_dir);
96
97 return (const char *)totem_dir;
98 }
99
100 char *
totem_pictures_dir(void)101 totem_pictures_dir (void)
102 {
103 return g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_PICTURES));
104 }
105
106 static GMount *
totem_get_mount_for_uri(const char * path)107 totem_get_mount_for_uri (const char *path)
108 {
109 GMount *mount;
110 GFile *file;
111
112 file = g_file_new_for_path (path);
113 mount = g_file_find_enclosing_mount (file, NULL, NULL);
114 g_object_unref (file);
115
116 if (mount == NULL)
117 return NULL;
118
119 /* FIXME: We used to explicitly check whether it was a CD/DVD */
120 if (g_mount_can_eject (mount) == FALSE) {
121 g_object_unref (mount);
122 return NULL;
123 }
124
125 return mount;
126 }
127
128 static GMount *
totem_get_mount_for_dvd(const char * uri)129 totem_get_mount_for_dvd (const char *uri)
130 {
131 GMount *mount;
132 char *path;
133
134 mount = NULL;
135 path = g_strdup (uri + strlen ("dvd://"));
136
137 /* If it's a device, we need to find the volume that corresponds to it,
138 * and then the mount for the volume */
139 if (g_str_has_prefix (path, "/dev/")) {
140 GVolumeMonitor *volume_monitor;
141 GList *volumes, *l;
142
143 volume_monitor = g_volume_monitor_get ();
144 volumes = g_volume_monitor_get_volumes (volume_monitor);
145 g_object_unref (volume_monitor);
146
147 for (l = volumes; l != NULL; l = l->next) {
148 char *id;
149
150 id = g_volume_get_identifier (l->data, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
151 if (g_strcmp0 (id, path) == 0) {
152 g_free (id);
153 mount = g_volume_get_mount (l->data);
154 break;
155 }
156 g_free (id);
157 }
158 g_list_free_full (volumes, (GDestroyNotify) g_object_unref);
159 } else {
160 mount = totem_get_mount_for_uri (path);
161 g_free (path);
162 }
163 /* We have a path to the file itself */
164 return mount;
165 }
166
167 static char *
totem_get_mountpoint_for_vcd(const char * uri)168 totem_get_mountpoint_for_vcd (const char *uri)
169 {
170 return NULL;
171 }
172
173 GMount *
totem_get_mount_for_media(const char * uri)174 totem_get_mount_for_media (const char *uri)
175 {
176 GMount *ret;
177 char *mount_path;
178
179 if (uri == NULL)
180 return NULL;
181
182 mount_path = NULL;
183
184 if (g_str_has_prefix (uri, "dvd://") != FALSE)
185 return totem_get_mount_for_dvd (uri);
186 else if (g_str_has_prefix (uri, "vcd:") != FALSE)
187 mount_path = totem_get_mountpoint_for_vcd (uri);
188 else if (g_str_has_prefix (uri, "file:") != FALSE)
189 mount_path = g_filename_from_uri (uri, NULL, NULL);
190
191 if (mount_path == NULL)
192 return NULL;
193
194 ret = totem_get_mount_for_uri (mount_path);
195 g_free (mount_path);
196
197 return ret;
198 }
199
200 gboolean
totem_is_special_mrl(const char * uri)201 totem_is_special_mrl (const char *uri)
202 {
203 GMount *mount;
204
205 if (uri == NULL || g_str_has_prefix (uri, "file:") != FALSE)
206 return FALSE;
207 if (g_str_has_prefix (uri, "dvb:") != FALSE)
208 return TRUE;
209
210 mount = totem_get_mount_for_media (uri);
211 if (mount != NULL)
212 g_object_unref (mount);
213
214 return (mount != NULL);
215 }
216
217 gboolean
totem_is_block_device(const char * uri)218 totem_is_block_device (const char *uri)
219 {
220 struct stat buf;
221 char *local;
222
223 if (uri == NULL)
224 return FALSE;
225
226 if (g_str_has_prefix (uri, "file:") == FALSE)
227 return FALSE;
228 local = g_filename_from_uri (uri, NULL, NULL);
229 if (local == NULL)
230 return FALSE;
231 if (stat (local, &buf) != 0) {
232 g_free (local);
233 return FALSE;
234 }
235 g_free (local);
236
237 return (S_ISBLK (buf.st_mode));
238 }
239
240 char *
totem_create_full_path(const char * path)241 totem_create_full_path (const char *path)
242 {
243 GFile *file;
244 char *retval;
245
246 g_return_val_if_fail (path != NULL, NULL);
247
248 if (strstr (path, "://") != NULL)
249 return NULL;
250 if (totem_is_special_mrl (path) != FALSE)
251 return NULL;
252
253 file = g_file_new_for_commandline_arg (path);
254 retval = g_file_get_uri (file);
255 g_object_unref (file);
256
257 return retval;
258 }
259
260 static void
totem_object_on_unmount(GVolumeMonitor * volume_monitor,GMount * mount,Totem * totem)261 totem_object_on_unmount (GVolumeMonitor *volume_monitor,
262 GMount *mount,
263 Totem *totem)
264 {
265 totem_playlist_clear_with_g_mount (totem->playlist, mount);
266 }
267
268 void
totem_setup_file_monitoring(Totem * totem)269 totem_setup_file_monitoring (Totem *totem)
270 {
271 totem->monitor = g_volume_monitor_get ();
272
273 g_signal_connect (G_OBJECT (totem->monitor),
274 "mount-pre-unmount",
275 G_CALLBACK (totem_object_on_unmount),
276 totem);
277 g_signal_connect (G_OBJECT (totem->monitor),
278 "mount-removed",
279 G_CALLBACK (totem_object_on_unmount),
280 totem);
281 }
282
283 /* List from xine-lib's demux_sputext.c.
284 * Keep in sync with the list in totem_setup_file_filters() in this file.
285 * Don't add .txt extensions, as there are too many false positives. */
286 static const char subtitle_ext[][4] = {
287 "sub",
288 "srt",
289 "vtt",
290 "smi",
291 "ssa",
292 "ass",
293 "mpl",
294 "asc"
295 };
296
297 gboolean
totem_uri_is_subtitle(const char * uri)298 totem_uri_is_subtitle (const char *uri)
299 {
300 guint len, i;
301
302 len = strlen (uri);
303 if (len < 4 || uri[len - 4] != '.')
304 return FALSE;
305 for (i = 0; i < G_N_ELEMENTS (subtitle_ext); i++) {
306 if (g_str_has_suffix (uri, subtitle_ext[i]) != FALSE)
307 return TRUE;
308 }
309 return FALSE;
310 }
311
312 char *
totem_uri_escape_for_display(const char * uri)313 totem_uri_escape_for_display (const char *uri)
314 {
315 GFile *file;
316 char *disp;
317
318 file = g_file_new_for_uri (uri);
319 disp = g_file_get_parse_name (file);
320 g_object_unref (file);
321
322 return disp;
323 }
324
325 void
totem_setup_file_filters(void)326 totem_setup_file_filters (void)
327 {
328 guint i;
329
330 filter_all = gtk_file_filter_new ();
331 gtk_file_filter_set_name (filter_all, _("All files"));
332 gtk_file_filter_add_pattern (filter_all, "*");
333 g_object_ref_sink (filter_all);
334
335 /* Video files */
336 filter_video = gtk_file_filter_new ();
337 gtk_file_filter_set_name (filter_video, _("Video files"));
338 for (i = 0; video_mime_types[i] != NULL; i++) {
339 gtk_file_filter_add_mime_type (filter_video, video_mime_types[i]);
340 }
341 /* Add the special Disc-as-files formats */
342 gtk_file_filter_add_mime_type (filter_video, "application/x-cd-image");
343 gtk_file_filter_add_mime_type (filter_video, "application/x-cue");
344 g_object_ref_sink (filter_video);
345
346 /* Subtitles files. Keep in sync with subtitle_ext in this file. */
347 filter_subs = gtk_file_filter_new ();
348 gtk_file_filter_set_name (filter_subs, _("Subtitle files"));
349 gtk_file_filter_add_mime_type (filter_subs, "application/x-subrip"); /* *.srt */
350 gtk_file_filter_add_mime_type (filter_subs, "text/plain"); /* *.asc, *.txt */
351 gtk_file_filter_add_mime_type (filter_subs, "text/x-mpl2"); /* *.mpl */
352 gtk_file_filter_add_mime_type (filter_subs, "text/vtt"); /* *.vtt */
353 gtk_file_filter_add_mime_type (filter_subs, "application/x-sami"); /* *.smi, *.sami */
354 gtk_file_filter_add_mime_type (filter_subs, "text/x-microdvd"); /* *.sub */
355 gtk_file_filter_add_mime_type (filter_subs, "text/x-mpsub"); /* *.sub */
356 gtk_file_filter_add_mime_type (filter_subs, "text/x-ssa"); /* *.ssa, *.ass */
357 gtk_file_filter_add_mime_type (filter_subs, "text/x-subviewer"); /* *.sub */
358 g_object_ref_sink (filter_subs);
359 }
360
361 void
totem_destroy_file_filters(void)362 totem_destroy_file_filters (void)
363 {
364 if (filter_all != NULL) {
365 g_object_unref (filter_all);
366 filter_all = NULL;
367 g_object_unref (filter_video);
368 g_object_unref (filter_subs);
369 }
370 }
371
372 static const GUserDirectory dir_types[] = {
373 G_USER_DIRECTORY_VIDEOS,
374 G_USER_DIRECTORY_MUSIC
375 };
376
377 static void
totem_add_default_dirs(GtkFileChooser * dialog)378 totem_add_default_dirs (GtkFileChooser *dialog)
379 {
380 guint i;
381 for (i = 0; i < G_N_ELEMENTS (dir_types); i++) {
382 const char *dir;
383
384 dir = g_get_user_special_dir (dir_types[i]);
385 if (dir == NULL)
386 continue;
387 gtk_file_chooser_add_shortcut_folder (dialog, dir, NULL);
388 }
389 }
390
391 char *
totem_add_subtitle(GtkWindow * parent,const char * uri)392 totem_add_subtitle (GtkWindow *parent, const char *uri)
393 {
394 GtkWidget *fs;
395 GSettings *settings;
396 char *new_path;
397 char *subtitle = NULL;
398 gboolean folder_set;
399
400 fs = gtk_file_chooser_dialog_new (_("Select Text Subtitles"),
401 parent,
402 GTK_FILE_CHOOSER_ACTION_OPEN,
403 _("_Cancel"), GTK_RESPONSE_CANCEL,
404 _("_Open"), GTK_RESPONSE_ACCEPT,
405 NULL);
406 gtk_dialog_set_default_response (GTK_DIALOG (fs), GTK_RESPONSE_ACCEPT);
407 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), FALSE);
408 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (fs), filter_subs);
409
410 settings = g_settings_new (TOTEM_GSETTINGS_SCHEMA);
411 folder_set = FALSE;
412
413 /* Add the subtitles cache dir as a shortcut */
414 new_path = g_build_filename (g_get_user_cache_dir (),
415 "totem",
416 "subtitles",
417 NULL);
418 gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (fs), new_path, NULL);
419 g_free (new_path);
420
421 /* Add the last open path as a shortcut */
422 new_path = g_settings_get_string (settings, "open-uri");
423 if (*new_path != '\0')
424 gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (fs), new_path, NULL);
425 g_free (new_path);
426
427 /* Try to set the passed path as the current folder */
428 if (uri != NULL) {
429 folder_set = gtk_file_chooser_set_current_folder_uri
430 (GTK_FILE_CHOOSER (fs), uri);
431 gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (fs), uri, NULL);
432 }
433
434 /* And set it as home if it fails */
435 if (folder_set == FALSE) {
436 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fs),
437 g_get_home_dir ());
438 }
439 totem_add_default_dirs (GTK_FILE_CHOOSER (fs));
440
441 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT) {
442 subtitle = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fs));
443 }
444
445 gtk_widget_destroy (fs);
446 g_object_unref (settings);
447
448 return subtitle;
449 }
450
451 GSList *
totem_add_files(GtkWindow * parent,const char * path)452 totem_add_files (GtkWindow *parent, const char *path)
453 {
454 GtkWidget *fs;
455 int response;
456 GSList *filenames;
457 char *mrl, *new_path;
458 GSettings *settings;
459 gboolean set_folder;
460
461 fs = gtk_file_chooser_dialog_new (_("Add Videos"),
462 parent,
463 GTK_FILE_CHOOSER_ACTION_OPEN,
464 _("_Cancel"), GTK_RESPONSE_CANCEL,
465 _("_Add"), GTK_RESPONSE_ACCEPT,
466 NULL);
467 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (fs), filter_video);
468 gtk_dialog_set_default_response (GTK_DIALOG (fs), GTK_RESPONSE_ACCEPT);
469 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (fs), TRUE);
470 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), FALSE);
471
472 settings = g_settings_new (TOTEM_GSETTINGS_SCHEMA);
473 set_folder = TRUE;
474 if (path != NULL) {
475 set_folder = gtk_file_chooser_set_current_folder_uri
476 (GTK_FILE_CHOOSER (fs), path);
477 } else {
478 new_path = g_settings_get_string (settings, "open-uri");
479 if (*new_path != '\0') {
480 set_folder = gtk_file_chooser_set_current_folder_uri
481 (GTK_FILE_CHOOSER (fs), new_path);
482 }
483 g_free (new_path);
484 }
485
486 /* We didn't manage to change the directory */
487 if (set_folder == FALSE) {
488 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fs),
489 g_get_home_dir ());
490 }
491 totem_add_default_dirs (GTK_FILE_CHOOSER (fs));
492
493 response = gtk_dialog_run (GTK_DIALOG (fs));
494
495 filenames = NULL;
496 if (response == GTK_RESPONSE_ACCEPT)
497 filenames = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (fs));
498
499 if (filenames == NULL) {
500 gtk_widget_destroy (fs);
501 g_object_unref (settings);
502 return NULL;
503 }
504 gtk_widget_destroy (fs);
505
506 mrl = filenames->data;
507 if (mrl != NULL) {
508 new_path = g_path_get_dirname (mrl);
509 g_settings_set_string (settings, "open-uri", new_path);
510 g_free (new_path);
511 }
512
513 g_object_unref (settings);
514
515 return filenames;
516 }
517