diff options
87 files changed, 1943 insertions, 2247 deletions
@@ -5,5 +5,5 @@ DOXYFILE = docs/doxygen/Doxyfile.doxy PROJECT_LOGO = docs/doxygen/Thumbnail-symbol-whitebg-small.jpg INPUT= xbmc \ - docs/doxygen/CODING_GUIDELINES.dox \ + docs/CODE_GUIDELINES.md \ docs/doxygen diff --git a/.github/triage.yml b/.github/triage.yml new file mode 100644 index 0000000000..857bcd4ed7 --- /dev/null +++ b/.github/triage.yml @@ -0,0 +1,2 @@ +label: "Triage: Needed" +enabled: true diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 803f3b5b5f..ebaa166c03 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -9094,7 +9094,7 @@ msgid "Find similar" msgstr "" #. label for epg import progress control -#: xbmc/epg/EpgContainer.cpp +#: xbmc/pvr/epg/EpgContainer.cpp msgctxt "#19004" msgid "Importing guide from clients" msgstr "" @@ -9443,7 +9443,7 @@ msgstr "" #: addons/skin.estuary/xml/MyPics.xml #: addons/skin.estuary/xml/MyVideoNav.xml #: addons/skin.estuary/xml/View_50_List.xml -#: xbmc/epg/EpgInfoTag.cpp +#: xbmc/pvr/epg/EpgInfoTag.cpp #: xbmc/FileItem.cpp #: xbmc/GUIInfoManager.cpp #: xbmc/pvr/PVRManager.cpp @@ -10608,7 +10608,7 @@ msgid "Filter channels" msgstr "" #. label 'loading epg data from database' for progress dialog text -#: xbmc/epg/EpgContainer.cpp +#: xbmc/pvr/epg/EpgContainer.cpp msgctxt "#19250" msgid "Loading guide from database" msgstr "" @@ -10702,7 +10702,7 @@ msgid "The entered PIN was incorrect." msgstr "" #. label to use for epg tag title instead of actual event title if the respective channel is parental locked -#: xbmc/epg/EpgInfoTag.cpp +#: xbmc/pvr/PVRGUIActions.cpp msgctxt "#19266" msgid "Parental locked" msgstr "" @@ -10935,63 +10935,63 @@ msgstr "" #empty strings from id 19305 to 19498 #. label for epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19499" msgid "Other / Unknown" msgstr "" #. label for epg "movie/drama" genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19500" msgid "Movie / Drama" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19501" msgid "Detective / Thriller" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19502" msgid "Adventure / Western / War" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19503" msgid "Science fiction / Fantasy / Horror" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19504" msgid "Comedy" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19505" msgid "Soap / Melodrama / Folkloric" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19506" msgid "Romance" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19507" msgid "Serious / Classical / Religious / Historical movie / drama" msgstr "" #. label for epg "movie/drama" subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19508" msgid "Adult movie / drama" msgstr "" @@ -10999,32 +10999,32 @@ msgstr "" #empty strings from id 19509 to 19515 #. label for "news/current affairs" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19516" msgid "News / Current affairs" msgstr "" #. label for "news/current affairs" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19517" msgid "News / Weather report" msgstr "" #. label for "news/current affairs" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19518" msgid "News magazine" msgstr "" #. label for "news/current affairs" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19519" msgid "Documentary" msgstr "" #. label for "news/current affairs" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19520" msgid "Discussion / Interview / Debate" msgstr "" @@ -11032,26 +11032,26 @@ msgstr "" #empty strings from id 19521 to 19531 #. label for "show/game show" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19532" msgid "Show / Game show" msgstr "" #. label for "show/game show" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19533" msgid "Game show / Quiz / Contest" msgstr "" #. label for "show/game show" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19534" msgid "Variety show" msgstr "" #. label for "show/game show" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19535" msgid "Talk show" msgstr "" @@ -11059,74 +11059,74 @@ msgstr "" #empty strings from id 19536 to 19547 #. label for "sports" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19548" msgid "Sports" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19549" msgid "Special event" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19550" msgid "Sport magazine" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19551" msgid "Football" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19552" msgid "Tennis / Squash" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19553" msgid "Team sports" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19554" msgid "Athletics" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19555" msgid "Motor sport" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19556" msgid "Water sport" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19557" msgid "Winter sports" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19558" msgid "Equestrian" msgstr "" #. label for "sports" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19559" msgid "Martial sports" msgstr "" @@ -11134,38 +11134,38 @@ msgstr "" #empty strings from id 19560 to 19563 #. label for "children's/youth programmes" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19564" msgid "Children's / Youth programmes" msgstr "" #. label for "children's/youth programmes" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19565" msgid "Pre-school children's programmes" msgstr "" #. label for "children's/youth programmes" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19566" msgid "Entertainment programmes for 6 to 14" msgstr "" #. label for "children's/youth programmes" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19567" msgid "Entertainment programmes for 10 to 16" msgstr "" #. label for "children's/youth programmes" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19568" msgid "Informational / Educational / School programme" msgstr "" #. label for "children's/youth programmes" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19569" msgid "Cartoons / Puppets" msgstr "" @@ -11173,38 +11173,38 @@ msgstr "" #empty strings from id 19570 to 19579 #. label for "music/ballet/dance" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19580" msgid "Music / Ballet / Dance" msgstr "" #. label for "music/ballet/dance" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19581" msgid "Rock / Pop" msgstr "" #. label for "music/ballet/dance" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19582" msgid "Serious / Classical music" msgstr "" #. label for "music/ballet/dance" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19583" msgid "Folk / Traditional music" msgstr "" #. label for "music/ballet/dance" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19584" msgid "Musical / Opera" msgstr "" #. label for "music/ballet/dance" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19585" msgid "Ballet" msgstr "" @@ -11212,74 +11212,74 @@ msgstr "" #empty strings from id 19586 to 19595 #. label for "arts/culture" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19596" msgid "Arts / Culture" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19597" msgid "Performing arts" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19598" msgid "Fine arts" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19599" msgid "Religion" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19600" msgid "Popular culture / Traditional arts" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19601" msgid "Literature" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19602" msgid "Film / Cinema" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19603" msgid "Experimental film / video" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19604" msgid "Broadcasting / Press" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19605" msgid "New media" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19606" msgid "Arts / Culture magazines" msgstr "" #. label for "arts/culture" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19607" msgid "Fashion" msgstr "" @@ -11287,26 +11287,26 @@ msgstr "" #empty strings from id 19608 to 19611 #. label for "social/political/economics" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19612" msgid "Social / Political / Economics" msgstr "" #. label for "social/political/economics" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19613" msgid "Magazines / Reports / Documentary" msgstr "" #. label for "social/political/economics" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19614" msgid "Economics / Social advisory" msgstr "" #. label for "social/political/economics" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19615" msgid "Remarkable people" msgstr "" @@ -11314,50 +11314,50 @@ msgstr "" #empty strings from id 19616 to 19627 #. label for "education/science/factual" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19628" msgid "Education / Science / Factual" msgstr "" #. label for "education/science/factual" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19629" msgid "Nature / Animals / Environment" msgstr "" #. label for "education/science/factual" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19630" msgid "Technology / Natural sciences" msgstr "" #. label for "education/science/factual" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19631" msgid "Medicine / Physiology / Psychology" msgstr "" #. label for "education/science/factual" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19632" msgid "Foreign countries / Expeditions" msgstr "" #. label for "education/science/factual" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19633" msgid "Social / Spiritual sciences" msgstr "" #. label for "education/science/factual" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19634" msgid "Further education" msgstr "" #. label for "education/science/factual" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19635" msgid "Languages" msgstr "" @@ -11365,50 +11365,50 @@ msgstr "" #empty strings from id 19636 to 19643 #. label for "leisure/hobbies" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19644" msgid "Leisure / Hobbies" msgstr "" #. label for "leisure/hobbies" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19645" msgid "Tourism / Travel" msgstr "" #. label for "leisure/hobbies" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19646" msgid "Handicraft" msgstr "" #. label for "leisure/hobbies" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19647" msgid "Motoring" msgstr "" #. label for "leisure/hobbies" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19648" msgid "Fitness & health" msgstr "" #. label for "leisure/hobbies" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19649" msgid "Cooking" msgstr "" #. label for "leisure/hobbies" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19650" msgid "Advertisement / Shopping" msgstr "" #. label for "leisure/hobbies" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19651" msgid "Gardening" msgstr "" @@ -11416,32 +11416,32 @@ msgstr "" #empty strings from id 19652 to 19659 #. label for "special characteristics" epg genre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp #: xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp msgctxt "#19660" msgid "Special characteristics" msgstr "" #. label for "special characteristics" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19661" msgid "Original language" msgstr "" #. label for "special characteristics" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19662" msgid "Black & white" msgstr "" #. label for "special characteristics" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19663" msgid "Unpublished" msgstr "" #. label for "special characteristics" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19664" msgid "Live broadcast" msgstr "" @@ -11449,55 +11449,55 @@ msgstr "" #empty strings from id 19665 to 19675 #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19676" msgid "Drama" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19677" msgid "Detective / Thriller" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19678" msgid "Adventure / Western / War" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19679" msgid "Science fiction / Fantasy / Horror" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19680" msgid "Comedy" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19681" msgid "Soap / Melodrama / Folkloric" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19682" msgid "Romance" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19683" msgid "Serious / Classical / Religion / Historical" msgstr "" #. label for "user defined" epg subgenre value -#: xbmc/epg/Epg.cpp +#: xbmc/pvr/epg/Epg.cpp msgctxt "#19684" msgid "Adult" msgstr "" diff --git a/addons/skin.estouchy/xml/MyWeather.xml b/addons/skin.estouchy/xml/MyWeather.xml index 3e84723ffd..f097c82361 100644 --- a/addons/skin.estouchy/xml/MyWeather.xml +++ b/addons/skin.estouchy/xml/MyWeather.xml @@ -294,7 +294,7 @@ <posx>100</posx> <posy>70</posy> <width>200</width> - <height>30</height> + <height>34</height> <font>font22</font> <selectedcolor>selected</selectedcolor> <align>center</align> @@ -306,13 +306,13 @@ <posx>210</posx> <posy>70</posy> <width>200</width> - <height>30</height> + <height>34</height> <font>font22</font> <selectedcolor>selected</selectedcolor> <align>center</align> <aligny>center</aligny> <label>[COLOR=grey]$LOCALIZE[418][/COLOR][CR][B]$INFO[ListItem.Property(LowTemp)][/B]$INFO[ListItem.Property(TempUnits)]</label> - <visible>!String.IsEmpty(ListItem.Property(HighTemp))</visible> + <visible>!String.IsEmpty(ListItem.Property(LowTemp))</visible> </control> <control type="textbox"> <posx>20</posx> @@ -355,7 +355,7 @@ <posx>100</posx> <posy>70</posy> <width>200</width> - <height>30</height> + <height>34</height> <font>font22</font> <selectedcolor>selected</selectedcolor> <align>center</align> @@ -367,13 +367,13 @@ <posx>210</posx> <posy>70</posy> <width>200</width> - <height>30</height> + <height>34</height> <font>font22</font> <selectedcolor>selected</selectedcolor> <align>center</align> <aligny>center</aligny> <label>[COLOR=grey]$LOCALIZE[418][/COLOR][CR][B]$INFO[ListItem.Property(LowTemp)][/B]$INFO[ListItem.Property(TempUnits)]</label> - <visible>!String.IsEmpty(ListItem.Property(HighTemp))</visible> + <visible>!String.IsEmpty(ListItem.Property(LowTemp))</visible> </control> <control type="textbox"> <posx>20</posx> diff --git a/addons/skin.estuary/xml/Variables.xml b/addons/skin.estuary/xml/Variables.xml index 78e1421301..2da53aab5f 100644 --- a/addons/skin.estuary/xml/Variables.xml +++ b/addons/skin.estuary/xml/Variables.xml @@ -271,14 +271,14 @@ <variable name="NowPlayingBreadcrumbsVar"> <value condition="VideoPlayer.Content(livetv)">$INFO[VideoPlayer.Title]</value> <value condition="VideoPlayer.Content(episodes) + !String.IsEmpty(Player.Art(tvshow.clearlogo))">$INFO[VideoPlayer.Season,[COLOR button_focus]S,[/COLOR]]$INFO[VideoPlayer.Episode,[COLOR button_focus]E,: [/COLOR]]$INFO[VideoPlayer.Title]</value> - <value condition="Window.IsActive(fullscreenvideo)">$INFO[VideoPlayer.Title]$INFO[VideoPlayer.Year, ([COLOR button_focus],[/COLOR])]</value> + <value condition="Window.IsActive(fullscreenvideo)">$INFO[VideoPlayer.TVShowTitle]$INFO[VideoPlayer.Year, ([COLOR button_focus],[/COLOR])]</value> <value condition="MusicPartyMode.Enabled">$LOCALIZE[589]</value> <value>$LOCALIZE[31000]...</value> </variable> <variable name="OSDSubLabelVar"> <value condition="Window.IsActive(visualisation) + Integer.IsGreater(Playlist.Length,1) + Integer.IsGreater(Playlist.Position,0)">$LOCALIZE[554] $INFO[Playlist.Position] / $INFO[Playlist.Length]</value> <value condition="VideoPlayer.Content(musicvideos)">$INFO[VideoPlayer.Artist]$INFO[VideoPlayer.Album, - ]</value> - <value condition="VideoPlayer.Content(episodes)">$INFO[VideoPlayer.Season,[COLOR button_focus]S,[/COLOR]]$INFO[VideoPlayer.Episode,[COLOR button_focus]E,: [/COLOR]]$INFO[VideoPlayer.TVShowTitle]</value> + <value condition="VideoPlayer.Content(episodes)">$INFO[VideoPlayer.Season,[COLOR button_focus]S,[/COLOR]]$INFO[VideoPlayer.Episode,[COLOR button_focus]E,: [/COLOR]]$INFO[VideoPlayer.Title]</value> <value condition="VideoPlayer.Content(LiveTV) | PVR.IsPlayingRecording | PVR.IsPlayingEpgTag">$INFO[VideoPlayer.ChannelNumberLabel,([COLOR button_focus],[/COLOR]) ]$INFO[VideoPlayer.ChannelName] $INFO[VideoPlayer.EpisodeName, - ]</value> <value>$INFO[VideoPlayer.Genre]</value> </variable> diff --git a/docs/CODE_GUIDELINES.md b/docs/CODE_GUIDELINES.md index 8e2c421363..81c235f48e 100644 --- a/docs/CODE_GUIDELINES.md +++ b/docs/CODE_GUIDELINES.md @@ -1,7 +1,7 @@ -![Kodi Logo](resources/banner_slim.png) - # Code Guidelines +![Kodi Logo](https://github.com/xbmc/xbmc/raw/master/docs/resources/banner_slim.png) + ## Table of Contents * [1. Motivation](#1-motivation) * [2. Language standard](#2-language-standard) diff --git a/docs/doxygen/CODING_GUIDELINES.dox b/docs/doxygen/CODING_GUIDELINES.dox deleted file mode 100644 index bd22c1b168..0000000000 --- a/docs/doxygen/CODING_GUIDELINES.dox +++ /dev/null @@ -1,414 +0,0 @@ -/*! - -\page code_guidelines Code guidelines and formatting conventions - -@brief \doc_header{ Code guidelines and formatting conventions } - -\tableofcontents - -When working in a large group, the two most important values are readability -and maintainability. We code for other people, not computers. To accomplish -these goals, we have created a unified set of code conventions. - -Conventions can be bent or broken in the interest of making code more readable -and maintainable. However, if you submit a patch that contains excessive style -conflicts, you may be asked to improve your code before your pull request is -reviewed. - -================================================================================ -\section code_guidelines_1 Indentation - -Use spaces as tab policy with an indentation size of 2 - --------------------------------------------------------------------------------- -\subsection code_guidelines_1_1 Statements - -No multiple statements on a single line, like this: -~~~~~~~~~~~~~ -std::vector<std::string> test; test.push_back("foobar"); // This is the bad way -~~~~~~~~~~~~~ - -Always use a new line for a new statement: -~~~~~~~~~~~~~ -std::vector<std::string> test; -test.push_back("foobar"); -~~~~~~~~~~~~~ - -With them becomes it much more easy for debugging of faults to see direct on the -line what has created the fault. - - --------------------------------------------------------------------------------- -\subsection code_guidelines_1_2 Namespaces - -Namespaces are not required to use any indentation to simplify nested namespaces -and wrapping `.cpp` files in a namespace - -~~~~~~~~~~~~~ -namespace KODI -{ -namespace UTILS -{ -class ILogger -{ - void Log(...) = 0; -} -} -} -~~~~~~~~~~~~~ - -\subsection code_guidelines_1_3 Headers - -Included header files have to be sorted alphabetically to prevent duplicates and -allow better overview, with an empty line clearly separating sections. - -Header order has to be: - -- Own header file -- Other Kodi includes -- C and C++ system files -- Other libraries' header files - -~~~~~~~~~~~~~ -#include "PVRManager.h" - -#include "addons/AddonInstaller.h" -#include "dialogs/GUIDialogExtendedProgressBar.h" -#include "messaging/helpers/DialogHelper.h" -#include "messaging/ApplicationMessenger.h" -#include "messaging/ThreadMessage.h" -#include "music/tags/MusicInfoTag.h" -#include "music/MusicDatabase.h" -#include "network/Network.h" -#include "pvr/addons/PVRClients.h" -#include "pvr/channels/PVRChannel.h" -#include "settings/Settings.h" -#include "threads/SingleLock.h" -#include "utils/JobManager.h" -#include "utils/log.h" -#include "utils/Variant.h" -#include "video/VideoDatabase.h" -#include "Application.h" -#include "ServiceBroker.h" - -#include <cassert> -#include <utility> - -#include <libavutil/pixfmt.h> -~~~~~~~~~~~~~ - -Place directories before files. If the headers aren't sorted, either do your best -to match the existing order, or precede your commit with an alphabetization commit. - -If possible, avoid including headers in another header. Instead, you can -forward-declare the class and use a `std::unique_ptr`: - -~~~~~~~~~~~~~ -class CFileItem; - -class Example -{ - ... - std::unique_ptr<CFileItem> m_fileItem; -} -~~~~~~~~~~~~~ - -================================================================================ -\section code_guidelines_2 Braces - -Braces have to go to a new line. - -~~~~~~~~~~~~~ -if (int i = 0; i < t; i++) -{ - [...] -} -else -{ - [...] -} - -class Dummy() -{ - [...] -} -~~~~~~~~~~~~~ - -================================================================================ -\section code_guidelines_3 Whitespaces - -Conventional operators have to be surrounded by a whitespace. - -~~~~~~~~~~~~~ -a = (b + c) * d; -~~~~~~~~~~~~~ - -Reserved words have to be separated from opening parentheses by a whitespace. - -~~~~~~~~~~~~~ -while (true) -for (int i = 0; i < x; ++i) -~~~~~~~~~~~~~ - -Commas have to be followed by a whitespace. - -~~~~~~~~~~~~~ -void Dummy::Method(int a, int b, int c); -int d, e; -~~~~~~~~~~~~~ - -Semicolons have to be followed by a newline. - -~~~~~~~~~~~~~ -for (int i = 0; i < x; ++i) -doSomething(e); -doSomething(f); -~~~~~~~~~~~~~ - -Initializer lists have spaces between elements, but no surrounding spaces. - -~~~~~~~~~~~~~ -const char *aStringArray[] = {"one", "two", "three"}; -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_3_1 No vertical alignment - -Do not use whitespaces to align value names together. This causes problems -on code review if one needs to realign all values to their new position. - -Wrong: -~~~~~~~~~~~~~ -... - -int value1 = 0; -int value2 = 0; -CExampleClass *exampleClass = nullptr; -CBiggerExampleClass *biggerExampleClass = nullptr; - -exampleClass = new CExampleClass (value1, value2); -biggerExampleClass = new CBiggerExampleClass(value1, value2); - -exampleClass ->InitExample(); -biggerExampleClass->InitExample(); - -... -~~~~~~~~~~~~~ - -Right: -~~~~~~~~~~~~~ -... - -int value1 = 0; -int value2 = 0; -CExampleClass *exampleClass = nullptr; -CBiggerExampleClass *biggerExampleClass = nullptr; - -exampleClass = new CExampleClass(value1, value2); -biggerExampleClass = new CBiggerExampleClass(value1, value2); - -exampleClass->InitExample(); -biggerExampleClass->InitExample(); - -... -~~~~~~~~~~~~~ - -================================================================================ -\section code_guidelines_4 Control statements - -Insert new line before - -- else in an if statement -- catch in a try statement -- while in a do statement - - --------------------------------------------------------------------------------- -\subsection code_guidelines_4_1 if else - -- put then statement, return or throw to new line -- keep else if on one line - -~~~~~~~~~~~~~ -if (true) - return; - -if (true) -{ - [...] -} -else if (false) -{ - return; -} -else - return; -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_4_2 switch / case - -~~~~~~~~~~~~~ -switch (cmd) -{ - case x: - { - doSomething(); - break; - } - case x: - case z: - return true; - default: - doSomething(); -} -~~~~~~~~~~~~~ - -================================================================================ -\section code_guidelines_5 Naming -\subsection code_guidelines_5_1 Namespaces - -Namespaces have to be in uppercase. - -~~~~~~~~~~~~~ -namespace KODI -{ -... -} -~~~~~~~~~~~~~ - -\subsection code_guidelines_5_2 Constants - -Use uppercase with underscore spacing where necessary. - -~~~~~~~~~~~~~ -const int MY_CONSTANT = 1; -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_5_3 Enums - -Use CamelCase for the enum name and uppercase for the values. - -~~~~~~~~~~~~~ -enum Dummy -{ - VALUE_X, - VALUE_Y -}; -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_5_4 Interfaces - -Use CamelCase for interface names and they have to be prefixed with an -uppercase I. Filename has to match the interface name, e.g. `ILogger.h` - -~~~~~~~~~~~~~ -class ILogger -{ - void Log(...) = 0; -} -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_5_5 Classes - -We use CamelCase for class names and they have to be prefixed with an uppercase C. -Filenamehas match the class name without the prefixed C, e.g. `Logger.cpp` - -~~~~~~~~~~~~~ -class CLogger : public ILogger -{ - void Log(...) -} -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_5_6 Methods - -Use CamelCase for method names and first letter shas to be uppercase. -Even if the methods are private or protected. - -~~~~~~~~~~~~~ -void MyDummyClass::DoSomething(); -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_5_7 Variables - -We use CamelCase for variables. Type prefixing is optional. -\subsubsection code_guidelines_5_7_1 Global Variables - -Prefix global variables with g_ - -~~~~~~~~~~~~~ -int g_globalVariableA; -~~~~~~~~~~~~~ - -\warning Use of globals reduces the chance of submitted code to be accepted to a minimum - -\subsubsection code_guidelines_5_7_2 Member Variables - -Prefix member variables with m_ - -~~~~~~~~~~~~~ -int m_variableA; -~~~~~~~~~~~~~ - -================================================================================ -\section code_guidelines_6 Conventions - --------------------------------------------------------------------------------- -\subsection code_guidelines_6_1 Casts - -New code has to use C++ style casts and not older C style casts. When modifying -existing code the developer can choose to update it to C++ style casts or leave -as is. Remember that whenever a dynamic_cast is used the result can be a nullptr -and needs to be checked accordingly. - --------------------------------------------------------------------------------- -\subsection code_guidelines_6_2 NULL vs nullptr - -Prefer the use of nullptr instead of NULL. nullptr is a typesafe version and as -such can't be implicitly converted to int or anything else. - --------------------------------------------------------------------------------- -\subsection code_guidelines_6_3 auto - -Feel free to use auto wherever it improves readability. Good places are -iterators or when dealing with containers. - -~~~~~~~~~~~~~ -std::map<std::string, std::vector<int>>::iterator i = var.begin(); -vs -auto i = var.being(); -~~~~~~~~~~~~~ - --------------------------------------------------------------------------------- -\subsection code_guidelines_6_4 for loops - -Use newer style foreach loops whenever it makes sense. If iterators are used see -above about using auto. - -~~~~~~~~~~~~~ -for (auto& : var) -{ - ... -} -~~~~~~~~~~~~~ - -Use const auto& if there's no reason to modify the value. - --------------------------------------------------------------------------------- -\subsection code_guidelines_6_5 default member initialization - -Use default member initialization instead of initializer lists or constructor assignments whenever it makes sense. -~~~~~~~~~~~~~ -class Foo -{ - bool bar = false; -}; -~~~~~~~~~~~~~ - -*/ diff --git a/docs/doxygen/Doxyfile.doxy b/docs/doxygen/Doxyfile.doxy index 9115f3a19a..5806aa4a6c 100644 --- a/docs/doxygen/Doxyfile.doxy +++ b/docs/doxygen/Doxyfile.doxy @@ -807,7 +807,7 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = ../../xbmc \ - CODING_GUIDELINES.dox \ + ../CODE_GUIDELINES.md \ . # This tag can be used to specify the character encoding of the source files diff --git a/tools/depends/target/ffmpeg/FFMPEG-VERSION b/tools/depends/target/ffmpeg/FFMPEG-VERSION index d678d32e23..d7883f0ee8 100644 --- a/tools/depends/target/ffmpeg/FFMPEG-VERSION +++ b/tools/depends/target/ffmpeg/FFMPEG-VERSION @@ -1,4 +1,4 @@ LIBNAME=ffmpeg BASE_URL=https://github.com/xbmc/FFmpeg -VERSION=4.0.3-Leia-RC5 +VERSION=4.0.3-Leia-18.2 ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp index f184899b37..791a1d7079 100644 --- a/xbmc/FileItem.cpp +++ b/xbmc/FileItem.cpp @@ -32,6 +32,8 @@ #include "CueDocument.h" #include "video/VideoDatabase.h" #include "music/MusicDatabase.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/channels/PVRChannel.h" #include "pvr/epg/Epg.h" #include "pvr/recordings/PVRRecording.h" @@ -112,6 +114,20 @@ CFileItem::CFileItem(const CVideoInfoTag& movie) SetFromVideoInfoTag(movie); } +namespace +{ + std::string GetEpgTagTitle(const std::shared_ptr<CPVREpgInfoTag>& epgTag) + { + if (CServiceBroker::GetPVRManager().IsParentalLocked(epgTag)) + return g_localizeStrings.Get(19266); // Parental locked + else if (epgTag->Title().empty() && + !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_HIDENOINFOAVAILABLE)) + return g_localizeStrings.Get(19055); // no information available + else + return epgTag->Title(); + } +} // unnamed namespace + void CFileItem::FillMusicInfoTag(const CPVRChannelPtr& channel, const CPVREpgInfoTagPtr& tag) { if (channel && channel->IsRadio() && !HasMusicInfoTag()) @@ -120,7 +136,7 @@ void CFileItem::FillMusicInfoTag(const CPVRChannelPtr& channel, const CPVREpgInf if (tag) { - musictag->SetTitle(tag->Title()); + musictag->SetTitle(GetEpgTagTitle(tag)); musictag->SetGenre(tag->Genre()); musictag->SetDuration(tag->GetDuration()); } @@ -144,15 +160,23 @@ CFileItem::CFileItem(const CPVREpgInfoTagPtr& tag) m_bIsFolder = false; m_epgInfoTag = tag; m_strPath = tag->Path(); - SetLabel(tag->Title()); + SetLabel(GetEpgTagTitle(tag)); m_dateTime = tag->StartAsLocalTime(); if (!tag->Icon().empty()) SetIconImage(tag->Icon()); - else if (tag->HasChannel() && !tag->Channel()->IconPath().empty()) - SetIconImage(tag->Channel()->IconPath()); + else + { + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag); + if (channel) + { + if (!channel->IconPath().empty()) + SetIconImage(channel->IconPath()); + + FillMusicInfoTag(channel, tag); + } + } - FillMusicInfoTag(tag->Channel(), tag); FillInMimeType(false); } @@ -3597,7 +3621,7 @@ CFileItem CFileItem::GetItemToPlay() const { if (HasEPGInfoTag()) { - const CPVRChannelPtr channel(GetEPGInfoTag()->Channel()); + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(GetEPGInfoTag()); if (channel) return CFileItem(channel); } diff --git a/xbmc/addons/PVRClient.cpp b/xbmc/addons/PVRClient.cpp index c5b3fae92e..5748f5295a 100644 --- a/xbmc/addons/PVRClient.cpp +++ b/xbmc/addons/PVRClient.cpp @@ -35,6 +35,7 @@ extern "C" { #include "pvr/channels/PVRChannelGroupInternal.h" #include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/Epg.h" +#include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgContainer.h" #include "pvr/epg/EpgInfoTag.h" #include "pvr/recordings/PVRRecordings.h" @@ -609,11 +610,25 @@ PVR_ERROR CPVRClient::RenameChannel(const CPVRChannelPtr &channel) }, m_clientCapabilities.SupportsChannelSettings()); } -PVR_ERROR CPVRClient::GetEPGForChannel(const CPVRChannelPtr &channel, CPVREpg *epg, time_t start /* = 0 */, time_t end /* = 0 */, bool bSaveInDb /* = false*/) +PVR_ERROR CPVRClient::GetEPGForChannel(const std::shared_ptr<CPVREpgChannelData>& channelData, + CPVREpg* epg, + time_t start /* = 0 */, + time_t end /* = 0 */, + bool bSaveInDb /* = false */) { - return DoAddonCall(__FUNCTION__, [this, channel, epg, start, end, bSaveInDb](const AddonInstance* addon) { - PVR_CHANNEL addonChannel; - WriteClientChannelInfo(channel, addonChannel); + return DoAddonCall(__FUNCTION__, [this, channelData, epg, start, end, bSaveInDb](const AddonInstance* addon) { + + //! @todo PVR Addon API Change: Change GetEPGForChannel param from 'PVR_CHANNEL channel' to 'int iUniqueId'. + PVR_CHANNEL addonChannel = {0}; + + // mandatory + addonChannel.iUniqueId = channelData->UniqueClientChannelId(); + addonChannel.bIsRadio = channelData->IsRadio(); + + // optional + strncpy(addonChannel.strChannelName, channelData->ChannelName().c_str(), sizeof(addonChannel.strChannelName) - 1); + strncpy(addonChannel.strIconPath, channelData->IconPath().c_str(), sizeof(addonChannel.strIconPath) - 1); + addonChannel.bIsHidden = channelData->IsHidden(); ADDON_HANDLE_STRUCT handle; handle.callerAddress = this; @@ -647,15 +662,15 @@ class CAddonEpgTag : public EPG_TAG public: CAddonEpgTag() = delete; explicit CAddonEpgTag(const CConstPVREpgInfoTagPtr kodiTag) : - m_strTitle(kodiTag->Title(true)), - m_strPlotOutline(kodiTag->PlotOutline(true)), - m_strPlot(kodiTag->Plot(true)), - m_strOriginalTitle(kodiTag->OriginalTitle(true)), + m_strTitle(kodiTag->Title()), + m_strPlotOutline(kodiTag->PlotOutline()), + m_strPlot(kodiTag->Plot()), + m_strOriginalTitle(kodiTag->OriginalTitle()), m_strCast(kodiTag->DeTokenize(kodiTag->Cast())), m_strDirector(kodiTag->DeTokenize(kodiTag->Directors())), m_strWriter(kodiTag->DeTokenize(kodiTag->Writers())), m_strIMDBNumber(kodiTag->IMDBNumber()), - m_strEpisodeName(kodiTag->EpisodeName(true)), + m_strEpisodeName(kodiTag->EpisodeName()), m_strIconPath(kodiTag->Icon()), m_strSeriesLink(kodiTag->SeriesLink()), m_strGenreDescription(kodiTag->GetGenresLabel()) @@ -1484,7 +1499,7 @@ void CPVRClient::cb_transfer_channel_group(void *kodiInstance, const ADDON_HANDL } /* transfer this entry to the groups container */ - CPVRChannelGroup transferGroup(*group); + CPVRChannelGroup transferGroup(*group, kodiGroups->GetGroupAll()); kodiGroups->UpdateFromClient(transferGroup); } @@ -1664,7 +1679,6 @@ void CPVRClient::cb_trigger_channel_groups_update(void *kodiInstance) void CPVRClient::cb_trigger_epg_update(void *kodiInstance, unsigned int iChannelUid) { - // get the client CPVRClient *client = static_cast<CPVRClient*>(kodiInstance); if (!client) { @@ -1718,7 +1732,9 @@ void CPVRClient::cb_epg_event_state_change(void* kodiInstance, EPG_TAG* tag, EPG return; } - CServiceBroker::GetPVRManager().EpgContainer().UpdateFromClient(std::make_shared<CPVREpgInfoTag>(*tag, client->GetID()), newState); + // Note: channel data and epg id may not yet be available. Tag will be fully initialized later. + const std::shared_ptr<CPVREpgInfoTag> epgTag = std::make_shared<CPVREpgInfoTag>(*tag, client->GetID(), nullptr, -1); + CServiceBroker::GetPVRManager().EpgContainer().UpdateFromClient(epgTag, newState); } class CCodecIds diff --git a/xbmc/addons/PVRClient.h b/xbmc/addons/PVRClient.h index e88362618e..604ad2b166 100644 --- a/xbmc/addons/PVRClient.h +++ b/xbmc/addons/PVRClient.h @@ -22,6 +22,7 @@ namespace PVR { class CPVRChannelGroups; + class CPVREpgChannelData; class CPVRTimersContainer; class CPVRClientMenuHook; class CPVRClientMenuHooks; @@ -400,14 +401,14 @@ namespace PVR /*! * @brief Request an EPG table for a channel from the client. - * @param channel The channel to get the EPG table for. + * @param channelData The data for the channel to get the EPG table for. * @param epg The table to write the data to. * @param start The start time to use. * @param end The end time to use. * @param bSaveInDb If true, tell the callback method to save any new entry in the database or not. see CAddonCallbacksPVR::PVRTransferEpgEntry() * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully. */ - PVR_ERROR GetEPGForChannel(const CPVRChannelPtr &channel, CPVREpg *epg, time_t start = 0, time_t end = 0, bool bSaveInDb = false); + PVR_ERROR GetEPGForChannel(const std::shared_ptr<CPVREpgChannelData>& channelData, CPVREpg* epg, time_t start = 0, time_t end = 0, bool bSaveInDb = false); /*! * Tell the client the time frame to use when notifying epg events back to Kodi. The client might push epg events asynchronously diff --git a/xbmc/addons/kodi-addon-dev-kit/doxygen/Doxyfile b/xbmc/addons/kodi-addon-dev-kit/doxygen/Doxyfile index 06eb8be6da..1fba414e96 100644 --- a/xbmc/addons/kodi-addon-dev-kit/doxygen/Doxyfile +++ b/xbmc/addons/kodi-addon-dev-kit/doxygen/Doxyfile @@ -812,7 +812,6 @@ WARN_LOGFILE = INPUT = main.txt \ General/General.dox \ General/DoxygenOnAddon.dox \ - ../../../../docs/doxygen/CODING_GUIDELINES.dox \ ../../../GUIInfoManager.cpp \ Modules/modules_general.dox \ Modules/modules_cpp.dox \ diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp index c8a799c71b..eb51c164ba 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp @@ -1061,11 +1061,6 @@ void CDVDVideoCodecAndroidMediaCodec::SetCodecControl(int flags) { CLog::Log(LOGDEBUG, LOGVIDEO, "CDVDVideoCodecAndroidMediaCodec::%s %x->%x", __func__, m_codecControlFlags, flags); m_codecControlFlags = flags; - - if (m_codecControlFlags & DVD_CODEC_CTRL_DROP) - m_videobuffer.iFlags |= DVP_FLAG_DROPPED; - else - m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED; } } @@ -1227,8 +1222,9 @@ int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void) if (m_codecControlFlags & DVD_CODEC_CTRL_DROP) { + m_noPictureLoop = 0; AMediaCodec_releaseOutputBuffer(m_codec->codec(), index, false); - return -1; + return -2; } if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp index dec7d04567..11af64a251 100644 --- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp +++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp @@ -2078,6 +2078,21 @@ bool CDVDDemuxFFmpeg::IsVideoReady() hasVideo = true; } } + // Workaround for live audio-only MPEG-TS streams: If there are no elementary video streams + // present attempt to set the start time from the first available elementary audio stream instead + if (!hasVideo && !m_startTime) + { + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + { + int idx = m_pFormatContext->programs[m_program]->stream_index[i]; + st = m_pFormatContext->streams[idx]; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + { + m_startTime = static_cast<double>(av_rescale(st->cur_dts, st->time_base.num, st->time_base.den)); + break; + } + } + } } else { @@ -2095,6 +2110,20 @@ bool CDVDDemuxFFmpeg::IsVideoReady() hasVideo = true; } } + // Workaround for live audio-only MPEG-TS streams: If there are no elementary video streams + // present attempt to set the start time from the first available elementary audio stream instead + if (!hasVideo && !m_startTime) + { + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + st = m_pFormatContext->streams[i]; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + { + m_startTime = static_cast<double>(av_rescale(st->cur_dts, st->time_base.num, st->time_base.den)); + break; + } + } + } } return !hasVideo; } diff --git a/xbmc/cores/VideoPlayer/Edl.cpp b/xbmc/cores/VideoPlayer/Edl.cpp index 09023321c9..039614edc7 100644 --- a/xbmc/cores/VideoPlayer/Edl.cpp +++ b/xbmc/cores/VideoPlayer/Edl.cpp @@ -575,7 +575,7 @@ bool CEdl::ReadPvr(const CFileItem &fileItem) } else if (fileItem.HasEPGInfoTag()) { - CLog::Log(LOGDEBUG, "%s - Reading Edl for EPG: %s", __FUNCTION__, fileItem.GetEPGInfoTag()->Title(true).c_str()); + CLog::Log(LOGDEBUG, "%s - Reading Edl for EPG: %s", __FUNCTION__, fileItem.GetEPGInfoTag()->Title().c_str()); edl = fileItem.GetEPGInfoTag()->GetEdl(); } else diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp index 5ad2bcbf33..0cb65c7850 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp @@ -462,6 +462,7 @@ void CVideoPlayerVideo::Process() } m_renderManager.DiscardBuffer(); + FlushMessages(); } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { @@ -776,7 +777,6 @@ void CVideoPlayerVideo::Flush(bool sync) /* flush using message as this get's called from VideoPlayer thread */ /* and any demux packet that has been taken out of queue need to */ /* be disposed of before we flush */ - FlushMessages(); SendMessage(new CDVDMsgBool(CDVDMsg::GENERAL_FLUSH, sync), 1); m_bAbortOutput = true; } diff --git a/xbmc/filesystem/CurlFile.cpp b/xbmc/filesystem/CurlFile.cpp index d5dcc7d430..3bc6527d21 100644 --- a/xbmc/filesystem/CurlFile.cpp +++ b/xbmc/filesystem/CurlFile.cpp @@ -1024,7 +1024,7 @@ bool CCurlFile::Open(const CURL& url) // since we can't know the stream size up front if we're gzipped/deflated // flag the stream with an unknown file size rather than the compressed // file size. - if (!m_acceptencoding.empty()) + if (!m_state->m_httpheader.GetValue("Content-Encoding").empty() && !StringUtils::EqualsNoCase(m_state->m_httpheader.GetValue("Content-Encoding"), "identity")) m_state->m_fileSize = 0; // check if this stream is a shoutcast stream. sometimes checking the protocol line is not enough so examine other headers as well. diff --git a/xbmc/games/controllers/windows/GUIControllerWindow.cpp b/xbmc/games/controllers/windows/GUIControllerWindow.cpp index f8a2b9ffdc..65cfba0563 100644 --- a/xbmc/games/controllers/windows/GUIControllerWindow.cpp +++ b/xbmc/games/controllers/windows/GUIControllerWindow.cpp @@ -190,6 +190,8 @@ void CGUIControllerWindow::OnEvent(const ADDON::CRepositoryUpdater::RepositoryUp void CGUIControllerWindow::OnInitWindow(void) { + // subscribe to events + CServiceBroker::GetRepositoryUpdater().Events().Subscribe(this, &CGUIControllerWindow::OnEvent); // Get active game add-on GameClientPtr gameClient; { @@ -237,6 +239,8 @@ void CGUIControllerWindow::OnInitWindow(void) void CGUIControllerWindow::OnDeinitWindow(int nextWindowID) { + CServiceBroker::GetRepositoryUpdater().Events().Unsubscribe(this); + if (m_controllerList) { m_controllerList->Deinitialize(); diff --git a/xbmc/interfaces/json-rpc/PVROperations.cpp b/xbmc/interfaces/json-rpc/PVROperations.cpp index 9442fa443d..0e33a03ddf 100644 --- a/xbmc/interfaces/json-rpc/PVROperations.cpp +++ b/xbmc/interfaces/json-rpc/PVROperations.cpp @@ -157,7 +157,12 @@ JSONRPC_STATUS CPVROperations::GetBroadcasts(const std::string &method, ITranspo return InternalError; CFileItemList programFull; - channelEpg->Get(programFull); + + const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags = channelEpg->GetTags(); + for (const auto& tag : tags) + { + programFull.Add(std::make_shared<CFileItem>(tag)); + } HandleFileItemList("broadcastid", false, "broadcasts", programFull, parameterObject, result, programFull.Size(), true); @@ -169,7 +174,8 @@ JSONRPC_STATUS CPVROperations::GetBroadcastDetails(const std::string &method, IT if (!CServiceBroker::GetPVRManager().IsStarted()) return FailedToExecute; - const CPVREpgInfoTagPtr epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(CPVRChannelPtr(), parameterObject["broadcastid"].asUnsignedInteger()); + const std::shared_ptr<CPVREpgInfoTag> epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(nullptr, + parameterObject["broadcastid"].asUnsignedInteger()); if (!epgTag) return InvalidParams; @@ -210,13 +216,14 @@ JSONRPC_STATUS CPVROperations::Record(const std::string &method, ITransportLayer return FailedToExecute; CVariant record = parameterObject["record"]; + bool bIsRecording = CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*pChannel); bool toggle = true; - if (record.isBoolean() && record.asBoolean() == pChannel->IsRecording()) + if (record.isBoolean() && record.asBoolean() == bIsRecording) toggle = false; if (toggle) { - if (!CServiceBroker::GetPVRManager().GUIActions()->SetRecordingOnChannel(pChannel, !pChannel->IsRecording())) + if (!CServiceBroker::GetPVRManager().GUIActions()->SetRecordingOnChannel(pChannel, !bIsRecording)) return FailedToExecute; } @@ -321,12 +328,13 @@ JSONRPC_STATUS CPVROperations::AddTimer(const std::string &method, ITransportLay if (!CServiceBroker::GetPVRManager().IsStarted()) return FailedToExecute; - const CPVREpgInfoTagPtr epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(CPVRChannelPtr(), parameterObject["broadcastid"].asUnsignedInteger()); + const std::shared_ptr<CPVREpgInfoTag> epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(nullptr, + parameterObject["broadcastid"].asUnsignedInteger()); if (!epgTag) return InvalidParams; - if (epgTag->HasTimer()) + if (CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag)) return InvalidParams; CPVRTimerInfoTagPtr newTimer = CPVRTimerInfoTag::CreateFromEpg(epgTag, parameterObject["timerrule"].asBoolean(false)); @@ -363,14 +371,15 @@ JSONRPC_STATUS CPVROperations::ToggleTimer(const std::string &method, ITransport if (!CServiceBroker::GetPVRManager().IsStarted()) return FailedToExecute; - const CPVREpgInfoTagPtr epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(CPVRChannelPtr(), parameterObject["broadcastid"].asUnsignedInteger()); + const std::shared_ptr<CPVREpgInfoTag> epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(nullptr, + parameterObject["broadcastid"].asUnsignedInteger()); if (!epgTag) return InvalidParams; bool timerrule = parameterObject["timerrule"].asBoolean(false); bool sentOkay = false; - CPVRTimerInfoTagPtr timer(epgTag->Timer()); + std::shared_ptr<CPVRTimerInfoTag> timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag); if (timer) { if (timerrule) diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp index 26b2723d03..c3a750e7d2 100644 --- a/xbmc/music/MusicDatabase.cpp +++ b/xbmc/music/MusicDatabase.cpp @@ -1401,7 +1401,7 @@ bool CMusicDatabase::GetArtist(int idArtist, CArtist &artist, bool fetchAll /* = int discographyOffset = artist_enumCount; artist.discography.clear(); - artist = GetArtistFromDataset(m_pDS.get()->get_sql_record(), 0, fetchAll); + artist = GetArtistFromDataset(m_pDS.get()->get_sql_record(), 0, true); // inc scraped art URLs if (fetchAll) { while (!m_pDS->eof()) @@ -9707,7 +9707,7 @@ void CMusicDatabase::ExportToXML(const CLibExportSettings& settings, CGUIDialog for (const auto &artistId : artistIds) { CArtist artist; - GetArtist(artistId, artist); + GetArtist(artistId, artist, true); // include discography std::string strPath; std::map<std::string, std::string> artwork; if (settings.IsSingleFile()) @@ -9866,7 +9866,7 @@ void CMusicDatabase::ImportFromXML(const std::string &xmlFile) if (idArtist > -1) { CArtist artist; - GetArtist(idArtist, artist); + GetArtist(idArtist, artist, true); // include discography artist.MergeScrapedArtist(importedArtist, true); UpdateArtist(artist); } @@ -10484,13 +10484,6 @@ bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription } // remove the null string filter.AppendWhere("artistview.strArtist != ''"); - - // and the various artist entry if applicable - if (!albumArtistsOnly) - { - std::string strVariousArtists = g_localizeStrings.Get(340); - filter.AppendWhere(PrepareSQL("artistview.strArtist <> '%s'", strVariousArtists.c_str())); - } } else if (type == "albums") { diff --git a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp index e65bf729b3..802dd0779e 100644 --- a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp +++ b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp @@ -599,6 +599,8 @@ void CPeripheralCecAdapter::OnTvStandby(void) case LOCALISED_ID_STOP: KODI::MESSAGING::CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_STOP); break; + case LOCALISED_ID_IGNORE: + break; default: CLog::Log(LOGERROR, "%s - Unexpected [standby_pc_on_tv_standby] setting value", __FUNCTION__); break; diff --git a/xbmc/platform/android/activity/XBMCApp.cpp b/xbmc/platform/android/activity/XBMCApp.cpp index 3a6e6f65c5..e9ae1ab073 100644 --- a/xbmc/platform/android/activity/XBMCApp.cpp +++ b/xbmc/platform/android/activity/XBMCApp.cpp @@ -120,7 +120,6 @@ int CXBMCApp::m_batteryLevel = 0; bool CXBMCApp::m_hasFocus = false; bool CXBMCApp::m_headsetPlugged = false; bool CXBMCApp::m_hdmiPlugged = true; -bool CXBMCApp::m_hdmiReportedState = true; bool CXBMCApp::m_hdmiSource = false; IInputDeviceCallbacks* CXBMCApp::m_inputDeviceCallbacks = nullptr; IInputDeviceEventHandler* CXBMCApp::m_inputDeviceEventHandler = nullptr; @@ -524,11 +523,6 @@ void CXBMCApp::SetRefreshRateCallback(CVariant* rateVariant) CJNIWindowManagerLayoutParams params = window.getAttributes(); if (fabs(params.getpreferredRefreshRate() - rate) > 0.001) { - if (m_hdmiSource && g_application.GetAppPlayer().IsPlaying()) - { - dynamic_cast<CWinSystemAndroid*>(CServiceBroker::GetWinSystem())->SetHDMIState(false, 1000); - m_hdmiReportedState = false; - } params.setpreferredRefreshRate(rate); if (params.getpreferredRefreshRate() > 0.0) { @@ -552,11 +546,6 @@ void CXBMCApp::SetDisplayModeCallback(CVariant* variant) CJNIWindowManagerLayoutParams params = window.getAttributes(); if (params.getpreferredDisplayModeId() != mode) { - if (m_hdmiSource && g_application.GetAppPlayer().IsPlaying()) - { - dynamic_cast<CWinSystemAndroid*>(CServiceBroker::GetWinSystem())->SetHDMIState(false); - m_hdmiReportedState = false; - } params.setpreferredDisplayModeId(mode); params.setpreferredRefreshRate(rate); window.setAttributes(params); @@ -585,7 +574,11 @@ void CXBMCApp::SetRefreshRate(float rate) CVariant *variant = new CVariant(rate); runNativeOnUiThread(SetRefreshRateCallback, variant); if (g_application.IsInitialized()) + { m_displayChangeEvent.WaitMSec(5000); + if (m_hdmiSource && g_application.GetAppPlayer().IsPlaying()) + dynamic_cast<CWinSystemAndroid*>(CServiceBroker::GetWinSystem())->SetHDMIState(false); + } } void CXBMCApp::SetDisplayMode(int mode, float rate) @@ -602,7 +595,6 @@ void CXBMCApp::SetDisplayMode(int mode, float rate) } m_displayChangeEvent.Reset(); - std::map<std::string, CVariant> vmap; vmap["mode"] = mode; vmap["rate"] = rate; @@ -610,7 +602,11 @@ void CXBMCApp::SetDisplayMode(int mode, float rate) CVariant *variant = new CVariant(vmap); runNativeOnUiThread(SetDisplayModeCallback, variant); if (g_application.IsInitialized()) + { m_displayChangeEvent.WaitMSec(5000); + if (m_hdmiSource && g_application.GetAppPlayer().IsPlaying()) + dynamic_cast<CWinSystemAndroid*>(CServiceBroker::GetWinSystem())->SetHDMIState(false); + } } int CXBMCApp::android_printf(const char *format, ...) @@ -1018,24 +1014,13 @@ void CXBMCApp::onReceive(CJNIIntent intent) } else if (action == "android.media.action.HDMI_AUDIO_PLUG") { - bool newstate; - newstate = (intent.getIntExtra("android.media.extra.AUDIO_PLUG_STATE", 0) != 0); - - if (newstate != m_hdmiPlugged) + m_hdmiPlugged = (intent.getIntExtra("android.media.extra.AUDIO_PLUG_STATE", 0) != 0); + CLog::Log(LOGDEBUG, "-- HDMI state: %s", m_hdmiPlugged ? "on" : "off"); + if (m_hdmiSource && g_application.IsInitialized()) { - CLog::Log(LOGDEBUG, "-- HDMI state: %s", newstate ? "on" : "off"); - m_hdmiPlugged = newstate; - if (m_hdmiPlugged != m_hdmiReportedState) - { - if (g_application.IsInitialized()) - { - CWinSystemBase* winSystem = CServiceBroker::GetWinSystem(); - if (winSystem && dynamic_cast<CWinSystemAndroid*>(winSystem)) - dynamic_cast<CWinSystemAndroid*>(winSystem)->SetHDMIState(m_hdmiPlugged); - - m_hdmiReportedState = m_hdmiPlugged; - } - } + CWinSystemBase* winSystem = CServiceBroker::GetWinSystem(); + if (winSystem && dynamic_cast<CWinSystemAndroid*>(winSystem)) + dynamic_cast<CWinSystemAndroid*>(winSystem)->SetHDMIState(m_hdmiPlugged); } } else if (action == "android.intent.action.SCREEN_OFF") @@ -1409,6 +1394,7 @@ void CXBMCApp::onDisplayAdded(int displayId) void CXBMCApp::onDisplayChanged(int displayId) { + CLog::Log(LOGDEBUG, "CXBMCApp::%s: id: %d", __FUNCTION__, displayId); m_displayChangeEvent.Set(); android_printf("%s: ", __PRETTY_FUNCTION__); } diff --git a/xbmc/pvr/PVRContextMenus.cpp b/xbmc/pvr/PVRContextMenus.cpp index 653b5f3e49..0eb9802383 100644 --- a/xbmc/pvr/PVRContextMenus.cpp +++ b/xbmc/pvr/PVRContextMenus.cpp @@ -20,8 +20,10 @@ #include "pvr/channels/PVRChannel.h" #include "pvr/epg/EpgInfoTag.h" #include "pvr/recordings/PVRRecording.h" +#include "pvr/recordings/PVRRecordings.h" #include "pvr/recordings/PVRRecordingsPath.h" #include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimersPath.h" namespace PVR { @@ -85,7 +87,7 @@ namespace PVR const CPVREpgInfoTagPtr epg(item.GetEPGInfoTag()); if (epg) - timer = epg->Timer(); + timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg); if (!timer) timer = item.GetPVRTimerInfoTag(); @@ -115,12 +117,7 @@ namespace PVR bool PlayRecording::IsVisible(const CFileItem &item) const { - CPVRRecordingPtr recording; - - const CPVREpgInfoTagPtr epg(item.GetEPGInfoTag()); - if (epg) - recording = epg->Recording(); - + const std::shared_ptr<CPVRRecording> recording = CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(item.GetEPGInfoTag()); if (recording) return !recording->IsDeleted(); @@ -220,10 +217,13 @@ namespace PVR const CPVRChannelPtr channel = item.GetPVRChannelInfoTag(); if (channel) - return !channel->IsRecording() && client && client->GetClientCapabilities().SupportsTimers(); + return client && client->GetClientCapabilities().SupportsTimers() && + !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel); const CPVREpgInfoTagPtr epg = item.GetEPGInfoTag(); - if (epg && !epg->Timer() && epg->Channel() && epg->IsRecordable()) + if (epg && + !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg) && + epg->IsRecordable()) return client && client->GetClientCapabilities().SupportsTimers(); return false; @@ -245,7 +245,7 @@ namespace PVR const CPVRChannelPtr channel(item.GetPVRChannelInfoTag()); if (channel) - return channel->IsRecording(); + return CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel); const CPVRTimerInfoTagPtr timer(GetTimerInfoTagFromItem(item)); if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER)) @@ -381,7 +381,7 @@ namespace PVR bool AddTimerRule::IsVisible(const CFileItem &item) const { const CPVREpgInfoTagPtr epg = item.GetEPGInfoTag(); - if (epg && epg->Channel() && !epg->Timer()) + if (epg && !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg)) { const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(item); return client && client->GetClientCapabilities().SupportsTimers(); diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp index 6eaa6131d2..ffcdb13d3b 100644 --- a/xbmc/pvr/PVRDatabase.cpp +++ b/xbmc/pvr/PVRDatabase.cpp @@ -521,7 +521,7 @@ bool CPVRDatabase::Get(CPVRChannelGroups &results) { while (!m_pDS->eof()) { - CPVRChannelGroup data(m_pDS->fv("bIsRadio").get_asBool(), m_pDS->fv("idGroup").get_asInt(), m_pDS->fv("sName").get_asString()); + CPVRChannelGroup data(m_pDS->fv("bIsRadio").get_asBool(), m_pDS->fv("idGroup").get_asInt(), m_pDS->fv("sName").get_asString(), results.GetGroupAll()); data.SetGroupType(m_pDS->fv("iGroupType").get_asInt()); data.SetLastWatched(static_cast<time_t>(m_pDS->fv("iLastWatched").get_asInt())); data.SetHidden(m_pDS->fv("bIsHidden").get_asBool()); diff --git a/xbmc/pvr/PVRGUIActions.cpp b/xbmc/pvr/PVRGUIActions.cpp index c320ae9d32..d6d2f01bd9 100644 --- a/xbmc/pvr/PVRGUIActions.cpp +++ b/xbmc/pvr/PVRGUIActions.cpp @@ -308,7 +308,7 @@ namespace PVR return false; } - CPVRTimerInfoTagPtr timer(bCreateRule || !epgTag ? nullptr : epgTag->Timer()); + CPVRTimerInfoTagPtr timer(bCreateRule || !epgTag ? nullptr : CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag)); CPVRTimerInfoTagPtr rule (bCreateRule ? CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer) : nullptr); if (timer || rule) { @@ -483,7 +483,7 @@ namespace PVR { const CPVRChannelPtr channel = CServiceBroker::GetPVRManager().GetPlayingChannel(); if (channel && channel->CanRecord()) - return SetRecordingOnChannel(channel, !channel->IsRecording()); + return SetRecordingOnChannel(channel, !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel)); return false; } @@ -502,7 +502,7 @@ namespace PVR if (client && client->GetClientCapabilities().SupportsTimers()) { /* timers are supported on this channel */ - if (bOnOff && !channel->IsRecording()) + if (bOnOff && !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel)) { CPVREpgInfoTagPtr epgTag; int iDuration = m_settings.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME); @@ -537,15 +537,19 @@ namespace PVR epgTag = channel->GetEPGNow(); if (epgTag) { + bool bLocked = CServiceBroker::GetPVRManager().IsParentalLocked(epgTag); + // "now" - selector.AddAction(RECORD_CURRENT_SHOW, epgTag->Title()); + const std::string currentTitle = bLocked ? g_localizeStrings.Get(19266) /* Parental locked */ : epgTag->Title(); + selector.AddAction(RECORD_CURRENT_SHOW, currentTitle); ePreselect = RECORD_CURRENT_SHOW; // "next" epgTagNext = channel->GetEPGNext(); if (epgTagNext) { - selector.AddAction(RECORD_NEXT_SHOW, epgTagNext->Title()); + const std::string nextTitle = bLocked ? g_localizeStrings.Get(19266) /* Parental locked */ : epgTagNext->Title(); + selector.AddAction(RECORD_NEXT_SHOW, nextTitle); // be smart. if current show is almost over, preselect next show. if (epgTag->ProgressPercentage() > 90.0f) @@ -609,7 +613,7 @@ namespace PVR if (!bReturn) HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19164}); // "Error", "Could not start recording. Check the log for more information about this message." } - else if (!bOnOff && channel->IsRecording()) + else if (!bOnOff && CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel)) { /* delete active timers */ bReturn = CServiceBroker::GetPVRManager().Timers()->DeleteTimersOnChannel(channel, true, true); @@ -751,7 +755,7 @@ namespace PVR CPVRTimerInfoTagPtr timer; const CPVRRecordingPtr recording(CPVRItem(item).GetRecording()); if (recording) - timer = CServiceBroker::GetPVRManager().Timers()->GetRecordingTimerForRecording(*recording); + timer = recording->GetRecordingTimer(); if (!timer) timer = CPVRItem(item).GetTimerInfoTag(); @@ -1130,20 +1134,33 @@ namespace PVR if (item->m_bIsFolder) return false; + std::shared_ptr<CPVRRecording> recording; const CPVRChannelPtr channel(CPVRItem(item).GetChannel()); - if ((channel && CServiceBroker::GetPVRManager().IsPlayingChannel(channel)) || - (channel && channel->HasRecording() && CServiceBroker::GetPVRManager().IsPlayingRecording(channel->GetRecording()))) + if (channel) { - CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); - CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); - return true; + bool bSwitchToFullscreen = CServiceBroker::GetPVRManager().IsPlayingChannel(channel); + + if (!bSwitchToFullscreen) + { + recording = CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(channel->GetEPGNow()); + bSwitchToFullscreen = recording && CServiceBroker::GetPVRManager().IsPlayingRecording(recording); + } + + if (bSwitchToFullscreen) + { + CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + return true; + } } ParentalCheckResult result = channel ? CheckParentalLock(channel) : ParentalCheckResult::FAILED; if (result == ParentalCheckResult::SUCCESS) { // switch to channel or if recording present, ask whether to switch or play recording... - const CPVRRecordingPtr recording(channel->GetRecording()); + if (!recording) + recording = CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(channel->GetEPGNow()); + if (recording) { bool bCancel(false); @@ -1682,12 +1699,12 @@ namespace PVR bool CPVRGUIActions::AllLocalBackendsIdle(CPVRTimerInfoTagPtr& causingEvent) const { // active recording on local backend? - const std::vector<CFileItemPtr> activeRecordings = CServiceBroker::GetPVRManager().Timers()->GetActiveRecordings(); + const std::vector<std::shared_ptr<CPVRTimerInfoTag>> activeRecordings = CServiceBroker::GetPVRManager().Timers()->GetActiveRecordings(); for (const auto& timer : activeRecordings) { - if (EventOccursOnLocalBackend(timer)) + if (EventOccursOnLocalBackend(std::make_shared<CFileItem>(timer))) { - causingEvent = timer->GetPVRTimerInfoTag(); + causingEvent = timer; return false; } } @@ -1695,17 +1712,17 @@ namespace PVR // soon recording on local backend? if (IsNextEventWithinBackendIdleTime()) { - const CFileItemPtr item = CServiceBroker::GetPVRManager().Timers()->GetNextActiveTimer(); - if (!item) + const std::shared_ptr<CPVRTimerInfoTag> timer = CServiceBroker::GetPVRManager().Timers()->GetNextActiveTimer(); + if (!timer) { // Next event is due to automatic daily wakeup of PVR! causingEvent.reset(); return false; } - if (EventOccursOnLocalBackend(item)) + if (EventOccursOnLocalBackend(std::make_shared<CFileItem>(timer))) { - causingEvent = item->GetPVRTimerInfoTag(); + causingEvent = timer; return false; } } diff --git a/xbmc/pvr/PVRGUIInfo.cpp b/xbmc/pvr/PVRGUIInfo.cpp index a09cb158a3..7988e610c5 100644 --- a/xbmc/pvr/PVRGUIInfo.cpp +++ b/xbmc/pvr/PVRGUIInfo.cpp @@ -16,6 +16,7 @@ #include "ServiceBroker.h" #include "guilib/GUIComponent.h" #include "guilib/LocalizeStrings.h" +#include "guilib/GUIWindowManager.h" #include "guilib/guiinfo/GUIInfo.h" #include "guilib/guiinfo/GUIInfoHelper.h" #include "guilib/guiinfo/GUIInfoLabels.h" @@ -275,6 +276,25 @@ bool CPVRGUIInfo::GetLabel(std::string& value, const CFileItem *item, int contex GetRadioRDSLabel(item, info, value); } +namespace +{ + std::string GetAsLocalizedDateString(const CDateTime& datetime, bool bLongDate) + { + return datetime.IsValid() ? datetime.GetAsLocalizedDate(bLongDate) : ""; + } + + std::string GetAsLocalizedTimeString(const CDateTime& datetime) + { + return datetime.IsValid() ? datetime.GetAsLocalizedTime("", false) : ""; + } + + std::string GetAsLocalizedDateTimeString(const CDateTime& datetime) + { + return datetime.IsValid() ? datetime.GetAsLocalizedDateTime(false, false) : ""; + } + +} // unnamed namespace + bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInfo &info, std::string &strValue) const { const CPVRTimerInfoTagPtr timer = item->GetPVRTimerInfoTag(); @@ -286,16 +306,16 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInf strValue = timer->Summary(); return true; case LISTITEM_STARTDATE: - strValue = timer->StartAsLocalTime().GetAsLocalizedDate(true); + strValue = GetAsLocalizedDateString(timer->StartAsLocalTime(), true); return true; case LISTITEM_STARTTIME: - strValue = timer->StartAsLocalTime().GetAsLocalizedTime("", false); + strValue = GetAsLocalizedTimeString(timer->StartAsLocalTime()); return true; case LISTITEM_ENDDATE: - strValue = timer->EndAsLocalTime().GetAsLocalizedDate(true); + strValue = GetAsLocalizedDateString(timer->EndAsLocalTime(), true); return true; case LISTITEM_ENDTIME: - strValue = timer->EndAsLocalTime().GetAsLocalizedTime("", false); + strValue = GetAsLocalizedTimeString(timer->EndAsLocalTime()); return true; case LISTITEM_DURATION: if (timer->GetDuration() > 0) @@ -308,7 +328,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInf strValue = timer->Title(); return true; case LISTITEM_COMMENT: - strValue = timer->GetStatus(); + strValue = timer->GetStatus(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_RADIO_TIMER_RULES); return true; case LISTITEM_TIMERTYPE: strValue = timer->GetTypeAsString(); @@ -343,33 +363,33 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInf switch (info.m_info) { case LISTITEM_DATE: - strValue = recording->RecordingTimeAsLocalTime().GetAsLocalizedDateTime(false, false); + strValue = GetAsLocalizedDateTimeString(recording->RecordingTimeAsLocalTime()); return true; case LISTITEM_STARTDATE: - strValue = recording->RecordingTimeAsLocalTime().GetAsLocalizedDate(true); + strValue = GetAsLocalizedDateString(recording->RecordingTimeAsLocalTime(), true); return true; case VIDEOPLAYER_STARTTIME: case LISTITEM_STARTTIME: - strValue = recording->RecordingTimeAsLocalTime().GetAsLocalizedTime("", false); + strValue = GetAsLocalizedTimeString(recording->RecordingTimeAsLocalTime()); return true; case LISTITEM_ENDDATE: - strValue = recording->EndTimeAsLocalTime().GetAsLocalizedDate(true); + strValue = GetAsLocalizedDateString(recording->EndTimeAsLocalTime(), true); return true; case VIDEOPLAYER_ENDTIME: case LISTITEM_ENDTIME: - strValue = recording->EndTimeAsLocalTime().GetAsLocalizedTime("", false); + strValue = GetAsLocalizedTimeString(recording->EndTimeAsLocalTime()); return true; case LISTITEM_EXPIRATION_DATE: if (recording->HasExpirationTime()) { - strValue = recording->ExpirationTimeAsLocalTime().GetAsLocalizedDate(false); + strValue = GetAsLocalizedDateString(recording->ExpirationTimeAsLocalTime(), false); return true; } break; case LISTITEM_EXPIRATION_TIME: if (recording->HasExpirationTime()) { - strValue = recording->ExpirationTimeAsLocalTime().GetAsLocalizedTime("", false);; + strValue = GetAsLocalizedTimeString(recording->ExpirationTimeAsLocalTime());; return true; } break; @@ -448,7 +468,10 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInf // Note: in difference to LISTITEM_TITLE, LISTITEM_EPG_EVENT_TITLE returns the title // associated with the epg event of a timer, if any, and not the title of the timer. if (epgTag) - strValue = epgTag->Title(); + { + bool bLocked = CServiceBroker::GetPVRManager().IsParentalLocked(epgTag); + strValue = bLocked ? g_localizeStrings.Get(19266) /* Parental locked */ : epgTag->Title(); + } if (strValue.empty() && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_HIDENOINFOAVAILABLE)) strValue = g_localizeStrings.Get(19055); // no information available return true; @@ -469,36 +492,38 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInf case LISTITEM_PLOT: case VIDEOPLAYER_NEXT_PLOT: case LISTITEM_NEXT_PLOT: - strValue = epgTag->Plot(); + if (!CServiceBroker::GetPVRManager().IsParentalLocked(epgTag)) + strValue = epgTag->Plot(); return true; case VIDEOPLAYER_PLOT_OUTLINE: case LISTITEM_PLOT_OUTLINE: case VIDEOPLAYER_NEXT_PLOT_OUTLINE: case LISTITEM_NEXT_PLOT_OUTLINE: - strValue = epgTag->PlotOutline(); + if (!CServiceBroker::GetPVRManager().IsParentalLocked(epgTag)) + strValue = epgTag->PlotOutline(); return true; case LISTITEM_DATE: - strValue = epgTag->StartAsLocalTime().GetAsLocalizedDateTime(false, false); + strValue = GetAsLocalizedDateTimeString(epgTag->StartAsLocalTime()); return true; case LISTITEM_STARTDATE: case LISTITEM_NEXT_STARTDATE: - strValue = epgTag->StartAsLocalTime().GetAsLocalizedDate(true); + strValue = GetAsLocalizedDateString(epgTag->StartAsLocalTime(), true); return true; case VIDEOPLAYER_STARTTIME: case VIDEOPLAYER_NEXT_STARTTIME: case LISTITEM_STARTTIME: case LISTITEM_NEXT_STARTTIME: - strValue = epgTag->StartAsLocalTime().GetAsLocalizedTime("", false); + strValue = GetAsLocalizedTimeString(epgTag->StartAsLocalTime()); return true; case LISTITEM_ENDDATE: case LISTITEM_NEXT_ENDDATE: - strValue = epgTag->EndAsLocalTime().GetAsLocalizedDate(true); + strValue = GetAsLocalizedDateString(epgTag->EndAsLocalTime(), true); return true; case VIDEOPLAYER_ENDTIME: case VIDEOPLAYER_NEXT_ENDTIME: case LISTITEM_ENDTIME: case LISTITEM_NEXT_ENDTIME: - strValue = epgTag->EndAsLocalTime().GetAsLocalizedTime("", false); + strValue = GetAsLocalizedTimeString(epgTag->EndAsLocalTime()); return true; // note: for some reason, there is no VIDEOPLAYER_DURATION case LISTITEM_DURATION: @@ -516,7 +541,8 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInf return true; case VIDEOPLAYER_ORIGINALTITLE: case LISTITEM_ORIGINALTITLE: - strValue = epgTag->OriginalTitle(); + if (!CServiceBroker::GetPVRManager().IsParentalLocked(epgTag)) + strValue = epgTag->OriginalTitle(); return true; case VIDEOPLAYER_YEAR: case LISTITEM_YEAR: @@ -547,7 +573,8 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem *item, const CGUIInf return false; case VIDEOPLAYER_EPISODENAME: case LISTITEM_EPISODENAME: - strValue = epgTag->EpisodeName(); + if (!CServiceBroker::GetPVRManager().IsParentalLocked(epgTag)) + strValue = epgTag->EpisodeName(); return true; case VIDEOPLAYER_CAST: case LISTITEM_CAST: @@ -1083,7 +1110,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem *item, const CGUIInfo case LISTITEM_ISRECORDING: if (item->IsPVRChannel()) { - bValue = item->GetPVRChannelInfoTag()->IsRecording(); + bValue = CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*item->GetPVRChannelInfoTag()); return true; } else if (item->IsEPG() || item->IsPVRTimer()) @@ -1113,7 +1140,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem *item, const CGUIInfo { const CPVREpgInfoTagPtr epgTag = CPVRItem(item).GetEpgInfoTag(); if (epgTag) - bValue = epgTag->HasTimer(); + bValue = !!CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag); return true; } break; @@ -1158,7 +1185,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem *item, const CGUIInfo { const CPVREpgInfoTagPtr epgTag = CPVRItem(item).GetEpgInfoTag(); if (epgTag) - bValue = epgTag->HasRecording(); + bValue = !!CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(epgTag); return true; } break; @@ -1205,8 +1232,9 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem *item, const CGUIInfo if (item->IsPVRRecording()) { const CPVRRecordingPtr recording = item->GetPVRRecordingInfoTag(); - const CPVREpgInfoTagPtr epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(recording->Channel(), recording->BroadcastUid()); - bValue = (epgTag && epgTag->IsActive() && epgTag->Channel()); + const std::shared_ptr<CPVREpg> epg = recording->Channel() ? recording->Channel()->GetEPG() : nullptr; + const std::shared_ptr<CPVREpgInfoTag> epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(epg, recording->BroadcastUid()); + bValue = (epgTag && epgTag->IsActive()); return true; } break; diff --git a/xbmc/pvr/PVRGUITimerInfo.cpp b/xbmc/pvr/PVRGUITimerInfo.cpp index 12b8870bfc..eafbbaa5ab 100644 --- a/xbmc/pvr/PVRGUITimerInfo.cpp +++ b/xbmc/pvr/PVRGUITimerInfo.cpp @@ -87,10 +87,10 @@ void CPVRGUITimerInfo::UpdateTimersToggle() /* safe to fetch these unlocked, since they're updated from the same thread as this one */ if (m_iRecordingTimerAmount > 0) { - std::vector<CFileItemPtr> activeTags = GetActiveRecordings(); - if (m_iTimerInfoToggleCurrent < activeTags.size() && activeTags.at(m_iTimerInfoToggleCurrent)->HasPVRTimerInfoTag()) + std::vector<std::shared_ptr<CPVRTimerInfoTag>> activeTags = GetActiveRecordings(); + if (m_iTimerInfoToggleCurrent < activeTags.size()) { - CPVRTimerInfoTagPtr tag = activeTags.at(m_iTimerInfoToggleCurrent)->GetPVRTimerInfoTag(); + const std::shared_ptr<CPVRTimerInfoTag> tag = activeTags.at(m_iTimerInfoToggleCurrent); strActiveTimerTitle = StringUtils::Format("%s", tag->Title().c_str()); strActiveTimerChannelName = StringUtils::Format("%s", tag->ChannelName().c_str()); strActiveTimerChannelIcon = StringUtils::Format("%s", tag->ChannelIcon().c_str()); @@ -128,10 +128,9 @@ void CPVRGUITimerInfo::UpdateNextTimer() std::string strNextRecordingTime; std::string strNextTimerInfo; - CFileItemPtr tag = GetNextActiveTimer(); - if (tag && tag->HasPVRTimerInfoTag()) + const std::shared_ptr<CPVRTimerInfoTag> timer = GetNextActiveTimer(); + if (timer) { - CPVRTimerInfoTagPtr timer = tag->GetPVRTimerInfoTag(); strNextRecordingTitle = StringUtils::Format("%s", timer->Title().c_str()); strNextRecordingChannelName = StringUtils::Format("%s", timer->ChannelName().c_str()); strNextRecordingChannelIcon = StringUtils::Format("%s", timer->ChannelIcon().c_str()); @@ -216,12 +215,12 @@ int CPVRGUIAnyTimerInfo::AmountActiveRecordings() return CServiceBroker::GetPVRManager().Timers()->AmountActiveRecordings(); } -std::vector<CFileItemPtr> CPVRGUIAnyTimerInfo::GetActiveRecordings() +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRGUIAnyTimerInfo::GetActiveRecordings() { return CServiceBroker::GetPVRManager().Timers()->GetActiveRecordings(); } -CFileItemPtr CPVRGUIAnyTimerInfo::GetNextActiveTimer() +std::shared_ptr<CPVRTimerInfoTag> CPVRGUIAnyTimerInfo::GetNextActiveTimer() { return CServiceBroker::GetPVRManager().Timers()->GetNextActiveTimer(); } @@ -236,12 +235,12 @@ int CPVRGUITVTimerInfo::AmountActiveRecordings() return CServiceBroker::GetPVRManager().Timers()->AmountActiveTVRecordings(); } -std::vector<CFileItemPtr> CPVRGUITVTimerInfo::GetActiveRecordings() +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRGUITVTimerInfo::GetActiveRecordings() { return CServiceBroker::GetPVRManager().Timers()->GetActiveTVRecordings(); } -CFileItemPtr CPVRGUITVTimerInfo::GetNextActiveTimer() +std::shared_ptr<CPVRTimerInfoTag> CPVRGUITVTimerInfo::GetNextActiveTimer() { return CServiceBroker::GetPVRManager().Timers()->GetNextActiveTVTimer(); } @@ -256,12 +255,12 @@ int CPVRGUIRadioTimerInfo::AmountActiveRecordings() return CServiceBroker::GetPVRManager().Timers()->AmountActiveRadioRecordings(); } -std::vector<CFileItemPtr> CPVRGUIRadioTimerInfo::GetActiveRecordings() +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRGUIRadioTimerInfo::GetActiveRecordings() { return CServiceBroker::GetPVRManager().Timers()->GetActiveRadioRecordings(); } -CFileItemPtr CPVRGUIRadioTimerInfo::GetNextActiveTimer() +std::shared_ptr<CPVRTimerInfoTag> CPVRGUIRadioTimerInfo::GetNextActiveTimer() { return CServiceBroker::GetPVRManager().Timers()->GetNextActiveRadioTimer(); } diff --git a/xbmc/pvr/PVRGUITimerInfo.h b/xbmc/pvr/PVRGUITimerInfo.h index 2bc6f2f21b..4db4edb5b4 100644 --- a/xbmc/pvr/PVRGUITimerInfo.h +++ b/xbmc/pvr/PVRGUITimerInfo.h @@ -14,11 +14,10 @@ #include "threads/CriticalSection.h" -class CFileItem; -typedef std::shared_ptr<CFileItem> CFileItemPtr; - namespace PVR { + class CPVRTimerInfoTag; + class CPVRGUITimerInfo { public: @@ -50,8 +49,8 @@ namespace PVR virtual int AmountActiveTimers() = 0; virtual int AmountActiveRecordings() = 0; - virtual std::vector<CFileItemPtr> GetActiveRecordings() = 0; - virtual CFileItemPtr GetNextActiveTimer() = 0; + virtual std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveRecordings() = 0; + virtual std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTimer() = 0; unsigned int m_iTimerAmount; unsigned int m_iRecordingTimerAmount; @@ -80,8 +79,8 @@ namespace PVR private: int AmountActiveTimers() override; int AmountActiveRecordings() override; - std::vector<CFileItemPtr> GetActiveRecordings() override; - CFileItemPtr GetNextActiveTimer() override; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveRecordings() override; + std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTimer() override; }; class CPVRGUITVTimerInfo : public CPVRGUITimerInfo @@ -92,8 +91,8 @@ namespace PVR private: int AmountActiveTimers() override; int AmountActiveRecordings() override; - std::vector<CFileItemPtr> GetActiveRecordings() override; - CFileItemPtr GetNextActiveTimer() override; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveRecordings() override; + std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTimer() override; }; class CPVRGUIRadioTimerInfo : public CPVRGUITimerInfo @@ -104,8 +103,8 @@ namespace PVR private: int AmountActiveTimers() override; int AmountActiveRecordings() override; - std::vector<CFileItemPtr> GetActiveRecordings() override; - CFileItemPtr GetNextActiveTimer() override; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveRecordings() override; + std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTimer() override; }; } // namespace PVR diff --git a/xbmc/pvr/PVRGUITimesInfo.cpp b/xbmc/pvr/PVRGUITimesInfo.cpp index 365458164c..41f73971eb 100644 --- a/xbmc/pvr/PVRGUITimesInfo.cpp +++ b/xbmc/pvr/PVRGUITimesInfo.cpp @@ -19,6 +19,7 @@ #include "utils/StringUtils.h" #include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" using namespace PVR; @@ -56,9 +57,11 @@ void CPVRGUITimesInfo::UpdatePlayingTag() if (currentChannel && !currentTag) currentTag = currentChannel->GetEPGNow(); + const std::shared_ptr<CPVRChannelGroupsContainer> groups = CServiceBroker::GetPVRManager().ChannelGroups(); + CSingleLock lock(m_critSection); - const CPVRChannelPtr playingChannel = m_playingEpgTag ? m_playingEpgTag->Channel() : nullptr; + const std::shared_ptr<CPVRChannel> playingChannel = m_playingEpgTag ? groups->GetChannelForEpgTag(m_playingEpgTag) : nullptr; if (!m_playingEpgTag || !m_playingEpgTag->IsActive() || !playingChannel || !currentChannel || *playingChannel != *currentChannel) { diff --git a/xbmc/pvr/PVRItem.cpp b/xbmc/pvr/PVRItem.cpp index d9667d2477..1f51ed6693 100644 --- a/xbmc/pvr/PVRItem.cpp +++ b/xbmc/pvr/PVRItem.cpp @@ -14,8 +14,10 @@ #include "pvr/PVRManager.h" #include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/EpgInfoTag.h" #include "pvr/recordings/PVRRecording.h" +#include "pvr/recordings/PVRRecordings.h" #include "pvr/timers/PVRTimerInfoTag.h" #include "pvr/timers/PVRTimers.h" @@ -46,7 +48,7 @@ namespace PVR { if (m_item->IsEPG()) { - const CPVRChannelPtr channel = m_item->GetEPGInfoTag()->Channel(); + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(m_item->GetEPGInfoTag()); if (channel) return channel->GetEPGNext(); } @@ -75,7 +77,7 @@ namespace PVR } else if (m_item->IsEPG()) { - return m_item->GetEPGInfoTag()->Channel(); + return CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(m_item->GetEPGInfoTag()); } else if (m_item->IsPVRTimer()) { @@ -96,19 +98,11 @@ namespace PVR } else if (m_item->IsEPG()) { - return m_item->GetEPGInfoTag()->Timer(); + return CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(m_item->GetEPGInfoTag()); } else if (m_item->IsPVRChannel()) { - CPVRTimerInfoTagPtr timer; - const CPVREpgInfoTagPtr epgTag(m_item->GetPVRChannelInfoTag()->GetEPGNow()); - if (epgTag) - timer = epgTag->Timer(); // cheap method, but not reliable as timers get set at epg tags asynchronously - - if (timer) - return timer; - - return CServiceBroker::GetPVRManager().Timers()->GetActiveTimerForChannel(m_item->GetPVRChannelInfoTag()); // more expensive, but reliable and works even for channels with no epg data + return CServiceBroker::GetPVRManager().Timers()->GetActiveTimerForChannel(m_item->GetPVRChannelInfoTag()); } else { @@ -125,7 +119,7 @@ namespace PVR } else if (m_item->IsEPG()) { - return m_item->GetEPGInfoTag()->Recording(); + return CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(m_item->GetEPGInfoTag()); } else { @@ -142,8 +136,7 @@ namespace PVR } else if (m_item->IsEPG()) { - const CPVRChannelPtr channel(m_item->GetEPGInfoTag()->Channel()); - return (channel && channel->IsRadio()); + return m_item->GetEPGInfoTag()->IsRadio(); } else if (m_item->IsPVRRecording()) { diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp index 42a394d41b..df41b440e2 100644 --- a/xbmc/pvr/PVRManager.cpp +++ b/xbmc/pvr/PVRManager.cpp @@ -650,6 +650,14 @@ bool CPVRManager::IsPlayingEpgTag(const CPVREpgInfoTagPtr &epgTag) const return bReturn; } +bool CPVRManager::MatchPlayingChannel(int iClientID, int iUniqueChannelID) const +{ + if (m_playingChannel) + return m_playingChannel->ClientID() == iClientID && m_playingChannel->UniqueID() == iUniqueChannelID; + + return false; +} + CPVRChannelPtr CPVRManager::GetPlayingChannel(void) const { return m_playingChannel; @@ -678,7 +686,7 @@ int CPVRManager::GetPlayingClientID(void) const bool CPVRManager::IsRecordingOnPlayingChannel(void) const { const CPVRChannelPtr currentChannel = GetPlayingChannel(); - return currentChannel && currentChannel->IsRecording(); + return currentChannel && CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*currentChannel); } bool CPVRManager::CanRecordOnPlayingChannel(void) const @@ -693,19 +701,33 @@ void CPVRManager::RestartParentalTimer() m_parentalTimer->StartZero(); } -bool CPVRManager::IsParentalLocked(const CPVRChannelPtr &channel) +bool CPVRManager::IsParentalLocked(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const { - bool bReturn(false); - if (!IsStarted()) + return m_channelGroups && + epgTag && + IsCurrentlyParentalLocked(m_channelGroups->GetByUniqueID(epgTag->UniqueChannelID(), epgTag->ClientID()), + epgTag->IsParentalLocked()); +} + +bool CPVRManager::IsParentalLocked(const std::shared_ptr<CPVRChannel>& channel) const +{ + return channel && + IsCurrentlyParentalLocked(channel, channel->IsLocked()); +} + +bool CPVRManager::IsCurrentlyParentalLocked(const std::shared_ptr<CPVRChannel>& channel, bool bGenerallyLocked) const +{ + bool bReturn = false; + + if (!channel || !bGenerallyLocked) return bReturn; - CPVRChannelPtr currentChannel(GetPlayingChannel()); - if (// different channel + const std::shared_ptr<CPVRChannel> currentChannel = GetPlayingChannel(); + + if (// if channel in question is currently playing it must be currently unlocked. (!currentChannel || channel != currentChannel) && // parental control enabled - m_settings.GetBoolValue(CSettings::SETTING_PVRPARENTAL_ENABLED) && - // channel is locked - channel && channel->IsLocked()) + m_settings.GetBoolValue(CSettings::SETTING_PVRPARENTAL_ENABLED)) { float parentalDurationMs = m_settings.GetIntValue(CSettings::SETTING_PVRPARENTAL_DURATION) * 1000.0f; bReturn = m_parentalTimer && diff --git a/xbmc/pvr/PVRManager.h b/xbmc/pvr/PVRManager.h index bbb9673b1f..76a2290b1e 100644 --- a/xbmc/pvr/PVRManager.h +++ b/xbmc/pvr/PVRManager.h @@ -240,6 +240,14 @@ namespace PVR } /*! + * @brief Check whether playing channel matches given uids. + * @param iClientID The client id. + * @param iUniqueChannelID The channel uid. + * @return True on match, false if there is no match or no channel is playing. + */ + bool MatchPlayingChannel(int iClientID, int iUniqueChannelID) const; + + /*! * @brief Return the channel that is currently playing. * @return The channel or NULL if none is playing. */ @@ -403,10 +411,17 @@ namespace PVR /*! * @brief Check if parental lock is overridden at the given moment. - * @param channel The channel to open. + * @param channel The channel to check. + * @return True if parental lock is overridden, false otherwise. + */ + bool IsParentalLocked(const std::shared_ptr<CPVRChannel>& channel) const; + + /*! + * @brief Check if parental lock is overridden at the given moment. + * @param epgTag The epg tag to check. * @return True if parental lock is overridden, false otherwise. */ - bool IsParentalLocked(const CPVRChannelPtr &channel); + bool IsParentalLocked(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const; /*! * @brief Restart the parental timer. @@ -509,6 +524,8 @@ namespace PVR */ void SetState(ManagerState state); + bool IsCurrentlyParentalLocked(const std::shared_ptr<CPVRChannel>& channel, bool bGenerallyLocked) const; + /** @name containers */ //@{ CPVRChannelGroupsContainerPtr m_channelGroups; /*!< pointer to the channel groups container */ diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp index 36dd2717d8..54f79330e4 100644 --- a/xbmc/pvr/addons/PVRClients.cpp +++ b/xbmc/pvr/addons/PVRClients.cpp @@ -233,7 +233,8 @@ void CPVRClients::OnAddonEvent(const AddonEvent& event) typeid(event) == typeid(AddonEvents::ReInstalled)) { // update addons - CJobManager::GetInstance().AddJob(new CPVRUpdateAddonsJob(event.id), nullptr); + if (CServiceBroker::GetAddonMgr().HasType(event.id, ADDON_PVRDLL)) + CJobManager::GetInstance().AddJob(new CPVRUpdateAddonsJob(event.id), nullptr); } } diff --git a/xbmc/pvr/channels/PVRChannel.cpp b/xbmc/pvr/channels/PVRChannel.cpp index 173100c0ea..3ddc457369 100644 --- a/xbmc/pvr/channels/PVRChannel.cpp +++ b/xbmc/pvr/channels/PVRChannel.cpp @@ -19,9 +19,8 @@ #include "pvr/PVRDatabase.h" #include "pvr/PVRManager.h" -#include "pvr/channels/PVRChannelGroupInternal.h" +#include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgContainer.h" -#include "pvr/timers/PVRTimers.h" using namespace PVR; @@ -49,7 +48,6 @@ CPVRChannel::CPVRChannel(bool bRadio /* = false */) m_bChanged = false; m_iEpgId = -1; - m_bEPGCreated = false; m_bEPGEnabled = true; m_strEPGScraper = "client"; @@ -79,7 +77,6 @@ CPVRChannel::CPVRChannel(const PVR_CHANNEL &channel, unsigned int iClientId) m_bEPGEnabled = !channel.bIsHidden; m_strEPGScraper = "client"; m_iEpgId = -1; - m_bEPGCreated = false; m_bChanged = false; if (m_strChannelName.empty()) @@ -115,7 +112,7 @@ void CPVRChannel::Serialize(CVariant& value) const if (epg) epg->Serialize(value["broadcastnext"]); - value["isrecording"] = IsRecording(); + value["isrecording"] = false; // compat } /********** XBMC related channel methods **********/ @@ -130,11 +127,10 @@ bool CPVRChannel::Delete(void) const CPVREpgPtr epg = GetEPG(); if (epg) { - epg->SetChannel(CPVRChannelPtr()); CServiceBroker::GetPVRManager().EpgContainer().DeleteEpg(epg, true); CSingleLock lock(m_critSection); - m_bEPGCreated = false; + m_epg.reset(); } bReturn = database->Delete(*this); @@ -143,28 +139,26 @@ bool CPVRChannel::Delete(void) CPVREpgPtr CPVRChannel::GetEPG(void) const { - int iEpgId(-1); - { - CSingleLock lock(m_critSection); - if (!m_bIsHidden && m_bEPGEnabled && m_iEpgId > 0) - iEpgId = m_iEpgId; - } + CSingleLock lock(m_critSection); + if (!m_bIsHidden && m_bEPGEnabled) + return m_epg; - return iEpgId > 0 ? CServiceBroker::GetPVRManager().EpgContainer().GetById(iEpgId) : CPVREpgPtr(); + return {}; } -bool CPVRChannel::CreateEPG(bool bForce) +bool CPVRChannel::CreateEPG() { CSingleLock lock(m_critSection); - if (!m_bEPGCreated || bForce) + if (!m_epg) { - const CPVREpgPtr epg = CServiceBroker::GetPVRManager().EpgContainer().CreateChannelEpg(shared_from_this()); - if (epg) + m_epg = CServiceBroker::GetPVRManager().EpgContainer().CreateChannelEpg(m_iEpgId, + m_strEPGScraper, + std::make_shared<CPVREpgChannelData>(*this)); + if (m_epg) { - m_bEPGCreated = true; - if (epg->EpgID() != m_iEpgId) + if (m_epg->EpgID() != m_iEpgId) { - m_iEpgId = epg->EpgID(); + m_iEpgId = m_epg->EpgID(); m_bChanged = true; } return true; @@ -230,6 +224,11 @@ bool CPVRChannel::SetChannelID(int iChannelId) if (m_iChannelId != iChannelId) { m_iChannelId = iChannelId; + + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + epg->GetChannelData()->SetChannelId(m_iChannelId); + SetChanged(); m_bChanged = true; return true; @@ -252,6 +251,14 @@ bool CPVRChannel::SetHidden(bool bIsHidden) { m_bIsHidden = bIsHidden; m_bEPGEnabled = !bIsHidden; + + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + { + epg->GetChannelData()->SetHidden(m_bIsHidden); + epg->GetChannelData()->SetEPGEnabled(m_bEPGEnabled); + } + SetChanged(); m_bChanged = true; return true; @@ -267,6 +274,11 @@ bool CPVRChannel::SetLocked(bool bIsLocked) if (m_bIsLocked != bIsLocked) { m_bIsLocked = bIsLocked; + + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + epg->GetChannelData()->SetLocked(m_bIsLocked); + SetChanged(); m_bChanged = true; return true; @@ -287,30 +299,17 @@ void CPVRChannel::SetRadioRDSInfoTag(const std::shared_ptr<CPVRRadioRDSInfoTag>& m_rdsTag = tag; } -bool CPVRChannel::IsRecording(void) const -{ - return CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*this); -} - -CPVRRecordingPtr CPVRChannel::GetRecording(void) const -{ - const CPVREpgInfoTagPtr epgTag = GetEPGNow(); - return (epgTag && epgTag->HasRecording()) ? epgTag->Recording() : CPVRRecordingPtr(); -} - -bool CPVRChannel::HasRecording(void) const -{ - const CPVREpgInfoTagPtr epgTag = GetEPGNow(); - return epgTag && epgTag->HasRecording(); -} - bool CPVRChannel::SetIconPath(const std::string &strIconPath, bool bIsUserSetIcon /* = false */) { CSingleLock lock(m_critSection); - if (m_strIconPath != strIconPath) { m_strIconPath = StringUtils::Format("%s", strIconPath.c_str()); + + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + epg->GetChannelData()->SetIconPath(m_strIconPath); + SetChanged(); m_bChanged = true; m_bIsUserSetIcon = bIsUserSetIcon && !m_strIconPath.empty(); @@ -341,8 +340,13 @@ bool CPVRChannel::SetChannelName(const std::string &strChannelName, bool bIsUser m_strChannelName = ClientChannelName(); } + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + epg->GetChannelData()->SetChannelName(m_strChannelName); + SetChanged(); m_bChanged = true; + return true; } @@ -353,7 +357,14 @@ bool CPVRChannel::SetLastWatched(time_t iLastWatched) { { CSingleLock lock(m_critSection); - m_iLastWatched = iLastWatched; + if (m_iLastWatched != iLastWatched) + { + m_iLastWatched = iLastWatched; + + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + epg->GetChannelData()->SetLastWatched(iLastWatched); + } } const CPVRDatabasePtr database = CServiceBroker::GetPVRManager().GetTVDatabase(); @@ -386,17 +397,14 @@ bool CPVRChannel::SetClientID(int iClientId) return false; } -void CPVRChannel::UpdatePath(CPVRChannelGroupInternal* group) +void CPVRChannel::UpdatePath(const std::string& groupPath) { - if (!group) - return; - const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(m_iClientId); if (client) { CSingleLock lock(m_critSection); const std::string strFileNameAndPath = StringUtils::Format("%s%s_%d.pvr", - group->GetPath(), + groupPath, client->ID().c_str(), m_iUniqueId); if (m_strFileNameAndPath != strFileNameAndPath) @@ -519,16 +527,16 @@ void CPVRChannel::UpdateEncryptionName(void) /********** EPG methods **********/ -int CPVRChannel::GetEPG(CFileItemList &results) const +std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVRChannel::GetEpgTags() const { const CPVREpgPtr epg = GetEPG(); if (!epg) { CLog::LogFC(LOGDEBUG, LOGPVR, "Cannot get EPG for channel '%s'", m_strChannelName.c_str()); - return -1; + return {}; } - return epg->Get(results); + return epg->GetTags(); } bool CPVRChannel::ClearEPG() const @@ -577,11 +585,16 @@ bool CPVRChannel::SetEPGEnabled(bool bEPGEnabled) if (m_bEPGEnabled != bEPGEnabled) { m_bEPGEnabled = bEPGEnabled; + + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + epg->GetChannelData()->SetEPGEnabled(m_bEPGEnabled); + SetChanged(); m_bChanged = true; /* clear the previous EPG entries if needed */ - if (!m_bEPGEnabled && m_bEPGCreated) + if (!m_bEPGEnabled && m_epg) ClearEPG(); return true; @@ -603,7 +616,7 @@ bool CPVRChannel::SetEPGScraper(const std::string &strScraper) m_bChanged = true; /* clear the previous EPG entries if needed */ - if (bCleanEPG && m_bEPGEnabled && m_bEPGCreated) + if (bCleanEPG && m_bEPGEnabled && m_epg) ClearEPG(); return true; @@ -615,7 +628,14 @@ bool CPVRChannel::SetEPGScraper(const std::string &strScraper) void CPVRChannel::SetChannelNumber(const CPVRChannelNumber& channelNumber) { CSingleLock lock(m_critSection); - m_channelNumber = channelNumber; + if (m_channelNumber != channelNumber) + { + m_channelNumber = channelNumber; + + const std::shared_ptr<CPVREpg> epg = GetEPG(); + if (epg) + epg->GetChannelData()->SetSortableChannelNumber(m_channelNumber.SortableChannelNumber()); + } } void CPVRChannel::ToSortable(SortItem& sortable, Field field) const @@ -765,10 +785,10 @@ int CPVRChannel::EpgID(void) const void CPVRChannel::SetEpgID(int iEpgId) { CSingleLock lock(m_critSection); - if (m_iEpgId != iEpgId) { m_iEpgId = iEpgId; + m_epg.reset(); SetChanged(); m_bChanged = true; } diff --git a/xbmc/pvr/channels/PVRChannel.h b/xbmc/pvr/channels/PVRChannel.h index 243ef7a3af..cf040c3e58 100644 --- a/xbmc/pvr/channels/PVRChannel.h +++ b/xbmc/pvr/channels/PVRChannel.h @@ -11,6 +11,7 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h" #include "threads/CriticalSection.h" @@ -22,18 +23,16 @@ #include "pvr/PVRTypes.h" class CVariant; -class CFileItemList; namespace PVR { - class CPVRChannelGroupInternal; + class CPVREpg; class CPVRRadioRDSInfoTag; /** PVR Channel class */ class CPVRChannel : public Observable, public ISerializable, - public ISortable, - public std::enable_shared_from_this<CPVRChannel> + public ISortable { friend class CPVRDatabase; @@ -140,16 +139,6 @@ namespace PVR bool SetLocked(bool bIsLocked); /*! - * @return True if a recording is currently running on this channel. False if not. - */ - bool IsRecording(void) const; - - /*! - * @return If recording, gets the recording if the add-on provides the epg id in recordings - */ - CPVRRecordingPtr GetRecording(void) const; - - /*! * @brief Obtain the Radio RDS data for this channel, if available. * @return The Radio RDS data or nullptr. */ @@ -162,11 +151,6 @@ namespace PVR void SetRadioRDSInfoTag(const std::shared_ptr<CPVRRadioRDSInfoTag>& tag); /*! - * @return True if this channel has a corresponding recording, false otherwise - */ - bool HasRecording(void) const; - - /*! * @return The path to the icon for this channel. */ std::string IconPath(void) const; @@ -292,10 +276,10 @@ namespace PVR void ToSortable(SortItem& sortable, Field field) const override; /*! - * @brief Update the path this channel got added to the internal group - * @param group The internal group that contains this channel + * @brief Update the channel path + * @param groupPath The new path of the group this channel belongs to */ - void UpdatePath(CPVRChannelGroupInternal* group); + void UpdatePath(const std::string& groupPath); /*! * @return Storage id for this channel in CPVRChannelGroup @@ -345,10 +329,9 @@ namespace PVR /*! * @brief Create the EPG for this channel, if it does not yet exist - * @param bForce to create a new EPG, even if it already exists. * @return true if a new epg was created, false otherwise. */ - bool CreateEPG(bool bForce); + bool CreateEPG(); /*! * @brief Get the EPG table for this channel. @@ -357,11 +340,10 @@ namespace PVR CPVREpgPtr GetEPG(void) const; /*! - * @brief Get the EPG table for this channel. - * @param results The file list to store the results in. - * @return The number of tables that were added. + * @brief Get the EPG tags for this channel. + * @return The tags. */ - int GetEPG(CFileItemList &results) const; + std::vector<std::shared_ptr<CPVREpgInfoTag>> GetEpgTags() const; /*! * @brief Clear the EPG for this channel. @@ -464,9 +446,9 @@ namespace PVR */ //@{ int m_iEpgId; /*!< the id of the EPG for this channel */ - bool m_bEPGCreated; /*!< true if an EPG has been created for this channel */ bool m_bEPGEnabled; /*!< don't use an EPG for this channel if set to false */ std::string m_strEPGScraper; /*!< the name of the scraper to be used for this channel */ + std::shared_ptr<CPVREpg> m_epg; //@} /*! @name Client related channel data diff --git a/xbmc/pvr/channels/PVRChannelGroup.cpp b/xbmc/pvr/channels/PVRChannelGroup.cpp index 4293a18cf5..ac140fcae2 100644 --- a/xbmc/pvr/channels/PVRChannelGroup.cpp +++ b/xbmc/pvr/channels/PVRChannelGroup.cpp @@ -29,7 +29,7 @@ #include "pvr/PVRGUIProgressHandler.h" #include "pvr/PVRManager.h" #include "pvr/addons/PVRClients.h" -#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgContainer.h" using namespace PVR; @@ -39,18 +39,24 @@ CPVRChannelGroup::CPVRChannelGroup(void) OnInit(); } -CPVRChannelGroup::CPVRChannelGroup(bool bRadio, unsigned int iGroupId, const std::string &strGroupName) : +CPVRChannelGroup::CPVRChannelGroup(bool bRadio, + unsigned int iGroupId, + const std::string& strGroupName, + const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup) : m_bRadio(bRadio), m_iGroupId(iGroupId), - m_strGroupName(strGroupName) + m_strGroupName(strGroupName), + m_allChannelsGroup(allChannelsGroup) { OnInit(); } -CPVRChannelGroup::CPVRChannelGroup(const PVR_CHANNEL_GROUP &group) : +CPVRChannelGroup::CPVRChannelGroup(const PVR_CHANNEL_GROUP& group, + const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup) : m_bRadio(group.bIsRadio), m_strGroupName(group.strGroupName), - m_iPosition(group.iPosition) + m_iPosition(group.iPosition), + m_allChannelsGroup(allChannelsGroup) { OnInit(); } @@ -73,6 +79,8 @@ CPVRChannelGroup::CPVRChannelGroup(const CPVRChannelGroup &group) : m_iPosition = group.m_iPosition; m_failedClientsForChannels = group.m_failedClientsForChannels; m_failedClientsForChannelGroupMembers = group.m_failedClientsForChannelGroupMembers; + m_allChannelsGroup = group.m_allChannelsGroup; + OnInit(); } @@ -106,7 +114,7 @@ void CPVRChannelGroup::OnInit(void) }); } -bool CPVRChannelGroup::Load(void) +bool CPVRChannelGroup::Load(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) { /* make sure this container is empty before loading */ Unload(); @@ -118,7 +126,7 @@ bool CPVRChannelGroup::Load(void) int iChannelCount = m_iGroupId > 0 ? LoadFromDb() : 0; CLog::LogFC(LOGDEBUG, LOGPVR, "%d channels loaded from the database for group '%s'", iChannelCount, m_strGroupName.c_str()); - if (!Update()) + if (!Update(channelsToRemove)) { CLog::LogF(LOGERROR, "Failed to update channels for group '%s', m_strGroupName.c_str()"); return false; @@ -146,17 +154,17 @@ void CPVRChannelGroup::Unload(void) m_failedClientsForChannelGroupMembers.clear(); } -bool CPVRChannelGroup::Update(void) +bool CPVRChannelGroup::Update(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) { if (GroupType() == PVR_GROUP_TYPE_USER_DEFINED || !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PVRMANAGER_SYNCCHANNELGROUPS)) return true; - CPVRChannelGroup PVRChannels_tmp(m_bRadio, m_iGroupId, m_strGroupName); + CPVRChannelGroup PVRChannels_tmp(m_bRadio, m_iGroupId, m_strGroupName, m_allChannelsGroup); PVRChannels_tmp.SetPreventSortAndRenumber(); PVRChannels_tmp.LoadFromClients(); m_failedClientsForChannelGroupMembers = PVRChannels_tmp.m_failedClientsForChannelGroupMembers; - return UpdateGroupEntries(PVRChannels_tmp); + return UpdateGroupEntries(PVRChannels_tmp, channelsToRemove); } std::string CPVRChannelGroup::GetPath() const @@ -522,18 +530,6 @@ void CPVRChannelGroup::GetChannelNumbers(std::vector<std::string>& channelNumber channelNumbers.emplace_back(member.channelNumber.FormattedChannelNumber()); } -CPVRChannelGroupPtr CPVRChannelGroup::GetNextGroup(void) const -{ - return CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bRadio)->GetNextGroup(*this); -} - -CPVRChannelGroupPtr CPVRChannelGroup::GetPreviousGroup(void) const -{ - return CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bRadio)->GetPreviousGroup(*this); -} - -/********** private methods **********/ - int CPVRChannelGroup::LoadFromDb(bool bCompress /* = false */) { const CPVRDatabasePtr database(CServiceBroker::GetPVRManager().GetTVDatabase()); @@ -542,11 +538,7 @@ int CPVRChannelGroup::LoadFromDb(bool bCompress /* = false */) int iChannelCount = Size(); - const CPVRChannelGroupPtr allGroup = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(IsRadio()); - if (!allGroup) - return -1; - - database->Get(*this, *allGroup); + database->Get(*this, *m_allChannelsGroup); return Size() - iChannelCount; } @@ -560,14 +552,13 @@ bool CPVRChannelGroup::LoadFromClients(void) bool CPVRChannelGroup::AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers) { bool bReturn(false); - const CPVRChannelGroupPtr groupAll(CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bRadio)); /* go through the channel list and check for new channels. channels will only by updated in CPVRChannelGroupInternal to prevent dupe updates */ for (PVR_CHANNEL_GROUP_MEMBERS::const_iterator it = channels.m_members.begin(); it != channels.m_members.end(); ++it) { /* check whether this channel is known in the internal group */ - const PVRChannelGroupMember& existingChannel(groupAll->GetByUniqueID(it->first)); + const PVRChannelGroupMember& existingChannel(m_allChannelsGroup->GetByUniqueID(it->first)); if (!existingChannel.channel) continue; @@ -633,7 +624,7 @@ std::vector<CPVRChannelPtr> CPVRChannelGroup::RemoveDeletedChannels(const CPVRCh return removedChannels; } -bool CPVRChannelGroup::UpdateGroupEntries(const CPVRChannelGroup &channels) +bool CPVRChannelGroup::UpdateGroupEntries(const CPVRChannelGroup& channels, std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) { bool bReturn(false); bool bChanged(false); @@ -644,7 +635,8 @@ bool CPVRChannelGroup::UpdateGroupEntries(const CPVRChannelGroup &channels) bool bUseBackendChannelNumbers(m_members.empty() || m_bUsingBackendChannelOrder); SetPreventSortAndRenumber(true); - bRemoved = !RemoveDeletedChannels(channels).empty(); + channelsToRemove = RemoveDeletedChannels(channels); + bRemoved = !channelsToRemove.empty(); bChanged = AddAndUpdateChannels(channels, bUseBackendChannelNumbers) || bRemoved; SetPreventSortAndRenumber(false); @@ -703,14 +695,13 @@ bool CPVRChannelGroup::RemoveFromGroup(const CPVRChannelPtr &channel) bool CPVRChannelGroup::AddToGroup(const CPVRChannelPtr &channel, const CPVRChannelNumber &channelNumber, bool bUseBackendChannelNumbers) { bool bReturn(false); - const CPVRChannelGroupPtr groupAll(CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bRadio)); CSingleLock lock(m_critSection); if (!CPVRChannelGroup::IsGroupMember(channel)) { const PVRChannelGroupMember& realChannel(IsInternalGroup() ? GetByUniqueID(channel->StorageId()) : - groupAll->GetByUniqueID(channel->StorageId())); + m_allChannelsGroup->GetByUniqueID(channel->StorageId())); if (realChannel.channel) { @@ -814,9 +805,6 @@ bool CPVRChannelGroup::Renumber(void) unsigned int iChannelNumber(0); bool bUseBackendChannelNumbers(CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERS) && CServiceBroker::GetPVRManager().Clients()->EnabledClientAmount() == 1); - CPVRChannelGroupPtr groupAll; - if (!bUseBackendChannelNumbers && !IsInternalGroup()) - groupAll = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bRadio); CSingleLock lock(m_critSection); @@ -836,7 +824,7 @@ bool CPVRChannelGroup::Renumber(void) if (IsInternalGroup()) currentChannelNumber = CPVRChannelNumber(++iChannelNumber, 0); else - currentChannelNumber = groupAll->GetChannelNumber((*it).channel); + currentChannelNumber = m_allChannelsGroup->GetChannelNumber((*it).channel); } if ((*it).channelNumber != currentChannelNumber) @@ -925,9 +913,10 @@ void CPVRChannelGroup::OnSettingChanged(std::shared_ptr<const CSetting> setting) } } -int CPVRChannelGroup::GetEPGAll(CFileItemList &results, bool bIncludeChannelsWithoutEPG /* = false */) const +std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVRChannelGroup::GetEPGAll(bool bIncludeChannelsWithoutEPG /* = false */) const { - int iInitialSize = results.Size(); + std::vector<std::shared_ptr<CPVREpgInfoTag>> tags; + CPVREpgInfoTagPtr epgTag; CPVRChannelPtr channel; CSingleLock lock(m_critSection); @@ -937,26 +926,31 @@ int CPVRChannelGroup::GetEPGAll(CFileItemList &results, bool bIncludeChannelsWit channel = (*it).channel; if (!channel->IsHidden()) { - int iAdded = 0; + bool bEmpty = false; CPVREpgPtr epg = channel->GetEPG(); if (epg) { - // XXX channel pointers aren't set in some occasions. this works around the issue, but is not very nice - epg->SetChannel(channel); - iAdded = epg->Get(results); + const std::vector<std::shared_ptr<CPVREpgInfoTag>> epgTags = epg->GetTags(); + bEmpty = epgTags.empty(); + if (!bEmpty) + tags.insert(tags.end(), epgTags.begin(), epgTags.end()); } - if (bIncludeChannelsWithoutEPG && iAdded == 0) + if (bIncludeChannelsWithoutEPG && bEmpty) { // Add dummy EPG tag associated with this channel - epgTag = std::make_shared<CPVREpgInfoTag>(channel); - results.Add(std::make_shared<CFileItem>(epgTag)); + if (epg) + epgTag = std::make_shared<CPVREpgInfoTag>(epg->GetChannelData(), epg->EpgID()); + else + epgTag = std::make_shared<CPVREpgInfoTag>(std::make_shared<CPVREpgChannelData>(*channel), -1); + + tags.emplace_back(epgTag); } } } - return results.Size() - iInitialSize; + return tags; } CDateTime CPVRChannelGroup::GetEPGDate(EpgDateType epgDateType) const diff --git a/xbmc/pvr/channels/PVRChannelGroup.h b/xbmc/pvr/channels/PVRChannelGroup.h index de3e5ce5a6..eca9feb89d 100644 --- a/xbmc/pvr/channels/PVRChannelGroup.h +++ b/xbmc/pvr/channels/PVRChannelGroup.h @@ -22,6 +22,8 @@ class CFileItem; typedef std::shared_ptr<CFileItem> CFileItemPtr; +class CFileItemList; + namespace PVR { #define PVR_GROUP_TYPE_DEFAULT 0 @@ -64,14 +66,16 @@ namespace PVR * @param bRadio True if this group holds radio channels. * @param iGroupId The database ID of this group. * @param strGroupName The name of this group. + * @param allChannelsGroup The channel group containing all TV or radio channels. */ - CPVRChannelGroup(bool bRadio, unsigned int iGroupId, const std::string &strGroupName); + CPVRChannelGroup(bool bRadio, unsigned int iGroupId, const std::string& strGroupName, const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup); /*! * @brief Create a new channel group instance from a channel group provided by an add-on. * @param group The channel group provided by the add-on. + * @param allChannelsGroup The channel group containing all TV or radio channels. */ - explicit CPVRChannelGroup(const PVR_CHANNEL_GROUP &group); + CPVRChannelGroup(const PVR_CHANNEL_GROUP& group, const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup); /*! * @brief Copy constructor @@ -91,9 +95,10 @@ namespace PVR /*! * @brief Load the channels from the database. + * @param channelsToRemove Returns the channels to be removed from all groups, if any * @return True when loaded successfully, false otherwise. */ - virtual bool Load(void); + virtual bool Load(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove); /*! * @return The amount of group members @@ -102,8 +107,9 @@ namespace PVR /*! * @brief Refresh the channel list from the clients. + * @param channelsToRemove Returns the channels to be removed from all groups, if any */ - virtual bool Update(void); + virtual bool Update(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove); /*! * @brief Get the path of this group. @@ -336,16 +342,6 @@ namespace PVR void GetChannelNumbers(std::vector<std::string>& channelNumbers) const; /*! - * @return The next channel group. - */ - CPVRChannelGroupPtr GetNextGroup(void) const; - - /*! - * @return The previous channel group. - */ - CPVRChannelGroupPtr GetPreviousGroup(void) const; - - /*! * @brief The amount of hidden channels in this container. * @return The amount of hidden channels in this container. */ @@ -380,12 +376,11 @@ namespace PVR virtual bool CreateChannelEpgs(bool bForce = false); /*! - * @brief Get all EPG tables. - * @param results The fileitem list to store the results in. + * @brief Get all EPG tags for all channels in this group. * @param bIncludeChannelsWithoutEPG, for channels without EPG data, put an empty EPG tag associated with the channel into results - * @return The amount of entries that were added. + * @return The tags. */ - int GetEPGAll(CFileItemList &results, bool bIncludeChannelsWithoutEPG = false) const; + std::vector<std::shared_ptr<CPVREpgInfoTag>> GetEPGAll(bool bIncludeChannelsWithoutEPG = false) const; /*! * @brief Get the start time of the first entry. @@ -457,9 +452,10 @@ namespace PVR * Only the new channels will be present in the passed list after this call. * * @param channels The channels to use to update this list. + * @param channelsToRemove Returns the channels to be removed from all groups, if any * @return True if everything went well, false otherwise. */ - virtual bool UpdateGroupEntries(const CPVRChannelGroup &channels); + virtual bool UpdateGroupEntries(const CPVRChannelGroup& channels, std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove); /*! * @brief Add new channels to this group; updtae data. @@ -522,5 +518,7 @@ namespace PVR private: CDateTime GetEPGDate(EpgDateType epgDateType) const; + + std::shared_ptr<CPVRChannelGroup> m_allChannelsGroup; }; } diff --git a/xbmc/pvr/channels/PVRChannelGroupInternal.cpp b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp index 4f95705f36..015fa291ba 100644 --- a/xbmc/pvr/channels/PVRChannelGroupInternal.cpp +++ b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp @@ -22,9 +22,7 @@ #include "pvr/PVRDatabase.h" #include "pvr/PVRManager.h" #include "pvr/addons/PVRClients.h" -#include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/EpgContainer.h" -#include "pvr/timers/PVRTimers.h" using namespace PVR; using namespace KODI::MESSAGING; @@ -49,9 +47,9 @@ CPVRChannelGroupInternal::~CPVRChannelGroupInternal(void) CServiceBroker::GetPVRManager().Events().Unsubscribe(this); } -bool CPVRChannelGroupInternal::Load(void) +bool CPVRChannelGroupInternal::Load(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) { - if (CPVRChannelGroup::Load()) + if (CPVRChannelGroup::Load(channelsToRemove)) { UpdateChannelPaths(); CServiceBroker::GetPVRManager().Events().Subscribe(this, &CPVRChannelGroupInternal::OnPVRManagerEvent); @@ -84,7 +82,7 @@ void CPVRChannelGroupInternal::UpdateChannelPaths(void) if (it->second.channel->IsHidden()) ++m_iHiddenChannels; else - it->second.channel->UpdatePath(this); + it->second.channel->UpdatePath(GetPath()); } } @@ -104,7 +102,7 @@ CPVRChannelPtr CPVRChannelGroupInternal::UpdateFromClient(const CPVRChannelPtr & iChannelNumber = static_cast<int>(m_sortedMembers.size()) + 1; PVRChannelGroupMember newMember(channel, CPVRChannelNumber(iChannelNumber, channelNumber.GetSubChannelNumber()), 0); - channel->UpdatePath(this); + channel->UpdatePath(GetPath()); m_sortedMembers.push_back(newMember); m_members.insert(std::make_pair(channel->StorageId(), newMember)); m_bChanged = true; @@ -114,13 +112,13 @@ CPVRChannelPtr CPVRChannelGroupInternal::UpdateFromClient(const CPVRChannelPtr & return channel; } -bool CPVRChannelGroupInternal::Update(void) +bool CPVRChannelGroupInternal::Update(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) { CPVRChannelGroupInternal PVRChannels_tmp(m_bRadio); PVRChannels_tmp.SetPreventSortAndRenumber(); PVRChannels_tmp.LoadFromClients(); m_failedClientsForChannels = PVRChannels_tmp.m_failedClientsForChannels; - return UpdateGroupEntries(PVRChannels_tmp); + return UpdateGroupEntries(PVRChannels_tmp, channelsToRemove); } bool CPVRChannelGroupInternal::AddToGroup(const CPVRChannelPtr &channel, const CPVRChannelNumber &channelNumber, bool bUseBackendChannelNumbers) @@ -270,12 +268,8 @@ std::vector<CPVRChannelPtr> CPVRChannelGroupInternal::RemoveDeletedChannels(cons if (!removedChannels.empty()) { - CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bRadio); for (const auto& channel : removedChannels) { - /* remove this channel from all non-system groups */ - groups->RemoveFromAllGroups(channel); - /* do we have valid data from channel's client? */ if (!IsMissingChannelsFromClient(channel->ClientID())) { @@ -288,17 +282,16 @@ std::vector<CPVRChannelPtr> CPVRChannelGroupInternal::RemoveDeletedChannels(cons return removedChannels; } -bool CPVRChannelGroupInternal::UpdateGroupEntries(const CPVRChannelGroup &channels) +bool CPVRChannelGroupInternal::UpdateGroupEntries(const CPVRChannelGroup& channels, std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) { bool bReturn(false); - if (CPVRChannelGroup::UpdateGroupEntries(channels)) + if (CPVRChannelGroup::UpdateGroupEntries(channels, channelsToRemove)) { /* try to find channel icons */ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bPVRChannelIconsAutoScan) SearchAndSetChannelIcons(); - CServiceBroker::GetPVRManager().Timers()->UpdateChannels(); Persist(); bReturn = true; @@ -307,10 +300,10 @@ bool CPVRChannelGroupInternal::UpdateGroupEntries(const CPVRChannelGroup &channe return bReturn; } -void CPVRChannelGroupInternal::CreateChannelEpg(const CPVRChannelPtr &channel, bool bForce /* = false */) +void CPVRChannelGroupInternal::CreateChannelEpg(const std::shared_ptr<CPVRChannel>& channel) { if (channel) - channel->CreateEPG(bForce); + channel->CreateEPG(); } bool CPVRChannelGroupInternal::CreateChannelEpgs(bool bForce /* = false */) diff --git a/xbmc/pvr/channels/PVRChannelGroupInternal.h b/xbmc/pvr/channels/PVRChannelGroupInternal.h index bcf5adf0db..7dc4a87d80 100644 --- a/xbmc/pvr/channels/PVRChannelGroupInternal.h +++ b/xbmc/pvr/channels/PVRChannelGroupInternal.h @@ -95,9 +95,10 @@ namespace PVR * Only the new channels will be present in the passed list after this call. * * @param channels The channels to use to update this list. + * @param channelsToRemove Returns the channels to be removed from all groups, if any * @return True if everything went well, false otherwise. */ - bool UpdateGroupEntries(const CPVRChannelGroup &channels) override; + bool UpdateGroupEntries(const CPVRChannelGroup& channels, std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) override; /*! * @brief Add new channels to this group; updtae data. @@ -116,8 +117,9 @@ namespace PVR /*! * @brief Refresh the channel list from the clients. + * @param channelsToRemove Returns the channels to be removed from all groups, if any */ - bool Update(void) override; + bool Update(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) override; /*! * @brief Load the channels from the database. @@ -125,16 +127,17 @@ namespace PVR * Load the channels from the database. * If no channels are stored in the database, then the channels will be loaded from the clients. * + * @param channelsToRemove Returns the channels to be removed from all groups, if any * @return True when loaded successfully, false otherwise. */ - bool Load(void) override; + bool Load(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) override; /*! * @brief Update the vfs paths of all channels. */ void UpdateChannelPaths(void); - void CreateChannelEpg(const CPVRChannelPtr &channel, bool bForce = false); + void CreateChannelEpg(const std::shared_ptr<CPVRChannel>& channel); size_t m_iHiddenChannels; /*!< the amount of hidden channels in this container */ diff --git a/xbmc/pvr/channels/PVRChannelGroups.cpp b/xbmc/pvr/channels/PVRChannelGroups.cpp index bf4b1d9286..9218c3852b 100644 --- a/xbmc/pvr/channels/PVRChannelGroups.cpp +++ b/xbmc/pvr/channels/PVRChannelGroups.cpp @@ -76,7 +76,7 @@ bool CPVRChannelGroups::Update(const CPVRChannelGroup &group, bool bUpdateFromCl { // create a new group if none was found. Copy the properties immediately // so the group doesn't get flagged as "changed" further down. - updateGroup = CPVRChannelGroupPtr(new CPVRChannelGroup(group.IsRadio(), group.GroupID(), group.GroupName())); + updateGroup.reset(new CPVRChannelGroup(group.IsRadio(), group.GroupID(), group.GroupName(), GetGroupAll())); m_groups.push_back(updateGroup); } @@ -189,6 +189,15 @@ CPVRChannelGroupPtr CPVRChannelGroups::GetByName(const std::string &strName) con return empty; } +void CPVRChannelGroups::RemoveFromAllGroups(const std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove) +{ + for (const auto& channel : channelsToRemove) + { + // remove this channel from all non-system groups + RemoveFromAllGroups(channel); + } +} + void CPVRChannelGroups::RemoveFromAllGroups(const CPVRChannelPtr &channel) { CSingleLock lock(m_critSection); @@ -221,7 +230,11 @@ bool CPVRChannelGroups::Update(bool bChannelsOnly /* = false */) for (const auto &group : groups) { if (bUpdateAllGroups || group->IsInternalGroup()) - bReturn = group->Update() && bReturn; + { + std::vector<std::shared_ptr<CPVRChannel>> channelsToRemove; + bReturn = group->Update(channelsToRemove) && bReturn; + RemoveFromAllGroups(channelsToRemove); + } } // persist changes @@ -252,12 +265,15 @@ bool CPVRChannelGroups::LoadUserDefinedChannelGroups(void) // load only user defined groups, as internal group is already loaded if (!(*it)->IsInternalGroup()) { - if (!(*it)->Load()) + std::vector<std::shared_ptr<CPVRChannel>> channelsToRemove; + if (!(*it)->Load(channelsToRemove)) { CLog::LogFC(LOGDEBUG, LOGPVR, "Failed to load user defined channel group '%s'", (*it)->GroupName().c_str()); return false; } + RemoveFromAllGroups(channelsToRemove); + // remove empty groups when sync with backend is enabled if (bSyncWithBackends && (*it)->Size() == 0) emptyGroups.push_back(*it); @@ -296,12 +312,15 @@ bool CPVRChannelGroups::Load(void) CLog::LogFC(LOGDEBUG, LOGPVR, "%d %s groups fetched from the database", m_groups.size(), m_bRadio ? "radio" : "TV"); // load channels of internal group - if (!internalGroup->Load()) + std::vector<std::shared_ptr<CPVRChannel>> channelsToRemove; + if (!internalGroup->Load(channelsToRemove)) { CLog::LogF(LOGERROR, "Failed to load 'all channels' group"); return false; } + RemoveFromAllGroups(channelsToRemove); + // load the other groups from the database if (!LoadUserDefinedChannelGroups()) { @@ -528,7 +547,7 @@ bool CPVRChannelGroups::DeleteGroup(const CPVRChannelGroup &group) } if (playingGroup) - CServiceBroker::GetPVRManager().SetPlayingGroup(playingGroup); + SetSelectedGroup(playingGroup); if (group.GroupID() > 0) { @@ -546,7 +565,7 @@ bool CPVRChannelGroups::CreateChannelEpgs(void) CSingleLock lock(m_critSection); for (std::vector<CPVRChannelGroupPtr>::iterator it = m_groups.begin(); it != m_groups.end(); ++it) { - /* Only create EPGs for the internatl groups */ + /* Only create EPGs for the internal groups */ if ((*it)->IsInternalGroup()) bReturn = (*it)->CreateChannelEpgs(); } diff --git a/xbmc/pvr/channels/PVRChannelGroups.h b/xbmc/pvr/channels/PVRChannelGroups.h index edccd36d0b..61c853682f 100644 --- a/xbmc/pvr/channels/PVRChannelGroups.h +++ b/xbmc/pvr/channels/PVRChannelGroups.h @@ -179,12 +179,6 @@ namespace PVR bool CreateChannelEpgs(void); /*! - * @brief Remove a channel from all non-system groups. - * @param channel The channel to remove. - */ - void RemoveFromAllGroups(const CPVRChannelPtr &channel); - - /*! * @brief Persist all changes in channel groups. * @return True if everything was persisted, false otherwise. */ @@ -207,6 +201,18 @@ namespace PVR bool GetGroupsFromClients(void); void SortGroups(void); + /*! + * @brief Remove the given channels from all non-system groups. + * @param channel The channels to remove. + */ + void RemoveFromAllGroups(const std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove); + + /*! + * @brief Remove a channel from all non-system groups. + * @param channel The channel to remove. + */ + void RemoveFromAllGroups(const std::shared_ptr<CPVRChannel>& channel); + bool m_bRadio; /*!< true if this is a container for radio channels, false if it is for tv channels */ CPVRChannelGroupPtr m_selectedGroup; /*!< the group that's currently selected in the UI */ std::vector<CPVRChannelGroupPtr> m_groups; /*!< the groups in this container */ diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp index 2f879d28c8..fb47e02d2c 100644 --- a/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp +++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp @@ -15,6 +15,7 @@ #include "utils/log.h" #include "pvr/PVRManager.h" +#include "pvr/epg/EpgInfoTag.h" using namespace PVR; @@ -105,6 +106,15 @@ CPVRChannelPtr CPVRChannelGroupsContainer::GetChannelByEpgId(int iEpgId) const return channel; } +std::shared_ptr<CPVRChannel> CPVRChannelGroupsContainer::GetChannelForEpgTag(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const +{ + if (!epgTag) + return {}; + + const CPVRChannelGroups* groups = epgTag->IsRadio() ? m_groupsRadio : m_groupsTV; + return groups->GetGroupAll()->GetByUniqueID(epgTag->UniqueChannelID(), epgTag->ClientID()); +} + bool CPVRChannelGroupsContainer::GetGroupsDirectory(CFileItemList *results, bool bRadio) const { const CPVRChannelGroups *channelGroups = Get(bRadio); diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.h b/xbmc/pvr/channels/PVRChannelGroupsContainer.h index baa02ac145..5417e45611 100644 --- a/xbmc/pvr/channels/PVRChannelGroupsContainer.h +++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.h @@ -113,6 +113,13 @@ namespace PVR CPVRChannelPtr GetChannelByEpgId(int iEpgId) const; /*! + * @brief Get the channel for the given epg tag. + * @param epgTag The epg tag. + * @return The channel. + */ + std::shared_ptr<CPVRChannel> GetChannelForEpgTag(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const; + + /*! * @brief Get the groups list for a directory. * @param strBase The directory path. * @param results The file list to store the results in. diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp index c0adbce456..ffda657cd6 100644 --- a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp @@ -42,7 +42,12 @@ void CGUIDialogPVRChannelGuide::OnInitWindow() Init(); - m_channel->GetEPG(*m_vecItems); + const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags = m_channel->GetEpgTags(); + for (const auto& tag : tags) + { + m_vecItems->Add(std::make_shared<CFileItem>(tag)); + } + m_viewControl.SetItems(*m_vecItems); CGUIDialogPVRItemsViewBase::OnInitWindow(); diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp index 13805ce1d9..be73f42332 100644 --- a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp @@ -21,6 +21,7 @@ #include "pvr/PVRGUIActions.h" #include "pvr/PVRManager.h" #include "pvr/channels/PVRChannelGroup.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/EpgContainer.h" using namespace PVR; @@ -113,7 +114,10 @@ bool CGUIDialogPVRChannelsOSD::OnAction(const CAction &action) SaveControlStates(); // switch to next or previous group - const CPVRChannelGroupPtr nextGroup = action.GetID() == ACTION_NEXT_CHANNELGROUP ? m_group->GetNextGroup() : m_group->GetPreviousGroup(); + const CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_group->IsRadio()); + const std::shared_ptr<CPVRChannelGroup> nextGroup = action.GetID() == ACTION_NEXT_CHANNELGROUP + ? groups->GetNextGroup(*m_group) + : groups->GetPreviousGroup(*m_group); CServiceBroker::GetPVRManager().SetPlayingGroup(nextGroup); m_group = nextGroup; Init(); diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp index aa95a7d76c..e2ae28ed9d 100644 --- a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp @@ -25,7 +25,9 @@ #include "pvr/PVRManager.h" #include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/EpgInfoTag.h" +#include "pvr/recordings/PVRRecordings.h" #include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/timers/PVRTimers.h" #include "pvr/windows/GUIWindowPVRSearch.h" using namespace PVR; @@ -67,15 +69,7 @@ bool CGUIDialogPVRGuideInfo::OnClickButtonRecord(CGUIMessage &message) { bReturn = true; - if (!m_progItem || !m_progItem->HasChannel()) - { - /* invalid channel */ - HELPERS::ShowOKDialogText(CVariant{19033}, CVariant{19067}); - Close(); - return bReturn; - } - - const CPVRTimerInfoTagPtr timerTag(m_progItem->Timer()); + const std::shared_ptr<CPVRTimerInfoTag> timerTag = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(m_progItem); if (timerTag) { const CFileItemPtr item(new CFileItem(timerTag)); @@ -103,7 +97,7 @@ bool CGUIDialogPVRGuideInfo::OnClickButtonAddTimer(CGUIMessage &message) if (message.GetSenderId() == CONTROL_BTN_ADD_TIMER) { - if (m_progItem && !m_progItem->Timer()) + if (m_progItem && !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(m_progItem)) { const CFileItemPtr item(new CFileItem(m_progItem)); bReturn = CServiceBroker::GetPVRManager().GUIActions()->AddTimerRule(item, true); @@ -191,7 +185,7 @@ void CGUIDialogPVRGuideInfo::OnInitWindow() return; } - if (!m_progItem->HasRecording()) + if (!CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(m_progItem)) { /* not recording. hide the play recording button */ SET_CONTROL_HIDDEN(CONTROL_BTN_PLAY_RECORDING); @@ -200,20 +194,21 @@ void CGUIDialogPVRGuideInfo::OnInitWindow() bool bHideRecord(true); bool bHideAddTimer(true); - if (m_progItem->HasTimer()) + const std::shared_ptr<CPVRTimerInfoTag> timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(m_progItem); + if (timer) { - if (m_progItem->Timer()->IsRecording()) + if (timer->IsRecording()) { SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19059); /* Stop recording */ bHideRecord = false; } - else if (m_progItem->Timer()->HasTimerType() && !m_progItem->Timer()->GetTimerType()->IsReadOnly()) + else if (timer->HasTimerType() && !timer->GetTimerType()->IsReadOnly()) { SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19060); /* Delete timer */ bHideRecord = false; } } - else if (m_progItem->Channel() && m_progItem->IsRecordable()) + else if (m_progItem->IsRecordable()) { const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(m_progItem->ClientID()); if (client && client->GetClientCapabilities().SupportsTimers()) diff --git a/xbmc/pvr/epg/CMakeLists.txt b/xbmc/pvr/epg/CMakeLists.txt index bef9dc29f3..9b350ab18b 100644 --- a/xbmc/pvr/epg/CMakeLists.txt +++ b/xbmc/pvr/epg/CMakeLists.txt @@ -2,12 +2,14 @@ set(SOURCES EpgContainer.cpp Epg.cpp EpgDatabase.cpp EpgInfoTag.cpp - EpgSearchFilter.cpp) + EpgSearchFilter.cpp + EpgChannelData.cpp) set(HEADERS Epg.h EpgContainer.h EpgDatabase.h EpgInfoTag.h - EpgSearchFilter.h) + EpgSearchFilter.h + EpgChannelData.h) core_add_library(pvr_epg) diff --git a/xbmc/pvr/epg/Epg.cpp b/xbmc/pvr/epg/Epg.cpp index eee188f7f7..42ec788c20 100644 --- a/xbmc/pvr/epg/Epg.cpp +++ b/xbmc/pvr/epg/Epg.cpp @@ -12,35 +12,35 @@ #include "addons/PVRClient.h" #include "addons/kodi-addon-dev-kit/include/kodi/xbmc_epg_types.h" -#include "EpgContainer.h" -#include "EpgDatabase.h" #include "ServiceBroker.h" #include "guilib/LocalizeStrings.h" #include "settings/AdvancedSettings.h" +#include "settings/Settings.h" #include "settings/SettingsComponent.h" #include "threads/SingleLock.h" #include "utils/log.h" #include "pvr/PVRManager.h" -#include "pvr/recordings/PVRRecordings.h" -#include "pvr/timers/PVRTimers.h" +#include "pvr/epg/EpgChannelData.h" +#include "pvr/epg/EpgDatabase.h" using namespace PVR; -CPVREpg::CPVREpg(int iEpgID, const std::string &strName, const std::string &strScraperName, bool bLoadedFromDb) -: m_bChanged(!bLoadedFromDb), +CPVREpg::CPVREpg(int iEpgID, const std::string& strName, const std::string& strScraperName) +: m_bChanged(false), m_iEpgID(iEpgID), m_strName(strName), - m_strScraperName(strScraperName) + m_strScraperName(strScraperName), + m_channelData(new CPVREpgChannelData) { } -CPVREpg::CPVREpg(const CPVRChannelPtr &channel) +CPVREpg::CPVREpg(int iEpgID, const std::string& strName, const std::string& strScraperName, const std::shared_ptr<CPVREpgChannelData>& channelData) : m_bChanged(true), - m_iEpgID(channel->EpgID()), - m_strName(channel->ChannelName()), - m_strScraperName(channel->EPGScraper()), - m_pvrChannel(channel) + m_iEpgID(iEpgID), + m_strName(strName), + m_strScraperName(strScraperName), + m_channelData(channelData) { } @@ -56,7 +56,8 @@ void CPVREpg::ForceUpdate(void) m_bUpdatePending = true; } - CServiceBroker::GetPVRManager().EpgContainer().SetHasPendingUpdates(true); + SetChanged(true); + NotifyObservers(ObservableMessageEpgUpdatePending); } bool CPVREpg::HasValidEntries(void) const @@ -73,9 +74,8 @@ void CPVREpg::Clear(void) m_tags.clear(); } -void CPVREpg::Cleanup(void) +void CPVREpg::Cleanup(int iPastDays) { - int iPastDays = CServiceBroker::GetPVRManager().EpgContainer().GetPastDaysToDisplay(); const CDateTime cleanupTime = CDateTime::GetUTCDateTime() - CDateTimeSpan(iPastDays, 0, 0, 0); Cleanup(cleanupTime); } @@ -90,8 +90,6 @@ void CPVREpg::Cleanup(const CDateTime &time) if (m_nowActiveStart == it->first) m_nowActiveStart.SetValid(false); - it->second->ClearTimer(); - it->second->ClearRecording(); it = m_tags.erase(it); } else @@ -236,75 +234,41 @@ CPVREpgInfoTagPtr CPVREpg::GetTagBetween(const CDateTime &beginTime, const CDate time_t e; endTime.GetAsTime(e); - const CPVRChannelPtr channel = Channel(); - const CPVREpgPtr tmpEpg = channel - ? std::make_shared<CPVREpg>(channel) - : std::make_shared<CPVREpg>(m_iEpgID, m_strName, m_strScraperName, false); - + const std::shared_ptr<CPVREpg> tmpEpg = std::make_shared<CPVREpg>(m_iEpgID, m_strName, m_strScraperName, m_channelData); if (tmpEpg->UpdateFromScraper(b, e, true)) tag = tmpEpg->GetTagBetween(beginTime, endTime, false); if (tag) { m_tags.insert(std::make_pair(tag->StartAsUTC(), tag)); - UpdateEntry(tag, !CServiceBroker::GetPVRManager().EpgContainer().IgnoreDB()); + UpdateEntry(tag, !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_IGNOREDBFORCLIENT)); } } return tag; } -std::vector<CPVREpgInfoTagPtr> CPVREpg::GetTagsBetween(const CDateTime &beginTime, const CDateTime &endTime) const -{ - std::vector<CPVREpgInfoTagPtr> epgTags; - - CSingleLock lock(m_critSection); - for (const auto &infoTag : m_tags) - { - if (infoTag.second->StartAsUTC() >= beginTime) - { - if (infoTag.second->EndAsUTC() <= endTime) - epgTags.emplace_back(infoTag.second); - else - break; // done. - } - } - - return epgTags; -} - void CPVREpg::AddEntry(const CPVREpgInfoTag &tag) { CPVREpgInfoTagPtr newTag; - CPVRChannelPtr channel; - { - CSingleLock lock(m_critSection); - const auto it = m_tags.find(tag.StartAsUTC()); - if (it != m_tags.end()) - newTag = it->second; - else - { - newTag = std::make_shared<CPVREpgInfoTag>(m_pvrChannel, this, m_strName); - m_tags.insert(std::make_pair(tag.StartAsUTC(), newTag)); - } - - channel = m_pvrChannel; - } - if (newTag) + CSingleLock lock(m_critSection); + const auto it = m_tags.find(tag.StartAsUTC()); + if (it != m_tags.end()) + newTag = it->second; + else { - newTag->Update(tag); - newTag->SetChannel(channel); - newTag->SetEpg(this); - newTag->SetTimer(CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(newTag)); - newTag->SetRecording(CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(newTag)); + newTag = std::make_shared<CPVREpgInfoTag>(m_channelData, m_iEpgID); + m_tags.insert(std::make_pair(tag.StartAsUTC(), newTag)); } + + newTag->Update(tag); + newTag->SetEpgID(m_iEpgID); } -bool CPVREpg::Load(void) +bool CPVREpg::Load(const std::shared_ptr<CPVREpgDatabase>& database) { bool bReturn = false; - CPVREpgDatabasePtr database = CServiceBroker::GetPVRManager().EpgContainer().GetEpgDatabase(); if (!database) { @@ -324,7 +288,15 @@ bool CPVREpg::Load(void) for (const auto& entry : result) AddEntry(*entry); - m_lastScanTime = GetLastScanTime(); + if (!m_lastScanTime.IsValid()) + database->GetLastEpgScanTime(m_iEpgID, &m_lastScanTime); + + if (!m_lastScanTime.IsValid()) + { + m_lastScanTime.SetDateTime(1970, 1, 1, 0, 0, 0); + m_bUpdateLastScanTime = true; + } + bReturn = true; } @@ -343,7 +315,7 @@ bool CPVREpg::UpdateEntries(const CPVREpg &epg, bool bStoreInDb /* = true */) FixOverlappingEvents(bStoreInDb); /* update the last scan time of this table */ - m_lastScanTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime(); + m_lastScanTime = CDateTime::GetUTCDateTime(); m_bUpdateLastScanTime = true; SetChanged(true); @@ -354,35 +326,12 @@ bool CPVREpg::UpdateEntries(const CPVREpg &epg, bool bStoreInDb /* = true */) return true; } -CDateTime CPVREpg::GetLastScanTime(void) -{ - bool bIgnore = CServiceBroker::GetPVRManager().EpgContainer().IgnoreDB(); - CPVREpgDatabasePtr database = CServiceBroker::GetPVRManager().EpgContainer().GetEpgDatabase(); - - CDateTime lastScanTime; - { - CSingleLock lock(m_critSection); - - if (!m_lastScanTime.IsValid()) - { - if (!bIgnore && database) - database->GetLastEpgScanTime(m_iEpgID, &m_lastScanTime); - - if (!m_lastScanTime.IsValid()) - m_lastScanTime.SetDateTime(1970, 1, 1, 0, 0, 0); - } - lastScanTime = m_lastScanTime; - } - - return lastScanTime; -} - bool CPVREpg::UpdateEntry(const EPG_TAG *data, int iClientId, bool bUpdateDatabase) { if (!data) return false; - const CPVREpgInfoTagPtr tag = std::make_shared<CPVREpgInfoTag>(*data, iClientId); + const std::shared_ptr<CPVREpgInfoTag> tag = std::make_shared<CPVREpgInfoTag>(*data, iClientId, m_channelData, m_iEpgID); return UpdateEntry(tag, bUpdateDatabase); } @@ -390,32 +339,26 @@ bool CPVREpg::UpdateEntry(const CPVREpgInfoTagPtr &tag, bool bUpdateDatabase) { CPVREpgInfoTagPtr infoTag; + CSingleLock lock(m_critSection); + const auto it = m_tags.find(tag->StartAsUTC()); + bool bNewTag = false; + if (it != m_tags.end()) { - CSingleLock lock(m_critSection); - const auto it = m_tags.find(tag->StartAsUTC()); - bool bNewTag = false; - if (it != m_tags.end()) - { - infoTag = it->second; - } - else - { - infoTag = std::make_shared<CPVREpgInfoTag>(m_pvrChannel, this, m_strName); - infoTag->SetUniqueBroadcastID(tag->UniqueBroadcastID()); - m_tags.insert(std::make_pair(tag->StartAsUTC(), infoTag)); - bNewTag = true; - } - - infoTag->Update(*tag, bNewTag); - infoTag->SetEpg(this); - infoTag->SetChannel(m_pvrChannel); - - if (bUpdateDatabase) - m_changedTags.insert(std::make_pair(infoTag->UniqueBroadcastID(), infoTag)); + infoTag = it->second; + } + else + { + infoTag = std::make_shared<CPVREpgInfoTag>(m_channelData, m_iEpgID); + infoTag->SetUniqueBroadcastID(tag->UniqueBroadcastID()); + m_tags.insert(std::make_pair(tag->StartAsUTC(), infoTag)); + bNewTag = true; } - infoTag->SetTimer(CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(infoTag)); - infoTag->SetRecording(CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(infoTag)); + infoTag->Update(*tag, bNewTag); + infoTag->SetEpgID(m_iEpgID); + + if (bUpdateDatabase) + m_changedTags.insert(std::make_pair(infoTag->UniqueBroadcastID(), infoTag)); return true; } @@ -446,15 +389,13 @@ bool CPVREpg::UpdateEntry(const CPVREpgInfoTagPtr &tag, EPG_EVENT_STATE newState else { // Respect epg linger time. - int iPastDays = CServiceBroker::GetPVRManager().EpgContainer().GetPastDaysToDisplay(); + int iPastDays = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_EPG_PAST_DAYSTODISPLAY); const CDateTime cleanupTime(CDateTime::GetUTCDateTime() - CDateTimeSpan(iPastDays, 0, 0, 0)); if (it->second->EndAsUTC() < cleanupTime) { if (bUpdateDatabase) m_deletedTags.insert(std::make_pair(it->second->UniqueBroadcastID(), it->second)); - it->second->ClearTimer(); - it->second->ClearRecording(); m_tags.erase(it); } else @@ -478,24 +419,26 @@ bool CPVREpg::UpdateEntry(const CPVREpgInfoTagPtr &tag, EPG_EVENT_STATE newState return bRet; } -bool CPVREpg::Update(const time_t start, const time_t end, int iUpdateTime, bool bForceUpdate /* = false */) +bool CPVREpg::Update(time_t start, + time_t end, + int iUpdateTime, + int iPastDays, + const std::shared_ptr<CPVREpgDatabase>& database, + bool bForceUpdate /* = false */) { bool bGrabSuccess = true; bool bUpdate = false; /* load the entries from the db first */ - if (!m_bLoaded && !CServiceBroker::GetPVRManager().EpgContainer().IgnoreDB()) - Load(); + if (!m_bLoaded && database) + Load(database); /* clean up if needed */ if (m_bLoaded) - Cleanup(); - - /* get the last update time from the database */ - const CDateTime lastScanTime = GetLastScanTime(); + Cleanup(iPastDays); - /* enforce advanced settings update interval override for TV Channels with no EPG data */ - if (m_tags.empty() && !bUpdate && ChannelID() > 0 && !Channel()->IsRadio()) + /* enforce advanced settings update interval override for channels with no EPG data */ + if (m_tags.empty() && !bUpdate && ChannelID() > 0) iUpdateTime = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iEpgUpdateEmptyTagsInterval; if (!bForceUpdate) @@ -503,8 +446,8 @@ bool CPVREpg::Update(const time_t start, const time_t end, int iUpdateTime, bool /* check if we have to update */ time_t iNow = 0; time_t iLastUpdate = 0; - CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); - lastScanTime.GetAsTime(iLastUpdate); + CDateTime::GetUTCDateTime().GetAsTime(iNow); + m_lastScanTime.GetAsTime(iLastUpdate); bUpdate = (iNow > iLastUpdate + iUpdateTime); } else @@ -524,43 +467,22 @@ bool CPVREpg::Update(const time_t start, const time_t end, int iUpdateTime, bool return bGrabSuccess; } -int CPVREpg::Get(CFileItemList &results) const -{ - int iInitialSize = results.Size(); - - CSingleLock lock(m_critSection); - for (const auto& tag : m_tags) - results.Add(std::make_shared<CFileItem>(tag.second)); - - return results.Size() - iInitialSize; -} - -int CPVREpg::Get(CFileItemList &results, const CPVREpgSearchFilter &filter) const +std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpg::GetTags() const { - int iInitialSize = results.Size(); - - if (!HasValidEntries()) - return -1; + std::vector<std::shared_ptr<CPVREpgInfoTag>> tags; CSingleLock lock(m_critSection); for (const auto& tag : m_tags) - { - if (filter.FilterEntry(tag.second)) - results.Add(std::make_shared<CFileItem>(tag.second)); - } + tags.emplace_back(tag.second); - return results.Size() - iInitialSize; + return tags; } -bool CPVREpg::Persist(void) +bool CPVREpg::Persist(const std::shared_ptr<CPVREpgDatabase>& database) { - if (CServiceBroker::GetPVRManager().EpgContainer().IgnoreDB() || !NeedsSave()) - return true; - - const CPVREpgDatabasePtr database = CServiceBroker::GetPVRManager().EpgContainer().GetEpgDatabase(); if (!database) { - CLog::LogF(LOGERROR, "Could not open the EPG database"); + CLog::LogF(LOGERROR, "No EPG database"); return false; } @@ -568,21 +490,31 @@ bool CPVREpg::Persist(void) { CSingleLock lock(m_critSection); + bool bEpgIdChanged = false; if (m_iEpgID <= 0 || m_bChanged) { int iId = database->Persist(*this, m_iEpgID > 0); - if (iId > 0) + if (iId > 0 && m_iEpgID != iId) + { m_iEpgID = iId; + bEpgIdChanged = true; + } } for (const auto& tag : m_deletedTags) database->Delete(*tag.second); for (const auto& tag : m_changedTags) - tag.second->Persist(false); + tag.second->Persist(database, false); if (m_bUpdateLastScanTime) - database->PersistLastEpgScanTime(m_iEpgID, true); + database->PersistLastEpgScanTime(m_iEpgID, m_lastScanTime, true); + + if (bEpgIdChanged) + { + for (const auto& tag : m_tags) + tag.second->SetEpgID(m_iEpgID); + } m_deletedTags.clear(); m_changedTags.clear(); @@ -642,8 +574,6 @@ bool CPVREpg::FixOverlappingEvents(bool bUpdateDb /* = false */) if (m_nowActiveStart == it->first) m_nowActiveStart.SetValid(false); - it->second->ClearTimer(); - it->second->ClearRecording(); m_tags.erase(it++); } else if (previousTag->EndAsUTC() > currentTag->StartAsUTC()) @@ -671,43 +601,38 @@ bool CPVREpg::UpdateFromScraper(time_t start, time_t end, bool bForceUpdate) } else if (m_strScraperName == "client") { - const CPVRChannelPtr channel = Channel(); - if (channel) + if (!CServiceBroker::GetPVRManager().EpgsCreated()) + return false; + + if (!m_channelData->IsEPGEnabled() || m_channelData->IsHidden()) + { + // ignore. not interested in any updates. + return true; + } + + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId()); + if (client) { - if (!channel->EPGEnabled() || channel->IsHidden()) + if (!client->GetClientCapabilities().SupportsEPG()) { - // ignore. not interested in any updates. - return true; + CLog::LogF(LOGERROR, "The backend for channel '%s' on client '%i' does not support EPGs", + m_channelData->ChannelName().c_str(), m_channelData->ClientId()); } - - const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(channel->ClientID()); - if (client) + else if (!bForceUpdate && client->GetClientCapabilities().SupportsAsyncEPGTransfer()) { - if (!client->GetClientCapabilities().SupportsEPG()) - { - CLog::LogF(LOGERROR, "The backend for channel '%s' on client '%i' does not support EPGs", - channel->ChannelName().c_str(), channel->ClientID()); - } - else if (!bForceUpdate && client->GetClientCapabilities().SupportsAsyncEPGTransfer()) - { - // nothing to do. client will provide epg updates asynchronously - return true; - } - else - { - CLog::LogFC(LOGDEBUG, LOGEPG, "Updating EPG for channel '%s' from client '%i'", - channel->ChannelName().c_str(), channel->ClientID()); - return (client->GetEPGForChannel(channel, this, start, end) == PVR_ERROR_NO_ERROR); - } + // nothing to do. client will provide epg updates asynchronously + return true; } else { - CLog::LogF(LOGERROR, "Client '%i' not found, can't update", channel->ClientID()); + CLog::LogFC(LOGDEBUG, LOGEPG, "Updating EPG for channel '%s' from client '%i'", + m_channelData->ChannelName().c_str(), m_channelData->ClientId()); + return (client->GetEPGForChannel(m_channelData, this, start, end) == PVR_ERROR_NO_ERROR); } } else { - CLog::LogF(LOGERROR, "Channel not found, can't update"); + CLog::LogF(LOGERROR, "Client '%i' not found, can't update", m_channelData->ClientId()); } } else // other non-empty scraper name... @@ -771,49 +696,32 @@ bool CPVREpg::LoadFromClients(time_t start, time_t end, bool bForceUpdate) { bool bReturn = false; - const CPVRChannelPtr channel = Channel(); - const CPVREpgPtr tmpEpg = channel - ? std::make_shared<CPVREpg>(channel) - : std::make_shared<CPVREpg>(m_iEpgID, m_strName, m_strScraperName, false); - + const std::shared_ptr<CPVREpg> tmpEpg = std::make_shared<CPVREpg>(m_iEpgID, m_strName, m_strScraperName, m_channelData); if (tmpEpg->UpdateFromScraper(start, end, bForceUpdate)) - bReturn = UpdateEntries(*tmpEpg, !CServiceBroker::GetPVRManager().EpgContainer().IgnoreDB()); + bReturn = UpdateEntries(*tmpEpg, !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_IGNOREDBFORCLIENT)); return bReturn; } -CPVRChannelPtr CPVREpg::Channel(void) const +std::shared_ptr<CPVREpgChannelData> CPVREpg::GetChannelData() const { CSingleLock lock(m_critSection); - return m_pvrChannel; + return m_channelData; } -int CPVREpg::ChannelID(void) const +void CPVREpg::SetChannelData(const std::shared_ptr<CPVREpgChannelData>& data) { CSingleLock lock(m_critSection); - return m_pvrChannel ? m_pvrChannel->ChannelID() : -1; + m_channelData = data; + + for (const auto& tag : m_tags) + tag.second->SetChannelData(data); } -void CPVREpg::SetChannel(const CPVRChannelPtr &channel) +int CPVREpg::ChannelID(void) const { CSingleLock lock(m_critSection); - if (m_pvrChannel != channel) - { - if (channel) - { - if (m_strName != channel->ChannelName()) - { - m_bChanged = true; - m_strName = channel->ChannelName(); - } - - channel->SetEpgID(m_iEpgID); - } - - m_pvrChannel = channel; - for (const auto& tag : m_tags) - tag.second->SetChannel(m_pvrChannel); - } + return m_channelData->ChannelId(); } const std::string& CPVREpg::ScraperName(void) const @@ -850,7 +758,7 @@ bool CPVREpg::IsValid(void) const { CSingleLock lock(m_critSection); if (ScraperName() == "client") - return m_pvrChannel != nullptr; + return m_channelData->ClientId() != -1 && m_channelData->UniqueClientChannelId() != PVR_CHANNEL_INVALID_UID; return true; } diff --git a/xbmc/pvr/epg/Epg.h b/xbmc/pvr/epg/Epg.h index c1f9fda627..84f423f4b5 100644 --- a/xbmc/pvr/epg/Epg.h +++ b/xbmc/pvr/epg/Epg.h @@ -12,18 +12,17 @@ #include <string> #include <vector> -#include "FileItem.h" #include "threads/CriticalSection.h" #include "utils/Observer.h" #include "pvr/PVRTypes.h" -#include "pvr/channels/PVRChannel.h" #include "pvr/epg/EpgInfoTag.h" -#include "pvr/epg/EpgSearchFilter.h" /** EPG container for CPVREpgInfoTag instances */ namespace PVR { + class CPVREpgChannelData; + class CPVREpg : public Observable { friend class CPVREpgDatabase; @@ -34,15 +33,17 @@ namespace PVR * @param iEpgID The ID of this table or <= 0 to create a new ID. * @param strName The name of this table. * @param strScraperName The name of the scraper to use. - * @param bLoadedFromDb True if this table was loaded from the database, false otherwise. */ - CPVREpg(int iEpgID, const std::string &strName, const std::string &strScraperName, bool bLoadedFromDb); + CPVREpg(int iEpgID, const std::string& strName, const std::string& strScraperName); /*! - * @brief Create a new EPG instance for a channel. - * @param channel The channel to create the EPG for. + * @brief Create a new EPG instance. + * @param iEpgID The ID of this table or <= 0 to create a new ID. + * @param strName The name of this table. + * @param strScraperName The name of the scraper to use. + * @param channelData The channel data. */ - CPVREpg(const CPVRChannelPtr &channel); + CPVREpg(int iEpgID, const std::string& strName, const std::string& strScraperName, const std::shared_ptr<CPVREpgChannelData>& channelData); /*! * @brief Destroy this EPG instance. @@ -50,28 +51,29 @@ namespace PVR ~CPVREpg(void) override; /*! - * @brief Load all entries for this table from the database. + * @brief Load all entries for this table from the given database. + * @param database The database. * @return True if any entries were loaded, false otherwise. */ - bool Load(void); + bool Load(const std::shared_ptr<CPVREpgDatabase>& database); /*! - * @brief The channel this EPG belongs to. - * @return The channel this EPG belongs to + * @brief Get data for the channel associated with this EPG. + * @return The data. */ - CPVRChannelPtr Channel(void) const; + std::shared_ptr<CPVREpgChannelData> GetChannelData() const; /*! - * @brief The id of the channel this EPG belongs to. - * @return The channel id or -1 if no channel + * @brief Set data for the channel associated with this EPG. + * @param data The data. */ - int ChannelID(void) const; + void SetChannelData(const std::shared_ptr<CPVREpgChannelData>& data); /*! - * @brief Channel the channel tag linked to this EPG table. - * @param channel The new channel tag. + * @brief The id of the channel associated with this EPG. + * @return The channel id or -1 if no channel is associated */ - void SetChannel(const CPVRChannelPtr &channel); + int ChannelID(void) const; /*! * @brief Get the name of the scraper to use for this table. @@ -109,19 +111,12 @@ namespace PVR bool HasValidEntries(void) const; /*! - * @brief Remove all entries from this EPG that finished before the given time - * and that have no timers set. + * @brief Remove all entries from this EPG that finished before the given time. * @param time Delete entries with an end time before this time in UTC. */ void Cleanup(const CDateTime &time); /*! - * @brief Remove all entries from this EPG that finished before the given time - * and that have no timers set. - */ - void Cleanup(void); - - /*! * @brief Remove all entries from this EPG. */ void Clear(void); @@ -154,14 +149,6 @@ namespace PVR CPVREpgInfoTagPtr GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime, bool bUpdateFromClient = false); /*! - * @brief Get all events occurring between the given begin and end time. - * @param beginTime Minimum start time in UTC of the event. - * @param endTime Maximum end time in UTC of the event. - * @return The tags found or an empty vector if none was found. - */ - std::vector<CPVREpgInfoTagPtr> GetTagsBetween(const CDateTime &beginTime, const CDateTime &endTime) const; - - /*! * @brief Get the event matching the given unique broadcast id * @param iUniqueBroadcastId The uid to look up * @return The matching event or NULL if it wasn't found. @@ -199,31 +186,25 @@ namespace PVR * @param start The start time. * @param end The end time. * @param iUpdateTime Update the table after the given amount of time has passed. + * @param iPastDays Amount of past days from now on, for which past entries are to be kept. + * @param database If given, the database to store the data. * @param bForceUpdate Force update from client even if it's not the time to * @return True if the update was successful, false otherwise. */ - bool Update(const time_t start, const time_t end, int iUpdateTime, bool bForceUpdate = false); - - /*! - * @brief Get all EPG entries. - * @param results The file list to store the results in. - * @return The amount of entries that were added. - */ - int Get(CFileItemList &results) const; + bool Update(time_t start, time_t end, int iUpdateTime, int iPastDays, const std::shared_ptr<CPVREpgDatabase>& database, bool bForceUpdate = false); /*! - * @brief Get all EPG entries that and apply a filter. - * @param results The file list to store the results in. - * @param filter The filter to apply. - * @return The amount of entries that were added. + * @brief Get all EPG tags. + * @return The tags. */ - int Get(CFileItemList &results, const CPVREpgSearchFilter &filter) const; + std::vector<std::shared_ptr<CPVREpgInfoTag>> GetTags() const; /*! - * @brief Persist this table in the database. + * @brief Persist this table in the given database + * @param database The database. * @return True if the table was persisted, false otherwise. */ - bool Persist(void); + bool Persist(const std::shared_ptr<CPVREpgDatabase>& database); /*! * @brief Get the start time of the first entry in this table. @@ -238,12 +219,6 @@ namespace PVR CDateTime GetLastDate(void) const; /*! - * @brief Get the time the EPG data were last updated. - * @return The last time this table was scanned. - */ - CDateTime GetLastScanTime(void); - - /*! * @brief Notify observers when the currently active tag changed. * @return True if the playing tag has changed, false otherwise. */ @@ -314,6 +289,12 @@ namespace PVR */ bool UpdateEntries(const CPVREpg &epg, bool bStoreInDb = true); + /*! + * @brief Remove all entries from this EPG that finished before the given amount of days. + * @param iPastDays Delete entries with an end time before the given amount of days from now on. + */ + void Cleanup(int iPastDays); + std::map<CDateTime, CPVREpgInfoTagPtr> m_tags; std::map<int, CPVREpgInfoTagPtr> m_changedTags; std::map<int, CPVREpgInfoTagPtr> m_deletedTags; @@ -325,12 +306,10 @@ namespace PVR std::string m_strName; /*!< the name of this table */ std::string m_strScraperName; /*!< the name of the scraper to use */ mutable CDateTime m_nowActiveStart; /*!< the start time of the tag that is currently active */ - CDateTime m_lastScanTime; /*!< the last time the EPG has been updated */ - - CPVRChannelPtr m_pvrChannel; /*!< the channel this EPG belongs to */ - mutable CCriticalSection m_critSection; /*!< critical section for changes in this table */ bool m_bUpdateLastScanTime = false; + + std::shared_ptr<CPVREpgChannelData> m_channelData; }; } diff --git a/xbmc/pvr/epg/EpgChannelData.cpp b/xbmc/pvr/epg/EpgChannelData.cpp new file mode 100644 index 0000000000..7f6ca36edd --- /dev/null +++ b/xbmc/pvr/epg/EpgChannelData.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "EpgChannelData.h" + +#include "XBDateTime.h" + +#include "pvr/channels/PVRChannel.h" + +using namespace PVR; + +CPVREpgChannelData::CPVREpgChannelData(int iClientId, int iUniqueClientChannelId) +: m_iClientId(iClientId), + m_iUniqueClientChannelId(iUniqueClientChannelId) +{ +} + +CPVREpgChannelData::CPVREpgChannelData(const CPVRChannel& channel) +: m_bIsRadio(channel.IsRadio()), + m_iClientId(channel.ClientID()), + m_iUniqueClientChannelId(channel.UniqueID()), + m_bIsHidden(channel.IsHidden()), + m_bIsLocked(channel.IsLocked()), + m_bIsEPGEnabled(channel.EPGEnabled()), + m_iChannelId(channel.ChannelID()), + m_strIconPath(channel.IconPath()), + m_strChannelName(channel.ChannelName()), + m_strSortableChannelNumber(channel.ChannelNumber().SortableChannelNumber()) +{ + SetLastWatched(channel.LastWatched()); +} + +bool CPVREpgChannelData::IsRadio() const +{ + return m_bIsRadio; +} + +int CPVREpgChannelData::ClientId() const +{ + return m_iClientId; +} + +int CPVREpgChannelData::UniqueClientChannelId() const +{ + return m_iUniqueClientChannelId; +} + +bool CPVREpgChannelData::IsHidden() const +{ + return m_bIsHidden; +} + +void CPVREpgChannelData::SetHidden(bool bIsHidden) +{ + m_bIsHidden = bIsHidden; +} + +bool CPVREpgChannelData::IsLocked() const +{ + return m_bIsLocked; +} + +void CPVREpgChannelData::SetLocked(bool bIsLocked) +{ + m_bIsLocked = bIsLocked; +} + +bool CPVREpgChannelData::IsEPGEnabled() const +{ + return m_bIsEPGEnabled; +} + +void CPVREpgChannelData::SetEPGEnabled(bool bIsEPGEnabled) +{ + m_bIsEPGEnabled = bIsEPGEnabled; +} + +int CPVREpgChannelData::ChannelId() const +{ + return m_iChannelId; +} + +void CPVREpgChannelData::SetChannelId(int iChannelId) +{ + m_iChannelId = iChannelId; +} + +const std::string& CPVREpgChannelData::IconPath() const +{ + return m_strIconPath; +} + +void CPVREpgChannelData::SetIconPath(const std::string& strIconPath) +{ + m_strIconPath = strIconPath; +} + +const std::string& CPVREpgChannelData::ChannelName() const +{ + return m_strChannelName; +} + +void CPVREpgChannelData::SetChannelName(const std::string& strChannelName) +{ + m_strChannelName = strChannelName; +} + +const std::string& CPVREpgChannelData::SortableChannelNumber() const +{ + return m_strSortableChannelNumber; +} + +void CPVREpgChannelData::SetSortableChannelNumber(const std::string& strSortableChannelNumber) +{ + m_strSortableChannelNumber = strSortableChannelNumber; +} + +const std::string& CPVREpgChannelData::LastWatched() const +{ + return m_strLastWatched; +} + +void CPVREpgChannelData::SetLastWatched(time_t iLastWatched) +{ + const CDateTime lastWatched(iLastWatched); + if (lastWatched.IsValid()) + m_strLastWatched = lastWatched.GetAsDBDateTime(); + else + m_strLastWatched.clear(); +} diff --git a/xbmc/pvr/epg/EpgChannelData.h b/xbmc/pvr/epg/EpgChannelData.h new file mode 100644 index 0000000000..45bd1dafb8 --- /dev/null +++ b/xbmc/pvr/epg/EpgChannelData.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <ctime> +#include <string> + +namespace PVR +{ + class CPVRChannel; + + class CPVREpgChannelData + { + public: + CPVREpgChannelData() = default; + CPVREpgChannelData(int iClientId, int iUniqueClientChannelId); + explicit CPVREpgChannelData(const CPVRChannel& channel); + + int ClientId() const; + int UniqueClientChannelId() const; + bool IsRadio() const; + + bool IsHidden() const; + void SetHidden(bool bIsHidden); + + bool IsLocked() const; + void SetLocked(bool bIsLocked); + + bool IsEPGEnabled() const; + void SetEPGEnabled(bool bIsEPGEnabled); + + int ChannelId() const; + void SetChannelId(int iChannelId); + + const std::string& IconPath() const; + void SetIconPath(const std::string& strIconPath); + + const std::string& ChannelName() const; + void SetChannelName(const std::string& strChannelName); + + const std::string& SortableChannelNumber() const; + void SetSortableChannelNumber(const std::string& strSortableChannelNumber); + + const std::string& LastWatched() const; + void SetLastWatched(time_t iLastWatched); + + private: + const bool m_bIsRadio = false; + const int m_iClientId = -1; + const int m_iUniqueClientChannelId = -1; + + bool m_bIsHidden = false; + bool m_bIsLocked = false; + bool m_bIsEPGEnabled = true; + int m_iChannelId = -1; + std::string m_strIconPath; + std::string m_strChannelName; + std::string m_strSortableChannelNumber; + std::string m_strLastWatched; + }; +} diff --git a/xbmc/pvr/epg/EpgContainer.cpp b/xbmc/pvr/epg/EpgContainer.cpp index 0e13688a0f..1d4bdc966f 100644 --- a/xbmc/pvr/epg/EpgContainer.cpp +++ b/xbmc/pvr/epg/EpgContainer.cpp @@ -8,8 +8,6 @@ #include "EpgContainer.h" -#include <utility> - #include "ServiceBroker.h" #include "guilib/LocalizeStrings.h" #include "settings/AdvancedSettings.h" @@ -22,11 +20,9 @@ #include "pvr/PVRManager.h" #include "pvr/PVRGUIProgressHandler.h" -#include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/Epg.h" +#include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgSearchFilter.h" -#include "pvr/recordings/PVRRecordings.h" -#include "pvr/timers/PVRTimerInfoTag.h" namespace PVR { @@ -35,29 +31,22 @@ class CEpgUpdateRequest { public: CEpgUpdateRequest() : CEpgUpdateRequest(-1, PVR_CHANNEL_INVALID_UID) {} - CEpgUpdateRequest(int iClientID, unsigned int iUniqueChannelID) : m_iClientID(iClientID), m_iUniqueChannelID(iUniqueChannelID) {} + CEpgUpdateRequest(int iClientID, int iUniqueChannelID) : m_iClientID(iClientID), m_iUniqueChannelID(iUniqueChannelID) {} void Deliver(); private: int m_iClientID; - unsigned int m_iUniqueChannelID; + int m_iUniqueChannelID; }; void CEpgUpdateRequest::Deliver() { - const CPVRChannelPtr channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetByUniqueID(m_iUniqueChannelID, m_iClientID); - if (!channel) - { - CLog::LogF(LOGERROR, "Invalid channel (%d)! Unable to deliver the epg update!", m_iUniqueChannelID); - return; - } - - const CPVREpgPtr epg = channel->GetEPG(); + const std::shared_ptr<CPVREpg> epg = CServiceBroker::GetPVRManager().EpgContainer().GetByChannelUid(m_iClientID, m_iUniqueChannelID); if (!epg) { - CLog::LogF(LOGERROR, "Channel '%s' does not have an EPG! Unable to deliver the epg update!", - channel->ChannelName().c_str()); + CLog::LogF(LOGERROR, "Unable to obtain EPG for client %d and channel %d! Unable to deliver the epg update request!", + m_iClientID, m_iUniqueChannelID); return; } @@ -79,25 +68,27 @@ private: void CEpgTagStateChange::Deliver() { - const CPVRChannelPtr channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetByUniqueID(m_epgtag->UniqueChannelID(), m_epgtag->ClientID()); - if (!channel) + const std::shared_ptr<CPVREpg> epg = CServiceBroker::GetPVRManager().EpgContainer().GetByChannelUid(m_epgtag->ClientID(), m_epgtag->UniqueChannelID()); + if (!epg) { - CLog::LogF(LOGERROR, "Invalid channel (%d)! Unable to deliver state change for tag '%d'!", - m_epgtag->UniqueChannelID(), m_epgtag->UniqueBroadcastID()); + CLog::LogF(LOGERROR, "Unable to obtain EPG for client %d and channel %d! Unable to deliver state change for tag '%d'!", + m_epgtag->ClientID(), m_epgtag->UniqueChannelID(), m_epgtag->UniqueBroadcastID()); return; } - const CPVREpgPtr epg = channel->GetEPG(); - if (!epg) + if (m_epgtag->EpgID() < 0) { - CLog::LogF(LOGERROR, "Channel '%s' does not have an EPG! Unable to deliver state change for tag '%d'!", - channel->ChannelName().c_str(), m_epgtag->UniqueBroadcastID()); - return; + // now that we have the epg instance, fully initialize the tag + m_epgtag->SetEpgID(epg->EpgID()); + m_epgtag->SetChannelData(epg->GetChannelData()); } // update if (!epg->UpdateEntry(m_epgtag, m_state, false)) - CLog::LogF(LOGERROR, "Update failed for epgtag change for channel '%s'", channel->ChannelName().c_str()); + CLog::LogF(LOGWARNING, "State update failed for epgtag (%s | %s | %s | %s | %s)", + m_state == EPG_EVENT_DELETED ? "DELETED" : m_state == EPG_EVENT_UPDATED ? "UPDTAED" : m_state == EPG_EVENT_CREATED ? "CREATED" : "UNKNOWN", + epg->GetChannelData()->ChannelName().c_str(), m_epgtag->StartAsLocalTime().GetAsDBDateTime(), m_epgtag->EndAsLocalTime().GetAsDBDateTime(), + m_epgtag->Title().c_str()); } CPVREpgContainer::CPVREpgContainer(void) : @@ -136,7 +127,7 @@ bool CPVREpgContainer::IsStarted(void) const return m_bStarted; } -unsigned int CPVREpgContainer::NextEpgId(void) +int CPVREpgContainer::NextEpgId(void) { CSingleLock lock(m_critSection); return ++m_iNextEpgId; @@ -152,10 +143,11 @@ void CPVREpgContainer::Clear() { CSingleLock lock(m_critSection); /* clear all epg tables and remove pointers to epg tables on channels */ - for (const auto &epgEntry : m_epgs) + for (const auto &epgEntry : m_epgIdToEpgMap) epgEntry.second->UnregisterObserver(this); - m_epgs.clear(); + m_epgIdToEpgMap.clear(); + m_channelUidToEpgMap.clear(); m_iNextEpgUpdate = 0; m_bStarted = false; m_bIsInitialising = true; @@ -254,6 +246,11 @@ void CPVREpgContainer::Notify(const Observable &obs, const ObservableMessage msg m_bUpdateNotificationPending = true; return; } + else if (msg == ObservableMessageEpgUpdatePending) + { + SetHasPendingUpdates(true); + return; + } SetChanged(); CSingleExit ex(m_critSection); @@ -276,21 +273,21 @@ void CPVREpgContainer::LoadFromDB(void) m_database->Lock(); m_iNextEpgId = m_database->GetLastEPGId(); m_database->DeleteEpgEntries(cleanupTime); - const std::vector<CPVREpgPtr> result = m_database->Get(*this); + const std::vector<std::shared_ptr<CPVREpg>> result = m_database->GetAll(); m_database->Unlock(); for (const auto& entry : result) InsertFromDB(entry); - for (const auto &epgEntry : m_epgs) + for (const auto &epgEntry : m_epgIdToEpgMap) { if (m_bStop) break; - progressHandler->UpdateProgress(epgEntry.second->Name(), ++iCounter, m_epgs.size()); + progressHandler->UpdateProgress(epgEntry.second->Name(), ++iCounter, m_epgIdToEpgMap.size()); lock.Leave(); - epgEntry.second->Load(); + epgEntry.second->Load(GetEpgDatabase()); lock.Enter(); } @@ -301,16 +298,21 @@ void CPVREpgContainer::LoadFromDB(void) bool CPVREpgContainer::PersistAll(void) { - bool bReturn = true; - - m_critSection.lock(); - const auto epgs = m_epgs; - m_critSection.unlock(); + bool bReturn = IgnoreDB(); - for (const auto& epg : epgs) + if (!bReturn) { - if (epg.second && epg.second->NeedsSave()) - bReturn &= epg.second->Persist(); + m_critSection.lock(); + const auto epgs = m_epgIdToEpgMap; + m_critSection.unlock(); + + const std::shared_ptr<CPVREpgDatabase> database = GetEpgDatabase(); + + for (const auto& epg : epgs) + { + if (epg.second && epg.second->NeedsSave()) + bReturn &= epg.second->Persist(database); + } } return bReturn; @@ -440,29 +442,42 @@ CPVREpgPtr CPVREpgContainer::GetById(int iEpgId) const return retval; CSingleLock lock(m_critSection); - const auto &epgEntry = m_epgs.find(static_cast<unsigned int>(iEpgId)); - if (epgEntry != m_epgs.end()) + const auto &epgEntry = m_epgIdToEpgMap.find(iEpgId); + if (epgEntry != m_epgIdToEpgMap.end()) retval = epgEntry->second; return retval; } -CPVREpgInfoTagPtr CPVREpgContainer::GetTagById(const CPVRChannelPtr &channel, unsigned int iBroadcastId) const +std::shared_ptr<CPVREpg> CPVREpgContainer::GetByChannelUid(int iClientId, int iChannelUid) const +{ + std::shared_ptr<CPVREpg> epg; + + if (iClientId < 0 || iChannelUid < 0) + return epg; + + CSingleLock lock(m_critSection); + const auto& epgEntry = m_channelUidToEpgMap.find(std::pair<int, int>(iClientId, iChannelUid)); + if (epgEntry != m_channelUidToEpgMap.end()) + epg = epgEntry->second; + + return epg; +} + +std::shared_ptr<CPVREpgInfoTag> CPVREpgContainer::GetTagById(const std::shared_ptr<CPVREpg>& epg, unsigned int iBroadcastId) const { CPVREpgInfoTagPtr retval; if (iBroadcastId == EPG_TAG_INVALID_UID) return retval; - if (channel) + if (epg) { - const CPVREpgPtr epg = channel->GetEPG(); - if (epg) - retval = epg->GetTagByBroadcastId(iBroadcastId); + retval = epg->GetTagByBroadcastId(iBroadcastId); } else { - for (const auto &epgEntry : m_epgs) + for (const auto& epgEntry : m_epgIdToEpgMap) { retval = epgEntry.second->GetTagByBroadcastId(iBroadcastId); if (retval) @@ -473,20 +488,18 @@ CPVREpgInfoTagPtr CPVREpgContainer::GetTagById(const CPVRChannelPtr &channel, un return retval; } -std::vector<CPVREpgInfoTagPtr> CPVREpgContainer::GetEpgTagsForTimer(const CPVRTimerInfoTagPtr &timer) const +std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgContainer::GetAllTags() const { - CPVRChannelPtr channel = timer->Channel(); + std::vector<std::shared_ptr<CPVREpgInfoTag>> allTags; - if (!channel) - channel = timer->UpdateChannel(); - - if (channel) + CSingleLock lock(m_critSection); + for (const auto& epgEntry : m_epgIdToEpgMap) { - const CPVREpgPtr epg = channel->GetEPG(); - if (epg) - return epg->GetTagsBetween(timer->StartAsUTC(), timer->EndAsUTC()); + const std::vector<std::shared_ptr<CPVREpgInfoTag>> epgTags = epgEntry.second->GetTags(); + allTags.insert(allTags.end(), epgTags.begin(), epgTags.end()); } - return std::vector<CPVREpgInfoTagPtr>(); + + return allTags; } void CPVREpgContainer::InsertFromDB(const CPVREpgPtr &newEpg) @@ -506,38 +519,41 @@ void CPVREpgContainer::InsertFromDB(const CPVREpgPtr &newEpg) { // create a new epg table epg = newEpg; - m_epgs.insert(std::make_pair(epg->EpgID(), epg)); + m_epgIdToEpgMap.insert({epg->EpgID(), epg}); SetChanged(); epg->RegisterObserver(this); } } -CPVREpgPtr CPVREpgContainer::CreateChannelEpg(const CPVRChannelPtr &channel) +CPVREpgPtr CPVREpgContainer::CreateChannelEpg(int iEpgId, const std::string& strScraperName, const std::shared_ptr<CPVREpgChannelData>& channelData) { CPVREpgPtr epg; - if (!channel) - return epg; WaitForUpdateFinish(); LoadFromDB(); - if (channel->EpgID() > 0) - epg = GetById(channel->EpgID()); + if (iEpgId > 0) + epg = GetById(iEpgId); if (!epg) { - if (channel->EpgID() <= 0) - channel->SetEpgID(NextEpgId()); + if (iEpgId <= 0) + iEpgId = NextEpgId(); - epg.reset(new CPVREpg(channel)); + epg.reset(new CPVREpg(iEpgId, channelData->ChannelName(), strScraperName, channelData)); CSingleLock lock(m_critSection); - m_epgs.insert(std::make_pair(static_cast<unsigned int>(epg->EpgID()), epg)); + m_epgIdToEpgMap.insert({iEpgId, epg}); + m_channelUidToEpgMap.insert({{channelData->ClientId(), channelData->UniqueClientChannelId()}, epg}); SetChanged(); epg->RegisterObserver(this); } - - epg->SetChannel(channel); + else if (epg->ChannelID() == -1) + { + CSingleLock lock(m_critSection); + m_channelUidToEpgMap.insert({{channelData->ClientId(), channelData->UniqueClientChannelId()}, epg}); + epg->SetChannelData(channelData); + } { CSingleLock lock(m_critSection); @@ -556,7 +572,7 @@ bool CPVREpgContainer::RemoveOldEntries(void) const CDateTime cleanupTime(CDateTime::GetUTCDateTime() - CDateTimeSpan(GetPastDaysToDisplay(), 0, 0, 0)); /* call Cleanup() on all known EPG tables */ - for (const auto &epgEntry : m_epgs) + for (const auto& epgEntry : m_epgIdToEpgMap) epgEntry.second->Cleanup(cleanupTime); /* remove the old entries from the database */ @@ -576,16 +592,21 @@ bool CPVREpgContainer::DeleteEpg(const CPVREpgPtr &epg, bool bDeleteFromDatabase CSingleLock lock(m_critSection); - const auto &epgEntry = m_epgs.find(static_cast<unsigned int>(epg->EpgID())); - if (epgEntry == m_epgs.end()) + const auto& epgEntry = m_epgIdToEpgMap.find(epg->EpgID()); + if (epgEntry == m_epgIdToEpgMap.end()) return false; + const auto& epgEntry1 = m_channelUidToEpgMap.find(std::make_pair(epg->GetChannelData()->ClientId(), + epg->GetChannelData()->UniqueClientChannelId())); + if (epgEntry1 != m_channelUidToEpgMap.end()) + m_channelUidToEpgMap.erase(epgEntry1); + CLog::LogFC(LOGDEBUG, LOGEPG, "Deleting EPG table %s (%d)", epg->Name().c_str(), epg->EpgID()); if (bDeleteFromDatabase && !IgnoreDB()) m_database->Delete(*epgEntry->second); epgEntry->second->UnregisterObserver(this); - m_epgs.erase(epgEntry); + m_epgIdToEpgMap.erase(epgEntry); return true; } @@ -652,7 +673,8 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */) /* load or update all EPG tables */ unsigned int iCounter = 0; - for (const auto &epgEntry : m_epgs) + const std::shared_ptr<CPVREpgDatabase> database = IgnoreDB() ? nullptr : GetEpgDatabase(); + for (const auto& epgEntry : m_epgIdToEpgMap) { if (InterruptUpdate()) { @@ -665,22 +687,15 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */) continue; if (bShowProgress && !bOnlyPending) - progressHandler->UpdateProgress(epg->Name(), ++iCounter, m_epgs.size()); - - // we currently only support update via pvr add-ons. skip update when the pvr manager isn't started - if (!CServiceBroker::GetPVRManager().IsStarted()) - continue; - - // check the pvr manager when the channel pointer isn't set - if (!epg->Channel()) - { - const CPVRChannelPtr channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelByEpgId(epg->EpgID()); - if (channel) - epg->SetChannel(channel); - } + progressHandler->UpdateProgress(epg->Name(), ++iCounter, m_epgIdToEpgMap.size()); if ((!bOnlyPending || epg->UpdatePending()) && - epg->Update(start, end, m_settings.GetIntValue(CSettings::SETTING_EPG_EPGUPDATE) * 60, bOnlyPending)) + epg->Update(start, + end, + m_settings.GetIntValue(CSettings::SETTING_EPG_EPGUPDATE) * 60, + m_settings.GetIntValue(CSettings::SETTING_EPG_PAST_DAYSTODISPLAY), + database, + bOnlyPending)) { iUpdatedTables++; } @@ -732,7 +747,7 @@ const CDateTime CPVREpgContainer::GetFirstEPGDate(void) CDateTime returnValue; m_critSection.lock(); - const auto epgs = m_epgs; + const auto epgs = m_epgIdToEpgMap; m_critSection.unlock(); for (const auto &epgEntry : epgs) @@ -750,7 +765,7 @@ const CDateTime CPVREpgContainer::GetLastEPGDate(void) CDateTime returnValue; m_critSection.lock(); - const auto epgs = m_epgs; + const auto epgs = m_epgIdToEpgMap; m_critSection.unlock(); for (const auto &epgEntry : epgs) @@ -763,24 +778,6 @@ const CDateTime CPVREpgContainer::GetLastEPGDate(void) return returnValue; } -int CPVREpgContainer::GetEPGSearch(CFileItemList &results, const CPVREpgSearchFilter &filter) -{ - int iInitialSize = results.Size(); - - /* get filtered results from all tables */ - { - CSingleLock lock(m_critSection); - for (const auto &epgEntry : m_epgs) - epgEntry.second->Get(results, filter); - } - - /* remove duplicate entries */ - if (filter.ShouldRemoveDuplicates()) - filter.RemoveDuplicates(results); - - return results.Size() - iInitialSize; -} - bool CPVREpgContainer::CheckPlayingEvents(void) { bool bReturn = false; @@ -796,7 +793,7 @@ bool CPVREpgContainer::CheckPlayingEvents(void) CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); if (iNow >= iNextEpgActiveTagCheck) { - for (const auto &epgEntry : m_epgs) + for (const auto& epgEntry : m_epgIdToEpgMap) bFoundChanges = epgEntry.second->CheckPlayingEvent() || bFoundChanges; CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNextEpgActiveTagCheck); @@ -833,7 +830,7 @@ void CPVREpgContainer::SetHasPendingUpdates(bool bHasPendingUpdates /* = true */ m_pendingUpdates = 0; } -void CPVREpgContainer::UpdateRequest(int iClientID, unsigned int iUniqueChannelID) +void CPVREpgContainer::UpdateRequest(int iClientID, int iUniqueChannelID) { CSingleLock lock(m_updateRequestsLock); m_updateRequests.emplace_back(CEpgUpdateRequest(iClientID, iUniqueChannelID)); diff --git a/xbmc/pvr/epg/EpgContainer.h b/xbmc/pvr/epg/EpgContainer.h index cd43345ab8..6e10e6eaee 100644 --- a/xbmc/pvr/epg/EpgContainer.h +++ b/xbmc/pvr/epg/EpgContainer.h @@ -8,6 +8,11 @@ #pragma once +#include <list> +#include <map> +#include <memory> +#include <utility> + #include "XBDateTime.h" #include "threads/CriticalSection.h" #include "threads/Thread.h" @@ -18,10 +23,11 @@ #include "pvr/epg/Epg.h" #include "pvr/epg/EpgDatabase.h" -class CFileItemList; +class CFileItem; namespace PVR { + class CPVREpgChannelData; class CEpgUpdateRequest; class CEpgTagStateChange; @@ -85,18 +91,12 @@ namespace PVR /*! * @brief Create the EPg for a given channel. - * @param channel The channel. + * @param iEpgId The EPG id. + * @param strScraperName The scraper name. + * @param channelData The channel data. * @return the created EPG */ - CPVREpgPtr CreateChannelEpg(const CPVRChannelPtr &channel); - - /*! - * @brief Get all EPG tables and apply a filter. - * @param results The fileitem list to store the results in. - * @param filter The filter to apply. - * @return The amount of entries that were added. - */ - int GetEPGSearch(CFileItemList &results, const CPVREpgSearchFilter &filter); + std::shared_ptr<CPVREpg> CreateChannelEpg(int iEpgId, const std::string& strScraperName, const std::shared_ptr<CPVREpgChannelData>& channelData); /*! * @brief Get the start time of the first entry. @@ -111,26 +111,33 @@ namespace PVR const CDateTime GetLastEPGDate(void); /*! - * @brief Get an EPG table given it's ID. + * @brief Get an EPG given its ID. * @param iEpgId The database ID of the table. - * @return The table or NULL if it wasn't found. + * @return The EPG or nullptr if it wasn't found. */ CPVREpgPtr GetById(int iEpgId) const; /*! + * @brief Get an EPG given its client id and channel uid. + * @param iClientId the id of the pvr client providing the EPG + * @param iChannelUid the uid of the channel for the EPG + * @return The EPG or nullptr if it wasn't found. + */ + std::shared_ptr<CPVREpg> GetByChannelUid(int iClientId, int iChannelUid) const; + + /*! * @brief Get the EPG event with the given event id - * @param channel The channel to get the event for. - * @param iBroadcastId The event id to get + * @param epg The epg to lookup the event. + * @param iBroadcastId The event id to lookup. * @return The requested event, or an empty tag when not found */ - CPVREpgInfoTagPtr GetTagById(const CPVRChannelPtr &channel, unsigned int iBroadcastId) const; + std::shared_ptr<CPVREpgInfoTag> GetTagById(const std::shared_ptr<CPVREpg>& epg, unsigned int iBroadcastId) const; /*! - * @brief Get the EPG events matching the given timer - * @param timer The timer to get the matching events for. - * @return The matching events, or an empty vector when no matching tag was found + * @brief Get all EPG tags. + * @return The tags. */ - std::vector<CPVREpgInfoTagPtr> GetEpgTagsForTimer(const CPVRTimerInfoTagPtr &timer) const; + std::vector<std::shared_ptr<CPVREpgInfoTag>> GetAllTags() const; /*! * @brief Check whether data should be persisted to the EPG database. @@ -149,7 +156,7 @@ namespace PVR * @param iClientID The id of the client which triggered the update request * @param iUniqueChannelID The uid of the channel for which the epg shall be updated */ - void UpdateRequest(int iClientID, unsigned int iUniqueChannelID); + void UpdateRequest(int iClientID, int iUniqueChannelID); /*! * @brief A client announced an updated epg tag for a channel @@ -174,14 +181,13 @@ namespace PVR * @brief Inform the epg container that playback of an item just started. * @param item The item that started to play. */ - void OnPlaybackStarted(const CFileItemPtr &item); + void OnPlaybackStarted(const std::shared_ptr<CFileItem>& item); /*! * @brief Inform the epg container that playback of an item was stopped due to user interaction. * @param item The item that stopped to play. */ - void OnPlaybackStopped(const CFileItemPtr &item); - + void OnPlaybackStopped(const std::shared_ptr<CFileItem>& item); private: /*! @@ -194,7 +200,7 @@ namespace PVR * @brief The next EPG ID to be given to a table when the db isn't being used. * @return The next ID. */ - unsigned int NextEpgId(void); + int NextEpgId(void); /*! * @brief Wait for an EPG update to finish. @@ -254,8 +260,10 @@ namespace PVR time_t m_iLastEpgCleanup = 0; /*!< the time the EPG was cleaned up */ time_t m_iNextEpgUpdate = 0; /*!< the time the EPG will be updated */ time_t m_iNextEpgActiveTagCheck = 0; /*!< the time the EPG will be checked for active tag updates */ - unsigned int m_iNextEpgId = 0; /*!< the next epg ID that will be given to a new table when the db isn't being used */ - std::map<unsigned int, CPVREpgPtr> m_epgs; /*!< the EPGs in this container */ + int m_iNextEpgId = 0; /*!< the next epg ID that will be given to a new table when the db isn't being used */ + + std::map<int, std::shared_ptr<CPVREpg>> m_epgIdToEpgMap; /*!< the EPGs in this container. maps epg ids to epgs */ + std::map<std::pair<int, int>, std::shared_ptr<CPVREpg>> m_channelUidToEpgMap; /*!< the EPGs in this container. maps channel uids to epgs */ mutable CCriticalSection m_critSection; /*!< a critical section for changes to this container */ CEvent m_updateEvent; /*!< trigger when an update finishes */ diff --git a/xbmc/pvr/epg/EpgDatabase.cpp b/xbmc/pvr/epg/EpgDatabase.cpp index 5cae646895..8d08d45ee4 100644 --- a/xbmc/pvr/epg/EpgDatabase.cpp +++ b/xbmc/pvr/epg/EpgDatabase.cpp @@ -15,10 +15,12 @@ #include "dbwrappers/dataset.h" #include "settings/AdvancedSettings.h" #include "settings/SettingsComponent.h" +#include "threads/SingleLock.h" #include "utils/log.h" #include "utils/StringUtils.h" -#include "pvr/epg/EpgContainer.h" +#include "pvr/epg/Epg.h" +#include "pvr/epg/EpgInfoTag.h" using namespace dbiplus; using namespace PVR; @@ -197,9 +199,9 @@ bool CPVREpgDatabase::Delete(const CPVREpgInfoTag &tag) return DeleteValues("epgtags", filter); } -std::vector<CPVREpgPtr> CPVREpgDatabase::Get(const CPVREpgContainer &container) +std::vector<std::shared_ptr<CPVREpg>> CPVREpgDatabase::GetAll() { - std::vector<CPVREpgPtr> result; + std::vector<std::shared_ptr<CPVREpg>> result; CSingleLock lock(m_critSection); std::string strQuery = PrepareSQL("SELECT idEpg, sName, sScraperName FROM epg;"); @@ -213,7 +215,7 @@ std::vector<CPVREpgPtr> CPVREpgDatabase::Get(const CPVREpgContainer &container) std::string strName = m_pDS->fv("sName").get_asString().c_str(); std::string strScraperName = m_pDS->fv("sScraperName").get_asString().c_str(); - result.emplace_back(new CPVREpg(iEpgID, strName, strScraperName, true)); + result.emplace_back(new CPVREpg(iEpgID, strName, strScraperName)); m_pDS->next(); } m_pDS->close(); @@ -227,9 +229,9 @@ std::vector<CPVREpgPtr> CPVREpgDatabase::Get(const CPVREpgContainer &container) return result; } -std::vector<CPVREpgInfoTagPtr> CPVREpgDatabase::Get(const CPVREpg &epg) +std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::Get(const CPVREpg &epg) { - std::vector<CPVREpgInfoTagPtr> result; + std::vector<std::shared_ptr<CPVREpgInfoTag>> result; CSingleLock lock(m_critSection); std::string strQuery = PrepareSQL("SELECT * FROM epgtags WHERE idEpg = %u;", epg.EpgID()); @@ -239,7 +241,7 @@ std::vector<CPVREpgInfoTagPtr> CPVREpgDatabase::Get(const CPVREpg &epg) { while (!m_pDS->eof()) { - CPVREpgInfoTagPtr newTag(new CPVREpgInfoTag()); + std::shared_ptr<CPVREpgInfoTag> newTag(new CPVREpgInfoTag()); time_t iStartTime, iEndTime, iFirstAired; iStartTime = (time_t) m_pDS->fv("iStartTime").get_asInt(); @@ -317,11 +319,11 @@ bool CPVREpgDatabase::GetLastEpgScanTime(int iEpgId, CDateTime *lastScan) return bReturn; } -bool CPVREpgDatabase::PersistLastEpgScanTime(int iEpgId /* = 0 */, bool bQueueWrite /* = false */) +bool CPVREpgDatabase::PersistLastEpgScanTime(int iEpgId, const CDateTime& lastScanTime, bool bQueueWrite /* = false */) { CSingleLock lock(m_critSection); std::string strQuery = PrepareSQL("REPLACE INTO lastepgscan(idEpg, sLastScan) VALUES (%u, '%s');", - iEpgId, CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsDBDateTime().c_str()); + iEpgId, lastScanTime.GetAsDBDateTime().c_str()); return bQueueWrite ? QueueInsertQuery(strQuery) : ExecuteQuery(strQuery); } @@ -359,7 +361,7 @@ int CPVREpgDatabase::Persist(const CPVREpgInfoTag &tag, bool bSingleUpdate /* = if (tag.EpgID() <= 0) { - CLog::LogF(LOGERROR, "Tag '%s' does not have a valid table", tag.Title(true).c_str()); + CLog::LogF(LOGERROR, "Tag '%s' does not have a valid table", tag.Title().c_str()); return iReturn; } @@ -384,12 +386,12 @@ int CPVREpgDatabase::Persist(const CPVREpgInfoTag &tag, bool bSingleUpdate /* = "iEpisodeId, iEpisodePart, sEpisodeName, iFlags, sSeriesLink, iBroadcastUid) " "VALUES (%u, %u, %u, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, '%s', %i);", tag.EpgID(), static_cast<unsigned int>(iStartTime), static_cast<unsigned int>(iEndTime), - tag.Title(true).c_str(), tag.PlotOutline(true).c_str(), tag.Plot(true).c_str(), - tag.OriginalTitle(true).c_str(), tag.DeTokenize(tag.Cast()).c_str(), tag.DeTokenize(tag.Directors()).c_str(), + tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(), + tag.OriginalTitle().c_str(), tag.DeTokenize(tag.Cast()).c_str(), tag.DeTokenize(tag.Directors()).c_str(), tag.DeTokenize(tag.Writers()).c_str(), tag.Year(), tag.IMDBNumber().c_str(), tag.Icon().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), static_cast<unsigned int>(iFirstAired), tag.ParentalRating(), tag.StarRating(), tag.Notify(), - tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName(true).c_str(), tag.Flags(), tag.SeriesLink().c_str(), + tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(), tag.Flags(), tag.SeriesLink().c_str(), tag.UniqueBroadcastID()); } else @@ -400,12 +402,12 @@ int CPVREpgDatabase::Persist(const CPVREpgInfoTag &tag, bool bSingleUpdate /* = "iEpisodeId, iEpisodePart, sEpisodeName, iFlags, sSeriesLink, iBroadcastUid, idBroadcast) " "VALUES (%u, %u, %u, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, '%s', %i, %i);", tag.EpgID(), static_cast<unsigned int>(iStartTime), static_cast<unsigned int>(iEndTime), - tag.Title(true).c_str(), tag.PlotOutline(true).c_str(), tag.Plot(true).c_str(), - tag.OriginalTitle(true).c_str(), tag.DeTokenize(tag.Cast()).c_str(), tag.DeTokenize(tag.Directors()).c_str(), + tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(), + tag.OriginalTitle().c_str(), tag.DeTokenize(tag.Cast()).c_str(), tag.DeTokenize(tag.Directors()).c_str(), tag.DeTokenize(tag.Writers()).c_str(), tag.Year(), tag.IMDBNumber().c_str(), tag.Icon().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), static_cast<unsigned int>(iFirstAired), tag.ParentalRating(), tag.StarRating(), tag.Notify(), - tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName(true).c_str(), tag.Flags(), tag.SeriesLink().c_str(), + tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(), tag.Flags(), tag.SeriesLink().c_str(), tag.UniqueBroadcastID(), iBroadcastId); } diff --git a/xbmc/pvr/epg/EpgDatabase.h b/xbmc/pvr/epg/EpgDatabase.h index 011b134149..c7f5308be0 100644 --- a/xbmc/pvr/epg/EpgDatabase.h +++ b/xbmc/pvr/epg/EpgDatabase.h @@ -8,16 +8,15 @@ #pragma once -#include "XBDateTime.h" #include "dbwrappers/Database.h" #include "threads/CriticalSection.h" -#include "pvr/epg/Epg.h" +class CDateTime; namespace PVR { + class CPVREpg; class CPVREpgInfoTag; - class CPVREpgContainer; /** The EPG database */ @@ -99,17 +98,16 @@ namespace PVR /*! * @brief Get all EPG tables from the database. Does not get the EPG tables' entries. - * @param container The container to get the EPG tables for. * @return The entries. */ - std::vector<CPVREpgPtr> Get(const CPVREpgContainer &container); + std::vector<std::shared_ptr<CPVREpg>> GetAll(); /*! * @brief Get all EPG entries for a table. * @param epg The EPG table to get the entries for. * @return The entries. */ - std::vector<CPVREpgInfoTagPtr> Get(const CPVREpg &epg); + std::vector<std::shared_ptr<CPVREpgInfoTag>> Get(const CPVREpg &epg); /*! * @brief Get the last stored EPG scan time. @@ -121,11 +119,12 @@ namespace PVR /*! * @brief Update the last scan time. - * @param iEpgId The table to update the time for. Use 0 for a global value. + * @param iEpgId The table to update the time for. + * @param lastScanTime The time to write to the database. * @param bQueueWrite Don't execute the query immediately but queue it if true. * @return True if it was updated successfully, false otherwise. */ - bool PersistLastEpgScanTime(int iEpgId = 0, bool bQueueWrite = false); + bool PersistLastEpgScanTime(int iEpgId, const CDateTime& lastScanTime, bool bQueueWrite = false); /*! * @brief Persist an EPG table. It's entries are not persisted. diff --git a/xbmc/pvr/epg/EpgInfoTag.cpp b/xbmc/pvr/epg/EpgInfoTag.cpp index a6b592c737..8c37ffe3b7 100644 --- a/xbmc/pvr/epg/EpgInfoTag.cpp +++ b/xbmc/pvr/epg/EpgInfoTag.cpp @@ -12,48 +12,66 @@ #include "addons/PVRClient.h" #include "addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h" #include "cores/DataCacheCore.h" -#include "guilib/LocalizeStrings.h" #include "settings/AdvancedSettings.h" -#include "settings/Settings.h" #include "settings/SettingsComponent.h" #include "utils/StringUtils.h" #include "utils/Variant.h" #include "utils/log.h" #include "pvr/PVRManager.h" -#include "pvr/epg/Epg.h" -#include "pvr/epg/EpgContainer.h" +#include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgDatabase.h" -#include "pvr/timers/PVRTimers.h" using namespace PVR; -CPVREpgInfoTag::CPVREpgInfoTag(const CPVRChannelPtr &channel, CPVREpg *epg /* = nullptr */, const std::string &strTableName /* = "" */) -: m_iClientId(channel ? channel->ClientID() : -1), - m_iUniqueChannelID(channel ? channel->UniqueID() : PVR_CHANNEL_INVALID_UID), - m_strIconPath(channel ? channel->IconPath() : ""), - m_epg(epg), - m_channel(channel) +CPVREpgInfoTag::CPVREpgInfoTag() +: m_channelData(new CPVREpgChannelData) { +} + +CPVREpgInfoTag::CPVREpgInfoTag(const std::shared_ptr<CPVREpgChannelData>& channelData, int iEpgID) +: m_iEpgID(iEpgID) +{ + if (channelData) + m_channelData = channelData; + else + m_channelData = std::make_shared<CPVREpgChannelData>(); + UpdatePath(); } -CPVREpgInfoTag::CPVREpgInfoTag(const EPG_TAG &data, int iClientId) +CPVREpgInfoTag::CPVREpgInfoTag(const EPG_TAG& data, int iClientId, const std::shared_ptr<CPVREpgChannelData>& channelData, int iEpgID) : m_bNotify(data.bNotify), - m_iClientId(iClientId), m_iParentalRating(data.iParentalRating), m_iStarRating(data.iStarRating), m_iSeriesNumber(data.iSeriesNumber), m_iEpisodeNumber(data.iEpisodeNumber), m_iEpisodePart(data.iEpisodePartNumber), m_iUniqueBroadcastID(data.iUniqueBroadcastId), - m_iUniqueChannelID(data.iUniqueChannelId), m_iYear(data.iYear), m_startTime(data.startTime + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection), m_endTime(data.endTime + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection), m_firstAired(data.firstAired + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection), - m_iFlags(data.iFlags) + m_iFlags(data.iFlags), + m_iEpgID(iEpgID) { + if (channelData) + { + m_channelData = channelData; + + if (m_channelData->ClientId() != iClientId) + CLog::LogF(LOGERROR, "Client id mismatch (channel: %d, epg: %d)!", + m_channelData->ClientId(), iClientId); + if (m_channelData->UniqueClientChannelId() != static_cast<int>(data.iUniqueChannelId)) + CLog::LogF(LOGERROR, "Channel uid mismatch (channel: %d, epg: %d)!", + m_channelData->UniqueClientChannelId(), data.iUniqueChannelId); + } + else + { + // provide minimalistic channel data until we get fully initialized later + m_channelData = std::make_shared<CPVREpgChannelData>(iClientId, data.iUniqueChannelId); + } + SetGenre(data.iGenreType, data.iGenreSubType, data.strGenreDescription); // explicit NULL check, because there is no implicit NULL constructor for std::string @@ -83,19 +101,22 @@ CPVREpgInfoTag::CPVREpgInfoTag(const EPG_TAG &data, int iClientId) UpdatePath(); } +void CPVREpgInfoTag::SetChannelData(const std::shared_ptr<CPVREpgChannelData>& data) +{ + CSingleLock lock(m_critSection); + if (data) + m_channelData = data; + else + m_channelData.reset(new CPVREpgChannelData); +} + bool CPVREpgInfoTag::operator ==(const CPVREpgInfoTag& right) const { if (this == &right) return true; - bool bChannelMatch = false; - { - CSingleLock lock(m_critSection); - bChannelMatch = (m_channel == right.m_channel); - } - return (bChannelMatch && - m_bNotify == right.m_bNotify && - m_iClientId == right.m_iClientId && + CSingleLock lock(m_critSection); + return (m_bNotify == right.m_bNotify && m_iDatabaseID == right.m_iDatabaseID && m_iGenreType == right.m_iGenreType && m_iGenreSubType == right.m_iGenreSubType && @@ -106,7 +127,6 @@ bool CPVREpgInfoTag::operator ==(const CPVREpgInfoTag& right) const m_iEpisodeNumber == right.m_iEpisodeNumber && m_iEpisodePart == right.m_iEpisodePart && m_iUniqueBroadcastID == right.m_iUniqueBroadcastID && - m_iUniqueChannelID == right.m_iUniqueChannelID && m_strTitle == right.m_strTitle && m_strPlotOutline == right.m_strPlotOutline && m_strPlot == right.m_strPlot && @@ -118,12 +138,14 @@ bool CPVREpgInfoTag::operator ==(const CPVREpgInfoTag& right) const m_strIMDBNumber == right.m_strIMDBNumber && m_genre == right.m_genre && m_strEpisodeName == right.m_strEpisodeName && + m_iEpgID == right.m_iEpgID && m_strIconPath == right.m_strIconPath && m_strFileNameAndPath == right.m_strFileNameAndPath && m_startTime == right.m_startTime && m_endTime == right.m_endTime && m_iFlags == right.m_iFlags && - m_strSeriesLink == right.m_strSeriesLink); + m_strSeriesLink == right.m_strSeriesLink && + m_channelData == right.m_channelData); } bool CPVREpgInfoTag::operator !=(const CPVREpgInfoTag& right) const @@ -136,10 +158,9 @@ bool CPVREpgInfoTag::operator !=(const CPVREpgInfoTag& right) const void CPVREpgInfoTag::Serialize(CVariant &value) const { - const CPVRRecordingPtr recording = Recording(); - + CSingleLock lock(m_critSection); value["broadcastid"] = m_iUniqueBroadcastID; - value["channeluid"] = m_iUniqueChannelID; + value["channeluid"] = m_channelData->UniqueClientChannelId(); value["parentalrating"] = m_iParentalRating; value["rating"] = m_iStarRating; value["title"] = m_strTitle; @@ -162,37 +183,37 @@ void CPVREpgInfoTag::Serialize(CVariant &value) const value["episodename"] = m_strEpisodeName; value["episodenum"] = m_iEpisodeNumber; value["episodepart"] = m_iEpisodePart; - value["hastimer"] = HasTimer(); - value["hastimerrule"] = HasTimerRule(); - value["hasrecording"] = HasRecording(); - value["recording"] = recording ? recording->m_strFileNameAndPath : ""; + value["hastimer"] = false; // compat + value["hastimerrule"] = false; // compat + value["hasrecording"] = false; // compat + value["recording"] = ""; // compat value["isactive"] = IsActive(); value["wasactive"] = WasActive(); value["isseries"] = IsSeries(); value["serieslink"] = m_strSeriesLink; } -void CPVREpgInfoTag::ToSortable(SortItem& sortable, Field field) const +int CPVREpgInfoTag::ClientID() const { CSingleLock lock(m_critSection); + return m_channelData->ClientId(); +} - if (!m_channel) - return; +void CPVREpgInfoTag::ToSortable(SortItem& sortable, Field field) const +{ + CSingleLock lock(m_critSection); switch (field) { case FieldChannelName: - sortable[FieldChannelName] = m_channel->ChannelName(); + sortable[FieldChannelName] = m_channelData->ChannelName(); break; case FieldChannelNumber: - sortable[FieldChannelNumber] = m_channel->ChannelNumber().SortableChannelNumber(); + sortable[FieldChannelNumber] = m_channelData->SortableChannelNumber(); break; case FieldLastPlayed: - { - const CDateTime lastWatched(m_channel->LastWatched()); - sortable[FieldLastPlayed] = lastWatched.IsValid() ? lastWatched.GetAsDBDateTime() : StringUtils::Empty; + sortable[FieldLastPlayed] = m_channelData->LastWatched(); break; - } default: break; } @@ -200,7 +221,7 @@ void CPVREpgInfoTag::ToSortable(SortItem& sortable, Field field) const CDateTime CPVREpgInfoTag::GetCurrentPlayingTime() const { - if (CServiceBroker::GetPVRManager().GetPlayingChannel() == Channel()) + if (CServiceBroker::GetPVRManager().MatchPlayingChannel(ClientID(), UniqueChannelID())) { // start time valid? time_t startTime = CServiceBroker::GetDataCacheCore().GetStartTime(); @@ -277,9 +298,10 @@ int CPVREpgInfoTag::DatabaseID(void) const return m_iDatabaseID; } -unsigned int CPVREpgInfoTag::UniqueChannelID(void) const +int CPVREpgInfoTag::UniqueChannelID(void) const { - return m_iUniqueChannelID; + CSingleLock lock(m_critSection); + return m_channelData->UniqueClientChannelId(); } CDateTime CPVREpgInfoTag::StartAsUTC(void) const @@ -319,59 +341,24 @@ int CPVREpgInfoTag::GetDuration(void) const return end - start > 0 ? end - start : 3600; } -bool CPVREpgInfoTag::IsParentalLocked() const -{ - CPVRChannelPtr channel; - { - CSingleLock lock(m_critSection); - channel = m_channel; - } - - return channel && CServiceBroker::GetPVRManager().IsParentalLocked(channel); -} - -std::string CPVREpgInfoTag::Title(bool bOverrideParental /* = false */) const +std::string CPVREpgInfoTag::Title() const { - std::string strTitle; - - if (!bOverrideParental && IsParentalLocked()) - strTitle = g_localizeStrings.Get(19266); // parental locked - else if (m_strTitle.empty() && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_HIDENOINFOAVAILABLE)) - strTitle = g_localizeStrings.Get(19055); // no information available - else - strTitle = m_strTitle; - - return strTitle; + return m_strTitle; } -std::string CPVREpgInfoTag::PlotOutline(bool bOverrideParental /* = false */) const +std::string CPVREpgInfoTag::PlotOutline() const { - std::string retVal; - - if (bOverrideParental || !IsParentalLocked()) - retVal = m_strPlotOutline; - - return retVal; + return m_strPlotOutline; } -std::string CPVREpgInfoTag::Plot(bool bOverrideParental /* = false */) const +std::string CPVREpgInfoTag::Plot() const { - std::string retVal; - - if (bOverrideParental || !IsParentalLocked()) - retVal = m_strPlot; - - return retVal; + return m_strPlot; } -std::string CPVREpgInfoTag::OriginalTitle(bool bOverrideParental /* = false */) const +std::string CPVREpgInfoTag::OriginalTitle() const { - std::string retVal; - - if (bOverrideParental || !IsParentalLocked()) - retVal = m_strOriginalTitle; - - return retVal; + return m_strOriginalTitle; } const std::vector<std::string> CPVREpgInfoTag::Cast(void) const @@ -506,14 +493,9 @@ int CPVREpgInfoTag::EpisodePart(void) const return m_iEpisodePart; } -std::string CPVREpgInfoTag::EpisodeName(bool bOverrideParental /* = false */) const +std::string CPVREpgInfoTag::EpisodeName() const { - std::string retVal; - - if (bOverrideParental || !IsParentalLocked()) - retVal = m_strEpisodeName; - - return retVal; + return m_strEpisodeName; } std::string CPVREpgInfoTag::Icon(void) const @@ -526,149 +508,98 @@ std::string CPVREpgInfoTag::Path(void) const return m_strFileNameAndPath; } -bool CPVREpgInfoTag::HasTimer(void) const -{ - CSingleLock lock(m_critSection); - return m_timer != nullptr; -} - -bool CPVREpgInfoTag::HasTimerRule(void) const -{ - CSingleLock lock(m_critSection); - return m_timer && (m_timer->GetTimerRuleId() != PVR_TIMER_NO_PARENT); -} - -CPVRTimerInfoTagPtr CPVREpgInfoTag::Timer(void) const -{ - CSingleLock lock(m_critSection); - return m_timer; -} - -void CPVREpgInfoTag::SetChannel(const CPVRChannelPtr &channel) -{ - CSingleLock lock(m_critSection); - m_channel = channel; - m_iClientId = m_channel ? m_channel->ClientID() : -1; - m_iUniqueChannelID = m_channel ? m_channel->UniqueID() : PVR_CHANNEL_INVALID_UID; -} - -bool CPVREpgInfoTag::HasChannel(void) const -{ - CSingleLock lock(m_critSection); - return m_channel != nullptr; -} - -const CPVRChannelPtr CPVREpgInfoTag::Channel() const -{ - CSingleLock lock(m_critSection); - return m_channel; -} - bool CPVREpgInfoTag::Update(const CPVREpgInfoTag &tag, bool bUpdateBroadcastId /* = true */) { - bool bChanged = false; - { - CSingleLock lock(m_critSection); - bChanged = (m_channel != tag.m_channel); - } + CSingleLock lock(m_critSection); + bool bChanged = ( + m_strTitle != tag.m_strTitle || + m_strPlotOutline != tag.m_strPlotOutline || + m_strPlot != tag.m_strPlot || + m_strOriginalTitle != tag.m_strOriginalTitle || + m_cast != tag.m_cast || + m_directors != tag.m_directors || + m_writers != tag.m_writers || + m_iYear != tag.m_iYear || + m_strIMDBNumber != tag.m_strIMDBNumber || + m_startTime != tag.m_startTime || + m_endTime != tag.m_endTime || + m_iGenreType != tag.m_iGenreType || + m_iGenreSubType != tag.m_iGenreSubType || + m_firstAired != tag.m_firstAired || + m_iParentalRating != tag.m_iParentalRating || + m_iStarRating != tag.m_iStarRating || + m_bNotify != tag.m_bNotify || + m_iEpisodeNumber != tag.m_iEpisodeNumber || + m_iEpisodePart != tag.m_iEpisodePart || + m_iSeriesNumber != tag.m_iSeriesNumber || + m_strEpisodeName != tag.m_strEpisodeName || + m_iUniqueBroadcastID != tag.m_iUniqueBroadcastID || + m_iEpgID != tag.m_iEpgID || + m_genre != tag.m_genre || + m_strIconPath != tag.m_strIconPath || + m_iFlags != tag.m_iFlags || + m_strSeriesLink != tag.m_strSeriesLink || + m_channelData != tag.m_channelData + ); + + if (bUpdateBroadcastId) + bChanged |= (m_iDatabaseID != tag.m_iDatabaseID); + if (bChanged) { - bChanged |= ( - m_iClientId != tag.m_iClientId || - m_strTitle != tag.m_strTitle || - m_strPlotOutline != tag.m_strPlotOutline || - m_strPlot != tag.m_strPlot || - m_strOriginalTitle != tag.m_strOriginalTitle || - m_cast != tag.m_cast || - m_directors != tag.m_directors || - m_writers != tag.m_writers || - m_iYear != tag.m_iYear || - m_strIMDBNumber != tag.m_strIMDBNumber || - m_startTime != tag.m_startTime || - m_endTime != tag.m_endTime || - m_iGenreType != tag.m_iGenreType || - m_iGenreSubType != tag.m_iGenreSubType || - m_firstAired != tag.m_firstAired || - m_iParentalRating != tag.m_iParentalRating || - m_iStarRating != tag.m_iStarRating || - m_bNotify != tag.m_bNotify || - m_iEpisodeNumber != tag.m_iEpisodeNumber || - m_iEpisodePart != tag.m_iEpisodePart || - m_iSeriesNumber != tag.m_iSeriesNumber || - m_strEpisodeName != tag.m_strEpisodeName || - m_iUniqueBroadcastID != tag.m_iUniqueBroadcastID || - m_iUniqueChannelID != tag.m_iUniqueChannelID || - EpgID() != tag.EpgID() || - m_genre != tag.m_genre || - m_strIconPath != tag.m_strIconPath || - m_iFlags != tag.m_iFlags || - m_strSeriesLink != tag.m_strSeriesLink - ); if (bUpdateBroadcastId) - bChanged |= (m_iDatabaseID != tag.m_iDatabaseID); - - if (bChanged) + m_iDatabaseID = tag.m_iDatabaseID; + + m_strTitle = tag.m_strTitle; + m_strPlotOutline = tag.m_strPlotOutline; + m_strPlot = tag.m_strPlot; + m_strOriginalTitle = tag.m_strOriginalTitle; + m_cast = tag.m_cast; + m_directors = tag.m_directors; + m_writers = tag.m_writers; + m_iYear = tag.m_iYear; + m_strIMDBNumber = tag.m_strIMDBNumber; + m_startTime = tag.m_startTime; + m_endTime = tag.m_endTime; + m_iGenreType = tag.m_iGenreType; + m_iGenreSubType = tag.m_iGenreSubType; + m_iEpgID = tag.m_iEpgID; + m_iFlags = tag.m_iFlags; + m_strSeriesLink = tag.m_strSeriesLink; + + if (m_iGenreType == EPG_GENRE_USE_STRING) { - if (bUpdateBroadcastId) - m_iDatabaseID = tag.m_iDatabaseID; - - m_iClientId = tag.m_iClientId; - m_strTitle = tag.m_strTitle; - m_strPlotOutline = tag.m_strPlotOutline; - m_strPlot = tag.m_strPlot; - m_strOriginalTitle = tag.m_strOriginalTitle; - m_cast = tag.m_cast; - m_directors = tag.m_directors; - m_writers = tag.m_writers; - m_iYear = tag.m_iYear; - m_strIMDBNumber = tag.m_strIMDBNumber; - m_startTime = tag.m_startTime; - m_endTime = tag.m_endTime; - m_iGenreType = tag.m_iGenreType; - m_iGenreSubType = tag.m_iGenreSubType; - m_epg = tag.m_epg; - m_iFlags = tag.m_iFlags; - m_strSeriesLink = tag.m_strSeriesLink; - - { - CSingleLock lock(m_critSection); - m_channel = tag.m_channel; - } - - if (m_iGenreType == EPG_GENRE_USE_STRING) - { - /* No type/subtype. Use the provided description */ - m_genre = tag.m_genre; - } - else - { - /* Determine genre description by type/subtype */ - m_genre = StringUtils::Split(CPVREpg::ConvertGenreIdToString(tag.m_iGenreType, tag.m_iGenreSubType), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); - } - m_firstAired = tag.m_firstAired; - m_iParentalRating = tag.m_iParentalRating; - m_iStarRating = tag.m_iStarRating; - m_bNotify = tag.m_bNotify; - m_iEpisodeNumber = tag.m_iEpisodeNumber; - m_iEpisodePart = tag.m_iEpisodePart; - m_iSeriesNumber = tag.m_iSeriesNumber; - m_strEpisodeName = tag.m_strEpisodeName; - m_iUniqueBroadcastID = tag.m_iUniqueBroadcastID; - m_iUniqueChannelID = tag.m_iUniqueChannelID; - m_strIconPath = tag.m_strIconPath; + /* No type/subtype. Use the provided description */ + m_genre = tag.m_genre; } + else + { + /* Determine genre description by type/subtype */ + m_genre = StringUtils::Split(CPVREpg::ConvertGenreIdToString(tag.m_iGenreType, tag.m_iGenreSubType), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); + } + m_firstAired = tag.m_firstAired; + m_iParentalRating = tag.m_iParentalRating; + m_iStarRating = tag.m_iStarRating; + m_bNotify = tag.m_bNotify; + m_iEpisodeNumber = tag.m_iEpisodeNumber; + m_iEpisodePart = tag.m_iEpisodePart; + m_iSeriesNumber = tag.m_iSeriesNumber; + m_strEpisodeName = tag.m_strEpisodeName; + m_iUniqueBroadcastID = tag.m_iUniqueBroadcastID; + m_strIconPath = tag.m_strIconPath; + m_channelData = tag.m_channelData; } + if (bChanged) UpdatePath(); return bChanged; } -bool CPVREpgInfoTag::Persist(bool bSingleUpdate /* = true */) +bool CPVREpgInfoTag::Persist(const std::shared_ptr<CPVREpgDatabase>& database, bool bSingleUpdate /* = true */) { bool bReturn = false; - const CPVREpgDatabasePtr database = CServiceBroker::GetPVRManager().EpgContainer().GetEpgDatabase(); if (!database) { CLog::LogF(LOGERROR, "Could not open the EPG database"); @@ -690,7 +621,9 @@ bool CPVREpgInfoTag::Persist(bool bSingleUpdate /* = true */) std::vector<PVR_EDL_ENTRY> CPVREpgInfoTag::GetEdl() const { std::vector<PVR_EDL_ENTRY> edls; - const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(m_iClientId); + + CSingleLock lock(m_critSection); + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId()); if (client && client->GetClientCapabilities().SupportsEpgTagEdl()) client->GetEpgTagEdl(shared_from_this(), edls); @@ -705,55 +638,21 @@ void CPVREpgInfoTag::UpdatePath(void) int CPVREpgInfoTag::EpgID(void) const { - return m_epg ? m_epg->EpgID() : -1; -} - -void CPVREpgInfoTag::SetTimer(const CPVRTimerInfoTagPtr &timer) -{ - CSingleLock lock(m_critSection); - m_timer = timer; -} - -void CPVREpgInfoTag::ClearTimer(void) -{ - CPVRTimerInfoTagPtr previousTag; - { - CSingleLock lock(m_critSection); - previousTag = std::move(m_timer); - } - - if (previousTag) - previousTag->ClearEpgTag(); + return m_iEpgID; } -void CPVREpgInfoTag::SetRecording(const CPVRRecordingPtr &recording) +void CPVREpgInfoTag::SetEpgID(int iEpgID) { - CSingleLock lock(m_critSection); - m_recording = recording; -} - -void CPVREpgInfoTag::ClearRecording(void) -{ - CSingleLock lock(m_critSection); - m_recording.reset(); -} - -bool CPVREpgInfoTag::HasRecording(void) const -{ - CSingleLock lock(m_critSection); - return m_recording != nullptr; -} - -CPVRRecordingPtr CPVREpgInfoTag::Recording(void) const -{ - CSingleLock lock(m_critSection); - return m_recording; + m_iEpgID = iEpgID; + UpdatePath(); // Note: path contains epg id. } bool CPVREpgInfoTag::IsRecordable(void) const { bool bIsRecordable = false; - const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(m_iClientId); + + CSingleLock lock(m_critSection); + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId()); if (!client || (client->IsRecordable(shared_from_this(), bIsRecordable) != PVR_ERROR_NO_ERROR)) { // event end time based fallback @@ -765,7 +664,9 @@ bool CPVREpgInfoTag::IsRecordable(void) const bool CPVREpgInfoTag::IsPlayable(void) const { bool bIsPlayable = false; - const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(m_iClientId); + + CSingleLock lock(m_critSection); + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId()); if (!client || (client->IsPlayable(shared_from_this(), bIsPlayable) != PVR_ERROR_NO_ERROR)) { // fallback @@ -774,12 +675,6 @@ bool CPVREpgInfoTag::IsPlayable(void) const return bIsPlayable; } -void CPVREpgInfoTag::SetEpg(CPVREpg *epg) -{ - m_epg = epg; - UpdatePath(); // Note: path contains epg id. -} - bool CPVREpgInfoTag::IsSeries(void) const { if ((m_iFlags & EPG_TAG_FLAG_IS_SERIES) > 0 || SeriesNumber() > 0 || EpisodeNumber() > 0 || EpisodePart() > 0) @@ -788,6 +683,18 @@ bool CPVREpgInfoTag::IsSeries(void) const return false; } +bool CPVREpgInfoTag::IsRadio() const +{ + CSingleLock lock(m_critSection); + return m_channelData->IsRadio(); +} + +bool CPVREpgInfoTag::IsParentalLocked() const +{ + CSingleLock lock(m_critSection); + return m_channelData->IsLocked(); +} + const std::vector<std::string> CPVREpgInfoTag::Tokenize(const std::string &str) { return StringUtils::Split(str.c_str(), EPG_STRING_TOKEN_SEPARATOR); diff --git a/xbmc/pvr/epg/EpgInfoTag.h b/xbmc/pvr/epg/EpgInfoTag.h index 75625d2543..6166f667fe 100644 --- a/xbmc/pvr/epg/EpgInfoTag.h +++ b/xbmc/pvr/epg/EpgInfoTag.h @@ -14,19 +14,17 @@ #include "XBDateTime.h" #include "addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h" +#include "threads/CriticalSection.h" #include "utils/ISerializable.h" #include "utils/ISortable.h" #include "pvr/PVRTypes.h" -#include "pvr/channels/PVRChannel.h" -#include "pvr/recordings/PVRRecording.h" -#include "pvr/timers/PVRTimerInfoTag.h" class CVariant; namespace PVR { - class CPVREpg; + class CPVREpgChannelData; class CPVREpgInfoTag final : public ISerializable, public ISortable, public std::enable_shared_from_this<CPVREpgInfoTag> { @@ -38,16 +36,23 @@ namespace PVR * @brief Create a new EPG infotag. * @param data The tag's data. * @param iClientId The client id. + * @param channelData The channel data. + * @param iEpgId The id of the EPG this tag belongs to. */ - CPVREpgInfoTag(const EPG_TAG &data, int iClientId); + CPVREpgInfoTag(const EPG_TAG& data, int iClientId, const std::shared_ptr<CPVREpgChannelData>& channelData, int iEpgID); /*! * @brief Create a new EPG infotag. - * @param channel The channel. - * @param epg The epg data. - * @param strTabelName The name of the epg database table. + * @param channelData The channel data. + * @param iEpgId The id of the EPG this tag belongs to. */ - CPVREpgInfoTag(const CPVRChannelPtr &channel, CPVREpg *epg = nullptr, const std::string &strTableName = ""); + CPVREpgInfoTag(const std::shared_ptr<CPVREpgChannelData>& channelData, int iEpgID); + + /*! + * @brief Set data for the channel linked to this EPG infotag. + * @param data The channel data. + */ + void SetChannelData(const std::shared_ptr<CPVREpgChannelData>& data); bool operator ==(const CPVREpgInfoTag& right) const; bool operator !=(const CPVREpgInfoTag& right) const; @@ -62,7 +67,7 @@ namespace PVR * @brief Get the identifier of the client that serves this event. * @return The identifier. */ - int ClientID(void) const { return m_iClientId; } + int ClientID() const; /*! * @brief Check if this event is currently active. @@ -101,10 +106,10 @@ namespace PVR int EpgID(void) const; /*! - * @brief Sets the EPG for this event. - * @param epg The epg. + * @brief Sets the EPG id for this event. + * @param iEpgID The EPG id. */ - void SetEpg(CPVREpg *epg); + void SetEpgID(int iEpgID); /*! * @brief Change the unique broadcast ID of this event. @@ -128,7 +133,7 @@ namespace PVR * @brief Get the unique ID of the channel associated with this event. * @return The unique channel ID. */ - unsigned int UniqueChannelID(void) const; + int UniqueChannelID(void) const; /*! * @brief Get the event's start time. @@ -168,31 +173,27 @@ namespace PVR /*! * @brief Get the title of this event. - * @param bOverrideParental True to override parental control, false to check it. * @return The title. */ - std::string Title(bool bOverrideParental = false) const; + std::string Title() const; /*! * @brief Get the plot outline of this event. - * @param bOverrideParental True to override parental control, false to check it. * @return The plot outline. */ - std::string PlotOutline(bool bOverrideParental = false) const; + std::string PlotOutline() const; /*! * @brief Get the plot of this event. - * @param bOverrideParental True to override parental control, false to check it. * @return The plot. */ - std::string Plot(bool bOverrideParental = false) const; + std::string Plot() const; /*! * @brief Get the original title of this event. - * @param bOverrideParental True to override parental control, false check it. * @return The original title. */ - std::string OriginalTitle(bool bOverrideParental = false) const; + std::string OriginalTitle() const; /*! * @brief Get the cast of this event. @@ -322,10 +323,9 @@ namespace PVR /*! * @brief The episode name of this event. - * @param bOverrideParental True to override parental control, false to check it. * @return The episode name. */ - std::string EpisodeName(bool bOverrideParental = false) const; + std::string EpisodeName() const; /*! * @brief Get the path to the icon for this event. @@ -340,58 +340,6 @@ namespace PVR std::string Path(void) const; /*! - * @brief Set a timer for this event. - * @param timer The timer. - */ - void SetTimer(const CPVRTimerInfoTagPtr &timer); - - /*! - * @brief Clear the timer for this event. - */ - void ClearTimer(void); - - /*! - * @brief Check whether this event has a timer tag. - * @return True if it has a timer tag, false if not. - */ - bool HasTimer(void) const; - - /*! - * @brief Check whether this event has a timer rule. - * @return True if it has a timer rule, false if not. - */ - bool HasTimerRule(void) const; - - /*! - * @brief Get the timer for this event, if any. - * @return The timer or nullptr if there is none. - */ - CPVRTimerInfoTagPtr Timer(void) const; - - /*! - * @brief Set a recording for this event. - * @param recording The recording. - */ - void SetRecording(const CPVRRecordingPtr &recording); - - /*! - * @brief Clear a recording for this event. - */ - void ClearRecording(void); - - /*! - * @brief Check whether this event has a recording. - * @return True if it has a recording, false if not. - */ - bool HasRecording(void) const; - - /*! - * @brief Get the recording for this event, if any. - * @return The pointer or nullptr if there is none. - */ - CPVRRecordingPtr Recording(void) const; - - /*! * @brief Check if this event can be recorded. * @return True if it can be recorded, false otherwise. */ @@ -404,29 +352,12 @@ namespace PVR bool IsPlayable(void) const; /*! - * @brief Set the channel of this epg tag - * @param channel The channel - */ - void SetChannel(const CPVRChannelPtr &channel); - - /*! - * @brief Check whether this event has a channel. - * @return True if it has a channel, false if not. - */ - bool HasChannel(void) const; - - /*! - * @brief Get the channel for this event. - * @return The channel. - */ - const CPVRChannelPtr Channel(void) const; - - /*! - * @brief Persist this tag in the database. + * @brief Persist this tag in the given database. + * @param database The database. * @param bSingleUpdate True if this is a single update, false if more updates will follow. * @return True if the tag was persisted correctly, false otherwise. */ - bool Persist(bool bSingleUpdate = true); + bool Persist(const std::shared_ptr<CPVREpgDatabase>& database, bool bSingleUpdate = true); /*! * @brief Update the information in this tag with the info in the given tag. @@ -449,6 +380,18 @@ namespace PVR bool IsSeries() const; /*! + * @brief Check whether this tag is associated with a radion or TV channel. + * @return True if this tag is associated with a radio channel, false otherwise. + */ + bool IsRadio() const; + + /*! + * @brief Check whether this event is parental locked. + * @return True if whether this event is parental locked, false otherwise. + */ + bool IsParentalLocked() const; + + /*! * @brief Return the flags (EPG_TAG_FLAG_*) of this event as a bitfield. * @return the flags. */ @@ -469,18 +412,12 @@ namespace PVR static const std::string DeTokenize(const std::vector<std::string> &tokens); private: - CPVREpgInfoTag() = default; + CPVREpgInfoTag(); CPVREpgInfoTag(const CPVREpgInfoTag &tag) = delete; CPVREpgInfoTag &operator =(const CPVREpgInfoTag &other) = delete; /*! - * @brief Check whether this event is parental locked. - * @return True if whether this event is parental locked, false otherwise. - */ - bool IsParentalLocked() const; - - /*! * @brief Change the genre of this event. * @param iGenreType The genre type ID. * @param iGenreSubType The genre subtype ID. @@ -499,7 +436,6 @@ namespace PVR CDateTime GetCurrentPlayingTime(void) const; bool m_bNotify = false; /*!< notify on start */ - int m_iClientId = -1; /*!< client id */ int m_iDatabaseID = -1; /*!< database ID */ int m_iGenreType = 0; /*!< genre type */ int m_iGenreSubType = 0; /*!< genre subtype */ @@ -509,7 +445,6 @@ namespace PVR int m_iEpisodeNumber = 0; /*!< episode number */ int m_iEpisodePart = 0; /*!< episode part number */ unsigned int m_iUniqueBroadcastID = EPG_TAG_INVALID_UID; /*!< unique broadcast ID */ - unsigned int m_iUniqueChannelID = PVR_CHANNEL_INVALID_UID; /*!< unique channel ID */ std::string m_strTitle; /*!< title */ std::string m_strPlotOutline; /*!< plot outline */ std::string m_strPlot; /*!< plot */ @@ -530,9 +465,7 @@ namespace PVR std::string m_strSeriesLink; /*!< series link */ mutable CCriticalSection m_critSection; - CPVREpg *m_epg = nullptr; - CPVRChannelPtr m_channel; - CPVRTimerInfoTagPtr m_timer; - CPVRRecordingPtr m_recording; + std::shared_ptr<CPVREpgChannelData> m_channelData; + int m_iEpgID = -1; }; } diff --git a/xbmc/pvr/epg/EpgSearchFilter.cpp b/xbmc/pvr/epg/EpgSearchFilter.cpp index 7660fd43ab..829d8daae8 100644 --- a/xbmc/pvr/epg/EpgSearchFilter.cpp +++ b/xbmc/pvr/epg/EpgSearchFilter.cpp @@ -111,9 +111,11 @@ bool CPVREpgSearchFilter::MatchSearchTerm(const CPVREpgInfoTagPtr &tag) const if (!m_strSearchTerm.empty()) { CTextSearch search(m_strSearchTerm, m_bIsCaseSensitive, SEARCH_DEFAULT_OR); - bReturn = search.Search(tag->Title()) || - search.Search(tag->PlotOutline()) || - (m_bSearchInDescription && search.Search(tag->Plot())); + bReturn = !CServiceBroker::GetPVRManager().IsParentalLocked(tag); + if (bReturn) + bReturn = search.Search(tag->Title()) || + search.Search(tag->PlotOutline()) || + (m_bSearchInDescription && search.Search(tag->Plot())); } return bReturn; @@ -136,63 +138,42 @@ bool CPVREpgSearchFilter::FilterEntry(const CPVREpgInfoTagPtr &tag) const MatchSearchTerm(tag) && MatchTimers(tag) && MatchRecordings(tag)) && - (!tag->HasChannel() || - (MatchChannelType(tag) && - MatchChannelNumber(tag) && - MatchChannelGroup(tag) && - MatchFreeToAir(tag))); + MatchChannelType(tag) && + MatchChannelNumber(tag) && + MatchChannelGroup(tag) && + MatchFreeToAir(tag); } -int CPVREpgSearchFilter::RemoveDuplicates(CFileItemList &results) +void CPVREpgSearchFilter::RemoveDuplicates(std::vector<std::shared_ptr<CPVREpgInfoTag>>& results) { - unsigned int iSize = results.Size(); - - for (unsigned int iResultPtr = 0; iResultPtr < iSize; iResultPtr++) + for (auto it = results.begin(); it != results.end();) { - const CPVREpgInfoTagPtr epgentry_1(results.Get(iResultPtr)->GetEPGInfoTag()); - if (!epgentry_1) - continue; - - for (unsigned int iTagPtr = 0; iTagPtr < iSize; iTagPtr++) - { - if (iResultPtr == iTagPtr) - continue; - - const CPVREpgInfoTagPtr epgentry_2(results.Get(iTagPtr)->GetEPGInfoTag()); - if (!epgentry_2) - continue; - - if (epgentry_1->Title() != epgentry_2->Title() || - epgentry_1->Plot() != epgentry_2->Plot() || - epgentry_1->PlotOutline() != epgentry_2->PlotOutline()) - continue; - - results.Remove(iTagPtr); - iResultPtr--; - iTagPtr--; - iSize--; - } + it = results.erase(std::remove_if(results.begin(), + results.end(), + [&it](const std::shared_ptr<CPVREpgInfoTag>& entry) + { + return *it != entry && + (*it)->Title() == entry->Title() && + (*it)->Plot() == entry->Plot() && + (*it)->PlotOutline() == entry->PlotOutline(); + }), + results.end()); } - - return iSize; } bool CPVREpgSearchFilter::MatchChannelType(const CPVREpgInfoTagPtr &tag) const { - return (CServiceBroker::GetPVRManager().IsStarted() && tag->Channel()->IsRadio() == m_bIsRadio); + return tag && (tag->IsRadio() == m_bIsRadio); } bool CPVREpgSearchFilter::MatchChannelNumber(const CPVREpgInfoTagPtr &tag) const { bool bReturn(true); - if (m_channelNumber.IsValid() && CServiceBroker::GetPVRManager().IsStarted()) + if (m_channelNumber.IsValid()) { - CPVRChannelGroupPtr group = (m_iChannelGroup != EPG_SEARCH_UNSET) ? CServiceBroker::GetPVRManager().ChannelGroups()->GetByIdFromAll(m_iChannelGroup) : CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAllTV(); - if (!group) - group = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAllTV(); - - bReturn = (m_channelNumber == group->GetChannelNumber(tag->Channel())); + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag); + bReturn = channel && (m_channelNumber == channel->ChannelNumber()); } return bReturn; @@ -202,10 +183,14 @@ bool CPVREpgSearchFilter::MatchChannelGroup(const CPVREpgInfoTagPtr &tag) const { bool bReturn(true); - if (m_iChannelGroup != EPG_SEARCH_UNSET && CServiceBroker::GetPVRManager().IsStarted()) + if (m_iChannelGroup != EPG_SEARCH_UNSET) { CPVRChannelGroupPtr group = CServiceBroker::GetPVRManager().ChannelGroups()->GetByIdFromAll(m_iChannelGroup); - bReturn = (group && group->IsGroupMember(tag->Channel())); + if (group) + { + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag); + bReturn = (channel && group->IsGroupMember(channel)); + } } return bReturn; @@ -213,7 +198,13 @@ bool CPVREpgSearchFilter::MatchChannelGroup(const CPVREpgInfoTagPtr &tag) const bool CPVREpgSearchFilter::MatchFreeToAir(const CPVREpgInfoTagPtr &tag) const { - return (!m_bFreeToAirOnly || !tag->Channel()->IsEncrypted()); + if (m_bFreeToAirOnly) + { + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag); + return channel && !channel->IsEncrypted(); + } + + return true; } bool CPVREpgSearchFilter::MatchTimers(const CPVREpgInfoTagPtr &tag) const diff --git a/xbmc/pvr/epg/EpgSearchFilter.h b/xbmc/pvr/epg/EpgSearchFilter.h index 47b135d58d..597ab8e392 100644 --- a/xbmc/pvr/epg/EpgSearchFilter.h +++ b/xbmc/pvr/epg/EpgSearchFilter.h @@ -8,13 +8,15 @@ #pragma once +#include <memory> +#include <string> +#include <vector> + #include "XBDateTime.h" #include "pvr/PVRTypes.h" #include "pvr/channels/PVRChannelNumber.h" -class CFileItemList; - namespace PVR { #define EPG_SEARCH_UNSET (-1) @@ -46,10 +48,9 @@ namespace PVR /*! * @brief remove duplicates from a list of epg tags. - * @param results the list of epg tags. - * @return the number of items in the list after removing duplicates. + * @param results The list of epg tags. */ - static int RemoveDuplicates(CFileItemList &results); + static void RemoveDuplicates(std::vector<std::shared_ptr<CPVREpgInfoTag>>& results); /*! * @brief Get the type of channels to search. diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp index b50f68e0d7..33d3c7f4f4 100644 --- a/xbmc/pvr/recordings/PVRRecording.cpp +++ b/xbmc/pvr/recordings/PVRRecording.cpp @@ -217,25 +217,7 @@ void CPVRRecording::Reset(void) bool CPVRRecording::Delete(void) { CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(m_iClientId); - if (!client || client->DeleteRecording(*this) != PVR_ERROR_NO_ERROR) - return false; - - OnDelete(); - return true; -} - -void CPVRRecording::OnDelete(void) -{ - if (m_iEpgEventId != EPG_TAG_INVALID_UID) - { - const CPVRChannelPtr channel(Channel()); - if (channel) - { - const CPVREpgInfoTagPtr epgTag(CServiceBroker::GetPVRManager().EpgContainer().GetTagById(channel, m_iEpgEventId)); - if (epgTag) - epgTag->ClearRecording(); - } - } + return client && (client->DeleteRecording(*this) == PVR_ERROR_NO_ERROR); } bool CPVRRecording::Undelete(void) @@ -410,9 +392,6 @@ void CPVRRecording::Update(const CPVRRecording &tag) m_strShowTitle = strEpisode; } - if (m_bIsDeleted) - OnDelete(); - UpdatePath(); } @@ -483,13 +462,38 @@ int CPVRRecording::ClientID(void) const return m_iClientId; } +std::shared_ptr<CPVRTimerInfoTag> CPVRRecording::GetRecordingTimer() const +{ + const std::vector<std::shared_ptr<CPVRTimerInfoTag>> recordingTimers = CServiceBroker::GetPVRManager().Timers()->GetActiveRecordings(); + + for (const auto& timer : recordingTimers) + { + if (timer->m_iClientId == ClientID() && + timer->m_iClientChannelUid == ChannelUid()) + { + // first, match epg event uids, if available + if (timer->UniqueBroadcastID() == BroadcastUid() && + timer->UniqueBroadcastID() != EPG_TAG_INVALID_UID) + return timer; + + // alternatively, match start and end times + const CDateTime timerStart = timer->StartAsUTC() - CDateTimeSpan(0, 0, timer->m_iMarginStart, 0); + const CDateTime timerEnd = timer->EndAsUTC() + CDateTimeSpan(0, 0, timer->m_iMarginEnd, 0); + if (timerStart <= RecordingTimeAsUTC() && + timerEnd >= EndTimeAsUTC()) + return timer; + } + } + return {}; +} + bool CPVRRecording::IsInProgress() const { // Note: It is not enough to only check recording time and duration against 'now'. // Only the state of the related timer is a safe indicator that the backend // actually is recording this. - return CServiceBroker::GetPVRManager().Timers()->HasRecordingTimerForRecording(*this); + return GetRecordingTimer() != nullptr; } void CPVRRecording::SetGenre(int iGenreType, int iGenreSubType, const std::string &strGenre) diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h index 72e57eda8e..d038343187 100644 --- a/xbmc/pvr/recordings/PVRRecording.h +++ b/xbmc/pvr/recordings/PVRRecording.h @@ -95,11 +95,6 @@ namespace PVR bool Delete(void); /*! - * @brief Called when this recording has been deleted - */ - void OnDelete(void); - - /*! * @brief Undelete this recording on the client (if supported). * @return True if it was undeleted successfully, false otherwise. */ @@ -281,6 +276,12 @@ namespace PVR bool IsInProgress() const; /*! + * @brief return the timer for an in-progress recording, if any + * @return the timer if the recording is in progress, nullptr otherwise + */ + std::shared_ptr<CPVRTimerInfoTag> GetRecordingTimer() const; + + /*! * @brief set the genre for this recording. * @param iGenreType The genre type ID. If set to EPG_GENRE_USE_STRING, set genre to the value provided with strGenre. Otherwise, compile the genre string from the values given by iGenreType and iGenreSubType * @param iGenreSubType The genre subtype ID diff --git a/xbmc/pvr/recordings/PVRRecordings.cpp b/xbmc/pvr/recordings/PVRRecordings.cpp index 492f5412ed..777e5a1320 100644 --- a/xbmc/pvr/recordings/PVRRecordings.cpp +++ b/xbmc/pvr/recordings/PVRRecordings.cpp @@ -395,16 +395,6 @@ void CPVRRecordings::UpdateFromClient(const CPVRRecordingPtr &tag) { newTag = CPVRRecordingPtr(new CPVRRecording); newTag->Update(*tag); - if (newTag->BroadcastUid() != EPG_TAG_INVALID_UID) - { - const CPVRChannelPtr channel(newTag->Channel()); - if (channel) - { - const CPVREpgInfoTagPtr epgTag = CServiceBroker::GetPVRManager().EpgContainer().GetTagById(channel, newTag->BroadcastUid()); - if (epgTag) - epgTag->SetRecording(newTag); - } - } newTag->m_iRecordingId = ++m_iLastId; m_recordings.insert(std::make_pair(CPVRRecordingUid(newTag->m_iClientId, newTag->m_strRecordingId), newTag)); if (newTag->IsRadio()) @@ -416,6 +406,9 @@ void CPVRRecordings::UpdateFromClient(const CPVRRecordingPtr &tag) CPVRRecordingPtr CPVRRecordings::GetRecordingForEpgTag(const CPVREpgInfoTagPtr &epgTag) const { + if (!epgTag) + return {}; + CSingleLock lock(m_critSection); for (const auto recording : m_recordings) @@ -423,25 +416,21 @@ CPVRRecordingPtr CPVRRecordings::GetRecordingForEpgTag(const CPVREpgInfoTagPtr & if (recording.second->IsDeleted()) continue; + if (recording.second->ClientID() != epgTag->ClientID()) + continue; + + if (recording.second->ChannelUid() != epgTag->UniqueChannelID()) + continue; + unsigned int iEpgEvent = recording.second->BroadcastUid(); if (iEpgEvent != EPG_TAG_INVALID_UID) { if (iEpgEvent == epgTag->UniqueBroadcastID()) - { - // uid matches. perfect. return recording.second; - } } else { - // uid is optional, so check other relevant data. - - // note: don't use recording.second->Channel() for comparing channels here as this can lead - // to deadlocks. compare client ids and channel ids instead, this has the same effect. - if (epgTag->Channel() && - recording.second->ClientID() == epgTag->Channel()->ClientID() && - recording.second->ChannelUid() == epgTag->Channel()->UniqueID() && - recording.second->RecordingTimeAsUTC() <= epgTag->StartAsUTC() && + if (recording.second->RecordingTimeAsUTC() <= epgTag->StartAsUTC() && recording.second->EndTimeAsUTC() >= epgTag->EndAsUTC()) return recording.second; } diff --git a/xbmc/pvr/timers/CMakeLists.txt b/xbmc/pvr/timers/CMakeLists.txt index 52026ef0b8..fd4f19ce68 100644 --- a/xbmc/pvr/timers/CMakeLists.txt +++ b/xbmc/pvr/timers/CMakeLists.txt @@ -1,9 +1,11 @@ set(SOURCES PVRTimerInfoTag.cpp PVRTimers.cpp + PVRTimersPath.cpp PVRTimerType.cpp) set(HEADERS PVRTimerInfoTag.h PVRTimers.h + PVRTimersPath.h PVRTimerType.h) core_add_library(pvr_timers) diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.cpp b/xbmc/pvr/timers/PVRTimerInfoTag.cpp index 363ebd6c7c..7dcf26efad 100644 --- a/xbmc/pvr/timers/PVRTimerInfoTag.cpp +++ b/xbmc/pvr/timers/PVRTimerInfoTag.cpp @@ -23,7 +23,7 @@ #include "pvr/addons/PVRClients.h" #include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/Epg.h" -#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimersPath.h" using namespace PVR; @@ -189,10 +189,14 @@ bool CPVRTimerInfoTag::operator ==(const CPVRTimerInfoTag& right) const m_iTimerId == right.m_iTimerId && m_strSeriesLink == right.m_strSeriesLink && m_iEpgUid == right.m_iEpgUid && - m_iActiveChildTimers == right.m_iActiveChildTimers && - m_bHasChildConflictNOK== right.m_bHasChildConflictNOK && - m_bHasChildRecording == right.m_bHasChildRecording && - m_bHasChildErrors == right.m_bHasChildErrors); + m_iActiveTVChildTimers == right.m_iActiveTVChildTimers && + m_iActiveRadioChildTimers == right.m_iActiveRadioChildTimers && + m_bHasTVChildConflictNOK == right.m_bHasTVChildConflictNOK && + m_bHasRadioChildConflictNOK == right.m_bHasRadioChildConflictNOK && + m_bHasTVChildRecording == right.m_bHasTVChildRecording && + m_bHasRadioChildRecording == right.m_bHasRadioChildRecording && + m_bHasTVChildErrors == right.m_bHasTVChildErrors && + m_bHasRadioChildErrors == right.m_bHasRadioChildErrors); } /** @@ -291,13 +295,6 @@ void CPVRTimerInfoTag::Serialize(CVariant &value) const value["serieslink"] = m_strSeriesLink; } -void CPVRTimerInfoTag::UpdateEpgInfoTag(void) -{ - CSingleLock lock(m_critSection); - m_epgTag.reset(); - GetEpgInfoTag(); -} - void CPVRTimerInfoTag::UpdateSummary(void) { CSingleLock lock(m_critSection); @@ -362,7 +359,7 @@ void CPVRTimerInfoTag::SetTimerType(const CPVRTimerTypePtr &type) /** * Get the status string of this Timer, is used by the GUIInfoManager */ -std::string CPVRTimerInfoTag::GetStatus() const +std::string CPVRTimerInfoTag::GetStatus(bool bRadio) const { std::string strReturn = g_localizeStrings.Get(305); CSingleLock lock(m_critSection); @@ -381,20 +378,21 @@ std::string CPVRTimerInfoTag::GetStatus() const else if (m_state == PVR_TIMER_STATE_DISABLED) strReturn = g_localizeStrings.Get(13106); else if (m_state == PVR_TIMER_STATE_COMPLETED) - if (m_bHasChildRecording) + if ((m_bHasTVChildRecording && !bRadio) || (m_bHasRadioChildRecording && bRadio)) strReturn = g_localizeStrings.Get(19162); // "Recording active" else strReturn = g_localizeStrings.Get(19256); // "Completed" else if (m_state == PVR_TIMER_STATE_SCHEDULED || m_state == PVR_TIMER_STATE_NEW) { - if (m_bHasChildRecording) + if ((m_bHasTVChildRecording && !bRadio) || (m_bHasRadioChildRecording && bRadio)) strReturn = g_localizeStrings.Get(19162); // "Recording active" - else if (m_bHasChildErrors) + else if ((m_bHasTVChildErrors && !bRadio) || (m_bHasRadioChildErrors && bRadio)) strReturn = g_localizeStrings.Get(257); // "Error" - else if (m_bHasChildConflictNOK) + else if ((m_bHasTVChildConflictNOK && !bRadio) || (m_bHasRadioChildConflictNOK && bRadio)) strReturn = g_localizeStrings.Get(19276); // "Conflict error" - else if (m_iActiveChildTimers > 0) - strReturn = StringUtils::Format(g_localizeStrings.Get(19255).c_str(), m_iActiveChildTimers); // "%d scheduled" + else if ((m_iActiveTVChildTimers > 0 && !bRadio) || (m_iActiveRadioChildTimers > 0 && bRadio)) + strReturn = StringUtils::Format(g_localizeStrings.Get(19255).c_str(), + bRadio ? m_iActiveRadioChildTimers : m_iActiveTVChildTimers); // "%d scheduled" } return strReturn; @@ -581,17 +579,34 @@ bool CPVRTimerInfoTag::UpdateChildState(const CPVRTimerInfoTagPtr &childTimer) case PVR_TIMER_STATE_NEW: case PVR_TIMER_STATE_SCHEDULED: case PVR_TIMER_STATE_CONFLICT_OK: - m_iActiveChildTimers++; + if (childTimer->m_bIsRadio) + m_iActiveRadioChildTimers++; + else + m_iActiveTVChildTimers++; break; case PVR_TIMER_STATE_RECORDING: - m_iActiveChildTimers++; - m_bHasChildRecording = true; + if (childTimer->m_bIsRadio) + { + m_iActiveRadioChildTimers++; + m_bHasRadioChildRecording = true; + } + else + { + m_iActiveTVChildTimers++; + m_bHasTVChildRecording = true; + } break; case PVR_TIMER_STATE_CONFLICT_NOK: - m_bHasChildConflictNOK = true; + if (childTimer->m_bIsRadio) + m_bHasRadioChildConflictNOK = true; + else + m_bHasTVChildConflictNOK = true; break; case PVR_TIMER_STATE_ERROR: - m_bHasChildErrors = true; + if (childTimer->m_bIsRadio) + m_bHasRadioChildErrors = true; + else + m_bHasTVChildErrors = true; break; case PVR_TIMER_STATE_COMPLETED: case PVR_TIMER_STATE_ABORTED: @@ -605,10 +620,14 @@ bool CPVRTimerInfoTag::UpdateChildState(const CPVRTimerInfoTagPtr &childTimer) void CPVRTimerInfoTag::ResetChildState() { - m_iActiveChildTimers = 0; - m_bHasChildConflictNOK = false; - m_bHasChildRecording = false; - m_bHasChildErrors = false; + m_iActiveTVChildTimers = 0; + m_iActiveRadioChildTimers = 0; + m_bHasTVChildConflictNOK = false; + m_bHasRadioChildConflictNOK = false; + m_bHasTVChildRecording = false; + m_bHasRadioChildRecording = false; + m_bHasTVChildErrors = false; + m_bHasRadioChildErrors = false; } bool CPVRTimerInfoTag::UpdateOnClient() @@ -687,7 +706,7 @@ CPVRTimerInfoTagPtr CPVRTimerInfoTag::CreateInstantTimerTag(const CPVRChannelPtr newTimer->SetTimerType(timerType); if (epgTag) - newTimer->SetEpgTag(epgTag); + newTimer->SetEpgInfoTag(epgTag); else newTimer->UpdateEpgInfoTag(); } @@ -723,7 +742,7 @@ CPVRTimerInfoTagPtr CPVRTimerInfoTag::CreateFromEpg(const CPVREpgInfoTagPtr &tag CPVRTimerInfoTagPtr newTag(new CPVRTimerInfoTag()); /* check if a valid channel is set */ - CPVRChannelPtr channel = tag->Channel(); + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag); if (!channel) { CLog::LogF(LOGERROR, "EPG tag has no channel"); @@ -735,7 +754,10 @@ CPVRTimerInfoTagPtr CPVRTimerInfoTag::CreateFromEpg(const CPVREpgInfoTagPtr &tag CDateTime newEnd = tag->EndAsUTC(); newTag->m_iClientIndex = PVR_TIMER_NO_CLIENT_INDEX; newTag->m_iParentClientIndex = PVR_TIMER_NO_PARENT; - newTag->m_strTitle = tag->Title().empty() ? channel->ChannelName() : tag->Title(); + if (!CServiceBroker::GetPVRManager().IsParentalLocked(tag)) + newTag->m_strTitle = tag->Title(); + if (newTag->m_strTitle.empty()) + newTag->m_strTitle = channel->ChannelName(); newTag->m_iClientChannelUid = channel->UniqueID(); newTag->m_iClientId = channel->ClientID(); newTag->m_bIsRadio = channel->IsRadio(); @@ -794,7 +816,7 @@ CPVRTimerInfoTagPtr CPVRTimerInfoTag::CreateFromEpg(const CPVREpgInfoTagPtr &tag newTag->SetTimerType(timerType); newTag->UpdateSummary(); - newTag->SetEpgTag(tag); + newTag->SetEpgInfoTag(tag); /* unused only for reference */ newTag->m_strFileNameAndPath = CPVRTimersPath::PATH_NEW; @@ -912,6 +934,19 @@ std::string CPVRTimerInfoTag::GetDeletedNotificationText() const return StringUtils::Format("%s: '%s'", g_localizeStrings.Get(stringID).c_str(), m_strTitle.c_str()); } +void CPVRTimerInfoTag::SetEpgInfoTag(const std::shared_ptr<CPVREpgInfoTag>& tag) +{ + CSingleLock lock(m_critSection); + m_epgTag = tag; +} + +void CPVRTimerInfoTag::UpdateEpgInfoTag() +{ + CSingleLock lock(m_critSection); + m_epgTag.reset(); + GetEpgInfoTag(); +} + CPVREpgInfoTagPtr CPVRTimerInfoTag::GetEpgInfoTag(bool bCreate /* = true */) const { if (!m_epgTag && bCreate) @@ -939,7 +974,7 @@ CPVREpgInfoTagPtr CPVRTimerInfoTag::GetEpgInfoTag(bool bCreate /* = true */) con } } - if (!m_epgTag && m_epTagRefetchTimeout.IsTimePast()) + if (!IsTimerRule() && !m_epgTag && m_epTagRefetchTimeout.IsTimePast()) { m_epTagRefetchTimeout.Set(30000); // try to fetch missing epg tag from backend at most every 30 secs @@ -953,26 +988,9 @@ CPVREpgInfoTagPtr CPVRTimerInfoTag::GetEpgInfoTag(bool bCreate /* = true */) con if (startTime > 0 && endTime > 0) { // try to fetch missing epg tag from backend - const CPVREpgInfoTagPtr epgTag = epg->GetTagBetween(StartAsUTC() - CDateTimeSpan(0, 0, 2, 0), EndAsUTC() + CDateTimeSpan(0, 0, 2, 0), true); - if (epgTag) - { - bool bTagMatches = !IsTimerRule(); - if (!bTagMatches) - { - // Check whether the tag actually is an event that belongs to a child of this timer rule - const CPVRTimerInfoTagPtr timer = epgTag->Timer(); - if (timer && (timer->GetTimerRuleId() == m_iClientIndex)) - { - bTagMatches = true; - } - } - - if (bTagMatches) - { - m_epgTag = epgTag; - m_iEpgUid = m_epgTag->UniqueBroadcastID(); - } - } + m_epgTag = epg->GetTagBetween(StartAsUTC() - CDateTimeSpan(0, 0, 2, 0), EndAsUTC() + CDateTimeSpan(0, 0, 2, 0), true); + if (m_epgTag) + m_iEpgUid = m_epgTag->UniqueBroadcastID(); } } } @@ -981,24 +999,6 @@ CPVREpgInfoTagPtr CPVRTimerInfoTag::GetEpgInfoTag(bool bCreate /* = true */) con return m_epgTag; } -void CPVRTimerInfoTag::SetEpgTag(const CPVREpgInfoTagPtr &tag) -{ - CPVREpgInfoTagPtr previousTag; - { - CSingleLock lock(m_critSection); - previousTag = m_epgTag; - m_epgTag = tag; - } - - if (previousTag) - previousTag->ClearTimer(); -} - -void CPVRTimerInfoTag::ClearEpgTag(void) -{ - SetEpgTag(CPVREpgInfoTagPtr()); -} - bool CPVRTimerInfoTag::HasChannel() const { return m_channel.get() != nullptr; diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.h b/xbmc/pvr/timers/PVRTimerInfoTag.h index e1408eae32..868d3569f0 100644 --- a/xbmc/pvr/timers/PVRTimerInfoTag.h +++ b/xbmc/pvr/timers/PVRTimerInfoTag.h @@ -55,7 +55,7 @@ namespace PVR void UpdateSummary(void); - std::string GetStatus() const; + std::string GetStatus(bool bRadio) const; std::string GetTypeAsString() const; static const int DEFAULT_PVRRECORD_INSTANTRECORDTIME = -1; @@ -77,6 +77,12 @@ namespace PVR static CPVRTimerInfoTagPtr CreateFromEpg(const CPVREpgInfoTagPtr &tag, bool bCreateRule = false); /*! + * @brief Associate the given epg tag with this timer. + * @param tag The epg tag to assign. + */ + void SetEpgInfoTag(const std::shared_ptr<CPVREpgInfoTag>& tag); + + /*! * @brief get the epg info tag associated with this timer, if any * @param bCreate if true, try to find the epg tag if not yet set (lazy evaluation) * @return the epg info tag associated with this timer or null if there is no tag @@ -247,17 +253,6 @@ namespace PVR bool UpdateOnClient(); /*! - * @brief Associate the given epg tag with this timer; before, clear old timer at associated epg tag, if any. - * @param tag The epg tag to assign. - */ - void SetEpgTag(const CPVREpgInfoTagPtr &tag); - - /*! - * @brief Clear the epg tag associated with this timer; before, clear this timer at associated epg tag, if any. - */ - void ClearEpgTag(void); - - /*! * @brief Update the channel associated with this timer. * @return the channel for the timer. Can be empty for epg based repeating timers (e.g. "match any channel" rules) */ @@ -316,10 +311,14 @@ namespace PVR CDateTime m_FirstDay; /*!< if it is a manual timer rule the first date it starts */ CPVRTimerTypePtr m_timerType; /*!< the type of this timer */ - unsigned int m_iActiveChildTimers; /*!< @brief Number of active timers which have this timer as their m_iParentClientIndex */ - bool m_bHasChildConflictNOK; /*!< @brief Has at least one child timer with status PVR_TIMER_STATE_CONFLICT_NOK */ - bool m_bHasChildRecording; /*!< @brief Has at least one child timer with status PVR_TIMER_STATE_RECORDING */ - bool m_bHasChildErrors; /*!< @brief Has at least one child timer with status PVR_TIMER_STATE_ERROR */ + unsigned int m_iActiveTVChildTimers; /*!< @brief Number of active TV timers which have this timer as their m_iParentClientIndex */ + unsigned int m_iActiveRadioChildTimers; /*!< @brief Number of active radio timers which have this timer as their m_iParentClientIndex */ + bool m_bHasTVChildConflictNOK; /*!< @brief Has at least one child TV timer with status PVR_TIMER_STATE_CONFLICT_NOK */ + bool m_bHasRadioChildConflictNOK; /*!< @brief Has at least one child radio timer with status PVR_TIMER_STATE_CONFLICT_NOK */ + bool m_bHasTVChildRecording; /*!< @brief Has at least one TV child timer with status PVR_TIMER_STATE_RECORDING */ + bool m_bHasRadioChildRecording; /*!< @brief Has at least one radio child timer with status PVR_TIMER_STATE_RECORDING */ + bool m_bHasTVChildErrors; /*!< @brief Has at least one child TV timer with status PVR_TIMER_STATE_ERROR */ + bool m_bHasRadioChildErrors; /*!< @brief Has at least one child radio timer with status PVR_TIMER_STATE_ERROR */ std::string m_strSeriesLink; /*!< series link */ diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp index 1fcda74243..23e18c3f9e 100644 --- a/xbmc/pvr/timers/PVRTimers.cpp +++ b/xbmc/pvr/timers/PVRTimers.cpp @@ -8,7 +8,6 @@ #include "PVRTimers.h" -#include <cstdlib> #include <utility> #include "FileItem.h" @@ -17,8 +16,6 @@ #include "guilib/LocalizeStrings.h" #include "settings/Settings.h" #include "threads/SingleLock.h" -#include "utils/StringUtils.h" -#include "utils/URIUtils.h" #include "utils/log.h" #include "pvr/PVRJobs.h" @@ -26,6 +23,7 @@ #include "pvr/addons/PVRClients.h" #include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/epg/EpgContainer.h" +#include "pvr/timers/PVRTimersPath.h" using namespace PVR; @@ -140,42 +138,6 @@ bool CPVRTimers::IsRecording(void) const return false; } -bool CPVRTimers::SetEpgTagTimer(const CPVRTimerInfoTagPtr &timer) -{ - if (timer->IsTimerRule() || timer->m_bStartAnyTime || timer->m_bEndAnyTime) - return false; - - std::vector<CPVREpgInfoTagPtr> tags(CServiceBroker::GetPVRManager().EpgContainer().GetEpgTagsForTimer(timer)); - - if (tags.empty()) - return false; - - // assign first matching epg tag to the timer. - timer->SetEpgTag(tags.front()); - - // assign timer to every matching epg tag. - for (const auto &tag : tags) - tag->SetTimer(timer); - - return true; -} - -bool CPVRTimers::ClearEpgTagTimer(const CPVRTimerInfoTagPtr &timer) -{ - if (timer->IsTimerRule() || timer->m_bStartAnyTime || timer->m_bEndAnyTime) - return false; - - std::vector<CPVREpgInfoTagPtr> tags(CServiceBroker::GetPVRManager().EpgContainer().GetEpgTagsForTimer(timer)); - - if (tags.empty()) - return false; - - for (const auto &tag : tags) - tag->ClearTimer(); - - return true; -} - bool CPVRTimers::UpdateEntries(const CPVRTimersContainer &timers, const std::vector<int> &failedClients) { bool bChanged(false); @@ -195,11 +157,8 @@ bool CPVRTimers::UpdateEntries(const CPVRTimersContainer &timers, const std::vec { /* if it's present, update the current tag */ bool bStateChanged(existingTimer->m_state != (*timerIt)->m_state); - ClearEpgTagTimer(existingTimer); if (existingTimer->UpdateEntry(*timerIt)) { - SetEpgTagTimer(existingTimer); - bChanged = true; existingTimer->ResetChildState(); @@ -220,7 +179,6 @@ bool CPVRTimers::UpdateEntries(const CPVRTimersContainer &timers, const std::vec CPVRTimerInfoTagPtr newTimer = CPVRTimerInfoTagPtr(new CPVRTimerInfoTag); newTimer->UpdateEntry(*timerIt); newTimer->m_iTimerId = ++m_iLastId; - SetEpgTagTimer(newTimer); InsertTimer(newTimer); bChanged = true; @@ -269,8 +227,6 @@ bool CPVRTimers::UpdateEntries(const CPVRTimersContainer &timers, const std::vec timerNotifications.push_back(std::make_pair(timer->m_iClientId, timer->GetDeletedNotificationText())); - ClearEpgTagTimer(timer); - it2 = it->second.erase(it2); bChanged = true; @@ -283,8 +239,6 @@ bool CPVRTimers::UpdateEntries(const CPVRTimersContainer &timers, const std::vec CLog::LogFC(LOGDEBUG, LOGPVR, "Changed start time timer %d on client %d", timer->m_iClientIndex, timer->m_iClientId); - ClearEpgTagTimer(timer); - /* remember timer */ timersToMove.push_back(timer); @@ -307,10 +261,7 @@ bool CPVRTimers::UpdateEntries(const CPVRTimersContainer &timers, const std::vec /* reinsert timers with changed timer start */ for (VecTimerInfoTag::const_iterator timerIt = timersToMove.begin(); timerIt != timersToMove.end(); ++timerIt) - { - SetEpgTagTimer(*timerIt); InsertTimer(*timerIt); - } /* update child information for all parent timers */ for (const auto &tagsEntry : m_tags) @@ -367,7 +318,7 @@ bool CPVRTimers::KindMatchesTag(const TimerKind &eKind, const CPVRTimerInfoTagPt (eKind == TimerKindRadio && tag->m_bIsRadio); } -CFileItemPtr CPVRTimers::GetNextActiveTimer(const TimerKind &eKind) const +std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetNextActiveTimer(const TimerKind &eKind) const { CSingleLock lock(m_critSection); @@ -380,31 +331,31 @@ CFileItemPtr CPVRTimers::GetNextActiveTimer(const TimerKind &eKind) const !timersEntry->IsRecording() && !timersEntry->IsTimerRule() && !timersEntry->IsBroken()) - return CFileItemPtr(new CFileItem(timersEntry)); + return timersEntry; } } - return CFileItemPtr(); + return std::shared_ptr<CPVRTimerInfoTag>(); } -CFileItemPtr CPVRTimers::GetNextActiveTimer(void) const +std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetNextActiveTimer(void) const { return GetNextActiveTimer(TimerKindAny); } -CFileItemPtr CPVRTimers::GetNextActiveTVTimer(void) const +std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetNextActiveTVTimer(void) const { return GetNextActiveTimer(TimerKindTV); } -CFileItemPtr CPVRTimers::GetNextActiveRadioTimer(void) const +std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetNextActiveRadioTimer(void) const { return GetNextActiveTimer(TimerKindRadio); } -std::vector<CFileItemPtr> CPVRTimers::GetActiveTimers(void) const +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRTimers::GetActiveTimers(void) const { - std::vector<CFileItemPtr> tags; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> tags; CSingleLock lock(m_critSection); for (MapTags::const_iterator it = m_tags.begin(); it != m_tags.end(); ++it) @@ -414,8 +365,7 @@ std::vector<CFileItemPtr> CPVRTimers::GetActiveTimers(void) const CPVRTimerInfoTagPtr current = *timerIt; if (current->IsActive() && !current->IsTimerRule() && !current->IsBroken()) { - CFileItemPtr fileItem(new CFileItem(current)); - tags.push_back(fileItem); + tags.emplace_back(current); } } } @@ -458,9 +408,9 @@ int CPVRTimers::AmountActiveRadioTimers(void) const return AmountActiveTimers(TimerKindRadio); } -std::vector<CFileItemPtr> CPVRTimers::GetActiveRecordings(const TimerKind &eKind) const +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRTimers::GetActiveRecordings(const TimerKind& eKind) const { - std::vector<CFileItemPtr> tags; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> tags; CSingleLock lock(m_critSection); for (const auto &tagsEntry : m_tags) @@ -472,8 +422,7 @@ std::vector<CFileItemPtr> CPVRTimers::GetActiveRecordings(const TimerKind &eKind !timersEntry->IsTimerRule() && !timersEntry->IsBroken()) { - CFileItemPtr fileItem(new CFileItem(timersEntry)); - tags.push_back(fileItem); + tags.emplace_back(timersEntry); } } } @@ -481,17 +430,17 @@ std::vector<CFileItemPtr> CPVRTimers::GetActiveRecordings(const TimerKind &eKind return tags; } -std::vector<CFileItemPtr> CPVRTimers::GetActiveRecordings(void) const +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRTimers::GetActiveRecordings() const { return GetActiveRecordings(TimerKindAny); } -std::vector<CFileItemPtr> CPVRTimers::GetActiveTVRecordings(void) const +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRTimers::GetActiveTVRecordings() const { return GetActiveRecordings(TimerKindTV); } -std::vector<CFileItemPtr> CPVRTimers::GetActiveRadioRecordings(void) const +std::vector<std::shared_ptr<CPVRTimerInfoTag>> CPVRTimers::GetActiveRadioRecordings() const { return GetActiveRecordings(TimerKindRadio); } @@ -561,7 +510,7 @@ bool CPVRTimers::GetRootDirectory(const CPVRTimersPath &path, CFileItemList &ite { for (const auto &timer : tagsEntry.second) { - if ((bRadio == timer->m_bIsRadio) && + if ((bRadio == timer->m_bIsRadio || (bRules && timer->m_iClientChannelUid == PVR_TIMER_ANY_CHANNEL)) && (bRules == timer->IsTimerRule()) && (!bHideDisabled || (timer->m_state != PVR_TIMER_STATE_DISABLED))) { @@ -742,39 +691,29 @@ CPVRTimerInfoTagPtr CPVRTimers::GetTimerForEpgTag(const CPVREpgInfoTagPtr &epgTa { if (epgTag) { - // already a timer assigned to tag? - const CPVRTimerInfoTagPtr timer(epgTag->Timer()); - if (timer) - return timer; + CSingleLock lock(m_critSection); - // try to find a matching timer for the tag. - const CPVRChannelPtr channel(epgTag->Channel()); - if (channel) + for (const auto &tagsEntry : m_tags) { - CSingleLock lock(m_critSection); - - for (const auto &tagsEntry : m_tags) + for (const auto &timersEntry : tagsEntry.second) { - for (const auto &timersEntry : tagsEntry.second) - { - if (timersEntry->IsTimerRule()) - continue; + if (timersEntry->IsTimerRule()) + continue; + + if (timersEntry->GetEpgInfoTag(false) == epgTag) + return timersEntry; - if (timersEntry->GetEpgInfoTag(false) == epgTag) + if (timersEntry->m_iClientChannelUid != PVR_CHANNEL_INVALID_UID && + timersEntry->m_iClientChannelUid == epgTag->UniqueChannelID()) + { + if (timersEntry->UniqueBroadcastID() != EPG_TAG_INVALID_UID && + timersEntry->UniqueBroadcastID() == epgTag->UniqueBroadcastID()) return timersEntry; - if (timersEntry->m_iClientChannelUid != PVR_CHANNEL_INVALID_UID && - timersEntry->m_iClientChannelUid == channel->UniqueID()) - { - if (timersEntry->UniqueBroadcastID() != EPG_TAG_INVALID_UID && - timersEntry->UniqueBroadcastID() == epgTag->UniqueBroadcastID()) - return timersEntry; - - if (timersEntry->m_bIsRadio == channel->IsRadio() && - timersEntry->StartAsUTC() <= epgTag->StartAsUTC() && - timersEntry->EndAsUTC() >= epgTag->EndAsUTC()) - return timersEntry; - } + if (timersEntry->m_bIsRadio == epgTag->IsRadio() && + timersEntry->StartAsUTC() <= epgTag->StartAsUTC() && + timersEntry->EndAsUTC() >= epgTag->EndAsUTC()) + return timersEntry; } } } @@ -783,43 +722,6 @@ CPVRTimerInfoTagPtr CPVRTimers::GetTimerForEpgTag(const CPVREpgInfoTagPtr &epgTa return CPVRTimerInfoTagPtr(); } -bool CPVRTimers::HasRecordingTimerForRecording(const CPVRRecording &recording) const -{ - return GetRecordingTimerForRecording(recording) != nullptr; -} - -CPVRTimerInfoTagPtr CPVRTimers::GetRecordingTimerForRecording(const CPVRRecording &recording) const -{ - CSingleLock lock(m_critSection); - - for (const auto &tagsEntry : m_tags) - { - for (const auto &timersEntry : tagsEntry.second) - { - if (timersEntry->IsRecording() && - !timersEntry->IsTimerRule() && - !timersEntry->IsBroken() && - timersEntry->m_iClientId == recording.ClientID() && - timersEntry->m_iClientChannelUid == recording.ChannelUid()) - { - // first, match epg event uids, if available - if (timersEntry->UniqueBroadcastID() == recording.BroadcastUid() && - timersEntry->UniqueBroadcastID() != EPG_TAG_INVALID_UID) - return timersEntry; - - // alternatively, match start and end times - const CDateTime timerStart = timersEntry->StartAsUTC() - CDateTimeSpan(0, 0, timersEntry->m_iMarginStart, 0); - const CDateTime timerEnd = timersEntry->EndAsUTC() + CDateTimeSpan(0, 0, timersEntry->m_iMarginEnd, 0); - if (timerStart <= recording.RecordingTimeAsUTC() && - timerEnd >= recording.EndTimeAsUTC()) - return timersEntry; - } - } - } - - return CPVRTimerInfoTagPtr(); -} - CPVRTimerInfoTagPtr CPVRTimers::GetTimerRule(const CPVRTimerInfoTagPtr &timer) const { if (timer) @@ -847,7 +749,7 @@ CFileItemPtr CPVRTimers::GetTimerRule(const CFileItemPtr &item) const { CPVRTimerInfoTagPtr timer; if (item && item->HasEPGInfoTag()) - timer = item->GetEPGInfoTag()->Timer(); + timer = GetTimerForEpgTag(item->GetEPGInfoTag()); else if (item && item->HasPVRTimerInfoTag()) timer = item->GetPVRTimerInfoTag(); @@ -876,11 +778,11 @@ CDateTime CPVRTimers::GetNextEventTime(void) const CDateTime wakeuptime; /* Check next active time */ - CFileItemPtr item = GetNextActiveTimer(); - if (item && item->HasPVRTimerInfoTag()) + const std::shared_ptr<CPVRTimerInfoTag> timer = GetNextActiveTimer(); + if (timer) { - const CDateTimeSpan prestart(0, 0, item->GetPVRTimerInfoTag()->MarginStart(), 0); - const CDateTime start = item->GetPVRTimerInfoTag()->StartAsUTC(); + const CDateTimeSpan prestart(0, 0, timer->MarginStart(), 0); + const CDateTime start = timer->StartAsUTC(); wakeuptime = ((start - prestart - prewakeup - idle) > now) ? start - prestart - prewakeup : now + idle; @@ -949,73 +851,3 @@ CPVRTimerInfoTagPtr CPVRTimers::GetById(unsigned int iTimerId) const } return item; } - - -//= CPVRTimersPath ============================================================ - -const std::string CPVRTimersPath::PATH_ADDTIMER = "pvr://timers/addtimer/"; -const std::string CPVRTimersPath::PATH_NEW = "pvr://timers/new/"; - -CPVRTimersPath::CPVRTimersPath(const std::string &strPath) -{ - Init(strPath); -} - -CPVRTimersPath::CPVRTimersPath(const std::string &strPath, int iClientId, unsigned int iParentId) -{ - if (Init(strPath)) - { - /* set/replace client and parent id. */ - m_path = StringUtils::Format("pvr://timers/%s/%s/%d/%d", - m_bRadio ? "radio" : "tv", - m_bTimerRules ? "rules" : "timers", - iClientId, - iParentId); - m_iClientId = iClientId; - m_iParentId = iParentId; - m_bRoot = false; - } -} - -CPVRTimersPath::CPVRTimersPath(bool bRadio, bool bTimerRules) : - m_path(StringUtils::Format( - "pvr://timers/%s/%s", bRadio ? "radio" : "tv", bTimerRules ? "rules" : "timers")), - m_bValid(true), - m_bRoot(true), - m_bRadio(bRadio), - m_bTimerRules(bTimerRules), - m_iClientId(-1), - m_iParentId(0) -{ -} - -bool CPVRTimersPath::Init(const std::string &strPath) -{ - std::string strVarPath(strPath); - URIUtils::RemoveSlashAtEnd(strVarPath); - - m_path = strVarPath; - const std::vector<std::string> segments = URIUtils::SplitPath(m_path); - - m_bValid = (((segments.size() == 4) || (segments.size() == 6)) && - (segments.at(1) == "timers") && - ((segments.at(2) == "radio") || (segments.at(2) == "tv"))&& - ((segments.at(3) == "rules") || (segments.at(3) == "timers"))); - m_bRoot = (m_bValid && (segments.size() == 4)); - m_bRadio = (m_bValid && (segments.at(2) == "radio")); - m_bTimerRules = (m_bValid && (segments.at(3) == "rules")); - - if (!m_bValid || m_bRoot) - { - m_iClientId = -1; - m_iParentId = 0; - } - else - { - char *end; - m_iClientId = std::strtol (segments.at(4).c_str(), &end, 10); - m_iParentId = std::strtoul(segments.at(5).c_str(), &end, 10); - } - - return m_bValid; -} diff --git a/xbmc/pvr/timers/PVRTimers.h b/xbmc/pvr/timers/PVRTimers.h index 32231e6656..4664c37a1b 100644 --- a/xbmc/pvr/timers/PVRTimers.h +++ b/xbmc/pvr/timers/PVRTimers.h @@ -26,7 +26,6 @@ typedef std::shared_ptr<CFileItem> CFileItemPtr; namespace PVR { - class CPVRRecording; class CPVRTimersPath; class CPVRTimersContainer @@ -87,24 +86,24 @@ namespace PVR bool Update(void); /*! - * @return The tv or radio timer that will be active next (state scheduled), or an empty fileitemptr if none. + * @return The tv or radio timer that will be active next (state scheduled), or nullptr if none. */ - CFileItemPtr GetNextActiveTimer(void) const; + std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTimer(void) const; /*! - * @return The tv timer that will be active next (state scheduled), or an empty fileitemptr if none. + * @return The tv timer that will be active next (state scheduled), or nullptr if none. */ - CFileItemPtr GetNextActiveTVTimer(void) const; + std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTVTimer(void) const; /*! - * @return The radio timer that will be active next (state scheduled), or an empty fileitemptr if none. + * @return The radio timer that will be active next (state scheduled), or nullptr if none. */ - CFileItemPtr GetNextActiveRadioTimer(void) const; + std::shared_ptr<CPVRTimerInfoTag> GetNextActiveRadioTimer(void) const; /*! * @return All timers that are active (states scheduled or recording) */ - std::vector<CFileItemPtr> GetActiveTimers(void) const; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveTimers(void) const; /*! * Get all timers @@ -135,17 +134,17 @@ namespace PVR /*! * @return All tv and radio timers that are recording */ - std::vector<CFileItemPtr> GetActiveRecordings(void) const; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveRecordings() const; /*! * @return All tv timers that are recording */ - std::vector<CFileItemPtr> GetActiveTVRecordings(void) const; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveTVRecordings() const; /*! * @return All radio timers that are recording */ - std::vector<CFileItemPtr> GetActiveRadioRecordings(void) const; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveRadioRecordings() const; /*! * @return True when recording, false otherwise. @@ -237,28 +236,14 @@ namespace PVR /*! * @brief Get the timer tag that matches the given epg tag. * @param epgTag The epg tag. - * @return The requested timer tag, or an empty fileitemptr if none was found. + * @return The requested timer tag, or nullptr if none was found. */ CPVRTimerInfoTagPtr GetTimerForEpgTag(const CPVREpgInfoTagPtr &epgTag) const; /*! - * @brief Check whether there is a timer currently recording the given recording. - * @param recording The recording to check. - * @return true if there is a timer currently recording the given recording, false otherwise. - */ - bool HasRecordingTimerForRecording(const CPVRRecording &recording) const; - - /*! - * @brief Get the timer currently recording the given recording, if any. - * @param recording The recording to check. - * @return The requested timer tag, or an null if none was found. - */ - CPVRTimerInfoTagPtr GetRecordingTimerForRecording(const CPVRRecording &recording) const; - - /*! * Get the timer rule for a given timer tag * @param timer The timer to query the timer rule for - * @return The timer rule, or null if none was found. + * @return The timer rule, or nullptr if none was found. */ CPVRTimerInfoTagPtr GetTimerRule(const CPVRTimerInfoTagPtr &timer) const; @@ -287,8 +272,6 @@ namespace PVR bool UpdateEntries(const CPVRTimersContainer &timers, const std::vector<int> &failedClients); bool GetRootDirectory(const CPVRTimersPath &path, CFileItemList &items) const; bool GetSubDirectory(const CPVRTimersPath &path, CFileItemList &items) const; - bool SetEpgTagTimer(const CPVRTimerInfoTagPtr &timer); - bool ClearEpgTagTimer(const CPVRTimerInfoTagPtr &timer); enum TimerKind { @@ -299,44 +282,12 @@ namespace PVR bool KindMatchesTag(const TimerKind &eKind, const CPVRTimerInfoTagPtr &tag) const; - CFileItemPtr GetNextActiveTimer(const TimerKind &eKind) const; + std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTimer(const TimerKind& eKind) const; int AmountActiveTimers(const TimerKind &eKind) const; - std::vector<CFileItemPtr> GetActiveRecordings(const TimerKind &eKind) const; + std::vector<std::shared_ptr<CPVRTimerInfoTag>> GetActiveRecordings(const TimerKind& eKind) const; int AmountActiveRecordings(const TimerKind &eKind) const; bool m_bIsUpdating = false; CPVRSettings m_settings; }; - - class CPVRTimersPath - { - public: - static const std::string PATH_ADDTIMER; - static const std::string PATH_NEW; - - explicit CPVRTimersPath(const std::string &strPath); - CPVRTimersPath(const std::string &strPath, int iClientId, unsigned int iParentId); - CPVRTimersPath(bool bRadio, bool bTimerRules); - - bool IsValid() const { return m_bValid; } - - const std::string &GetPath() const { return m_path; } - bool IsTimersRoot() const { return m_bRoot; } - bool IsTimerRule() const { return !IsTimersRoot(); } - bool IsRadio() const { return m_bRadio; } - bool IsRules() const { return m_bTimerRules; } - int GetClientId() const { return m_iClientId; } - unsigned int GetParentId() const { return m_iParentId; } - - private: - bool Init(const std::string &strPath); - - std::string m_path; - bool m_bValid; - bool m_bRoot; - bool m_bRadio; - bool m_bTimerRules; - int m_iClientId; - unsigned int m_iParentId; - }; } diff --git a/xbmc/pvr/timers/PVRTimersPath.cpp b/xbmc/pvr/timers/PVRTimersPath.cpp new file mode 100644 index 0000000000..43c6ea03e7 --- /dev/null +++ b/xbmc/pvr/timers/PVRTimersPath.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PVRTimersPath.h" + +#include <cstdlib> +#include <vector> + +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" + +using namespace PVR; + +const std::string CPVRTimersPath::PATH_ADDTIMER = "pvr://timers/addtimer/"; +const std::string CPVRTimersPath::PATH_NEW = "pvr://timers/new/"; + +CPVRTimersPath::CPVRTimersPath(const std::string& strPath) +{ + Init(strPath); +} + +CPVRTimersPath::CPVRTimersPath(const std::string& strPath, int iClientId, unsigned int iParentId) +{ + if (Init(strPath)) + { + // set/replace client and parent id. + m_path = StringUtils::Format("pvr://timers/%s/%s/%d/%d", + m_bRadio ? "radio" : "tv", + m_bTimerRules ? "rules" : "timers", + iClientId, + iParentId); + m_iClientId = iClientId; + m_iParentId = iParentId; + m_bRoot = false; + } +} + +CPVRTimersPath::CPVRTimersPath(bool bRadio, bool bTimerRules) : + m_path(StringUtils::Format( + "pvr://timers/%s/%s", bRadio ? "radio" : "tv", bTimerRules ? "rules" : "timers")), + m_bValid(true), + m_bRoot(true), + m_bRadio(bRadio), + m_bTimerRules(bTimerRules), + m_iClientId(-1), + m_iParentId(0) +{ +} + +bool CPVRTimersPath::Init(const std::string& strPath) +{ + std::string strVarPath(strPath); + URIUtils::RemoveSlashAtEnd(strVarPath); + + m_path = strVarPath; + const std::vector<std::string> segments = URIUtils::SplitPath(m_path); + + m_bValid = (((segments.size() == 4) || (segments.size() == 6)) && + (segments.at(1) == "timers") && + ((segments.at(2) == "radio") || (segments.at(2) == "tv"))&& + ((segments.at(3) == "rules") || (segments.at(3) == "timers"))); + m_bRoot = (m_bValid && (segments.size() == 4)); + m_bRadio = (m_bValid && (segments.at(2) == "radio")); + m_bTimerRules = (m_bValid && (segments.at(3) == "rules")); + + if (!m_bValid || m_bRoot) + { + m_iClientId = -1; + m_iParentId = 0; + } + else + { + m_iClientId = std::stoi(segments.at(4)); + m_iParentId = std::stoul(segments.at(5)); + } + + return m_bValid; +} diff --git a/xbmc/pvr/timers/PVRTimersPath.h b/xbmc/pvr/timers/PVRTimersPath.h new file mode 100644 index 0000000000..f3e6584f61 --- /dev/null +++ b/xbmc/pvr/timers/PVRTimersPath.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <string> + +namespace PVR +{ + class CPVRTimersPath + { + public: + static const std::string PATH_ADDTIMER; + static const std::string PATH_NEW; + + explicit CPVRTimersPath(const std::string& strPath); + CPVRTimersPath(const std::string& strPath, int iClientId, unsigned int iParentId); + CPVRTimersPath(bool bRadio, bool bTimerRules); + + bool IsValid() const { return m_bValid; } + + const std::string& GetPath() const { return m_path; } + bool IsTimersRoot() const { return m_bRoot; } + bool IsTimerRule() const { return !IsTimersRoot(); } + bool IsRadio() const { return m_bRadio; } + bool IsRules() const { return m_bTimerRules; } + int GetClientId() const { return m_iClientId; } + unsigned int GetParentId() const { return m_iParentId; } + + private: + bool Init(const std::string &strPath); + + std::string m_path; + bool m_bValid = false; + bool m_bRoot = false; + bool m_bRadio = false; + bool m_bTimerRules = false; + int m_iClientId = -1; + unsigned int m_iParentId = 0; + }; +} diff --git a/xbmc/pvr/windows/GUIEPGGridContainer.cpp b/xbmc/pvr/windows/GUIEPGGridContainer.cpp index bdff6f3dd5..432a695eeb 100644 --- a/xbmc/pvr/windows/GUIEPGGridContainer.cpp +++ b/xbmc/pvr/windows/GUIEPGGridContainer.cpp @@ -23,7 +23,7 @@ #include "utils/Variant.h" #include "pvr/channels/PVRChannel.h" -#include "pvr/epg/Epg.h" +#include "pvr/epg/EpgInfoTag.h" #include "pvr/windows/GUIEPGGridContainerModel.h" using namespace PVR; @@ -692,17 +692,14 @@ void CGUIEPGGridContainer::UpdateItems() newBlockIndex = m_gridModel->GetFirstEventBlock(prevSelectedEpgTag) + eventOffset; } - const CPVRChannelPtr channel(prevSelectedEpgTag->Channel()); - if (channel) - channelUid = channel->UniqueID(); - + channelUid = prevSelectedEpgTag->UniqueChannelID(); broadcastUid = prevSelectedEpgTag->UniqueBroadcastID(); } else // "gap" tag selected { const GridItem *currItem(GetItem(m_channelCursor)); if (currItem) - channelUid = currItem->item->GetEPGInfoTag()->Channel()->UniqueID(); + channelUid = currItem->item->GetEPGInfoTag()->UniqueChannelID(); const GridItem *prevItem(GetPrevItem(m_channelCursor)); if (prevItem) @@ -768,7 +765,8 @@ void CGUIEPGGridContainer::UpdateItems() newChannelIndex = iChannelIndex; } else if (newChannelIndex >= m_gridModel->ChannelItemsSize() || - m_gridModel->GetGridItem(newChannelIndex, newBlockIndex)->GetEPGInfoTag()->Channel() != prevSelectedEpgTag->Channel()) + (m_gridModel->GetGridItem(newChannelIndex, newBlockIndex)->GetEPGInfoTag()->UniqueChannelID() != prevSelectedEpgTag->UniqueChannelID() && + m_gridModel->GetGridItem(newChannelIndex, newBlockIndex)->GetEPGInfoTag()->ClientID() != prevSelectedEpgTag->ClientID())) { // default to first channel newChannelIndex = 0; @@ -1314,13 +1312,13 @@ int CGUIEPGGridContainer::GetSelectedItem() const return m_gridModel->GetGridItemIndex(m_channelCursor + m_channelOffset, m_blockCursor + m_blockOffset); } -CFileItemPtr CGUIEPGGridContainer::GetSelectedChannelItem() const +CFileItemPtr CGUIEPGGridContainer::GetSelectedGridItem(int offset /*= 0*/) const { CFileItemPtr item; if (m_gridModel->HasGridItems() && m_gridModel->ChannelItemsSize() > 0 && - m_channelCursor + m_channelOffset < m_gridModel->ChannelItemsSize() && + m_channelCursor + m_channelOffset + offset < m_gridModel->ChannelItemsSize() && m_blockCursor + m_blockOffset < m_gridModel->GetBlockCount()) item = m_gridModel->GetGridItem(m_channelCursor + m_channelOffset, m_blockCursor + m_blockOffset); diff --git a/xbmc/pvr/windows/GUIEPGGridContainer.h b/xbmc/pvr/windows/GUIEPGGridContainer.h index db78ed711a..551fa2ac3e 100644 --- a/xbmc/pvr/windows/GUIEPGGridContainer.h +++ b/xbmc/pvr/windows/GUIEPGGridContainer.h @@ -58,7 +58,7 @@ namespace PVR CGUIListItemPtr GetListItem(int offset, unsigned int flag = 0) const override; std::string GetLabel(int info) const override; - CFileItemPtr GetSelectedChannelItem() const; + CFileItemPtr GetSelectedGridItem(int offset = 0) const; PVR::CPVRChannelPtr GetSelectedChannel() const; CDateTime GetSelectedDate() const; diff --git a/xbmc/pvr/windows/GUIEPGGridContainerModel.cpp b/xbmc/pvr/windows/GUIEPGGridContainerModel.cpp index a085b2d05f..023fb7001d 100644 --- a/xbmc/pvr/windows/GUIEPGGridContainerModel.cpp +++ b/xbmc/pvr/windows/GUIEPGGridContainerModel.cpp @@ -16,7 +16,8 @@ #include "utils/log.h" #include "pvr/PVRManager.h" -#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgInfoTag.h" class CGUIListItem; @@ -36,6 +37,20 @@ void CGUIEPGGridContainerModel::SetInvalid() ruler->SetInvalid(); } +std::shared_ptr<CFileItem> CGUIEPGGridContainerModel::CreateGapItem(int iChannel) const +{ + const std::shared_ptr<CPVRChannel> channel = m_channelItems[iChannel]->GetPVRChannelInfoTag(); + + std::shared_ptr<CPVREpgInfoTag> gapTag; + const std::shared_ptr<CPVREpg> epg = channel->GetEPG(); + if (epg) + gapTag = std::make_shared<CPVREpgInfoTag>(epg->GetChannelData(), epg->EpgID()); + else + gapTag = std::make_shared<CPVREpgInfoTag>(std::make_shared<CPVREpgChannelData>(*channel), -1); + + return std::make_shared<CFileItem>(gapTag); +} + void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr<CFileItemList> &items, const CDateTime &gridStart, const CDateTime &gridEnd, int iRulerUnit, int iBlocksPerPage, float fBlockSize) { if (!m_channelItems.empty()) @@ -48,33 +63,36 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr<CFileItemList> // Create programme & channel items m_programmeItems.reserve(items->Size()); CFileItemPtr fileItem; - int iLastChannelID = -1; + int iLastChannelUID = -1; + int iLastClientUID = -1; ItemsPtr itemsPointer; itemsPointer.start = 0; - CPVRChannelPtr channel; int j = 0; for (int i = 0; i < items->Size(); ++i) { fileItem = items->Get(i); - if (!fileItem->HasEPGInfoTag() || !fileItem->GetEPGInfoTag()->HasChannel()) + if (!fileItem->HasEPGInfoTag()) continue; m_programmeItems.emplace_back(fileItem); - channel = fileItem->GetEPGInfoTag()->Channel(); - if (!channel) - continue; - - int iCurrentChannelID = channel->ChannelID(); - if (iCurrentChannelID != iLastChannelID) + int iCurrentChannelUID = fileItem->GetEPGInfoTag()->UniqueChannelID(); + int iCurrentClientUID = fileItem->GetEPGInfoTag()->ClientID(); + if (iCurrentChannelUID != iLastChannelUID || iCurrentClientUID != iLastClientUID) { + iLastChannelUID = iCurrentChannelUID; + iLastClientUID = iCurrentClientUID; + + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(fileItem->GetEPGInfoTag()); + if (!channel) + continue; + if (j > 0) { itemsPointer.stop = j - 1; m_epgItemsPtr.emplace_back(itemsPointer); itemsPointer.start = j; } - iLastChannelID = iCurrentChannelID; m_channelItems.emplace_back(CFileItemPtr(new CFileItem(channel))); } ++j; @@ -203,8 +221,7 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr<CFileItemList> } else { - const CPVREpgInfoTagPtr gapTag(new CPVREpgInfoTag(m_channelItems[channel]->GetPVRChannelInfoTag())); - const CFileItemPtr gapItem(new CFileItem(gapTag)); + const std::shared_ptr<CFileItem> gapItem = CreateGapItem(channel); for (int i = block + blockDelta; i >= block - itemSize + sizeDelta; --i) { m_gridIndex[channel][i].item = gapItem; @@ -227,9 +244,7 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr<CFileItemList> } else { - const CPVREpgInfoTagPtr gapTag(new CPVREpgInfoTag(m_channelItems[channel]->GetPVRChannelInfoTag())); - const CFileItemPtr gapItem(new CFileItem(gapTag)); - m_gridIndex[channel][block].item = gapItem; + m_gridIndex[channel][block].item = CreateGapItem(channel); } m_gridIndex[channel][savedBlock].originWidth = fBlockSize; // size always 1 block here diff --git a/xbmc/pvr/windows/GUIEPGGridContainerModel.h b/xbmc/pvr/windows/GUIEPGGridContainerModel.h index 146ecbe2fe..e0ca295613 100644 --- a/xbmc/pvr/windows/GUIEPGGridContainerModel.h +++ b/xbmc/pvr/windows/GUIEPGGridContainerModel.h @@ -84,6 +84,7 @@ namespace PVR private: void FreeItemsMemory(); + std::shared_ptr<CFileItem> CreateGapItem(int iChannel) const; struct ItemsPtr { diff --git a/xbmc/pvr/windows/GUIViewStatePVR.cpp b/xbmc/pvr/windows/GUIViewStatePVR.cpp index 5d39a04e18..fc755209bd 100644 --- a/xbmc/pvr/windows/GUIViewStatePVR.cpp +++ b/xbmc/pvr/windows/GUIViewStatePVR.cpp @@ -15,7 +15,7 @@ #include "view/ViewStateSettings.h" #include "pvr/recordings/PVRRecordingsPath.h" -#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimersPath.h" using namespace PVR; diff --git a/xbmc/pvr/windows/GUIWindowPVRBase.cpp b/xbmc/pvr/windows/GUIWindowPVRBase.cpp index 56433ef1df..6affc09428 100644 --- a/xbmc/pvr/windows/GUIWindowPVRBase.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRBase.cpp @@ -179,9 +179,11 @@ bool CGUIWindowPVRBase::OnAction(const CAction &action) case ACTION_NEXT_CHANNELGROUP: { // switch to next or previous group - if (const CPVRChannelGroupPtr channelGroup = GetChannelGroup()) + const std::shared_ptr<CPVRChannelGroup> channelGroup = GetChannelGroup(); + if (channelGroup) { - SetChannelGroup(action.GetID() == ACTION_NEXT_CHANNELGROUP ? channelGroup->GetNextGroup() : channelGroup->GetPreviousGroup()); + const CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(channelGroup->IsRadio()); + SetChannelGroup(action.GetID() == ACTION_NEXT_CHANNELGROUP ? groups->GetNextGroup(*channelGroup) : groups->GetPreviousGroup(*channelGroup)); } return true; } diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp index 0473c5f9a9..580bcf4ed2 100644 --- a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp @@ -26,7 +26,10 @@ #include "pvr/PVRGUIActions.h" #include "pvr/PVRManager.h" #include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgContainer.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" #include "pvr/windows/GUIEPGGridContainer.h" using namespace KODI::MESSAGING; @@ -34,7 +37,8 @@ using namespace PVR; CGUIWindowPVRGuideBase::CGUIWindowPVRGuideBase(bool bRadio, int id, const std::string &xmlFile) : CGUIWindowPVRBase(bRadio, id, xmlFile), - m_bChannelSelectionRestored(false) + m_bChannelSelectionRestored(false), + m_bFirstOpen(true) { m_bRefreshTimelineItems = false; m_bSyncRefreshTimelineItems = false; @@ -223,7 +227,7 @@ bool CGUIWindowPVRGuideBase::GetDirectory(const std::string &strDirectory, CFile // never call DoRefresh with locked mutex! if (m_bSyncRefreshTimelineItems) - m_refreshTimelineItemsThread->DoRefresh(); + m_refreshTimelineItemsThread->DoRefresh(true); { CSingleLock lock(m_critSection); @@ -250,6 +254,19 @@ void CGUIWindowPVRGuideBase::FormatAndSort(CFileItemList &items) CGUIWindowPVRBase::FormatAndSort(items); } +CFileItemPtr CGUIWindowPVRGuideBase::GetCurrentListItem(int offset /*= 0*/) +{ + CFileItemPtr item = CGUIWindowPVRBase::GetCurrentListItem(offset); + if (!item) + { + // EPG "gap" item selected? + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + item = epgGridContainer->GetSelectedGridItem(offset); + } + return item; +} + bool CGUIWindowPVRGuideBase::ShouldNavigateToGridContainer(int iAction) { CGUIEPGGridContainer *epgGridContainer = GetGridControl(); @@ -421,7 +438,7 @@ bool CGUIWindowPVRGuideBase::OnMessage(CGUIMessage& message) else if (now < start) { // future event - if (tag->HasTimer()) + if (CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(tag)) CServiceBroker::GetPVRManager().GUIActions()->EditTimer(pItem); else { @@ -439,7 +456,7 @@ bool CGUIWindowPVRGuideBase::OnMessage(CGUIMessage& message) else { // past event - if (tag->HasRecording()) + if (CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(tag)) CServiceBroker::GetPVRManager().GUIActions()->PlayRecording(pItem, true); else if (tag->IsPlayable()) CServiceBroker::GetPVRManager().GUIActions()->PlayEpgTag(pItem); @@ -488,7 +505,7 @@ bool CGUIWindowPVRGuideBase::OnMessage(CGUIMessage& message) CGUIEPGGridContainer *epgGridContainer = GetGridControl(); if (epgGridContainer) { - const CFileItemPtr item(epgGridContainer->GetSelectedChannelItem()); + const CFileItemPtr item(epgGridContainer->GetSelectedGridItem()); if (item) { CServiceBroker::GetPVRManager().GUIActions()->SwitchToChannel(item, true); @@ -603,8 +620,34 @@ bool CGUIWindowPVRGuideBase::RefreshTimelineItems() std::unique_ptr<CFileItemList> timeline(new CFileItemList); - // can be very expensive. never call with lock acquired. - group->GetEPGAll(*timeline, true); + if (m_bFirstOpen) + { + m_bFirstOpen = false; + + // very first open of the window. come up with some data very fast... + const std::vector<PVRChannelGroupMember> groupMembers = group->GetMembers(); + for (const auto& groupMember : groupMembers) + { + // fake a channel without epg + + const std::shared_ptr<CPVREpgInfoTag> gapTag + = std::make_shared<CPVREpgInfoTag>(std::make_shared<CPVREpgChannelData>(*(groupMember.channel)), -1); + timeline->Add(std::make_shared<CFileItem>(gapTag)); + } + + // next, fetch actual data. + m_bRefreshTimelineItems = true; + m_refreshTimelineItemsThread->DoRefresh(false); + } + else + { + // can be very expensive. never call with lock acquired. + const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags = group->GetEPGAll(true); + for (const auto& tag : tags) + { + timeline->Add(std::make_shared<CFileItem>(tag)); + } + } CDateTime startDate(group->GetFirstEPGDate()); CDateTime endDate(group->GetLastEPGDate()); @@ -688,15 +731,15 @@ void CGUIWindowPVRGuideBase::OnInputDone() const CPVRChannelNumber channelNumber = GetChannelNumber(); if (channelNumber.IsValid()) { - for (const CFileItemPtr event : *m_vecItems) + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) { - const CPVREpgInfoTagPtr tag(event->GetEPGInfoTag()); - if (tag->HasChannel() && tag->Channel()->ChannelNumber() == channelNumber) + for (const std::shared_ptr<CFileItem>& event : *m_vecItems) { - CGUIEPGGridContainer* epgGridContainer = GetGridControl(); - if (epgGridContainer) + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(event->GetEPGInfoTag()); + if (channel && channel->ChannelNumber() == channelNumber) { - epgGridContainer->SetChannel(tag->Channel()); + epgGridContainer->SetChannel(channel); return; } } @@ -733,11 +776,15 @@ void CPVRRefreshTimelineItemsThread::Stop() m_ready.Set(); // wake up the worker thread to let it exit } -void CPVRRefreshTimelineItemsThread::DoRefresh() +void CPVRRefreshTimelineItemsThread::DoRefresh(bool bWait) { m_ready.Set(); // wake up the worker thread - m_done.Reset(); - CGUIDialogBusy::WaitOnEvent(m_done, 100, false); + + if (bWait) + { + m_done.Reset(); + CGUIDialogBusy::WaitOnEvent(m_done, 100, false); + } } void CPVRRefreshTimelineItemsThread::Process() diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.h b/xbmc/pvr/windows/GUIWindowPVRGuide.h index 30583f30cb..868451c215 100644 --- a/xbmc/pvr/windows/GUIWindowPVRGuide.h +++ b/xbmc/pvr/windows/GUIWindowPVRGuide.h @@ -50,6 +50,7 @@ namespace PVR std::string GetDirectoryPath(void) override { return ""; } bool GetDirectory(const std::string &strDirectory, CFileItemList &items) override; void FormatAndSort(CFileItemList &items) override; + CFileItemPtr GetCurrentListItem(int offset = 0) override; void ClearData() override; @@ -77,6 +78,7 @@ namespace PVR std::unique_ptr<CFileItemList> m_newTimeline; bool m_bChannelSelectionRestored; + std::atomic_bool m_bFirstOpen; }; class CGUIWindowPVRTVGuide : public CGUIWindowPVRGuideBase @@ -99,7 +101,7 @@ namespace PVR void Process() override; - void DoRefresh(); + void DoRefresh(bool bWait); void Stop(); private: diff --git a/xbmc/pvr/windows/GUIWindowPVRSearch.cpp b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp index 05a88a0fae..4c21029ccc 100644 --- a/xbmc/pvr/windows/GUIWindowPVRSearch.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp @@ -23,6 +23,7 @@ #include "pvr/PVRItem.h" #include "pvr/PVRManager.h" #include "pvr/addons/PVRClients.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" #include "pvr/dialogs/GUIDialogPVRGuideSearch.h" #include "pvr/epg/EpgContainer.h" #include "pvr/epg/EpgSearchFilter.h" @@ -55,7 +56,25 @@ namespace void AsyncSearchAction::Run() { - CServiceBroker::GetPVRManager().EpgContainer().GetEPGSearch(*m_items, *m_filter); + std::vector<std::shared_ptr<CPVREpgInfoTag>> results = CServiceBroker::GetPVRManager().EpgContainer().GetAllTags(); + for (auto it = results.begin(); it != results.end();) + { + it = results.erase(std::remove_if(results.begin(), + results.end(), + [this](const std::shared_ptr<CPVREpgInfoTag>& entry) + { + return !m_filter->FilterEntry(entry); + }), + results.end()); + } + + if (m_filter->ShouldRemoveDuplicates()) + m_filter->RemoveDuplicates(results); + + for (const auto& tag : results) + { + m_items->Add(std::make_shared<CFileItem>(tag)); + } } } // unnamed namespace @@ -65,6 +84,10 @@ CGUIWindowPVRSearchBase::CGUIWindowPVRSearchBase(bool bRadio, int id, const std: { } +CGUIWindowPVRSearchBase::~CGUIWindowPVRSearchBase() +{ +} + void CGUIWindowPVRSearchBase::GetContextButtons(int itemNumber, CContextButtons &buttons) { if (itemNumber < 0 || itemNumber >= m_vecItems->Size()) @@ -96,7 +119,7 @@ void CGUIWindowPVRSearchBase::SetItemToSearch(const CFileItemPtr &item) else { const CPVREpgInfoTagPtr epgTag(CPVRItem(item).GetEpgInfoTag()); - if (epgTag) + if (epgTag && !CServiceBroker::GetPVRManager().IsParentalLocked(epgTag)) m_searchfilter->SetSearchPhrase(epgTag->Title()); } diff --git a/xbmc/pvr/windows/GUIWindowPVRSearch.h b/xbmc/pvr/windows/GUIWindowPVRSearch.h index d287941f11..8bca6bd18c 100644 --- a/xbmc/pvr/windows/GUIWindowPVRSearch.h +++ b/xbmc/pvr/windows/GUIWindowPVRSearch.h @@ -20,7 +20,7 @@ namespace PVR { public: CGUIWindowPVRSearchBase(bool bRadio, int id, const std::string &xmlFile); - ~CGUIWindowPVRSearchBase() override = default; + ~CGUIWindowPVRSearchBase() override; bool OnMessage(CGUIMessage& message) override; void GetContextButtons(int itemNumber, CContextButtons &buttons) override; diff --git a/xbmc/pvr/windows/GUIWindowPVRTimerRules.cpp b/xbmc/pvr/windows/GUIWindowPVRTimerRules.cpp index eea4ca1662..c494d9e833 100644 --- a/xbmc/pvr/windows/GUIWindowPVRTimerRules.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRTimerRules.cpp @@ -11,7 +11,7 @@ #include "FileItem.h" #include "utils/URIUtils.h" -#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimersPath.h" using namespace PVR; diff --git a/xbmc/pvr/windows/GUIWindowPVRTimers.cpp b/xbmc/pvr/windows/GUIWindowPVRTimers.cpp index 29e82f5e1f..01d60a2278 100644 --- a/xbmc/pvr/windows/GUIWindowPVRTimers.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRTimers.cpp @@ -11,7 +11,7 @@ #include "FileItem.h" #include "utils/URIUtils.h" -#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimersPath.h" using namespace PVR; diff --git a/xbmc/pvr/windows/GUIWindowPVRTimersBase.cpp b/xbmc/pvr/windows/GUIWindowPVRTimersBase.cpp index 80bf4e0e9a..5f7784c249 100644 --- a/xbmc/pvr/windows/GUIWindowPVRTimersBase.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRTimersBase.cpp @@ -22,7 +22,8 @@ #include "pvr/PVRGUIActions.h" #include "pvr/PVRManager.h" #include "pvr/addons/PVRClients.h" -#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/timers/PVRTimersPath.h" using namespace PVR; using namespace KODI::MESSAGING; diff --git a/xbmc/utils/Observer.h b/xbmc/utils/Observer.h index 4c177a26fe..2d9de72ac4 100644 --- a/xbmc/utils/Observer.h +++ b/xbmc/utils/Observer.h @@ -24,6 +24,7 @@ typedef enum ObservableMessageEpgContainer, ObservableMessageEpgActiveItem, ObservableMessageEpgItemUpdate, + ObservableMessageEpgUpdatePending, ObservableMessageChannelGroup, ObservableMessageChannelGroupReset, ObservableMessageTimers, diff --git a/xbmc/windowing/X11/GLContextEGL.cpp b/xbmc/windowing/X11/GLContextEGL.cpp index 36ae4c0fb9..227ba4b16d 100644 --- a/xbmc/windowing/X11/GLContextEGL.cpp +++ b/xbmc/windowing/X11/GLContextEGL.cpp @@ -424,7 +424,7 @@ void CGLContextEGL::SwapBuffers() eglSwapBuffers(m_eglDisplay, m_eglSurface); clock_gettime(CLOCK_MONOTONIC, &nowTs); - now = nowTs.tv_sec * 1000000000 + nowTs.tv_nsec; + now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec; eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust2, &msc2, &sbc2); @@ -499,7 +499,7 @@ uint64_t CGLContextEGL::GetVblankTiming(uint64_t &msc, uint64_t &interval) struct timespec nowTs; uint64_t now; clock_gettime(CLOCK_MONOTONIC, &nowTs); - now = nowTs.tv_sec * 1000000000 + nowTs.tv_nsec; + now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec; now /= 1000; CSingleLock lock(m_syncLock); diff --git a/xbmc/windowing/android/WinSystemAndroid.cpp b/xbmc/windowing/android/WinSystemAndroid.cpp index def4ca4cdd..9c7807321b 100644 --- a/xbmc/windowing/android/WinSystemAndroid.cpp +++ b/xbmc/windowing/android/WinSystemAndroid.cpp @@ -201,29 +201,37 @@ void CWinSystemAndroid::OnTimeout() SetHDMIState(true); } -void CWinSystemAndroid::SetHDMIState(bool connected, uint32_t timeoutMs) +void CWinSystemAndroid::SetHDMIState(bool connected) { CSingleLock lock(m_resourceSection); - if (connected && m_dispResetState == RESET_WAITEVENT) + CLog::Log(LOGDEBUG, "CWinSystemAndroid::SetHDMIState: connected: %d, dispResetState: %d", static_cast<int>(connected), m_dispResetState); + if (connected && m_dispResetState != RESET_NOTWAITING) { for (auto resource : m_resources) resource->OnResetDisplay(); + m_dispResetState = RESET_NOTWAITING; + m_dispResetTimer->Stop(); } else if (!connected) { + if (m_dispResetState == RESET_WAITTIMER) + { + //HDMI_AUDIOPLUG arrived, use this + m_dispResetTimer->Stop(); + m_dispResetState = RESET_WAITEVENT; + return; + } + else if (m_dispResetState != RESET_NOTWAITING) + return; + int delay = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("videoscreen.delayrefreshchange") * 100; - if (timeoutMs > delay) - delay = timeoutMs; + if (delay < 2000) + delay = 2000; - if (delay > 0) - { - m_dispResetState = RESET_WAITTIMER; - m_dispResetTimer->Stop(); - m_dispResetTimer->Start(delay); - } - else - m_dispResetState = RESET_WAITEVENT; + m_dispResetState = RESET_WAITTIMER; + m_dispResetTimer->Stop(); + m_dispResetTimer->Start(delay); for (auto resource : m_resources) resource->OnLostDisplay(); diff --git a/xbmc/windowing/android/WinSystemAndroid.h b/xbmc/windowing/android/WinSystemAndroid.h index fec003c926..04a8f4a099 100644 --- a/xbmc/windowing/android/WinSystemAndroid.h +++ b/xbmc/windowing/android/WinSystemAndroid.h @@ -34,7 +34,7 @@ public: bool DestroyWindow() override; void UpdateResolutions() override; - void SetHDMIState(bool connected, uint32_t timeoutMs = 0); + void SetHDMIState(bool connected); bool HasCursor() override { return false; }; |