|
63 | 63 |
|
64 | 64 | itemType = item.type |
65 | 65 |
|
66 | | - ' Determine isMovie/isSeries flags for ratings display settings |
67 | | - ' TODO Phase 6: For TvChannel/Program types, isMovie/isSeries should come from CurrentProgram data |
68 | | - isMovieFlag = item.isMovie |
69 | | - isSeriesFlag = item.isSeries |
| 66 | + ' Determine isMovie/isSeries flags for ratings display settings. |
| 67 | + ' For TvChannel, use the currently-airing program's flags if available. |
| 68 | + if itemType = "TvChannel" and isValid(item.currentProgram) |
| 69 | + isMovieFlag = item.currentProgram.isMovie |
| 70 | + isSeriesFlag = item.currentProgram.isSeries |
| 71 | + else |
| 72 | + isMovieFlag = item.isMovie |
| 73 | + isSeriesFlag = item.isSeries |
| 74 | + end if |
70 | 75 |
|
71 | 76 | ' Validate mutual exclusivity: both flags cannot be true |
72 | 77 | if isMovieFlag and isSeriesFlag |
|
95 | 100 | ' Stream counts - read pre-computed values from item node |
96 | 101 | m.top.numAudioStreams = item.audioStreamCount |
97 | 102 |
|
98 | | - ' Runtime |
99 | | - if item.runTimeTicks > 0 |
100 | | - m.top.runTimeMinutes = ticksToMinutes(item.runTimeTicks) |
| 103 | + ' Runtime — for TvChannel, use the currently-airing program's runtime if available |
| 104 | + runTimeTicks = item.runTimeTicks |
| 105 | + if itemType = "TvChannel" and isValid(item.currentProgram) and item.currentProgram.runTimeTicks > 0 |
| 106 | + runTimeTicks = item.currentProgram.runTimeTicks |
| 107 | + end if |
| 108 | + if runTimeTicks > 0 |
| 109 | + m.top.runTimeMinutes = ticksToMinutes(runTimeTicks) |
101 | 110 | else |
102 | 111 | m.top.runTimeMinutes = 0 |
103 | 112 | end if |
104 | 113 |
|
105 | | - ' TODO Phase 6: Live TV CurrentProgram override for runTimeTicks/runTimeMinutes |
106 | | - ' When currentProgram data is available on JellyfinBaseItem, override here. |
107 | | - |
108 | 114 | setButtonStates() |
109 | 115 | populateData() |
110 | 116 | end sub |
|
150 | 156 | end sub |
151 | 157 |
|
152 | 158 | sub setEndsAtText() |
| 159 | + endsAtText = m.top.findNode("endsAtText") |
| 160 | + |
153 | 161 | if m.global.user.settings.uiDesignHideClock |
154 | | - endsAtText = m.top.findNode("endsAtText") |
155 | 162 | endsAtText.visible = false |
156 | 163 | m.endsAtTime.text = "" |
157 | 164 | return |
158 | 165 | end if |
159 | 166 |
|
160 | | - ' TODO Phase 6: Live TV "ends at" time from CurrentProgram.EndDate |
161 | | - ' When currentProgram data is available on JellyfinBaseItem, restore TvChannel EndDate display here. |
| 167 | + ' For live TV channels, use the currently-airing program's EndDate for end time. |
| 168 | + ' remainingPositionTime ≈ 0 for live streams so the default calculation would show current time. |
| 169 | + if isValid(m.itemData) and m.itemData.type = "TvChannel" |
| 170 | + currentProgram = m.itemData.currentProgram |
| 171 | + if isValid(currentProgram) and isValidAndNotEmpty(currentProgram.endDate) |
| 172 | + endDt = CreateObject("roDateTime") |
| 173 | + endDt.FromISO8601String(currentProgram.endDate) |
| 174 | + endDt.toLocalTime() |
| 175 | + m.endsAtTime.text = formatTime(endDt) |
| 176 | + endsAtText.visible = true |
| 177 | + else |
| 178 | + endsAtText.visible = false |
| 179 | + m.endsAtTime.text = "" |
| 180 | + end if |
| 181 | + return |
| 182 | + end if |
162 | 183 |
|
163 | 184 | ' Calculate endsAtTime based on remainingPositionTime |
164 | 185 | date = CreateObject("roDateTime") |
|
171 | 192 |
|
172 | 193 | sub setVideoLogoGroup() |
173 | 194 | m.videoLogo.uri = m.top.videoLogo |
174 | | - |
175 | | - ' TODO Phase 6: Live TV channel logo from CurrentProgram image tags |
176 | | - ' When currentProgram data is available on JellyfinBaseItem, restore TvChannel logo override here. |
177 | 195 | end sub |
178 | 196 |
|
179 | 197 | sub setVideoTitle() |
180 | | - m.videoTitle.text = m.itemData.name |
181 | | - |
182 | | - ' TODO Phase 6: Live TV channel title from CurrentProgram.Name |
183 | | - ' When currentProgram data is available on JellyfinBaseItem, restore TvChannel title override here. |
| 198 | + item = m.itemData |
| 199 | + ' For TvChannel, display the currently-airing program name rather than the channel name |
| 200 | + if item.type = "TvChannel" and isValid(item.currentProgram) and isValidAndNotEmpty(item.currentProgram.name) |
| 201 | + m.videoTitle.text = item.currentProgram.name |
| 202 | + else |
| 203 | + m.videoTitle.text = item.name |
| 204 | + end if |
184 | 205 | end sub |
185 | 206 |
|
186 | 207 | sub setVideoSubTitle() |
|
191 | 212 | item = m.itemData |
192 | 213 | itemType = item.type |
193 | 214 |
|
| 215 | + ' For TvChannel, ratings and dates are sourced from the currently-airing program when available |
| 216 | + metaItem = item |
| 217 | + if itemType = "TvChannel" and isValid(item.currentProgram) |
| 218 | + metaItem = item.currentProgram |
| 219 | + end if |
| 220 | + |
194 | 221 | ' EPISODE |
195 | 222 | if itemType = "Episode" or itemType = "Recording" |
196 | 223 | ' Title |
|
241 | 268 | displaySubtitleNode(productionYearNode) |
242 | 269 | end if |
243 | 270 | else if itemType = "TvChannel" |
244 | | - ' TODO Phase 6: Live TV channel number/name from CurrentProgram data |
245 | | - ' When currentProgram data is available on JellyfinBaseItem, restore channel info display here. |
| 271 | + ' Display currently-airing program metadata (episode info or movie year) |
| 272 | + currentProgram = item.currentProgram |
| 273 | + if isValid(currentProgram) |
| 274 | + if currentProgram.isSeries |
| 275 | + ' Episode info: S1E2 - Episode Name |
| 276 | + episodeInfoText = "" |
| 277 | + if currentProgram.parentIndexNumber > 0 |
| 278 | + episodeInfoText = episodeInfoText + `${tr("S")}${currentProgram.parentIndexNumber}` |
| 279 | + end if |
| 280 | + if currentProgram.indexNumber > 0 |
| 281 | + episodeInfoText = episodeInfoText + `${tr("E")}${currentProgram.indexNumber}` |
| 282 | + end if |
| 283 | + if isValidAndNotEmpty(currentProgram.name) |
| 284 | + if isValidAndNotEmpty(episodeInfoText) |
| 285 | + episodeInfoText = episodeInfoText + ` - ${currentProgram.name}` |
| 286 | + else |
| 287 | + episodeInfoText = currentProgram.name |
| 288 | + end if |
| 289 | + end if |
| 290 | + if isValidAndNotEmpty(episodeInfoText) |
| 291 | + episodeInfoNode = createSubtitleLabelNode("episodeInfo") |
| 292 | + episodeInfoNode.text = episodeInfoText |
| 293 | + displaySubtitleNode(episodeInfoNode) |
| 294 | + end if |
| 295 | + else if currentProgram.isMovie and currentProgram.productionYear > 0 |
| 296 | + airDateNodeCreated = true |
| 297 | + productionYearNode = createSubtitleLabelNode("productionYear") |
| 298 | + productionYearNode.text = currentProgram.productionYear.toStr().trim() |
| 299 | + displaySubtitleNode(productionYearNode) |
| 300 | + end if |
| 301 | + end if |
| 302 | + ' Channel number (e.g. "CH 4") — always shown when available |
| 303 | + if isValidAndNotEmpty(item.channelNumber) |
| 304 | + channelNumberNode = createSubtitleLabelNode("channelNumber") |
| 305 | + channelNumberNode.text = `${tr("CH")} ${item.channelNumber}` |
| 306 | + displaySubtitleNode(channelNumberNode) |
| 307 | + end if |
246 | 308 | end if |
247 | 309 |
|
248 | 310 | ' append these to all video types |
249 | 311 | ' |
250 | 312 | userSettings = m.global.user.settings |
251 | 313 |
|
252 | 314 | ' Official Rating |
253 | | - if isValidAndNotEmpty(item.officialRating) |
| 315 | + if isValidAndNotEmpty(metaItem.officialRating) |
254 | 316 | officialRatingNode = createSubtitleLabelNode("officialRating") |
255 | | - officialRatingNode.text = item.officialRating |
| 317 | + officialRatingNode.text = metaItem.officialRating |
256 | 318 | displaySubtitleNode(officialRatingNode) |
257 | 319 | end if |
258 | 320 |
|
|
271 | 333 |
|
272 | 334 | if showRatings |
273 | 335 | ' communityRating (star + rating) |
274 | | - ' TODO Phase 6: For TvChannel/Program, communityRating should come from CurrentProgram |
275 | | - if item.communityRating <> 0 |
| 336 | + if metaItem.communityRating <> 0 |
276 | 337 | communityRatingNode = CreateObject("roSGNode", "CommunityRating") |
277 | 338 | communityRatingNode.id = "communityRating" |
278 | | - communityRatingNode.rating = item.communityRating |
| 339 | + communityRatingNode.rating = metaItem.communityRating |
279 | 340 | communityRatingNode.iconSize = 30 |
280 | 341 | displaySubtitleNode(communityRatingNode) |
281 | 342 | end if |
282 | 343 |
|
283 | 344 | ' criticRating (tomato + rating) |
284 | | - ' TODO Phase 6: For TvChannel/Program, criticRating should come from CurrentProgram |
285 | | - if item.criticRating <> 0 |
| 345 | + if metaItem.criticRating <> 0 |
286 | 346 | criticRatingNode = CreateObject("roSGNode", "CriticRating") |
287 | 347 | criticRatingNode.id = "criticRating" |
288 | | - criticRatingNode.rating = item.criticRating |
| 348 | + criticRatingNode.rating = metaItem.criticRating |
289 | 349 | criticRatingNode.iconSize = 30 |
290 | 350 | displaySubtitleNode(criticRatingNode) |
291 | 351 | end if |
292 | 352 | end if |
293 | 353 |
|
294 | 354 | ' videoAirDate if needed |
295 | | - if not airDateNodeCreated and isValidAndNotEmpty(item.premiereDate) |
| 355 | + if not airDateNodeCreated and isValidAndNotEmpty(metaItem.premiereDate) |
296 | 356 | premiereDateNode = createSubtitleLabelNode("videoAirDate") |
297 | | - premiereDateNode.text = formatIsoDateVideo(item.premiereDate) |
| 357 | + premiereDateNode.text = formatIsoDateVideo(metaItem.premiereDate) |
298 | 358 | displaySubtitleNode(premiereDateNode) |
299 | 359 | end if |
300 | 360 |
|
|
0 commit comments