1/*- 2 * Copyright (c) 2013-2019 elementary, Inc. (https://elementary.io) 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * Authored by: Corentin Noël <corentin@elementaryos.org> 18 */ 19 20public class Audience.Widgets.SettingsPopover : Gtk.Popover { 21 public bool is_setup = false; 22 23 private Gtk.ComboBoxText languages; 24 private Gtk.ComboBoxText subtitles; 25 private Gtk.FileChooserButton external_subtitle_file; 26 private ClutterGst.Playback playback; 27 28 public SettingsPopover (ClutterGst.Playback playback) { 29 this.playback = playback; 30 opacity = GLOBAL_OPACITY; 31 32 languages = new Gtk.ComboBoxText (); 33 subtitles = new Gtk.ComboBoxText (); 34 35 var all_files_filter = new Gtk.FileFilter (); 36 all_files_filter.set_filter_name (_("All files")); 37 all_files_filter.add_pattern ("*"); 38 39 var subtitle_files_filter = new Gtk.FileFilter (); 40 subtitle_files_filter.set_filter_name (_("Subtitle files")); 41 subtitle_files_filter.add_mime_type ("application/smil"); // .smi 42 subtitle_files_filter.add_mime_type ("application/x-subrip"); // .srt 43 subtitle_files_filter.add_mime_type ("text/x-microdvd"); // .sub 44 subtitle_files_filter.add_mime_type ("text/x-ssa"); // .ssa & .ass 45 // exclude .asc, mimetype is generic "application/pgp-encrypted" 46 47 external_subtitle_file = new Gtk.FileChooserButton (_("External Subtitles"), Gtk.FileChooserAction.OPEN); 48 external_subtitle_file.add_filter (subtitle_files_filter); 49 external_subtitle_file.add_filter (all_files_filter); 50 51 var lang_label = new Gtk.Label (_("Audio:")); 52 lang_label.halign = Gtk.Align.END; 53 54 var sub_label = new Gtk.Label (_("Subtitles:")); 55 sub_label.halign = Gtk.Align.END; 56 57 var sub_ext_label = new Gtk.Label (_("External Subtitles:")); 58 sub_ext_label.halign = Gtk.Align.END; 59 60 var setupgrid = new Gtk.Grid (); 61 setupgrid.row_spacing = 6; 62 setupgrid.margin = 6; 63 setupgrid.attach (lang_label, 0, 1, 1, 1); 64 setupgrid.attach (languages, 1, 1, 1, 1); 65 setupgrid.attach (sub_label, 0, 2, 1, 1); 66 setupgrid.attach (subtitles, 1, 2, 1, 1); 67 setupgrid.attach (sub_ext_label, 0, 3, 1, 1); 68 setupgrid.attach (external_subtitle_file, 1, 3, 1, 1); 69 setupgrid.column_spacing = 12; 70 71 external_subtitle_file.file_set.connect (() => { 72 App.get_instance ().mainwindow.player_page.set_subtitle (external_subtitle_file.get_uri ()); 73 }); 74 75 unowned Gst.Pipeline pipeline = playback.get_pipeline () as Gst.Pipeline; 76 /* playback.subtitle_uri does not seem to notify so connect directly to the pipeline */ 77 pipeline.notify["suburi"].connect (() => { 78 /* Easier to retrieve the uri from the playback than the pipeline */ 79 external_subtitle_file.select_uri (playback.subtitle_uri ?? ""); 80 }); 81 82 subtitles.changed.connect (on_subtitles_changed); 83 84 languages.changed.connect (on_languages_changed); 85 86 add (setupgrid); 87 } 88 89 public void setup () { 90 if (!is_setup) { 91 is_setup = true; 92 setup_text (); 93 setup_audio (); 94 } 95 } 96 97 private void on_subtitles_changed () { 98 if (subtitles.active < 0) { 99 return; 100 } 101 102 if (subtitles.active_id == "none") { 103 playback.subtitle_track = -1; 104 } else { 105 playback.subtitle_track = subtitles.active; 106 } 107 } 108 109 private void on_languages_changed () { 110 if (languages.active < 0 || languages.active_id == "def") { 111 return; 112 } 113 114 playback.audio_stream = languages.active; 115 } 116 117 private void setup_text () { 118 subtitles.changed.disconnect (on_subtitles_changed); 119 120 if (subtitles.model.iter_n_children (null) > 0) { 121 subtitles.remove_all (); 122 } 123 124 uint track = 1; 125 playback.get_subtitle_tracks ().foreach ((lang) => { 126 // FIXME: Using Track since lang is actually a bad pointer :/ 127 subtitles.append (lang, _("Track %u").printf (track++)); 128 }); 129 subtitles.append ("none", _("None")); 130 131 int count = subtitles.model.iter_n_children (null); 132 subtitles.sensitive = count > 1; 133 if (subtitles.sensitive && (playback.subtitle_track >= 0)) { 134 subtitles.active = playback.subtitle_track; 135 } else { 136 subtitles.active = count - 1; 137 } 138 139 subtitles.changed.connect (on_subtitles_changed); 140 } 141 142 private void setup_audio () { 143 languages.changed.disconnect (on_languages_changed); 144 145 if (languages.model.iter_n_children (null) > 0) { 146 languages.remove_all (); 147 } 148 149 var languages_names = get_audio_track_names (); 150 uint track = 1; 151 playback.get_audio_streams ().foreach ((lang) => { 152 var audio_stream_lang = languages_names.nth_data (track - 1); 153 if (audio_stream_lang != null) { 154 languages.append (lang, audio_stream_lang); 155 } else { 156 languages.append (lang, _("Track %u").printf (track)); 157 } 158 track++; 159 }); 160 161 int count = languages.model.iter_n_children (null); 162 languages.sensitive = count > 1; 163 if (languages.sensitive) { 164 languages.active = playback.audio_stream; 165 } else { 166 if (count != 0) { 167 languages.remove_all (); 168 } 169 languages.append ("def", _("Default")); 170 languages.active = 0; 171 } 172 173 languages.changed.connect (on_languages_changed); 174 } 175 176 public void next_audio () { 177 setup (); 178 int count = languages.model.iter_n_children (null); 179 if (count > 0) { 180 languages.active = (languages.active + 1) % count; 181 } 182 } 183 184 public void next_text () { 185 setup (); 186 int count = subtitles.model.iter_n_children (null); 187 if (count > 0) { 188 subtitles.active = (subtitles.active + 1) % count; 189 } 190 } 191 192 private GLib.List<string?> get_audio_track_names () { 193 GLib.List<string?> audio_languages = null; 194 195 var discoverer_info = Audience.get_discoverer_info (playback.uri); 196 if (discoverer_info != null) { 197 var audio_streams = discoverer_info.get_audio_streams (); 198 199 foreach (var audio_stream in audio_streams) { 200 unowned string language_code = ((Gst.PbUtils.DiscovererAudioInfo)(audio_stream)).get_language (); 201 if (language_code != null) { 202 var language_name = Gst.Tag.get_language_name (language_code); 203 audio_languages.append (language_name); 204 } else { 205 audio_languages.append (null); 206 } 207 } 208 209 // Both ClutterGst and DiscovererAudioInfo return tracks in opposite order. 210 audio_languages.reverse (); 211 } 212 213 return audio_languages; 214 } 215 216 217} 218