Status: | Needs review | ||||||||
---|---|---|---|---|---|---|---|---|---|
Proposed branch: | lp:~agronick/vocal/vocal | ||||||||
Merge into: | lp:vocal | ||||||||
Diff against target: |
448 lines (+114/-117) 4 files modified
CMakeLists.txt (+2/-1) src/Objects/Episode.vala (+7/-3) src/Utils/Player.vala (+103/-112) src/Widgets/EpisodeDetailBox.vala (+2/-1) |
||||||||
To merge this branch: | bzr merge lp:~agronick/vocal/vocal | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nathan Dyer | Pending | ||
Review via email: mp+261302@code.launchpad.net |
Commit message
Description of the change
Vocal was crashing with a segmentation fault when playing certain videos. When I looked at the URL I found that it was doing a 302 redirect and setting the Location header. In Player.vala Gst.PbUtils.
Many podcasting services use redirects for analytics and tracking. All of the videos in this RSS feed will crash Vocal unless my code is used: http://
The new code also prevents Vocal from cashing if get_video_streams() returns an empty list for some other reason.
Kyle (agronick) wrote : | # |
With regard to the first bug, some of the URLs in the feed may work. If you use this URL:
http://
It is one that will definitely crash with the current code and work with the new code.
Kyle (agronick) wrote : | # |
Turns out redirects were not the issue with the original bug. The timeouts were too small. Removed redirect handling code. Kept code preventing segmentation faults. CMakeLists.txt can be kept in it's original state but we probably want to remove the -g option for release builds. The first fix is in Player.vala and the second one is in Episode.vala. Email me for any questions: <email address hidden>
Unmerged revisions
- 299. By Kyle
-
New seek method makes the player a lot more responsive.
- 298. By Kyle
-
Turns out redirects were not the issue with the origional bug. The timeouts were too small. Removed redirect handling code. Kept code preventing segmentation faults. Increased timeouts.
- 297. By Kyle
-
Merging changes
- 296. By Kyle
-
Fixed segfault with certain podcasts
- 295. By Kyle
-
Fixed bug with redirecting URLs
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2015-04-05 16:46:38 +0000 | |||
3 | +++ CMakeLists.txt 2015-06-08 19:25:26 +0000 | |||
4 | @@ -15,7 +15,7 @@ | |||
5 | 15 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) | 15 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) |
6 | 16 | 16 | ||
7 | 17 | file(GLOB_RECURSE sources src/*.vala) | 17 | file(GLOB_RECURSE sources src/*.vala) |
9 | 18 | 18 | ||
10 | 19 | # Some configuration | 19 | # Some configuration |
11 | 20 | configure_file (${CMAKE_SOURCE_DIR}/src/config.vala.cmake ${CMAKE_SOURCE_DIR}/src/config.vala) | 20 | configure_file (${CMAKE_SOURCE_DIR}/src/config.vala.cmake ${CMAKE_SOURCE_DIR}/src/config.vala) |
12 | 21 | 21 | ||
13 | @@ -35,6 +35,7 @@ | |||
14 | 35 | --disable-warnings | 35 | --disable-warnings |
15 | 36 | --target-glib=2.32 | 36 | --target-glib=2.32 |
16 | 37 | --thread | 37 | --thread |
17 | 38 | --save-temps | ||
18 | 38 | -g | 39 | -g |
19 | 39 | ) | 40 | ) |
20 | 40 | 41 | ||
21 | 41 | 42 | ||
22 | === modified file 'src/Objects/Episode.vala' | |||
23 | --- src/Objects/Episode.vala 2015-04-17 01:14:15 +0000 | |||
24 | +++ src/Objects/Episode.vala 2015-06-08 19:25:26 +0000 | |||
25 | @@ -122,15 +122,19 @@ | |||
26 | 122 | offset = -2; | 122 | offset = -2; |
27 | 123 | } | 123 | } |
28 | 124 | 124 | ||
31 | 125 | 125 | if(fields.length < 7) | |
32 | 126 | 126 | return; | |
33 | 127 | |||
34 | 127 | // Since the pubdate is a standard, we can hard-code the values where each item is located | 128 | // Since the pubdate is a standard, we can hard-code the values where each item is located |
35 | 128 | |||
36 | 129 | int day = int.parse(fields[2 + offset]); | 129 | int day = int.parse(fields[2 + offset]); |
37 | 130 | int year = int.parse(fields[4 + offset]); | 130 | int year = int.parse(fields[4 + offset]); |
38 | 131 | int hour = int.parse(fields[5 + offset]); | 131 | int hour = int.parse(fields[5 + offset]); |
39 | 132 | int minute = int.parse(fields[6 + offset]); | 132 | int minute = int.parse(fields[6 + offset]); |
40 | 133 | int seconds = int.parse(fields[7 + offset]); | 133 | int seconds = int.parse(fields[7 + offset]); |
41 | 134 | |||
42 | 135 | if(day == 0 || year == 0) | ||
43 | 136 | return; | ||
44 | 137 | |||
45 | 134 | 138 | ||
46 | 135 | 139 | ||
47 | 136 | // Determine the month number from the string as read from the pubdate | 140 | // Determine the month number from the string as read from the pubdate |
48 | 137 | 141 | ||
49 | === modified file 'src/Utils/Player.vala' | |||
50 | --- src/Utils/Player.vala 2015-04-04 21:51:53 +0000 | |||
51 | +++ src/Utils/Player.vala 2015-06-08 19:25:26 +0000 | |||
52 | @@ -20,18 +20,18 @@ | |||
53 | 20 | using Clutter; | 20 | using Clutter; |
54 | 21 | using GLib; | 21 | using GLib; |
55 | 22 | using Gst; | 22 | using Gst; |
57 | 23 | using Gst.PbUtils; | 23 | using Gst.PbUtils; |
58 | 24 | 24 | ||
59 | 25 | namespace Vocal { | 25 | namespace Vocal { |
60 | 26 | class Player : Actor { | 26 | class Player : Actor { |
61 | 27 | 27 | ||
63 | 28 | private static Player? player = null; | 28 | private static Player? player = null; |
64 | 29 | public static Player? get_default (string[] args) { | 29 | public static Player? get_default (string[] args) { |
65 | 30 | if (player == null) | 30 | if (player == null) |
66 | 31 | player = new Player(args); | 31 | player = new Player(args); |
67 | 32 | return player; | 32 | return player; |
68 | 33 | } | 33 | } |
70 | 34 | 34 | ||
71 | 35 | public signal void state_changed(Gst.State new_state); | 35 | public signal void state_changed(Gst.State new_state); |
72 | 36 | public signal void stream_ended(); | 36 | public signal void stream_ended(); |
73 | 37 | public signal void additional_plugins_required(Gst.Message message); | 37 | public signal void additional_plugins_required(Gst.Message message); |
74 | @@ -43,20 +43,20 @@ | |||
75 | 43 | 43 | ||
76 | 44 | private MainLoop loop = new MainLoop (); | 44 | private MainLoop loop = new MainLoop (); |
77 | 45 | public signal void new_position_available(); | 45 | public signal void new_position_available(); |
80 | 46 | private string tag_string; | 46 | private string tag_string; |
81 | 47 | 47 | ||
82 | 48 | public bool is_currently_playing; | 48 | public bool is_currently_playing; |
84 | 49 | 49 | ||
85 | 50 | public Gst.State current_state; | 50 | public Gst.State current_state; |
87 | 51 | 51 | ||
88 | 52 | public Episode current_episode; | 52 | public Episode current_episode; |
90 | 53 | 53 | ||
91 | 54 | private Player(string[]? args) { | 54 | private Player(string[]? args) { |
92 | 55 | 55 | ||
95 | 56 | /* NOTE: | 56 | /* NOTE: |
96 | 57 | This code is heavily influenced by the Audience source code, written by Tom Beckmann <tomjonabc@gmail.com> | 57 | This code is heavily influenced by the Audience source code, written by Tom Beckmann <tomjonabc@gmail.com> |
97 | 58 | and Corentin Noël <corentin@elementaryos.org>, available at https://launchpad.net/audience. | 58 | and Corentin Noël <corentin@elementaryos.org>, available at https://launchpad.net/audience. |
99 | 59 | */ | 59 | */ |
100 | 60 | 60 | ||
101 | 61 | // Set up the Clutter actor | 61 | // Set up the Clutter actor |
102 | 62 | video = new Clutter.Texture (); | 62 | video = new Clutter.Texture (); |
103 | @@ -77,7 +77,7 @@ | |||
104 | 77 | current_episode = null; | 77 | current_episode = null; |
105 | 78 | 78 | ||
106 | 79 | is_currently_playing = false; | 79 | is_currently_playing = false; |
108 | 80 | 80 | ||
109 | 81 | // Check every half-second if the current media is playing, and if it is | 81 | // Check every half-second if the current media is playing, and if it is |
110 | 82 | // send a signal that there is a new position available | 82 | // send a signal that there is a new position available |
111 | 83 | GLib.Timeout.add(500, () => { | 83 | GLib.Timeout.add(500, () => { |
112 | @@ -85,14 +85,14 @@ | |||
113 | 85 | new_position_available(); | 85 | new_position_available(); |
114 | 86 | return true; | 86 | return true; |
115 | 87 | }); | 87 | }); |
117 | 88 | 88 | ||
118 | 89 | } | 89 | } |
120 | 90 | 90 | ||
121 | 91 | /* | 91 | /* |
122 | 92 | * Bus callback | 92 | * Bus callback |
123 | 93 | */ | 93 | */ |
124 | 94 | private bool bus_callback (Gst.Bus bus, Gst.Message message) { | 94 | private bool bus_callback (Gst.Bus bus, Gst.Message message) { |
126 | 95 | 95 | ||
127 | 96 | switch (message.type) { | 96 | switch (message.type) { |
128 | 97 | case MessageType.ERROR: | 97 | case MessageType.ERROR: |
129 | 98 | GLib.Error err; | 98 | GLib.Error err; |
130 | @@ -115,11 +115,11 @@ | |||
131 | 115 | Gst.State oldstate; | 115 | Gst.State oldstate; |
132 | 116 | Gst.State newstate; | 116 | Gst.State newstate; |
133 | 117 | Gst.State pending; | 117 | Gst.State pending; |
136 | 118 | 118 | ||
137 | 119 | 119 | ||
138 | 120 | message.parse_state_changed (out oldstate, out newstate, | 120 | message.parse_state_changed (out oldstate, out newstate, |
139 | 121 | out pending); | 121 | out pending); |
141 | 122 | 122 | ||
142 | 123 | current_state = newstate; | 123 | current_state = newstate; |
143 | 124 | state_changed(newstate); | 124 | state_changed(newstate); |
144 | 125 | 125 | ||
145 | @@ -135,30 +135,30 @@ | |||
146 | 135 | 135 | ||
147 | 136 | return true; | 136 | return true; |
148 | 137 | } | 137 | } |
150 | 138 | 138 | ||
151 | 139 | /* | 139 | /* |
154 | 140 | * Reconfigures the video (width, positions, etc.) as needed | 140 | * Reconfigures the video (width, positions, etc.) as needed |
155 | 141 | */ | 141 | */ |
156 | 142 | public void configure_video() { | 142 | public void configure_video() { |
157 | 143 | 143 | ||
173 | 144 | // Make sure the video has both a known width and height at this point | 144 | // Make sure the video has both a known width and height at this point |
174 | 145 | if(video_width > 0 && video_height > 0) { | 145 | if(video_width > 0 && video_height > 0) { |
175 | 146 | 146 | ||
176 | 147 | // Get the stage | 147 | // Get the stage |
177 | 148 | var stage = get_stage (); | 148 | var stage = get_stage (); |
178 | 149 | 149 | ||
179 | 150 | // Calculate the videos aspect ratio | 150 | // Calculate the videos aspect ratio |
180 | 151 | 151 | ||
181 | 152 | var aspect = (float)stage.width / (float)video_width < (float)stage.height / (float)video_height ? | 152 | var aspect = (float)stage.width / (float)video_width < (float)stage.height / (float)video_height ? |
182 | 153 | (float)stage.width / (float)video_width : (float)stage.height / (float)video_height; | 153 | (float)stage.width / (float)video_width : (float)stage.height / (float)video_height; |
183 | 154 | 154 | ||
184 | 155 | video.width = video_width * aspect; | 155 | video.width = video_width * aspect; |
185 | 156 | video.height = video_height * aspect; | 156 | video.height = video_height * aspect; |
186 | 157 | video.x = (stage.width - video.width) / 2; | 157 | video.x = (stage.width - video.width) / 2; |
187 | 158 | video.y = (stage.height - video.height) / 2; | 158 | video.y = (stage.height - video.height) / 2; |
188 | 159 | } | 159 | } |
189 | 160 | } | 160 | } |
191 | 161 | 161 | ||
192 | 162 | /* | 162 | /* |
193 | 163 | * Iterates through a list of tags and sets matching properties | 163 | * Iterates through a list of tags and sets matching properties |
194 | 164 | */ | 164 | */ |
195 | @@ -171,28 +171,28 @@ | |||
196 | 171 | break; | 171 | break; |
197 | 172 | } | 172 | } |
198 | 173 | } | 173 | } |
200 | 174 | 174 | ||
201 | 175 | /* | 175 | /* |
202 | 176 | * Returns the total duration of the currently playing media | 176 | * Returns the total duration of the currently playing media |
203 | 177 | */ | 177 | */ |
204 | 178 | public int64 get_duration () { | 178 | public int64 get_duration () { |
205 | 179 | int64 rv = (int64)0; | 179 | int64 rv = (int64)0; |
206 | 180 | Gst.Format f = Gst.Format.TIME; | 180 | Gst.Format f = Gst.Format.TIME; |
208 | 181 | 181 | ||
209 | 182 | playbin.query_duration (f, out rv); | 182 | playbin.query_duration (f, out rv); |
211 | 183 | 183 | ||
212 | 184 | return rv; | 184 | return rv; |
213 | 185 | } | 185 | } |
215 | 186 | 186 | ||
216 | 187 | /* | 187 | /* |
217 | 188 | * Returns the current position of the media | 188 | * Returns the current position of the media |
218 | 189 | */ | 189 | */ |
219 | 190 | public int64 get_position () { | 190 | public int64 get_position () { |
220 | 191 | int64 rv = (int64)0; | 191 | int64 rv = (int64)0; |
221 | 192 | Gst.Format f = Gst.Format.TIME; | 192 | Gst.Format f = Gst.Format.TIME; |
223 | 193 | 193 | ||
224 | 194 | playbin.query_position (f, out rv); | 194 | playbin.query_position (f, out rv); |
226 | 195 | 195 | ||
227 | 196 | return rv; | 196 | return rv; |
228 | 197 | } | 197 | } |
229 | 198 | /* | 198 | /* |
230 | @@ -202,21 +202,21 @@ | |||
231 | 202 | } | 202 | } |
232 | 203 | 203 | ||
233 | 204 | public double get_volume () { | 204 | public double get_volume () { |
238 | 205 | var val = GLib.Value (typeof(double)); | 205 | var val = GLib.Value (typeof(double)); |
239 | 206 | playbin.get_property ("volume", ref val); | 206 | playbin.get_property ("volume", ref val); |
240 | 207 | return (double)val; | 207 | return (double)val; |
241 | 208 | } | 208 | } |
242 | 209 | */ | 209 | */ |
245 | 210 | 210 | ||
246 | 211 | /* | 211 | /* |
247 | 212 | * Sets the current state to pause | 212 | * Sets the current state to pause |
248 | 213 | */ | 213 | */ |
249 | 214 | public void pause () { | 214 | public void pause () { |
250 | 215 | set_state (Gst.State.PAUSED); | 215 | set_state (Gst.State.PAUSED); |
251 | 216 | is_currently_playing = false; | 216 | is_currently_playing = false; |
252 | 217 | } | 217 | } |
255 | 218 | 218 | ||
256 | 219 | /* | 219 | /* |
257 | 220 | * Sets the current state to playing | 220 | * Sets the current state to playing |
258 | 221 | */ | 221 | */ |
259 | 222 | public void play () { | 222 | public void play () { |
260 | @@ -225,7 +225,7 @@ | |||
261 | 225 | 225 | ||
262 | 226 | is_currently_playing = true; | 226 | is_currently_playing = true; |
263 | 227 | } | 227 | } |
265 | 228 | 228 | ||
266 | 229 | /* | 229 | /* |
267 | 230 | * Seeks backward in the currently playing media n number of seconds | 230 | * Seeks backward in the currently playing media n number of seconds |
268 | 231 | */ | 231 | */ |
269 | @@ -235,13 +235,13 @@ | |||
270 | 235 | int64 current = this.get_position(); | 235 | int64 current = this.get_position(); |
271 | 236 | int64 new_position = current - mil; | 236 | int64 new_position = current - mil; |
272 | 237 | this.set_position(new_position); | 237 | this.set_position(new_position); |
274 | 238 | 238 | ||
275 | 239 | new_position_available(); | 239 | new_position_available(); |
277 | 240 | 240 | ||
278 | 241 | // If it was paused before, pause it again | 241 | // If it was paused before, pause it again |
279 | 242 | set_state(previous); | 242 | set_state(previous); |
280 | 243 | } | 243 | } |
282 | 244 | 244 | ||
283 | 245 | /* | 245 | /* |
284 | 246 | * Seeks forward in the currently playing media n number of seconds | 246 | * Seeks forward in the currently playing media n number of seconds |
285 | 247 | */ | 247 | */ |
286 | @@ -251,13 +251,13 @@ | |||
287 | 251 | int64 current = this.get_position(); | 251 | int64 current = this.get_position(); |
288 | 252 | int64 new_position = current + mil; | 252 | int64 new_position = current + mil; |
289 | 253 | this.set_position(new_position); | 253 | this.set_position(new_position); |
291 | 254 | 254 | ||
292 | 255 | new_position_available(); | 255 | new_position_available(); |
294 | 256 | 256 | ||
295 | 257 | // If it was paused before, pause it again | 257 | // If it was paused before, pause it again |
296 | 258 | set_state(previous); | 258 | set_state(previous); |
297 | 259 | } | 259 | } |
299 | 260 | 260 | ||
300 | 261 | /* | 261 | /* |
301 | 262 | * Sets the episode that is currently being played | 262 | * Sets the episode that is currently being played |
302 | 263 | */ | 263 | */ |
303 | @@ -266,38 +266,44 @@ | |||
304 | 266 | // Set the previous state to ready | 266 | // Set the previous state to ready |
305 | 267 | set_state(Gst.State.PAUSED); | 267 | set_state(Gst.State.PAUSED); |
306 | 268 | set_state(Gst.State.READY); | 268 | set_state(Gst.State.READY); |
308 | 269 | 269 | ||
309 | 270 | this.current_episode = episode; | 270 | this.current_episode = episode; |
311 | 271 | 271 | ||
312 | 272 | // Set playbin to null | 272 | // Set playbin to null |
313 | 273 | set_state(Gst.State.NULL); | 273 | set_state(Gst.State.NULL); |
315 | 274 | 274 | ||
316 | 275 | // Set the URI | 275 | // Set the URI |
317 | 276 | playbin.uri = episode.playback_uri; | 276 | playbin.uri = episode.playback_uri; |
318 | 277 | info("Setting URI: %s".printf(episode.playback_uri)); | 277 | info("Setting URI: %s".printf(episode.playback_uri)); |
320 | 278 | 278 | ||
321 | 279 | Gst.Bus bus = playbin.get_bus (); | ||
322 | 280 | bus.add_watch (0, bus_callback); | ||
323 | 281 | |||
324 | 279 | // If it's a video podcast, get the width and height and configure that information | 282 | // If it's a video podcast, get the width and height and configure that information |
325 | 280 | if(episode.parent.content_type == MediaType.VIDEO) { | 283 | if(episode.parent.content_type == MediaType.VIDEO) { |
344 | 281 | try { | 284 | |
345 | 282 | var info = new Gst.PbUtils.Discoverer (10 * Gst.SECOND).discover_uri (playbin.uri); | 285 | var info = new Gst.PbUtils.Discoverer (50 * Gst.SECOND).discover_uri (episode.playback_uri); |
346 | 283 | var video = info.get_video_streams (); | 286 | var video = info.get_video_streams (); |
347 | 284 | if (video.data != null) { | 287 | |
348 | 285 | var video_info = (Gst.PbUtils.DiscovererVideoInfo)video.data; | 288 | try { |
349 | 286 | video_width = video_info.get_width (); | 289 | |
350 | 287 | video_height = video_info.get_height (); | 290 | video.foreach ((entry) => { |
351 | 288 | 291 | DiscovererVideoInfo video_info = (DiscovererVideoInfo)entry; | |
352 | 289 | configure_video(); | 292 | video_width = video_info.get_width (); |
353 | 290 | 293 | video_height = video_info.get_height (); | |
354 | 291 | } | 294 | }); |
355 | 292 | }catch(Error e) { | 295 | |
356 | 293 | warning(e.message); | 296 | configure_video(); |
357 | 294 | } | 297 | |
358 | 295 | } | 298 | }catch(Error e) { |
359 | 296 | 299 | warning(e.message); | |
360 | 297 | Gst.Bus bus = playbin.get_bus (); | 300 | } |
361 | 298 | bus.add_watch (0, bus_callback); | 301 | } |
362 | 302 | |||
363 | 303 | |||
364 | 299 | } | 304 | } |
366 | 300 | 305 | ||
367 | 306 | |||
368 | 301 | /* | 307 | /* |
369 | 302 | * Change the playback rate | 308 | * Change the playback rate |
370 | 303 | */ | 309 | */ |
371 | @@ -307,35 +313,20 @@ | |||
372 | 307 | Gst.Format.TIME, Gst.SeekFlags.SKIP, | 313 | Gst.Format.TIME, Gst.SeekFlags.SKIP, |
373 | 308 | Gst.SeekType.NONE, pos, | 314 | Gst.SeekType.NONE, pos, |
374 | 309 | Gst.SeekType.NONE, get_duration ()); | 315 | Gst.SeekType.NONE, get_duration ()); |
377 | 310 | } | 316 | } |
378 | 311 | 317 | ||
379 | 312 | /* | 318 | /* |
380 | 313 | * Sets the currently playing media position | 319 | * Sets the currently playing media position |
381 | 314 | */ | 320 | */ |
404 | 315 | public void set_position (int64 pos) { | 321 | public void set_position (int64 pos) { |
405 | 316 | 322 | ||
406 | 317 | // Slowly dial the volume back (should help avoid the crappy choppy sound when streaming) | 323 | playbin.seek_simple (Gst.Format.TIME, |
407 | 318 | for(double i = 0.1; i <= 1; i += 0.1) { | 324 | Gst.SeekFlags.FLUSH | |
408 | 319 | set_volume(1.0 - i); | 325 | Gst.SeekFlags.KEY_UNIT, pos); |
409 | 320 | Thread.usleep(50000); | 326 | |
410 | 321 | } | 327 | |
389 | 322 | |||
390 | 323 | playbin.seek (1.0, | ||
391 | 324 | Gst.Format.TIME, Gst.SeekFlags.FLUSH, | ||
392 | 325 | Gst.SeekType.SET, pos, | ||
393 | 326 | Gst.SeekType.NONE, get_duration ()); | ||
394 | 327 | |||
395 | 328 | |||
396 | 329 | // Let this thread sleep for .7 seconds to let GStreamer get caught up | ||
397 | 330 | Thread.usleep(700000); | ||
398 | 331 | |||
399 | 332 | // Turn the volume back up | ||
400 | 333 | for(double j = 0.1; j <= 1; j += 0.1) { | ||
401 | 334 | set_volume(j); | ||
402 | 335 | Thread.usleep(10000); | ||
403 | 336 | } | ||
411 | 337 | } | 328 | } |
413 | 338 | 329 | ||
414 | 339 | /* | 330 | /* |
415 | 340 | * Set the player to a designated state | 331 | * Set the player to a designated state |
416 | 341 | */ | 332 | */ |
417 | @@ -348,11 +339,11 @@ | |||
418 | 348 | playbin.set_state (s); | 339 | playbin.set_state (s); |
419 | 349 | } | 340 | } |
420 | 350 | 341 | ||
427 | 351 | /* | 342 | /* |
428 | 352 | * Sets the current volume | 343 | * Sets the current volume |
429 | 353 | */ | 344 | */ |
430 | 354 | public void set_volume (double val) { | 345 | public void set_volume (double val) { |
431 | 355 | playbin.set_property ("volume", val); | 346 | playbin.set_property ("volume", val); |
432 | 356 | } | 347 | } |
433 | 357 | } | 348 | } |
434 | 358 | } | 349 | } |
435 | 359 | 350 | ||
436 | === modified file 'src/Widgets/EpisodeDetailBox.vala' | |||
437 | --- src/Widgets/EpisodeDetailBox.vala 2015-04-04 19:32:30 +0000 | |||
438 | +++ src/Widgets/EpisodeDetailBox.vala 2015-06-08 19:25:26 +0000 | |||
439 | @@ -150,7 +150,8 @@ | |||
440 | 150 | if(episode.datetime_released != null) { | 150 | if(episode.datetime_released != null) { |
441 | 151 | release_label = new Gtk.Label(episode.datetime_released.format(" %x")); | 151 | release_label = new Gtk.Label(episode.datetime_released.format(" %x")); |
442 | 152 | } else { | 152 | } else { |
444 | 153 | release_label = new Gtk.Label(episode.date_released); | 153 | //We must fill the label with an empty string otherwise it prints "(null)" |
445 | 154 | release_label = new Gtk.Label(""); | ||
446 | 154 | } | 155 | } |
447 | 155 | release_label.xalign = 0; | 156 | release_label.xalign = 0; |
448 | 156 | release_label.justify = Gtk.Justification.LEFT; | 157 | release_label.justify = Gtk.Justification.LEFT; |
I also fixed the bug that was causing the segfault with certain feeds. This was from accessing an array out of bounds in Eppisode.vala if the dates are not formatted correctly. After checking the length of the array it was displaying "(null)" where the dates are set to appear on the sidepane. I made it so it shows an empty string instead.